mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
* add DateTime sans mgo * change all uses of strfmt.DateTime to common.DateTime, remove test strfmt usage * remove api tests, system-test dep on api test multiple reasons to remove the api tests: * awkward dependency with fn_go meant generating bindings on a branched fn to vendor those to test new stuff. this is at a minimum not at all intuitive, worth it, nor a fun way to spend the finite amount of time we have to live. * api tests only tested a subset of functionality that the server/ api tests already test, and we risk having tests where one tests some thing and the other doesn't. let's not. we have too many test suites as it is, and these pretty much only test that we updated the fn_go bindings, which is actually a hassle as noted above and the cli will pretty quickly figure out anyway. * fn_go relies on openapi, which relies on mgo, which is deprecated and we'd like to remove as a dependency. openapi is a _huge_ dep built in a NIH fashion, that cannot simply remove the mgo dep as users may be using it. we've now stolen their date time and otherwise killed usage of it in fn core, for fn_go it still exists but that's less of a problem. * update deps removals: * easyjson * mgo * go-openapi * mapstructure * fn_go * purell * go-validator also, had to lock docker. we shouldn't use docker on master anyway, they strongly advise against that. had no luck with latest version rev, so i locked it to what we were using before. until next time. the rest is just playing dep roulette, those end up removing a ton tho * fix exec test to work * account for john le cache
392 lines
9.7 KiB
Go
392 lines
9.7 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"
|
|
"errors"
|
|
"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))
|
|
}
|
|
|
|
var (
|
|
errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
|
|
errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
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}
|
|
}
|
|
|
|
// 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 {
|
|
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 == "*"
|
|
}
|