github.com/searKing/golang/go@v1.2.117/net/tcp/reader.go (about)

     1  // Copyright 2020 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package tcp
     6  
     7  import (
     8  	"io"
     9  	"net"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  // connReader is the io.onMsgRead wrapper used by *conn. It combines a
    15  // selectively-activated io.LimitedReader (to bound request header
    16  // read sizes) with support for selectively keeping an io.onMsgRead.onMsgRead
    17  // call blocked in a background goroutine to wait for activity and
    18  // trigger a CloseNotifier channel.
    19  type connReader struct {
    20  	conn *conn
    21  
    22  	mu      sync.Mutex // guards following
    23  	hasByte bool
    24  	byteBuf [1]byte
    25  	cond    *sync.Cond
    26  	inRead  bool
    27  	aborted bool  // set true before conn.rwc deadline is set to past
    28  	remain  int64 // bytes remaining
    29  }
    30  
    31  func (cr *connReader) lock() {
    32  	cr.mu.Lock()
    33  	if cr.cond == nil {
    34  		cr.cond = sync.NewCond(&cr.mu)
    35  	}
    36  }
    37  
    38  func (cr *connReader) unlock() { cr.mu.Unlock() }
    39  
    40  func (cr *connReader) startBackgroundRead() {
    41  	cr.lock()
    42  	defer cr.unlock()
    43  	if cr.inRead {
    44  		panic("invalid concurrent Body.onMsgRead call")
    45  	}
    46  	if cr.hasByte {
    47  		return
    48  	}
    49  	cr.inRead = true
    50  	cr.conn.rwc.SetReadDeadline(time.Time{})
    51  	go cr.backgroundRead()
    52  }
    53  
    54  // conn -> byteBuf -> p
    55  func (cr *connReader) backgroundRead() {
    56  	n, err := cr.conn.rwc.Read(cr.byteBuf[:])
    57  	cr.lock()
    58  	if n == 1 {
    59  		cr.hasByte = true
    60  		// We were past the end of the previous request's body already
    61  		// (since we wouldn't be in a background read otherwise), so
    62  		// this is a pipelined HTTP request. Prior to Go 1.11 we used to
    63  		// send on the CloseNotify channel and cancel the context here,
    64  		// but the behavior was documented as only "may", and we only
    65  		// did that because that's how CloseNotify accidentally behaved
    66  		// in very early Go releases prior to context support. Once we
    67  		// added context support, people used a onMsgHandle's
    68  		// Request.Context() and passed it along. Having that context
    69  		// cancel on pipelined HTTP requests caused problems.
    70  		// Fortunately, almost nothing uses HTTP/1.x pipelining.
    71  		// Unfortunately, apt-get does, or sometimes does.
    72  		// New Go 1.11 behavior: don't fire CloseNotify or cancel
    73  		// contexts on pipelined requests. Shouldn't affect people, but
    74  		// fixes cases like Issue 23921. This does mean that a client
    75  		// closing their TCP connection after sending a pipelined
    76  		// request won't cancel the context, but we'll catch that on any
    77  		// write failure (in checkConnErrorWriter.Write).
    78  		// If the server never writes, yes, there are still contrived
    79  		// server & client behaviors where this fails to ever cancel the
    80  		// context, but that's kinda why HTTP/1.x pipelining died
    81  		// anyway.
    82  	}
    83  	if ne, ok := err.(net.Error); ok && cr.aborted && ne.Timeout() {
    84  		// Ignore this error. It's the expected error from
    85  		// another goroutine calling abortPendingRead.
    86  	} else if err != nil {
    87  		cr.handleReadError(err)
    88  	}
    89  	cr.aborted = false
    90  	cr.inRead = false
    91  	cr.unlock()
    92  	cr.cond.Broadcast()
    93  }
    94  
    95  func (cr *connReader) abortPendingRead() {
    96  	cr.lock()
    97  	defer cr.unlock()
    98  	if !cr.inRead {
    99  		return
   100  	}
   101  	cr.aborted = true
   102  	cr.conn.rwc.SetReadDeadline(aLongTimeAgo)
   103  	for cr.inRead {
   104  		cr.cond.Wait()
   105  	}
   106  	cr.conn.rwc.SetReadDeadline(time.Time{})
   107  }
   108  
   109  func (cr *connReader) setReadLimit(remain int64) { cr.remain = remain }
   110  func (cr *connReader) setInfiniteReadLimit()     { cr.remain = maxInt64 }
   111  func (cr *connReader) hitReadLimit() bool        { return cr.remain <= 0 }
   112  
   113  // handleReadError is called whenever a onMsgRead from the client returns a
   114  // non-nil error.
   115  //
   116  // The provided non-nil err is almost always io.EOF or a "use of
   117  // closed network connection". In any case, the error is not
   118  // particularly interesting, except perhaps for debugging during
   119  // development. Any error means the connection is dead and we should
   120  // down its context.
   121  //
   122  // It may be called from multiple goroutines.
   123  func (cr *connReader) handleReadError(_ error) {
   124  	cr.conn.cancelCtx()
   125  }
   126  
   127  func (cr *connReader) Read(p []byte) (n int, err error) {
   128  	cr.lock()
   129  	if cr.inRead {
   130  		cr.unlock()
   131  		panic("invalid concurrent Body.onMsgRead call")
   132  	}
   133  	if cr.hitReadLimit() {
   134  		cr.unlock()
   135  		return 0, io.EOF
   136  	}
   137  	if len(p) == 0 {
   138  		cr.unlock()
   139  		return 0, nil
   140  	}
   141  	if int64(len(p)) > cr.remain {
   142  		p = p[:cr.remain]
   143  	}
   144  	if cr.hasByte {
   145  		p[0] = cr.byteBuf[0]
   146  		cr.hasByte = false
   147  		cr.unlock()
   148  		return 1, nil
   149  	}
   150  	cr.inRead = true
   151  	cr.unlock()
   152  	n, err = cr.conn.rwc.Read(p)
   153  
   154  	cr.lock()
   155  	cr.inRead = false
   156  	if err != nil {
   157  		cr.handleReadError(err)
   158  	}
   159  	cr.remain -= int64(n)
   160  	cr.unlock()
   161  
   162  	cr.cond.Broadcast()
   163  	return n, err
   164  }