123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- // 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 implements the HTTP/2 protocol.
- //
- // This package is low-level and intended to be used directly by very
- // few people. Most users will use it indirectly through the automatic
- // use by the net/http package (from Go 1.6 and later).
- // For use in earlier Go versions see ConfigureServer. (Transport support
- // requires Go 1.6 or later)
- //
- // See https://http2.github.io/ for more information on HTTP/2.
- //
- // See https://http2.golang.org/ for a test server running this code.
- package http2
- import (
- "bufio"
- "crypto/tls"
- "errors"
- "fmt"
- "io"
- "net/http"
- "os"
- "strconv"
- "strings"
- "sync"
- )
- var (
- VerboseLogs bool
- logFrameWrites bool
- logFrameReads bool
- )
- func init() {
- e := os.Getenv("GODEBUG")
- if strings.Contains(e, "http2debug=1") {
- VerboseLogs = true
- }
- if strings.Contains(e, "http2debug=2") {
- VerboseLogs = true
- logFrameWrites = true
- logFrameReads = true
- }
- }
- const (
- // ClientPreface is the string that must be sent by new
- // connections from clients.
- ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
- // SETTINGS_MAX_FRAME_SIZE default
- // http://http2.github.io/http2-spec/#rfc.section.6.5.2
- initialMaxFrameSize = 16384
- // NextProtoTLS is the NPN/ALPN protocol negotiated during
- // HTTP/2's TLS setup.
- NextProtoTLS = "h2"
- // http://http2.github.io/http2-spec/#SettingValues
- initialHeaderTableSize = 4096
- initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
- defaultMaxReadFrameSize = 1 << 20
- )
- var (
- clientPreface = []byte(ClientPreface)
- )
- type streamState int
- const (
- stateIdle streamState = iota
- stateOpen
- stateHalfClosedLocal
- stateHalfClosedRemote
- stateResvLocal
- stateResvRemote
- stateClosed
- )
- var stateName = [...]string{
- stateIdle: "Idle",
- stateOpen: "Open",
- stateHalfClosedLocal: "HalfClosedLocal",
- stateHalfClosedRemote: "HalfClosedRemote",
- stateResvLocal: "ResvLocal",
- stateResvRemote: "ResvRemote",
- stateClosed: "Closed",
- }
- func (st streamState) String() string {
- return stateName[st]
- }
- // Setting is a setting parameter: which setting it is, and its value.
- type Setting struct {
- // ID is which setting is being set.
- // See http://http2.github.io/http2-spec/#SettingValues
- ID SettingID
- // Val is the value.
- Val uint32
- }
- func (s Setting) String() string {
- return fmt.Sprintf("[%v = %d]", s.ID, s.Val)
- }
- // Valid reports whether the setting is valid.
- func (s Setting) Valid() error {
- // Limits and error codes from 6.5.2 Defined SETTINGS Parameters
- switch s.ID {
- case SettingEnablePush:
- if s.Val != 1 && s.Val != 0 {
- return ConnectionError(ErrCodeProtocol)
- }
- case SettingInitialWindowSize:
- if s.Val > 1<<31-1 {
- return ConnectionError(ErrCodeFlowControl)
- }
- case SettingMaxFrameSize:
- if s.Val < 16384 || s.Val > 1<<24-1 {
- return ConnectionError(ErrCodeProtocol)
- }
- }
- return nil
- }
- // A SettingID is an HTTP/2 setting as defined in
- // http://http2.github.io/http2-spec/#iana-settings
- type SettingID uint16
- const (
- SettingHeaderTableSize SettingID = 0x1
- SettingEnablePush SettingID = 0x2
- SettingMaxConcurrentStreams SettingID = 0x3
- SettingInitialWindowSize SettingID = 0x4
- SettingMaxFrameSize SettingID = 0x5
- SettingMaxHeaderListSize SettingID = 0x6
- )
- var settingName = map[SettingID]string{
- SettingHeaderTableSize: "HEADER_TABLE_SIZE",
- SettingEnablePush: "ENABLE_PUSH",
- SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS",
- SettingInitialWindowSize: "INITIAL_WINDOW_SIZE",
- SettingMaxFrameSize: "MAX_FRAME_SIZE",
- SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE",
- }
- func (s SettingID) String() string {
- if v, ok := settingName[s]; ok {
- return v
- }
- return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
- }
- var (
- errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
- errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
- )
- // validHeaderFieldName reports whether v is a valid header field name (key).
- // RFC 7230 says:
- // header-field = field-name ":" OWS field-value OWS
- // field-name = token
- // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
- // "^" / "_" / "
- // Further, http2 says:
- // "Just as in HTTP/1.x, header field names are strings of ASCII
- // characters that are compared in a case-insensitive
- // fashion. However, header field names MUST be converted to
- // lowercase prior to their encoding in HTTP/2. "
- func validHeaderFieldName(v string) bool {
- if len(v) == 0 {
- return false
- }
- for _, r := range v {
- if int(r) >= len(isTokenTable) || ('A' <= r && r <= 'Z') {
- return false
- }
- if !isTokenTable[byte(r)] {
- return false
- }
- }
- return true
- }
- // validHeaderFieldValue reports whether v is a valid header field value.
- //
- // RFC 7230 says:
- // field-value = *( field-content / obs-fold )
- // obj-fold = N/A to http2, and deprecated
- // field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
- // field-vchar = VCHAR / obs-text
- // obs-text = %x80-FF
- // VCHAR = "any visible [USASCII] character"
- //
- // http2 further says: "Similarly, HTTP/2 allows header field values
- // that are not valid. While most of the values that can be encoded
- // will not alter header field parsing, carriage return (CR, ASCII
- // 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII
- // 0x0) might be exploited by an attacker if they are translated
- // verbatim. Any request or response that contains a character not
- // permitted in a header field value MUST be treated as malformed
- // (Section 8.1.2.6). Valid characters are defined by the
- // field-content ABNF rule in Section 3.2 of [RFC7230]."
- //
- // This function does not (yet?) properly handle the rejection of
- // strings that begin or end with SP or HTAB.
- func validHeaderFieldValue(v string) bool {
- for i := 0; i < len(v); i++ {
- if b := v[i]; b < ' ' && b != '\t' || b == 0x7f {
- return false
- }
- }
- return true
- }
- var httpCodeStringCommon = map[int]string{} // n -> strconv.Itoa(n)
- func init() {
- for i := 100; i <= 999; i++ {
- if v := http.StatusText(i); v != "" {
- httpCodeStringCommon[i] = strconv.Itoa(i)
- }
- }
- }
- func httpCodeString(code int) string {
- if s, ok := httpCodeStringCommon[code]; ok {
- return s
- }
- return strconv.Itoa(code)
- }
- // from pkg io
- type stringWriter interface {
- WriteString(s string) (n int, err error)
- }
- // A gate lets two goroutines coordinate their activities.
- type gate chan struct{}
- func (g gate) Done() { g <- struct{}{} }
- func (g gate) Wait() { <-g }
- // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed).
- type closeWaiter chan struct{}
- // Init makes a closeWaiter usable.
- // It exists because so a closeWaiter value can be placed inside a
- // larger struct and have the Mutex and Cond's memory in the same
- // allocation.
- func (cw *closeWaiter) Init() {
- *cw = make(chan struct{})
- }
- // Close marks the closeWaiter as closed and unblocks any waiters.
- func (cw closeWaiter) Close() {
- close(cw)
- }
- // Wait waits for the closeWaiter to become closed.
- func (cw closeWaiter) Wait() {
- <-cw
- }
- // bufferedWriter is a buffered writer that writes to w.
- // Its buffered writer is lazily allocated as needed, to minimize
- // idle memory usage with many connections.
- type bufferedWriter struct {
- w io.Writer // immutable
- bw *bufio.Writer // non-nil when data is buffered
- }
- func newBufferedWriter(w io.Writer) *bufferedWriter {
- return &bufferedWriter{w: w}
- }
- var bufWriterPool = sync.Pool{
- New: func() interface{} {
- // TODO: pick something better? this is a bit under
- // (3 x typical 1500 byte MTU) at least.
- return bufio.NewWriterSize(nil, 4<<10)
- },
- }
- func (w *bufferedWriter) Write(p []byte) (n int, err error) {
- if w.bw == nil {
- bw := bufWriterPool.Get().(*bufio.Writer)
- bw.Reset(w.w)
- w.bw = bw
- }
- return w.bw.Write(p)
- }
- func (w *bufferedWriter) Flush() error {
- bw := w.bw
- if bw == nil {
- return nil
- }
- err := bw.Flush()
- bw.Reset(nil)
- bufWriterPool.Put(bw)
- w.bw = nil
- return err
- }
- func mustUint31(v int32) uint32 {
- if v < 0 || v > 2147483647 {
- panic("out of range")
- }
- return uint32(v)
- }
- // bodyAllowedForStatus reports whether a given response status code
- // permits a body. See RFC2616, section 4.4.
- func bodyAllowedForStatus(status int) bool {
- switch {
- case status >= 100 && status <= 199:
- return false
- case status == 204:
- return false
- case status == 304:
- return false
- }
- return true
- }
- type httpError struct {
- msg string
- timeout bool
- }
- func (e *httpError) Error() string { return e.msg }
- func (e *httpError) Timeout() bool { return e.timeout }
- func (e *httpError) Temporary() bool { return true }
- var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true}
- var isTokenTable = [127]bool{
- '!': true,
- '#': true,
- '$': true,
- '%': true,
- '&': true,
- '\'': true,
- '*': true,
- '+': true,
- '-': true,
- '.': true,
- '0': true,
- '1': true,
- '2': true,
- '3': true,
- '4': true,
- '5': true,
- '6': true,
- '7': true,
- '8': true,
- '9': true,
- 'A': true,
- 'B': true,
- 'C': true,
- 'D': true,
- 'E': true,
- 'F': true,
- 'G': true,
- 'H': true,
- 'I': true,
- 'J': true,
- 'K': true,
- 'L': true,
- 'M': true,
- 'N': true,
- 'O': true,
- 'P': true,
- 'Q': true,
- 'R': true,
- 'S': true,
- 'T': true,
- 'U': true,
- 'W': true,
- 'V': true,
- 'X': true,
- 'Y': true,
- 'Z': true,
- '^': true,
- '_': true,
- '`': true,
- 'a': true,
- 'b': true,
- 'c': true,
- 'd': true,
- 'e': true,
- 'f': true,
- 'g': true,
- 'h': true,
- 'i': true,
- 'j': true,
- 'k': true,
- 'l': true,
- 'm': true,
- 'n': true,
- 'o': true,
- 'p': true,
- 'q': true,
- 'r': true,
- 's': true,
- 't': true,
- 'u': true,
- 'v': true,
- 'w': true,
- 'x': true,
- 'y': true,
- 'z': true,
- '|': true,
- '~': true,
- }
- type connectionStater interface {
- ConnectionState() tls.ConnectionState
- }
|