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 }