mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-22 13:00:19 +00:00
91774fc936
Uses github.com/libopenstorage/secrets to communicate with Vault. This removes the need for maintaining our own limited Vault APIs. By adding the new dependency, several other packages got updated in the process. Unused indirect dependencies have been removed from go.mod. Signed-off-by: Niels de Vos <ndevos@redhat.com>
386 lines
9.5 KiB
Go
386 lines
9.5 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 "golang.org/x/net/http2"
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"golang.org/x/net/http/httpguts"
|
|
)
|
|
|
|
var (
|
|
VerboseLogs bool
|
|
logFrameWrites bool
|
|
logFrameReads bool
|
|
inTests 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
|
|
|
|
// 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 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))
|
|
}
|
|
|
|
// 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 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 {
|
|
_ incomparable
|
|
w io.Writer // immutable
|
|
bw *bufio.Writer // non-nil when data is buffered
|
|
}
|
|
|
|
func newBufferedWriter(w io.Writer) *bufferedWriter {
|
|
return &bufferedWriter{w: w}
|
|
}
|
|
|
|
// 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(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 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
|
|
}
|
|
|
|
// validPseudoPath reports whether v is a valid :path pseudo-header
|
|
// value. It must be either:
|
|
//
|
|
// *) a non-empty string starting with '/'
|
|
// *) the string '*', for OPTIONS requests.
|
|
//
|
|
// For now this is only used a quick check for deciding when to clean
|
|
// up Opaque URLs before sending requests from the Transport.
|
|
// 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 {
|
|
return (len(v) > 0 && v[0] == '/') || v == "*"
|
|
}
|
|
|
|
// 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()
|