mirror of
https://github.com/containers/podman.git
synced 2025-12-19 07:09:39 +08:00
They require go 1.24 and now that we bumped it we can update them. Seem easier to do it her eonce than having to wait for renovate to update each individually. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
411 lines
10 KiB
Go
411 lines
10 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.
|
|
package http2 // import "golang.org/x/net/http2"
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/net/http/httpguts"
|
|
)
|
|
|
|
var (
|
|
VerboseLogs bool
|
|
logFrameWrites bool
|
|
logFrameReads bool
|
|
inTests bool
|
|
|
|
// Enabling extended CONNECT by causes browsers to attempt to use
|
|
// WebSockets-over-HTTP/2. This results in problems when the server's websocket
|
|
// package doesn't support extended CONNECT.
|
|
//
|
|
// Disable extended CONNECT by default for now.
|
|
//
|
|
// Issue #71128.
|
|
disableExtendedConnectProtocol = true
|
|
)
|
|
|
|
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
|
|
}
|
|
if strings.Contains(e, "http2xconnect=1") {
|
|
disableExtendedConnectProtocol = false
|
|
}
|
|
}
|
|
|
|
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
|
|
// https://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2
|
|
initialMaxFrameSize = 16384
|
|
|
|
// NextProtoTLS is the NPN/ALPN protocol negotiated during
|
|
// HTTP/2's TLS setup.
|
|
NextProtoTLS = "h2"
|
|
|
|
// https://httpwg.org/specs/rfc7540.html#SettingValues
|
|
initialHeaderTableSize = 4096
|
|
|
|
initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
|
|
|
|
defaultMaxReadFrameSize = 1 << 20
|
|
)
|
|
|
|
var (
|
|
clientPreface = []byte(ClientPreface)
|
|
)
|
|
|
|
type streamState int
|
|
|
|
// HTTP/2 stream states.
|
|
//
|
|
// See http://tools.ietf.org/html/rfc7540#section-5.1.
|
|
//
|
|
// For simplicity, the server code merges "reserved (local)" into
|
|
// "half-closed (remote)". This is one less state transition to track.
|
|
// The only downside is that we send PUSH_PROMISEs slightly less
|
|
// liberally than allowable. More discussion here:
|
|
// https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0599.html
|
|
//
|
|
// "reserved (remote)" is omitted since the client code does not
|
|
// support server push.
|
|
const (
|
|
stateIdle streamState = iota
|
|
stateOpen
|
|
stateHalfClosedLocal
|
|
stateHalfClosedRemote
|
|
stateClosed
|
|
)
|
|
|
|
var stateName = [...]string{
|
|
stateIdle: "Idle",
|
|
stateOpen: "Open",
|
|
stateHalfClosedLocal: "HalfClosedLocal",
|
|
stateHalfClosedRemote: "HalfClosedRemote",
|
|
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 https://httpwg.org/specs/rfc7540.html#SettingFormat
|
|
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)
|
|
}
|
|
case SettingEnableConnectProtocol:
|
|
if s.Val != 1 && s.Val != 0 {
|
|
return ConnectionError(ErrCodeProtocol)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// A SettingID is an HTTP/2 setting as defined in
|
|
// https://httpwg.org/specs/rfc7540.html#iana-settings
|
|
type SettingID uint16
|
|
|
|
const (
|
|
SettingHeaderTableSize SettingID = 0x1
|
|
SettingEnablePush SettingID = 0x2
|
|
SettingMaxConcurrentStreams SettingID = 0x3
|
|
SettingInitialWindowSize SettingID = 0x4
|
|
SettingMaxFrameSize SettingID = 0x5
|
|
SettingMaxHeaderListSize SettingID = 0x6
|
|
SettingEnableConnectProtocol SettingID = 0x8
|
|
)
|
|
|
|
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",
|
|
SettingEnableConnectProtocol: "ENABLE_CONNECT_PROTOCOL",
|
|
}
|
|
|
|
func (s SettingID) String() string {
|
|
if v, ok := settingName[s]; ok {
|
|
return v
|
|
}
|
|
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
|
}
|
|
|
|
// validWireHeaderFieldName reports whether v is a valid header field
|
|
// name (key). See httpguts.ValidHeaderName for the base rules.
|
|
//
|
|
// 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 validWireHeaderFieldName(v string) bool {
|
|
if len(v) == 0 {
|
|
return false
|
|
}
|
|
for _, r := range v {
|
|
if !httpguts.IsTokenRune(r) {
|
|
return false
|
|
}
|
|
if 'A' <= r && r <= 'Z' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func httpCodeString(code int) string {
|
|
switch code {
|
|
case 200:
|
|
return "200"
|
|
case 404:
|
|
return "404"
|
|
}
|
|
return strconv.Itoa(code)
|
|
}
|
|
|
|
// from pkg io
|
|
type stringWriter interface {
|
|
WriteString(s string) (n int, err error)
|
|
}
|
|
|
|
// 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 {
|
|
_ incomparable
|
|
conn net.Conn // immutable
|
|
bw *bufio.Writer // non-nil when data is buffered
|
|
byteTimeout time.Duration // immutable, WriteByteTimeout
|
|
}
|
|
|
|
func newBufferedWriter(conn net.Conn, timeout time.Duration) *bufferedWriter {
|
|
return &bufferedWriter{
|
|
conn: conn,
|
|
byteTimeout: timeout,
|
|
}
|
|
}
|
|
|
|
// bufWriterPoolBufferSize is the size of bufio.Writer's
|
|
// buffers created using bufWriterPool.
|
|
//
|
|
// TODO: pick a less arbitrary value? this is a bit under
|
|
// (3 x typical 1500 byte MTU) at least. Other than that,
|
|
// not much thought went into it.
|
|
const bufWriterPoolBufferSize = 4 << 10
|
|
|
|
var bufWriterPool = sync.Pool{
|
|
New: func() interface{} {
|
|
return bufio.NewWriterSize(nil, bufWriterPoolBufferSize)
|
|
},
|
|
}
|
|
|
|
func (w *bufferedWriter) Available() int {
|
|
if w.bw == nil {
|
|
return bufWriterPoolBufferSize
|
|
}
|
|
return w.bw.Available()
|
|
}
|
|
|
|
func (w *bufferedWriter) Write(p []byte) (n int, err error) {
|
|
if w.bw == nil {
|
|
bw := bufWriterPool.Get().(*bufio.Writer)
|
|
bw.Reset((*bufferedWriterTimeoutWriter)(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
|
|
}
|
|
|
|
type bufferedWriterTimeoutWriter bufferedWriter
|
|
|
|
func (w *bufferedWriterTimeoutWriter) Write(p []byte) (n int, err error) {
|
|
return writeWithByteTimeout(w.conn, w.byteTimeout, p)
|
|
}
|
|
|
|
// writeWithByteTimeout writes to conn.
|
|
// If more than timeout passes without any bytes being written to the connection,
|
|
// the write fails.
|
|
func writeWithByteTimeout(conn net.Conn, timeout time.Duration, p []byte) (n int, err error) {
|
|
if timeout <= 0 {
|
|
return conn.Write(p)
|
|
}
|
|
for {
|
|
conn.SetWriteDeadline(time.Now().Add(timeout))
|
|
nn, err := conn.Write(p[n:])
|
|
n += nn
|
|
if n == len(p) || nn == 0 || !errors.Is(err, os.ErrDeadlineExceeded) {
|
|
// Either we finished the write, made no progress, or hit the deadline.
|
|
// Whichever it is, we're done now.
|
|
conn.SetWriteDeadline(time.Time{})
|
|
return n, 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 RFC 7230, section 3.3.
|
|
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 {
|
|
_ incomparable
|
|
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}
|
|
|
|
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 owns, so
|
|
// stash it away while we sort the user's buffer.
|
|
save := s.v
|
|
s.v = ss
|
|
sort.Sort(s)
|
|
s.v = save
|
|
}
|
|
|
|
// incomparable is a zero-width, non-comparable type. Adding it to a struct
|
|
// makes that struct also non-comparable, and generally doesn't add
|
|
// any size (as long as it's first).
|
|
type incomparable [0]func()
|