464 lines
11 KiB
Go
464 lines
11 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 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"
|
||
|
"sort"
|
||
|
"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
|
||
|
}
|
||
|
|
||
|
var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }}
|
||
|
|
||
|
type sorter struct {
|
||
|
v []string // owned by sorter
|
||
|
}
|
||
|
|
||
|
func (s *sorter) Len() int { return len(s.v) }
|
||
|
func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] }
|
||
|
func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] }
|
||
|
|
||
|
// Keys returns the sorted keys of h.
|
||
|
//
|
||
|
// The returned slice is only valid until s used again or returned to
|
||
|
// its pool.
|
||
|
func (s *sorter) Keys(h http.Header) []string {
|
||
|
keys := s.v[:0]
|
||
|
for k := range h {
|
||
|
keys = append(keys, k)
|
||
|
}
|
||
|
s.v = keys
|
||
|
sort.Sort(s)
|
||
|
return keys
|
||
|
}
|
||
|
|
||
|
func (s *sorter) SortStrings(ss []string) {
|
||
|
// Our sorter works on s.v, which sorter owners, so
|
||
|
// stash it away while we sort the user's buffer.
|
||
|
save := s.v
|
||
|
s.v = ss
|
||
|
sort.Sort(s)
|
||
|
s.v = save
|
||
|
}
|