mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 11:52:24 -05:00 
			
		
		
		
	
		
			
	
	
		
			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 | ||
|  | 	// 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) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	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 | ||
|  | ) | ||
|  | 
 | ||
|  | 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() |