mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-02 16:52:25 -06: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()
							 |