Update golang.org/x/net to not panic

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2017-12-07 18:31:19 +01:00 committed by Silvin Lubecki
parent 1a2244e384
commit 12c0825a4c
23 changed files with 3022 additions and 2561 deletions

View File

@ -67,7 +67,7 @@ github.com/xeipuuv/gojsonpointer e0fe6f68307607d540ed8eac07a342c33fa1b54a
github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45 github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45
github.com/xeipuuv/gojsonschema 93e72a773fade158921402d6a24c819b48aba29d github.com/xeipuuv/gojsonschema 93e72a773fade158921402d6a24c819b48aba29d
golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 golang.org/x/net a8b9294777976932365dabb6640cf1468d95c70f
golang.org/x/sys 95c6576299259db960f6c5b9b69ea52422860fce golang.org/x/sys 95c6576299259db960f6c5b9b69ea52422860fce
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb

3
vendor/golang.org/x/net/README generated vendored
View File

@ -1,3 +0,0 @@
This repository holds supplementary Go networking libraries.
To submit changes to this repository, see http://golang.org/doc/contribute.html.

16
vendor/golang.org/x/net/README.md generated vendored Normal file
View File

@ -0,0 +1,16 @@
# Go Networking
This repository holds supplementary Go networking libraries.
## Download/Install
The easiest way to install is to run `go get -u golang.org/x/net`. You can
also manually git clone the repository to `$GOPATH/src/golang.org/x/net`.
## Report Issues / Send Patches
This repository uses Gerrit for code changes. To learn how to submit
changes to this repository, see https://golang.org/doc/contribute.html.
The main issue tracker for the net repository is located at
https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the
subject line, so it is easy to find.

View File

@ -5,6 +5,8 @@
// Package context defines the Context type, which carries deadlines, // Package context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries // cancelation signals, and other request-scoped values across API boundaries
// and between processes. // and between processes.
// As of Go 1.7 this package is available in the standard library under the
// name context. https://golang.org/pkg/context.
// //
// Incoming requests to a server should create a Context, and outgoing calls to // Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between must // servers should accept a Context. The chain of function calls between must
@ -36,103 +38,6 @@
// Contexts. // Contexts.
package context // import "golang.org/x/net/context" package context // import "golang.org/x/net/context"
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// Background returns a non-nil, empty Context. It is never canceled, has no // Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function, // values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming // initialization, and tests, and as the top-level Context for incoming
@ -149,8 +54,3 @@ func Background() Context {
func TODO() Context { func TODO() Context {
return todo return todo
} }
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

20
vendor/golang.org/x/net/context/go19.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package context
import "context" // standard library's context, as of Go 1.7
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context = context.Context
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc = context.CancelFunc

109
vendor/golang.org/x/net/context/pre_go19.go generated vendored Normal file
View File

@ -0,0 +1,109 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.9
package context
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

View File

@ -56,7 +56,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
} }
// registerHTTPSProtocol calls Transport.RegisterProtocol but // registerHTTPSProtocol calls Transport.RegisterProtocol but
// convering panics into errors. // converting panics into errors.
func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) { func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {

View File

@ -87,13 +87,16 @@ type goAwayFlowError struct{}
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" } func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
// connErrorReason wraps a ConnectionError with an informative error about why it occurs. // connError represents an HTTP/2 ConnectionError error code, along
// with a string (for debugging) explaining why.
//
// Errors of this type are only returned by the frame parser functions // Errors of this type are only returned by the frame parser functions
// and converted into ConnectionError(ErrCodeProtocol). // and converted into ConnectionError(Code), after stashing away
// the Reason into the Framer's errDetail field, accessible via
// the (*Framer).ErrorDetail method.
type connError struct { type connError struct {
Code ErrCode Code ErrCode // the ConnectionError error code
Reason string Reason string // additional reason
} }
func (e connError) Error() string { func (e connError) Error() string {

View File

@ -52,3 +52,5 @@ func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
func reqBodyIsNoBody(body io.ReadCloser) bool { func reqBodyIsNoBody(body io.ReadCloser) bool {
return body == http.NoBody return body == http.NoBody
} }
func go18httpNoBody() io.ReadCloser { return http.NoBody } // for tests only

View File

@ -376,12 +376,16 @@ func (s *sorter) SortStrings(ss []string) {
// validPseudoPath reports whether v is a valid :path pseudo-header // validPseudoPath reports whether v is a valid :path pseudo-header
// value. It must be either: // value. It must be either:
// //
// *) a non-empty string starting with '/', but not with with "//", // *) a non-empty string starting with '/'
// *) the string '*', for OPTIONS requests. // *) the string '*', for OPTIONS requests.
// //
// For now this is only used a quick check for deciding when to clean // For now this is only used a quick check for deciding when to clean
// up Opaque URLs before sending requests from the Transport. // up Opaque URLs before sending requests from the Transport.
// See golang.org/issue/16847 // See golang.org/issue/16847
//
// We used to enforce that the path also didn't start with "//", but
// Google's GFE accepts such paths and Chrome sends them, so ignore
// that part of the spec. See golang.org/issue/19103.
func validPseudoPath(v string) bool { func validPseudoPath(v string) bool {
return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*" return (len(v) > 0 && v[0] == '/') || v == "*"
} }

View File

@ -25,3 +25,5 @@ func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
} }
func reqBodyIsNoBody(io.ReadCloser) bool { return false } func reqBodyIsNoBody(io.ReadCloser) bool { return false }
func go18httpNoBody() io.ReadCloser { return nil } // for tests only

View File

@ -50,7 +50,7 @@ func (p *pipe) Read(d []byte) (n int, err error) {
if p.breakErr != nil { if p.breakErr != nil {
return 0, p.breakErr return 0, p.breakErr
} }
if p.b.Len() > 0 { if p.b != nil && p.b.Len() > 0 {
return p.b.Read(d) return p.b.Read(d)
} }
if p.err != nil { if p.err != nil {

View File

@ -220,12 +220,15 @@ func ConfigureServer(s *http.Server, conf *Server) error {
} else if s.TLSConfig.CipherSuites != nil { } else if s.TLSConfig.CipherSuites != nil {
// If they already provided a CipherSuite list, return // If they already provided a CipherSuite list, return
// an error if it has a bad order or is missing // an error if it has a bad order or is missing
// ECDHE_RSA_WITH_AES_128_GCM_SHA256. // ECDHE_RSA_WITH_AES_128_GCM_SHA256 or ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.
const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
haveRequired := false haveRequired := false
sawBad := false sawBad := false
for i, cs := range s.TLSConfig.CipherSuites { for i, cs := range s.TLSConfig.CipherSuites {
if cs == requiredCipher { switch cs {
case tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
// Alternative MTI cipher to not discourage ECDSA-only servers.
// See http://golang.org/cl/30721 for further information.
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
haveRequired = true haveRequired = true
} }
if isBadCipher(cs) { if isBadCipher(cs) {
@ -235,7 +238,7 @@ func ConfigureServer(s *http.Server, conf *Server) error {
} }
} }
if !haveRequired { if !haveRequired {
return fmt.Errorf("http2: TLSConfig.CipherSuites is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher.")
} }
} }
@ -649,7 +652,7 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
if err == nil { if err == nil {
return return
} }
if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) { if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) || err == errPrefaceTimeout {
// Boring, expected errors. // Boring, expected errors.
sc.vlogf(format, args...) sc.vlogf(format, args...)
} else { } else {
@ -853,8 +856,13 @@ func (sc *serverConn) serve() {
} }
} }
if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame { // Start the shutdown timer after sending a GOAWAY. When sending GOAWAY
return // with no error code (graceful shutdown), don't start the timer until
// all open streams have been completed.
sentGoAway := sc.inGoAway && !sc.needToSendGoAway && !sc.writingFrame
gracefulShutdownComplete := sc.goAwayCode == ErrCodeNo && sc.curOpenStreams() == 0
if sentGoAway && sc.shutdownTimer == nil && (sc.goAwayCode != ErrCodeNo || gracefulShutdownComplete) {
sc.shutDownIn(goAwayTimeout)
} }
} }
} }
@ -889,8 +897,11 @@ func (sc *serverConn) sendServeMsg(msg interface{}) {
} }
} }
// readPreface reads the ClientPreface greeting from the peer var errPrefaceTimeout = errors.New("timeout waiting for client preface")
// or returns an error on timeout or an invalid greeting.
// readPreface reads the ClientPreface greeting from the peer or
// returns errPrefaceTimeout on timeout, or an error if the greeting
// is invalid.
func (sc *serverConn) readPreface() error { func (sc *serverConn) readPreface() error {
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { go func() {
@ -908,7 +919,7 @@ func (sc *serverConn) readPreface() error {
defer timer.Stop() defer timer.Stop()
select { select {
case <-timer.C: case <-timer.C:
return errors.New("timeout waiting for client preface") return errPrefaceTimeout
case err := <-errc: case err := <-errc:
if err == nil { if err == nil {
if VerboseLogs { if VerboseLogs {
@ -1218,30 +1229,31 @@ func (sc *serverConn) startGracefulShutdown() {
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) }) sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
} }
// After sending GOAWAY, the connection will close after goAwayTimeout.
// If we close the connection immediately after sending GOAWAY, there may
// be unsent data in our kernel receive buffer, which will cause the kernel
// to send a TCP RST on close() instead of a FIN. This RST will abort the
// connection immediately, whether or not the client had received the GOAWAY.
//
// Ideally we should delay for at least 1 RTT + epsilon so the client has
// a chance to read the GOAWAY and stop sending messages. Measuring RTT
// is hard, so we approximate with 1 second. See golang.org/issue/18701.
//
// This is a var so it can be shorter in tests, where all requests uses the
// loopback interface making the expected RTT very small.
//
// TODO: configurable?
var goAwayTimeout = 1 * time.Second
func (sc *serverConn) startGracefulShutdownInternal() { func (sc *serverConn) startGracefulShutdownInternal() {
sc.goAwayIn(ErrCodeNo, 0) sc.goAway(ErrCodeNo)
} }
func (sc *serverConn) goAway(code ErrCode) { func (sc *serverConn) goAway(code ErrCode) {
sc.serveG.check()
var forceCloseIn time.Duration
if code != ErrCodeNo {
forceCloseIn = 250 * time.Millisecond
} else {
// TODO: configurable
forceCloseIn = 1 * time.Second
}
sc.goAwayIn(code, forceCloseIn)
}
func (sc *serverConn) goAwayIn(code ErrCode, forceCloseIn time.Duration) {
sc.serveG.check() sc.serveG.check()
if sc.inGoAway { if sc.inGoAway {
return return
} }
if forceCloseIn != 0 {
sc.shutDownIn(forceCloseIn)
}
sc.inGoAway = true sc.inGoAway = true
sc.needToSendGoAway = true sc.needToSendGoAway = true
sc.goAwayCode = code sc.goAwayCode = code
@ -2252,6 +2264,7 @@ type responseWriterState struct {
wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet.
sentHeader bool // have we sent the header frame? sentHeader bool // have we sent the header frame?
handlerDone bool // handler has finished handlerDone bool // handler has finished
dirty bool // a Write failed; don't reuse this responseWriterState
sentContentLen int64 // non-zero if handler set a Content-Length header sentContentLen int64 // non-zero if handler set a Content-Length header
wroteBytes int64 wroteBytes int64
@ -2309,7 +2322,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
clen = strconv.Itoa(len(p)) clen = strconv.Itoa(len(p))
} }
_, hasContentType := rws.snapHeader["Content-Type"] _, hasContentType := rws.snapHeader["Content-Type"]
if !hasContentType && bodyAllowedForStatus(rws.status) { if !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
ctype = http.DetectContentType(p) ctype = http.DetectContentType(p)
} }
var date string var date string
@ -2333,6 +2346,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
date: date, date: date,
}) })
if err != nil { if err != nil {
rws.dirty = true
return 0, err return 0, err
} }
if endStream { if endStream {
@ -2354,6 +2368,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
if len(p) > 0 || endStream { if len(p) > 0 || endStream {
// only send a 0 byte DATA frame if we're ending the stream. // only send a 0 byte DATA frame if we're ending the stream.
if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil {
rws.dirty = true
return 0, err return 0, err
} }
} }
@ -2365,6 +2380,9 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
trailers: rws.trailers, trailers: rws.trailers,
endStream: true, endStream: true,
}) })
if err != nil {
rws.dirty = true
}
return len(p), err return len(p), err
} }
return len(p), nil return len(p), nil
@ -2472,7 +2490,26 @@ func (w *responseWriter) Header() http.Header {
return rws.handlerHeader return rws.handlerHeader
} }
// checkWriteHeaderCode is a copy of net/http's checkWriteHeaderCode.
func checkWriteHeaderCode(code int) {
// Issue 22880: require valid WriteHeader status codes.
// For now we only enforce that it's three digits.
// In the future we might block things over 599 (600 and above aren't defined
// at http://httpwg.org/specs/rfc7231.html#status.codes)
// and we might block under 200 (once we have more mature 1xx support).
// But for now any three digits.
//
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
// no equivalent bogus thing we can realistically send in HTTP/2,
// so we'll consistently panic instead and help people find their bugs
// early. (We can't return an error from WriteHeader even if we wanted to.)
if code < 100 || code > 999 {
panic(fmt.Sprintf("invalid WriteHeader code %v", code))
}
}
func (w *responseWriter) WriteHeader(code int) { func (w *responseWriter) WriteHeader(code int) {
checkWriteHeaderCode(code)
rws := w.rws rws := w.rws
if rws == nil { if rws == nil {
panic("WriteHeader called after Handler finished") panic("WriteHeader called after Handler finished")
@ -2504,7 +2541,7 @@ func cloneHeader(h http.Header) http.Header {
// //
// * Handler calls w.Write or w.WriteString -> // * Handler calls w.Write or w.WriteString ->
// * -> rws.bw (*bufio.Writer) -> // * -> rws.bw (*bufio.Writer) ->
// * (Handler migth call Flush) // * (Handler might call Flush)
// * -> chunkWriter{rws} // * -> chunkWriter{rws}
// * -> responseWriterState.writeChunk(p []byte) // * -> responseWriterState.writeChunk(p []byte)
// * -> responseWriterState.writeChunk (most of the magic; see comment there) // * -> responseWriterState.writeChunk (most of the magic; see comment there)
@ -2543,10 +2580,19 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int,
func (w *responseWriter) handlerDone() { func (w *responseWriter) handlerDone() {
rws := w.rws rws := w.rws
dirty := rws.dirty
rws.handlerDone = true rws.handlerDone = true
w.Flush() w.Flush()
w.rws = nil w.rws = nil
responseWriterStatePool.Put(rws) if !dirty {
// Only recycle the pool if all prior Write calls to
// the serverConn goroutine completed successfully. If
// they returned earlier due to resets from the peer
// there might still be write goroutines outstanding
// from the serverConn referencing the rws memory. See
// issue 20704.
responseWriterStatePool.Put(rws)
}
} }
// Push errors. // Push errors.

View File

@ -18,6 +18,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"math" "math"
mathrand "math/rand"
"net" "net"
"net/http" "net/http"
"sort" "sort"
@ -86,7 +87,7 @@ type Transport struct {
// MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
// send in the initial settings frame. It is how many bytes // send in the initial settings frame. It is how many bytes
// of response headers are allow. Unlike the http2 spec, zero here // of response headers are allowed. Unlike the http2 spec, zero here
// means to use a default limit (currently 10MB). If you actually // means to use a default limit (currently 10MB). If you actually
// want to advertise an ulimited value to the peer, Transport // want to advertise an ulimited value to the peer, Transport
// interprets the highest possible value here (0xffffffff or 1<<32-1) // interprets the highest possible value here (0xffffffff or 1<<32-1)
@ -164,15 +165,17 @@ type ClientConn struct {
goAwayDebug string // goAway frame's debug data, retained as a string goAwayDebug string // goAway frame's debug data, retained as a string
streams map[uint32]*clientStream // client-initiated streams map[uint32]*clientStream // client-initiated
nextStreamID uint32 nextStreamID uint32
pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
pings map[[8]byte]chan struct{} // in flight ping data to notification channel pings map[[8]byte]chan struct{} // in flight ping data to notification channel
bw *bufio.Writer bw *bufio.Writer
br *bufio.Reader br *bufio.Reader
fr *Framer fr *Framer
lastActive time.Time lastActive time.Time
// Settings from peer: (also guarded by mu) // Settings from peer: (also guarded by mu)
maxFrameSize uint32 maxFrameSize uint32
maxConcurrentStreams uint32 maxConcurrentStreams uint32
initialWindowSize uint32 peerMaxHeaderListSize uint64
initialWindowSize uint32
hbuf bytes.Buffer // HPACK encoder writes into this hbuf bytes.Buffer // HPACK encoder writes into this
henc *hpack.Encoder henc *hpack.Encoder
@ -216,35 +219,45 @@ type clientStream struct {
resTrailer *http.Header // client's Response.Trailer resTrailer *http.Header // client's Response.Trailer
} }
// awaitRequestCancel runs in its own goroutine and waits for the user // awaitRequestCancel waits for the user to cancel a request or for the done
// to cancel a RoundTrip request, its context to expire, or for the // channel to be signaled. A non-nil error is returned only if the request was
// request to be done (any way it might be removed from the cc.streams // canceled.
// map: peer reset, successful completion, TCP connection breakage, func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
// etc)
func (cs *clientStream) awaitRequestCancel(req *http.Request) {
ctx := reqContext(req) ctx := reqContext(req)
if req.Cancel == nil && ctx.Done() == nil { if req.Cancel == nil && ctx.Done() == nil {
return return nil
} }
select { select {
case <-req.Cancel: case <-req.Cancel:
cs.cancelStream() return errRequestCanceled
cs.bufPipe.CloseWithError(errRequestCanceled)
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err()
case <-done:
return nil
}
}
// awaitRequestCancel waits for the user to cancel a request, its context to
// expire, or for the request to be done (any way it might be removed from the
// cc.streams map: peer reset, successful completion, TCP connection breakage,
// etc). If the request is canceled, then cs will be canceled and closed.
func (cs *clientStream) awaitRequestCancel(req *http.Request) {
if err := awaitRequestCancel(req, cs.done); err != nil {
cs.cancelStream() cs.cancelStream()
cs.bufPipe.CloseWithError(ctx.Err()) cs.bufPipe.CloseWithError(err)
case <-cs.done:
} }
} }
func (cs *clientStream) cancelStream() { func (cs *clientStream) cancelStream() {
cs.cc.mu.Lock() cc := cs.cc
cc.mu.Lock()
didReset := cs.didReset didReset := cs.didReset
cs.didReset = true cs.didReset = true
cs.cc.mu.Unlock() cc.mu.Unlock()
if !didReset { if !didReset {
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
cc.forgetStreamID(cs.ID)
} }
} }
@ -261,6 +274,13 @@ func (cs *clientStream) checkResetOrDone() error {
} }
} }
func (cs *clientStream) getStartedWrite() bool {
cc := cs.cc
cc.mu.Lock()
defer cc.mu.Unlock()
return cs.startedWrite
}
func (cs *clientStream) abortRequestBodyWrite(err error) { func (cs *clientStream) abortRequestBodyWrite(err error) {
if err == nil { if err == nil {
panic("nil error") panic("nil error")
@ -329,17 +349,28 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
} }
addr := authorityAddr(req.URL.Scheme, req.URL.Host) addr := authorityAddr(req.URL.Scheme, req.URL.Host)
for { for retry := 0; ; retry++ {
cc, err := t.connPool().GetClientConn(req, addr) cc, err := t.connPool().GetClientConn(req, addr)
if err != nil { if err != nil {
t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
return nil, err return nil, err
} }
traceGotConn(req, cc) traceGotConn(req, cc)
res, err := cc.RoundTrip(req) res, gotErrAfterReqBodyWrite, err := cc.roundTrip(req)
if err != nil { if err != nil && retry <= 6 {
if req, err = shouldRetryRequest(req, err); err == nil { if req, err = shouldRetryRequest(req, err, gotErrAfterReqBodyWrite); err == nil {
continue // After the first retry, do exponential backoff with 10% jitter.
if retry == 0 {
continue
}
backoff := float64(uint(1) << (uint(retry) - 1))
backoff += backoff * (0.1 * mathrand.Float64())
select {
case <-time.After(time.Second * time.Duration(backoff)):
continue
case <-reqContext(req).Done():
return nil, reqContext(req).Err()
}
} }
} }
if err != nil { if err != nil {
@ -360,43 +391,50 @@ func (t *Transport) CloseIdleConnections() {
} }
var ( var (
errClientConnClosed = errors.New("http2: client conn is closed") errClientConnClosed = errors.New("http2: client conn is closed")
errClientConnUnusable = errors.New("http2: client conn not usable") errClientConnUnusable = errors.New("http2: client conn not usable")
errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY")
errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY")
errClientConnGotGoAwayAfterSomeReqBody = errors.New("http2: Transport received Server's graceful shutdown GOAWAY; some request body already written")
) )
// shouldRetryRequest is called by RoundTrip when a request fails to get // shouldRetryRequest is called by RoundTrip when a request fails to get
// response headers. It is always called with a non-nil error. // response headers. It is always called with a non-nil error.
// It returns either a request to retry (either the same request, or a // It returns either a request to retry (either the same request, or a
// modified clone), or an error if the request can't be replayed. // modified clone), or an error if the request can't be replayed.
func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) { func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*http.Request, error) {
switch err { if !canRetryError(err) {
default:
return nil, err return nil, err
case errClientConnUnusable, errClientConnGotGoAway:
return req, nil
case errClientConnGotGoAwayAfterSomeReqBody:
// If the Body is nil (or http.NoBody), it's safe to reuse
// this request and its Body.
if req.Body == nil || reqBodyIsNoBody(req.Body) {
return req, nil
}
// Otherwise we depend on the Request having its GetBody
// func defined.
getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody
if getBody == nil {
return nil, errors.New("http2: Transport: peer server initiated graceful shutdown after some of Request.Body was written; define Request.GetBody to avoid this error")
}
body, err := getBody()
if err != nil {
return nil, err
}
newReq := *req
newReq.Body = body
return &newReq, nil
} }
if !afterBodyWrite {
return req, nil
}
// If the Body is nil (or http.NoBody), it's safe to reuse
// this request and its Body.
if req.Body == nil || reqBodyIsNoBody(req.Body) {
return req, nil
}
// Otherwise we depend on the Request having its GetBody
// func defined.
getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody
if getBody == nil {
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
}
body, err := getBody()
if err != nil {
return nil, err
}
newReq := *req
newReq.Body = body
return &newReq, nil
}
func canRetryError(err error) bool {
if err == errClientConnUnusable || err == errClientConnGotGoAway {
return true
}
if se, ok := err.(StreamError); ok {
return se.Code == ErrCodeRefusedStream
}
return false
} }
func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, error) { func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, error) {
@ -474,17 +512,18 @@ func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
cc := &ClientConn{ cc := &ClientConn{
t: t, t: t,
tconn: c, tconn: c,
readerDone: make(chan struct{}), readerDone: make(chan struct{}),
nextStreamID: 1, nextStreamID: 1,
maxFrameSize: 16 << 10, // spec default maxFrameSize: 16 << 10, // spec default
initialWindowSize: 65535, // spec default initialWindowSize: 65535, // spec default
maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough.
streams: make(map[uint32]*clientStream), peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
singleUse: singleUse, streams: make(map[uint32]*clientStream),
wantSettingsAck: true, singleUse: singleUse,
pings: make(map[[8]byte]chan struct{}), wantSettingsAck: true,
pings: make(map[[8]byte]chan struct{}),
} }
if d := t.idleConnTimeout(); d != 0 { if d := t.idleConnTimeout(); d != 0 {
cc.idleTimeout = d cc.idleTimeout = d
@ -560,6 +599,8 @@ func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
} }
} }
// CanTakeNewRequest reports whether the connection can take a new request,
// meaning it has not been closed or received or sent a GOAWAY.
func (cc *ClientConn) CanTakeNewRequest() bool { func (cc *ClientConn) CanTakeNewRequest() bool {
cc.mu.Lock() cc.mu.Lock()
defer cc.mu.Unlock() defer cc.mu.Unlock()
@ -571,8 +612,7 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool {
return false return false
} }
return cc.goAway == nil && !cc.closed && return cc.goAway == nil && !cc.closed &&
int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) && int64(cc.nextStreamID)+int64(cc.pendingRequests) < math.MaxInt32
cc.nextStreamID < math.MaxInt32
} }
// onIdleTimeout is called from a time.AfterFunc goroutine. It will // onIdleTimeout is called from a time.AfterFunc goroutine. It will
@ -694,7 +734,7 @@ func checkConnHeaders(req *http.Request) error {
// req.ContentLength, where 0 actually means zero (not unknown) and -1 // req.ContentLength, where 0 actually means zero (not unknown) and -1
// means unknown. // means unknown.
func actualContentLength(req *http.Request) int64 { func actualContentLength(req *http.Request) int64 {
if req.Body == nil { if req.Body == nil || reqBodyIsNoBody(req.Body) {
return 0 return 0
} }
if req.ContentLength != 0 { if req.ContentLength != 0 {
@ -704,8 +744,13 @@ func actualContentLength(req *http.Request) int64 {
} }
func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
resp, _, err := cc.roundTrip(req)
return resp, err
}
func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAfterReqBodyWrite bool, err error) {
if err := checkConnHeaders(req); err != nil { if err := checkConnHeaders(req); err != nil {
return nil, err return nil, false, err
} }
if cc.idleTimer != nil { if cc.idleTimer != nil {
cc.idleTimer.Stop() cc.idleTimer.Stop()
@ -713,20 +758,19 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
trailers, err := commaSeparatedTrailers(req) trailers, err := commaSeparatedTrailers(req)
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
hasTrailers := trailers != "" hasTrailers := trailers != ""
cc.mu.Lock() cc.mu.Lock()
cc.lastActive = time.Now() if err := cc.awaitOpenSlotForRequest(req); err != nil {
if cc.closed || !cc.canTakeNewRequestLocked() {
cc.mu.Unlock() cc.mu.Unlock()
return nil, errClientConnUnusable return nil, false, err
} }
body := req.Body body := req.Body
hasBody := body != nil
contentLen := actualContentLength(req) contentLen := actualContentLength(req)
hasBody := contentLen != 0
// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
var requestedGzip bool var requestedGzip bool
@ -755,7 +799,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
hdrs, err := cc.encodeHeaders(req, requestedGzip, trailers, contentLen) hdrs, err := cc.encodeHeaders(req, requestedGzip, trailers, contentLen)
if err != nil { if err != nil {
cc.mu.Unlock() cc.mu.Unlock()
return nil, err return nil, false, err
} }
cs := cc.newStream() cs := cc.newStream()
@ -767,7 +811,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
cc.wmu.Lock() cc.wmu.Lock()
endStream := !hasBody && !hasTrailers endStream := !hasBody && !hasTrailers
werr := cc.writeHeaders(cs.ID, endStream, hdrs) werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
cc.wmu.Unlock() cc.wmu.Unlock()
traceWroteHeaders(cs.trace) traceWroteHeaders(cs.trace)
cc.mu.Unlock() cc.mu.Unlock()
@ -781,7 +825,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
// Don't bother sending a RST_STREAM (our write already failed; // Don't bother sending a RST_STREAM (our write already failed;
// no need to keep writing) // no need to keep writing)
traceWroteRequest(cs.trace, werr) traceWroteRequest(cs.trace, werr)
return nil, werr return nil, false, werr
} }
var respHeaderTimer <-chan time.Time var respHeaderTimer <-chan time.Time
@ -800,7 +844,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
bodyWritten := false bodyWritten := false
ctx := reqContext(req) ctx := reqContext(req)
handleReadLoopResponse := func(re resAndError) (*http.Response, error) { handleReadLoopResponse := func(re resAndError) (*http.Response, bool, error) {
res := re.res res := re.res
if re.err != nil || res.StatusCode > 299 { if re.err != nil || res.StatusCode > 299 {
// On error or status code 3xx, 4xx, 5xx, etc abort any // On error or status code 3xx, 4xx, 5xx, etc abort any
@ -816,19 +860,12 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
cs.abortRequestBodyWrite(errStopReqBodyWrite) cs.abortRequestBodyWrite(errStopReqBodyWrite)
} }
if re.err != nil { if re.err != nil {
if re.err == errClientConnGotGoAway {
cc.mu.Lock()
if cs.startedWrite {
re.err = errClientConnGotGoAwayAfterSomeReqBody
}
cc.mu.Unlock()
}
cc.forgetStreamID(cs.ID) cc.forgetStreamID(cs.ID)
return nil, re.err return nil, cs.getStartedWrite(), re.err
} }
res.Request = req res.Request = req
res.TLS = cc.tlsState res.TLS = cc.tlsState
return res, nil return res, false, nil
} }
for { for {
@ -836,37 +873,37 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
case re := <-readLoopResCh: case re := <-readLoopResCh:
return handleReadLoopResponse(re) return handleReadLoopResponse(re)
case <-respHeaderTimer: case <-respHeaderTimer:
cc.forgetStreamID(cs.ID)
if !hasBody || bodyWritten { if !hasBody || bodyWritten {
cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
} else { } else {
bodyWriter.cancel() bodyWriter.cancel()
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
} }
return nil, errTimeout cc.forgetStreamID(cs.ID)
return nil, cs.getStartedWrite(), errTimeout
case <-ctx.Done(): case <-ctx.Done():
cc.forgetStreamID(cs.ID)
if !hasBody || bodyWritten { if !hasBody || bodyWritten {
cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
} else { } else {
bodyWriter.cancel() bodyWriter.cancel()
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
} }
return nil, ctx.Err() cc.forgetStreamID(cs.ID)
return nil, cs.getStartedWrite(), ctx.Err()
case <-req.Cancel: case <-req.Cancel:
cc.forgetStreamID(cs.ID)
if !hasBody || bodyWritten { if !hasBody || bodyWritten {
cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
} else { } else {
bodyWriter.cancel() bodyWriter.cancel()
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
} }
return nil, errRequestCanceled cc.forgetStreamID(cs.ID)
return nil, cs.getStartedWrite(), errRequestCanceled
case <-cs.peerReset: case <-cs.peerReset:
// processResetStream already removed the // processResetStream already removed the
// stream from the streams map; no need for // stream from the streams map; no need for
// forgetStreamID. // forgetStreamID.
return nil, cs.resetErr return nil, cs.getStartedWrite(), cs.resetErr
case err := <-bodyWriter.resc: case err := <-bodyWriter.resc:
// Prefer the read loop's response, if available. Issue 16102. // Prefer the read loop's response, if available. Issue 16102.
select { select {
@ -875,7 +912,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
default: default:
} }
if err != nil { if err != nil {
return nil, err return nil, cs.getStartedWrite(), err
} }
bodyWritten = true bodyWritten = true
if d := cc.responseHeaderTimeout(); d != 0 { if d := cc.responseHeaderTimeout(); d != 0 {
@ -887,14 +924,52 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
} }
} }
// awaitOpenSlotForRequest waits until len(streams) < maxConcurrentStreams.
// Must hold cc.mu.
func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
var waitingForConn chan struct{}
var waitingForConnErr error // guarded by cc.mu
for {
cc.lastActive = time.Now()
if cc.closed || !cc.canTakeNewRequestLocked() {
return errClientConnUnusable
}
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
if waitingForConn != nil {
close(waitingForConn)
}
return nil
}
// Unfortunately, we cannot wait on a condition variable and channel at
// the same time, so instead, we spin up a goroutine to check if the
// request is canceled while we wait for a slot to open in the connection.
if waitingForConn == nil {
waitingForConn = make(chan struct{})
go func() {
if err := awaitRequestCancel(req, waitingForConn); err != nil {
cc.mu.Lock()
waitingForConnErr = err
cc.cond.Broadcast()
cc.mu.Unlock()
}
}()
}
cc.pendingRequests++
cc.cond.Wait()
cc.pendingRequests--
if waitingForConnErr != nil {
return waitingForConnErr
}
}
}
// requires cc.wmu be held // requires cc.wmu be held
func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, hdrs []byte) error { func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, maxFrameSize int, hdrs []byte) error {
first := true // first frame written (HEADERS is first, then CONTINUATION) first := true // first frame written (HEADERS is first, then CONTINUATION)
frameSize := int(cc.maxFrameSize)
for len(hdrs) > 0 && cc.werr == nil { for len(hdrs) > 0 && cc.werr == nil {
chunk := hdrs chunk := hdrs
if len(chunk) > frameSize { if len(chunk) > maxFrameSize {
chunk = chunk[:frameSize] chunk = chunk[:maxFrameSize]
} }
hdrs = hdrs[len(chunk):] hdrs = hdrs[len(chunk):]
endHeaders := len(hdrs) == 0 endHeaders := len(hdrs) == 0
@ -1002,17 +1077,26 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
var trls []byte var trls []byte
if hasTrailers { if hasTrailers {
cc.mu.Lock() cc.mu.Lock()
defer cc.mu.Unlock() trls, err = cc.encodeTrailers(req)
trls = cc.encodeTrailers(req) cc.mu.Unlock()
if err != nil {
cc.writeStreamReset(cs.ID, ErrCodeInternal, err)
cc.forgetStreamID(cs.ID)
return err
}
} }
cc.mu.Lock()
maxFrameSize := int(cc.maxFrameSize)
cc.mu.Unlock()
cc.wmu.Lock() cc.wmu.Lock()
defer cc.wmu.Unlock() defer cc.wmu.Unlock()
// Two ways to send END_STREAM: either with trailers, or // Two ways to send END_STREAM: either with trailers, or
// with an empty DATA frame. // with an empty DATA frame.
if len(trls) > 0 { if len(trls) > 0 {
err = cc.writeHeaders(cs.ID, true, trls) err = cc.writeHeaders(cs.ID, true, maxFrameSize, trls)
} else { } else {
err = cc.fr.WriteData(cs.ID, true, nil) err = cc.fr.WriteData(cs.ID, true, nil)
} }
@ -1106,62 +1190,86 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
} }
} }
// 8.1.2.3 Request Pseudo-Header Fields enumerateHeaders := func(f func(name, value string)) {
// The :path pseudo-header field includes the path and query parts of the // 8.1.2.3 Request Pseudo-Header Fields
// target URI (the path-absolute production and optionally a '?' character // The :path pseudo-header field includes the path and query parts of the
// followed by the query production (see Sections 3.3 and 3.4 of // target URI (the path-absolute production and optionally a '?' character
// [RFC3986]). // followed by the query production (see Sections 3.3 and 3.4 of
cc.writeHeader(":authority", host) // [RFC3986]).
cc.writeHeader(":method", req.Method) f(":authority", host)
if req.Method != "CONNECT" { f(":method", req.Method)
cc.writeHeader(":path", path) if req.Method != "CONNECT" {
cc.writeHeader(":scheme", req.URL.Scheme) f(":path", path)
} f(":scheme", req.URL.Scheme)
if trailers != "" { }
cc.writeHeader("trailer", trailers) if trailers != "" {
f("trailer", trailers)
}
var didUA bool
for k, vv := range req.Header {
if strings.EqualFold(k, "host") || strings.EqualFold(k, "content-length") {
// Host is :authority, already sent.
// Content-Length is automatic, set below.
continue
} else if strings.EqualFold(k, "connection") || strings.EqualFold(k, "proxy-connection") ||
strings.EqualFold(k, "transfer-encoding") || strings.EqualFold(k, "upgrade") ||
strings.EqualFold(k, "keep-alive") {
// Per 8.1.2.2 Connection-Specific Header
// Fields, don't send connection-specific
// fields. We have already checked if any
// are error-worthy so just ignore the rest.
continue
} else if strings.EqualFold(k, "user-agent") {
// Match Go's http1 behavior: at most one
// User-Agent. If set to nil or empty string,
// then omit it. Otherwise if not mentioned,
// include the default (below).
didUA = true
if len(vv) < 1 {
continue
}
vv = vv[:1]
if vv[0] == "" {
continue
}
}
for _, v := range vv {
f(k, v)
}
}
if shouldSendReqContentLength(req.Method, contentLength) {
f("content-length", strconv.FormatInt(contentLength, 10))
}
if addGzipHeader {
f("accept-encoding", "gzip")
}
if !didUA {
f("user-agent", defaultUserAgent)
}
} }
var didUA bool // Do a first pass over the headers counting bytes to ensure
for k, vv := range req.Header { // we don't exceed cc.peerMaxHeaderListSize. This is done as a
lowKey := strings.ToLower(k) // separate pass before encoding the headers to prevent
switch lowKey { // modifying the hpack state.
case "host", "content-length": hlSize := uint64(0)
// Host is :authority, already sent. enumerateHeaders(func(name, value string) {
// Content-Length is automatic, set below. hf := hpack.HeaderField{Name: name, Value: value}
continue hlSize += uint64(hf.Size())
case "connection", "proxy-connection", "transfer-encoding", "upgrade", "keep-alive": })
// Per 8.1.2.2 Connection-Specific Header
// Fields, don't send connection-specific if hlSize > cc.peerMaxHeaderListSize {
// fields. We have already checked if any return nil, errRequestHeaderListSize
// are error-worthy so just ignore the rest.
continue
case "user-agent":
// Match Go's http1 behavior: at most one
// User-Agent. If set to nil or empty string,
// then omit it. Otherwise if not mentioned,
// include the default (below).
didUA = true
if len(vv) < 1 {
continue
}
vv = vv[:1]
if vv[0] == "" {
continue
}
}
for _, v := range vv {
cc.writeHeader(lowKey, v)
}
}
if shouldSendReqContentLength(req.Method, contentLength) {
cc.writeHeader("content-length", strconv.FormatInt(contentLength, 10))
}
if addGzipHeader {
cc.writeHeader("accept-encoding", "gzip")
}
if !didUA {
cc.writeHeader("user-agent", defaultUserAgent)
} }
// Header list size is ok. Write the headers.
enumerateHeaders(func(name, value string) {
cc.writeHeader(strings.ToLower(name), value)
})
return cc.hbuf.Bytes(), nil return cc.hbuf.Bytes(), nil
} }
@ -1188,17 +1296,29 @@ func shouldSendReqContentLength(method string, contentLength int64) bool {
} }
// requires cc.mu be held. // requires cc.mu be held.
func (cc *ClientConn) encodeTrailers(req *http.Request) []byte { func (cc *ClientConn) encodeTrailers(req *http.Request) ([]byte, error) {
cc.hbuf.Reset() cc.hbuf.Reset()
hlSize := uint64(0)
for k, vv := range req.Trailer { for k, vv := range req.Trailer {
// Transfer-Encoding, etc.. have already been filter at the for _, v := range vv {
hf := hpack.HeaderField{Name: k, Value: v}
hlSize += uint64(hf.Size())
}
}
if hlSize > cc.peerMaxHeaderListSize {
return nil, errRequestHeaderListSize
}
for k, vv := range req.Trailer {
// Transfer-Encoding, etc.. have already been filtered at the
// start of RoundTrip // start of RoundTrip
lowKey := strings.ToLower(k) lowKey := strings.ToLower(k)
for _, v := range vv { for _, v := range vv {
cc.writeHeader(lowKey, v) cc.writeHeader(lowKey, v)
} }
} }
return cc.hbuf.Bytes() return cc.hbuf.Bytes(), nil
} }
func (cc *ClientConn) writeHeader(name, value string) { func (cc *ClientConn) writeHeader(name, value string) {
@ -1246,7 +1366,9 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
cc.idleTimer.Reset(cc.idleTimeout) cc.idleTimer.Reset(cc.idleTimeout)
} }
close(cs.done) close(cs.done)
cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl // Wake up checkResetOrDone via clientStream.awaitFlowControl and
// wake up RoundTrip if there is a pending request.
cc.cond.Broadcast()
} }
return cs return cs
} }
@ -1254,17 +1376,12 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop. // clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop.
type clientConnReadLoop struct { type clientConnReadLoop struct {
cc *ClientConn cc *ClientConn
activeRes map[uint32]*clientStream // keyed by streamID
closeWhenIdle bool closeWhenIdle bool
} }
// readLoop runs in its own goroutine and reads and dispatches frames. // readLoop runs in its own goroutine and reads and dispatches frames.
func (cc *ClientConn) readLoop() { func (cc *ClientConn) readLoop() {
rl := &clientConnReadLoop{ rl := &clientConnReadLoop{cc: cc}
cc: cc,
activeRes: make(map[uint32]*clientStream),
}
defer rl.cleanup() defer rl.cleanup()
cc.readerErr = rl.run() cc.readerErr = rl.run()
if ce, ok := cc.readerErr.(ConnectionError); ok { if ce, ok := cc.readerErr.(ConnectionError); ok {
@ -1319,10 +1436,8 @@ func (rl *clientConnReadLoop) cleanup() {
} else if err == io.EOF { } else if err == io.EOF {
err = io.ErrUnexpectedEOF err = io.ErrUnexpectedEOF
} }
for _, cs := range rl.activeRes {
cs.bufPipe.CloseWithError(err)
}
for _, cs := range cc.streams { for _, cs := range cc.streams {
cs.bufPipe.CloseWithError(err) // no-op if already closed
select { select {
case cs.resc <- resAndError{err: err}: case cs.resc <- resAndError{err: err}:
default: default:
@ -1345,8 +1460,9 @@ func (rl *clientConnReadLoop) run() error {
cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err)
} }
if se, ok := err.(StreamError); ok { if se, ok := err.(StreamError); ok {
if cs := cc.streamByID(se.StreamID, true /*ended; remove it*/); cs != nil { if cs := cc.streamByID(se.StreamID, false); cs != nil {
cs.cc.writeStreamReset(cs.ID, se.Code, err) cs.cc.writeStreamReset(cs.ID, se.Code, err)
cs.cc.forgetStreamID(cs.ID)
if se.Cause == nil { if se.Cause == nil {
se.Cause = cc.fr.errDetail se.Cause = cc.fr.errDetail
} }
@ -1399,7 +1515,7 @@ func (rl *clientConnReadLoop) run() error {
} }
return err return err
} }
if rl.closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 { if rl.closeWhenIdle && gotReply && maybeIdle {
cc.closeIfIdle() cc.closeIfIdle()
} }
} }
@ -1407,13 +1523,31 @@ func (rl *clientConnReadLoop) run() error {
func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error { func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
cc := rl.cc cc := rl.cc
cs := cc.streamByID(f.StreamID, f.StreamEnded()) cs := cc.streamByID(f.StreamID, false)
if cs == nil { if cs == nil {
// We'd get here if we canceled a request while the // We'd get here if we canceled a request while the
// server had its response still in flight. So if this // server had its response still in flight. So if this
// was just something we canceled, ignore it. // was just something we canceled, ignore it.
return nil return nil
} }
if f.StreamEnded() {
// Issue 20521: If the stream has ended, streamByID() causes
// clientStream.done to be closed, which causes the request's bodyWriter
// to be closed with an errStreamClosed, which may be received by
// clientConn.RoundTrip before the result of processing these headers.
// Deferring stream closure allows the header processing to occur first.
// clientConn.RoundTrip may still receive the bodyWriter error first, but
// the fix for issue 16102 prioritises any response.
//
// Issue 22413: If there is no request body, we should close the
// stream before writing to cs.resc so that the stream is closed
// immediately once RoundTrip returns.
if cs.req.Body != nil {
defer cc.forgetStreamID(f.StreamID)
} else {
cc.forgetStreamID(f.StreamID)
}
}
if !cs.firstByte { if !cs.firstByte {
if cs.trace != nil { if cs.trace != nil {
// TODO(bradfitz): move first response byte earlier, // TODO(bradfitz): move first response byte earlier,
@ -1437,6 +1571,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
} }
// Any other error type is a stream error. // Any other error type is a stream error.
cs.cc.writeStreamReset(f.StreamID, ErrCodeProtocol, err) cs.cc.writeStreamReset(f.StreamID, ErrCodeProtocol, err)
cc.forgetStreamID(cs.ID)
cs.resc <- resAndError{err: err} cs.resc <- resAndError{err: err}
return nil // return nil from process* funcs to keep conn alive return nil // return nil from process* funcs to keep conn alive
} }
@ -1444,9 +1579,6 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
// (nil, nil) special case. See handleResponse docs. // (nil, nil) special case. See handleResponse docs.
return nil return nil
} }
if res.Body != noBody {
rl.activeRes[cs.ID] = cs
}
cs.resTrailer = &res.Trailer cs.resTrailer = &res.Trailer
cs.resc <- resAndError{res: res} cs.resc <- resAndError{res: res}
return nil return nil
@ -1466,11 +1598,11 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
status := f.PseudoValue("status") status := f.PseudoValue("status")
if status == "" { if status == "" {
return nil, errors.New("missing status pseudo header") return nil, errors.New("malformed response from server: missing status pseudo header")
} }
statusCode, err := strconv.Atoi(status) statusCode, err := strconv.Atoi(status)
if err != nil { if err != nil {
return nil, errors.New("malformed non-numeric status pseudo header") return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
} }
if statusCode == 100 { if statusCode == 100 {
@ -1668,6 +1800,7 @@ func (b transportResponseBody) Close() error {
} }
cs.bufPipe.BreakWithError(errClosedResponseBody) cs.bufPipe.BreakWithError(errClosedResponseBody)
cc.forgetStreamID(cs.ID)
return nil return nil
} }
@ -1702,7 +1835,23 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
} }
return nil return nil
} }
if !cs.firstByte {
cc.logf("protocol error: received DATA before a HEADERS frame")
rl.endStreamError(cs, StreamError{
StreamID: f.StreamID,
Code: ErrCodeProtocol,
})
return nil
}
if f.Length > 0 { if f.Length > 0 {
if cs.req.Method == "HEAD" && len(data) > 0 {
cc.logf("protocol error: received DATA on a HEAD request")
rl.endStreamError(cs, StreamError{
StreamID: f.StreamID,
Code: ErrCodeProtocol,
})
return nil
}
// Check connection-level flow control. // Check connection-level flow control.
cc.mu.Lock() cc.mu.Lock()
if cs.inflow.available() >= int32(f.Length) { if cs.inflow.available() >= int32(f.Length) {
@ -1713,16 +1862,27 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
} }
// Return any padded flow control now, since we won't // Return any padded flow control now, since we won't
// refund it later on body reads. // refund it later on body reads.
if pad := int32(f.Length) - int32(len(data)); pad > 0 { var refund int
cs.inflow.add(pad) if pad := int(f.Length) - len(data); pad > 0 {
cc.inflow.add(pad) refund += pad
}
// Return len(data) now if the stream is already closed,
// since data will never be read.
didReset := cs.didReset
if didReset {
refund += len(data)
}
if refund > 0 {
cc.inflow.add(int32(refund))
cc.wmu.Lock() cc.wmu.Lock()
cc.fr.WriteWindowUpdate(0, uint32(pad)) cc.fr.WriteWindowUpdate(0, uint32(refund))
cc.fr.WriteWindowUpdate(cs.ID, uint32(pad)) if !didReset {
cs.inflow.add(int32(refund))
cc.fr.WriteWindowUpdate(cs.ID, uint32(refund))
}
cc.bw.Flush() cc.bw.Flush()
cc.wmu.Unlock() cc.wmu.Unlock()
} }
didReset := cs.didReset
cc.mu.Unlock() cc.mu.Unlock()
if len(data) > 0 && !didReset { if len(data) > 0 && !didReset {
@ -1753,11 +1913,10 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
err = io.EOF err = io.EOF
code = cs.copyTrailers code = cs.copyTrailers
} }
cs.bufPipe.closeWithErrorAndCode(err, code)
delete(rl.activeRes, cs.ID)
if isConnectionCloseRequest(cs.req) { if isConnectionCloseRequest(cs.req) {
rl.closeWhenIdle = true rl.closeWhenIdle = true
} }
cs.bufPipe.closeWithErrorAndCode(err, code)
select { select {
case cs.resc <- resAndError{err: err}: case cs.resc <- resAndError{err: err}:
@ -1805,6 +1964,8 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
cc.maxFrameSize = s.Val cc.maxFrameSize = s.Val
case SettingMaxConcurrentStreams: case SettingMaxConcurrentStreams:
cc.maxConcurrentStreams = s.Val cc.maxConcurrentStreams = s.Val
case SettingMaxHeaderListSize:
cc.peerMaxHeaderListSize = uint64(s.Val)
case SettingInitialWindowSize: case SettingInitialWindowSize:
// Values above the maximum flow-control // Values above the maximum flow-control
// window size of 2^31-1 MUST be treated as a // window size of 2^31-1 MUST be treated as a
@ -1882,7 +2043,6 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
cs.bufPipe.CloseWithError(err) cs.bufPipe.CloseWithError(err)
cs.cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl cs.cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl
} }
delete(rl.activeRes, cs.ID)
return nil return nil
} }
@ -1971,6 +2131,7 @@ func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error)
var ( var (
errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit") errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
errRequestHeaderListSize = errors.New("http2: request header list larger than peer's advertised limit")
errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers") errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers")
) )

View File

@ -10,7 +10,6 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"time"
"golang.org/x/net/http2/hpack" "golang.org/x/net/http2/hpack"
"golang.org/x/net/lex/httplex" "golang.org/x/net/lex/httplex"
@ -90,11 +89,7 @@ type writeGoAway struct {
func (p *writeGoAway) writeFrame(ctx writeContext) error { func (p *writeGoAway) writeFrame(ctx writeContext) error {
err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil) err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
if p.code != 0 { ctx.Flush() // ignore error: we're hanging up on them anyway
ctx.Flush() // ignore error: we're hanging up on them anyway
time.Sleep(50 * time.Millisecond)
ctx.CloseConn()
}
return err return err
} }

126
vendor/golang.org/x/net/idna/idna.go generated vendored
View File

@ -21,6 +21,7 @@ import (
"unicode/utf8" "unicode/utf8"
"golang.org/x/text/secure/bidirule" "golang.org/x/text/secure/bidirule"
"golang.org/x/text/unicode/bidi"
"golang.org/x/text/unicode/norm" "golang.org/x/text/unicode/norm"
) )
@ -67,6 +68,15 @@ func VerifyDNSLength(verify bool) Option {
return func(o *options) { o.verifyDNSLength = verify } return func(o *options) { o.verifyDNSLength = verify }
} }
// RemoveLeadingDots removes leading label separators. Leading runes that map to
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
//
// This is the behavior suggested by the UTS #46 and is adopted by some
// browsers.
func RemoveLeadingDots(remove bool) Option {
return func(o *options) { o.removeLeadingDots = remove }
}
// ValidateLabels sets whether to check the mandatory label validation criteria // ValidateLabels sets whether to check the mandatory label validation criteria
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use // as defined in Section 5.4 of RFC 5891. This includes testing for correct use
// of hyphens ('-'), normalization, validity of runes, and the context rules. // of hyphens ('-'), normalization, validity of runes, and the context rules.
@ -83,7 +93,7 @@ func ValidateLabels(enable bool) Option {
} }
} }
// StrictDomainName limits the set of permissable ASCII characters to those // StrictDomainName limits the set of permissible ASCII characters to those
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the // allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
// hyphen). This is set by default for MapForLookup and ValidateForRegistration. // hyphen). This is set by default for MapForLookup and ValidateForRegistration.
// //
@ -137,10 +147,11 @@ func MapForLookup() Option {
} }
type options struct { type options struct {
transitional bool transitional bool
useSTD3Rules bool useSTD3Rules bool
validateLabels bool validateLabels bool
verifyDNSLength bool verifyDNSLength bool
removeLeadingDots bool
trie *idnaTrie trie *idnaTrie
@ -149,14 +160,14 @@ type options struct {
// mapping implements a validation and mapping step as defined in RFC 5895 // mapping implements a validation and mapping step as defined in RFC 5895
// or UTS 46, tailored to, for example, domain registration or lookup. // or UTS 46, tailored to, for example, domain registration or lookup.
mapping func(p *Profile, s string) (string, error) mapping func(p *Profile, s string) (mapped string, isBidi bool, err error)
// bidirule, if specified, checks whether s conforms to the Bidi Rule // bidirule, if specified, checks whether s conforms to the Bidi Rule
// defined in RFC 5893. // defined in RFC 5893.
bidirule func(s string) bool bidirule func(s string) bool
} }
// A Profile defines the configuration of a IDNA mapper. // A Profile defines the configuration of an IDNA mapper.
type Profile struct { type Profile struct {
options options
} }
@ -289,12 +300,16 @@ func (e runeError) Error() string {
// see http://www.unicode.org/reports/tr46. // see http://www.unicode.org/reports/tr46.
func (p *Profile) process(s string, toASCII bool) (string, error) { func (p *Profile) process(s string, toASCII bool) (string, error) {
var err error var err error
var isBidi bool
if p.mapping != nil { if p.mapping != nil {
s, err = p.mapping(p, s) s, isBidi, err = p.mapping(p, s)
} }
// Remove leading empty labels. // Remove leading empty labels.
for ; len(s) > 0 && s[0] == '.'; s = s[1:] { if p.removeLeadingDots {
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
}
} }
// TODO: allow for a quick check of the tables data.
// It seems like we should only create this error on ToASCII, but the // It seems like we should only create this error on ToASCII, but the
// UTS 46 conformance tests suggests we should always check this. // UTS 46 conformance tests suggests we should always check this.
if err == nil && p.verifyDNSLength && s == "" { if err == nil && p.verifyDNSLength && s == "" {
@ -320,6 +335,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
// Spec says keep the old label. // Spec says keep the old label.
continue continue
} }
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
labels.set(u) labels.set(u)
if err == nil && p.validateLabels { if err == nil && p.validateLabels {
err = p.fromPuny(p, u) err = p.fromPuny(p, u)
@ -334,6 +350,14 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
err = p.validateLabel(label) err = p.validateLabel(label)
} }
} }
if isBidi && p.bidirule != nil && err == nil {
for labels.reset(); !labels.done(); labels.next() {
if !p.bidirule(labels.label()) {
err = &labelError{s, "B"}
break
}
}
}
if toASCII { if toASCII {
for labels.reset(); !labels.done(); labels.next() { for labels.reset(); !labels.done(); labels.next() {
label := labels.label() label := labels.label()
@ -365,41 +389,77 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
return s, err return s, err
} }
func normalize(p *Profile, s string) (string, error) { func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
return norm.NFC.String(s), nil // TODO: consider first doing a quick check to see if any of these checks
// need to be done. This will make it slower in the general case, but
// faster in the common case.
mapped = norm.NFC.String(s)
isBidi = bidirule.DirectionString(mapped) == bidi.RightToLeft
return mapped, isBidi, nil
} }
func validateRegistration(p *Profile, s string) (string, error) { func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) {
// TODO: filter need for normalization in loop below.
if !norm.NFC.IsNormalString(s) { if !norm.NFC.IsNormalString(s) {
return s, &labelError{s, "V1"} return s, false, &labelError{s, "V1"}
} }
var err error
for i := 0; i < len(s); { for i := 0; i < len(s); {
v, sz := trie.lookupString(s[i:]) v, sz := trie.lookupString(s[i:])
i += sz if sz == 0 {
return s, bidi, runeError(utf8.RuneError)
}
bidi = bidi || info(v).isBidi(s[i:])
// Copy bytes not copied so far. // Copy bytes not copied so far.
switch p.simplify(info(v).category()) { switch p.simplify(info(v).category()) {
// TODO: handle the NV8 defined in the Unicode idna data set to allow // TODO: handle the NV8 defined in the Unicode idna data set to allow
// for strict conformance to IDNA2008. // for strict conformance to IDNA2008.
case valid, deviation: case valid, deviation:
case disallowed, mapped, unknown, ignored: case disallowed, mapped, unknown, ignored:
if err == nil { r, _ := utf8.DecodeRuneInString(s[i:])
r, _ := utf8.DecodeRuneInString(s[i:]) return s, bidi, runeError(r)
err = runeError(r)
}
} }
i += sz
} }
return s, err return s, bidi, nil
} }
func validateAndMap(p *Profile, s string) (string, error) { func (c info) isBidi(s string) bool {
if !c.isMapped() {
return c&attributesMask == rtl
}
// TODO: also store bidi info for mapped data. This is possible, but a bit
// cumbersome and not for the common case.
p, _ := bidi.LookupString(s)
switch p.Class() {
case bidi.R, bidi.AL, bidi.AN:
return true
}
return false
}
func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
var ( var (
err error b []byte
b []byte k int
k int
) )
// combinedInfoBits contains the or-ed bits of all runes. We use this
// to derive the mayNeedNorm bit later. This may trigger normalization
// overeagerly, but it will not do so in the common case. The end result
// is another 10% saving on BenchmarkProfile for the common case.
var combinedInfoBits info
for i := 0; i < len(s); { for i := 0; i < len(s); {
v, sz := trie.lookupString(s[i:]) v, sz := trie.lookupString(s[i:])
if sz == 0 {
b = append(b, s[k:i]...)
b = append(b, "\ufffd"...)
k = len(s)
if err == nil {
err = runeError(utf8.RuneError)
}
break
}
combinedInfoBits |= info(v)
bidi = bidi || info(v).isBidi(s[i:])
start := i start := i
i += sz i += sz
// Copy bytes not copied so far. // Copy bytes not copied so far.
@ -408,7 +468,7 @@ func validateAndMap(p *Profile, s string) (string, error) {
continue continue
case disallowed: case disallowed:
if err == nil { if err == nil {
r, _ := utf8.DecodeRuneInString(s[i:]) r, _ := utf8.DecodeRuneInString(s[start:])
err = runeError(r) err = runeError(r)
} }
continue continue
@ -426,7 +486,9 @@ func validateAndMap(p *Profile, s string) (string, error) {
} }
if k == 0 { if k == 0 {
// No changes so far. // No changes so far.
s = norm.NFC.String(s) if combinedInfoBits&mayNeedNorm != 0 {
s = norm.NFC.String(s)
}
} else { } else {
b = append(b, s[k:]...) b = append(b, s[k:]...)
if norm.NFC.QuickSpan(b) != len(b) { if norm.NFC.QuickSpan(b) != len(b) {
@ -435,7 +497,7 @@ func validateAndMap(p *Profile, s string) (string, error) {
// TODO: the punycode converters require strings as input. // TODO: the punycode converters require strings as input.
s = string(b) s = string(b)
} }
return s, err return s, bidi, err
} }
// A labelIter allows iterating over domain name labels. // A labelIter allows iterating over domain name labels.
@ -530,8 +592,13 @@ func validateFromPunycode(p *Profile, s string) error {
if !norm.NFC.IsNormalString(s) { if !norm.NFC.IsNormalString(s) {
return &labelError{s, "V1"} return &labelError{s, "V1"}
} }
// TODO: detect whether string may have to be normalized in the following
// loop.
for i := 0; i < len(s); { for i := 0; i < len(s); {
v, sz := trie.lookupString(s[i:]) v, sz := trie.lookupString(s[i:])
if sz == 0 {
return runeError(utf8.RuneError)
}
if c := p.simplify(info(v).category()); c != valid && c != deviation { if c := p.simplify(info(v).category()); c != valid && c != deviation {
return &labelError{s, "V6"} return &labelError{s, "V6"}
} }
@ -604,16 +671,13 @@ var joinStates = [][numJoinTypes]joinState{
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are // validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
// already implicitly satisfied by the overall implementation. // already implicitly satisfied by the overall implementation.
func (p *Profile) validateLabel(s string) error { func (p *Profile) validateLabel(s string) (err error) {
if s == "" { if s == "" {
if p.verifyDNSLength { if p.verifyDNSLength {
return &labelError{s, "A4"} return &labelError{s, "A4"}
} }
return nil return nil
} }
if p.bidirule != nil && !p.bidirule(s) {
return &labelError{s, "B"}
}
if !p.validateLabels { if !p.validateLabels {
return nil return nil
} }

4396
vendor/golang.org/x/net/idna/tables.go generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -26,9 +26,9 @@ package idna
// 15..3 index into xor or mapping table // 15..3 index into xor or mapping table
// } // }
// } else { // } else {
// 15..13 unused // 15..14 unused
// 12 modifier (including virama) // 13 mayNeedNorm
// 11 virama modifier // 12..11 attributes
// 10..8 joining type // 10..8 joining type
// 7..3 category type // 7..3 category type
// } // }
@ -49,15 +49,20 @@ const (
joinShift = 8 joinShift = 8
joinMask = 0x07 joinMask = 0x07
viramaModifier = 0x0800 // Attributes
attributesMask = 0x1800
viramaModifier = 0x1800
modifier = 0x1000 modifier = 0x1000
rtl = 0x0800
mayNeedNorm = 0x2000
) )
// A category corresponds to a category defined in the IDNA mapping table. // A category corresponds to a category defined in the IDNA mapping table.
type category uint16 type category uint16
const ( const (
unknown category = 0 // not defined currently in unicode. unknown category = 0 // not currently defined in unicode.
mapped category = 1 mapped category = 1
disallowedSTD3Mapped category = 2 disallowedSTD3Mapped category = 2
deviation category = 3 deviation category = 3
@ -110,5 +115,5 @@ func (c info) isModifier() bool {
} }
func (c info) isViramaModifier() bool { func (c info) isViramaModifier() bool {
return c&(viramaModifier|catSmallMask) == viramaModifier return c&(attributesMask|catSmallMask) == viramaModifier
} }

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
) )
// A PerHost directs connections to a default Dialer unless the hostname // A PerHost directs connections to a default Dialer unless the host name
// requested matches one of a number of exceptions. // requested matches one of a number of exceptions.
type PerHost struct { type PerHost struct {
def, bypass Dialer def, bypass Dialer
@ -61,7 +61,7 @@ func (p *PerHost) dialerForRequest(host string) Dialer {
return p.bypass return p.bypass
} }
if host == zone[1:] { if host == zone[1:] {
// For a zone "example.com", we match "example.com" // For a zone ".example.com", we match "example.com"
// too. // too.
return p.bypass return p.bypass
} }
@ -76,7 +76,7 @@ func (p *PerHost) dialerForRequest(host string) Dialer {
// AddFromString parses a string that contains comma-separated values // AddFromString parses a string that contains comma-separated values
// specifying hosts that should use the bypass proxy. Each value is either an // specifying hosts that should use the bypass proxy. Each value is either an
// IP address, a CIDR range, a zone (*.example.com) or a hostname // IP address, a CIDR range, a zone (*.example.com) or a host name
// (localhost). A best effort is made to parse the string and errors are // (localhost). A best effort is made to parse the string and errors are
// ignored. // ignored.
func (p *PerHost) AddFromString(s string) { func (p *PerHost) AddFromString(s string) {
@ -131,7 +131,7 @@ func (p *PerHost) AddZone(zone string) {
p.bypassZones = append(p.bypassZones, zone) p.bypassZones = append(p.bypassZones, zone)
} }
// AddHost specifies a hostname that will use the bypass proxy. // AddHost specifies a host name that will use the bypass proxy.
func (p *PerHost) AddHost(host string) { func (p *PerHost) AddHost(host string) {
if strings.HasSuffix(host, ".") { if strings.HasSuffix(host, ".") {
host = host[:len(host)-1] host = host[:len(host)-1]

View File

@ -11,6 +11,7 @@ import (
"net" "net"
"net/url" "net/url"
"os" "os"
"sync"
) )
// A Dialer is a means to establish a connection. // A Dialer is a means to establish a connection.
@ -27,7 +28,7 @@ type Auth struct {
// FromEnvironment returns the dialer specified by the proxy related variables in // FromEnvironment returns the dialer specified by the proxy related variables in
// the environment. // the environment.
func FromEnvironment() Dialer { func FromEnvironment() Dialer {
allProxy := os.Getenv("all_proxy") allProxy := allProxyEnv.Get()
if len(allProxy) == 0 { if len(allProxy) == 0 {
return Direct return Direct
} }
@ -41,7 +42,7 @@ func FromEnvironment() Dialer {
return Direct return Direct
} }
noProxy := os.Getenv("no_proxy") noProxy := noProxyEnv.Get()
if len(noProxy) == 0 { if len(noProxy) == 0 {
return proxy return proxy
} }
@ -92,3 +93,42 @@ func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
return nil, errors.New("proxy: unknown scheme: " + u.Scheme) return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
} }
var (
allProxyEnv = &envOnce{
names: []string{"ALL_PROXY", "all_proxy"},
}
noProxyEnv = &envOnce{
names: []string{"NO_PROXY", "no_proxy"},
}
)
// envOnce looks up an environment variable (optionally by multiple
// names) once. It mitigates expensive lookups on some platforms
// (e.g. Windows).
// (Borrowed from net/http/transport.go)
type envOnce struct {
names []string
once sync.Once
val string
}
func (e *envOnce) Get() string {
e.once.Do(e.init)
return e.val
}
func (e *envOnce) init() {
for _, n := range e.names {
e.val = os.Getenv(n)
if e.val != "" {
return
}
}
}
// reset is used by tests
func (e *envOnce) reset() {
e.once = sync.Once{}
e.val = ""
}

View File

@ -12,7 +12,7 @@ import (
) )
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
// with an optional username and password. See RFC 1928. // with an optional username and password. See RFC 1928 and RFC 1929.
func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) { func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) {
s := &socks5{ s := &socks5{
network: network, network: network,
@ -60,7 +60,7 @@ var socks5Errors = []string{
"address type not supported", "address type not supported",
} }
// Dial connects to the address addr on the network net via the SOCKS5 proxy. // Dial connects to the address addr on the given network via the SOCKS5 proxy.
func (s *socks5) Dial(network, addr string) (net.Conn, error) { func (s *socks5) Dial(network, addr string) (net.Conn, error) {
switch network { switch network {
case "tcp", "tcp6", "tcp4": case "tcp", "tcp6", "tcp4":
@ -120,6 +120,7 @@ func (s *socks5) connect(conn net.Conn, target string) error {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
} }
// See RFC 1929
if buf[1] == socks5AuthPassword { if buf[1] == socks5AuthPassword {
buf = buf[:0] buf = buf[:0]
buf = append(buf, 1 /* password protocol version */) buf = append(buf, 1 /* password protocol version */)
@ -154,7 +155,7 @@ func (s *socks5) connect(conn net.Conn, target string) error {
buf = append(buf, ip...) buf = append(buf, ip...)
} else { } else {
if len(host) > 255 { if len(host) > 255 {
return errors.New("proxy: destination hostname too long: " + host) return errors.New("proxy: destination host name too long: " + host)
} }
buf = append(buf, socks5Domain) buf = append(buf, socks5Domain)
buf = append(buf, byte(len(host))) buf = append(buf, byte(len(host)))

View File

@ -39,9 +39,9 @@ var buckets = []bucket{
} }
// RenderEvents renders the HTML page typically served at /debug/events. // RenderEvents renders the HTML page typically served at /debug/events.
// It does not do any auth checking; see AuthRequest for the default auth check // It does not do any auth checking. The request may be nil.
// used by the handler registered on http.DefaultServeMux. //
// req may be nil. // Most users will use the Events handler.
func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) { func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) {
now := time.Now() now := time.Now()
data := &struct { data := &struct {

View File

@ -110,30 +110,46 @@ var AuthRequest = func(req *http.Request) (any, sensitive bool) {
} }
func init() { func init() {
http.HandleFunc("/debug/requests", func(w http.ResponseWriter, req *http.Request) { // TODO(jbd): Serve Traces from /debug/traces in the future?
any, sensitive := AuthRequest(req) // There is no requirement for a request to be present to have traces.
if !any { http.HandleFunc("/debug/requests", Traces)
http.Error(w, "not allowed", http.StatusUnauthorized) http.HandleFunc("/debug/events", Events)
return }
}
w.Header().Set("Content-Type", "text/html; charset=utf-8") // Traces responds with traces from the program.
Render(w, req, sensitive) // The package initialization registers it in http.DefaultServeMux
}) // at /debug/requests.
http.HandleFunc("/debug/events", func(w http.ResponseWriter, req *http.Request) { //
any, sensitive := AuthRequest(req) // It performs authorization by running AuthRequest.
if !any { func Traces(w http.ResponseWriter, req *http.Request) {
http.Error(w, "not allowed", http.StatusUnauthorized) any, sensitive := AuthRequest(req)
return if !any {
} http.Error(w, "not allowed", http.StatusUnauthorized)
w.Header().Set("Content-Type", "text/html; charset=utf-8") return
RenderEvents(w, req, sensitive) }
}) w.Header().Set("Content-Type", "text/html; charset=utf-8")
Render(w, req, sensitive)
}
// Events responds with a page of events collected by EventLogs.
// The package initialization registers it in http.DefaultServeMux
// at /debug/events.
//
// It performs authorization by running AuthRequest.
func Events(w http.ResponseWriter, req *http.Request) {
any, sensitive := AuthRequest(req)
if !any {
http.Error(w, "not allowed", http.StatusUnauthorized)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
RenderEvents(w, req, sensitive)
} }
// Render renders the HTML page typically served at /debug/requests. // Render renders the HTML page typically served at /debug/requests.
// It does not do any auth checking; see AuthRequest for the default auth check // It does not do any auth checking. The request may be nil.
// used by the handler registered on http.DefaultServeMux. //
// req may be nil. // Most users will use the Traces handler.
func Render(w io.Writer, req *http.Request, sensitive bool) { func Render(w io.Writer, req *http.Request, sensitive bool) {
data := &struct { data := &struct {
Families []string Families []string