Example #1
0
func (pc *persistConn) readLoop() {
	// eofc is used to block http.Handler goroutines reading from Response.Body
	// at EOF until this goroutines has (potentially) added the connection
	// back to the idle pool.
	eofc := make(chan struct{})
	defer close(eofc) // unblock reader on errors

	// Read this once, before loop starts. (to avoid races in tests)
	testHookMu.Lock()
	testHookReadLoopBeforeNextRead := testHookReadLoopBeforeNextRead
	testHookMu.Unlock()

	alive := true
	for alive {
		pb, err := pc.br.Peek(1)

		pc.lk.Lock()
		if pc.numExpectedResponses == 0 {
			if !pc.closed {
				pc.closeLocked()
				if len(pb) > 0 {
					log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
						string(pb), err)
				}
			}
			pc.lk.Unlock()
			return
		}
		pc.lk.Unlock()

		rc := <-pc.reqch

		var resp *http.Response
		if err == nil {
			resp, err = http.ReadResponse(pc.br, rc.req)
			if err == nil && resp.StatusCode == 100 {
				// Skip any 100-continue for now.
				// TODO(bradfitz): if rc.req had "Expect: 100-continue",
				// actually block the request body write and signal the
				// writeLoop now to begin sending it. (Issue 2184) For now we
				// eat it, since we're never expecting one.
				resp, err = http.ReadResponse(pc.br, rc.req)
			}
		}

		if resp != nil {
			resp.TLS = pc.tlsState
		}

		hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0

		if err != nil {
			pc.close()
		} else {
			resp.Body = &bodyEOFSignal{body: resp.Body}
		}

		if err != nil || resp.Close || rc.req.Close || resp.StatusCode <= 199 {
			// Don't do keep-alive on error if either party requested a close
			// or we get an unexpected informational (1xx) response.
			// StatusCode 100 is already handled above.
			alive = false
		}

		var waitForBodyRead chan bool // channel is nil when there's no body
		if hasBody {
			waitForBodyRead = make(chan bool, 2)
			resp.Body.(*bodyEOFSignal).earlyCloseFn = func() error {
				waitForBodyRead <- false
				return nil
			}
			resp.Body.(*bodyEOFSignal).fn = func(err error) error {
				isEOF := err == io.EOF
				waitForBodyRead <- isEOF
				if isEOF {
					<-eofc // see comment at top
				} else if err != nil && pc.isCanceled() {
					return errRequestCanceled
				}
				return err
			}
		}

		pc.lk.Lock()
		pc.numExpectedResponses--
		pc.lk.Unlock()

		// The connection might be going away when we put the
		// idleConn below. When that happens, we close the response channel to signal
		// to roundTrip that the connection is gone. roundTrip waits for
		// both closing and a response in a select, so it might choose
		// the close channel, rather than the response.
		// We send the response first so that roundTrip can check
		// if there is a pending one with a non-blocking select
		// on the response channel before erroring out.
		rc.ch <- responseAndError{resp, err}

		if hasBody {
			// To avoid a race, wait for the just-returned
			// response body to be fully consumed before peek on
			// the underlying bufio reader.
			select {
			case bodyEOF := <-waitForBodyRead:
				pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool
				alive = alive &&
					bodyEOF &&
					!pc.sawEOF &&
					pc.wroteRequest() &&
					pc.t.putIdleConn(pc)
				if bodyEOF {
					eofc <- struct{}{}
				}
			case <-pc.closech:
				alive = false
			}
		} else {
			pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool
			alive = alive &&
				!pc.sawEOF &&
				pc.wroteRequest() &&
				pc.t.putIdleConn(pc)
		}

		if hook := testHookReadLoopBeforeNextRead; hook != nil {
			hook()
		}
	}
	pc.close()
}