mirror of
https://github.com/golang/net.git
synced 2026-04-01 02:47:08 +09:00
If a server sends a stream error of type "protocol error" to a client, that's the server saying "you're speaking http2 wrong". At that point, regardless of whether we're in the right or not (that is, regardless of whether the Transport is bug-free), clearly there's some confusion and one of the two parties is either wrong or confused. There's no point pushing on and trying to use the connection and potentially exacerbating the confusion (as we saw in golang/go#47635). Instead, make the client "poison" the connection by setting a new "do not reuse" bit on it. Existing streams can finish up but new requests won't pick that connection. Also, make those requests as retryable without the caller getting an error. Given that golang/go#42777 existed, there are HTTP/2 servers in the wild that incorrectly set RST_STREAM PROTOCOL_ERROR codes. But even once those go away, this is still a reasonable fix for preventing a broken connection from being stuck in the connection pool that fails all future requests if a similar bug happens in another HTTP/2 server. Updates golang/go#47635 Change-Id: I3f89ecd1d3710e49f7219ccb846e016eb269515b Reviewed-on: https://go-review.googlesource.com/c/net/+/347033 Trust: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Damien Neil <dneil@google.com>
139 lines
4.4 KiB
Go
139 lines
4.4 KiB
Go
// 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.
|
|
|
|
package http2
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec.
|
|
type ErrCode uint32
|
|
|
|
const (
|
|
ErrCodeNo ErrCode = 0x0
|
|
ErrCodeProtocol ErrCode = 0x1
|
|
ErrCodeInternal ErrCode = 0x2
|
|
ErrCodeFlowControl ErrCode = 0x3
|
|
ErrCodeSettingsTimeout ErrCode = 0x4
|
|
ErrCodeStreamClosed ErrCode = 0x5
|
|
ErrCodeFrameSize ErrCode = 0x6
|
|
ErrCodeRefusedStream ErrCode = 0x7
|
|
ErrCodeCancel ErrCode = 0x8
|
|
ErrCodeCompression ErrCode = 0x9
|
|
ErrCodeConnect ErrCode = 0xa
|
|
ErrCodeEnhanceYourCalm ErrCode = 0xb
|
|
ErrCodeInadequateSecurity ErrCode = 0xc
|
|
ErrCodeHTTP11Required ErrCode = 0xd
|
|
)
|
|
|
|
var errCodeName = map[ErrCode]string{
|
|
ErrCodeNo: "NO_ERROR",
|
|
ErrCodeProtocol: "PROTOCOL_ERROR",
|
|
ErrCodeInternal: "INTERNAL_ERROR",
|
|
ErrCodeFlowControl: "FLOW_CONTROL_ERROR",
|
|
ErrCodeSettingsTimeout: "SETTINGS_TIMEOUT",
|
|
ErrCodeStreamClosed: "STREAM_CLOSED",
|
|
ErrCodeFrameSize: "FRAME_SIZE_ERROR",
|
|
ErrCodeRefusedStream: "REFUSED_STREAM",
|
|
ErrCodeCancel: "CANCEL",
|
|
ErrCodeCompression: "COMPRESSION_ERROR",
|
|
ErrCodeConnect: "CONNECT_ERROR",
|
|
ErrCodeEnhanceYourCalm: "ENHANCE_YOUR_CALM",
|
|
ErrCodeInadequateSecurity: "INADEQUATE_SECURITY",
|
|
ErrCodeHTTP11Required: "HTTP_1_1_REQUIRED",
|
|
}
|
|
|
|
func (e ErrCode) String() string {
|
|
if s, ok := errCodeName[e]; ok {
|
|
return s
|
|
}
|
|
return fmt.Sprintf("unknown error code 0x%x", uint32(e))
|
|
}
|
|
|
|
// ConnectionError is an error that results in the termination of the
|
|
// entire connection.
|
|
type ConnectionError ErrCode
|
|
|
|
func (e ConnectionError) Error() string { return fmt.Sprintf("connection error: %s", ErrCode(e)) }
|
|
|
|
// StreamError is an error that only affects one stream within an
|
|
// HTTP/2 connection.
|
|
type StreamError struct {
|
|
StreamID uint32
|
|
Code ErrCode
|
|
Cause error // optional additional detail
|
|
}
|
|
|
|
// errFromPeer is a sentinel error value for StreamError.Cause to
|
|
// indicate that the StreamError was sent from the peer over the wire
|
|
// and wasn't locally generated in the Transport.
|
|
var errFromPeer = errors.New("received from peer")
|
|
|
|
func streamError(id uint32, code ErrCode) StreamError {
|
|
return StreamError{StreamID: id, Code: code}
|
|
}
|
|
|
|
func (e StreamError) Error() string {
|
|
if e.Cause != nil {
|
|
return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause)
|
|
}
|
|
return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code)
|
|
}
|
|
|
|
// 6.9.1 The Flow Control Window
|
|
// "If a sender receives a WINDOW_UPDATE that causes a flow control
|
|
// window to exceed this maximum it MUST terminate either the stream
|
|
// or the connection, as appropriate. For streams, [...]; for the
|
|
// connection, a GOAWAY frame with a FLOW_CONTROL_ERROR code."
|
|
type goAwayFlowError struct{}
|
|
|
|
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
|
|
|
|
// 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
|
|
// 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 {
|
|
Code ErrCode // the ConnectionError error code
|
|
Reason string // additional reason
|
|
}
|
|
|
|
func (e connError) Error() string {
|
|
return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason)
|
|
}
|
|
|
|
type pseudoHeaderError string
|
|
|
|
func (e pseudoHeaderError) Error() string {
|
|
return fmt.Sprintf("invalid pseudo-header %q", string(e))
|
|
}
|
|
|
|
type duplicatePseudoHeaderError string
|
|
|
|
func (e duplicatePseudoHeaderError) Error() string {
|
|
return fmt.Sprintf("duplicate pseudo-header %q", string(e))
|
|
}
|
|
|
|
type headerFieldNameError string
|
|
|
|
func (e headerFieldNameError) Error() string {
|
|
return fmt.Sprintf("invalid header field name %q", string(e))
|
|
}
|
|
|
|
type headerFieldValueError string
|
|
|
|
func (e headerFieldValueError) Error() string {
|
|
return fmt.Sprintf("invalid header field value %q", string(e))
|
|
}
|
|
|
|
var (
|
|
errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers")
|
|
errPseudoAfterRegular = errors.New("pseudo header field after regular")
|
|
)
|