| //go:build !nethttpomithttp2 | |
| // +build !nethttpomithttp2 | |
| // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. | |
| // $ bundle -o=h2_bundle.go -prefix=http2 -tags=!nethttpomithttp2 golang.org/x/net/http2 | |
| // 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 http | |
| import ( | |
| "bufio" | |
| "bytes" | |
| "compress/gzip" | |
| "context" | |
| "crypto/rand" | |
| "crypto/tls" | |
| "encoding/binary" | |
| "errors" | |
| "fmt" | |
| "io" | |
| "io/ioutil" | |
| "log" | |
| "math" | |
| mathrand "math/rand" | |
| "net" | |
| "net/http/httptrace" | |
| "net/textproto" | |
| "net/url" | |
| "os" | |
| "reflect" | |
| "runtime" | |
| "sort" | |
| "strconv" | |
| "strings" | |
| "sync" | |
| "sync/atomic" | |
| "time" | |
| "golang.org/x/net/http/httpguts" | |
| "golang.org/x/net/http2/hpack" | |
| "golang.org/x/net/idna" | |
| ) | |
| // A list of the possible cipher suite ids. Taken from | |
| // https://www.iana.org/assignments/tls-parameters/tls-parameters.txt | |
| const ( | |
| http2cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000 | |
| http2cipher_TLS_RSA_WITH_NULL_MD5 uint16 = 0x0001 | |
| http2cipher_TLS_RSA_WITH_NULL_SHA uint16 = 0x0002 | |
| http2cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0003 | |
| http2cipher_TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004 | |
| http2cipher_TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 | |
| http2cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x0006 | |
| http2cipher_TLS_RSA_WITH_IDEA_CBC_SHA uint16 = 0x0007 | |
| http2cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0008 | |
| http2cipher_TLS_RSA_WITH_DES_CBC_SHA uint16 = 0x0009 | |
| http2cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000A | |
| http2cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000B | |
| http2cipher_TLS_DH_DSS_WITH_DES_CBC_SHA uint16 = 0x000C | |
| http2cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x000D | |
| http2cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000E | |
| http2cipher_TLS_DH_RSA_WITH_DES_CBC_SHA uint16 = 0x000F | |
| http2cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0010 | |
| http2cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0011 | |
| http2cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA uint16 = 0x0012 | |
| http2cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x0013 | |
| http2cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0014 | |
| http2cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA uint16 = 0x0015 | |
| http2cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0016 | |
| http2cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0017 | |
| http2cipher_TLS_DH_anon_WITH_RC4_128_MD5 uint16 = 0x0018 | |
| http2cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0019 | |
| http2cipher_TLS_DH_anon_WITH_DES_CBC_SHA uint16 = 0x001A | |
| http2cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0x001B | |
| // Reserved uint16 = 0x001C-1D | |
| http2cipher_TLS_KRB5_WITH_DES_CBC_SHA uint16 = 0x001E | |
| http2cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA uint16 = 0x001F | |
| http2cipher_TLS_KRB5_WITH_RC4_128_SHA uint16 = 0x0020 | |
| http2cipher_TLS_KRB5_WITH_IDEA_CBC_SHA uint16 = 0x0021 | |
| http2cipher_TLS_KRB5_WITH_DES_CBC_MD5 uint16 = 0x0022 | |
| http2cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5 uint16 = 0x0023 | |
| http2cipher_TLS_KRB5_WITH_RC4_128_MD5 uint16 = 0x0024 | |
| http2cipher_TLS_KRB5_WITH_IDEA_CBC_MD5 uint16 = 0x0025 | |
| http2cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA uint16 = 0x0026 | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA uint16 = 0x0027 | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA uint16 = 0x0028 | |
| http2cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 uint16 = 0x0029 | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x002A | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5 uint16 = 0x002B | |
| http2cipher_TLS_PSK_WITH_NULL_SHA uint16 = 0x002C | |
| http2cipher_TLS_DHE_PSK_WITH_NULL_SHA uint16 = 0x002D | |
| http2cipher_TLS_RSA_PSK_WITH_NULL_SHA uint16 = 0x002E | |
| http2cipher_TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002F | |
| http2cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0030 | |
| http2cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0031 | |
| http2cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0032 | |
| http2cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0033 | |
| http2cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA uint16 = 0x0034 | |
| http2cipher_TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 | |
| http2cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0036 | |
| http2cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0037 | |
| http2cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0038 | |
| http2cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0039 | |
| http2cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA uint16 = 0x003A | |
| http2cipher_TLS_RSA_WITH_NULL_SHA256 uint16 = 0x003B | |
| http2cipher_TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003C | |
| http2cipher_TLS_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x003D | |
| http2cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x003E | |
| http2cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003F | |
| http2cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x0040 | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0041 | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0042 | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0043 | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0044 | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0045 | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0046 | |
| // Reserved uint16 = 0x0047-4F | |
| // Reserved uint16 = 0x0050-58 | |
| // Reserved uint16 = 0x0059-5C | |
| // Unassigned uint16 = 0x005D-5F | |
| // Reserved uint16 = 0x0060-66 | |
| http2cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x0067 | |
| http2cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x0068 | |
| http2cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x0069 | |
| http2cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x006A | |
| http2cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x006B | |
| http2cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256 uint16 = 0x006C | |
| http2cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256 uint16 = 0x006D | |
| // Unassigned uint16 = 0x006E-83 | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0084 | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0085 | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0086 | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0087 | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0088 | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0089 | |
| http2cipher_TLS_PSK_WITH_RC4_128_SHA uint16 = 0x008A | |
| http2cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008B | |
| http2cipher_TLS_PSK_WITH_AES_128_CBC_SHA uint16 = 0x008C | |
| http2cipher_TLS_PSK_WITH_AES_256_CBC_SHA uint16 = 0x008D | |
| http2cipher_TLS_DHE_PSK_WITH_RC4_128_SHA uint16 = 0x008E | |
| http2cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008F | |
| http2cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0090 | |
| http2cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0091 | |
| http2cipher_TLS_RSA_PSK_WITH_RC4_128_SHA uint16 = 0x0092 | |
| http2cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x0093 | |
| http2cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0094 | |
| http2cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0095 | |
| http2cipher_TLS_RSA_WITH_SEED_CBC_SHA uint16 = 0x0096 | |
| http2cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA uint16 = 0x0097 | |
| http2cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA uint16 = 0x0098 | |
| http2cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA uint16 = 0x0099 | |
| http2cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA uint16 = 0x009A | |
| http2cipher_TLS_DH_anon_WITH_SEED_CBC_SHA uint16 = 0x009B | |
| http2cipher_TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009C | |
| http2cipher_TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009D | |
| http2cipher_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009E | |
| http2cipher_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009F | |
| http2cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x00A0 | |
| http2cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x00A1 | |
| http2cipher_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A2 | |
| http2cipher_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A3 | |
| http2cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A4 | |
| http2cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A5 | |
| http2cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256 uint16 = 0x00A6 | |
| http2cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384 uint16 = 0x00A7 | |
| http2cipher_TLS_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00A8 | |
| http2cipher_TLS_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00A9 | |
| http2cipher_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AA | |
| http2cipher_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AB | |
| http2cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AC | |
| http2cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AD | |
| http2cipher_TLS_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00AE | |
| http2cipher_TLS_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00AF | |
| http2cipher_TLS_PSK_WITH_NULL_SHA256 uint16 = 0x00B0 | |
| http2cipher_TLS_PSK_WITH_NULL_SHA384 uint16 = 0x00B1 | |
| http2cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B2 | |
| http2cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B3 | |
| http2cipher_TLS_DHE_PSK_WITH_NULL_SHA256 uint16 = 0x00B4 | |
| http2cipher_TLS_DHE_PSK_WITH_NULL_SHA384 uint16 = 0x00B5 | |
| http2cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B6 | |
| http2cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B7 | |
| http2cipher_TLS_RSA_PSK_WITH_NULL_SHA256 uint16 = 0x00B8 | |
| http2cipher_TLS_RSA_PSK_WITH_NULL_SHA384 uint16 = 0x00B9 | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BA | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BB | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BC | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BD | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BE | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BF | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C0 | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C1 | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C2 | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C3 | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C4 | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C5 | |
| // Unassigned uint16 = 0x00C6-FE | |
| http2cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV uint16 = 0x00FF | |
| // Unassigned uint16 = 0x01-55,* | |
| http2cipher_TLS_FALLBACK_SCSV uint16 = 0x5600 | |
| // Unassigned uint16 = 0x5601 - 0xC000 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA uint16 = 0xC001 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA uint16 = 0xC002 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC003 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC004 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC005 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA uint16 = 0xC006 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xC007 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC008 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC009 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC00A | |
| http2cipher_TLS_ECDH_RSA_WITH_NULL_SHA uint16 = 0xC00B | |
| http2cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA uint16 = 0xC00C | |
| http2cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC00D | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC00E | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC00F | |
| http2cipher_TLS_ECDHE_RSA_WITH_NULL_SHA uint16 = 0xC010 | |
| http2cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xC011 | |
| http2cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC012 | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC013 | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC014 | |
| http2cipher_TLS_ECDH_anon_WITH_NULL_SHA uint16 = 0xC015 | |
| http2cipher_TLS_ECDH_anon_WITH_RC4_128_SHA uint16 = 0xC016 | |
| http2cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0xC017 | |
| http2cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA uint16 = 0xC018 | |
| http2cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA uint16 = 0xC019 | |
| http2cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01A | |
| http2cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01B | |
| http2cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01C | |
| http2cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA uint16 = 0xC01D | |
| http2cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC01E | |
| http2cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA uint16 = 0xC01F | |
| http2cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA uint16 = 0xC020 | |
| http2cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC021 | |
| http2cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA uint16 = 0xC022 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC023 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC024 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC025 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC026 | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC027 | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC028 | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC029 | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC02A | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02B | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02C | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02D | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02E | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02F | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC030 | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC031 | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC032 | |
| http2cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA uint16 = 0xC033 | |
| http2cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0xC034 | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xC035 | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xC036 | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0xC037 | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0xC038 | |
| http2cipher_TLS_ECDHE_PSK_WITH_NULL_SHA uint16 = 0xC039 | |
| http2cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256 uint16 = 0xC03A | |
| http2cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384 uint16 = 0xC03B | |
| http2cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03C | |
| http2cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03D | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03E | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03F | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC040 | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC041 | |
| http2cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC042 | |
| http2cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC043 | |
| http2cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC044 | |
| http2cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC045 | |
| http2cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC046 | |
| http2cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC047 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC048 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC049 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04A | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04B | |
| http2cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04C | |
| http2cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04D | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04E | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04F | |
| http2cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC050 | |
| http2cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC051 | |
| http2cipher_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC052 | |
| http2cipher_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC053 | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC054 | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC055 | |
| http2cipher_TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC056 | |
| http2cipher_TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC057 | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC058 | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC059 | |
| http2cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05A | |
| http2cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05B | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05C | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05D | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05E | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05F | |
| http2cipher_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC060 | |
| http2cipher_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC061 | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC062 | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC063 | |
| http2cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC064 | |
| http2cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC065 | |
| http2cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC066 | |
| http2cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC067 | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC068 | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC069 | |
| http2cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06A | |
| http2cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06B | |
| http2cipher_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06C | |
| http2cipher_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06D | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06E | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06F | |
| http2cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC070 | |
| http2cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC071 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC072 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC073 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC074 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC075 | |
| http2cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC076 | |
| http2cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC077 | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC078 | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC079 | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07A | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07B | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07C | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07D | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07E | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07F | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC080 | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC081 | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC082 | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC083 | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC084 | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC085 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC086 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC087 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC088 | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC089 | |
| http2cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08A | |
| http2cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08B | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08C | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08D | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08E | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08F | |
| http2cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC090 | |
| http2cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC091 | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC092 | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC093 | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC094 | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC095 | |
| http2cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC096 | |
| http2cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC097 | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC098 | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC099 | |
| http2cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC09A | |
| http2cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC09B | |
| http2cipher_TLS_RSA_WITH_AES_128_CCM uint16 = 0xC09C | |
| http2cipher_TLS_RSA_WITH_AES_256_CCM uint16 = 0xC09D | |
| http2cipher_TLS_DHE_RSA_WITH_AES_128_CCM uint16 = 0xC09E | |
| http2cipher_TLS_DHE_RSA_WITH_AES_256_CCM uint16 = 0xC09F | |
| http2cipher_TLS_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A0 | |
| http2cipher_TLS_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A1 | |
| http2cipher_TLS_DHE_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A2 | |
| http2cipher_TLS_DHE_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A3 | |
| http2cipher_TLS_PSK_WITH_AES_128_CCM uint16 = 0xC0A4 | |
| http2cipher_TLS_PSK_WITH_AES_256_CCM uint16 = 0xC0A5 | |
| http2cipher_TLS_DHE_PSK_WITH_AES_128_CCM uint16 = 0xC0A6 | |
| http2cipher_TLS_DHE_PSK_WITH_AES_256_CCM uint16 = 0xC0A7 | |
| http2cipher_TLS_PSK_WITH_AES_128_CCM_8 uint16 = 0xC0A8 | |
| http2cipher_TLS_PSK_WITH_AES_256_CCM_8 uint16 = 0xC0A9 | |
| http2cipher_TLS_PSK_DHE_WITH_AES_128_CCM_8 uint16 = 0xC0AA | |
| http2cipher_TLS_PSK_DHE_WITH_AES_256_CCM_8 uint16 = 0xC0AB | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM uint16 = 0xC0AC | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM uint16 = 0xC0AD | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 uint16 = 0xC0AE | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 uint16 = 0xC0AF | |
| // Unassigned uint16 = 0xC0B0-FF | |
| // Unassigned uint16 = 0xC1-CB,* | |
| // Unassigned uint16 = 0xCC00-A7 | |
| http2cipher_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA8 | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA9 | |
| http2cipher_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAA | |
| http2cipher_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAB | |
| http2cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAC | |
| http2cipher_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAD | |
| http2cipher_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAE | |
| ) | |
| // isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec. | |
| // References: | |
| // https://tools.ietf.org/html/rfc7540#appendix-A | |
| // Reject cipher suites from Appendix A. | |
| // "This list includes those cipher suites that do not | |
| // offer an ephemeral key exchange and those that are | |
| // based on the TLS null, stream or block cipher type" | |
| func http2isBadCipher(cipher uint16) bool { | |
| switch cipher { | |
| case http2cipher_TLS_NULL_WITH_NULL_NULL, | |
| http2cipher_TLS_RSA_WITH_NULL_MD5, | |
| http2cipher_TLS_RSA_WITH_NULL_SHA, | |
| http2cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5, | |
| http2cipher_TLS_RSA_WITH_RC4_128_MD5, | |
| http2cipher_TLS_RSA_WITH_RC4_128_SHA, | |
| http2cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, | |
| http2cipher_TLS_RSA_WITH_IDEA_CBC_SHA, | |
| http2cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, | |
| http2cipher_TLS_RSA_WITH_DES_CBC_SHA, | |
| http2cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_WITH_DES_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_WITH_DES_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, | |
| http2cipher_TLS_DH_anon_WITH_RC4_128_MD5, | |
| http2cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, | |
| http2cipher_TLS_DH_anon_WITH_DES_CBC_SHA, | |
| http2cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_KRB5_WITH_DES_CBC_SHA, | |
| http2cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_KRB5_WITH_RC4_128_SHA, | |
| http2cipher_TLS_KRB5_WITH_IDEA_CBC_SHA, | |
| http2cipher_TLS_KRB5_WITH_DES_CBC_MD5, | |
| http2cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5, | |
| http2cipher_TLS_KRB5_WITH_RC4_128_MD5, | |
| http2cipher_TLS_KRB5_WITH_IDEA_CBC_MD5, | |
| http2cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA, | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA, | |
| http2cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5, | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5, | |
| http2cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5, | |
| http2cipher_TLS_PSK_WITH_NULL_SHA, | |
| http2cipher_TLS_DHE_PSK_WITH_NULL_SHA, | |
| http2cipher_TLS_RSA_PSK_WITH_NULL_SHA, | |
| http2cipher_TLS_RSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_RSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_RSA_WITH_NULL_SHA256, | |
| http2cipher_TLS_RSA_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_RSA_WITH_AES_256_CBC_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, | |
| http2cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, | |
| http2cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256, | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA, | |
| http2cipher_TLS_PSK_WITH_RC4_128_SHA, | |
| http2cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_PSK_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_PSK_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_DHE_PSK_WITH_RC4_128_SHA, | |
| http2cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_RSA_PSK_WITH_RC4_128_SHA, | |
| http2cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_RSA_WITH_SEED_CBC_SHA, | |
| http2cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA, | |
| http2cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA, | |
| http2cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA, | |
| http2cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA, | |
| http2cipher_TLS_DH_anon_WITH_SEED_CBC_SHA, | |
| http2cipher_TLS_RSA_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_RSA_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_PSK_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_PSK_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_PSK_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_PSK_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_PSK_WITH_NULL_SHA256, | |
| http2cipher_TLS_PSK_WITH_NULL_SHA384, | |
| http2cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_DHE_PSK_WITH_NULL_SHA256, | |
| http2cipher_TLS_DHE_PSK_WITH_NULL_SHA384, | |
| http2cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_RSA_PSK_WITH_NULL_SHA256, | |
| http2cipher_TLS_RSA_PSK_WITH_NULL_SHA384, | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256, | |
| http2cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256, | |
| http2cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256, | |
| http2cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_ECDH_RSA_WITH_NULL_SHA, | |
| http2cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA, | |
| http2cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_ECDHE_RSA_WITH_NULL_SHA, | |
| http2cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA, | |
| http2cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_ECDH_anon_WITH_NULL_SHA, | |
| http2cipher_TLS_ECDH_anon_WITH_RC4_128_SHA, | |
| http2cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, | |
| http2cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, | |
| http2cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA, | |
| http2cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, | |
| http2cipher_TLS_ECDHE_PSK_WITH_NULL_SHA, | |
| http2cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256, | |
| http2cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384, | |
| http2cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, | |
| http2cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, | |
| http2cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, | |
| http2cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, | |
| http2cipher_TLS_RSA_WITH_AES_128_CCM, | |
| http2cipher_TLS_RSA_WITH_AES_256_CCM, | |
| http2cipher_TLS_RSA_WITH_AES_128_CCM_8, | |
| http2cipher_TLS_RSA_WITH_AES_256_CCM_8, | |
| http2cipher_TLS_PSK_WITH_AES_128_CCM, | |
| http2cipher_TLS_PSK_WITH_AES_256_CCM, | |
| http2cipher_TLS_PSK_WITH_AES_128_CCM_8, | |
| http2cipher_TLS_PSK_WITH_AES_256_CCM_8: | |
| return true | |
| default: | |
| return false | |
| } | |
| } | |
| // ClientConnPool manages a pool of HTTP/2 client connections. | |
| type http2ClientConnPool interface { | |
| GetClientConn(req *Request, addr string) (*http2ClientConn, error) | |
| MarkDead(*http2ClientConn) | |
| } | |
| // clientConnPoolIdleCloser is the interface implemented by ClientConnPool | |
| // implementations which can close their idle connections. | |
| type http2clientConnPoolIdleCloser interface { | |
| http2ClientConnPool | |
| closeIdleConnections() | |
| } | |
| var ( | |
| _ http2clientConnPoolIdleCloser = (*http2clientConnPool)(nil) | |
| _ http2clientConnPoolIdleCloser = http2noDialClientConnPool{} | |
| ) | |
| // TODO: use singleflight for dialing and addConnCalls? | |
| type http2clientConnPool struct { | |
| t *http2Transport | |
| mu sync.Mutex // TODO: maybe switch to RWMutex | |
| // TODO: add support for sharing conns based on cert names | |
| // (e.g. share conn for googleapis.com and appspot.com) | |
| conns map[string][]*http2ClientConn // key is host:port | |
| dialing map[string]*http2dialCall // currently in-flight dials | |
| keys map[*http2ClientConn][]string | |
| addConnCalls map[string]*http2addConnCall // in-flight addConnIfNeede calls | |
| } | |
| func (p *http2clientConnPool) GetClientConn(req *Request, addr string) (*http2ClientConn, error) { | |
| return p.getClientConn(req, addr, http2dialOnMiss) | |
| } | |
| const ( | |
| http2dialOnMiss = true | |
| http2noDialOnMiss = false | |
| ) | |
| // shouldTraceGetConn reports whether getClientConn should call any | |
| // ClientTrace.GetConn hook associated with the http.Request. | |
| // | |
| // This complexity is needed to avoid double calls of the GetConn hook | |
| // during the back-and-forth between net/http and x/net/http2 (when the | |
| // net/http.Transport is upgraded to also speak http2), as well as support | |
| // the case where x/net/http2 is being used directly. | |
| func (p *http2clientConnPool) shouldTraceGetConn(st http2clientConnIdleState) bool { | |
| // If our Transport wasn't made via ConfigureTransport, always | |
| // trace the GetConn hook if provided, because that means the | |
| // http2 package is being used directly and it's the one | |
| // dialing, as opposed to net/http. | |
| if _, ok := p.t.ConnPool.(http2noDialClientConnPool); !ok { | |
| return true | |
| } | |
| // Otherwise, only use the GetConn hook if this connection has | |
| // been used previously for other requests. For fresh | |
| // connections, the net/http package does the dialing. | |
| return !st.freshConn | |
| } | |
| func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMiss bool) (*http2ClientConn, error) { | |
| if http2isConnectionCloseRequest(req) && dialOnMiss { | |
| // It gets its own connection. | |
| http2traceGetConn(req, addr) | |
| const singleUse = true | |
| cc, err := p.t.dialClientConn(addr, singleUse) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return cc, nil | |
| } | |
| p.mu.Lock() | |
| for _, cc := range p.conns[addr] { | |
| if st := cc.idleState(); st.canTakeNewRequest { | |
| if p.shouldTraceGetConn(st) { | |
| http2traceGetConn(req, addr) | |
| } | |
| p.mu.Unlock() | |
| return cc, nil | |
| } | |
| } | |
| if !dialOnMiss { | |
| p.mu.Unlock() | |
| return nil, http2ErrNoCachedConn | |
| } | |
| http2traceGetConn(req, addr) | |
| call := p.getStartDialLocked(addr) | |
| p.mu.Unlock() | |
| <-call.done | |
| return call.res, call.err | |
| } | |
| // dialCall is an in-flight Transport dial call to a host. | |
| type http2dialCall struct { | |
| _ http2incomparable | |
| p *http2clientConnPool | |
| done chan struct{} // closed when done | |
| res *http2ClientConn // valid after done is closed | |
| err error // valid after done is closed | |
| } | |
| // requires p.mu is held. | |
| func (p *http2clientConnPool) getStartDialLocked(addr string) *http2dialCall { | |
| if call, ok := p.dialing[addr]; ok { | |
| // A dial is already in-flight. Don't start another. | |
| return call | |
| } | |
| call := &http2dialCall{p: p, done: make(chan struct{})} | |
| if p.dialing == nil { | |
| p.dialing = make(map[string]*http2dialCall) | |
| } | |
| p.dialing[addr] = call | |
| go call.dial(addr) | |
| return call | |
| } | |
| // run in its own goroutine. | |
| func (c *http2dialCall) dial(addr string) { | |
| const singleUse = false // shared conn | |
| c.res, c.err = c.p.t.dialClientConn(addr, singleUse) | |
| close(c.done) | |
| c.p.mu.Lock() | |
| delete(c.p.dialing, addr) | |
| if c.err == nil { | |
| c.p.addConnLocked(addr, c.res) | |
| } | |
| c.p.mu.Unlock() | |
| } | |
| // addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't | |
| // already exist. It coalesces concurrent calls with the same key. | |
| // This is used by the http1 Transport code when it creates a new connection. Because | |
| // the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know | |
| // the protocol), it can get into a situation where it has multiple TLS connections. | |
| // This code decides which ones live or die. | |
| // The return value used is whether c was used. | |
| // c is never closed. | |
| func (p *http2clientConnPool) addConnIfNeeded(key string, t *http2Transport, c *tls.Conn) (used bool, err error) { | |
| p.mu.Lock() | |
| for _, cc := range p.conns[key] { | |
| if cc.CanTakeNewRequest() { | |
| p.mu.Unlock() | |
| return false, nil | |
| } | |
| } | |
| call, dup := p.addConnCalls[key] | |
| if !dup { | |
| if p.addConnCalls == nil { | |
| p.addConnCalls = make(map[string]*http2addConnCall) | |
| } | |
| call = &http2addConnCall{ | |
| p: p, | |
| done: make(chan struct{}), | |
| } | |
| p.addConnCalls[key] = call | |
| go call.run(t, key, c) | |
| } | |
| p.mu.Unlock() | |
| <-call.done | |
| if call.err != nil { | |
| return false, call.err | |
| } | |
| return !dup, nil | |
| } | |
| type http2addConnCall struct { | |
| _ http2incomparable | |
| p *http2clientConnPool | |
| done chan struct{} // closed when done | |
| err error | |
| } | |
| func (c *http2addConnCall) run(t *http2Transport, key string, tc *tls.Conn) { | |
| cc, err := t.NewClientConn(tc) | |
| p := c.p | |
| p.mu.Lock() | |
| if err != nil { | |
| c.err = err | |
| } else { | |
| p.addConnLocked(key, cc) | |
| } | |
| delete(p.addConnCalls, key) | |
| p.mu.Unlock() | |
| close(c.done) | |
| } | |
| // p.mu must be held | |
| func (p *http2clientConnPool) addConnLocked(key string, cc *http2ClientConn) { | |
| for _, v := range p.conns[key] { | |
| if v == cc { | |
| return | |
| } | |
| } | |
| if p.conns == nil { | |
| p.conns = make(map[string][]*http2ClientConn) | |
| } | |
| if p.keys == nil { | |
| p.keys = make(map[*http2ClientConn][]string) | |
| } | |
| p.conns[key] = append(p.conns[key], cc) | |
| p.keys[cc] = append(p.keys[cc], key) | |
| } | |
| func (p *http2clientConnPool) MarkDead(cc *http2ClientConn) { | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| for _, key := range p.keys[cc] { | |
| vv, ok := p.conns[key] | |
| if !ok { | |
| continue | |
| } | |
| newList := http2filterOutClientConn(vv, cc) | |
| if len(newList) > 0 { | |
| p.conns[key] = newList | |
| } else { | |
| delete(p.conns, key) | |
| } | |
| } | |
| delete(p.keys, cc) | |
| } | |
| func (p *http2clientConnPool) closeIdleConnections() { | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| // TODO: don't close a cc if it was just added to the pool | |
| // milliseconds ago and has never been used. There's currently | |
| // a small race window with the HTTP/1 Transport's integration | |
| // where it can add an idle conn just before using it, and | |
| // somebody else can concurrently call CloseIdleConns and | |
| // break some caller's RoundTrip. | |
| for _, vv := range p.conns { | |
| for _, cc := range vv { | |
| cc.closeIfIdle() | |
| } | |
| } | |
| } | |
| func http2filterOutClientConn(in []*http2ClientConn, exclude *http2ClientConn) []*http2ClientConn { | |
| out := in[:0] | |
| for _, v := range in { | |
| if v != exclude { | |
| out = append(out, v) | |
| } | |
| } | |
| // If we filtered it out, zero out the last item to prevent | |
| // the GC from seeing it. | |
| if len(in) != len(out) { | |
| in[len(in)-1] = nil | |
| } | |
| return out | |
| } | |
| // noDialClientConnPool is an implementation of http2.ClientConnPool | |
| // which never dials. We let the HTTP/1.1 client dial and use its TLS | |
| // connection instead. | |
| type http2noDialClientConnPool struct{ *http2clientConnPool } | |
| func (p http2noDialClientConnPool) GetClientConn(req *Request, addr string) (*http2ClientConn, error) { | |
| return p.getClientConn(req, addr, http2noDialOnMiss) | |
| } | |
| // Buffer chunks are allocated from a pool to reduce pressure on GC. | |
| // The maximum wasted space per dataBuffer is 2x the largest size class, | |
| // which happens when the dataBuffer has multiple chunks and there is | |
| // one unread byte in both the first and last chunks. We use a few size | |
| // classes to minimize overheads for servers that typically receive very | |
| // small request bodies. | |
| // | |
| // TODO: Benchmark to determine if the pools are necessary. The GC may have | |
| // improved enough that we can instead allocate chunks like this: | |
| // make([]byte, max(16<<10, expectedBytesRemaining)) | |
| var ( | |
| http2dataChunkSizeClasses = []int{ | |
| 1 << 10, | |
| 2 << 10, | |
| 4 << 10, | |
| 8 << 10, | |
| 16 << 10, | |
| } | |
| http2dataChunkPools = [...]sync.Pool{ | |
| {New: func() interface{} { return make([]byte, 1<<10) }}, | |
| {New: func() interface{} { return make([]byte, 2<<10) }}, | |
| {New: func() interface{} { return make([]byte, 4<<10) }}, | |
| {New: func() interface{} { return make([]byte, 8<<10) }}, | |
| {New: func() interface{} { return make([]byte, 16<<10) }}, | |
| } | |
| ) | |
| func http2getDataBufferChunk(size int64) []byte { | |
| i := 0 | |
| for ; i < len(http2dataChunkSizeClasses)-1; i++ { | |
| if size <= int64(http2dataChunkSizeClasses[i]) { | |
| break | |
| } | |
| } | |
| return http2dataChunkPools[i].Get().([]byte) | |
| } | |
| func http2putDataBufferChunk(p []byte) { | |
| for i, n := range http2dataChunkSizeClasses { | |
| if len(p) == n { | |
| http2dataChunkPools[i].Put(p) | |
| return | |
| } | |
| } | |
| panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) | |
| } | |
| // dataBuffer is an io.ReadWriter backed by a list of data chunks. | |
| // Each dataBuffer is used to read DATA frames on a single stream. | |
| // The buffer is divided into chunks so the server can limit the | |
| // total memory used by a single connection without limiting the | |
| // request body size on any single stream. | |
| type http2dataBuffer struct { | |
| chunks [][]byte | |
| r int // next byte to read is chunks[0][r] | |
| w int // next byte to write is chunks[len(chunks)-1][w] | |
| size int // total buffered bytes | |
| expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0) | |
| } | |
| var http2errReadEmpty = errors.New("read from empty dataBuffer") | |
| // Read copies bytes from the buffer into p. | |
| // It is an error to read when no data is available. | |
| func (b *http2dataBuffer) Read(p []byte) (int, error) { | |
| if b.size == 0 { | |
| return 0, http2errReadEmpty | |
| } | |
| var ntotal int | |
| for len(p) > 0 && b.size > 0 { | |
| readFrom := b.bytesFromFirstChunk() | |
| n := copy(p, readFrom) | |
| p = p[n:] | |
| ntotal += n | |
| b.r += n | |
| b.size -= n | |
| // If the first chunk has been consumed, advance to the next chunk. | |
| if b.r == len(b.chunks[0]) { | |
| http2putDataBufferChunk(b.chunks[0]) | |
| end := len(b.chunks) - 1 | |
| copy(b.chunks[:end], b.chunks[1:]) | |
| b.chunks[end] = nil | |
| b.chunks = b.chunks[:end] | |
| b.r = 0 | |
| } | |
| } | |
| return ntotal, nil | |
| } | |
| func (b *http2dataBuffer) bytesFromFirstChunk() []byte { | |
| if len(b.chunks) == 1 { | |
| return b.chunks[0][b.r:b.w] | |
| } | |
| return b.chunks[0][b.r:] | |
| } | |
| // Len returns the number of bytes of the unread portion of the buffer. | |
| func (b *http2dataBuffer) Len() int { | |
| return b.size | |
| } | |
| // Write appends p to the buffer. | |
| func (b *http2dataBuffer) Write(p []byte) (int, error) { | |
| ntotal := len(p) | |
| for len(p) > 0 { | |
| // If the last chunk is empty, allocate a new chunk. Try to allocate | |
| // enough to fully copy p plus any additional bytes we expect to | |
| // receive. However, this may allocate less than len(p). | |
| want := int64(len(p)) | |
| if b.expected > want { | |
| want = b.expected | |
| } | |
| chunk := b.lastChunkOrAlloc(want) | |
| n := copy(chunk[b.w:], p) | |
| p = p[n:] | |
| b.w += n | |
| b.size += n | |
| b.expected -= int64(n) | |
| } | |
| return ntotal, nil | |
| } | |
| func (b *http2dataBuffer) lastChunkOrAlloc(want int64) []byte { | |
| if len(b.chunks) != 0 { | |
| last := b.chunks[len(b.chunks)-1] | |
| if b.w < len(last) { | |
| return last | |
| } | |
| } | |
| chunk := http2getDataBufferChunk(want) | |
| b.chunks = append(b.chunks, chunk) | |
| b.w = 0 | |
| return chunk | |
| } | |
| // An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec. | |
| type http2ErrCode uint32 | |
| const ( | |
| http2ErrCodeNo http2ErrCode = 0x0 | |
| http2ErrCodeProtocol http2ErrCode = 0x1 | |
| http2ErrCodeInternal http2ErrCode = 0x2 | |
| http2ErrCodeFlowControl http2ErrCode = 0x3 | |
| http2ErrCodeSettingsTimeout http2ErrCode = 0x4 | |
| http2ErrCodeStreamClosed http2ErrCode = 0x5 | |
| http2ErrCodeFrameSize http2ErrCode = 0x6 | |
| http2ErrCodeRefusedStream http2ErrCode = 0x7 | |
| http2ErrCodeCancel http2ErrCode = 0x8 | |
| http2ErrCodeCompression http2ErrCode = 0x9 | |
| http2ErrCodeConnect http2ErrCode = 0xa | |
| http2ErrCodeEnhanceYourCalm http2ErrCode = 0xb | |
| http2ErrCodeInadequateSecurity http2ErrCode = 0xc | |
| http2ErrCodeHTTP11Required http2ErrCode = 0xd | |
| ) | |
| var http2errCodeName = map[http2ErrCode]string{ | |
| http2ErrCodeNo: "NO_ERROR", | |
| http2ErrCodeProtocol: "PROTOCOL_ERROR", | |
| http2ErrCodeInternal: "INTERNAL_ERROR", | |
| http2ErrCodeFlowControl: "FLOW_CONTROL_ERROR", | |
| http2ErrCodeSettingsTimeout: "SETTINGS_TIMEOUT", | |
| http2ErrCodeStreamClosed: "STREAM_CLOSED", | |
| http2ErrCodeFrameSize: "FRAME_SIZE_ERROR", | |
| http2ErrCodeRefusedStream: "REFUSED_STREAM", | |
| http2ErrCodeCancel: "CANCEL", | |
| http2ErrCodeCompression: "COMPRESSION_ERROR", | |
| http2ErrCodeConnect: "CONNECT_ERROR", | |
| http2ErrCodeEnhanceYourCalm: "ENHANCE_YOUR_CALM", | |
| http2ErrCodeInadequateSecurity: "INADEQUATE_SECURITY", | |
| http2ErrCodeHTTP11Required: "HTTP_1_1_REQUIRED", | |
| } | |
| func (e http2ErrCode) String() string { | |
| if s, ok := http2errCodeName[e]; ok { | |
| return s | |
| } | |
| return fmt.Sprintf("unknown error code 0x%x", uint32(e)) | |
| } | |
| // ConnectionError is an error that results in the termination of the | |
| // entire connection. | |
| type http2ConnectionError http2ErrCode | |
| func (e http2ConnectionError) Error() string { | |
| return fmt.Sprintf("connection error: %s", http2ErrCode(e)) | |
| } | |
| // StreamError is an error that only affects one stream within an | |
| // HTTP/2 connection. | |
| type http2StreamError struct { | |
| StreamID uint32 | |
| Code http2ErrCode | |
| Cause error // optional additional detail | |
| } | |
| func http2streamError(id uint32, code http2ErrCode) http2StreamError { | |
| return http2StreamError{StreamID: id, Code: code} | |
| } | |
| func (e http2StreamError) Error() string { | |
| if e.Cause != nil { | |
| return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause) | |
| } | |
| return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code) | |
| } | |
| // 6.9.1 The Flow Control Window | |
| // "If a sender receives a WINDOW_UPDATE that causes a flow control | |
| // window to exceed this maximum it MUST terminate either the stream | |
| // or the connection, as appropriate. For streams, [...]; for the | |
| // connection, a GOAWAY frame with a FLOW_CONTROL_ERROR code." | |
| type http2goAwayFlowError struct{} | |
| func (http2goAwayFlowError) Error() string { return "connection exceeded flow control window size" } | |
| // connError represents an HTTP/2 ConnectionError error code, along | |
| // with a string (for debugging) explaining why. | |
| // | |
| // Errors of this type are only returned by the frame parser functions | |
| // and converted into ConnectionError(Code), after stashing away | |
| // the Reason into the Framer's errDetail field, accessible via | |
| // the (*Framer).ErrorDetail method. | |
| type http2connError struct { | |
| Code http2ErrCode // the ConnectionError error code | |
| Reason string // additional reason | |
| } | |
| func (e http2connError) Error() string { | |
| return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason) | |
| } | |
| type http2pseudoHeaderError string | |
| func (e http2pseudoHeaderError) Error() string { | |
| return fmt.Sprintf("invalid pseudo-header %q", string(e)) | |
| } | |
| type http2duplicatePseudoHeaderError string | |
| func (e http2duplicatePseudoHeaderError) Error() string { | |
| return fmt.Sprintf("duplicate pseudo-header %q", string(e)) | |
| } | |
| type http2headerFieldNameError string | |
| func (e http2headerFieldNameError) Error() string { | |
| return fmt.Sprintf("invalid header field name %q", string(e)) | |
| } | |
| type http2headerFieldValueError string | |
| func (e http2headerFieldValueError) Error() string { | |
| return fmt.Sprintf("invalid header field value %q", string(e)) | |
| } | |
| var ( | |
| http2errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers") | |
| http2errPseudoAfterRegular = errors.New("pseudo header field after regular") | |
| ) | |
| // flow is the flow control window's size. | |
| type http2flow struct { | |
| _ http2incomparable | |
| // n is the number of DATA bytes we're allowed to send. | |
| // A flow is kept both on a conn and a per-stream. | |
| n int32 | |
| // conn points to the shared connection-level flow that is | |
| // shared by all streams on that conn. It is nil for the flow | |
| // that's on the conn directly. | |
| conn *http2flow | |
| } | |
| func (f *http2flow) setConnFlow(cf *http2flow) { f.conn = cf } | |
| func (f *http2flow) available() int32 { | |
| n := f.n | |
| if f.conn != nil && f.conn.n < n { | |
| n = f.conn.n | |
| } | |
| return n | |
| } | |
| func (f *http2flow) take(n int32) { | |
| if n > f.available() { | |
| panic("internal error: took too much") | |
| } | |
| f.n -= n | |
| if f.conn != nil { | |
| f.conn.n -= n | |
| } | |
| } | |
| // add adds n bytes (positive or negative) to the flow control window. | |
| // It returns false if the sum would exceed 2^31-1. | |
| func (f *http2flow) add(n int32) bool { | |
| sum := f.n + n | |
| if (sum > n) == (f.n > 0) { | |
| f.n = sum | |
| return true | |
| } | |
| return false | |
| } | |
| const http2frameHeaderLen = 9 | |
| var http2padZeros = make([]byte, 255) // zeros for padding | |
| // A FrameType is a registered frame type as defined in | |
| // http://http2.github.io/http2-spec/#rfc.section.11.2 | |
| type http2FrameType uint8 | |
| const ( | |
| http2FrameData http2FrameType = 0x0 | |
| http2FrameHeaders http2FrameType = 0x1 | |
| http2FramePriority http2FrameType = 0x2 | |
| http2FrameRSTStream http2FrameType = 0x3 | |
| http2FrameSettings http2FrameType = 0x4 | |
| http2FramePushPromise http2FrameType = 0x5 | |
| http2FramePing http2FrameType = 0x6 | |
| http2FrameGoAway http2FrameType = 0x7 | |
| http2FrameWindowUpdate http2FrameType = 0x8 | |
| http2FrameContinuation http2FrameType = 0x9 | |
| ) | |
| var http2frameName = map[http2FrameType]string{ | |
| http2FrameData: "DATA", | |
| http2FrameHeaders: "HEADERS", | |
| http2FramePriority: "PRIORITY", | |
| http2FrameRSTStream: "RST_STREAM", | |
| http2FrameSettings: "SETTINGS", | |
| http2FramePushPromise: "PUSH_PROMISE", | |
| http2FramePing: "PING", | |
| http2FrameGoAway: "GOAWAY", | |
| http2FrameWindowUpdate: "WINDOW_UPDATE", | |
| http2FrameContinuation: "CONTINUATION", | |
| } | |
| func (t http2FrameType) String() string { | |
| if s, ok := http2frameName[t]; ok { | |
| return s | |
| } | |
| return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", uint8(t)) | |
| } | |
| // Flags is a bitmask of HTTP/2 flags. | |
| // The meaning of flags varies depending on the frame type. | |
| type http2Flags uint8 | |
| // Has reports whether f contains all (0 or more) flags in v. | |
| func (f http2Flags) Has(v http2Flags) bool { | |
| return (f & v) == v | |
| } | |
| // Frame-specific FrameHeader flag bits. | |
| const ( | |
| // Data Frame | |
| http2FlagDataEndStream http2Flags = 0x1 | |
| http2FlagDataPadded http2Flags = 0x8 | |
| // Headers Frame | |
| http2FlagHeadersEndStream http2Flags = 0x1 | |
| http2FlagHeadersEndHeaders http2Flags = 0x4 | |
| http2FlagHeadersPadded http2Flags = 0x8 | |
| http2FlagHeadersPriority http2Flags = 0x20 | |
| // Settings Frame | |
| http2FlagSettingsAck http2Flags = 0x1 | |
| // Ping Frame | |
| http2FlagPingAck http2Flags = 0x1 | |
| // Continuation Frame | |
| http2FlagContinuationEndHeaders http2Flags = 0x4 | |
| http2FlagPushPromiseEndHeaders http2Flags = 0x4 | |
| http2FlagPushPromisePadded http2Flags = 0x8 | |
| ) | |
| var http2flagName = map[http2FrameType]map[http2Flags]string{ | |
| http2FrameData: { | |
| http2FlagDataEndStream: "END_STREAM", | |
| http2FlagDataPadded: "PADDED", | |
| }, | |
| http2FrameHeaders: { | |
| http2FlagHeadersEndStream: "END_STREAM", | |
| http2FlagHeadersEndHeaders: "END_HEADERS", | |
| http2FlagHeadersPadded: "PADDED", | |
| http2FlagHeadersPriority: "PRIORITY", | |
| }, | |
| http2FrameSettings: { | |
| http2FlagSettingsAck: "ACK", | |
| }, | |
| http2FramePing: { | |
| http2FlagPingAck: "ACK", | |
| }, | |
| http2FrameContinuation: { | |
| http2FlagContinuationEndHeaders: "END_HEADERS", | |
| }, | |
| http2FramePushPromise: { | |
| http2FlagPushPromiseEndHeaders: "END_HEADERS", | |
| http2FlagPushPromisePadded: "PADDED", | |
| }, | |
| } | |
| // a frameParser parses a frame given its FrameHeader and payload | |
| // bytes. The length of payload will always equal fh.Length (which | |
| // might be 0). | |
| type http2frameParser func(fc *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) | |
| var http2frameParsers = map[http2FrameType]http2frameParser{ | |
| http2FrameData: http2parseDataFrame, | |
| http2FrameHeaders: http2parseHeadersFrame, | |
| http2FramePriority: http2parsePriorityFrame, | |
| http2FrameRSTStream: http2parseRSTStreamFrame, | |
| http2FrameSettings: http2parseSettingsFrame, | |
| http2FramePushPromise: http2parsePushPromise, | |
| http2FramePing: http2parsePingFrame, | |
| http2FrameGoAway: http2parseGoAwayFrame, | |
| http2FrameWindowUpdate: http2parseWindowUpdateFrame, | |
| http2FrameContinuation: http2parseContinuationFrame, | |
| } | |
| func http2typeFrameParser(t http2FrameType) http2frameParser { | |
| if f := http2frameParsers[t]; f != nil { | |
| return f | |
| } | |
| return http2parseUnknownFrame | |
| } | |
| // A FrameHeader is the 9 byte header of all HTTP/2 frames. | |
| // | |
| // See http://http2.github.io/http2-spec/#FrameHeader | |
| type http2FrameHeader struct { | |
| valid bool // caller can access []byte fields in the Frame | |
| // Type is the 1 byte frame type. There are ten standard frame | |
| // types, but extension frame types may be written by WriteRawFrame | |
| // and will be returned by ReadFrame (as UnknownFrame). | |
| Type http2FrameType | |
| // Flags are the 1 byte of 8 potential bit flags per frame. | |
| // They are specific to the frame type. | |
| Flags http2Flags | |
| // Length is the length of the frame, not including the 9 byte header. | |
| // The maximum size is one byte less than 16MB (uint24), but only | |
| // frames up to 16KB are allowed without peer agreement. | |
| Length uint32 | |
| // StreamID is which stream this frame is for. Certain frames | |
| // are not stream-specific, in which case this field is 0. | |
| StreamID uint32 | |
| } | |
| // Header returns h. It exists so FrameHeaders can be embedded in other | |
| // specific frame types and implement the Frame interface. | |
| func (h http2FrameHeader) Header() http2FrameHeader { return h } | |
| func (h http2FrameHeader) String() string { | |
| var buf bytes.Buffer | |
| buf.WriteString("[FrameHeader ") | |
| h.writeDebug(&buf) | |
| buf.WriteByte(']') | |
| return buf.String() | |
| } | |
| func (h http2FrameHeader) writeDebug(buf *bytes.Buffer) { | |
| buf.WriteString(h.Type.String()) | |
| if h.Flags != 0 { | |
| buf.WriteString(" flags=") | |
| set := 0 | |
| for i := uint8(0); i < 8; i++ { | |
| if h.Flags&(1<<i) == 0 { | |
| continue | |
| } | |
| set++ | |
| if set > 1 { | |
| buf.WriteByte('|') | |
| } | |
| name := http2flagName[h.Type][http2Flags(1<<i)] | |
| if name != "" { | |
| buf.WriteString(name) | |
| } else { | |
| fmt.Fprintf(buf, "0x%x", 1<<i) | |
| } | |
| } | |
| } | |
| if h.StreamID != 0 { | |
| fmt.Fprintf(buf, " stream=%d", h.StreamID) | |
| } | |
| fmt.Fprintf(buf, " len=%d", h.Length) | |
| } | |
| func (h *http2FrameHeader) checkValid() { | |
| if !h.valid { | |
| panic("Frame accessor called on non-owned Frame") | |
| } | |
| } | |
| func (h *http2FrameHeader) invalidate() { h.valid = false } | |
| // frame header bytes. | |
| // Used only by ReadFrameHeader. | |
| var http2fhBytes = sync.Pool{ | |
| New: func() interface{} { | |
| buf := make([]byte, http2frameHeaderLen) | |
| return &buf | |
| }, | |
| } | |
| // ReadFrameHeader reads 9 bytes from r and returns a FrameHeader. | |
| // Most users should use Framer.ReadFrame instead. | |
| func http2ReadFrameHeader(r io.Reader) (http2FrameHeader, error) { | |
| bufp := http2fhBytes.Get().(*[]byte) | |
| defer http2fhBytes.Put(bufp) | |
| return http2readFrameHeader(*bufp, r) | |
| } | |
| func http2readFrameHeader(buf []byte, r io.Reader) (http2FrameHeader, error) { | |
| _, err := io.ReadFull(r, buf[:http2frameHeaderLen]) | |
| if err != nil { | |
| return http2FrameHeader{}, err | |
| } | |
| return http2FrameHeader{ | |
| Length: (uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])), | |
| Type: http2FrameType(buf[3]), | |
| Flags: http2Flags(buf[4]), | |
| StreamID: binary.BigEndian.Uint32(buf[5:]) & (1<<31 - 1), | |
| valid: true, | |
| }, nil | |
| } | |
| // A Frame is the base interface implemented by all frame types. | |
| // Callers will generally type-assert the specific frame type: | |
| // *HeadersFrame, *SettingsFrame, *WindowUpdateFrame, etc. | |
| // | |
| // Frames are only valid until the next call to Framer.ReadFrame. | |
| type http2Frame interface { | |
| Header() http2FrameHeader | |
| // invalidate is called by Framer.ReadFrame to make this | |
| // frame's buffers as being invalid, since the subsequent | |
| // frame will reuse them. | |
| invalidate() | |
| } | |
| // A Framer reads and writes Frames. | |
| type http2Framer struct { | |
| r io.Reader | |
| lastFrame http2Frame | |
| errDetail error | |
| // lastHeaderStream is non-zero if the last frame was an | |
| // unfinished HEADERS/CONTINUATION. | |
| lastHeaderStream uint32 | |
| maxReadSize uint32 | |
| headerBuf [http2frameHeaderLen]byte | |
| // TODO: let getReadBuf be configurable, and use a less memory-pinning | |
| // allocator in server.go to minimize memory pinned for many idle conns. | |
| // Will probably also need to make frame invalidation have a hook too. | |
| getReadBuf func(size uint32) []byte | |
| readBuf []byte // cache for default getReadBuf | |
| maxWriteSize uint32 // zero means unlimited; TODO: implement | |
| w io.Writer | |
| wbuf []byte | |
| // AllowIllegalWrites permits the Framer's Write methods to | |
| // write frames that do not conform to the HTTP/2 spec. This | |
| // permits using the Framer to test other HTTP/2 | |
| // implementations' conformance to the spec. | |
| // If false, the Write methods will prefer to return an error | |
| // rather than comply. | |
| AllowIllegalWrites bool | |
| // AllowIllegalReads permits the Framer's ReadFrame method | |
| // to return non-compliant frames or frame orders. | |
| // This is for testing and permits using the Framer to test | |
| // other HTTP/2 implementations' conformance to the spec. | |
| // It is not compatible with ReadMetaHeaders. | |
| AllowIllegalReads bool | |
| // ReadMetaHeaders if non-nil causes ReadFrame to merge | |
| // HEADERS and CONTINUATION frames together and return | |
| // MetaHeadersFrame instead. | |
| ReadMetaHeaders *hpack.Decoder | |
| // MaxHeaderListSize is the http2 MAX_HEADER_LIST_SIZE. | |
| // It's used only if ReadMetaHeaders is set; 0 means a sane default | |
| // (currently 16MB) | |
| // If the limit is hit, MetaHeadersFrame.Truncated is set true. | |
| MaxHeaderListSize uint32 | |
| // TODO: track which type of frame & with which flags was sent | |
| // last. Then return an error (unless AllowIllegalWrites) if | |
| // we're in the middle of a header block and a | |
| // non-Continuation or Continuation on a different stream is | |
| // attempted to be written. | |
| logReads, logWrites bool | |
| debugFramer *http2Framer // only use for logging written writes | |
| debugFramerBuf *bytes.Buffer | |
| debugReadLoggerf func(string, ...interface{}) | |
| debugWriteLoggerf func(string, ...interface{}) | |
| frameCache *http2frameCache // nil if frames aren't reused (default) | |
| } | |
| func (fr *http2Framer) maxHeaderListSize() uint32 { | |
| if fr.MaxHeaderListSize == 0 { | |
| return 16 << 20 // sane default, per docs | |
| } | |
| return fr.MaxHeaderListSize | |
| } | |
| func (f *http2Framer) startWrite(ftype http2FrameType, flags http2Flags, streamID uint32) { | |
| // Write the FrameHeader. | |
| f.wbuf = append(f.wbuf[:0], | |
| 0, // 3 bytes of length, filled in in endWrite | |
| 0, | |
| 0, | |
| byte(ftype), | |
| byte(flags), | |
| byte(streamID>>24), | |
| byte(streamID>>16), | |
| byte(streamID>>8), | |
| byte(streamID)) | |
| } | |
| func (f *http2Framer) endWrite() error { | |
| // Now that we know the final size, fill in the FrameHeader in | |
| // the space previously reserved for it. Abuse append. | |
| length := len(f.wbuf) - http2frameHeaderLen | |
| if length >= (1 << 24) { | |
| return http2ErrFrameTooLarge | |
| } | |
| _ = append(f.wbuf[:0], | |
| byte(length>>16), | |
| byte(length>>8), | |
| byte(length)) | |
| if f.logWrites { | |
| f.logWrite() | |
| } | |
| n, err := f.w.Write(f.wbuf) | |
| if err == nil && n != len(f.wbuf) { | |
| err = io.ErrShortWrite | |
| } | |
| return err | |
| } | |
| func (f *http2Framer) logWrite() { | |
| if f.debugFramer == nil { | |
| f.debugFramerBuf = new(bytes.Buffer) | |
| f.debugFramer = http2NewFramer(nil, f.debugFramerBuf) | |
| f.debugFramer.logReads = false // we log it ourselves, saying "wrote" below | |
| // Let us read anything, even if we accidentally wrote it | |
| // in the wrong order: | |
| f.debugFramer.AllowIllegalReads = true | |
| } | |
| f.debugFramerBuf.Write(f.wbuf) | |
| fr, err := f.debugFramer.ReadFrame() | |
| if err != nil { | |
| f.debugWriteLoggerf("http2: Framer %p: failed to decode just-written frame", f) | |
| return | |
| } | |
| f.debugWriteLoggerf("http2: Framer %p: wrote %v", f, http2summarizeFrame(fr)) | |
| } | |
| func (f *http2Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) } | |
| func (f *http2Framer) writeBytes(v []byte) { f.wbuf = append(f.wbuf, v...) } | |
| func (f *http2Framer) writeUint16(v uint16) { f.wbuf = append(f.wbuf, byte(v>>8), byte(v)) } | |
| func (f *http2Framer) writeUint32(v uint32) { | |
| f.wbuf = append(f.wbuf, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) | |
| } | |
| const ( | |
| http2minMaxFrameSize = 1 << 14 | |
| http2maxFrameSize = 1<<24 - 1 | |
| ) | |
| // SetReuseFrames allows the Framer to reuse Frames. | |
| // If called on a Framer, Frames returned by calls to ReadFrame are only | |
| // valid until the next call to ReadFrame. | |
| func (fr *http2Framer) SetReuseFrames() { | |
| if fr.frameCache != nil { | |
| return | |
| } | |
| fr.frameCache = &http2frameCache{} | |
| } | |
| type http2frameCache struct { | |
| dataFrame http2DataFrame | |
| } | |
| func (fc *http2frameCache) getDataFrame() *http2DataFrame { | |
| if fc == nil { | |
| return &http2DataFrame{} | |
| } | |
| return &fc.dataFrame | |
| } | |
| // NewFramer returns a Framer that writes frames to w and reads them from r. | |
| func http2NewFramer(w io.Writer, r io.Reader) *http2Framer { | |
| fr := &http2Framer{ | |
| w: w, | |
| r: r, | |
| logReads: http2logFrameReads, | |
| logWrites: http2logFrameWrites, | |
| debugReadLoggerf: log.Printf, | |
| debugWriteLoggerf: log.Printf, | |
| } | |
| fr.getReadBuf = func(size uint32) []byte { | |
| if cap(fr.readBuf) >= int(size) { | |
| return fr.readBuf[:size] | |
| } | |
| fr.readBuf = make([]byte, size) | |
| return fr.readBuf | |
| } | |
| fr.SetMaxReadFrameSize(http2maxFrameSize) | |
| return fr | |
| } | |
| // SetMaxReadFrameSize sets the maximum size of a frame | |
| // that will be read by a subsequent call to ReadFrame. | |
| // It is the caller's responsibility to advertise this | |
| // limit with a SETTINGS frame. | |
| func (fr *http2Framer) SetMaxReadFrameSize(v uint32) { | |
| if v > http2maxFrameSize { | |
| v = http2maxFrameSize | |
| } | |
| fr.maxReadSize = v | |
| } | |
| // ErrorDetail returns a more detailed error of the last error | |
| // returned by Framer.ReadFrame. For instance, if ReadFrame | |
| // returns a StreamError with code PROTOCOL_ERROR, ErrorDetail | |
| // will say exactly what was invalid. ErrorDetail is not guaranteed | |
| // to return a non-nil value and like the rest of the http2 package, | |
| // its return value is not protected by an API compatibility promise. | |
| // ErrorDetail is reset after the next call to ReadFrame. | |
| func (fr *http2Framer) ErrorDetail() error { | |
| return fr.errDetail | |
| } | |
| // ErrFrameTooLarge is returned from Framer.ReadFrame when the peer | |
| // sends a frame that is larger than declared with SetMaxReadFrameSize. | |
| var http2ErrFrameTooLarge = errors.New("http2: frame too large") | |
| // terminalReadFrameError reports whether err is an unrecoverable | |
| // error from ReadFrame and no other frames should be read. | |
| func http2terminalReadFrameError(err error) bool { | |
| if _, ok := err.(http2StreamError); ok { | |
| return false | |
| } | |
| return err != nil | |
| } | |
| // ReadFrame reads a single frame. The returned Frame is only valid | |
| // until the next call to ReadFrame. | |
| // | |
| // If the frame is larger than previously set with SetMaxReadFrameSize, the | |
| // returned error is ErrFrameTooLarge. Other errors may be of type | |
| // ConnectionError, StreamError, or anything else from the underlying | |
| // reader. | |
| func (fr *http2Framer) ReadFrame() (http2Frame, error) { | |
| fr.errDetail = nil | |
| if fr.lastFrame != nil { | |
| fr.lastFrame.invalidate() | |
| } | |
| fh, err := http2readFrameHeader(fr.headerBuf[:], fr.r) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if fh.Length > fr.maxReadSize { | |
| return nil, http2ErrFrameTooLarge | |
| } | |
| payload := fr.getReadBuf(fh.Length) | |
| if _, err := io.ReadFull(fr.r, payload); err != nil { | |
| return nil, err | |
| } | |
| f, err := http2typeFrameParser(fh.Type)(fr.frameCache, fh, payload) | |
| if err != nil { | |
| if ce, ok := err.(http2connError); ok { | |
| return nil, fr.connError(ce.Code, ce.Reason) | |
| } | |
| return nil, err | |
| } | |
| if err := fr.checkFrameOrder(f); err != nil { | |
| return nil, err | |
| } | |
| if fr.logReads { | |
| fr.debugReadLoggerf("http2: Framer %p: read %v", fr, http2summarizeFrame(f)) | |
| } | |
| if fh.Type == http2FrameHeaders && fr.ReadMetaHeaders != nil { | |
| return fr.readMetaFrame(f.(*http2HeadersFrame)) | |
| } | |
| return f, nil | |
| } | |
| // connError returns ConnectionError(code) but first | |
| // stashes away a public reason to the caller can optionally relay it | |
| // to the peer before hanging up on them. This might help others debug | |
| // their implementations. | |
| func (fr *http2Framer) connError(code http2ErrCode, reason string) error { | |
| fr.errDetail = errors.New(reason) | |
| return http2ConnectionError(code) | |
| } | |
| // checkFrameOrder reports an error if f is an invalid frame to return | |
| // next from ReadFrame. Mostly it checks whether HEADERS and | |
| // CONTINUATION frames are contiguous. | |
| func (fr *http2Framer) checkFrameOrder(f http2Frame) error { | |
| last := fr.lastFrame | |
| fr.lastFrame = f | |
| if fr.AllowIllegalReads { | |
| return nil | |
| } | |
| fh := f.Header() | |
| if fr.lastHeaderStream != 0 { | |
| if fh.Type != http2FrameContinuation { | |
| return fr.connError(http2ErrCodeProtocol, | |
| fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d", | |
| fh.Type, fh.StreamID, | |
| last.Header().Type, fr.lastHeaderStream)) | |
| } | |
| if fh.StreamID != fr.lastHeaderStream { | |
| return fr.connError(http2ErrCodeProtocol, | |
| fmt.Sprintf("got CONTINUATION for stream %d; expected stream %d", | |
| fh.StreamID, fr.lastHeaderStream)) | |
| } | |
| } else if fh.Type == http2FrameContinuation { | |
| return fr.connError(http2ErrCodeProtocol, fmt.Sprintf("unexpected CONTINUATION for stream %d", fh.StreamID)) | |
| } | |
| switch fh.Type { | |
| case http2FrameHeaders, http2FrameContinuation: | |
| if fh.Flags.Has(http2FlagHeadersEndHeaders) { | |
| fr.lastHeaderStream = 0 | |
| } else { | |
| fr.lastHeaderStream = fh.StreamID | |
| } | |
| } | |
| return nil | |
| } | |
| // A DataFrame conveys arbitrary, variable-length sequences of octets | |
| // associated with a stream. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.1 | |
| type http2DataFrame struct { | |
| http2FrameHeader | |
| data []byte | |
| } | |
| func (f *http2DataFrame) StreamEnded() bool { | |
| return f.http2FrameHeader.Flags.Has(http2FlagDataEndStream) | |
| } | |
| // Data returns the frame's data octets, not including any padding | |
| // size byte or padding suffix bytes. | |
| // The caller must not retain the returned memory past the next | |
| // call to ReadFrame. | |
| func (f *http2DataFrame) Data() []byte { | |
| f.checkValid() | |
| return f.data | |
| } | |
| func http2parseDataFrame(fc *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) { | |
| if fh.StreamID == 0 { | |
| // DATA frames MUST be associated with a stream. If a | |
| // DATA frame is received whose stream identifier | |
| // field is 0x0, the recipient MUST respond with a | |
| // connection error (Section 5.4.1) of type | |
| // PROTOCOL_ERROR. | |
| return nil, http2connError{http2ErrCodeProtocol, "DATA frame with stream ID 0"} | |
| } | |
| f := fc.getDataFrame() | |
| f.http2FrameHeader = fh | |
| var padSize byte | |
| if fh.Flags.Has(http2FlagDataPadded) { | |
| var err error | |
| payload, padSize, err = http2readByte(payload) | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| if int(padSize) > len(payload) { | |
| // If the length of the padding is greater than the | |
| // length of the frame payload, the recipient MUST | |
| // treat this as a connection error. | |
| // Filed: https://github.com/http2/http2-spec/issues/610 | |
| return nil, http2connError{http2ErrCodeProtocol, "pad size larger than data payload"} | |
| } | |
| f.data = payload[:len(payload)-int(padSize)] | |
| return f, nil | |
| } | |
| var ( | |
| http2errStreamID = errors.New("invalid stream ID") | |
| http2errDepStreamID = errors.New("invalid dependent stream ID") | |
| http2errPadLength = errors.New("pad length too large") | |
| http2errPadBytes = errors.New("padding bytes must all be zeros unless AllowIllegalWrites is enabled") | |
| ) | |
| func http2validStreamIDOrZero(streamID uint32) bool { | |
| return streamID&(1<<31) == 0 | |
| } | |
| func http2validStreamID(streamID uint32) bool { | |
| return streamID != 0 && streamID&(1<<31) == 0 | |
| } | |
| // WriteData writes a DATA frame. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility not to violate the maximum frame size | |
| // and to not call other Write methods concurrently. | |
| func (f *http2Framer) WriteData(streamID uint32, endStream bool, data []byte) error { | |
| return f.WriteDataPadded(streamID, endStream, data, nil) | |
| } | |
| // WriteDataPadded writes a DATA frame with optional padding. | |
| // | |
| // If pad is nil, the padding bit is not sent. | |
| // The length of pad must not exceed 255 bytes. | |
| // The bytes of pad must all be zero, unless f.AllowIllegalWrites is set. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility not to violate the maximum frame size | |
| // and to not call other Write methods concurrently. | |
| func (f *http2Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error { | |
| if !http2validStreamID(streamID) && !f.AllowIllegalWrites { | |
| return http2errStreamID | |
| } | |
| if len(pad) > 0 { | |
| if len(pad) > 255 { | |
| return http2errPadLength | |
| } | |
| if !f.AllowIllegalWrites { | |
| for _, b := range pad { | |
| if b != 0 { | |
| // "Padding octets MUST be set to zero when sending." | |
| return http2errPadBytes | |
| } | |
| } | |
| } | |
| } | |
| var flags http2Flags | |
| if endStream { | |
| flags |= http2FlagDataEndStream | |
| } | |
| if pad != nil { | |
| flags |= http2FlagDataPadded | |
| } | |
| f.startWrite(http2FrameData, flags, streamID) | |
| if pad != nil { | |
| f.wbuf = append(f.wbuf, byte(len(pad))) | |
| } | |
| f.wbuf = append(f.wbuf, data...) | |
| f.wbuf = append(f.wbuf, pad...) | |
| return f.endWrite() | |
| } | |
| // A SettingsFrame conveys configuration parameters that affect how | |
| // endpoints communicate, such as preferences and constraints on peer | |
| // behavior. | |
| // | |
| // See http://http2.github.io/http2-spec/#SETTINGS | |
| type http2SettingsFrame struct { | |
| http2FrameHeader | |
| p []byte | |
| } | |
| func http2parseSettingsFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { | |
| if fh.Flags.Has(http2FlagSettingsAck) && fh.Length > 0 { | |
| // When this (ACK 0x1) bit is set, the payload of the | |
| // SETTINGS frame MUST be empty. Receipt of a | |
| // SETTINGS frame with the ACK flag set and a length | |
| // field value other than 0 MUST be treated as a | |
| // connection error (Section 5.4.1) of type | |
| // FRAME_SIZE_ERROR. | |
| return nil, http2ConnectionError(http2ErrCodeFrameSize) | |
| } | |
| if fh.StreamID != 0 { | |
| // SETTINGS frames always apply to a connection, | |
| // never a single stream. The stream identifier for a | |
| // SETTINGS frame MUST be zero (0x0). If an endpoint | |
| // receives a SETTINGS frame whose stream identifier | |
| // field is anything other than 0x0, the endpoint MUST | |
| // respond with a connection error (Section 5.4.1) of | |
| // type PROTOCOL_ERROR. | |
| return nil, http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if len(p)%6 != 0 { | |
| // Expecting even number of 6 byte settings. | |
| return nil, http2ConnectionError(http2ErrCodeFrameSize) | |
| } | |
| f := &http2SettingsFrame{http2FrameHeader: fh, p: p} | |
| if v, ok := f.Value(http2SettingInitialWindowSize); ok && v > (1<<31)-1 { | |
| // Values above the maximum flow control window size of 2^31 - 1 MUST | |
| // be treated as a connection error (Section 5.4.1) of type | |
| // FLOW_CONTROL_ERROR. | |
| return nil, http2ConnectionError(http2ErrCodeFlowControl) | |
| } | |
| return f, nil | |
| } | |
| func (f *http2SettingsFrame) IsAck() bool { | |
| return f.http2FrameHeader.Flags.Has(http2FlagSettingsAck) | |
| } | |
| func (f *http2SettingsFrame) Value(id http2SettingID) (v uint32, ok bool) { | |
| f.checkValid() | |
| for i := 0; i < f.NumSettings(); i++ { | |
| if s := f.Setting(i); s.ID == id { | |
| return s.Val, true | |
| } | |
| } | |
| return 0, false | |
| } | |
| // Setting returns the setting from the frame at the given 0-based index. | |
| // The index must be >= 0 and less than f.NumSettings(). | |
| func (f *http2SettingsFrame) Setting(i int) http2Setting { | |
| buf := f.p | |
| return http2Setting{ | |
| ID: http2SettingID(binary.BigEndian.Uint16(buf[i*6 : i*6+2])), | |
| Val: binary.BigEndian.Uint32(buf[i*6+2 : i*6+6]), | |
| } | |
| } | |
| func (f *http2SettingsFrame) NumSettings() int { return len(f.p) / 6 } | |
| // HasDuplicates reports whether f contains any duplicate setting IDs. | |
| func (f *http2SettingsFrame) HasDuplicates() bool { | |
| num := f.NumSettings() | |
| if num == 0 { | |
| return false | |
| } | |
| // If it's small enough (the common case), just do the n^2 | |
| // thing and avoid a map allocation. | |
| if num < 10 { | |
| for i := 0; i < num; i++ { | |
| idi := f.Setting(i).ID | |
| for j := i + 1; j < num; j++ { | |
| idj := f.Setting(j).ID | |
| if idi == idj { | |
| return true | |
| } | |
| } | |
| } | |
| return false | |
| } | |
| seen := map[http2SettingID]bool{} | |
| for i := 0; i < num; i++ { | |
| id := f.Setting(i).ID | |
| if seen[id] { | |
| return true | |
| } | |
| seen[id] = true | |
| } | |
| return false | |
| } | |
| // ForeachSetting runs fn for each setting. | |
| // It stops and returns the first error. | |
| func (f *http2SettingsFrame) ForeachSetting(fn func(http2Setting) error) error { | |
| f.checkValid() | |
| for i := 0; i < f.NumSettings(); i++ { | |
| if err := fn(f.Setting(i)); err != nil { | |
| return err | |
| } | |
| } | |
| return nil | |
| } | |
| // WriteSettings writes a SETTINGS frame with zero or more settings | |
| // specified and the ACK bit not set. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility to not call other Write methods concurrently. | |
| func (f *http2Framer) WriteSettings(settings ...http2Setting) error { | |
| f.startWrite(http2FrameSettings, 0, 0) | |
| for _, s := range settings { | |
| f.writeUint16(uint16(s.ID)) | |
| f.writeUint32(s.Val) | |
| } | |
| return f.endWrite() | |
| } | |
| // WriteSettingsAck writes an empty SETTINGS frame with the ACK bit set. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility to not call other Write methods concurrently. | |
| func (f *http2Framer) WriteSettingsAck() error { | |
| f.startWrite(http2FrameSettings, http2FlagSettingsAck, 0) | |
| return f.endWrite() | |
| } | |
| // A PingFrame is a mechanism for measuring a minimal round trip time | |
| // from the sender, as well as determining whether an idle connection | |
| // is still functional. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.7 | |
| type http2PingFrame struct { | |
| http2FrameHeader | |
| Data [8]byte | |
| } | |
| func (f *http2PingFrame) IsAck() bool { return f.Flags.Has(http2FlagPingAck) } | |
| func http2parsePingFrame(_ *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) { | |
| if len(payload) != 8 { | |
| return nil, http2ConnectionError(http2ErrCodeFrameSize) | |
| } | |
| if fh.StreamID != 0 { | |
| return nil, http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| f := &http2PingFrame{http2FrameHeader: fh} | |
| copy(f.Data[:], payload) | |
| return f, nil | |
| } | |
| func (f *http2Framer) WritePing(ack bool, data [8]byte) error { | |
| var flags http2Flags | |
| if ack { | |
| flags = http2FlagPingAck | |
| } | |
| f.startWrite(http2FramePing, flags, 0) | |
| f.writeBytes(data[:]) | |
| return f.endWrite() | |
| } | |
| // A GoAwayFrame informs the remote peer to stop creating streams on this connection. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.8 | |
| type http2GoAwayFrame struct { | |
| http2FrameHeader | |
| LastStreamID uint32 | |
| ErrCode http2ErrCode | |
| debugData []byte | |
| } | |
| // DebugData returns any debug data in the GOAWAY frame. Its contents | |
| // are not defined. | |
| // The caller must not retain the returned memory past the next | |
| // call to ReadFrame. | |
| func (f *http2GoAwayFrame) DebugData() []byte { | |
| f.checkValid() | |
| return f.debugData | |
| } | |
| func http2parseGoAwayFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { | |
| if fh.StreamID != 0 { | |
| return nil, http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if len(p) < 8 { | |
| return nil, http2ConnectionError(http2ErrCodeFrameSize) | |
| } | |
| return &http2GoAwayFrame{ | |
| http2FrameHeader: fh, | |
| LastStreamID: binary.BigEndian.Uint32(p[:4]) & (1<<31 - 1), | |
| ErrCode: http2ErrCode(binary.BigEndian.Uint32(p[4:8])), | |
| debugData: p[8:], | |
| }, nil | |
| } | |
| func (f *http2Framer) WriteGoAway(maxStreamID uint32, code http2ErrCode, debugData []byte) error { | |
| f.startWrite(http2FrameGoAway, 0, 0) | |
| f.writeUint32(maxStreamID & (1<<31 - 1)) | |
| f.writeUint32(uint32(code)) | |
| f.writeBytes(debugData) | |
| return f.endWrite() | |
| } | |
| // An UnknownFrame is the frame type returned when the frame type is unknown | |
| // or no specific frame type parser exists. | |
| type http2UnknownFrame struct { | |
| http2FrameHeader | |
| p []byte | |
| } | |
| // Payload returns the frame's payload (after the header). It is not | |
| // valid to call this method after a subsequent call to | |
| // Framer.ReadFrame, nor is it valid to retain the returned slice. | |
| // The memory is owned by the Framer and is invalidated when the next | |
| // frame is read. | |
| func (f *http2UnknownFrame) Payload() []byte { | |
| f.checkValid() | |
| return f.p | |
| } | |
| func http2parseUnknownFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { | |
| return &http2UnknownFrame{fh, p}, nil | |
| } | |
| // A WindowUpdateFrame is used to implement flow control. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.9 | |
| type http2WindowUpdateFrame struct { | |
| http2FrameHeader | |
| Increment uint32 // never read with high bit set | |
| } | |
| func http2parseWindowUpdateFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { | |
| if len(p) != 4 { | |
| return nil, http2ConnectionError(http2ErrCodeFrameSize) | |
| } | |
| inc := binary.BigEndian.Uint32(p[:4]) & 0x7fffffff // mask off high reserved bit | |
| if inc == 0 { | |
| // A receiver MUST treat the receipt of a | |
| // WINDOW_UPDATE frame with an flow control window | |
| // increment of 0 as a stream error (Section 5.4.2) of | |
| // type PROTOCOL_ERROR; errors on the connection flow | |
| // control window MUST be treated as a connection | |
| // error (Section 5.4.1). | |
| if fh.StreamID == 0 { | |
| return nil, http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol) | |
| } | |
| return &http2WindowUpdateFrame{ | |
| http2FrameHeader: fh, | |
| Increment: inc, | |
| }, nil | |
| } | |
| // WriteWindowUpdate writes a WINDOW_UPDATE frame. | |
| // The increment value must be between 1 and 2,147,483,647, inclusive. | |
| // If the Stream ID is zero, the window update applies to the | |
| // connection as a whole. | |
| func (f *http2Framer) WriteWindowUpdate(streamID, incr uint32) error { | |
| // "The legal range for the increment to the flow control window is 1 to 2^31-1 (2,147,483,647) octets." | |
| if (incr < 1 || incr > 2147483647) && !f.AllowIllegalWrites { | |
| return errors.New("illegal window increment value") | |
| } | |
| f.startWrite(http2FrameWindowUpdate, 0, streamID) | |
| f.writeUint32(incr) | |
| return f.endWrite() | |
| } | |
| // A HeadersFrame is used to open a stream and additionally carries a | |
| // header block fragment. | |
| type http2HeadersFrame struct { | |
| http2FrameHeader | |
| // Priority is set if FlagHeadersPriority is set in the FrameHeader. | |
| Priority http2PriorityParam | |
| headerFragBuf []byte // not owned | |
| } | |
| func (f *http2HeadersFrame) HeaderBlockFragment() []byte { | |
| f.checkValid() | |
| return f.headerFragBuf | |
| } | |
| func (f *http2HeadersFrame) HeadersEnded() bool { | |
| return f.http2FrameHeader.Flags.Has(http2FlagHeadersEndHeaders) | |
| } | |
| func (f *http2HeadersFrame) StreamEnded() bool { | |
| return f.http2FrameHeader.Flags.Has(http2FlagHeadersEndStream) | |
| } | |
| func (f *http2HeadersFrame) HasPriority() bool { | |
| return f.http2FrameHeader.Flags.Has(http2FlagHeadersPriority) | |
| } | |
| func http2parseHeadersFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (_ http2Frame, err error) { | |
| hf := &http2HeadersFrame{ | |
| http2FrameHeader: fh, | |
| } | |
| if fh.StreamID == 0 { | |
| // HEADERS frames MUST be associated with a stream. If a HEADERS frame | |
| // is received whose stream identifier field is 0x0, the recipient MUST | |
| // respond with a connection error (Section 5.4.1) of type | |
| // PROTOCOL_ERROR. | |
| return nil, http2connError{http2ErrCodeProtocol, "HEADERS frame with stream ID 0"} | |
| } | |
| var padLength uint8 | |
| if fh.Flags.Has(http2FlagHeadersPadded) { | |
| if p, padLength, err = http2readByte(p); err != nil { | |
| return | |
| } | |
| } | |
| if fh.Flags.Has(http2FlagHeadersPriority) { | |
| var v uint32 | |
| p, v, err = http2readUint32(p) | |
| if err != nil { | |
| return nil, err | |
| } | |
| hf.Priority.StreamDep = v & 0x7fffffff | |
| hf.Priority.Exclusive = (v != hf.Priority.StreamDep) // high bit was set | |
| p, hf.Priority.Weight, err = http2readByte(p) | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| if len(p)-int(padLength) <= 0 { | |
| return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol) | |
| } | |
| hf.headerFragBuf = p[:len(p)-int(padLength)] | |
| return hf, nil | |
| } | |
| // HeadersFrameParam are the parameters for writing a HEADERS frame. | |
| type http2HeadersFrameParam struct { | |
| // StreamID is the required Stream ID to initiate. | |
| StreamID uint32 | |
| // BlockFragment is part (or all) of a Header Block. | |
| BlockFragment []byte | |
| // EndStream indicates that the header block is the last that | |
| // the endpoint will send for the identified stream. Setting | |
| // this flag causes the stream to enter one of "half closed" | |
| // states. | |
| EndStream bool | |
| // EndHeaders indicates that this frame contains an entire | |
| // header block and is not followed by any | |
| // CONTINUATION frames. | |
| EndHeaders bool | |
| // PadLength is the optional number of bytes of zeros to add | |
| // to this frame. | |
| PadLength uint8 | |
| // Priority, if non-zero, includes stream priority information | |
| // in the HEADER frame. | |
| Priority http2PriorityParam | |
| } | |
| // WriteHeaders writes a single HEADERS frame. | |
| // | |
| // This is a low-level header writing method. Encoding headers and | |
| // splitting them into any necessary CONTINUATION frames is handled | |
| // elsewhere. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility to not call other Write methods concurrently. | |
| func (f *http2Framer) WriteHeaders(p http2HeadersFrameParam) error { | |
| if !http2validStreamID(p.StreamID) && !f.AllowIllegalWrites { | |
| return http2errStreamID | |
| } | |
| var flags http2Flags | |
| if p.PadLength != 0 { | |
| flags |= http2FlagHeadersPadded | |
| } | |
| if p.EndStream { | |
| flags |= http2FlagHeadersEndStream | |
| } | |
| if p.EndHeaders { | |
| flags |= http2FlagHeadersEndHeaders | |
| } | |
| if !p.Priority.IsZero() { | |
| flags |= http2FlagHeadersPriority | |
| } | |
| f.startWrite(http2FrameHeaders, flags, p.StreamID) | |
| if p.PadLength != 0 { | |
| f.writeByte(p.PadLength) | |
| } | |
| if !p.Priority.IsZero() { | |
| v := p.Priority.StreamDep | |
| if !http2validStreamIDOrZero(v) && !f.AllowIllegalWrites { | |
| return http2errDepStreamID | |
| } | |
| if p.Priority.Exclusive { | |
| v |= 1 << 31 | |
| } | |
| f.writeUint32(v) | |
| f.writeByte(p.Priority.Weight) | |
| } | |
| f.wbuf = append(f.wbuf, p.BlockFragment...) | |
| f.wbuf = append(f.wbuf, http2padZeros[:p.PadLength]...) | |
| return f.endWrite() | |
| } | |
| // A PriorityFrame specifies the sender-advised priority of a stream. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.3 | |
| type http2PriorityFrame struct { | |
| http2FrameHeader | |
| http2PriorityParam | |
| } | |
| // PriorityParam are the stream prioritzation parameters. | |
| type http2PriorityParam struct { | |
| // StreamDep is a 31-bit stream identifier for the | |
| // stream that this stream depends on. Zero means no | |
| // dependency. | |
| StreamDep uint32 | |
| // Exclusive is whether the dependency is exclusive. | |
| Exclusive bool | |
| // Weight is the stream's zero-indexed weight. It should be | |
| // set together with StreamDep, or neither should be set. Per | |
| // the spec, "Add one to the value to obtain a weight between | |
| // 1 and 256." | |
| Weight uint8 | |
| } | |
| func (p http2PriorityParam) IsZero() bool { | |
| return p == http2PriorityParam{} | |
| } | |
| func http2parsePriorityFrame(_ *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) { | |
| if fh.StreamID == 0 { | |
| return nil, http2connError{http2ErrCodeProtocol, "PRIORITY frame with stream ID 0"} | |
| } | |
| if len(payload) != 5 { | |
| return nil, http2connError{http2ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len(payload))} | |
| } | |
| v := binary.BigEndian.Uint32(payload[:4]) | |
| streamID := v & 0x7fffffff // mask off high bit | |
| return &http2PriorityFrame{ | |
| http2FrameHeader: fh, | |
| http2PriorityParam: http2PriorityParam{ | |
| Weight: payload[4], | |
| StreamDep: streamID, | |
| Exclusive: streamID != v, // was high bit set? | |
| }, | |
| }, nil | |
| } | |
| // WritePriority writes a PRIORITY frame. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility to not call other Write methods concurrently. | |
| func (f *http2Framer) WritePriority(streamID uint32, p http2PriorityParam) error { | |
| if !http2validStreamID(streamID) && !f.AllowIllegalWrites { | |
| return http2errStreamID | |
| } | |
| if !http2validStreamIDOrZero(p.StreamDep) { | |
| return http2errDepStreamID | |
| } | |
| f.startWrite(http2FramePriority, 0, streamID) | |
| v := p.StreamDep | |
| if p.Exclusive { | |
| v |= 1 << 31 | |
| } | |
| f.writeUint32(v) | |
| f.writeByte(p.Weight) | |
| return f.endWrite() | |
| } | |
| // A RSTStreamFrame allows for abnormal termination of a stream. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.4 | |
| type http2RSTStreamFrame struct { | |
| http2FrameHeader | |
| ErrCode http2ErrCode | |
| } | |
| func http2parseRSTStreamFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { | |
| if len(p) != 4 { | |
| return nil, http2ConnectionError(http2ErrCodeFrameSize) | |
| } | |
| if fh.StreamID == 0 { | |
| return nil, http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| return &http2RSTStreamFrame{fh, http2ErrCode(binary.BigEndian.Uint32(p[:4]))}, nil | |
| } | |
| // WriteRSTStream writes a RST_STREAM frame. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility to not call other Write methods concurrently. | |
| func (f *http2Framer) WriteRSTStream(streamID uint32, code http2ErrCode) error { | |
| if !http2validStreamID(streamID) && !f.AllowIllegalWrites { | |
| return http2errStreamID | |
| } | |
| f.startWrite(http2FrameRSTStream, 0, streamID) | |
| f.writeUint32(uint32(code)) | |
| return f.endWrite() | |
| } | |
| // A ContinuationFrame is used to continue a sequence of header block fragments. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.10 | |
| type http2ContinuationFrame struct { | |
| http2FrameHeader | |
| headerFragBuf []byte | |
| } | |
| func http2parseContinuationFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { | |
| if fh.StreamID == 0 { | |
| return nil, http2connError{http2ErrCodeProtocol, "CONTINUATION frame with stream ID 0"} | |
| } | |
| return &http2ContinuationFrame{fh, p}, nil | |
| } | |
| func (f *http2ContinuationFrame) HeaderBlockFragment() []byte { | |
| f.checkValid() | |
| return f.headerFragBuf | |
| } | |
| func (f *http2ContinuationFrame) HeadersEnded() bool { | |
| return f.http2FrameHeader.Flags.Has(http2FlagContinuationEndHeaders) | |
| } | |
| // WriteContinuation writes a CONTINUATION frame. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility to not call other Write methods concurrently. | |
| func (f *http2Framer) WriteContinuation(streamID uint32, endHeaders bool, headerBlockFragment []byte) error { | |
| if !http2validStreamID(streamID) && !f.AllowIllegalWrites { | |
| return http2errStreamID | |
| } | |
| var flags http2Flags | |
| if endHeaders { | |
| flags |= http2FlagContinuationEndHeaders | |
| } | |
| f.startWrite(http2FrameContinuation, flags, streamID) | |
| f.wbuf = append(f.wbuf, headerBlockFragment...) | |
| return f.endWrite() | |
| } | |
| // A PushPromiseFrame is used to initiate a server stream. | |
| // See http://http2.github.io/http2-spec/#rfc.section.6.6 | |
| type http2PushPromiseFrame struct { | |
| http2FrameHeader | |
| PromiseID uint32 | |
| headerFragBuf []byte // not owned | |
| } | |
| func (f *http2PushPromiseFrame) HeaderBlockFragment() []byte { | |
| f.checkValid() | |
| return f.headerFragBuf | |
| } | |
| func (f *http2PushPromiseFrame) HeadersEnded() bool { | |
| return f.http2FrameHeader.Flags.Has(http2FlagPushPromiseEndHeaders) | |
| } | |
| func http2parsePushPromise(_ *http2frameCache, fh http2FrameHeader, p []byte) (_ http2Frame, err error) { | |
| pp := &http2PushPromiseFrame{ | |
| http2FrameHeader: fh, | |
| } | |
| if pp.StreamID == 0 { | |
| // PUSH_PROMISE frames MUST be associated with an existing, | |
| // peer-initiated stream. The stream identifier of a | |
| // PUSH_PROMISE frame indicates the stream it is associated | |
| // with. If the stream identifier field specifies the value | |
| // 0x0, a recipient MUST respond with a connection error | |
| // (Section 5.4.1) of type PROTOCOL_ERROR. | |
| return nil, http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| // The PUSH_PROMISE frame includes optional padding. | |
| // Padding fields and flags are identical to those defined for DATA frames | |
| var padLength uint8 | |
| if fh.Flags.Has(http2FlagPushPromisePadded) { | |
| if p, padLength, err = http2readByte(p); err != nil { | |
| return | |
| } | |
| } | |
| p, pp.PromiseID, err = http2readUint32(p) | |
| if err != nil { | |
| return | |
| } | |
| pp.PromiseID = pp.PromiseID & (1<<31 - 1) | |
| if int(padLength) > len(p) { | |
| // like the DATA frame, error out if padding is longer than the body. | |
| return nil, http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| pp.headerFragBuf = p[:len(p)-int(padLength)] | |
| return pp, nil | |
| } | |
| // PushPromiseParam are the parameters for writing a PUSH_PROMISE frame. | |
| type http2PushPromiseParam struct { | |
| // StreamID is the required Stream ID to initiate. | |
| StreamID uint32 | |
| // PromiseID is the required Stream ID which this | |
| // Push Promises | |
| PromiseID uint32 | |
| // BlockFragment is part (or all) of a Header Block. | |
| BlockFragment []byte | |
| // EndHeaders indicates that this frame contains an entire | |
| // header block and is not followed by any | |
| // CONTINUATION frames. | |
| EndHeaders bool | |
| // PadLength is the optional number of bytes of zeros to add | |
| // to this frame. | |
| PadLength uint8 | |
| } | |
| // WritePushPromise writes a single PushPromise Frame. | |
| // | |
| // As with Header Frames, This is the low level call for writing | |
| // individual frames. Continuation frames are handled elsewhere. | |
| // | |
| // It will perform exactly one Write to the underlying Writer. | |
| // It is the caller's responsibility to not call other Write methods concurrently. | |
| func (f *http2Framer) WritePushPromise(p http2PushPromiseParam) error { | |
| if !http2validStreamID(p.StreamID) && !f.AllowIllegalWrites { | |
| return http2errStreamID | |
| } | |
| var flags http2Flags | |
| if p.PadLength != 0 { | |
| flags |= http2FlagPushPromisePadded | |
| } | |
| if p.EndHeaders { | |
| flags |= http2FlagPushPromiseEndHeaders | |
| } | |
| f.startWrite(http2FramePushPromise, flags, p.StreamID) | |
| if p.PadLength != 0 { | |
| f.writeByte(p.PadLength) | |
| } | |
| if !http2validStreamID(p.PromiseID) && !f.AllowIllegalWrites { | |
| return http2errStreamID | |
| } | |
| f.writeUint32(p.PromiseID) | |
| f.wbuf = append(f.wbuf, p.BlockFragment...) | |
| f.wbuf = append(f.wbuf, http2padZeros[:p.PadLength]...) | |
| return f.endWrite() | |
| } | |
| // WriteRawFrame writes a raw frame. This can be used to write | |
| // extension frames unknown to this package. | |
| func (f *http2Framer) WriteRawFrame(t http2FrameType, flags http2Flags, streamID uint32, payload []byte) error { | |
| f.startWrite(t, flags, streamID) | |
| f.writeBytes(payload) | |
| return f.endWrite() | |
| } | |
| func http2readByte(p []byte) (remain []byte, b byte, err error) { | |
| if len(p) == 0 { | |
| return nil, 0, io.ErrUnexpectedEOF | |
| } | |
| return p[1:], p[0], nil | |
| } | |
| func http2readUint32(p []byte) (remain []byte, v uint32, err error) { | |
| if len(p) < 4 { | |
| return nil, 0, io.ErrUnexpectedEOF | |
| } | |
| return p[4:], binary.BigEndian.Uint32(p[:4]), nil | |
| } | |
| type http2streamEnder interface { | |
| StreamEnded() bool | |
| } | |
| type http2headersEnder interface { | |
| HeadersEnded() bool | |
| } | |
| type http2headersOrContinuation interface { | |
| http2headersEnder | |
| HeaderBlockFragment() []byte | |
| } | |
| // A MetaHeadersFrame is the representation of one HEADERS frame and | |
| // zero or more contiguous CONTINUATION frames and the decoding of | |
| // their HPACK-encoded contents. | |
| // | |
| // This type of frame does not appear on the wire and is only returned | |
| // by the Framer when Framer.ReadMetaHeaders is set. | |
| type http2MetaHeadersFrame struct { | |
| *http2HeadersFrame | |
| // Fields are the fields contained in the HEADERS and | |
| // CONTINUATION frames. The underlying slice is owned by the | |
| // Framer and must not be retained after the next call to | |
| // ReadFrame. | |
| // | |
| // Fields are guaranteed to be in the correct http2 order and | |
| // not have unknown pseudo header fields or invalid header | |
| // field names or values. Required pseudo header fields may be | |
| // missing, however. Use the MetaHeadersFrame.Pseudo accessor | |
| // method access pseudo headers. | |
| Fields []hpack.HeaderField | |
| // Truncated is whether the max header list size limit was hit | |
| // and Fields is incomplete. The hpack decoder state is still | |
| // valid, however. | |
| Truncated bool | |
| } | |
| // PseudoValue returns the given pseudo header field's value. | |
| // The provided pseudo field should not contain the leading colon. | |
| func (mh *http2MetaHeadersFrame) PseudoValue(pseudo string) string { | |
| for _, hf := range mh.Fields { | |
| if !hf.IsPseudo() { | |
| return "" | |
| } | |
| if hf.Name[1:] == pseudo { | |
| return hf.Value | |
| } | |
| } | |
| return "" | |
| } | |
| // RegularFields returns the regular (non-pseudo) header fields of mh. | |
| // The caller does not own the returned slice. | |
| func (mh *http2MetaHeadersFrame) RegularFields() []hpack.HeaderField { | |
| for i, hf := range mh.Fields { | |
| if !hf.IsPseudo() { | |
| return mh.Fields[i:] | |
| } | |
| } | |
| return nil | |
| } | |
| // PseudoFields returns the pseudo header fields of mh. | |
| // The caller does not own the returned slice. | |
| func (mh *http2MetaHeadersFrame) PseudoFields() []hpack.HeaderField { | |
| for i, hf := range mh.Fields { | |
| if !hf.IsPseudo() { | |
| return mh.Fields[:i] | |
| } | |
| } | |
| return mh.Fields | |
| } | |
| func (mh *http2MetaHeadersFrame) checkPseudos() error { | |
| var isRequest, isResponse bool | |
| pf := mh.PseudoFields() | |
| for i, hf := range pf { | |
| switch hf.Name { | |
| case ":method", ":path", ":scheme", ":authority": | |
| isRequest = true | |
| case ":status": | |
| isResponse = true | |
| default: | |
| return http2pseudoHeaderError(hf.Name) | |
| } | |
| // Check for duplicates. | |
| // This would be a bad algorithm, but N is 4. | |
| // And this doesn't allocate. | |
| for _, hf2 := range pf[:i] { | |
| if hf.Name == hf2.Name { | |
| return http2duplicatePseudoHeaderError(hf.Name) | |
| } | |
| } | |
| } | |
| if isRequest && isResponse { | |
| return http2errMixPseudoHeaderTypes | |
| } | |
| return nil | |
| } | |
| func (fr *http2Framer) maxHeaderStringLen() int { | |
| v := fr.maxHeaderListSize() | |
| if uint32(int(v)) == v { | |
| return int(v) | |
| } | |
| // They had a crazy big number for MaxHeaderBytes anyway, | |
| // so give them unlimited header lengths: | |
| return 0 | |
| } | |
| // readMetaFrame returns 0 or more CONTINUATION frames from fr and | |
| // merge them into the provided hf and returns a MetaHeadersFrame | |
| // with the decoded hpack values. | |
| func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFrame, error) { | |
| if fr.AllowIllegalReads { | |
| return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders") | |
| } | |
| mh := &http2MetaHeadersFrame{ | |
| http2HeadersFrame: hf, | |
| } | |
| var remainSize = fr.maxHeaderListSize() | |
| var sawRegular bool | |
| var invalid error // pseudo header field errors | |
| hdec := fr.ReadMetaHeaders | |
| hdec.SetEmitEnabled(true) | |
| hdec.SetMaxStringLength(fr.maxHeaderStringLen()) | |
| hdec.SetEmitFunc(func(hf hpack.HeaderField) { | |
| if http2VerboseLogs && fr.logReads { | |
| fr.debugReadLoggerf("http2: decoded hpack field %+v", hf) | |
| } | |
| if !httpguts.ValidHeaderFieldValue(hf.Value) { | |
| invalid = http2headerFieldValueError(hf.Value) | |
| } | |
| isPseudo := strings.HasPrefix(hf.Name, ":") | |
| if isPseudo { | |
| if sawRegular { | |
| invalid = http2errPseudoAfterRegular | |
| } | |
| } else { | |
| sawRegular = true | |
| if !http2validWireHeaderFieldName(hf.Name) { | |
| invalid = http2headerFieldNameError(hf.Name) | |
| } | |
| } | |
| if invalid != nil { | |
| hdec.SetEmitEnabled(false) | |
| return | |
| } | |
| size := hf.Size() | |
| if size > remainSize { | |
| hdec.SetEmitEnabled(false) | |
| mh.Truncated = true | |
| return | |
| } | |
| remainSize -= size | |
| mh.Fields = append(mh.Fields, hf) | |
| }) | |
| // Lose reference to MetaHeadersFrame: | |
| defer hdec.SetEmitFunc(func(hf hpack.HeaderField) {}) | |
| var hc http2headersOrContinuation = hf | |
| for { | |
| frag := hc.HeaderBlockFragment() | |
| if _, err := hdec.Write(frag); err != nil { | |
| return nil, http2ConnectionError(http2ErrCodeCompression) | |
| } | |
| if hc.HeadersEnded() { | |
| break | |
| } | |
| if f, err := fr.ReadFrame(); err != nil { | |
| return nil, err | |
| } else { | |
| hc = f.(*http2ContinuationFrame) // guaranteed by checkFrameOrder | |
| } | |
| } | |
| mh.http2HeadersFrame.headerFragBuf = nil | |
| mh.http2HeadersFrame.invalidate() | |
| if err := hdec.Close(); err != nil { | |
| return nil, http2ConnectionError(http2ErrCodeCompression) | |
| } | |
| if invalid != nil { | |
| fr.errDetail = invalid | |
| if http2VerboseLogs { | |
| log.Printf("http2: invalid header: %v", invalid) | |
| } | |
| return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol, invalid} | |
| } | |
| if err := mh.checkPseudos(); err != nil { | |
| fr.errDetail = err | |
| if http2VerboseLogs { | |
| log.Printf("http2: invalid pseudo headers: %v", err) | |
| } | |
| return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol, err} | |
| } | |
| return mh, nil | |
| } | |
| func http2summarizeFrame(f http2Frame) string { | |
| var buf bytes.Buffer | |
| f.Header().writeDebug(&buf) | |
| switch f := f.(type) { | |
| case *http2SettingsFrame: | |
| n := 0 | |
| f.ForeachSetting(func(s http2Setting) error { | |
| n++ | |
| if n == 1 { | |
| buf.WriteString(", settings:") | |
| } | |
| fmt.Fprintf(&buf, " %v=%v,", s.ID, s.Val) | |
| return nil | |
| }) | |
| if n > 0 { | |
| buf.Truncate(buf.Len() - 1) // remove trailing comma | |
| } | |
| case *http2DataFrame: | |
| data := f.Data() | |
| const max = 256 | |
| if len(data) > max { | |
| data = data[:max] | |
| } | |
| fmt.Fprintf(&buf, " data=%q", data) | |
| if len(f.Data()) > max { | |
| fmt.Fprintf(&buf, " (%d bytes omitted)", len(f.Data())-max) | |
| } | |
| case *http2WindowUpdateFrame: | |
| if f.StreamID == 0 { | |
| buf.WriteString(" (conn)") | |
| } | |
| fmt.Fprintf(&buf, " incr=%v", f.Increment) | |
| case *http2PingFrame: | |
| fmt.Fprintf(&buf, " ping=%q", f.Data[:]) | |
| case *http2GoAwayFrame: | |
| fmt.Fprintf(&buf, " LastStreamID=%v ErrCode=%v Debug=%q", | |
| f.LastStreamID, f.ErrCode, f.debugData) | |
| case *http2RSTStreamFrame: | |
| fmt.Fprintf(&buf, " ErrCode=%v", f.ErrCode) | |
| } | |
| return buf.String() | |
| } | |
| func http2traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { | |
| return trace != nil && trace.WroteHeaderField != nil | |
| } | |
| func http2traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { | |
| if trace != nil && trace.WroteHeaderField != nil { | |
| trace.WroteHeaderField(k, []string{v}) | |
| } | |
| } | |
| func http2traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { | |
| if trace != nil { | |
| return trace.Got1xxResponse | |
| } | |
| return nil | |
| } | |
| var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" | |
| type http2goroutineLock uint64 | |
| func http2newGoroutineLock() http2goroutineLock { | |
| if !http2DebugGoroutines { | |
| return 0 | |
| } | |
| return http2goroutineLock(http2curGoroutineID()) | |
| } | |
| func (g http2goroutineLock) check() { | |
| if !http2DebugGoroutines { | |
| return | |
| } | |
| if http2curGoroutineID() != uint64(g) { | |
| panic("running on the wrong goroutine") | |
| } | |
| } | |
| func (g http2goroutineLock) checkNotOn() { | |
| if !http2DebugGoroutines { | |
| return | |
| } | |
| if http2curGoroutineID() == uint64(g) { | |
| panic("running on the wrong goroutine") | |
| } | |
| } | |
| var http2goroutineSpace = []byte("goroutine ") | |
| func http2curGoroutineID() uint64 { | |
| bp := http2littleBuf.Get().(*[]byte) | |
| defer http2littleBuf.Put(bp) | |
| b := *bp | |
| b = b[:runtime.Stack(b, false)] | |
| // Parse the 4707 out of "goroutine 4707 [" | |
| b = bytes.TrimPrefix(b, http2goroutineSpace) | |
| i := bytes.IndexByte(b, ' ') | |
| if i < 0 { | |
| panic(fmt.Sprintf("No space found in %q", b)) | |
| } | |
| b = b[:i] | |
| n, err := http2parseUintBytes(b, 10, 64) | |
| if err != nil { | |
| panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) | |
| } | |
| return n | |
| } | |
| var http2littleBuf = sync.Pool{ | |
| New: func() interface{} { | |
| buf := make([]byte, 64) | |
| return &buf | |
| }, | |
| } | |
| // parseUintBytes is like strconv.ParseUint, but using a []byte. | |
| func http2parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { | |
| var cutoff, maxVal uint64 | |
| if bitSize == 0 { | |
| bitSize = int(strconv.IntSize) | |
| } | |
| s0 := s | |
| switch { | |
| case len(s) < 1: | |
| err = strconv.ErrSyntax | |
| goto Error | |
| case 2 <= base && base <= 36: | |
| // valid base; nothing to do | |
| case base == 0: | |
| // Look for octal, hex prefix. | |
| switch { | |
| case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): | |
| base = 16 | |
| s = s[2:] | |
| if len(s) < 1 { | |
| err = strconv.ErrSyntax | |
| goto Error | |
| } | |
| case s[0] == '0': | |
| base = 8 | |
| default: | |
| base = 10 | |
| } | |
| default: | |
| err = errors.New("invalid base " + strconv.Itoa(base)) | |
| goto Error | |
| } | |
| n = 0 | |
| cutoff = http2cutoff64(base) | |
| maxVal = 1<<uint(bitSize) - 1 | |
| for i := 0; i < len(s); i++ { | |
| var v byte | |
| d := s[i] | |
| switch { | |
| case '0' <= d && d <= '9': | |
| v = d - '0' | |
| case 'a' <= d && d <= 'z': | |
| v = d - 'a' + 10 | |
| case 'A' <= d && d <= 'Z': | |
| v = d - 'A' + 10 | |
| default: | |
| n = 0 | |
| err = strconv.ErrSyntax | |
| goto Error | |
| } | |
| if int(v) >= base { | |
| n = 0 | |
| err = strconv.ErrSyntax | |
| goto Error | |
| } | |
| if n >= cutoff { | |
| // n*base overflows | |
| n = 1<<64 - 1 | |
| err = strconv.ErrRange | |
| goto Error | |
| } | |
| n *= uint64(base) | |
| n1 := n + uint64(v) | |
| if n1 < n || n1 > maxVal { | |
| // n+v overflows | |
| n = 1<<64 - 1 | |
| err = strconv.ErrRange | |
| goto Error | |
| } | |
| n = n1 | |
| } | |
| return n, nil | |
| Error: | |
| return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} | |
| } | |
| // Return the first number n such that n*base >= 1<<64. | |
| func http2cutoff64(base int) uint64 { | |
| if base < 2 { | |
| return 0 | |
| } | |
| return (1<<64-1)/uint64(base) + 1 | |
| } | |
| var ( | |
| http2commonBuildOnce sync.Once | |
| http2commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case | |
| http2commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case | |
| ) | |
| func http2buildCommonHeaderMapsOnce() { | |
| http2commonBuildOnce.Do(http2buildCommonHeaderMaps) | |
| } | |
| func http2buildCommonHeaderMaps() { | |
| common := []string{ | |
| "accept", | |
| "accept-charset", | |
| "accept-encoding", | |
| "accept-language", | |
| "accept-ranges", | |
| "age", | |
| "access-control-allow-origin", | |
| "allow", | |
| "authorization", | |
| "cache-control", | |
| "content-disposition", | |
| "content-encoding", | |
| "content-language", | |
| "content-length", | |
| "content-location", | |
| "content-range", | |
| "content-type", | |
| "cookie", | |
| "date", | |
| "etag", | |
| "expect", | |
| "expires", | |
| "from", | |
| "host", | |
| "if-match", | |
| "if-modified-since", | |
| "if-none-match", | |
| "if-unmodified-since", | |
| "last-modified", | |
| "link", | |
| "location", | |
| "max-forwards", | |
| "proxy-authenticate", | |
| "proxy-authorization", | |
| "range", | |
| "referer", | |
| "refresh", | |
| "retry-after", | |
| "server", | |
| "set-cookie", | |
| "strict-transport-security", | |
| "trailer", | |
| "transfer-encoding", | |
| "user-agent", | |
| "vary", | |
| "via", | |
| "www-authenticate", | |
| } | |
| http2commonLowerHeader = make(map[string]string, len(common)) | |
| http2commonCanonHeader = make(map[string]string, len(common)) | |
| for _, v := range common { | |
| chk := CanonicalHeaderKey(v) | |
| http2commonLowerHeader[chk] = v | |
| http2commonCanonHeader[v] = chk | |
| } | |
| } | |
| func http2lowerHeader(v string) string { | |
| http2buildCommonHeaderMapsOnce() | |
| if s, ok := http2commonLowerHeader[v]; ok { | |
| return s | |
| } | |
| return strings.ToLower(v) | |
| } | |
| var ( | |
| http2VerboseLogs bool | |
| http2logFrameWrites bool | |
| http2logFrameReads bool | |
| http2inTests bool | |
| ) | |
| func init() { | |
| e := os.Getenv("GODEBUG") | |
| if strings.Contains(e, "http2debug=1") { | |
| http2VerboseLogs = true | |
| } | |
| if strings.Contains(e, "http2debug=2") { | |
| http2VerboseLogs = true | |
| http2logFrameWrites = true | |
| http2logFrameReads = true | |
| } | |
| } | |
| const ( | |
| // ClientPreface is the string that must be sent by new | |
| // connections from clients. | |
| http2ClientPreface = "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 | |
| http2initialMaxFrameSize = 16384 | |
| // NextProtoTLS is the NPN/ALPN protocol negotiated during | |
| // HTTP/2's TLS setup. | |
| http2NextProtoTLS = "h2" | |
| // http://http2.github.io/http2-spec/#SettingValues | |
| http2initialHeaderTableSize = 4096 | |
| http2initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size | |
| http2defaultMaxReadFrameSize = 1 << 20 | |
| ) | |
| var ( | |
| http2clientPreface = []byte(http2ClientPreface) | |
| ) | |
| type http2streamState 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 ( | |
| http2stateIdle http2streamState = iota | |
| http2stateOpen | |
| http2stateHalfClosedLocal | |
| http2stateHalfClosedRemote | |
| http2stateClosed | |
| ) | |
| var http2stateName = [...]string{ | |
| http2stateIdle: "Idle", | |
| http2stateOpen: "Open", | |
| http2stateHalfClosedLocal: "HalfClosedLocal", | |
| http2stateHalfClosedRemote: "HalfClosedRemote", | |
| http2stateClosed: "Closed", | |
| } | |
| func (st http2streamState) String() string { | |
| return http2stateName[st] | |
| } | |
| // Setting is a setting parameter: which setting it is, and its value. | |
| type http2Setting struct { | |
| // ID is which setting is being set. | |
| // See http://http2.github.io/http2-spec/#SettingValues | |
| ID http2SettingID | |
| // Val is the value. | |
| Val uint32 | |
| } | |
| func (s http2Setting) String() string { | |
| return fmt.Sprintf("[%v = %d]", s.ID, s.Val) | |
| } | |
| // Valid reports whether the setting is valid. | |
| func (s http2Setting) Valid() error { | |
| // Limits and error codes from 6.5.2 Defined SETTINGS Parameters | |
| switch s.ID { | |
| case http2SettingEnablePush: | |
| if s.Val != 1 && s.Val != 0 { | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| case http2SettingInitialWindowSize: | |
| if s.Val > 1<<31-1 { | |
| return http2ConnectionError(http2ErrCodeFlowControl) | |
| } | |
| case http2SettingMaxFrameSize: | |
| if s.Val < 16384 || s.Val > 1<<24-1 { | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| } | |
| return nil | |
| } | |
| // A SettingID is an HTTP/2 setting as defined in | |
| // http://http2.github.io/http2-spec/#iana-settings | |
| type http2SettingID uint16 | |
| const ( | |
| http2SettingHeaderTableSize http2SettingID = 0x1 | |
| http2SettingEnablePush http2SettingID = 0x2 | |
| http2SettingMaxConcurrentStreams http2SettingID = 0x3 | |
| http2SettingInitialWindowSize http2SettingID = 0x4 | |
| http2SettingMaxFrameSize http2SettingID = 0x5 | |
| http2SettingMaxHeaderListSize http2SettingID = 0x6 | |
| ) | |
| var http2settingName = map[http2SettingID]string{ | |
| http2SettingHeaderTableSize: "HEADER_TABLE_SIZE", | |
| http2SettingEnablePush: "ENABLE_PUSH", | |
| http2SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", | |
| http2SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", | |
| http2SettingMaxFrameSize: "MAX_FRAME_SIZE", | |
| http2SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", | |
| } | |
| func (s http2SettingID) String() string { | |
| if v, ok := http2settingName[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 http2validWireHeaderFieldName(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 http2httpCodeString(code int) string { | |
| switch code { | |
| case 200: | |
| return "200" | |
| case 404: | |
| return "404" | |
| } | |
| return strconv.Itoa(code) | |
| } | |
| // from pkg io | |
| type http2stringWriter interface { | |
| WriteString(s string) (n int, err error) | |
| } | |
| // A gate lets two goroutines coordinate their activities. | |
| type http2gate chan struct{} | |
| func (g http2gate) Done() { g <- struct{}{} } | |
| func (g http2gate) Wait() { <-g } | |
| // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed). | |
| type http2closeWaiter 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 *http2closeWaiter) Init() { | |
| *cw = make(chan struct{}) | |
| } | |
| // Close marks the closeWaiter as closed and unblocks any waiters. | |
| func (cw http2closeWaiter) Close() { | |
| close(cw) | |
| } | |
| // Wait waits for the closeWaiter to become closed. | |
| func (cw http2closeWaiter) 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 http2bufferedWriter struct { | |
| _ http2incomparable | |
| w io.Writer // immutable | |
| bw *bufio.Writer // non-nil when data is buffered | |
| } | |
| func http2newBufferedWriter(w io.Writer) *http2bufferedWriter { | |
| return &http2bufferedWriter{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 http2bufWriterPoolBufferSize = 4 << 10 | |
| var http2bufWriterPool = sync.Pool{ | |
| New: func() interface{} { | |
| return bufio.NewWriterSize(nil, http2bufWriterPoolBufferSize) | |
| }, | |
| } | |
| func (w *http2bufferedWriter) Available() int { | |
| if w.bw == nil { | |
| return http2bufWriterPoolBufferSize | |
| } | |
| return w.bw.Available() | |
| } | |
| func (w *http2bufferedWriter) Write(p []byte) (n int, err error) { | |
| if w.bw == nil { | |
| bw := http2bufWriterPool.Get().(*bufio.Writer) | |
| bw.Reset(w.w) | |
| w.bw = bw | |
| } | |
| return w.bw.Write(p) | |
| } | |
| func (w *http2bufferedWriter) Flush() error { | |
| bw := w.bw | |
| if bw == nil { | |
| return nil | |
| } | |
| err := bw.Flush() | |
| bw.Reset(nil) | |
| http2bufWriterPool.Put(bw) | |
| w.bw = nil | |
| return err | |
| } | |
| func http2mustUint31(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 http2bodyAllowedForStatus(status int) bool { | |
| switch { | |
| case status >= 100 && status <= 199: | |
| return false | |
| case status == 204: | |
| return false | |
| case status == 304: | |
| return false | |
| } | |
| return true | |
| } | |
| type http2httpError struct { | |
| _ http2incomparable | |
| msg string | |
| timeout bool | |
| } | |
| func (e *http2httpError) Error() string { return e.msg } | |
| func (e *http2httpError) Timeout() bool { return e.timeout } | |
| func (e *http2httpError) Temporary() bool { return true } | |
| var http2errTimeout error = &http2httpError{msg: "http2: timeout awaiting response headers", timeout: true} | |
| type http2connectionStater interface { | |
| ConnectionState() tls.ConnectionState | |
| } | |
| var http2sorterPool = sync.Pool{New: func() interface{} { return new(http2sorter) }} | |
| type http2sorter struct { | |
| v []string // owned by sorter | |
| } | |
| func (s *http2sorter) Len() int { return len(s.v) } | |
| func (s *http2sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] } | |
| func (s *http2sorter) 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 *http2sorter) Keys(h Header) []string { | |
| keys := s.v[:0] | |
| for k := range h { | |
| keys = append(keys, k) | |
| } | |
| s.v = keys | |
| sort.Sort(s) | |
| return keys | |
| } | |
| func (s *http2sorter) 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 http2validPseudoPath(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 http2incomparable [0]func() | |
| // pipe is a goroutine-safe io.Reader/io.Writer pair. It's like | |
| // io.Pipe except there are no PipeReader/PipeWriter halves, and the | |
| // underlying buffer is an interface. (io.Pipe is always unbuffered) | |
| type http2pipe struct { | |
| mu sync.Mutex | |
| c sync.Cond // c.L lazily initialized to &p.mu | |
| b http2pipeBuffer // nil when done reading | |
| unread int // bytes unread when done | |
| err error // read error once empty. non-nil means closed. | |
| breakErr error // immediate read error (caller doesn't see rest of b) | |
| donec chan struct{} // closed on error | |
| readFn func() // optional code to run in Read before error | |
| } | |
| type http2pipeBuffer interface { | |
| Len() int | |
| io.Writer | |
| io.Reader | |
| } | |
| func (p *http2pipe) Len() int { | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| if p.b == nil { | |
| return p.unread | |
| } | |
| return p.b.Len() | |
| } | |
| // Read waits until data is available and copies bytes | |
| // from the buffer into p. | |
| func (p *http2pipe) Read(d []byte) (n int, err error) { | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| if p.c.L == nil { | |
| p.c.L = &p.mu | |
| } | |
| for { | |
| if p.breakErr != nil { | |
| return 0, p.breakErr | |
| } | |
| if p.b != nil && p.b.Len() > 0 { | |
| return p.b.Read(d) | |
| } | |
| if p.err != nil { | |
| if p.readFn != nil { | |
| p.readFn() // e.g. copy trailers | |
| p.readFn = nil // not sticky like p.err | |
| } | |
| p.b = nil | |
| return 0, p.err | |
| } | |
| p.c.Wait() | |
| } | |
| } | |
| var http2errClosedPipeWrite = errors.New("write on closed buffer") | |
| // Write copies bytes from p into the buffer and wakes a reader. | |
| // It is an error to write more data than the buffer can hold. | |
| func (p *http2pipe) Write(d []byte) (n int, err error) { | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| if p.c.L == nil { | |
| p.c.L = &p.mu | |
| } | |
| defer p.c.Signal() | |
| if p.err != nil { | |
| return 0, http2errClosedPipeWrite | |
| } | |
| if p.breakErr != nil { | |
| p.unread += len(d) | |
| return len(d), nil // discard when there is no reader | |
| } | |
| return p.b.Write(d) | |
| } | |
| // CloseWithError causes the next Read (waking up a current blocked | |
| // Read if needed) to return the provided err after all data has been | |
| // read. | |
| // | |
| // The error must be non-nil. | |
| func (p *http2pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) } | |
| // BreakWithError causes the next Read (waking up a current blocked | |
| // Read if needed) to return the provided err immediately, without | |
| // waiting for unread data. | |
| func (p *http2pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) } | |
| // closeWithErrorAndCode is like CloseWithError but also sets some code to run | |
| // in the caller's goroutine before returning the error. | |
| func (p *http2pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) } | |
| func (p *http2pipe) closeWithError(dst *error, err error, fn func()) { | |
| if err == nil { | |
| panic("err must be non-nil") | |
| } | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| if p.c.L == nil { | |
| p.c.L = &p.mu | |
| } | |
| defer p.c.Signal() | |
| if *dst != nil { | |
| // Already been done. | |
| return | |
| } | |
| p.readFn = fn | |
| if dst == &p.breakErr { | |
| if p.b != nil { | |
| p.unread += p.b.Len() | |
| } | |
| p.b = nil | |
| } | |
| *dst = err | |
| p.closeDoneLocked() | |
| } | |
| // requires p.mu be held. | |
| func (p *http2pipe) closeDoneLocked() { | |
| if p.donec == nil { | |
| return | |
| } | |
| // Close if unclosed. This isn't racy since we always | |
| // hold p.mu while closing. | |
| select { | |
| case <-p.donec: | |
| default: | |
| close(p.donec) | |
| } | |
| } | |
| // Err returns the error (if any) first set by BreakWithError or CloseWithError. | |
| func (p *http2pipe) Err() error { | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| if p.breakErr != nil { | |
| return p.breakErr | |
| } | |
| return p.err | |
| } | |
| // Done returns a channel which is closed if and when this pipe is closed | |
| // with CloseWithError. | |
| func (p *http2pipe) Done() <-chan struct{} { | |
| p.mu.Lock() | |
| defer p.mu.Unlock() | |
| if p.donec == nil { | |
| p.donec = make(chan struct{}) | |
| if p.err != nil || p.breakErr != nil { | |
| // Already hit an error. | |
| p.closeDoneLocked() | |
| } | |
| } | |
| return p.donec | |
| } | |
| const ( | |
| http2prefaceTimeout = 10 * time.Second | |
| http2firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway | |
| http2handlerChunkWriteSize = 4 << 10 | |
| http2defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to? | |
| http2maxQueuedControlFrames = 10000 | |
| ) | |
| var ( | |
| http2errClientDisconnected = errors.New("client disconnected") | |
| http2errClosedBody = errors.New("body closed by handler") | |
| http2errHandlerComplete = errors.New("http2: request body closed due to handler exiting") | |
| http2errStreamClosed = errors.New("http2: stream closed") | |
| ) | |
| var http2responseWriterStatePool = sync.Pool{ | |
| New: func() interface{} { | |
| rws := &http2responseWriterState{} | |
| rws.bw = bufio.NewWriterSize(http2chunkWriter{rws}, http2handlerChunkWriteSize) | |
| return rws | |
| }, | |
| } | |
| // Test hooks. | |
| var ( | |
| http2testHookOnConn func() | |
| http2testHookGetServerConn func(*http2serverConn) | |
| http2testHookOnPanicMu *sync.Mutex // nil except in tests | |
| http2testHookOnPanic func(sc *http2serverConn, panicVal interface{}) (rePanic bool) | |
| ) | |
| // Server is an HTTP/2 server. | |
| type http2Server struct { | |
| // MaxHandlers limits the number of http.Handler ServeHTTP goroutines | |
| // which may run at a time over all connections. | |
| // Negative or zero no limit. | |
| // TODO: implement | |
| MaxHandlers int | |
| // MaxConcurrentStreams optionally specifies the number of | |
| // concurrent streams that each client may have open at a | |
| // time. This is unrelated to the number of http.Handler goroutines | |
| // which may be active globally, which is MaxHandlers. | |
| // If zero, MaxConcurrentStreams defaults to at least 100, per | |
| // the HTTP/2 spec's recommendations. | |
| MaxConcurrentStreams uint32 | |
| // MaxReadFrameSize optionally specifies the largest frame | |
| // this server is willing to read. A valid value is between | |
| // 16k and 16M, inclusive. If zero or otherwise invalid, a | |
| // default value is used. | |
| MaxReadFrameSize uint32 | |
| // PermitProhibitedCipherSuites, if true, permits the use of | |
| // cipher suites prohibited by the HTTP/2 spec. | |
| PermitProhibitedCipherSuites bool | |
| // IdleTimeout specifies how long until idle clients should be | |
| // closed with a GOAWAY frame. PING frames are not considered | |
| // activity for the purposes of IdleTimeout. | |
| IdleTimeout time.Duration | |
| // MaxUploadBufferPerConnection is the size of the initial flow | |
| // control window for each connections. The HTTP/2 spec does not | |
| // allow this to be smaller than 65535 or larger than 2^32-1. | |
| // If the value is outside this range, a default value will be | |
| // used instead. | |
| MaxUploadBufferPerConnection int32 | |
| // MaxUploadBufferPerStream is the size of the initial flow control | |
| // window for each stream. The HTTP/2 spec does not allow this to | |
| // be larger than 2^32-1. If the value is zero or larger than the | |
| // maximum, a default value will be used instead. | |
| MaxUploadBufferPerStream int32 | |
| // NewWriteScheduler constructs a write scheduler for a connection. | |
| // If nil, a default scheduler is chosen. | |
| NewWriteScheduler func() http2WriteScheduler | |
| // Internal state. This is a pointer (rather than embedded directly) | |
| // so that we don't embed a Mutex in this struct, which will make the | |
| // struct non-copyable, which might break some callers. | |
| state *http2serverInternalState | |
| } | |
| func (s *http2Server) initialConnRecvWindowSize() int32 { | |
| if s.MaxUploadBufferPerConnection > http2initialWindowSize { | |
| return s.MaxUploadBufferPerConnection | |
| } | |
| return 1 << 20 | |
| } | |
| func (s *http2Server) initialStreamRecvWindowSize() int32 { | |
| if s.MaxUploadBufferPerStream > 0 { | |
| return s.MaxUploadBufferPerStream | |
| } | |
| return 1 << 20 | |
| } | |
| func (s *http2Server) maxReadFrameSize() uint32 { | |
| if v := s.MaxReadFrameSize; v >= http2minMaxFrameSize && v <= http2maxFrameSize { | |
| return v | |
| } | |
| return http2defaultMaxReadFrameSize | |
| } | |
| func (s *http2Server) maxConcurrentStreams() uint32 { | |
| if v := s.MaxConcurrentStreams; v > 0 { | |
| return v | |
| } | |
| return http2defaultMaxStreams | |
| } | |
| // maxQueuedControlFrames is the maximum number of control frames like | |
| // SETTINGS, PING and RST_STREAM that will be queued for writing before | |
| // the connection is closed to prevent memory exhaustion attacks. | |
| func (s *http2Server) maxQueuedControlFrames() int { | |
| // TODO: if anybody asks, add a Server field, and remember to define the | |
| // behavior of negative values. | |
| return http2maxQueuedControlFrames | |
| } | |
| type http2serverInternalState struct { | |
| mu sync.Mutex | |
| activeConns map[*http2serverConn]struct{} | |
| } | |
| func (s *http2serverInternalState) registerConn(sc *http2serverConn) { | |
| if s == nil { | |
| return // if the Server was used without calling ConfigureServer | |
| } | |
| s.mu.Lock() | |
| s.activeConns[sc] = struct{}{} | |
| s.mu.Unlock() | |
| } | |
| func (s *http2serverInternalState) unregisterConn(sc *http2serverConn) { | |
| if s == nil { | |
| return // if the Server was used without calling ConfigureServer | |
| } | |
| s.mu.Lock() | |
| delete(s.activeConns, sc) | |
| s.mu.Unlock() | |
| } | |
| func (s *http2serverInternalState) startGracefulShutdown() { | |
| if s == nil { | |
| return // if the Server was used without calling ConfigureServer | |
| } | |
| s.mu.Lock() | |
| for sc := range s.activeConns { | |
| sc.startGracefulShutdown() | |
| } | |
| s.mu.Unlock() | |
| } | |
| // ConfigureServer adds HTTP/2 support to a net/http Server. | |
| // | |
| // The configuration conf may be nil. | |
| // | |
| // ConfigureServer must be called before s begins serving. | |
| func http2ConfigureServer(s *Server, conf *http2Server) error { | |
| if s == nil { | |
| panic("nil *http.Server") | |
| } | |
| if conf == nil { | |
| conf = new(http2Server) | |
| } | |
| conf.state = &http2serverInternalState{activeConns: make(map[*http2serverConn]struct{})} | |
| if h1, h2 := s, conf; h2.IdleTimeout == 0 { | |
| if h1.IdleTimeout != 0 { | |
| h2.IdleTimeout = h1.IdleTimeout | |
| } else { | |
| h2.IdleTimeout = h1.ReadTimeout | |
| } | |
| } | |
| s.RegisterOnShutdown(conf.state.startGracefulShutdown) | |
| if s.TLSConfig == nil { | |
| s.TLSConfig = new(tls.Config) | |
| } else if s.TLSConfig.CipherSuites != nil { | |
| // If they already provided a CipherSuite list, return | |
| // an error if it has a bad order or is missing | |
| // ECDHE_RSA_WITH_AES_128_GCM_SHA256 or ECDHE_ECDSA_WITH_AES_128_GCM_SHA256. | |
| haveRequired := false | |
| sawBad := false | |
| for i, cs := range s.TLSConfig.CipherSuites { | |
| switch cs { | |
| case tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| // Alternative MTI cipher to not discourage ECDSA-only servers. | |
| // See http://golang.org/cl/30721 for further information. | |
| tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: | |
| haveRequired = true | |
| } | |
| if http2isBadCipher(cs) { | |
| sawBad = true | |
| } else if sawBad { | |
| return fmt.Errorf("http2: TLSConfig.CipherSuites index %d contains an HTTP/2-approved cipher suite (%#04x), but it comes after unapproved cipher suites. With this configuration, clients that don't support previous, approved cipher suites may be given an unapproved one and reject the connection.", i, cs) | |
| } | |
| } | |
| if !haveRequired { | |
| return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 or TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).") | |
| } | |
| } | |
| // Note: not setting MinVersion to tls.VersionTLS12, | |
| // as we don't want to interfere with HTTP/1.1 traffic | |
| // on the user's server. We enforce TLS 1.2 later once | |
| // we accept a connection. Ideally this should be done | |
| // during next-proto selection, but using TLS <1.2 with | |
| // HTTP/2 is still the client's bug. | |
| s.TLSConfig.PreferServerCipherSuites = true | |
| haveNPN := false | |
| for _, p := range s.TLSConfig.NextProtos { | |
| if p == http2NextProtoTLS { | |
| haveNPN = true | |
| break | |
| } | |
| } | |
| if !haveNPN { | |
| s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, http2NextProtoTLS) | |
| } | |
| if s.TLSNextProto == nil { | |
| s.TLSNextProto = map[string]func(*Server, *tls.Conn, Handler){} | |
| } | |
| protoHandler := func(hs *Server, c *tls.Conn, h Handler) { | |
| if http2testHookOnConn != nil { | |
| http2testHookOnConn() | |
| } | |
| // The TLSNextProto interface predates contexts, so | |
| // the net/http package passes down its per-connection | |
| // base context via an exported but unadvertised | |
| // method on the Handler. This is for internal | |
| // net/http<=>http2 use only. | |
| var ctx context.Context | |
| type baseContexter interface { | |
| BaseContext() context.Context | |
| } | |
| if bc, ok := h.(baseContexter); ok { | |
| ctx = bc.BaseContext() | |
| } | |
| conf.ServeConn(c, &http2ServeConnOpts{ | |
| Context: ctx, | |
| Handler: h, | |
| BaseConfig: hs, | |
| }) | |
| } | |
| s.TLSNextProto[http2NextProtoTLS] = protoHandler | |
| return nil | |
| } | |
| // ServeConnOpts are options for the Server.ServeConn method. | |
| type http2ServeConnOpts struct { | |
| // Context is the base context to use. | |
| // If nil, context.Background is used. | |
| Context context.Context | |
| // BaseConfig optionally sets the base configuration | |
| // for values. If nil, defaults are used. | |
| BaseConfig *Server | |
| // Handler specifies which handler to use for processing | |
| // requests. If nil, BaseConfig.Handler is used. If BaseConfig | |
| // or BaseConfig.Handler is nil, http.DefaultServeMux is used. | |
| Handler Handler | |
| } | |
| func (o *http2ServeConnOpts) context() context.Context { | |
| if o != nil && o.Context != nil { | |
| return o.Context | |
| } | |
| return context.Background() | |
| } | |
| func (o *http2ServeConnOpts) baseConfig() *Server { | |
| if o != nil && o.BaseConfig != nil { | |
| return o.BaseConfig | |
| } | |
| return new(Server) | |
| } | |
| func (o *http2ServeConnOpts) handler() Handler { | |
| if o != nil { | |
| if o.Handler != nil { | |
| return o.Handler | |
| } | |
| if o.BaseConfig != nil && o.BaseConfig.Handler != nil { | |
| return o.BaseConfig.Handler | |
| } | |
| } | |
| return DefaultServeMux | |
| } | |
| // ServeConn serves HTTP/2 requests on the provided connection and | |
| // blocks until the connection is no longer readable. | |
| // | |
| // ServeConn starts speaking HTTP/2 assuming that c has not had any | |
| // reads or writes. It writes its initial settings frame and expects | |
| // to be able to read the preface and settings frame from the | |
| // client. If c has a ConnectionState method like a *tls.Conn, the | |
| // ConnectionState is used to verify the TLS ciphersuite and to set | |
| // the Request.TLS field in Handlers. | |
| // | |
| // ServeConn does not support h2c by itself. Any h2c support must be | |
| // implemented in terms of providing a suitably-behaving net.Conn. | |
| // | |
| // The opts parameter is optional. If nil, default values are used. | |
| func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { | |
| baseCtx, cancel := http2serverConnBaseContext(c, opts) | |
| defer cancel() | |
| sc := &http2serverConn{ | |
| srv: s, | |
| hs: opts.baseConfig(), | |
| conn: c, | |
| baseCtx: baseCtx, | |
| remoteAddrStr: c.RemoteAddr().String(), | |
| bw: http2newBufferedWriter(c), | |
| handler: opts.handler(), | |
| streams: make(map[uint32]*http2stream), | |
| readFrameCh: make(chan http2readFrameResult), | |
| wantWriteFrameCh: make(chan http2FrameWriteRequest, 8), | |
| serveMsgCh: make(chan interface{}, 8), | |
| wroteFrameCh: make(chan http2frameWriteResult, 1), // buffered; one send in writeFrameAsync | |
| bodyReadCh: make(chan http2bodyReadMsg), // buffering doesn't matter either way | |
| doneServing: make(chan struct{}), | |
| clientMaxStreams: math.MaxUint32, // Section 6.5.2: "Initially, there is no limit to this value" | |
| advMaxStreams: s.maxConcurrentStreams(), | |
| initialStreamSendWindowSize: http2initialWindowSize, | |
| maxFrameSize: http2initialMaxFrameSize, | |
| headerTableSize: http2initialHeaderTableSize, | |
| serveG: http2newGoroutineLock(), | |
| pushEnabled: true, | |
| } | |
| s.state.registerConn(sc) | |
| defer s.state.unregisterConn(sc) | |
| // The net/http package sets the write deadline from the | |
| // http.Server.WriteTimeout during the TLS handshake, but then | |
| // passes the connection off to us with the deadline already set. | |
| // Write deadlines are set per stream in serverConn.newStream. | |
| // Disarm the net.Conn write deadline here. | |
| if sc.hs.WriteTimeout != 0 { | |
| sc.conn.SetWriteDeadline(time.Time{}) | |
| } | |
| if s.NewWriteScheduler != nil { | |
| sc.writeSched = s.NewWriteScheduler() | |
| } else { | |
| sc.writeSched = http2NewRandomWriteScheduler() | |
| } | |
| // These start at the RFC-specified defaults. If there is a higher | |
| // configured value for inflow, that will be updated when we send a | |
| // WINDOW_UPDATE shortly after sending SETTINGS. | |
| sc.flow.add(http2initialWindowSize) | |
| sc.inflow.add(http2initialWindowSize) | |
| sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf) | |
| fr := http2NewFramer(sc.bw, c) | |
| fr.ReadMetaHeaders = hpack.NewDecoder(http2initialHeaderTableSize, nil) | |
| fr.MaxHeaderListSize = sc.maxHeaderListSize() | |
| fr.SetMaxReadFrameSize(s.maxReadFrameSize()) | |
| sc.framer = fr | |
| if tc, ok := c.(http2connectionStater); ok { | |
| sc.tlsState = new(tls.ConnectionState) | |
| *sc.tlsState = tc.ConnectionState() | |
| // 9.2 Use of TLS Features | |
| // An implementation of HTTP/2 over TLS MUST use TLS | |
| // 1.2 or higher with the restrictions on feature set | |
| // and cipher suite described in this section. Due to | |
| // implementation limitations, it might not be | |
| // possible to fail TLS negotiation. An endpoint MUST | |
| // immediately terminate an HTTP/2 connection that | |
| // does not meet the TLS requirements described in | |
| // this section with a connection error (Section | |
| // 5.4.1) of type INADEQUATE_SECURITY. | |
| if sc.tlsState.Version < tls.VersionTLS12 { | |
| sc.rejectConn(http2ErrCodeInadequateSecurity, "TLS version too low") | |
| return | |
| } | |
| if sc.tlsState.ServerName == "" { | |
| // Client must use SNI, but we don't enforce that anymore, | |
| // since it was causing problems when connecting to bare IP | |
| // addresses during development. | |
| // | |
| // TODO: optionally enforce? Or enforce at the time we receive | |
| // a new request, and verify the ServerName matches the :authority? | |
| // But that precludes proxy situations, perhaps. | |
| // | |
| // So for now, do nothing here again. | |
| } | |
| if !s.PermitProhibitedCipherSuites && http2isBadCipher(sc.tlsState.CipherSuite) { | |
| // "Endpoints MAY choose to generate a connection error | |
| // (Section 5.4.1) of type INADEQUATE_SECURITY if one of | |
| // the prohibited cipher suites are negotiated." | |
| // | |
| // We choose that. In my opinion, the spec is weak | |
| // here. It also says both parties must support at least | |
| // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 so there's no | |
| // excuses here. If we really must, we could allow an | |
| // "AllowInsecureWeakCiphers" option on the server later. | |
| // Let's see how it plays out first. | |
| sc.rejectConn(http2ErrCodeInadequateSecurity, fmt.Sprintf("Prohibited TLS 1.2 Cipher Suite: %x", sc.tlsState.CipherSuite)) | |
| return | |
| } | |
| } | |
| if hook := http2testHookGetServerConn; hook != nil { | |
| hook(sc) | |
| } | |
| sc.serve() | |
| } | |
| func http2serverConnBaseContext(c net.Conn, opts *http2ServeConnOpts) (ctx context.Context, cancel func()) { | |
| ctx, cancel = context.WithCancel(opts.context()) | |
| ctx = context.WithValue(ctx, LocalAddrContextKey, c.LocalAddr()) | |
| if hs := opts.baseConfig(); hs != nil { | |
| ctx = context.WithValue(ctx, ServerContextKey, hs) | |
| } | |
| return | |
| } | |
| func (sc *http2serverConn) rejectConn(err http2ErrCode, debug string) { | |
| sc.vlogf("http2: server rejecting conn: %v, %s", err, debug) | |
| // ignoring errors. hanging up anyway. | |
| sc.framer.WriteGoAway(0, err, []byte(debug)) | |
| sc.bw.Flush() | |
| sc.conn.Close() | |
| } | |
| type http2serverConn struct { | |
| // Immutable: | |
| srv *http2Server | |
| hs *Server | |
| conn net.Conn | |
| bw *http2bufferedWriter // writing to conn | |
| handler Handler | |
| baseCtx context.Context | |
| framer *http2Framer | |
| doneServing chan struct{} // closed when serverConn.serve ends | |
| readFrameCh chan http2readFrameResult // written by serverConn.readFrames | |
| wantWriteFrameCh chan http2FrameWriteRequest // from handlers -> serve | |
| wroteFrameCh chan http2frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes | |
| bodyReadCh chan http2bodyReadMsg // from handlers -> serve | |
| serveMsgCh chan interface{} // misc messages & code to send to / run on the serve loop | |
| flow http2flow // conn-wide (not stream-specific) outbound flow control | |
| inflow http2flow // conn-wide inbound flow control | |
| tlsState *tls.ConnectionState // shared by all handlers, like net/http | |
| remoteAddrStr string | |
| writeSched http2WriteScheduler | |
| // Everything following is owned by the serve loop; use serveG.check(): | |
| serveG http2goroutineLock // used to verify funcs are on serve() | |
| pushEnabled bool | |
| sawFirstSettings bool // got the initial SETTINGS frame after the preface | |
| needToSendSettingsAck bool | |
| unackedSettings int // how many SETTINGS have we sent without ACKs? | |
| queuedControlFrames int // control frames in the writeSched queue | |
| clientMaxStreams uint32 // SETTINGS_MAX_CONCURRENT_STREAMS from client (our PUSH_PROMISE limit) | |
| advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client | |
| curClientStreams uint32 // number of open streams initiated by the client | |
| curPushedStreams uint32 // number of open streams initiated by server push | |
| maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests | |
| maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes | |
| streams map[uint32]*http2stream | |
| initialStreamSendWindowSize int32 | |
| maxFrameSize int32 | |
| headerTableSize uint32 | |
| peerMaxHeaderListSize uint32 // zero means unknown (default) | |
| canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case | |
| writingFrame bool // started writing a frame (on serve goroutine or separate) | |
| writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh | |
| needsFrameFlush bool // last frame write wasn't a flush | |
| inGoAway bool // we've started to or sent GOAWAY | |
| inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop | |
| needToSendGoAway bool // we need to schedule a GOAWAY frame write | |
| goAwayCode http2ErrCode | |
| shutdownTimer *time.Timer // nil until used | |
| idleTimer *time.Timer // nil if unused | |
| // Owned by the writeFrameAsync goroutine: | |
| headerWriteBuf bytes.Buffer | |
| hpackEncoder *hpack.Encoder | |
| // Used by startGracefulShutdown. | |
| shutdownOnce sync.Once | |
| } | |
| func (sc *http2serverConn) maxHeaderListSize() uint32 { | |
| n := sc.hs.MaxHeaderBytes | |
| if n <= 0 { | |
| n = DefaultMaxHeaderBytes | |
| } | |
| // http2's count is in a slightly different unit and includes 32 bytes per pair. | |
| // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. | |
| const perFieldOverhead = 32 // per http2 spec | |
| const typicalHeaders = 10 // conservative | |
| return uint32(n + typicalHeaders*perFieldOverhead) | |
| } | |
| func (sc *http2serverConn) curOpenStreams() uint32 { | |
| sc.serveG.check() | |
| return sc.curClientStreams + sc.curPushedStreams | |
| } | |
| // stream represents a stream. This is the minimal metadata needed by | |
| // the serve goroutine. Most of the actual stream state is owned by | |
| // the http.Handler's goroutine in the responseWriter. Because the | |
| // responseWriter's responseWriterState is recycled at the end of a | |
| // handler, this struct intentionally has no pointer to the | |
| // *responseWriter{,State} itself, as the Handler ending nils out the | |
| // responseWriter's state field. | |
| type http2stream struct { | |
| // immutable: | |
| sc *http2serverConn | |
| id uint32 | |
| body *http2pipe // non-nil if expecting DATA frames | |
| cw http2closeWaiter // closed wait stream transitions to closed state | |
| ctx context.Context | |
| cancelCtx func() | |
| // owned by serverConn's serve loop: | |
| bodyBytes int64 // body bytes seen so far | |
| declBodyBytes int64 // or -1 if undeclared | |
| flow http2flow // limits writing from Handler to client | |
| inflow http2flow // what the client is allowed to POST/etc to us | |
| state http2streamState | |
| resetQueued bool // RST_STREAM queued for write; set by sc.resetStream | |
| gotTrailerHeader bool // HEADER frame for trailers was seen | |
| wroteHeaders bool // whether we wrote headers (not status 100) | |
| writeDeadline *time.Timer // nil if unused | |
| trailer Header // accumulated trailers | |
| reqTrailer Header // handler's Request.Trailer | |
| } | |
| func (sc *http2serverConn) Framer() *http2Framer { return sc.framer } | |
| func (sc *http2serverConn) CloseConn() error { return sc.conn.Close() } | |
| func (sc *http2serverConn) Flush() error { return sc.bw.Flush() } | |
| func (sc *http2serverConn) HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) { | |
| return sc.hpackEncoder, &sc.headerWriteBuf | |
| } | |
| func (sc *http2serverConn) state(streamID uint32) (http2streamState, *http2stream) { | |
| sc.serveG.check() | |
| // http://tools.ietf.org/html/rfc7540#section-5.1 | |
| if st, ok := sc.streams[streamID]; ok { | |
| return st.state, st | |
| } | |
| // "The first use of a new stream identifier implicitly closes all | |
| // streams in the "idle" state that might have been initiated by | |
| // that peer with a lower-valued stream identifier. For example, if | |
| // a client sends a HEADERS frame on stream 7 without ever sending a | |
| // frame on stream 5, then stream 5 transitions to the "closed" | |
| // state when the first frame for stream 7 is sent or received." | |
| if streamID%2 == 1 { | |
| if streamID <= sc.maxClientStreamID { | |
| return http2stateClosed, nil | |
| } | |
| } else { | |
| if streamID <= sc.maxPushPromiseID { | |
| return http2stateClosed, nil | |
| } | |
| } | |
| return http2stateIdle, nil | |
| } | |
| // setConnState calls the net/http ConnState hook for this connection, if configured. | |
| // Note that the net/http package does StateNew and StateClosed for us. | |
| // There is currently no plan for StateHijacked or hijacking HTTP/2 connections. | |
| func (sc *http2serverConn) setConnState(state ConnState) { | |
| if sc.hs.ConnState != nil { | |
| sc.hs.ConnState(sc.conn, state) | |
| } | |
| } | |
| func (sc *http2serverConn) vlogf(format string, args ...interface{}) { | |
| if http2VerboseLogs { | |
| sc.logf(format, args...) | |
| } | |
| } | |
| func (sc *http2serverConn) logf(format string, args ...interface{}) { | |
| if lg := sc.hs.ErrorLog; lg != nil { | |
| lg.Printf(format, args...) | |
| } else { | |
| log.Printf(format, args...) | |
| } | |
| } | |
| // errno returns v's underlying uintptr, else 0. | |
| // | |
| // TODO: remove this helper function once http2 can use build | |
| // tags. See comment in isClosedConnError. | |
| func http2errno(v error) uintptr { | |
| if rv := reflect.ValueOf(v); rv.Kind() == reflect.Uintptr { | |
| return uintptr(rv.Uint()) | |
| } | |
| return 0 | |
| } | |
| // isClosedConnError reports whether err is an error from use of a closed | |
| // network connection. | |
| func http2isClosedConnError(err error) bool { | |
| if err == nil { | |
| return false | |
| } | |
| // TODO: remove this string search and be more like the Windows | |
| // case below. That might involve modifying the standard library | |
| // to return better error types. | |
| str := err.Error() | |
| if strings.Contains(str, "use of closed network connection") { | |
| return true | |
| } | |
| // TODO(bradfitz): x/tools/cmd/bundle doesn't really support | |
| // build tags, so I can't make an http2_windows.go file with | |
| // Windows-specific stuff. Fix that and move this, once we | |
| // have a way to bundle this into std's net/http somehow. | |
| if runtime.GOOS == "windows" { | |
| if oe, ok := err.(*net.OpError); ok && oe.Op == "read" { | |
| if se, ok := oe.Err.(*os.SyscallError); ok && se.Syscall == "wsarecv" { | |
| const WSAECONNABORTED = 10053 | |
| const WSAECONNRESET = 10054 | |
| if n := http2errno(se.Err); n == WSAECONNRESET || n == WSAECONNABORTED { | |
| return true | |
| } | |
| } | |
| } | |
| } | |
| return false | |
| } | |
| func (sc *http2serverConn) condlogf(err error, format string, args ...interface{}) { | |
| if err == nil { | |
| return | |
| } | |
| if err == io.EOF || err == io.ErrUnexpectedEOF || http2isClosedConnError(err) || err == http2errPrefaceTimeout { | |
| // Boring, expected errors. | |
| sc.vlogf(format, args...) | |
| } else { | |
| sc.logf(format, args...) | |
| } | |
| } | |
| func (sc *http2serverConn) canonicalHeader(v string) string { | |
| sc.serveG.check() | |
| http2buildCommonHeaderMapsOnce() | |
| cv, ok := http2commonCanonHeader[v] | |
| if ok { | |
| return cv | |
| } | |
| cv, ok = sc.canonHeader[v] | |
| if ok { | |
| return cv | |
| } | |
| if sc.canonHeader == nil { | |
| sc.canonHeader = make(map[string]string) | |
| } | |
| cv = CanonicalHeaderKey(v) | |
| sc.canonHeader[v] = cv | |
| return cv | |
| } | |
| type http2readFrameResult struct { | |
| f http2Frame // valid until readMore is called | |
| err error | |
| // readMore should be called once the consumer no longer needs or | |
| // retains f. After readMore, f is invalid and more frames can be | |
| // read. | |
| readMore func() | |
| } | |
| // readFrames is the loop that reads incoming frames. | |
| // It takes care to only read one frame at a time, blocking until the | |
| // consumer is done with the frame. | |
| // It's run on its own goroutine. | |
| func (sc *http2serverConn) readFrames() { | |
| gate := make(http2gate) | |
| gateDone := gate.Done | |
| for { | |
| f, err := sc.framer.ReadFrame() | |
| select { | |
| case sc.readFrameCh <- http2readFrameResult{f, err, gateDone}: | |
| case <-sc.doneServing: | |
| return | |
| } | |
| select { | |
| case <-gate: | |
| case <-sc.doneServing: | |
| return | |
| } | |
| if http2terminalReadFrameError(err) { | |
| return | |
| } | |
| } | |
| } | |
| // frameWriteResult is the message passed from writeFrameAsync to the serve goroutine. | |
| type http2frameWriteResult struct { | |
| _ http2incomparable | |
| wr http2FrameWriteRequest // what was written (or attempted) | |
| err error // result of the writeFrame call | |
| } | |
| // writeFrameAsync runs in its own goroutine and writes a single frame | |
| // and then reports when it's done. | |
| // At most one goroutine can be running writeFrameAsync at a time per | |
| // serverConn. | |
| func (sc *http2serverConn) writeFrameAsync(wr http2FrameWriteRequest) { | |
| err := wr.write.writeFrame(sc) | |
| sc.wroteFrameCh <- http2frameWriteResult{wr: wr, err: err} | |
| } | |
| func (sc *http2serverConn) closeAllStreamsOnConnClose() { | |
| sc.serveG.check() | |
| for _, st := range sc.streams { | |
| sc.closeStream(st, http2errClientDisconnected) | |
| } | |
| } | |
| func (sc *http2serverConn) stopShutdownTimer() { | |
| sc.serveG.check() | |
| if t := sc.shutdownTimer; t != nil { | |
| t.Stop() | |
| } | |
| } | |
| func (sc *http2serverConn) notePanic() { | |
| // Note: this is for serverConn.serve panicking, not http.Handler code. | |
| if http2testHookOnPanicMu != nil { | |
| http2testHookOnPanicMu.Lock() | |
| defer http2testHookOnPanicMu.Unlock() | |
| } | |
| if http2testHookOnPanic != nil { | |
| if e := recover(); e != nil { | |
| if http2testHookOnPanic(sc, e) { | |
| panic(e) | |
| } | |
| } | |
| } | |
| } | |
| func (sc *http2serverConn) serve() { | |
| sc.serveG.check() | |
| defer sc.notePanic() | |
| defer sc.conn.Close() | |
| defer sc.closeAllStreamsOnConnClose() | |
| defer sc.stopShutdownTimer() | |
| defer close(sc.doneServing) // unblocks handlers trying to send | |
| if http2VerboseLogs { | |
| sc.vlogf("http2: server connection from %v on %p", sc.conn.RemoteAddr(), sc.hs) | |
| } | |
| sc.writeFrame(http2FrameWriteRequest{ | |
| write: http2writeSettings{ | |
| {http2SettingMaxFrameSize, sc.srv.maxReadFrameSize()}, | |
| {http2SettingMaxConcurrentStreams, sc.advMaxStreams}, | |
| {http2SettingMaxHeaderListSize, sc.maxHeaderListSize()}, | |
| {http2SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())}, | |
| }, | |
| }) | |
| sc.unackedSettings++ | |
| // Each connection starts with intialWindowSize inflow tokens. | |
| // If a higher value is configured, we add more tokens. | |
| if diff := sc.srv.initialConnRecvWindowSize() - http2initialWindowSize; diff > 0 { | |
| sc.sendWindowUpdate(nil, int(diff)) | |
| } | |
| if err := sc.readPreface(); err != nil { | |
| sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err) | |
| return | |
| } | |
| // Now that we've got the preface, get us out of the | |
| // "StateNew" state. We can't go directly to idle, though. | |
| // Active means we read some data and anticipate a request. We'll | |
| // do another Active when we get a HEADERS frame. | |
| sc.setConnState(StateActive) | |
| sc.setConnState(StateIdle) | |
| if sc.srv.IdleTimeout != 0 { | |
| sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) | |
| defer sc.idleTimer.Stop() | |
| } | |
| go sc.readFrames() // closed by defer sc.conn.Close above | |
| settingsTimer := time.AfterFunc(http2firstSettingsTimeout, sc.onSettingsTimer) | |
| defer settingsTimer.Stop() | |
| loopNum := 0 | |
| for { | |
| loopNum++ | |
| select { | |
| case wr := <-sc.wantWriteFrameCh: | |
| if se, ok := wr.write.(http2StreamError); ok { | |
| sc.resetStream(se) | |
| break | |
| } | |
| sc.writeFrame(wr) | |
| case res := <-sc.wroteFrameCh: | |
| sc.wroteFrame(res) | |
| case res := <-sc.readFrameCh: | |
| if !sc.processFrameFromReader(res) { | |
| return | |
| } | |
| res.readMore() | |
| if settingsTimer != nil { | |
| settingsTimer.Stop() | |
| settingsTimer = nil | |
| } | |
| case m := <-sc.bodyReadCh: | |
| sc.noteBodyRead(m.st, m.n) | |
| case msg := <-sc.serveMsgCh: | |
| switch v := msg.(type) { | |
| case func(int): | |
| v(loopNum) // for testing | |
| case *http2serverMessage: | |
| switch v { | |
| case http2settingsTimerMsg: | |
| sc.logf("timeout waiting for SETTINGS frames from %v", sc.conn.RemoteAddr()) | |
| return | |
| case http2idleTimerMsg: | |
| sc.vlogf("connection is idle") | |
| sc.goAway(http2ErrCodeNo) | |
| case http2shutdownTimerMsg: | |
| sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr()) | |
| return | |
| case http2gracefulShutdownMsg: | |
| sc.startGracefulShutdownInternal() | |
| default: | |
| panic("unknown timer") | |
| } | |
| case *http2startPushRequest: | |
| sc.startPush(v) | |
| default: | |
| panic(fmt.Sprintf("unexpected type %T", v)) | |
| } | |
| } | |
| // If the peer is causing us to generate a lot of control frames, | |
| // but not reading them from us, assume they are trying to make us | |
| // run out of memory. | |
| if sc.queuedControlFrames > sc.srv.maxQueuedControlFrames() { | |
| sc.vlogf("http2: too many control frames in send queue, closing connection") | |
| return | |
| } | |
| // Start the shutdown timer after sending a GOAWAY. When sending GOAWAY | |
| // with no error code (graceful shutdown), don't start the timer until | |
| // all open streams have been completed. | |
| sentGoAway := sc.inGoAway && !sc.needToSendGoAway && !sc.writingFrame | |
| gracefulShutdownComplete := sc.goAwayCode == http2ErrCodeNo && sc.curOpenStreams() == 0 | |
| if sentGoAway && sc.shutdownTimer == nil && (sc.goAwayCode != http2ErrCodeNo || gracefulShutdownComplete) { | |
| sc.shutDownIn(http2goAwayTimeout) | |
| } | |
| } | |
| } | |
| func (sc *http2serverConn) awaitGracefulShutdown(sharedCh <-chan struct{}, privateCh chan struct{}) { | |
| select { | |
| case <-sc.doneServing: | |
| case <-sharedCh: | |
| close(privateCh) | |
| } | |
| } | |
| type http2serverMessage int | |
| // Message values sent to serveMsgCh. | |
| var ( | |
| http2settingsTimerMsg = new(http2serverMessage) | |
| http2idleTimerMsg = new(http2serverMessage) | |
| http2shutdownTimerMsg = new(http2serverMessage) | |
| http2gracefulShutdownMsg = new(http2serverMessage) | |
| ) | |
| func (sc *http2serverConn) onSettingsTimer() { sc.sendServeMsg(http2settingsTimerMsg) } | |
| func (sc *http2serverConn) onIdleTimer() { sc.sendServeMsg(http2idleTimerMsg) } | |
| func (sc *http2serverConn) onShutdownTimer() { sc.sendServeMsg(http2shutdownTimerMsg) } | |
| func (sc *http2serverConn) sendServeMsg(msg interface{}) { | |
| sc.serveG.checkNotOn() // NOT | |
| select { | |
| case sc.serveMsgCh <- msg: | |
| case <-sc.doneServing: | |
| } | |
| } | |
| var http2errPrefaceTimeout = errors.New("timeout waiting for client preface") | |
| // readPreface reads the ClientPreface greeting from the peer or | |
| // returns errPrefaceTimeout on timeout, or an error if the greeting | |
| // is invalid. | |
| func (sc *http2serverConn) readPreface() error { | |
| errc := make(chan error, 1) | |
| go func() { | |
| // Read the client preface | |
| buf := make([]byte, len(http2ClientPreface)) | |
| if _, err := io.ReadFull(sc.conn, buf); err != nil { | |
| errc <- err | |
| } else if !bytes.Equal(buf, http2clientPreface) { | |
| errc <- fmt.Errorf("bogus greeting %q", buf) | |
| } else { | |
| errc <- nil | |
| } | |
| }() | |
| timer := time.NewTimer(http2prefaceTimeout) // TODO: configurable on *Server? | |
| defer timer.Stop() | |
| select { | |
| case <-timer.C: | |
| return http2errPrefaceTimeout | |
| case err := <-errc: | |
| if err == nil { | |
| if http2VerboseLogs { | |
| sc.vlogf("http2: server: client %v said hello", sc.conn.RemoteAddr()) | |
| } | |
| } | |
| return err | |
| } | |
| } | |
| var http2errChanPool = sync.Pool{ | |
| New: func() interface{} { return make(chan error, 1) }, | |
| } | |
| var http2writeDataPool = sync.Pool{ | |
| New: func() interface{} { return new(http2writeData) }, | |
| } | |
| // writeDataFromHandler writes DATA response frames from a handler on | |
| // the given stream. | |
| func (sc *http2serverConn) writeDataFromHandler(stream *http2stream, data []byte, endStream bool) error { | |
| ch := http2errChanPool.Get().(chan error) | |
| writeArg := http2writeDataPool.Get().(*http2writeData) | |
| *writeArg = http2writeData{stream.id, data, endStream} | |
| err := sc.writeFrameFromHandler(http2FrameWriteRequest{ | |
| write: writeArg, | |
| stream: stream, | |
| done: ch, | |
| }) | |
| if err != nil { | |
| return err | |
| } | |
| var frameWriteDone bool // the frame write is done (successfully or not) | |
| select { | |
| case err = <-ch: | |
| frameWriteDone = true | |
| case <-sc.doneServing: | |
| return http2errClientDisconnected | |
| case <-stream.cw: | |
| // If both ch and stream.cw were ready (as might | |
| // happen on the final Write after an http.Handler | |
| // ends), prefer the write result. Otherwise this | |
| // might just be us successfully closing the stream. | |
| // The writeFrameAsync and serve goroutines guarantee | |
| // that the ch send will happen before the stream.cw | |
| // close. | |
| select { | |
| case err = <-ch: | |
| frameWriteDone = true | |
| default: | |
| return http2errStreamClosed | |
| } | |
| } | |
| http2errChanPool.Put(ch) | |
| if frameWriteDone { | |
| http2writeDataPool.Put(writeArg) | |
| } | |
| return err | |
| } | |
| // writeFrameFromHandler sends wr to sc.wantWriteFrameCh, but aborts | |
| // if the connection has gone away. | |
| // | |
| // This must not be run from the serve goroutine itself, else it might | |
| // deadlock writing to sc.wantWriteFrameCh (which is only mildly | |
| // buffered and is read by serve itself). If you're on the serve | |
| // goroutine, call writeFrame instead. | |
| func (sc *http2serverConn) writeFrameFromHandler(wr http2FrameWriteRequest) error { | |
| sc.serveG.checkNotOn() // NOT | |
| select { | |
| case sc.wantWriteFrameCh <- wr: | |
| return nil | |
| case <-sc.doneServing: | |
| // Serve loop is gone. | |
| // Client has closed their connection to the server. | |
| return http2errClientDisconnected | |
| } | |
| } | |
| // writeFrame schedules a frame to write and sends it if there's nothing | |
| // already being written. | |
| // | |
| // There is no pushback here (the serve goroutine never blocks). It's | |
| // the http.Handlers that block, waiting for their previous frames to | |
| // make it onto the wire | |
| // | |
| // If you're not on the serve goroutine, use writeFrameFromHandler instead. | |
| func (sc *http2serverConn) writeFrame(wr http2FrameWriteRequest) { | |
| sc.serveG.check() | |
| // If true, wr will not be written and wr.done will not be signaled. | |
| var ignoreWrite bool | |
| // We are not allowed to write frames on closed streams. RFC 7540 Section | |
| // 5.1.1 says: "An endpoint MUST NOT send frames other than PRIORITY on | |
| // a closed stream." Our server never sends PRIORITY, so that exception | |
| // does not apply. | |
| // | |
| // The serverConn might close an open stream while the stream's handler | |
| // is still running. For example, the server might close a stream when it | |
| // receives bad data from the client. If this happens, the handler might | |
| // attempt to write a frame after the stream has been closed (since the | |
| // handler hasn't yet been notified of the close). In this case, we simply | |
| // ignore the frame. The handler will notice that the stream is closed when | |
| // it waits for the frame to be written. | |
| // | |
| // As an exception to this rule, we allow sending RST_STREAM after close. | |
| // This allows us to immediately reject new streams without tracking any | |
| // state for those streams (except for the queued RST_STREAM frame). This | |
| // may result in duplicate RST_STREAMs in some cases, but the client should | |
| // ignore those. | |
| if wr.StreamID() != 0 { | |
| _, isReset := wr.write.(http2StreamError) | |
| if state, _ := sc.state(wr.StreamID()); state == http2stateClosed && !isReset { | |
| ignoreWrite = true | |
| } | |
| } | |
| // Don't send a 100-continue response if we've already sent headers. | |
| // See golang.org/issue/14030. | |
| switch wr.write.(type) { | |
| case *http2writeResHeaders: | |
| wr.stream.wroteHeaders = true | |
| case http2write100ContinueHeadersFrame: | |
| if wr.stream.wroteHeaders { | |
| // We do not need to notify wr.done because this frame is | |
| // never written with wr.done != nil. | |
| if wr.done != nil { | |
| panic("wr.done != nil for write100ContinueHeadersFrame") | |
| } | |
| ignoreWrite = true | |
| } | |
| } | |
| if !ignoreWrite { | |
| if wr.isControl() { | |
| sc.queuedControlFrames++ | |
| // For extra safety, detect wraparounds, which should not happen, | |
| // and pull the plug. | |
| if sc.queuedControlFrames < 0 { | |
| sc.conn.Close() | |
| } | |
| } | |
| sc.writeSched.Push(wr) | |
| } | |
| sc.scheduleFrameWrite() | |
| } | |
| // startFrameWrite starts a goroutine to write wr (in a separate | |
| // goroutine since that might block on the network), and updates the | |
| // serve goroutine's state about the world, updated from info in wr. | |
| func (sc *http2serverConn) startFrameWrite(wr http2FrameWriteRequest) { | |
| sc.serveG.check() | |
| if sc.writingFrame { | |
| panic("internal error: can only be writing one frame at a time") | |
| } | |
| st := wr.stream | |
| if st != nil { | |
| switch st.state { | |
| case http2stateHalfClosedLocal: | |
| switch wr.write.(type) { | |
| case http2StreamError, http2handlerPanicRST, http2writeWindowUpdate: | |
| // RFC 7540 Section 5.1 allows sending RST_STREAM, PRIORITY, and WINDOW_UPDATE | |
| // in this state. (We never send PRIORITY from the server, so that is not checked.) | |
| default: | |
| panic(fmt.Sprintf("internal error: attempt to send frame on a half-closed-local stream: %v", wr)) | |
| } | |
| case http2stateClosed: | |
| panic(fmt.Sprintf("internal error: attempt to send frame on a closed stream: %v", wr)) | |
| } | |
| } | |
| if wpp, ok := wr.write.(*http2writePushPromise); ok { | |
| var err error | |
| wpp.promisedID, err = wpp.allocatePromisedID() | |
| if err != nil { | |
| sc.writingFrameAsync = false | |
| wr.replyToWriter(err) | |
| return | |
| } | |
| } | |
| sc.writingFrame = true | |
| sc.needsFrameFlush = true | |
| if wr.write.staysWithinBuffer(sc.bw.Available()) { | |
| sc.writingFrameAsync = false | |
| err := wr.write.writeFrame(sc) | |
| sc.wroteFrame(http2frameWriteResult{wr: wr, err: err}) | |
| } else { | |
| sc.writingFrameAsync = true | |
| go sc.writeFrameAsync(wr) | |
| } | |
| } | |
| // errHandlerPanicked is the error given to any callers blocked in a read from | |
| // Request.Body when the main goroutine panics. Since most handlers read in the | |
| // main ServeHTTP goroutine, this will show up rarely. | |
| var http2errHandlerPanicked = errors.New("http2: handler panicked") | |
| // wroteFrame is called on the serve goroutine with the result of | |
| // whatever happened on writeFrameAsync. | |
| func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) { | |
| sc.serveG.check() | |
| if !sc.writingFrame { | |
| panic("internal error: expected to be already writing a frame") | |
| } | |
| sc.writingFrame = false | |
| sc.writingFrameAsync = false | |
| wr := res.wr | |
| if http2writeEndsStream(wr.write) { | |
| st := wr.stream | |
| if st == nil { | |
| panic("internal error: expecting non-nil stream") | |
| } | |
| switch st.state { | |
| case http2stateOpen: | |
| // Here we would go to stateHalfClosedLocal in | |
| // theory, but since our handler is done and | |
| // the net/http package provides no mechanism | |
| // for closing a ResponseWriter while still | |
| // reading data (see possible TODO at top of | |
| // this file), we go into closed state here | |
| // anyway, after telling the peer we're | |
| // hanging up on them. We'll transition to | |
| // stateClosed after the RST_STREAM frame is | |
| // written. | |
| st.state = http2stateHalfClosedLocal | |
| // Section 8.1: a server MAY request that the client abort | |
| // transmission of a request without error by sending a | |
| // RST_STREAM with an error code of NO_ERROR after sending | |
| // a complete response. | |
| sc.resetStream(http2streamError(st.id, http2ErrCodeNo)) | |
| case http2stateHalfClosedRemote: | |
| sc.closeStream(st, http2errHandlerComplete) | |
| } | |
| } else { | |
| switch v := wr.write.(type) { | |
| case http2StreamError: | |
| // st may be unknown if the RST_STREAM was generated to reject bad input. | |
| if st, ok := sc.streams[v.StreamID]; ok { | |
| sc.closeStream(st, v) | |
| } | |
| case http2handlerPanicRST: | |
| sc.closeStream(wr.stream, http2errHandlerPanicked) | |
| } | |
| } | |
| // Reply (if requested) to unblock the ServeHTTP goroutine. | |
| wr.replyToWriter(res.err) | |
| sc.scheduleFrameWrite() | |
| } | |
| // scheduleFrameWrite tickles the frame writing scheduler. | |
| // | |
| // If a frame is already being written, nothing happens. This will be called again | |
| // when the frame is done being written. | |
| // | |
| // If a frame isn't being written and we need to send one, the best frame | |
| // to send is selected by writeSched. | |
| // | |
| // If a frame isn't being written and there's nothing else to send, we | |
| // flush the write buffer. | |
| func (sc *http2serverConn) scheduleFrameWrite() { | |
| sc.serveG.check() | |
| if sc.writingFrame || sc.inFrameScheduleLoop { | |
| return | |
| } | |
| sc.inFrameScheduleLoop = true | |
| for !sc.writingFrameAsync { | |
| if sc.needToSendGoAway { | |
| sc.needToSendGoAway = false | |
| sc.startFrameWrite(http2FrameWriteRequest{ | |
| write: &http2writeGoAway{ | |
| maxStreamID: sc.maxClientStreamID, | |
| code: sc.goAwayCode, | |
| }, | |
| }) | |
| continue | |
| } | |
| if sc.needToSendSettingsAck { | |
| sc.needToSendSettingsAck = false | |
| sc.startFrameWrite(http2FrameWriteRequest{write: http2writeSettingsAck{}}) | |
| continue | |
| } | |
| if !sc.inGoAway || sc.goAwayCode == http2ErrCodeNo { | |
| if wr, ok := sc.writeSched.Pop(); ok { | |
| if wr.isControl() { | |
| sc.queuedControlFrames-- | |
| } | |
| sc.startFrameWrite(wr) | |
| continue | |
| } | |
| } | |
| if sc.needsFrameFlush { | |
| sc.startFrameWrite(http2FrameWriteRequest{write: http2flushFrameWriter{}}) | |
| sc.needsFrameFlush = false // after startFrameWrite, since it sets this true | |
| continue | |
| } | |
| break | |
| } | |
| sc.inFrameScheduleLoop = false | |
| } | |
| // startGracefulShutdown gracefully shuts down a connection. This | |
| // sends GOAWAY with ErrCodeNo to tell the client we're gracefully | |
| // shutting down. The connection isn't closed until all current | |
| // streams are done. | |
| // | |
| // startGracefulShutdown returns immediately; it does not wait until | |
| // the connection has shut down. | |
| func (sc *http2serverConn) startGracefulShutdown() { | |
| sc.serveG.checkNotOn() // NOT | |
| sc.shutdownOnce.Do(func() { sc.sendServeMsg(http2gracefulShutdownMsg) }) | |
| } | |
| // After sending GOAWAY, the connection will close after goAwayTimeout. | |
| // If we close the connection immediately after sending GOAWAY, there may | |
| // be unsent data in our kernel receive buffer, which will cause the kernel | |
| // to send a TCP RST on close() instead of a FIN. This RST will abort the | |
| // connection immediately, whether or not the client had received the GOAWAY. | |
| // | |
| // Ideally we should delay for at least 1 RTT + epsilon so the client has | |
| // a chance to read the GOAWAY and stop sending messages. Measuring RTT | |
| // is hard, so we approximate with 1 second. See golang.org/issue/18701. | |
| // | |
| // This is a var so it can be shorter in tests, where all requests uses the | |
| // loopback interface making the expected RTT very small. | |
| // | |
| // TODO: configurable? | |
| var http2goAwayTimeout = 1 * time.Second | |
| func (sc *http2serverConn) startGracefulShutdownInternal() { | |
| sc.goAway(http2ErrCodeNo) | |
| } | |
| func (sc *http2serverConn) goAway(code http2ErrCode) { | |
| sc.serveG.check() | |
| if sc.inGoAway { | |
| return | |
| } | |
| sc.inGoAway = true | |
| sc.needToSendGoAway = true | |
| sc.goAwayCode = code | |
| sc.scheduleFrameWrite() | |
| } | |
| func (sc *http2serverConn) shutDownIn(d time.Duration) { | |
| sc.serveG.check() | |
| sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer) | |
| } | |
| func (sc *http2serverConn) resetStream(se http2StreamError) { | |
| sc.serveG.check() | |
| sc.writeFrame(http2FrameWriteRequest{write: se}) | |
| if st, ok := sc.streams[se.StreamID]; ok { | |
| st.resetQueued = true | |
| } | |
| } | |
| // processFrameFromReader processes the serve loop's read from readFrameCh from the | |
| // frame-reading goroutine. | |
| // processFrameFromReader returns whether the connection should be kept open. | |
| func (sc *http2serverConn) processFrameFromReader(res http2readFrameResult) bool { | |
| sc.serveG.check() | |
| err := res.err | |
| if err != nil { | |
| if err == http2ErrFrameTooLarge { | |
| sc.goAway(http2ErrCodeFrameSize) | |
| return true // goAway will close the loop | |
| } | |
| clientGone := err == io.EOF || err == io.ErrUnexpectedEOF || http2isClosedConnError(err) | |
| if clientGone { | |
| // TODO: could we also get into this state if | |
| // the peer does a half close | |
| // (e.g. CloseWrite) because they're done | |
| // sending frames but they're still wanting | |
| // our open replies? Investigate. | |
| // TODO: add CloseWrite to crypto/tls.Conn first | |
| // so we have a way to test this? I suppose | |
| // just for testing we could have a non-TLS mode. | |
| return false | |
| } | |
| } else { | |
| f := res.f | |
| if http2VerboseLogs { | |
| sc.vlogf("http2: server read frame %v", http2summarizeFrame(f)) | |
| } | |
| err = sc.processFrame(f) | |
| if err == nil { | |
| return true | |
| } | |
| } | |
| switch ev := err.(type) { | |
| case http2StreamError: | |
| sc.resetStream(ev) | |
| return true | |
| case http2goAwayFlowError: | |
| sc.goAway(http2ErrCodeFlowControl) | |
| return true | |
| case http2ConnectionError: | |
| sc.logf("http2: server connection error from %v: %v", sc.conn.RemoteAddr(), ev) | |
| sc.goAway(http2ErrCode(ev)) | |
| return true // goAway will handle shutdown | |
| default: | |
| if res.err != nil { | |
| sc.vlogf("http2: server closing client connection; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err) | |
| } else { | |
| sc.logf("http2: server closing client connection: %v", err) | |
| } | |
| return false | |
| } | |
| } | |
| func (sc *http2serverConn) processFrame(f http2Frame) error { | |
| sc.serveG.check() | |
| // First frame received must be SETTINGS. | |
| if !sc.sawFirstSettings { | |
| if _, ok := f.(*http2SettingsFrame); !ok { | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| sc.sawFirstSettings = true | |
| } | |
| switch f := f.(type) { | |
| case *http2SettingsFrame: | |
| return sc.processSettings(f) | |
| case *http2MetaHeadersFrame: | |
| return sc.processHeaders(f) | |
| case *http2WindowUpdateFrame: | |
| return sc.processWindowUpdate(f) | |
| case *http2PingFrame: | |
| return sc.processPing(f) | |
| case *http2DataFrame: | |
| return sc.processData(f) | |
| case *http2RSTStreamFrame: | |
| return sc.processResetStream(f) | |
| case *http2PriorityFrame: | |
| return sc.processPriority(f) | |
| case *http2GoAwayFrame: | |
| return sc.processGoAway(f) | |
| case *http2PushPromiseFrame: | |
| // A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE | |
| // frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| default: | |
| sc.vlogf("http2: server ignoring frame: %v", f.Header()) | |
| return nil | |
| } | |
| } | |
| func (sc *http2serverConn) processPing(f *http2PingFrame) error { | |
| sc.serveG.check() | |
| if f.IsAck() { | |
| // 6.7 PING: " An endpoint MUST NOT respond to PING frames | |
| // containing this flag." | |
| return nil | |
| } | |
| if f.StreamID != 0 { | |
| // "PING frames are not associated with any individual | |
| // stream. If a PING frame is received with a stream | |
| // identifier field value other than 0x0, the recipient MUST | |
| // respond with a connection error (Section 5.4.1) of type | |
| // PROTOCOL_ERROR." | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if sc.inGoAway && sc.goAwayCode != http2ErrCodeNo { | |
| return nil | |
| } | |
| sc.writeFrame(http2FrameWriteRequest{write: http2writePingAck{f}}) | |
| return nil | |
| } | |
| func (sc *http2serverConn) processWindowUpdate(f *http2WindowUpdateFrame) error { | |
| sc.serveG.check() | |
| switch { | |
| case f.StreamID != 0: // stream-level flow control | |
| state, st := sc.state(f.StreamID) | |
| if state == http2stateIdle { | |
| // Section 5.1: "Receiving any frame other than HEADERS | |
| // or PRIORITY on a stream in this state MUST be | |
| // treated as a connection error (Section 5.4.1) of | |
| // type PROTOCOL_ERROR." | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if st == nil { | |
| // "WINDOW_UPDATE can be sent by a peer that has sent a | |
| // frame bearing the END_STREAM flag. This means that a | |
| // receiver could receive a WINDOW_UPDATE frame on a "half | |
| // closed (remote)" or "closed" stream. A receiver MUST | |
| // NOT treat this as an error, see Section 5.1." | |
| return nil | |
| } | |
| if !st.flow.add(int32(f.Increment)) { | |
| return http2streamError(f.StreamID, http2ErrCodeFlowControl) | |
| } | |
| default: // connection-level flow control | |
| if !sc.flow.add(int32(f.Increment)) { | |
| return http2goAwayFlowError{} | |
| } | |
| } | |
| sc.scheduleFrameWrite() | |
| return nil | |
| } | |
| func (sc *http2serverConn) processResetStream(f *http2RSTStreamFrame) error { | |
| sc.serveG.check() | |
| state, st := sc.state(f.StreamID) | |
| if state == http2stateIdle { | |
| // 6.4 "RST_STREAM frames MUST NOT be sent for a | |
| // stream in the "idle" state. If a RST_STREAM frame | |
| // identifying an idle stream is received, the | |
| // recipient MUST treat this as a connection error | |
| // (Section 5.4.1) of type PROTOCOL_ERROR. | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if st != nil { | |
| st.cancelCtx() | |
| sc.closeStream(st, http2streamError(f.StreamID, f.ErrCode)) | |
| } | |
| return nil | |
| } | |
| func (sc *http2serverConn) closeStream(st *http2stream, err error) { | |
| sc.serveG.check() | |
| if st.state == http2stateIdle || st.state == http2stateClosed { | |
| panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state)) | |
| } | |
| st.state = http2stateClosed | |
| if st.writeDeadline != nil { | |
| st.writeDeadline.Stop() | |
| } | |
| if st.isPushed() { | |
| sc.curPushedStreams-- | |
| } else { | |
| sc.curClientStreams-- | |
| } | |
| delete(sc.streams, st.id) | |
| if len(sc.streams) == 0 { | |
| sc.setConnState(StateIdle) | |
| if sc.srv.IdleTimeout != 0 { | |
| sc.idleTimer.Reset(sc.srv.IdleTimeout) | |
| } | |
| if http2h1ServerKeepAlivesDisabled(sc.hs) { | |
| sc.startGracefulShutdownInternal() | |
| } | |
| } | |
| if p := st.body; p != nil { | |
| // Return any buffered unread bytes worth of conn-level flow control. | |
| // See golang.org/issue/16481 | |
| sc.sendWindowUpdate(nil, p.Len()) | |
| p.CloseWithError(err) | |
| } | |
| st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc | |
| sc.writeSched.CloseStream(st.id) | |
| } | |
| func (sc *http2serverConn) processSettings(f *http2SettingsFrame) error { | |
| sc.serveG.check() | |
| if f.IsAck() { | |
| sc.unackedSettings-- | |
| if sc.unackedSettings < 0 { | |
| // Why is the peer ACKing settings we never sent? | |
| // The spec doesn't mention this case, but | |
| // hang up on them anyway. | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| return nil | |
| } | |
| if f.NumSettings() > 100 || f.HasDuplicates() { | |
| // This isn't actually in the spec, but hang up on | |
| // suspiciously large settings frames or those with | |
| // duplicate entries. | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if err := f.ForeachSetting(sc.processSetting); err != nil { | |
| return err | |
| } | |
| // TODO: judging by RFC 7540, Section 6.5.3 each SETTINGS frame should be | |
| // acknowledged individually, even if multiple are received before the ACK. | |
| sc.needToSendSettingsAck = true | |
| sc.scheduleFrameWrite() | |
| return nil | |
| } | |
| func (sc *http2serverConn) processSetting(s http2Setting) error { | |
| sc.serveG.check() | |
| if err := s.Valid(); err != nil { | |
| return err | |
| } | |
| if http2VerboseLogs { | |
| sc.vlogf("http2: server processing setting %v", s) | |
| } | |
| switch s.ID { | |
| case http2SettingHeaderTableSize: | |
| sc.headerTableSize = s.Val | |
| sc.hpackEncoder.SetMaxDynamicTableSize(s.Val) | |
| case http2SettingEnablePush: | |
| sc.pushEnabled = s.Val != 0 | |
| case http2SettingMaxConcurrentStreams: | |
| sc.clientMaxStreams = s.Val | |
| case http2SettingInitialWindowSize: | |
| return sc.processSettingInitialWindowSize(s.Val) | |
| case http2SettingMaxFrameSize: | |
| sc.maxFrameSize = int32(s.Val) // the maximum valid s.Val is < 2^31 | |
| case http2SettingMaxHeaderListSize: | |
| sc.peerMaxHeaderListSize = s.Val | |
| default: | |
| // Unknown setting: "An endpoint that receives a SETTINGS | |
| // frame with any unknown or unsupported identifier MUST | |
| // ignore that setting." | |
| if http2VerboseLogs { | |
| sc.vlogf("http2: server ignoring unknown setting %v", s) | |
| } | |
| } | |
| return nil | |
| } | |
| func (sc *http2serverConn) processSettingInitialWindowSize(val uint32) error { | |
| sc.serveG.check() | |
| // Note: val already validated to be within range by | |
| // processSetting's Valid call. | |
| // "A SETTINGS frame can alter the initial flow control window | |
| // size for all current streams. When the value of | |
| // SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST | |
| // adjust the size of all stream flow control windows that it | |
| // maintains by the difference between the new value and the | |
| // old value." | |
| old := sc.initialStreamSendWindowSize | |
| sc.initialStreamSendWindowSize = int32(val) | |
| growth := int32(val) - old // may be negative | |
| for _, st := range sc.streams { | |
| if !st.flow.add(growth) { | |
| // 6.9.2 Initial Flow Control Window Size | |
| // "An endpoint MUST treat a change to | |
| // SETTINGS_INITIAL_WINDOW_SIZE that causes any flow | |
| // control window to exceed the maximum size as a | |
| // connection error (Section 5.4.1) of type | |
| // FLOW_CONTROL_ERROR." | |
| return http2ConnectionError(http2ErrCodeFlowControl) | |
| } | |
| } | |
| return nil | |
| } | |
| func (sc *http2serverConn) processData(f *http2DataFrame) error { | |
| sc.serveG.check() | |
| if sc.inGoAway && sc.goAwayCode != http2ErrCodeNo { | |
| return nil | |
| } | |
| data := f.Data() | |
| // "If a DATA frame is received whose stream is not in "open" | |
| // or "half closed (local)" state, the recipient MUST respond | |
| // with a stream error (Section 5.4.2) of type STREAM_CLOSED." | |
| id := f.Header().StreamID | |
| state, st := sc.state(id) | |
| if id == 0 || state == http2stateIdle { | |
| // Section 5.1: "Receiving any frame other than HEADERS | |
| // or PRIORITY on a stream in this state MUST be | |
| // treated as a connection error (Section 5.4.1) of | |
| // type PROTOCOL_ERROR." | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if st == nil || state != http2stateOpen || st.gotTrailerHeader || st.resetQueued { | |
| // This includes sending a RST_STREAM if the stream is | |
| // in stateHalfClosedLocal (which currently means that | |
| // the http.Handler returned, so it's done reading & | |
| // done writing). Try to stop the client from sending | |
| // more DATA. | |
| // But still enforce their connection-level flow control, | |
| // and return any flow control bytes since we're not going | |
| // to consume them. | |
| if sc.inflow.available() < int32(f.Length) { | |
| return http2streamError(id, http2ErrCodeFlowControl) | |
| } | |
| // Deduct the flow control from inflow, since we're | |
| // going to immediately add it back in | |
| // sendWindowUpdate, which also schedules sending the | |
| // frames. | |
| sc.inflow.take(int32(f.Length)) | |
| sc.sendWindowUpdate(nil, int(f.Length)) // conn-level | |
| if st != nil && st.resetQueued { | |
| // Already have a stream error in flight. Don't send another. | |
| return nil | |
| } | |
| return http2streamError(id, http2ErrCodeStreamClosed) | |
| } | |
| if st.body == nil { | |
| panic("internal error: should have a body in this state") | |
| } | |
| // Sender sending more than they'd declared? | |
| if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes { | |
| st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes)) | |
| // RFC 7540, sec 8.1.2.6: A request or response is also malformed if the | |
| // value of a content-length header field does not equal the sum of the | |
| // DATA frame payload lengths that form the body. | |
| return http2streamError(id, http2ErrCodeProtocol) | |
| } | |
| if f.Length > 0 { | |
| // Check whether the client has flow control quota. | |
| if st.inflow.available() < int32(f.Length) { | |
| return http2streamError(id, http2ErrCodeFlowControl) | |
| } | |
| st.inflow.take(int32(f.Length)) | |
| if len(data) > 0 { | |
| wrote, err := st.body.Write(data) | |
| if err != nil { | |
| sc.sendWindowUpdate(nil, int(f.Length)-wrote) | |
| return http2streamError(id, http2ErrCodeStreamClosed) | |
| } | |
| if wrote != len(data) { | |
| panic("internal error: bad Writer") | |
| } | |
| st.bodyBytes += int64(len(data)) | |
| } | |
| // Return any padded flow control now, since we won't | |
| // refund it later on body reads. | |
| if pad := int32(f.Length) - int32(len(data)); pad > 0 { | |
| sc.sendWindowUpdate32(nil, pad) | |
| sc.sendWindowUpdate32(st, pad) | |
| } | |
| } | |
| if f.StreamEnded() { | |
| st.endStream() | |
| } | |
| return nil | |
| } | |
| func (sc *http2serverConn) processGoAway(f *http2GoAwayFrame) error { | |
| sc.serveG.check() | |
| if f.ErrCode != http2ErrCodeNo { | |
| sc.logf("http2: received GOAWAY %+v, starting graceful shutdown", f) | |
| } else { | |
| sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f) | |
| } | |
| sc.startGracefulShutdownInternal() | |
| // http://tools.ietf.org/html/rfc7540#section-6.8 | |
| // We should not create any new streams, which means we should disable push. | |
| sc.pushEnabled = false | |
| return nil | |
| } | |
| // isPushed reports whether the stream is server-initiated. | |
| func (st *http2stream) isPushed() bool { | |
| return st.id%2 == 0 | |
| } | |
| // endStream closes a Request.Body's pipe. It is called when a DATA | |
| // frame says a request body is over (or after trailers). | |
| func (st *http2stream) endStream() { | |
| sc := st.sc | |
| sc.serveG.check() | |
| if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes { | |
| st.body.CloseWithError(fmt.Errorf("request declared a Content-Length of %d but only wrote %d bytes", | |
| st.declBodyBytes, st.bodyBytes)) | |
| } else { | |
| st.body.closeWithErrorAndCode(io.EOF, st.copyTrailersToHandlerRequest) | |
| st.body.CloseWithError(io.EOF) | |
| } | |
| st.state = http2stateHalfClosedRemote | |
| } | |
| // copyTrailersToHandlerRequest is run in the Handler's goroutine in | |
| // its Request.Body.Read just before it gets io.EOF. | |
| func (st *http2stream) copyTrailersToHandlerRequest() { | |
| for k, vv := range st.trailer { | |
| if _, ok := st.reqTrailer[k]; ok { | |
| // Only copy it over it was pre-declared. | |
| st.reqTrailer[k] = vv | |
| } | |
| } | |
| } | |
| // onWriteTimeout is run on its own goroutine (from time.AfterFunc) | |
| // when the stream's WriteTimeout has fired. | |
| func (st *http2stream) onWriteTimeout() { | |
| st.sc.writeFrameFromHandler(http2FrameWriteRequest{write: http2streamError(st.id, http2ErrCodeInternal)}) | |
| } | |
| func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { | |
| sc.serveG.check() | |
| id := f.StreamID | |
| if sc.inGoAway { | |
| // Ignore. | |
| return nil | |
| } | |
| // http://tools.ietf.org/html/rfc7540#section-5.1.1 | |
| // Streams initiated by a client MUST use odd-numbered stream | |
| // identifiers. [...] An endpoint that receives an unexpected | |
| // stream identifier MUST respond with a connection error | |
| // (Section 5.4.1) of type PROTOCOL_ERROR. | |
| if id%2 != 1 { | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| // A HEADERS frame can be used to create a new stream or | |
| // send a trailer for an open one. If we already have a stream | |
| // open, let it process its own HEADERS frame (trailers at this | |
| // point, if it's valid). | |
| if st := sc.streams[f.StreamID]; st != nil { | |
| if st.resetQueued { | |
| // We're sending RST_STREAM to close the stream, so don't bother | |
| // processing this frame. | |
| return nil | |
| } | |
| // RFC 7540, sec 5.1: If an endpoint receives additional frames, other than | |
| // WINDOW_UPDATE, PRIORITY, or RST_STREAM, for a stream that is in | |
| // this state, it MUST respond with a stream error (Section 5.4.2) of | |
| // type STREAM_CLOSED. | |
| if st.state == http2stateHalfClosedRemote { | |
| return http2streamError(id, http2ErrCodeStreamClosed) | |
| } | |
| return st.processTrailerHeaders(f) | |
| } | |
| // [...] The identifier of a newly established stream MUST be | |
| // numerically greater than all streams that the initiating | |
| // endpoint has opened or reserved. [...] An endpoint that | |
| // receives an unexpected stream identifier MUST respond with | |
| // a connection error (Section 5.4.1) of type PROTOCOL_ERROR. | |
| if id <= sc.maxClientStreamID { | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| sc.maxClientStreamID = id | |
| if sc.idleTimer != nil { | |
| sc.idleTimer.Stop() | |
| } | |
| // http://tools.ietf.org/html/rfc7540#section-5.1.2 | |
| // [...] Endpoints MUST NOT exceed the limit set by their peer. An | |
| // endpoint that receives a HEADERS frame that causes their | |
| // advertised concurrent stream limit to be exceeded MUST treat | |
| // this as a stream error (Section 5.4.2) of type PROTOCOL_ERROR | |
| // or REFUSED_STREAM. | |
| if sc.curClientStreams+1 > sc.advMaxStreams { | |
| if sc.unackedSettings == 0 { | |
| // They should know better. | |
| return http2streamError(id, http2ErrCodeProtocol) | |
| } | |
| // Assume it's a network race, where they just haven't | |
| // received our last SETTINGS update. But actually | |
| // this can't happen yet, because we don't yet provide | |
| // a way for users to adjust server parameters at | |
| // runtime. | |
| return http2streamError(id, http2ErrCodeRefusedStream) | |
| } | |
| initialState := http2stateOpen | |
| if f.StreamEnded() { | |
| initialState = http2stateHalfClosedRemote | |
| } | |
| st := sc.newStream(id, 0, initialState) | |
| if f.HasPriority() { | |
| if err := http2checkPriority(f.StreamID, f.Priority); err != nil { | |
| return err | |
| } | |
| sc.writeSched.AdjustStream(st.id, f.Priority) | |
| } | |
| rw, req, err := sc.newWriterAndRequest(st, f) | |
| if err != nil { | |
| return err | |
| } | |
| st.reqTrailer = req.Trailer | |
| if st.reqTrailer != nil { | |
| st.trailer = make(Header) | |
| } | |
| st.body = req.Body.(*http2requestBody).pipe // may be nil | |
| st.declBodyBytes = req.ContentLength | |
| handler := sc.handler.ServeHTTP | |
| if f.Truncated { | |
| // Their header list was too long. Send a 431 error. | |
| handler = http2handleHeaderListTooLong | |
| } else if err := http2checkValidHTTP2RequestHeaders(req.Header); err != nil { | |
| handler = http2new400Handler(err) | |
| } | |
| // The net/http package sets the read deadline from the | |
| // http.Server.ReadTimeout during the TLS handshake, but then | |
| // passes the connection off to us with the deadline already | |
| // set. Disarm it here after the request headers are read, | |
| // similar to how the http1 server works. Here it's | |
| // technically more like the http1 Server's ReadHeaderTimeout | |
| // (in Go 1.8), though. That's a more sane option anyway. | |
| if sc.hs.ReadTimeout != 0 { | |
| sc.conn.SetReadDeadline(time.Time{}) | |
| } | |
| go sc.runHandler(rw, req, handler) | |
| return nil | |
| } | |
| func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error { | |
| sc := st.sc | |
| sc.serveG.check() | |
| if st.gotTrailerHeader { | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| st.gotTrailerHeader = true | |
| if !f.StreamEnded() { | |
| return http2streamError(st.id, http2ErrCodeProtocol) | |
| } | |
| if len(f.PseudoFields()) > 0 { | |
| return http2streamError(st.id, http2ErrCodeProtocol) | |
| } | |
| if st.trailer != nil { | |
| for _, hf := range f.RegularFields() { | |
| key := sc.canonicalHeader(hf.Name) | |
| if !httpguts.ValidTrailerHeader(key) { | |
| // TODO: send more details to the peer somehow. But http2 has | |
| // no way to send debug data at a stream level. Discuss with | |
| // HTTP folk. | |
| return http2streamError(st.id, http2ErrCodeProtocol) | |
| } | |
| st.trailer[key] = append(st.trailer[key], hf.Value) | |
| } | |
| } | |
| st.endStream() | |
| return nil | |
| } | |
| func http2checkPriority(streamID uint32, p http2PriorityParam) error { | |
| if streamID == p.StreamDep { | |
| // Section 5.3.1: "A stream cannot depend on itself. An endpoint MUST treat | |
| // this as a stream error (Section 5.4.2) of type PROTOCOL_ERROR." | |
| // Section 5.3.3 says that a stream can depend on one of its dependencies, | |
| // so it's only self-dependencies that are forbidden. | |
| return http2streamError(streamID, http2ErrCodeProtocol) | |
| } | |
| return nil | |
| } | |
| func (sc *http2serverConn) processPriority(f *http2PriorityFrame) error { | |
| if sc.inGoAway { | |
| return nil | |
| } | |
| if err := http2checkPriority(f.StreamID, f.http2PriorityParam); err != nil { | |
| return err | |
| } | |
| sc.writeSched.AdjustStream(f.StreamID, f.http2PriorityParam) | |
| return nil | |
| } | |
| func (sc *http2serverConn) newStream(id, pusherID uint32, state http2streamState) *http2stream { | |
| sc.serveG.check() | |
| if id == 0 { | |
| panic("internal error: cannot create stream with id 0") | |
| } | |
| ctx, cancelCtx := context.WithCancel(sc.baseCtx) | |
| st := &http2stream{ | |
| sc: sc, | |
| id: id, | |
| state: state, | |
| ctx: ctx, | |
| cancelCtx: cancelCtx, | |
| } | |
| st.cw.Init() | |
| st.flow.conn = &sc.flow // link to conn-level counter | |
| st.flow.add(sc.initialStreamSendWindowSize) | |
| st.inflow.conn = &sc.inflow // link to conn-level counter | |
| st.inflow.add(sc.srv.initialStreamRecvWindowSize()) | |
| if sc.hs.WriteTimeout != 0 { | |
| st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) | |
| } | |
| sc.streams[id] = st | |
| sc.writeSched.OpenStream(st.id, http2OpenStreamOptions{PusherID: pusherID}) | |
| if st.isPushed() { | |
| sc.curPushedStreams++ | |
| } else { | |
| sc.curClientStreams++ | |
| } | |
| if sc.curOpenStreams() == 1 { | |
| sc.setConnState(StateActive) | |
| } | |
| return st | |
| } | |
| func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHeadersFrame) (*http2responseWriter, *Request, error) { | |
| sc.serveG.check() | |
| rp := http2requestParam{ | |
| method: f.PseudoValue("method"), | |
| scheme: f.PseudoValue("scheme"), | |
| authority: f.PseudoValue("authority"), | |
| path: f.PseudoValue("path"), | |
| } | |
| isConnect := rp.method == "CONNECT" | |
| if isConnect { | |
| if rp.path != "" || rp.scheme != "" || rp.authority == "" { | |
| return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) | |
| } | |
| } else if rp.method == "" || rp.path == "" || (rp.scheme != "https" && rp.scheme != "http") { | |
| // See 8.1.2.6 Malformed Requests and Responses: | |
| // | |
| // Malformed requests or responses that are detected | |
| // MUST be treated as a stream error (Section 5.4.2) | |
| // of type PROTOCOL_ERROR." | |
| // | |
| // 8.1.2.3 Request Pseudo-Header Fields | |
| // "All HTTP/2 requests MUST include exactly one valid | |
| // value for the :method, :scheme, and :path | |
| // pseudo-header fields" | |
| return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) | |
| } | |
| bodyOpen := !f.StreamEnded() | |
| if rp.method == "HEAD" && bodyOpen { | |
| // HEAD requests can't have bodies | |
| return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) | |
| } | |
| rp.header = make(Header) | |
| for _, hf := range f.RegularFields() { | |
| rp.header.Add(sc.canonicalHeader(hf.Name), hf.Value) | |
| } | |
| if rp.authority == "" { | |
| rp.authority = rp.header.Get("Host") | |
| } | |
| rw, req, err := sc.newWriterAndRequestNoBody(st, rp) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| if bodyOpen { | |
| if vv, ok := rp.header["Content-Length"]; ok { | |
| if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil { | |
| req.ContentLength = int64(cl) | |
| } else { | |
| req.ContentLength = 0 | |
| } | |
| } else { | |
| req.ContentLength = -1 | |
| } | |
| req.Body.(*http2requestBody).pipe = &http2pipe{ | |
| b: &http2dataBuffer{expected: req.ContentLength}, | |
| } | |
| } | |
| return rw, req, nil | |
| } | |
| type http2requestParam struct { | |
| method string | |
| scheme, authority, path string | |
| header Header | |
| } | |
| func (sc *http2serverConn) newWriterAndRequestNoBody(st *http2stream, rp http2requestParam) (*http2responseWriter, *Request, error) { | |
| sc.serveG.check() | |
| var tlsState *tls.ConnectionState // nil if not scheme https | |
| if rp.scheme == "https" { | |
| tlsState = sc.tlsState | |
| } | |
| needsContinue := rp.header.Get("Expect") == "100-continue" | |
| if needsContinue { | |
| rp.header.Del("Expect") | |
| } | |
| // Merge Cookie headers into one "; "-delimited value. | |
| if cookies := rp.header["Cookie"]; len(cookies) > 1 { | |
| rp.header.Set("Cookie", strings.Join(cookies, "; ")) | |
| } | |
| // Setup Trailers | |
| var trailer Header | |
| for _, v := range rp.header["Trailer"] { | |
| for _, key := range strings.Split(v, ",") { | |
| key = CanonicalHeaderKey(textproto.TrimString(key)) | |
| switch key { | |
| case "Transfer-Encoding", "Trailer", "Content-Length": | |
| // Bogus. (copy of http1 rules) | |
| // Ignore. | |
| default: | |
| if trailer == nil { | |
| trailer = make(Header) | |
| } | |
| trailer[key] = nil | |
| } | |
| } | |
| } | |
| delete(rp.header, "Trailer") | |
| var url_ *url.URL | |
| var requestURI string | |
| if rp.method == "CONNECT" { | |
| url_ = &url.URL{Host: rp.authority} | |
| requestURI = rp.authority // mimic HTTP/1 server behavior | |
| } else { | |
| var err error | |
| url_, err = url.ParseRequestURI(rp.path) | |
| if err != nil { | |
| return nil, nil, http2streamError(st.id, http2ErrCodeProtocol) | |
| } | |
| requestURI = rp.path | |
| } | |
| body := &http2requestBody{ | |
| conn: sc, | |
| stream: st, | |
| needsContinue: needsContinue, | |
| } | |
| req := &Request{ | |
| Method: rp.method, | |
| URL: url_, | |
| RemoteAddr: sc.remoteAddrStr, | |
| Header: rp.header, | |
| RequestURI: requestURI, | |
| Proto: "HTTP/2.0", | |
| ProtoMajor: 2, | |
| ProtoMinor: 0, | |
| TLS: tlsState, | |
| Host: rp.authority, | |
| Body: body, | |
| Trailer: trailer, | |
| } | |
| req = req.WithContext(st.ctx) | |
| rws := http2responseWriterStatePool.Get().(*http2responseWriterState) | |
| bwSave := rws.bw | |
| *rws = http2responseWriterState{} // zero all the fields | |
| rws.conn = sc | |
| rws.bw = bwSave | |
| rws.bw.Reset(http2chunkWriter{rws}) | |
| rws.stream = st | |
| rws.req = req | |
| rws.body = body | |
| rw := &http2responseWriter{rws: rws} | |
| return rw, req, nil | |
| } | |
| // Run on its own goroutine. | |
| func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, handler func(ResponseWriter, *Request)) { | |
| didPanic := true | |
| defer func() { | |
| rw.rws.stream.cancelCtx() | |
| if didPanic { | |
| e := recover() | |
| sc.writeFrameFromHandler(http2FrameWriteRequest{ | |
| write: http2handlerPanicRST{rw.rws.stream.id}, | |
| stream: rw.rws.stream, | |
| }) | |
| // Same as net/http: | |
| if e != nil && e != ErrAbortHandler { | |
| const size = 64 << 10 | |
| buf := make([]byte, size) | |
| buf = buf[:runtime.Stack(buf, false)] | |
| sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf) | |
| } | |
| return | |
| } | |
| rw.handlerDone() | |
| }() | |
| handler(rw, req) | |
| didPanic = false | |
| } | |
| func http2handleHeaderListTooLong(w ResponseWriter, r *Request) { | |
| // 10.5.1 Limits on Header Block Size: | |
| // .. "A server that receives a larger header block than it is | |
| // willing to handle can send an HTTP 431 (Request Header Fields Too | |
| // Large) status code" | |
| const statusRequestHeaderFieldsTooLarge = 431 // only in Go 1.6+ | |
| w.WriteHeader(statusRequestHeaderFieldsTooLarge) | |
| io.WriteString(w, "<h1>HTTP Error 431</h1><p>Request Header Field(s) Too Large</p>") | |
| } | |
| // called from handler goroutines. | |
| // h may be nil. | |
| func (sc *http2serverConn) writeHeaders(st *http2stream, headerData *http2writeResHeaders) error { | |
| sc.serveG.checkNotOn() // NOT on | |
| var errc chan error | |
| if headerData.h != nil { | |
| // If there's a header map (which we don't own), so we have to block on | |
| // waiting for this frame to be written, so an http.Flush mid-handler | |
| // writes out the correct value of keys, before a handler later potentially | |
| // mutates it. | |
| errc = http2errChanPool.Get().(chan error) | |
| } | |
| if err := sc.writeFrameFromHandler(http2FrameWriteRequest{ | |
| write: headerData, | |
| stream: st, | |
| done: errc, | |
| }); err != nil { | |
| return err | |
| } | |
| if errc != nil { | |
| select { | |
| case err := <-errc: | |
| http2errChanPool.Put(errc) | |
| return err | |
| case <-sc.doneServing: | |
| return http2errClientDisconnected | |
| case <-st.cw: | |
| return http2errStreamClosed | |
| } | |
| } | |
| return nil | |
| } | |
| // called from handler goroutines. | |
| func (sc *http2serverConn) write100ContinueHeaders(st *http2stream) { | |
| sc.writeFrameFromHandler(http2FrameWriteRequest{ | |
| write: http2write100ContinueHeadersFrame{st.id}, | |
| stream: st, | |
| }) | |
| } | |
| // A bodyReadMsg tells the server loop that the http.Handler read n | |
| // bytes of the DATA from the client on the given stream. | |
| type http2bodyReadMsg struct { | |
| st *http2stream | |
| n int | |
| } | |
| // called from handler goroutines. | |
| // Notes that the handler for the given stream ID read n bytes of its body | |
| // and schedules flow control tokens to be sent. | |
| func (sc *http2serverConn) noteBodyReadFromHandler(st *http2stream, n int, err error) { | |
| sc.serveG.checkNotOn() // NOT on | |
| if n > 0 { | |
| select { | |
| case sc.bodyReadCh <- http2bodyReadMsg{st, n}: | |
| case <-sc.doneServing: | |
| } | |
| } | |
| } | |
| func (sc *http2serverConn) noteBodyRead(st *http2stream, n int) { | |
| sc.serveG.check() | |
| sc.sendWindowUpdate(nil, n) // conn-level | |
| if st.state != http2stateHalfClosedRemote && st.state != http2stateClosed { | |
| // Don't send this WINDOW_UPDATE if the stream is closed | |
| // remotely. | |
| sc.sendWindowUpdate(st, n) | |
| } | |
| } | |
| // st may be nil for conn-level | |
| func (sc *http2serverConn) sendWindowUpdate(st *http2stream, n int) { | |
| sc.serveG.check() | |
| // "The legal range for the increment to the flow control | |
| // window is 1 to 2^31-1 (2,147,483,647) octets." | |
| // A Go Read call on 64-bit machines could in theory read | |
| // a larger Read than this. Very unlikely, but we handle it here | |
| // rather than elsewhere for now. | |
| const maxUint31 = 1<<31 - 1 | |
| for n >= maxUint31 { | |
| sc.sendWindowUpdate32(st, maxUint31) | |
| n -= maxUint31 | |
| } | |
| sc.sendWindowUpdate32(st, int32(n)) | |
| } | |
| // st may be nil for conn-level | |
| func (sc *http2serverConn) sendWindowUpdate32(st *http2stream, n int32) { | |
| sc.serveG.check() | |
| if n == 0 { | |
| return | |
| } | |
| if n < 0 { | |
| panic("negative update") | |
| } | |
| var streamID uint32 | |
| if st != nil { | |
| streamID = st.id | |
| } | |
| sc.writeFrame(http2FrameWriteRequest{ | |
| write: http2writeWindowUpdate{streamID: streamID, n: uint32(n)}, | |
| stream: st, | |
| }) | |
| var ok bool | |
| if st == nil { | |
| ok = sc.inflow.add(n) | |
| } else { | |
| ok = st.inflow.add(n) | |
| } | |
| if !ok { | |
| panic("internal error; sent too many window updates without decrements?") | |
| } | |
| } | |
| // requestBody is the Handler's Request.Body type. | |
| // Read and Close may be called concurrently. | |
| type http2requestBody struct { | |
| _ http2incomparable | |
| stream *http2stream | |
| conn *http2serverConn | |
| closed bool // for use by Close only | |
| sawEOF bool // for use by Read only | |
| pipe *http2pipe // non-nil if we have a HTTP entity message body | |
| needsContinue bool // need to send a 100-continue | |
| } | |
| func (b *http2requestBody) Close() error { | |
| if b.pipe != nil && !b.closed { | |
| b.pipe.BreakWithError(http2errClosedBody) | |
| } | |
| b.closed = true | |
| return nil | |
| } | |
| func (b *http2requestBody) Read(p []byte) (n int, err error) { | |
| if b.needsContinue { | |
| b.needsContinue = false | |
| b.conn.write100ContinueHeaders(b.stream) | |
| } | |
| if b.pipe == nil || b.sawEOF { | |
| return 0, io.EOF | |
| } | |
| n, err = b.pipe.Read(p) | |
| if err == io.EOF { | |
| b.sawEOF = true | |
| } | |
| if b.conn == nil && http2inTests { | |
| return | |
| } | |
| b.conn.noteBodyReadFromHandler(b.stream, n, err) | |
| return | |
| } | |
| // responseWriter is the http.ResponseWriter implementation. It's | |
| // intentionally small (1 pointer wide) to minimize garbage. The | |
| // responseWriterState pointer inside is zeroed at the end of a | |
| // request (in handlerDone) and calls on the responseWriter thereafter | |
| // simply crash (caller's mistake), but the much larger responseWriterState | |
| // and buffers are reused between multiple requests. | |
| type http2responseWriter struct { | |
| rws *http2responseWriterState | |
| } | |
| // Optional http.ResponseWriter interfaces implemented. | |
| var ( | |
| _ CloseNotifier = (*http2responseWriter)(nil) | |
| _ Flusher = (*http2responseWriter)(nil) | |
| _ http2stringWriter = (*http2responseWriter)(nil) | |
| ) | |
| type http2responseWriterState struct { | |
| // immutable within a request: | |
| stream *http2stream | |
| req *Request | |
| body *http2requestBody // to close at end of request, if DATA frames didn't | |
| conn *http2serverConn | |
| // TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc | |
| bw *bufio.Writer // writing to a chunkWriter{this *responseWriterState} | |
| // mutated by http.Handler goroutine: | |
| handlerHeader Header // nil until called | |
| snapHeader Header // snapshot of handlerHeader at WriteHeader time | |
| trailers []string // set in writeChunk | |
| status int // status code passed to WriteHeader | |
| wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. | |
| sentHeader bool // have we sent the header frame? | |
| handlerDone bool // handler has finished | |
| dirty bool // a Write failed; don't reuse this responseWriterState | |
| sentContentLen int64 // non-zero if handler set a Content-Length header | |
| wroteBytes int64 | |
| closeNotifierMu sync.Mutex // guards closeNotifierCh | |
| closeNotifierCh chan bool // nil until first used | |
| } | |
| type http2chunkWriter struct{ rws *http2responseWriterState } | |
| func (cw http2chunkWriter) Write(p []byte) (n int, err error) { return cw.rws.writeChunk(p) } | |
| func (rws *http2responseWriterState) hasTrailers() bool { return len(rws.trailers) > 0 } | |
| func (rws *http2responseWriterState) hasNonemptyTrailers() bool { | |
| for _, trailer := range rws.trailers { | |
| if _, ok := rws.handlerHeader[trailer]; ok { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // declareTrailer is called for each Trailer header when the | |
| // response header is written. It notes that a header will need to be | |
| // written in the trailers at the end of the response. | |
| func (rws *http2responseWriterState) declareTrailer(k string) { | |
| k = CanonicalHeaderKey(k) | |
| if !httpguts.ValidTrailerHeader(k) { | |
| // Forbidden by RFC 7230, section 4.1.2. | |
| rws.conn.logf("ignoring invalid trailer %q", k) | |
| return | |
| } | |
| if !http2strSliceContains(rws.trailers, k) { | |
| rws.trailers = append(rws.trailers, k) | |
| } | |
| } | |
| // writeChunk writes chunks from the bufio.Writer. But because | |
| // bufio.Writer may bypass its chunking, sometimes p may be | |
| // arbitrarily large. | |
| // | |
| // writeChunk is also responsible (on the first chunk) for sending the | |
| // HEADER response. | |
| func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { | |
| if !rws.wroteHeader { | |
| rws.writeHeader(200) | |
| } | |
| isHeadResp := rws.req.Method == "HEAD" | |
| if !rws.sentHeader { | |
| rws.sentHeader = true | |
| var ctype, clen string | |
| if clen = rws.snapHeader.Get("Content-Length"); clen != "" { | |
| rws.snapHeader.Del("Content-Length") | |
| if cl, err := strconv.ParseUint(clen, 10, 63); err == nil { | |
| rws.sentContentLen = int64(cl) | |
| } else { | |
| clen = "" | |
| } | |
| } | |
| if clen == "" && rws.handlerDone && http2bodyAllowedForStatus(rws.status) && (len(p) > 0 || !isHeadResp) { | |
| clen = strconv.Itoa(len(p)) | |
| } | |
| _, hasContentType := rws.snapHeader["Content-Type"] | |
| // If the Content-Encoding is non-blank, we shouldn't | |
| // sniff the body. See Issue golang.org/issue/31753. | |
| ce := rws.snapHeader.Get("Content-Encoding") | |
| hasCE := len(ce) > 0 | |
| if !hasCE && !hasContentType && http2bodyAllowedForStatus(rws.status) && len(p) > 0 { | |
| ctype = DetectContentType(p) | |
| } | |
| var date string | |
| if _, ok := rws.snapHeader["Date"]; !ok { | |
| // TODO(bradfitz): be faster here, like net/http? measure. | |
| date = time.Now().UTC().Format(TimeFormat) | |
| } | |
| for _, v := range rws.snapHeader["Trailer"] { | |
| http2foreachHeaderElement(v, rws.declareTrailer) | |
| } | |
| // "Connection" headers aren't allowed in HTTP/2 (RFC 7540, 8.1.2.2), | |
| // but respect "Connection" == "close" to mean sending a GOAWAY and tearing | |
| // down the TCP connection when idle, like we do for HTTP/1. | |
| // TODO: remove more Connection-specific header fields here, in addition | |
| // to "Connection". | |
| if _, ok := rws.snapHeader["Connection"]; ok { | |
| v := rws.snapHeader.Get("Connection") | |
| delete(rws.snapHeader, "Connection") | |
| if v == "close" { | |
| rws.conn.startGracefulShutdown() | |
| } | |
| } | |
| endStream := (rws.handlerDone && !rws.hasTrailers() && len(p) == 0) || isHeadResp | |
| err = rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{ | |
| streamID: rws.stream.id, | |
| httpResCode: rws.status, | |
| h: rws.snapHeader, | |
| endStream: endStream, | |
| contentType: ctype, | |
| contentLength: clen, | |
| date: date, | |
| }) | |
| if err != nil { | |
| rws.dirty = true | |
| return 0, err | |
| } | |
| if endStream { | |
| return 0, nil | |
| } | |
| } | |
| if isHeadResp { | |
| return len(p), nil | |
| } | |
| if len(p) == 0 && !rws.handlerDone { | |
| return 0, nil | |
| } | |
| if rws.handlerDone { | |
| rws.promoteUndeclaredTrailers() | |
| } | |
| // only send trailers if they have actually been defined by the | |
| // server handler. | |
| hasNonemptyTrailers := rws.hasNonemptyTrailers() | |
| endStream := rws.handlerDone && !hasNonemptyTrailers | |
| if len(p) > 0 || endStream { | |
| // only send a 0 byte DATA frame if we're ending the stream. | |
| if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { | |
| rws.dirty = true | |
| return 0, err | |
| } | |
| } | |
| if rws.handlerDone && hasNonemptyTrailers { | |
| err = rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{ | |
| streamID: rws.stream.id, | |
| h: rws.handlerHeader, | |
| trailers: rws.trailers, | |
| endStream: true, | |
| }) | |
| if err != nil { | |
| rws.dirty = true | |
| } | |
| return len(p), err | |
| } | |
| return len(p), nil | |
| } | |
| // TrailerPrefix is a magic prefix for ResponseWriter.Header map keys | |
| // that, if present, signals that the map entry is actually for | |
| // the response trailers, and not the response headers. The prefix | |
| // is stripped after the ServeHTTP call finishes and the values are | |
| // sent in the trailers. | |
| // | |
| // This mechanism is intended only for trailers that are not known | |
| // prior to the headers being written. If the set of trailers is fixed | |
| // or known before the header is written, the normal Go trailers mechanism | |
| // is preferred: | |
| // https://golang.org/pkg/net/http/#ResponseWriter | |
| // https://golang.org/pkg/net/http/#example_ResponseWriter_trailers | |
| const http2TrailerPrefix = "Trailer:" | |
| // promoteUndeclaredTrailers permits http.Handlers to set trailers | |
| // after the header has already been flushed. Because the Go | |
| // ResponseWriter interface has no way to set Trailers (only the | |
| // Header), and because we didn't want to expand the ResponseWriter | |
| // interface, and because nobody used trailers, and because RFC 7230 | |
| // says you SHOULD (but not must) predeclare any trailers in the | |
| // header, the official ResponseWriter rules said trailers in Go must | |
| // be predeclared, and then we reuse the same ResponseWriter.Header() | |
| // map to mean both Headers and Trailers. When it's time to write the | |
| // Trailers, we pick out the fields of Headers that were declared as | |
| // trailers. That worked for a while, until we found the first major | |
| // user of Trailers in the wild: gRPC (using them only over http2), | |
| // and gRPC libraries permit setting trailers mid-stream without | |
| // predeclaring them. So: change of plans. We still permit the old | |
| // way, but we also permit this hack: if a Header() key begins with | |
| // "Trailer:", the suffix of that key is a Trailer. Because ':' is an | |
| // invalid token byte anyway, there is no ambiguity. (And it's already | |
| // filtered out) It's mildly hacky, but not terrible. | |
| // | |
| // This method runs after the Handler is done and promotes any Header | |
| // fields to be trailers. | |
| func (rws *http2responseWriterState) promoteUndeclaredTrailers() { | |
| for k, vv := range rws.handlerHeader { | |
| if !strings.HasPrefix(k, http2TrailerPrefix) { | |
| continue | |
| } | |
| trailerKey := strings.TrimPrefix(k, http2TrailerPrefix) | |
| rws.declareTrailer(trailerKey) | |
| rws.handlerHeader[CanonicalHeaderKey(trailerKey)] = vv | |
| } | |
| if len(rws.trailers) > 1 { | |
| sorter := http2sorterPool.Get().(*http2sorter) | |
| sorter.SortStrings(rws.trailers) | |
| http2sorterPool.Put(sorter) | |
| } | |
| } | |
| func (w *http2responseWriter) Flush() { | |
| rws := w.rws | |
| if rws == nil { | |
| panic("Header called after Handler finished") | |
| } | |
| if rws.bw.Buffered() > 0 { | |
| if err := rws.bw.Flush(); err != nil { | |
| // Ignore the error. The frame writer already knows. | |
| return | |
| } | |
| } else { | |
| // The bufio.Writer won't call chunkWriter.Write | |
| // (writeChunk with zero bytes, so we have to do it | |
| // ourselves to force the HTTP response header and/or | |
| // final DATA frame (with END_STREAM) to be sent. | |
| rws.writeChunk(nil) | |
| } | |
| } | |
| func (w *http2responseWriter) CloseNotify() <-chan bool { | |
| rws := w.rws | |
| if rws == nil { | |
| panic("CloseNotify called after Handler finished") | |
| } | |
| rws.closeNotifierMu.Lock() | |
| ch := rws.closeNotifierCh | |
| if ch == nil { | |
| ch = make(chan bool, 1) | |
| rws.closeNotifierCh = ch | |
| cw := rws.stream.cw | |
| go func() { | |
| cw.Wait() // wait for close | |
| ch <- true | |
| }() | |
| } | |
| rws.closeNotifierMu.Unlock() | |
| return ch | |
| } | |
| func (w *http2responseWriter) Header() Header { | |
| rws := w.rws | |
| if rws == nil { | |
| panic("Header called after Handler finished") | |
| } | |
| if rws.handlerHeader == nil { | |
| rws.handlerHeader = make(Header) | |
| } | |
| return rws.handlerHeader | |
| } | |
| // checkWriteHeaderCode is a copy of net/http's checkWriteHeaderCode. | |
| func http2checkWriteHeaderCode(code int) { | |
| // Issue 22880: require valid WriteHeader status codes. | |
| // For now we only enforce that it's three digits. | |
| // In the future we might block things over 599 (600 and above aren't defined | |
| // at http://httpwg.org/specs/rfc7231.html#status.codes) | |
| // and we might block under 200 (once we have more mature 1xx support). | |
| // But for now any three digits. | |
| // | |
| // We used to send "HTTP/1.1 000 0" on the wire in responses but there's | |
| // no equivalent bogus thing we can realistically send in HTTP/2, | |
| // so we'll consistently panic instead and help people find their bugs | |
| // early. (We can't return an error from WriteHeader even if we wanted to.) | |
| if code < 100 || code > 999 { | |
| panic(fmt.Sprintf("invalid WriteHeader code %v", code)) | |
| } | |
| } | |
| func (w *http2responseWriter) WriteHeader(code int) { | |
| rws := w.rws | |
| if rws == nil { | |
| panic("WriteHeader called after Handler finished") | |
| } | |
| rws.writeHeader(code) | |
| } | |
| func (rws *http2responseWriterState) writeHeader(code int) { | |
| if !rws.wroteHeader { | |
| http2checkWriteHeaderCode(code) | |
| rws.wroteHeader = true | |
| rws.status = code | |
| if len(rws.handlerHeader) > 0 { | |
| rws.snapHeader = http2cloneHeader(rws.handlerHeader) | |
| } | |
| } | |
| } | |
| func http2cloneHeader(h Header) Header { | |
| h2 := make(Header, len(h)) | |
| for k, vv := range h { | |
| vv2 := make([]string, len(vv)) | |
| copy(vv2, vv) | |
| h2[k] = vv2 | |
| } | |
| return h2 | |
| } | |
| // The Life Of A Write is like this: | |
| // | |
| // * Handler calls w.Write or w.WriteString -> | |
| // * -> rws.bw (*bufio.Writer) -> | |
| // * (Handler might call Flush) | |
| // * -> chunkWriter{rws} | |
| // * -> responseWriterState.writeChunk(p []byte) | |
| // * -> responseWriterState.writeChunk (most of the magic; see comment there) | |
| func (w *http2responseWriter) Write(p []byte) (n int, err error) { | |
| return w.write(len(p), p, "") | |
| } | |
| func (w *http2responseWriter) WriteString(s string) (n int, err error) { | |
| return w.write(len(s), nil, s) | |
| } | |
| // either dataB or dataS is non-zero. | |
| func (w *http2responseWriter) write(lenData int, dataB []byte, dataS string) (n int, err error) { | |
| rws := w.rws | |
| if rws == nil { | |
| panic("Write called after Handler finished") | |
| } | |
| if !rws.wroteHeader { | |
| w.WriteHeader(200) | |
| } | |
| if !http2bodyAllowedForStatus(rws.status) { | |
| return 0, ErrBodyNotAllowed | |
| } | |
| rws.wroteBytes += int64(len(dataB)) + int64(len(dataS)) // only one can be set | |
| if rws.sentContentLen != 0 && rws.wroteBytes > rws.sentContentLen { | |
| // TODO: send a RST_STREAM | |
| return 0, errors.New("http2: handler wrote more than declared Content-Length") | |
| } | |
| if dataB != nil { | |
| return rws.bw.Write(dataB) | |
| } else { | |
| return rws.bw.WriteString(dataS) | |
| } | |
| } | |
| func (w *http2responseWriter) handlerDone() { | |
| rws := w.rws | |
| dirty := rws.dirty | |
| rws.handlerDone = true | |
| w.Flush() | |
| w.rws = nil | |
| if !dirty { | |
| // Only recycle the pool if all prior Write calls to | |
| // the serverConn goroutine completed successfully. If | |
| // they returned earlier due to resets from the peer | |
| // there might still be write goroutines outstanding | |
| // from the serverConn referencing the rws memory. See | |
| // issue 20704. | |
| http2responseWriterStatePool.Put(rws) | |
| } | |
| } | |
| // Push errors. | |
| var ( | |
| http2ErrRecursivePush = errors.New("http2: recursive push not allowed") | |
| http2ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS") | |
| ) | |
| var _ Pusher = (*http2responseWriter)(nil) | |
| func (w *http2responseWriter) Push(target string, opts *PushOptions) error { | |
| st := w.rws.stream | |
| sc := st.sc | |
| sc.serveG.checkNotOn() | |
| // No recursive pushes: "PUSH_PROMISE frames MUST only be sent on a peer-initiated stream." | |
| // http://tools.ietf.org/html/rfc7540#section-6.6 | |
| if st.isPushed() { | |
| return http2ErrRecursivePush | |
| } | |
| if opts == nil { | |
| opts = new(PushOptions) | |
| } | |
| // Default options. | |
| if opts.Method == "" { | |
| opts.Method = "GET" | |
| } | |
| if opts.Header == nil { | |
| opts.Header = Header{} | |
| } | |
| wantScheme := "http" | |
| if w.rws.req.TLS != nil { | |
| wantScheme = "https" | |
| } | |
| // Validate the request. | |
| u, err := url.Parse(target) | |
| if err != nil { | |
| return err | |
| } | |
| if u.Scheme == "" { | |
| if !strings.HasPrefix(target, "/") { | |
| return fmt.Errorf("target must be an absolute URL or an absolute path: %q", target) | |
| } | |
| u.Scheme = wantScheme | |
| u.Host = w.rws.req.Host | |
| } else { | |
| if u.Scheme != wantScheme { | |
| return fmt.Errorf("cannot push URL with scheme %q from request with scheme %q", u.Scheme, wantScheme) | |
| } | |
| if u.Host == "" { | |
| return errors.New("URL must have a host") | |
| } | |
| } | |
| for k := range opts.Header { | |
| if strings.HasPrefix(k, ":") { | |
| return fmt.Errorf("promised request headers cannot include pseudo header %q", k) | |
| } | |
| // These headers are meaningful only if the request has a body, | |
| // but PUSH_PROMISE requests cannot have a body. | |
| // http://tools.ietf.org/html/rfc7540#section-8.2 | |
| // Also disallow Host, since the promised URL must be absolute. | |
| switch strings.ToLower(k) { | |
| case "content-length", "content-encoding", "trailer", "te", "expect", "host": | |
| return fmt.Errorf("promised request headers cannot include %q", k) | |
| } | |
| } | |
| if err := http2checkValidHTTP2RequestHeaders(opts.Header); err != nil { | |
| return err | |
| } | |
| // The RFC effectively limits promised requests to GET and HEAD: | |
| // "Promised requests MUST be cacheable [GET, HEAD, or POST], and MUST be safe [GET or HEAD]" | |
| // http://tools.ietf.org/html/rfc7540#section-8.2 | |
| if opts.Method != "GET" && opts.Method != "HEAD" { | |
| return fmt.Errorf("method %q must be GET or HEAD", opts.Method) | |
| } | |
| msg := &http2startPushRequest{ | |
| parent: st, | |
| method: opts.Method, | |
| url: u, | |
| header: http2cloneHeader(opts.Header), | |
| done: http2errChanPool.Get().(chan error), | |
| } | |
| select { | |
| case <-sc.doneServing: | |
| return http2errClientDisconnected | |
| case <-st.cw: | |
| return http2errStreamClosed | |
| case sc.serveMsgCh <- msg: | |
| } | |
| select { | |
| case <-sc.doneServing: | |
| return http2errClientDisconnected | |
| case <-st.cw: | |
| return http2errStreamClosed | |
| case err := <-msg.done: | |
| http2errChanPool.Put(msg.done) | |
| return err | |
| } | |
| } | |
| type http2startPushRequest struct { | |
| parent *http2stream | |
| method string | |
| url *url.URL | |
| header Header | |
| done chan error | |
| } | |
| func (sc *http2serverConn) startPush(msg *http2startPushRequest) { | |
| sc.serveG.check() | |
| // http://tools.ietf.org/html/rfc7540#section-6.6. | |
| // PUSH_PROMISE frames MUST only be sent on a peer-initiated stream that | |
| // is in either the "open" or "half-closed (remote)" state. | |
| if msg.parent.state != http2stateOpen && msg.parent.state != http2stateHalfClosedRemote { | |
| // responseWriter.Push checks that the stream is peer-initiated. | |
| msg.done <- http2errStreamClosed | |
| return | |
| } | |
| // http://tools.ietf.org/html/rfc7540#section-6.6. | |
| if !sc.pushEnabled { | |
| msg.done <- ErrNotSupported | |
| return | |
| } | |
| // PUSH_PROMISE frames must be sent in increasing order by stream ID, so | |
| // we allocate an ID for the promised stream lazily, when the PUSH_PROMISE | |
| // is written. Once the ID is allocated, we start the request handler. | |
| allocatePromisedID := func() (uint32, error) { | |
| sc.serveG.check() | |
| // Check this again, just in case. Technically, we might have received | |
| // an updated SETTINGS by the time we got around to writing this frame. | |
| if !sc.pushEnabled { | |
| return 0, ErrNotSupported | |
| } | |
| // http://tools.ietf.org/html/rfc7540#section-6.5.2. | |
| if sc.curPushedStreams+1 > sc.clientMaxStreams { | |
| return 0, http2ErrPushLimitReached | |
| } | |
| // http://tools.ietf.org/html/rfc7540#section-5.1.1. | |
| // Streams initiated by the server MUST use even-numbered identifiers. | |
| // A server that is unable to establish a new stream identifier can send a GOAWAY | |
| // frame so that the client is forced to open a new connection for new streams. | |
| if sc.maxPushPromiseID+2 >= 1<<31 { | |
| sc.startGracefulShutdownInternal() | |
| return 0, http2ErrPushLimitReached | |
| } | |
| sc.maxPushPromiseID += 2 | |
| promisedID := sc.maxPushPromiseID | |
| // http://tools.ietf.org/html/rfc7540#section-8.2. | |
| // Strictly speaking, the new stream should start in "reserved (local)", then | |
| // transition to "half closed (remote)" after sending the initial HEADERS, but | |
| // we start in "half closed (remote)" for simplicity. | |
| // See further comments at the definition of stateHalfClosedRemote. | |
| promised := sc.newStream(promisedID, msg.parent.id, http2stateHalfClosedRemote) | |
| rw, req, err := sc.newWriterAndRequestNoBody(promised, http2requestParam{ | |
| method: msg.method, | |
| scheme: msg.url.Scheme, | |
| authority: msg.url.Host, | |
| path: msg.url.RequestURI(), | |
| header: http2cloneHeader(msg.header), // clone since handler runs concurrently with writing the PUSH_PROMISE | |
| }) | |
| if err != nil { | |
| // Should not happen, since we've already validated msg.url. | |
| panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) | |
| } | |
| go sc.runHandler(rw, req, sc.handler.ServeHTTP) | |
| return promisedID, nil | |
| } | |
| sc.writeFrame(http2FrameWriteRequest{ | |
| write: &http2writePushPromise{ | |
| streamID: msg.parent.id, | |
| method: msg.method, | |
| url: msg.url, | |
| h: msg.header, | |
| allocatePromisedID: allocatePromisedID, | |
| }, | |
| stream: msg.parent, | |
| done: msg.done, | |
| }) | |
| } | |
| // foreachHeaderElement splits v according to the "#rule" construction | |
| // in RFC 7230 section 7 and calls fn for each non-empty element. | |
| func http2foreachHeaderElement(v string, fn func(string)) { | |
| v = textproto.TrimString(v) | |
| if v == "" { | |
| return | |
| } | |
| if !strings.Contains(v, ",") { | |
| fn(v) | |
| return | |
| } | |
| for _, f := range strings.Split(v, ",") { | |
| if f = textproto.TrimString(f); f != "" { | |
| fn(f) | |
| } | |
| } | |
| } | |
| // From http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.2 | |
| var http2connHeaders = []string{ | |
| "Connection", | |
| "Keep-Alive", | |
| "Proxy-Connection", | |
| "Transfer-Encoding", | |
| "Upgrade", | |
| } | |
| // checkValidHTTP2RequestHeaders checks whether h is a valid HTTP/2 request, | |
| // per RFC 7540 Section 8.1.2.2. | |
| // The returned error is reported to users. | |
| func http2checkValidHTTP2RequestHeaders(h Header) error { | |
| for _, k := range http2connHeaders { | |
| if _, ok := h[k]; ok { | |
| return fmt.Errorf("request header %q is not valid in HTTP/2", k) | |
| } | |
| } | |
| te := h["Te"] | |
| if len(te) > 0 && (len(te) > 1 || (te[0] != "trailers" && te[0] != "")) { | |
| return errors.New(`request header "TE" may only be "trailers" in HTTP/2`) | |
| } | |
| return nil | |
| } | |
| func http2new400Handler(err error) HandlerFunc { | |
| return func(w ResponseWriter, r *Request) { | |
| Error(w, err.Error(), StatusBadRequest) | |
| } | |
| } | |
| // h1ServerKeepAlivesDisabled reports whether hs has its keep-alives | |
| // disabled. See comments on h1ServerShutdownChan above for why | |
| // the code is written this way. | |
| func http2h1ServerKeepAlivesDisabled(hs *Server) bool { | |
| var x interface{} = hs | |
| type I interface { | |
| doKeepAlives() bool | |
| } | |
| if hs, ok := x.(I); ok { | |
| return !hs.doKeepAlives() | |
| } | |
| return false | |
| } | |
| const ( | |
| // transportDefaultConnFlow is how many connection-level flow control | |
| // tokens we give the server at start-up, past the default 64k. | |
| http2transportDefaultConnFlow = 1 << 30 | |
| // transportDefaultStreamFlow is how many stream-level flow | |
| // control tokens we announce to the peer, and how many bytes | |
| // we buffer per stream. | |
| http2transportDefaultStreamFlow = 4 << 20 | |
| // transportDefaultStreamMinRefresh is the minimum number of bytes we'll send | |
| // a stream-level WINDOW_UPDATE for at a time. | |
| http2transportDefaultStreamMinRefresh = 4 << 10 | |
| http2defaultUserAgent = "Go-http-client/2.0" | |
| ) | |
| // Transport is an HTTP/2 Transport. | |
| // | |
| // A Transport internally caches connections to servers. It is safe | |
| // for concurrent use by multiple goroutines. | |
| type http2Transport struct { | |
| // DialTLS specifies an optional dial function for creating | |
| // TLS connections for requests. | |
| // | |
| // If DialTLS is nil, tls.Dial is used. | |
| // | |
| // If the returned net.Conn has a ConnectionState method like tls.Conn, | |
| // it will be used to set http.Response.TLS. | |
| DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error) | |
| // TLSClientConfig specifies the TLS configuration to use with | |
| // tls.Client. If nil, the default configuration is used. | |
| TLSClientConfig *tls.Config | |
| // ConnPool optionally specifies an alternate connection pool to use. | |
| // If nil, the default is used. | |
| ConnPool http2ClientConnPool | |
| // DisableCompression, if true, prevents the Transport from | |
| // requesting compression with an "Accept-Encoding: gzip" | |
| // request header when the Request contains no existing | |
| // Accept-Encoding value. If the Transport requests gzip on | |
| // its own and gets a gzipped response, it's transparently | |
| // decoded in the Response.Body. However, if the user | |
| // explicitly requested gzip it is not automatically | |
| // uncompressed. | |
| DisableCompression bool | |
| // AllowHTTP, if true, permits HTTP/2 requests using the insecure, | |
| // plain-text "http" scheme. Note that this does not enable h2c support. | |
| AllowHTTP bool | |
| // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to | |
| // send in the initial settings frame. It is how many bytes | |
| // of response headers are allowed. Unlike the http2 spec, zero here | |
| // means to use a default limit (currently 10MB). If you actually | |
| // want to advertise an unlimited value to the peer, Transport | |
| // interprets the highest possible value here (0xffffffff or 1<<32-1) | |
| // to mean no limit. | |
| MaxHeaderListSize uint32 | |
| // StrictMaxConcurrentStreams controls whether the server's | |
| // SETTINGS_MAX_CONCURRENT_STREAMS should be respected | |
| // globally. If false, new TCP connections are created to the | |
| // server as needed to keep each under the per-connection | |
| // SETTINGS_MAX_CONCURRENT_STREAMS limit. If true, the | |
| // server's SETTINGS_MAX_CONCURRENT_STREAMS is interpreted as | |
| // a global limit and callers of RoundTrip block when needed, | |
| // waiting for their turn. | |
| StrictMaxConcurrentStreams bool | |
| // ReadIdleTimeout is the timeout after which a health check using ping | |
| // frame will be carried out if no frame is received on the connection. | |
| // Note that a ping response will is considered a received frame, so if | |
| // there is no other traffic on the connection, the health check will | |
| // be performed every ReadIdleTimeout interval. | |
| // If zero, no health check is performed. | |
| ReadIdleTimeout time.Duration | |
| // PingTimeout is the timeout after which the connection will be closed | |
| // if a response to Ping is not received. | |
| // Defaults to 15s. | |
| PingTimeout time.Duration | |
| // t1, if non-nil, is the standard library Transport using | |
| // this transport. Its settings are used (but not its | |
| // RoundTrip method, etc). | |
| t1 *Transport | |
| connPoolOnce sync.Once | |
| connPoolOrDef http2ClientConnPool // non-nil version of ConnPool | |
| } | |
| func (t *http2Transport) maxHeaderListSize() uint32 { | |
| if t.MaxHeaderListSize == 0 { | |
| return 10 << 20 | |
| } | |
| if t.MaxHeaderListSize == 0xffffffff { | |
| return 0 | |
| } | |
| return t.MaxHeaderListSize | |
| } | |
| func (t *http2Transport) disableCompression() bool { | |
| return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) | |
| } | |
| func (t *http2Transport) pingTimeout() time.Duration { | |
| if t.PingTimeout == 0 { | |
| return 15 * time.Second | |
| } | |
| return t.PingTimeout | |
| } | |
| // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. | |
| // It returns an error if t1 has already been HTTP/2-enabled. | |
| // | |
| // Use ConfigureTransports instead to configure the HTTP/2 Transport. | |
| func http2ConfigureTransport(t1 *Transport) error { | |
| _, err := http2ConfigureTransports(t1) | |
| return err | |
| } | |
| // ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2. | |
| // It returns a new HTTP/2 Transport for further configuration. | |
| // It returns an error if t1 has already been HTTP/2-enabled. | |
| func http2ConfigureTransports(t1 *Transport) (*http2Transport, error) { | |
| return http2configureTransports(t1) | |
| } | |
| func http2configureTransports(t1 *Transport) (*http2Transport, error) { | |
| connPool := new(http2clientConnPool) | |
| t2 := &http2Transport{ | |
| ConnPool: http2noDialClientConnPool{connPool}, | |
| t1: t1, | |
| } | |
| connPool.t = t2 | |
| if err := http2registerHTTPSProtocol(t1, http2noDialH2RoundTripper{t2}); err != nil { | |
| return nil, err | |
| } | |
| if t1.TLSClientConfig == nil { | |
| t1.TLSClientConfig = new(tls.Config) | |
| } | |
| if !http2strSliceContains(t1.TLSClientConfig.NextProtos, "h2") { | |
| t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...) | |
| } | |
| if !http2strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") { | |
| t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1") | |
| } | |
| upgradeFn := func(authority string, c *tls.Conn) RoundTripper { | |
| addr := http2authorityAddr("https", authority) | |
| if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil { | |
| go c.Close() | |
| return http2erringRoundTripper{err} | |
| } else if !used { | |
| // Turns out we don't need this c. | |
| // For example, two goroutines made requests to the same host | |
| // at the same time, both kicking off TCP dials. (since protocol | |
| // was unknown) | |
| go c.Close() | |
| } | |
| return t2 | |
| } | |
| if m := t1.TLSNextProto; len(m) == 0 { | |
| t1.TLSNextProto = map[string]func(string, *tls.Conn) RoundTripper{ | |
| "h2": upgradeFn, | |
| } | |
| } else { | |
| m["h2"] = upgradeFn | |
| } | |
| return t2, nil | |
| } | |
| func (t *http2Transport) connPool() http2ClientConnPool { | |
| t.connPoolOnce.Do(t.initConnPool) | |
| return t.connPoolOrDef | |
| } | |
| func (t *http2Transport) initConnPool() { | |
| if t.ConnPool != nil { | |
| t.connPoolOrDef = t.ConnPool | |
| } else { | |
| t.connPoolOrDef = &http2clientConnPool{t: t} | |
| } | |
| } | |
| // ClientConn is the state of a single HTTP/2 client connection to an | |
| // HTTP/2 server. | |
| type http2ClientConn struct { | |
| t *http2Transport | |
| tconn net.Conn // usually *tls.Conn, except specialized impls | |
| tlsState *tls.ConnectionState // nil only for specialized impls | |
| reused uint32 // whether conn is being reused; atomic | |
| singleUse bool // whether being used for a single http.Request | |
| // readLoop goroutine fields: | |
| readerDone chan struct{} // closed on error | |
| readerErr error // set before readerDone is closed | |
| idleTimeout time.Duration // or 0 for never | |
| idleTimer *time.Timer | |
| mu sync.Mutex // guards following | |
| cond *sync.Cond // hold mu; broadcast on flow/closed changes | |
| flow http2flow // our conn-level flow control quota (cs.flow is per stream) | |
| inflow http2flow // peer's conn-level flow control | |
| closing bool | |
| closed bool | |
| wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back | |
| goAway *http2GoAwayFrame // if non-nil, the GoAwayFrame we received | |
| goAwayDebug string // goAway frame's debug data, retained as a string | |
| streams map[uint32]*http2clientStream // client-initiated | |
| nextStreamID uint32 | |
| pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams | |
| pings map[[8]byte]chan struct{} // in flight ping data to notification channel | |
| bw *bufio.Writer | |
| br *bufio.Reader | |
| fr *http2Framer | |
| lastActive time.Time | |
| lastIdle time.Time // time last idle | |
| // Settings from peer: (also guarded by mu) | |
| maxFrameSize uint32 | |
| maxConcurrentStreams uint32 | |
| peerMaxHeaderListSize uint64 | |
| initialWindowSize uint32 | |
| hbuf bytes.Buffer // HPACK encoder writes into this | |
| henc *hpack.Encoder | |
| freeBuf [][]byte | |
| wmu sync.Mutex // held while writing; acquire AFTER mu if holding both | |
| werr error // first write error that has occurred | |
| } | |
| // clientStream is the state for a single HTTP/2 stream. One of these | |
| // is created for each Transport.RoundTrip call. | |
| type http2clientStream struct { | |
| cc *http2ClientConn | |
| req *Request | |
| trace *httptrace.ClientTrace // or nil | |
| ID uint32 | |
| resc chan http2resAndError | |
| bufPipe http2pipe // buffered pipe with the flow-controlled response payload | |
| startedWrite bool // started request body write; guarded by cc.mu | |
| requestedGzip bool | |
| on100 func() // optional code to run if get a 100 continue response | |
| flow http2flow // guarded by cc.mu | |
| inflow http2flow // guarded by cc.mu | |
| bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read | |
| readErr error // sticky read error; owned by transportResponseBody.Read | |
| stopReqBody error // if non-nil, stop writing req body; guarded by cc.mu | |
| didReset bool // whether we sent a RST_STREAM to the server; guarded by cc.mu | |
| peerReset chan struct{} // closed on peer reset | |
| resetErr error // populated before peerReset is closed | |
| done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu | |
| // owned by clientConnReadLoop: | |
| firstByte bool // got the first response byte | |
| pastHeaders bool // got first MetaHeadersFrame (actual headers) | |
| pastTrailers bool // got optional second MetaHeadersFrame (trailers) | |
| num1xx uint8 // number of 1xx responses seen | |
| trailer Header // accumulated trailers | |
| resTrailer *Header // client's Response.Trailer | |
| } | |
| // awaitRequestCancel waits for the user to cancel a request or for the done | |
| // channel to be signaled. A non-nil error is returned only if the request was | |
| // canceled. | |
| func http2awaitRequestCancel(req *Request, done <-chan struct{}) error { | |
| ctx := req.Context() | |
| if req.Cancel == nil && ctx.Done() == nil { | |
| return nil | |
| } | |
| select { | |
| case <-req.Cancel: | |
| return http2errRequestCanceled | |
| case <-ctx.Done(): | |
| return ctx.Err() | |
| case <-done: | |
| return nil | |
| } | |
| } | |
| var http2got1xxFuncForTests func(int, textproto.MIMEHeader) error | |
| // get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func, | |
| // if any. It returns nil if not set or if the Go version is too old. | |
| func (cs *http2clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error { | |
| if fn := http2got1xxFuncForTests; fn != nil { | |
| return fn | |
| } | |
| return http2traceGot1xxResponseFunc(cs.trace) | |
| } | |
| // awaitRequestCancel waits for the user to cancel a request, its context to | |
| // expire, or for the request to be done (any way it might be removed from the | |
| // cc.streams map: peer reset, successful completion, TCP connection breakage, | |
| // etc). If the request is canceled, then cs will be canceled and closed. | |
| func (cs *http2clientStream) awaitRequestCancel(req *Request) { | |
| if err := http2awaitRequestCancel(req, cs.done); err != nil { | |
| cs.cancelStream() | |
| cs.bufPipe.CloseWithError(err) | |
| } | |
| } | |
| func (cs *http2clientStream) cancelStream() { | |
| cc := cs.cc | |
| cc.mu.Lock() | |
| didReset := cs.didReset | |
| cs.didReset = true | |
| cc.mu.Unlock() | |
| if !didReset { | |
| cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) | |
| cc.forgetStreamID(cs.ID) | |
| } | |
| } | |
| // checkResetOrDone reports any error sent in a RST_STREAM frame by the | |
| // server, or errStreamClosed if the stream is complete. | |
| func (cs *http2clientStream) checkResetOrDone() error { | |
| select { | |
| case <-cs.peerReset: | |
| return cs.resetErr | |
| case <-cs.done: | |
| return http2errStreamClosed | |
| default: | |
| return nil | |
| } | |
| } | |
| func (cs *http2clientStream) getStartedWrite() bool { | |
| cc := cs.cc | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| return cs.startedWrite | |
| } | |
| func (cs *http2clientStream) abortRequestBodyWrite(err error) { | |
| if err == nil { | |
| panic("nil error") | |
| } | |
| cc := cs.cc | |
| cc.mu.Lock() | |
| cs.stopReqBody = err | |
| cc.cond.Broadcast() | |
| cc.mu.Unlock() | |
| } | |
| type http2stickyErrWriter struct { | |
| w io.Writer | |
| err *error | |
| } | |
| func (sew http2stickyErrWriter) Write(p []byte) (n int, err error) { | |
| if *sew.err != nil { | |
| return 0, *sew.err | |
| } | |
| n, err = sew.w.Write(p) | |
| *sew.err = err | |
| return | |
| } | |
| // noCachedConnError is the concrete type of ErrNoCachedConn, which | |
| // needs to be detected by net/http regardless of whether it's its | |
| // bundled version (in h2_bundle.go with a rewritten type name) or | |
| // from a user's x/net/http2. As such, as it has a unique method name | |
| // (IsHTTP2NoCachedConnError) that net/http sniffs for via func | |
| // isNoCachedConnError. | |
| type http2noCachedConnError struct{} | |
| func (http2noCachedConnError) IsHTTP2NoCachedConnError() {} | |
| func (http2noCachedConnError) Error() string { return "http2: no cached connection was available" } | |
| // isNoCachedConnError reports whether err is of type noCachedConnError | |
| // or its equivalent renamed type in net/http2's h2_bundle.go. Both types | |
| // may coexist in the same running program. | |
| func http2isNoCachedConnError(err error) bool { | |
| _, ok := err.(interface{ IsHTTP2NoCachedConnError() }) | |
| return ok | |
| } | |
| var http2ErrNoCachedConn error = http2noCachedConnError{} | |
| // RoundTripOpt are options for the Transport.RoundTripOpt method. | |
| type http2RoundTripOpt struct { | |
| // OnlyCachedConn controls whether RoundTripOpt may | |
| // create a new TCP connection. If set true and | |
| // no cached connection is available, RoundTripOpt | |
| // will return ErrNoCachedConn. | |
| OnlyCachedConn bool | |
| } | |
| func (t *http2Transport) RoundTrip(req *Request) (*Response, error) { | |
| return t.RoundTripOpt(req, http2RoundTripOpt{}) | |
| } | |
| // authorityAddr returns a given authority (a host/IP, or host:port / ip:port) | |
| // and returns a host:port. The port 443 is added if needed. | |
| func http2authorityAddr(scheme string, authority string) (addr string) { | |
| host, port, err := net.SplitHostPort(authority) | |
| if err != nil { // authority didn't have a port | |
| port = "443" | |
| if scheme == "http" { | |
| port = "80" | |
| } | |
| host = authority | |
| } | |
| if a, err := idna.ToASCII(host); err == nil { | |
| host = a | |
| } | |
| // IPv6 address literal, without a port: | |
| if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { | |
| return host + ":" + port | |
| } | |
| return net.JoinHostPort(host, port) | |
| } | |
| // RoundTripOpt is like RoundTrip, but takes options. | |
| func (t *http2Transport) RoundTripOpt(req *Request, opt http2RoundTripOpt) (*Response, error) { | |
| if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) { | |
| return nil, errors.New("http2: unsupported scheme") | |
| } | |
| addr := http2authorityAddr(req.URL.Scheme, req.URL.Host) | |
| for retry := 0; ; retry++ { | |
| cc, err := t.connPool().GetClientConn(req, addr) | |
| if err != nil { | |
| t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) | |
| return nil, err | |
| } | |
| reused := !atomic.CompareAndSwapUint32(&cc.reused, 0, 1) | |
| http2traceGotConn(req, cc, reused) | |
| res, gotErrAfterReqBodyWrite, err := cc.roundTrip(req) | |
| if err != nil && retry <= 6 { | |
| if req, err = http2shouldRetryRequest(req, err, gotErrAfterReqBodyWrite); err == nil { | |
| // After the first retry, do exponential backoff with 10% jitter. | |
| if retry == 0 { | |
| continue | |
| } | |
| backoff := float64(uint(1) << (uint(retry) - 1)) | |
| backoff += backoff * (0.1 * mathrand.Float64()) | |
| select { | |
| case <-time.After(time.Second * time.Duration(backoff)): | |
| continue | |
| case <-req.Context().Done(): | |
| return nil, req.Context().Err() | |
| } | |
| } | |
| } | |
| if err != nil { | |
| t.vlogf("RoundTrip failure: %v", err) | |
| return nil, err | |
| } | |
| return res, nil | |
| } | |
| } | |
| // CloseIdleConnections closes any connections which were previously | |
| // connected from previous requests but are now sitting idle. | |
| // It does not interrupt any connections currently in use. | |
| func (t *http2Transport) CloseIdleConnections() { | |
| if cp, ok := t.connPool().(http2clientConnPoolIdleCloser); ok { | |
| cp.closeIdleConnections() | |
| } | |
| } | |
| var ( | |
| http2errClientConnClosed = errors.New("http2: client conn is closed") | |
| http2errClientConnUnusable = errors.New("http2: client conn not usable") | |
| http2errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") | |
| ) | |
| // shouldRetryRequest is called by RoundTrip when a request fails to get | |
| // response headers. It is always called with a non-nil error. | |
| // It returns either a request to retry (either the same request, or a | |
| // modified clone), or an error if the request can't be replayed. | |
| func http2shouldRetryRequest(req *Request, err error, afterBodyWrite bool) (*Request, error) { | |
| if !http2canRetryError(err) { | |
| return nil, err | |
| } | |
| // If the Body is nil (or http.NoBody), it's safe to reuse | |
| // this request and its Body. | |
| if req.Body == nil || req.Body == NoBody { | |
| return req, nil | |
| } | |
| // If the request body can be reset back to its original | |
| // state via the optional req.GetBody, do that. | |
| if req.GetBody != nil { | |
| // TODO: consider a req.Body.Close here? or audit that all caller paths do? | |
| body, err := req.GetBody() | |
| if err != nil { | |
| return nil, err | |
| } | |
| newReq := *req | |
| newReq.Body = body | |
| return &newReq, nil | |
| } | |
| // The Request.Body can't reset back to the beginning, but we | |
| // don't seem to have started to read from it yet, so reuse | |
| // the request directly. The "afterBodyWrite" means the | |
| // bodyWrite process has started, which becomes true before | |
| // the first Read. | |
| if !afterBodyWrite { | |
| return req, nil | |
| } | |
| return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err) | |
| } | |
| func http2canRetryError(err error) bool { | |
| if err == http2errClientConnUnusable || err == http2errClientConnGotGoAway { | |
| return true | |
| } | |
| if se, ok := err.(http2StreamError); ok { | |
| return se.Code == http2ErrCodeRefusedStream | |
| } | |
| return false | |
| } | |
| func (t *http2Transport) dialClientConn(addr string, singleUse bool) (*http2ClientConn, error) { | |
| host, _, err := net.SplitHostPort(addr) | |
| if err != nil { | |
| return nil, err | |
| } | |
| tconn, err := t.dialTLS()("tcp", addr, t.newTLSConfig(host)) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return t.newClientConn(tconn, singleUse) | |
| } | |
| func (t *http2Transport) newTLSConfig(host string) *tls.Config { | |
| cfg := new(tls.Config) | |
| if t.TLSClientConfig != nil { | |
| *cfg = *t.TLSClientConfig.Clone() | |
| } | |
| if !http2strSliceContains(cfg.NextProtos, http2NextProtoTLS) { | |
| cfg.NextProtos = append([]string{http2NextProtoTLS}, cfg.NextProtos...) | |
| } | |
| if cfg.ServerName == "" { | |
| cfg.ServerName = host | |
| } | |
| return cfg | |
| } | |
| func (t *http2Transport) dialTLS() func(string, string, *tls.Config) (net.Conn, error) { | |
| if t.DialTLS != nil { | |
| return t.DialTLS | |
| } | |
| return t.dialTLSDefault | |
| } | |
| func (t *http2Transport) dialTLSDefault(network, addr string, cfg *tls.Config) (net.Conn, error) { | |
| cn, err := tls.Dial(network, addr, cfg) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if err := cn.Handshake(); err != nil { | |
| return nil, err | |
| } | |
| if !cfg.InsecureSkipVerify { | |
| if err := cn.VerifyHostname(cfg.ServerName); err != nil { | |
| return nil, err | |
| } | |
| } | |
| state := cn.ConnectionState() | |
| if p := state.NegotiatedProtocol; p != http2NextProtoTLS { | |
| return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, http2NextProtoTLS) | |
| } | |
| if !state.NegotiatedProtocolIsMutual { | |
| return nil, errors.New("http2: could not negotiate protocol mutually") | |
| } | |
| return cn, nil | |
| } | |
| // disableKeepAlives reports whether connections should be closed as | |
| // soon as possible after handling the first request. | |
| func (t *http2Transport) disableKeepAlives() bool { | |
| return t.t1 != nil && t.t1.DisableKeepAlives | |
| } | |
| func (t *http2Transport) expectContinueTimeout() time.Duration { | |
| if t.t1 == nil { | |
| return 0 | |
| } | |
| return t.t1.ExpectContinueTimeout | |
| } | |
| func (t *http2Transport) NewClientConn(c net.Conn) (*http2ClientConn, error) { | |
| return t.newClientConn(c, t.disableKeepAlives()) | |
| } | |
| func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2ClientConn, error) { | |
| cc := &http2ClientConn{ | |
| t: t, | |
| tconn: c, | |
| readerDone: make(chan struct{}), | |
| nextStreamID: 1, | |
| maxFrameSize: 16 << 10, // spec default | |
| initialWindowSize: 65535, // spec default | |
| maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. | |
| peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. | |
| streams: make(map[uint32]*http2clientStream), | |
| singleUse: singleUse, | |
| wantSettingsAck: true, | |
| pings: make(map[[8]byte]chan struct{}), | |
| } | |
| if d := t.idleConnTimeout(); d != 0 { | |
| cc.idleTimeout = d | |
| cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout) | |
| } | |
| if http2VerboseLogs { | |
| t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) | |
| } | |
| cc.cond = sync.NewCond(&cc.mu) | |
| cc.flow.add(int32(http2initialWindowSize)) | |
| // TODO: adjust this writer size to account for frame size + | |
| // MTU + crypto/tls record padding. | |
| cc.bw = bufio.NewWriter(http2stickyErrWriter{c, &cc.werr}) | |
| cc.br = bufio.NewReader(c) | |
| cc.fr = http2NewFramer(cc.bw, cc.br) | |
| cc.fr.ReadMetaHeaders = hpack.NewDecoder(http2initialHeaderTableSize, nil) | |
| cc.fr.MaxHeaderListSize = t.maxHeaderListSize() | |
| // TODO: SetMaxDynamicTableSize, SetMaxDynamicTableSizeLimit on | |
| // henc in response to SETTINGS frames? | |
| cc.henc = hpack.NewEncoder(&cc.hbuf) | |
| if t.AllowHTTP { | |
| cc.nextStreamID = 3 | |
| } | |
| if cs, ok := c.(http2connectionStater); ok { | |
| state := cs.ConnectionState() | |
| cc.tlsState = &state | |
| } | |
| initialSettings := []http2Setting{ | |
| {ID: http2SettingEnablePush, Val: 0}, | |
| {ID: http2SettingInitialWindowSize, Val: http2transportDefaultStreamFlow}, | |
| } | |
| if max := t.maxHeaderListSize(); max != 0 { | |
| initialSettings = append(initialSettings, http2Setting{ID: http2SettingMaxHeaderListSize, Val: max}) | |
| } | |
| cc.bw.Write(http2clientPreface) | |
| cc.fr.WriteSettings(initialSettings...) | |
| cc.fr.WriteWindowUpdate(0, http2transportDefaultConnFlow) | |
| cc.inflow.add(http2transportDefaultConnFlow + http2initialWindowSize) | |
| cc.bw.Flush() | |
| if cc.werr != nil { | |
| cc.Close() | |
| return nil, cc.werr | |
| } | |
| go cc.readLoop() | |
| return cc, nil | |
| } | |
| func (cc *http2ClientConn) healthCheck() { | |
| pingTimeout := cc.t.pingTimeout() | |
| // We don't need to periodically ping in the health check, because the readLoop of ClientConn will | |
| // trigger the healthCheck again if there is no frame received. | |
| ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) | |
| defer cancel() | |
| err := cc.Ping(ctx) | |
| if err != nil { | |
| cc.closeForLostPing() | |
| cc.t.connPool().MarkDead(cc) | |
| return | |
| } | |
| } | |
| func (cc *http2ClientConn) setGoAway(f *http2GoAwayFrame) { | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| old := cc.goAway | |
| cc.goAway = f | |
| // Merge the previous and current GoAway error frames. | |
| if cc.goAwayDebug == "" { | |
| cc.goAwayDebug = string(f.DebugData()) | |
| } | |
| if old != nil && old.ErrCode != http2ErrCodeNo { | |
| cc.goAway.ErrCode = old.ErrCode | |
| } | |
| last := f.LastStreamID | |
| for streamID, cs := range cc.streams { | |
| if streamID > last { | |
| select { | |
| case cs.resc <- http2resAndError{err: http2errClientConnGotGoAway}: | |
| default: | |
| } | |
| } | |
| } | |
| } | |
| // CanTakeNewRequest reports whether the connection can take a new request, | |
| // meaning it has not been closed or received or sent a GOAWAY. | |
| func (cc *http2ClientConn) CanTakeNewRequest() bool { | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| return cc.canTakeNewRequestLocked() | |
| } | |
| // clientConnIdleState describes the suitability of a client | |
| // connection to initiate a new RoundTrip request. | |
| type http2clientConnIdleState struct { | |
| canTakeNewRequest bool | |
| freshConn bool // whether it's unused by any previous request | |
| } | |
| func (cc *http2ClientConn) idleState() http2clientConnIdleState { | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| return cc.idleStateLocked() | |
| } | |
| func (cc *http2ClientConn) idleStateLocked() (st http2clientConnIdleState) { | |
| if cc.singleUse && cc.nextStreamID > 1 { | |
| return | |
| } | |
| var maxConcurrentOkay bool | |
| if cc.t.StrictMaxConcurrentStreams { | |
| // We'll tell the caller we can take a new request to | |
| // prevent the caller from dialing a new TCP | |
| // connection, but then we'll block later before | |
| // writing it. | |
| maxConcurrentOkay = true | |
| } else { | |
| maxConcurrentOkay = int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) | |
| } | |
| st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay && | |
| int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 && | |
| !cc.tooIdleLocked() | |
| st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest | |
| return | |
| } | |
| func (cc *http2ClientConn) canTakeNewRequestLocked() bool { | |
| st := cc.idleStateLocked() | |
| return st.canTakeNewRequest | |
| } | |
| // tooIdleLocked reports whether this connection has been been sitting idle | |
| // for too much wall time. | |
| func (cc *http2ClientConn) tooIdleLocked() bool { | |
| // The Round(0) strips the monontonic clock reading so the | |
| // times are compared based on their wall time. We don't want | |
| // to reuse a connection that's been sitting idle during | |
| // VM/laptop suspend if monotonic time was also frozen. | |
| return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout | |
| } | |
| // onIdleTimeout is called from a time.AfterFunc goroutine. It will | |
| // only be called when we're idle, but because we're coming from a new | |
| // goroutine, there could be a new request coming in at the same time, | |
| // so this simply calls the synchronized closeIfIdle to shut down this | |
| // connection. The timer could just call closeIfIdle, but this is more | |
| // clear. | |
| func (cc *http2ClientConn) onIdleTimeout() { | |
| cc.closeIfIdle() | |
| } | |
| func (cc *http2ClientConn) closeIfIdle() { | |
| cc.mu.Lock() | |
| if len(cc.streams) > 0 { | |
| cc.mu.Unlock() | |
| return | |
| } | |
| cc.closed = true | |
| nextID := cc.nextStreamID | |
| // TODO: do clients send GOAWAY too? maybe? Just Close: | |
| cc.mu.Unlock() | |
| if http2VerboseLogs { | |
| cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2) | |
| } | |
| cc.tconn.Close() | |
| } | |
| var http2shutdownEnterWaitStateHook = func() {} | |
| // Shutdown gracefully close the client connection, waiting for running streams to complete. | |
| func (cc *http2ClientConn) Shutdown(ctx context.Context) error { | |
| if err := cc.sendGoAway(); err != nil { | |
| return err | |
| } | |
| // Wait for all in-flight streams to complete or connection to close | |
| done := make(chan error, 1) | |
| cancelled := false // guarded by cc.mu | |
| go func() { | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| for { | |
| if len(cc.streams) == 0 || cc.closed { | |
| cc.closed = true | |
| done <- cc.tconn.Close() | |
| break | |
| } | |
| if cancelled { | |
| break | |
| } | |
| cc.cond.Wait() | |
| } | |
| }() | |
| http2shutdownEnterWaitStateHook() | |
| select { | |
| case err := <-done: | |
| return err | |
| case <-ctx.Done(): | |
| cc.mu.Lock() | |
| // Free the goroutine above | |
| cancelled = true | |
| cc.cond.Broadcast() | |
| cc.mu.Unlock() | |
| return ctx.Err() | |
| } | |
| } | |
| func (cc *http2ClientConn) sendGoAway() error { | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| cc.wmu.Lock() | |
| defer cc.wmu.Unlock() | |
| if cc.closing { | |
| // GOAWAY sent already | |
| return nil | |
| } | |
| // Send a graceful shutdown frame to server | |
| maxStreamID := cc.nextStreamID | |
| if err := cc.fr.WriteGoAway(maxStreamID, http2ErrCodeNo, nil); err != nil { | |
| return err | |
| } | |
| if err := cc.bw.Flush(); err != nil { | |
| return err | |
| } | |
| // Prevent new requests | |
| cc.closing = true | |
| return nil | |
| } | |
| // closes the client connection immediately. In-flight requests are interrupted. | |
| // err is sent to streams. | |
| func (cc *http2ClientConn) closeForError(err error) error { | |
| cc.mu.Lock() | |
| defer cc.cond.Broadcast() | |
| defer cc.mu.Unlock() | |
| for id, cs := range cc.streams { | |
| select { | |
| case cs.resc <- http2resAndError{err: err}: | |
| default: | |
| } | |
| cs.bufPipe.CloseWithError(err) | |
| delete(cc.streams, id) | |
| } | |
| cc.closed = true | |
| return cc.tconn.Close() | |
| } | |
| // Close closes the client connection immediately. | |
| // | |
| // In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead. | |
| func (cc *http2ClientConn) Close() error { | |
| err := errors.New("http2: client connection force closed via ClientConn.Close") | |
| return cc.closeForError(err) | |
| } | |
| // closes the client connection immediately. In-flight requests are interrupted. | |
| func (cc *http2ClientConn) closeForLostPing() error { | |
| err := errors.New("http2: client connection lost") | |
| return cc.closeForError(err) | |
| } | |
| const http2maxAllocFrameSize = 512 << 10 | |
| // frameBuffer returns a scratch buffer suitable for writing DATA frames. | |
| // They're capped at the min of the peer's max frame size or 512KB | |
| // (kinda arbitrarily), but definitely capped so we don't allocate 4GB | |
| // bufers. | |
| func (cc *http2ClientConn) frameScratchBuffer() []byte { | |
| cc.mu.Lock() | |
| size := cc.maxFrameSize | |
| if size > http2maxAllocFrameSize { | |
| size = http2maxAllocFrameSize | |
| } | |
| for i, buf := range cc.freeBuf { | |
| if len(buf) >= int(size) { | |
| cc.freeBuf[i] = nil | |
| cc.mu.Unlock() | |
| return buf[:size] | |
| } | |
| } | |
| cc.mu.Unlock() | |
| return make([]byte, size) | |
| } | |
| func (cc *http2ClientConn) putFrameScratchBuffer(buf []byte) { | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| const maxBufs = 4 // arbitrary; 4 concurrent requests per conn? investigate. | |
| if len(cc.freeBuf) < maxBufs { | |
| cc.freeBuf = append(cc.freeBuf, buf) | |
| return | |
| } | |
| for i, old := range cc.freeBuf { | |
| if old == nil { | |
| cc.freeBuf[i] = buf | |
| return | |
| } | |
| } | |
| // forget about it. | |
| } | |
| // errRequestCanceled is a copy of net/http's errRequestCanceled because it's not | |
| // exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests. | |
| var http2errRequestCanceled = errors.New("net/http: request canceled") | |
| func http2commaSeparatedTrailers(req *Request) (string, error) { | |
| keys := make([]string, 0, len(req.Trailer)) | |
| for k := range req.Trailer { | |
| k = CanonicalHeaderKey(k) | |
| switch k { | |
| case "Transfer-Encoding", "Trailer", "Content-Length": | |
| return "", fmt.Errorf("invalid Trailer key %q", k) | |
| } | |
| keys = append(keys, k) | |
| } | |
| if len(keys) > 0 { | |
| sort.Strings(keys) | |
| return strings.Join(keys, ","), nil | |
| } | |
| return "", nil | |
| } | |
| func (cc *http2ClientConn) responseHeaderTimeout() time.Duration { | |
| if cc.t.t1 != nil { | |
| return cc.t.t1.ResponseHeaderTimeout | |
| } | |
| // No way to do this (yet?) with just an http2.Transport. Probably | |
| // no need. Request.Cancel this is the new way. We only need to support | |
| // this for compatibility with the old http.Transport fields when | |
| // we're doing transparent http2. | |
| return 0 | |
| } | |
| // checkConnHeaders checks whether req has any invalid connection-level headers. | |
| // per RFC 7540 section 8.1.2.2: Connection-Specific Header Fields. | |
| // Certain headers are special-cased as okay but not transmitted later. | |
| func http2checkConnHeaders(req *Request) error { | |
| if v := req.Header.Get("Upgrade"); v != "" { | |
| return fmt.Errorf("http2: invalid Upgrade request header: %q", req.Header["Upgrade"]) | |
| } | |
| if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") { | |
| return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv) | |
| } | |
| if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !strings.EqualFold(vv[0], "close") && !strings.EqualFold(vv[0], "keep-alive")) { | |
| return fmt.Errorf("http2: invalid Connection request header: %q", vv) | |
| } | |
| return nil | |
| } | |
| // actualContentLength returns a sanitized version of | |
| // req.ContentLength, where 0 actually means zero (not unknown) and -1 | |
| // means unknown. | |
| func http2actualContentLength(req *Request) int64 { | |
| if req.Body == nil || req.Body == NoBody { | |
| return 0 | |
| } | |
| if req.ContentLength != 0 { | |
| return req.ContentLength | |
| } | |
| return -1 | |
| } | |
| func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { | |
| resp, _, err := cc.roundTrip(req) | |
| return resp, err | |
| } | |
| func (cc *http2ClientConn) roundTrip(req *Request) (res *Response, gotErrAfterReqBodyWrite bool, err error) { | |
| if err := http2checkConnHeaders(req); err != nil { | |
| return nil, false, err | |
| } | |
| if cc.idleTimer != nil { | |
| cc.idleTimer.Stop() | |
| } | |
| trailers, err := http2commaSeparatedTrailers(req) | |
| if err != nil { | |
| return nil, false, err | |
| } | |
| hasTrailers := trailers != "" | |
| cc.mu.Lock() | |
| if err := cc.awaitOpenSlotForRequest(req); err != nil { | |
| cc.mu.Unlock() | |
| return nil, false, err | |
| } | |
| body := req.Body | |
| contentLen := http2actualContentLength(req) | |
| hasBody := contentLen != 0 | |
| // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? | |
| var requestedGzip bool | |
| if !cc.t.disableCompression() && | |
| req.Header.Get("Accept-Encoding") == "" && | |
| req.Header.Get("Range") == "" && | |
| req.Method != "HEAD" { | |
| // Request gzip only, not deflate. Deflate is ambiguous and | |
| // not as universally supported anyway. | |
| // See: https://zlib.net/zlib_faq.html#faq39 | |
| // | |
| // Note that we don't request this for HEAD requests, | |
| // due to a bug in nginx: | |
| // http://trac.nginx.org/nginx/ticket/358 | |
| // https://golang.org/issue/5522 | |
| // | |
| // We don't request gzip if the request is for a range, since | |
| // auto-decoding a portion of a gzipped document will just fail | |
| // anyway. See https://golang.org/issue/8923 | |
| requestedGzip = true | |
| } | |
| // we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is | |
| // sent by writeRequestBody below, along with any Trailers, | |
| // again in form HEADERS{1}, CONTINUATION{0,}) | |
| hdrs, err := cc.encodeHeaders(req, requestedGzip, trailers, contentLen) | |
| if err != nil { | |
| cc.mu.Unlock() | |
| return nil, false, err | |
| } | |
| cs := cc.newStream() | |
| cs.req = req | |
| cs.trace = httptrace.ContextClientTrace(req.Context()) | |
| cs.requestedGzip = requestedGzip | |
| bodyWriter := cc.t.getBodyWriterState(cs, body) | |
| cs.on100 = bodyWriter.on100 | |
| defer func() { | |
| cc.wmu.Lock() | |
| werr := cc.werr | |
| cc.wmu.Unlock() | |
| if werr != nil { | |
| cc.Close() | |
| } | |
| }() | |
| cc.wmu.Lock() | |
| endStream := !hasBody && !hasTrailers | |
| werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) | |
| cc.wmu.Unlock() | |
| http2traceWroteHeaders(cs.trace) | |
| cc.mu.Unlock() | |
| if werr != nil { | |
| if hasBody { | |
| req.Body.Close() // per RoundTripper contract | |
| bodyWriter.cancel() | |
| } | |
| cc.forgetStreamID(cs.ID) | |
| // Don't bother sending a RST_STREAM (our write already failed; | |
| // no need to keep writing) | |
| http2traceWroteRequest(cs.trace, werr) | |
| return nil, false, werr | |
| } | |
| var respHeaderTimer <-chan time.Time | |
| if hasBody { | |
| bodyWriter.scheduleBodyWrite() | |
| } else { | |
| http2traceWroteRequest(cs.trace, nil) | |
| if d := cc.responseHeaderTimeout(); d != 0 { | |
| timer := time.NewTimer(d) | |
| defer timer.Stop() | |
| respHeaderTimer = timer.C | |
| } | |
| } | |
| readLoopResCh := cs.resc | |
| bodyWritten := false | |
| ctx := req.Context() | |
| handleReadLoopResponse := func(re http2resAndError) (*Response, bool, error) { | |
| res := re.res | |
| if re.err != nil || res.StatusCode > 299 { | |
| // On error or status code 3xx, 4xx, 5xx, etc abort any | |
| // ongoing write, assuming that the server doesn't care | |
| // about our request body. If the server replied with 1xx or | |
| // 2xx, however, then assume the server DOES potentially | |
| // want our body (e.g. full-duplex streaming: | |
| // golang.org/issue/13444). If it turns out the server | |
| // doesn't, they'll RST_STREAM us soon enough. This is a | |
| // heuristic to avoid adding knobs to Transport. Hopefully | |
| // we can keep it. | |
| bodyWriter.cancel() | |
| cs.abortRequestBodyWrite(http2errStopReqBodyWrite) | |
| if hasBody && !bodyWritten { | |
| <-bodyWriter.resc | |
| } | |
| } | |
| if re.err != nil { | |
| cc.forgetStreamID(cs.ID) | |
| return nil, cs.getStartedWrite(), re.err | |
| } | |
| res.Request = req | |
| res.TLS = cc.tlsState | |
| return res, false, nil | |
| } | |
| for { | |
| select { | |
| case re := <-readLoopResCh: | |
| return handleReadLoopResponse(re) | |
| case <-respHeaderTimer: | |
| if !hasBody || bodyWritten { | |
| cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) | |
| } else { | |
| bodyWriter.cancel() | |
| cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) | |
| <-bodyWriter.resc | |
| } | |
| cc.forgetStreamID(cs.ID) | |
| return nil, cs.getStartedWrite(), http2errTimeout | |
| case <-ctx.Done(): | |
| if !hasBody || bodyWritten { | |
| cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) | |
| } else { | |
| bodyWriter.cancel() | |
| cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) | |
| <-bodyWriter.resc | |
| } | |
| cc.forgetStreamID(cs.ID) | |
| return nil, cs.getStartedWrite(), ctx.Err() | |
| case <-req.Cancel: | |
| if !hasBody || bodyWritten { | |
| cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) | |
| } else { | |
| bodyWriter.cancel() | |
| cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) | |
| <-bodyWriter.resc | |
| } | |
| cc.forgetStreamID(cs.ID) | |
| return nil, cs.getStartedWrite(), http2errRequestCanceled | |
| case <-cs.peerReset: | |
| // processResetStream already removed the | |
| // stream from the streams map; no need for | |
| // forgetStreamID. | |
| return nil, cs.getStartedWrite(), cs.resetErr | |
| case err := <-bodyWriter.resc: | |
| bodyWritten = true | |
| // Prefer the read loop's response, if available. Issue 16102. | |
| select { | |
| case re := <-readLoopResCh: | |
| return handleReadLoopResponse(re) | |
| default: | |
| } | |
| if err != nil { | |
| cc.forgetStreamID(cs.ID) | |
| return nil, cs.getStartedWrite(), err | |
| } | |
| if d := cc.responseHeaderTimeout(); d != 0 { | |
| timer := time.NewTimer(d) | |
| defer timer.Stop() | |
| respHeaderTimer = timer.C | |
| } | |
| } | |
| } | |
| } | |
| // awaitOpenSlotForRequest waits until len(streams) < maxConcurrentStreams. | |
| // Must hold cc.mu. | |
| func (cc *http2ClientConn) awaitOpenSlotForRequest(req *Request) error { | |
| var waitingForConn chan struct{} | |
| var waitingForConnErr error // guarded by cc.mu | |
| for { | |
| cc.lastActive = time.Now() | |
| if cc.closed || !cc.canTakeNewRequestLocked() { | |
| if waitingForConn != nil { | |
| close(waitingForConn) | |
| } | |
| return http2errClientConnUnusable | |
| } | |
| cc.lastIdle = time.Time{} | |
| if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) { | |
| if waitingForConn != nil { | |
| close(waitingForConn) | |
| } | |
| return nil | |
| } | |
| // Unfortunately, we cannot wait on a condition variable and channel at | |
| // the same time, so instead, we spin up a goroutine to check if the | |
| // request is canceled while we wait for a slot to open in the connection. | |
| if waitingForConn == nil { | |
| waitingForConn = make(chan struct{}) | |
| go func() { | |
| if err := http2awaitRequestCancel(req, waitingForConn); err != nil { | |
| cc.mu.Lock() | |
| waitingForConnErr = err | |
| cc.cond.Broadcast() | |
| cc.mu.Unlock() | |
| } | |
| }() | |
| } | |
| cc.pendingRequests++ | |
| cc.cond.Wait() | |
| cc.pendingRequests-- | |
| if waitingForConnErr != nil { | |
| return waitingForConnErr | |
| } | |
| } | |
| } | |
| // requires cc.wmu be held | |
| func (cc *http2ClientConn) writeHeaders(streamID uint32, endStream bool, maxFrameSize int, hdrs []byte) error { | |
| first := true // first frame written (HEADERS is first, then CONTINUATION) | |
| for len(hdrs) > 0 && cc.werr == nil { | |
| chunk := hdrs | |
| if len(chunk) > maxFrameSize { | |
| chunk = chunk[:maxFrameSize] | |
| } | |
| hdrs = hdrs[len(chunk):] | |
| endHeaders := len(hdrs) == 0 | |
| if first { | |
| cc.fr.WriteHeaders(http2HeadersFrameParam{ | |
| StreamID: streamID, | |
| BlockFragment: chunk, | |
| EndStream: endStream, | |
| EndHeaders: endHeaders, | |
| }) | |
| first = false | |
| } else { | |
| cc.fr.WriteContinuation(streamID, endHeaders, chunk) | |
| } | |
| } | |
| // TODO(bradfitz): this Flush could potentially block (as | |
| // could the WriteHeaders call(s) above), which means they | |
| // wouldn't respond to Request.Cancel being readable. That's | |
| // rare, but this should probably be in a goroutine. | |
| cc.bw.Flush() | |
| return cc.werr | |
| } | |
| // internal error values; they don't escape to callers | |
| var ( | |
| // abort request body write; don't send cancel | |
| http2errStopReqBodyWrite = errors.New("http2: aborting request body write") | |
| // abort request body write, but send stream reset of cancel. | |
| http2errStopReqBodyWriteAndCancel = errors.New("http2: canceling request") | |
| http2errReqBodyTooLong = errors.New("http2: request body larger than specified content length") | |
| ) | |
| func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (err error) { | |
| cc := cs.cc | |
| sentEnd := false // whether we sent the final DATA frame w/ END_STREAM | |
| buf := cc.frameScratchBuffer() | |
| defer cc.putFrameScratchBuffer(buf) | |
| defer func() { | |
| http2traceWroteRequest(cs.trace, err) | |
| // TODO: write h12Compare test showing whether | |
| // Request.Body is closed by the Transport, | |
| // and in multiple cases: server replies <=299 and >299 | |
| // while still writing request body | |
| cerr := bodyCloser.Close() | |
| if err == nil { | |
| err = cerr | |
| } | |
| }() | |
| req := cs.req | |
| hasTrailers := req.Trailer != nil | |
| remainLen := http2actualContentLength(req) | |
| hasContentLen := remainLen != -1 | |
| var sawEOF bool | |
| for !sawEOF { | |
| n, err := body.Read(buf[:len(buf)-1]) | |
| if hasContentLen { | |
| remainLen -= int64(n) | |
| if remainLen == 0 && err == nil { | |
| // The request body's Content-Length was predeclared and | |
| // we just finished reading it all, but the underlying io.Reader | |
| // returned the final chunk with a nil error (which is one of | |
| // the two valid things a Reader can do at EOF). Because we'd prefer | |
| // to send the END_STREAM bit early, double-check that we're actually | |
| // at EOF. Subsequent reads should return (0, EOF) at this point. | |
| // If either value is different, we return an error in one of two ways below. | |
| var n1 int | |
| n1, err = body.Read(buf[n:]) | |
| remainLen -= int64(n1) | |
| } | |
| if remainLen < 0 { | |
| err = http2errReqBodyTooLong | |
| cc.writeStreamReset(cs.ID, http2ErrCodeCancel, err) | |
| return err | |
| } | |
| } | |
| if err == io.EOF { | |
| sawEOF = true | |
| err = nil | |
| } else if err != nil { | |
| cc.writeStreamReset(cs.ID, http2ErrCodeCancel, err) | |
| return err | |
| } | |
| remain := buf[:n] | |
| for len(remain) > 0 && err == nil { | |
| var allowed int32 | |
| allowed, err = cs.awaitFlowControl(len(remain)) | |
| switch { | |
| case err == http2errStopReqBodyWrite: | |
| return err | |
| case err == http2errStopReqBodyWriteAndCancel: | |
| cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) | |
| return err | |
| case err != nil: | |
| return err | |
| } | |
| cc.wmu.Lock() | |
| data := remain[:allowed] | |
| remain = remain[allowed:] | |
| sentEnd = sawEOF && len(remain) == 0 && !hasTrailers | |
| err = cc.fr.WriteData(cs.ID, sentEnd, data) | |
| if err == nil { | |
| // TODO(bradfitz): this flush is for latency, not bandwidth. | |
| // Most requests won't need this. Make this opt-in or | |
| // opt-out? Use some heuristic on the body type? Nagel-like | |
| // timers? Based on 'n'? Only last chunk of this for loop, | |
| // unless flow control tokens are low? For now, always. | |
| // If we change this, see comment below. | |
| err = cc.bw.Flush() | |
| } | |
| cc.wmu.Unlock() | |
| } | |
| if err != nil { | |
| return err | |
| } | |
| } | |
| if sentEnd { | |
| // Already sent END_STREAM (which implies we have no | |
| // trailers) and flushed, because currently all | |
| // WriteData frames above get a flush. So we're done. | |
| return nil | |
| } | |
| var trls []byte | |
| if hasTrailers { | |
| cc.mu.Lock() | |
| trls, err = cc.encodeTrailers(req) | |
| cc.mu.Unlock() | |
| if err != nil { | |
| cc.writeStreamReset(cs.ID, http2ErrCodeInternal, err) | |
| cc.forgetStreamID(cs.ID) | |
| return err | |
| } | |
| } | |
| cc.mu.Lock() | |
| maxFrameSize := int(cc.maxFrameSize) | |
| cc.mu.Unlock() | |
| cc.wmu.Lock() | |
| defer cc.wmu.Unlock() | |
| // Two ways to send END_STREAM: either with trailers, or | |
| // with an empty DATA frame. | |
| if len(trls) > 0 { | |
| err = cc.writeHeaders(cs.ID, true, maxFrameSize, trls) | |
| } else { | |
| err = cc.fr.WriteData(cs.ID, true, nil) | |
| } | |
| if ferr := cc.bw.Flush(); ferr != nil && err == nil { | |
| err = ferr | |
| } | |
| return err | |
| } | |
| // awaitFlowControl waits for [1, min(maxBytes, cc.cs.maxFrameSize)] flow | |
| // control tokens from the server. | |
| // It returns either the non-zero number of tokens taken or an error | |
| // if the stream is dead. | |
| func (cs *http2clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) { | |
| cc := cs.cc | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| for { | |
| if cc.closed { | |
| return 0, http2errClientConnClosed | |
| } | |
| if cs.stopReqBody != nil { | |
| return 0, cs.stopReqBody | |
| } | |
| if err := cs.checkResetOrDone(); err != nil { | |
| return 0, err | |
| } | |
| if a := cs.flow.available(); a > 0 { | |
| take := a | |
| if int(take) > maxBytes { | |
| take = int32(maxBytes) // can't truncate int; take is int32 | |
| } | |
| if take > int32(cc.maxFrameSize) { | |
| take = int32(cc.maxFrameSize) | |
| } | |
| cs.flow.take(take) | |
| return take, nil | |
| } | |
| cc.cond.Wait() | |
| } | |
| } | |
| // requires cc.mu be held. | |
| func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { | |
| cc.hbuf.Reset() | |
| host := req.Host | |
| if host == "" { | |
| host = req.URL.Host | |
| } | |
| host, err := httpguts.PunycodeHostPort(host) | |
| if err != nil { | |
| return nil, err | |
| } | |
| var path string | |
| if req.Method != "CONNECT" { | |
| path = req.URL.RequestURI() | |
| if !http2validPseudoPath(path) { | |
| orig := path | |
| path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host) | |
| if !http2validPseudoPath(path) { | |
| if req.URL.Opaque != "" { | |
| return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque) | |
| } else { | |
| return nil, fmt.Errorf("invalid request :path %q", orig) | |
| } | |
| } | |
| } | |
| } | |
| // Check for any invalid headers and return an error before we | |
| // potentially pollute our hpack state. (We want to be able to | |
| // continue to reuse the hpack encoder for future requests) | |
| for k, vv := range req.Header { | |
| if !httpguts.ValidHeaderFieldName(k) { | |
| return nil, fmt.Errorf("invalid HTTP header name %q", k) | |
| } | |
| for _, v := range vv { | |
| if !httpguts.ValidHeaderFieldValue(v) { | |
| return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k) | |
| } | |
| } | |
| } | |
| enumerateHeaders := func(f func(name, value string)) { | |
| // 8.1.2.3 Request Pseudo-Header Fields | |
| // The :path pseudo-header field includes the path and query parts of the | |
| // target URI (the path-absolute production and optionally a '?' character | |
| // followed by the query production (see Sections 3.3 and 3.4 of | |
| // [RFC3986]). | |
| f(":authority", host) | |
| m := req.Method | |
| if m == "" { | |
| m = MethodGet | |
| } | |
| f(":method", m) | |
| if req.Method != "CONNECT" { | |
| f(":path", path) | |
| f(":scheme", req.URL.Scheme) | |
| } | |
| if trailers != "" { | |
| f("trailer", trailers) | |
| } | |
| var didUA bool | |
| for k, vv := range req.Header { | |
| if strings.EqualFold(k, "host") || strings.EqualFold(k, "content-length") { | |
| // Host is :authority, already sent. | |
| // Content-Length is automatic, set below. | |
| continue | |
| } else if strings.EqualFold(k, "connection") || strings.EqualFold(k, "proxy-connection") || | |
| strings.EqualFold(k, "transfer-encoding") || strings.EqualFold(k, "upgrade") || | |
| strings.EqualFold(k, "keep-alive") { | |
| // Per 8.1.2.2 Connection-Specific Header | |
| // Fields, don't send connection-specific | |
| // fields. We have already checked if any | |
| // are error-worthy so just ignore the rest. | |
| continue | |
| } else if strings.EqualFold(k, "user-agent") { | |
| // Match Go's http1 behavior: at most one | |
| // User-Agent. If set to nil or empty string, | |
| // then omit it. Otherwise if not mentioned, | |
| // include the default (below). | |
| didUA = true | |
| if len(vv) < 1 { | |
| continue | |
| } | |
| vv = vv[:1] | |
| if vv[0] == "" { | |
| continue | |
| } | |
| } else if strings.EqualFold(k, "cookie") { | |
| // Per 8.1.2.5 To allow for better compression efficiency, the | |
| // Cookie header field MAY be split into separate header fields, | |
| // each with one or more cookie-pairs. | |
| for _, v := range vv { | |
| for { | |
| p := strings.IndexByte(v, ';') | |
| if p < 0 { | |
| break | |
| } | |
| f("cookie", v[:p]) | |
| p++ | |
| // strip space after semicolon if any. | |
| for p+1 <= len(v) && v[p] == ' ' { | |
| p++ | |
| } | |
| v = v[p:] | |
| } | |
| if len(v) > 0 { | |
| f("cookie", v) | |
| } | |
| } | |
| continue | |
| } | |
| for _, v := range vv { | |
| f(k, v) | |
| } | |
| } | |
| if http2shouldSendReqContentLength(req.Method, contentLength) { | |
| f("content-length", strconv.FormatInt(contentLength, 10)) | |
| } | |
| if addGzipHeader { | |
| f("accept-encoding", "gzip") | |
| } | |
| if !didUA { | |
| f("user-agent", http2defaultUserAgent) | |
| } | |
| } | |
| // Do a first pass over the headers counting bytes to ensure | |
| // we don't exceed cc.peerMaxHeaderListSize. This is done as a | |
| // separate pass before encoding the headers to prevent | |
| // modifying the hpack state. | |
| hlSize := uint64(0) | |
| enumerateHeaders(func(name, value string) { | |
| hf := hpack.HeaderField{Name: name, Value: value} | |
| hlSize += uint64(hf.Size()) | |
| }) | |
| if hlSize > cc.peerMaxHeaderListSize { | |
| return nil, http2errRequestHeaderListSize | |
| } | |
| trace := httptrace.ContextClientTrace(req.Context()) | |
| traceHeaders := http2traceHasWroteHeaderField(trace) | |
| // Header list size is ok. Write the headers. | |
| enumerateHeaders(func(name, value string) { | |
| name = strings.ToLower(name) | |
| cc.writeHeader(name, value) | |
| if traceHeaders { | |
| http2traceWroteHeaderField(trace, name, value) | |
| } | |
| }) | |
| return cc.hbuf.Bytes(), nil | |
| } | |
| // shouldSendReqContentLength reports whether the http2.Transport should send | |
| // a "content-length" request header. This logic is basically a copy of the net/http | |
| // transferWriter.shouldSendContentLength. | |
| // The contentLength is the corrected contentLength (so 0 means actually 0, not unknown). | |
| // -1 means unknown. | |
| func http2shouldSendReqContentLength(method string, contentLength int64) bool { | |
| if contentLength > 0 { | |
| return true | |
| } | |
| if contentLength < 0 { | |
| return false | |
| } | |
| // For zero bodies, whether we send a content-length depends on the method. | |
| // It also kinda doesn't matter for http2 either way, with END_STREAM. | |
| switch method { | |
| case "POST", "PUT", "PATCH": | |
| return true | |
| default: | |
| return false | |
| } | |
| } | |
| // requires cc.mu be held. | |
| func (cc *http2ClientConn) encodeTrailers(req *Request) ([]byte, error) { | |
| cc.hbuf.Reset() | |
| hlSize := uint64(0) | |
| for k, vv := range req.Trailer { | |
| for _, v := range vv { | |
| hf := hpack.HeaderField{Name: k, Value: v} | |
| hlSize += uint64(hf.Size()) | |
| } | |
| } | |
| if hlSize > cc.peerMaxHeaderListSize { | |
| return nil, http2errRequestHeaderListSize | |
| } | |
| for k, vv := range req.Trailer { | |
| // Transfer-Encoding, etc.. have already been filtered at the | |
| // start of RoundTrip | |
| lowKey := strings.ToLower(k) | |
| for _, v := range vv { | |
| cc.writeHeader(lowKey, v) | |
| } | |
| } | |
| return cc.hbuf.Bytes(), nil | |
| } | |
| func (cc *http2ClientConn) writeHeader(name, value string) { | |
| if http2VerboseLogs { | |
| log.Printf("http2: Transport encoding header %q = %q", name, value) | |
| } | |
| cc.henc.WriteField(hpack.HeaderField{Name: name, Value: value}) | |
| } | |
| type http2resAndError struct { | |
| _ http2incomparable | |
| res *Response | |
| err error | |
| } | |
| // requires cc.mu be held. | |
| func (cc *http2ClientConn) newStream() *http2clientStream { | |
| cs := &http2clientStream{ | |
| cc: cc, | |
| ID: cc.nextStreamID, | |
| resc: make(chan http2resAndError, 1), | |
| peerReset: make(chan struct{}), | |
| done: make(chan struct{}), | |
| } | |
| cs.flow.add(int32(cc.initialWindowSize)) | |
| cs.flow.setConnFlow(&cc.flow) | |
| cs.inflow.add(http2transportDefaultStreamFlow) | |
| cs.inflow.setConnFlow(&cc.inflow) | |
| cc.nextStreamID += 2 | |
| cc.streams[cs.ID] = cs | |
| return cs | |
| } | |
| func (cc *http2ClientConn) forgetStreamID(id uint32) { | |
| cc.streamByID(id, true) | |
| } | |
| func (cc *http2ClientConn) streamByID(id uint32, andRemove bool) *http2clientStream { | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| cs := cc.streams[id] | |
| if andRemove && cs != nil && !cc.closed { | |
| cc.lastActive = time.Now() | |
| delete(cc.streams, id) | |
| if len(cc.streams) == 0 && cc.idleTimer != nil { | |
| cc.idleTimer.Reset(cc.idleTimeout) | |
| cc.lastIdle = time.Now() | |
| } | |
| close(cs.done) | |
| // Wake up checkResetOrDone via clientStream.awaitFlowControl and | |
| // wake up RoundTrip if there is a pending request. | |
| cc.cond.Broadcast() | |
| } | |
| return cs | |
| } | |
| // clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop. | |
| type http2clientConnReadLoop struct { | |
| _ http2incomparable | |
| cc *http2ClientConn | |
| closeWhenIdle bool | |
| } | |
| // readLoop runs in its own goroutine and reads and dispatches frames. | |
| func (cc *http2ClientConn) readLoop() { | |
| rl := &http2clientConnReadLoop{cc: cc} | |
| defer rl.cleanup() | |
| cc.readerErr = rl.run() | |
| if ce, ok := cc.readerErr.(http2ConnectionError); ok { | |
| cc.wmu.Lock() | |
| cc.fr.WriteGoAway(0, http2ErrCode(ce), nil) | |
| cc.wmu.Unlock() | |
| } | |
| } | |
| // GoAwayError is returned by the Transport when the server closes the | |
| // TCP connection after sending a GOAWAY frame. | |
| type http2GoAwayError struct { | |
| LastStreamID uint32 | |
| ErrCode http2ErrCode | |
| DebugData string | |
| } | |
| func (e http2GoAwayError) Error() string { | |
| return fmt.Sprintf("http2: server sent GOAWAY and closed the connection; LastStreamID=%v, ErrCode=%v, debug=%q", | |
| e.LastStreamID, e.ErrCode, e.DebugData) | |
| } | |
| func http2isEOFOrNetReadError(err error) bool { | |
| if err == io.EOF { | |
| return true | |
| } | |
| ne, ok := err.(*net.OpError) | |
| return ok && ne.Op == "read" | |
| } | |
| func (rl *http2clientConnReadLoop) cleanup() { | |
| cc := rl.cc | |
| defer cc.tconn.Close() | |
| defer cc.t.connPool().MarkDead(cc) | |
| defer close(cc.readerDone) | |
| if cc.idleTimer != nil { | |
| cc.idleTimer.Stop() | |
| } | |
| // Close any response bodies if the server closes prematurely. | |
| // TODO: also do this if we've written the headers but not | |
| // gotten a response yet. | |
| err := cc.readerErr | |
| cc.mu.Lock() | |
| if cc.goAway != nil && http2isEOFOrNetReadError(err) { | |
| err = http2GoAwayError{ | |
| LastStreamID: cc.goAway.LastStreamID, | |
| ErrCode: cc.goAway.ErrCode, | |
| DebugData: cc.goAwayDebug, | |
| } | |
| } else if err == io.EOF { | |
| err = io.ErrUnexpectedEOF | |
| } | |
| for _, cs := range cc.streams { | |
| cs.bufPipe.CloseWithError(err) // no-op if already closed | |
| select { | |
| case cs.resc <- http2resAndError{err: err}: | |
| default: | |
| } | |
| close(cs.done) | |
| } | |
| cc.closed = true | |
| cc.cond.Broadcast() | |
| cc.mu.Unlock() | |
| } | |
| func (rl *http2clientConnReadLoop) run() error { | |
| cc := rl.cc | |
| rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse | |
| gotReply := false // ever saw a HEADERS reply | |
| gotSettings := false | |
| readIdleTimeout := cc.t.ReadIdleTimeout | |
| var t *time.Timer | |
| if readIdleTimeout != 0 { | |
| t = time.AfterFunc(readIdleTimeout, cc.healthCheck) | |
| defer t.Stop() | |
| } | |
| for { | |
| f, err := cc.fr.ReadFrame() | |
| if t != nil { | |
| t.Reset(readIdleTimeout) | |
| } | |
| if err != nil { | |
| cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) | |
| } | |
| if se, ok := err.(http2StreamError); ok { | |
| if cs := cc.streamByID(se.StreamID, false); cs != nil { | |
| cs.cc.writeStreamReset(cs.ID, se.Code, err) | |
| cs.cc.forgetStreamID(cs.ID) | |
| if se.Cause == nil { | |
| se.Cause = cc.fr.errDetail | |
| } | |
| rl.endStreamError(cs, se) | |
| } | |
| continue | |
| } else if err != nil { | |
| return err | |
| } | |
| if http2VerboseLogs { | |
| cc.vlogf("http2: Transport received %s", http2summarizeFrame(f)) | |
| } | |
| if !gotSettings { | |
| if _, ok := f.(*http2SettingsFrame); !ok { | |
| cc.logf("protocol error: received %T before a SETTINGS frame", f) | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| gotSettings = true | |
| } | |
| maybeIdle := false // whether frame might transition us to idle | |
| switch f := f.(type) { | |
| case *http2MetaHeadersFrame: | |
| err = rl.processHeaders(f) | |
| maybeIdle = true | |
| gotReply = true | |
| case *http2DataFrame: | |
| err = rl.processData(f) | |
| maybeIdle = true | |
| case *http2GoAwayFrame: | |
| err = rl.processGoAway(f) | |
| maybeIdle = true | |
| case *http2RSTStreamFrame: | |
| err = rl.processResetStream(f) | |
| maybeIdle = true | |
| case *http2SettingsFrame: | |
| err = rl.processSettings(f) | |
| case *http2PushPromiseFrame: | |
| err = rl.processPushPromise(f) | |
| case *http2WindowUpdateFrame: | |
| err = rl.processWindowUpdate(f) | |
| case *http2PingFrame: | |
| err = rl.processPing(f) | |
| default: | |
| cc.logf("Transport: unhandled response frame type %T", f) | |
| } | |
| if err != nil { | |
| if http2VerboseLogs { | |
| cc.vlogf("http2: Transport conn %p received error from processing frame %v: %v", cc, http2summarizeFrame(f), err) | |
| } | |
| return err | |
| } | |
| if rl.closeWhenIdle && gotReply && maybeIdle { | |
| cc.closeIfIdle() | |
| } | |
| } | |
| } | |
| func (rl *http2clientConnReadLoop) processHeaders(f *http2MetaHeadersFrame) error { | |
| cc := rl.cc | |
| cs := cc.streamByID(f.StreamID, false) | |
| if cs == nil { | |
| // We'd get here if we canceled a request while the | |
| // server had its response still in flight. So if this | |
| // was just something we canceled, ignore it. | |
| return nil | |
| } | |
| if f.StreamEnded() { | |
| // Issue 20521: If the stream has ended, streamByID() causes | |
| // clientStream.done to be closed, which causes the request's bodyWriter | |
| // to be closed with an errStreamClosed, which may be received by | |
| // clientConn.RoundTrip before the result of processing these headers. | |
| // Deferring stream closure allows the header processing to occur first. | |
| // clientConn.RoundTrip may still receive the bodyWriter error first, but | |
| // the fix for issue 16102 prioritises any response. | |
| // | |
| // Issue 22413: If there is no request body, we should close the | |
| // stream before writing to cs.resc so that the stream is closed | |
| // immediately once RoundTrip returns. | |
| if cs.req.Body != nil { | |
| defer cc.forgetStreamID(f.StreamID) | |
| } else { | |
| cc.forgetStreamID(f.StreamID) | |
| } | |
| } | |
| if !cs.firstByte { | |
| if cs.trace != nil { | |
| // TODO(bradfitz): move first response byte earlier, | |
| // when we first read the 9 byte header, not waiting | |
| // until all the HEADERS+CONTINUATION frames have been | |
| // merged. This works for now. | |
| http2traceFirstResponseByte(cs.trace) | |
| } | |
| cs.firstByte = true | |
| } | |
| if !cs.pastHeaders { | |
| cs.pastHeaders = true | |
| } else { | |
| return rl.processTrailers(cs, f) | |
| } | |
| res, err := rl.handleResponse(cs, f) | |
| if err != nil { | |
| if _, ok := err.(http2ConnectionError); ok { | |
| return err | |
| } | |
| // Any other error type is a stream error. | |
| cs.cc.writeStreamReset(f.StreamID, http2ErrCodeProtocol, err) | |
| cc.forgetStreamID(cs.ID) | |
| cs.resc <- http2resAndError{err: err} | |
| return nil // return nil from process* funcs to keep conn alive | |
| } | |
| if res == nil { | |
| // (nil, nil) special case. See handleResponse docs. | |
| return nil | |
| } | |
| cs.resTrailer = &res.Trailer | |
| cs.resc <- http2resAndError{res: res} | |
| return nil | |
| } | |
| // may return error types nil, or ConnectionError. Any other error value | |
| // is a StreamError of type ErrCodeProtocol. The returned error in that case | |
| // is the detail. | |
| // | |
| // As a special case, handleResponse may return (nil, nil) to skip the | |
| // frame (currently only used for 1xx responses). | |
| func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http2MetaHeadersFrame) (*Response, error) { | |
| if f.Truncated { | |
| return nil, http2errResponseHeaderListSize | |
| } | |
| status := f.PseudoValue("status") | |
| if status == "" { | |
| return nil, errors.New("malformed response from server: missing status pseudo header") | |
| } | |
| statusCode, err := strconv.Atoi(status) | |
| if err != nil { | |
| return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header") | |
| } | |
| regularFields := f.RegularFields() | |
| strs := make([]string, len(regularFields)) | |
| header := make(Header, len(regularFields)) | |
| res := &Response{ | |
| Proto: "HTTP/2.0", | |
| ProtoMajor: 2, | |
| Header: header, | |
| StatusCode: statusCode, | |
| Status: status + " " + StatusText(statusCode), | |
| } | |
| for _, hf := range regularFields { | |
| key := CanonicalHeaderKey(hf.Name) | |
| if key == "Trailer" { | |
| t := res.Trailer | |
| if t == nil { | |
| t = make(Header) | |
| res.Trailer = t | |
| } | |
| http2foreachHeaderElement(hf.Value, func(v string) { | |
| t[CanonicalHeaderKey(v)] = nil | |
| }) | |
| } else { | |
| vv := header[key] | |
| if vv == nil && len(strs) > 0 { | |
| // More than likely this will be a single-element key. | |
| // Most headers aren't multi-valued. | |
| // Set the capacity on strs[0] to 1, so any future append | |
| // won't extend the slice into the other strings. | |
| vv, strs = strs[:1:1], strs[1:] | |
| vv[0] = hf.Value | |
| header[key] = vv | |
| } else { | |
| header[key] = append(vv, hf.Value) | |
| } | |
| } | |
| } | |
| if statusCode >= 100 && statusCode <= 199 { | |
| cs.num1xx++ | |
| const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http | |
| if cs.num1xx > max1xxResponses { | |
| return nil, errors.New("http2: too many 1xx informational responses") | |
| } | |
| if fn := cs.get1xxTraceFunc(); fn != nil { | |
| if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil { | |
| return nil, err | |
| } | |
| } | |
| if statusCode == 100 { | |
| http2traceGot100Continue(cs.trace) | |
| if cs.on100 != nil { | |
| cs.on100() // forces any write delay timer to fire | |
| } | |
| } | |
| cs.pastHeaders = false // do it all again | |
| return nil, nil | |
| } | |
| streamEnded := f.StreamEnded() | |
| isHead := cs.req.Method == "HEAD" | |
| if !streamEnded || isHead { | |
| res.ContentLength = -1 | |
| if clens := res.Header["Content-Length"]; len(clens) == 1 { | |
| if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil { | |
| res.ContentLength = int64(cl) | |
| } else { | |
| // TODO: care? unlike http/1, it won't mess up our framing, so it's | |
| // more safe smuggling-wise to ignore. | |
| } | |
| } else if len(clens) > 1 { | |
| // TODO: care? unlike http/1, it won't mess up our framing, so it's | |
| // more safe smuggling-wise to ignore. | |
| } | |
| } | |
| if streamEnded || isHead { | |
| res.Body = http2noBody | |
| return res, nil | |
| } | |
| cs.bufPipe = http2pipe{b: &http2dataBuffer{expected: res.ContentLength}} | |
| cs.bytesRemain = res.ContentLength | |
| res.Body = http2transportResponseBody{cs} | |
| go cs.awaitRequestCancel(cs.req) | |
| if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" { | |
| res.Header.Del("Content-Encoding") | |
| res.Header.Del("Content-Length") | |
| res.ContentLength = -1 | |
| res.Body = &http2gzipReader{body: res.Body} | |
| res.Uncompressed = true | |
| } | |
| return res, nil | |
| } | |
| func (rl *http2clientConnReadLoop) processTrailers(cs *http2clientStream, f *http2MetaHeadersFrame) error { | |
| if cs.pastTrailers { | |
| // Too many HEADERS frames for this stream. | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| cs.pastTrailers = true | |
| if !f.StreamEnded() { | |
| // We expect that any headers for trailers also | |
| // has END_STREAM. | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| if len(f.PseudoFields()) > 0 { | |
| // No pseudo header fields are defined for trailers. | |
| // TODO: ConnectionError might be overly harsh? Check. | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| trailer := make(Header) | |
| for _, hf := range f.RegularFields() { | |
| key := CanonicalHeaderKey(hf.Name) | |
| trailer[key] = append(trailer[key], hf.Value) | |
| } | |
| cs.trailer = trailer | |
| rl.endStream(cs) | |
| return nil | |
| } | |
| // transportResponseBody is the concrete type of Transport.RoundTrip's | |
| // Response.Body. It is an io.ReadCloser. On Read, it reads from cs.body. | |
| // On Close it sends RST_STREAM if EOF wasn't already seen. | |
| type http2transportResponseBody struct { | |
| cs *http2clientStream | |
| } | |
| func (b http2transportResponseBody) Read(p []byte) (n int, err error) { | |
| cs := b.cs | |
| cc := cs.cc | |
| if cs.readErr != nil { | |
| return 0, cs.readErr | |
| } | |
| n, err = b.cs.bufPipe.Read(p) | |
| if cs.bytesRemain != -1 { | |
| if int64(n) > cs.bytesRemain { | |
| n = int(cs.bytesRemain) | |
| if err == nil { | |
| err = errors.New("net/http: server replied with more than declared Content-Length; truncated") | |
| cc.writeStreamReset(cs.ID, http2ErrCodeProtocol, err) | |
| } | |
| cs.readErr = err | |
| return int(cs.bytesRemain), err | |
| } | |
| cs.bytesRemain -= int64(n) | |
| if err == io.EOF && cs.bytesRemain > 0 { | |
| err = io.ErrUnexpectedEOF | |
| cs.readErr = err | |
| return n, err | |
| } | |
| } | |
| if n == 0 { | |
| // No flow control tokens to send back. | |
| return | |
| } | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| var connAdd, streamAdd int32 | |
| // Check the conn-level first, before the stream-level. | |
| if v := cc.inflow.available(); v < http2transportDefaultConnFlow/2 { | |
| connAdd = http2transportDefaultConnFlow - v | |
| cc.inflow.add(connAdd) | |
| } | |
| if err == nil { // No need to refresh if the stream is over or failed. | |
| // Consider any buffered body data (read from the conn but not | |
| // consumed by the client) when computing flow control for this | |
| // stream. | |
| v := int(cs.inflow.available()) + cs.bufPipe.Len() | |
| if v < http2transportDefaultStreamFlow-http2transportDefaultStreamMinRefresh { | |
| streamAdd = int32(http2transportDefaultStreamFlow - v) | |
| cs.inflow.add(streamAdd) | |
| } | |
| } | |
| if connAdd != 0 || streamAdd != 0 { | |
| cc.wmu.Lock() | |
| defer cc.wmu.Unlock() | |
| if connAdd != 0 { | |
| cc.fr.WriteWindowUpdate(0, http2mustUint31(connAdd)) | |
| } | |
| if streamAdd != 0 { | |
| cc.fr.WriteWindowUpdate(cs.ID, http2mustUint31(streamAdd)) | |
| } | |
| cc.bw.Flush() | |
| } | |
| return | |
| } | |
| var http2errClosedResponseBody = errors.New("http2: response body closed") | |
| func (b http2transportResponseBody) Close() error { | |
| cs := b.cs | |
| cc := cs.cc | |
| serverSentStreamEnd := cs.bufPipe.Err() == io.EOF | |
| unread := cs.bufPipe.Len() | |
| if unread > 0 || !serverSentStreamEnd { | |
| cc.mu.Lock() | |
| cc.wmu.Lock() | |
| if !serverSentStreamEnd { | |
| cc.fr.WriteRSTStream(cs.ID, http2ErrCodeCancel) | |
| cs.didReset = true | |
| } | |
| // Return connection-level flow control. | |
| if unread > 0 { | |
| cc.inflow.add(int32(unread)) | |
| cc.fr.WriteWindowUpdate(0, uint32(unread)) | |
| } | |
| cc.bw.Flush() | |
| cc.wmu.Unlock() | |
| cc.mu.Unlock() | |
| } | |
| cs.bufPipe.BreakWithError(http2errClosedResponseBody) | |
| cc.forgetStreamID(cs.ID) | |
| return nil | |
| } | |
| func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error { | |
| cc := rl.cc | |
| cs := cc.streamByID(f.StreamID, f.StreamEnded()) | |
| data := f.Data() | |
| if cs == nil { | |
| cc.mu.Lock() | |
| neverSent := cc.nextStreamID | |
| cc.mu.Unlock() | |
| if f.StreamID >= neverSent { | |
| // We never asked for this. | |
| cc.logf("http2: Transport received unsolicited DATA frame; closing connection") | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| // We probably did ask for this, but canceled. Just ignore it. | |
| // TODO: be stricter here? only silently ignore things which | |
| // we canceled, but not things which were closed normally | |
| // by the peer? Tough without accumulating too much state. | |
| // But at least return their flow control: | |
| if f.Length > 0 { | |
| cc.mu.Lock() | |
| cc.inflow.add(int32(f.Length)) | |
| cc.mu.Unlock() | |
| cc.wmu.Lock() | |
| cc.fr.WriteWindowUpdate(0, uint32(f.Length)) | |
| cc.bw.Flush() | |
| cc.wmu.Unlock() | |
| } | |
| return nil | |
| } | |
| if !cs.firstByte { | |
| cc.logf("protocol error: received DATA before a HEADERS frame") | |
| rl.endStreamError(cs, http2StreamError{ | |
| StreamID: f.StreamID, | |
| Code: http2ErrCodeProtocol, | |
| }) | |
| return nil | |
| } | |
| if f.Length > 0 { | |
| if cs.req.Method == "HEAD" && len(data) > 0 { | |
| cc.logf("protocol error: received DATA on a HEAD request") | |
| rl.endStreamError(cs, http2StreamError{ | |
| StreamID: f.StreamID, | |
| Code: http2ErrCodeProtocol, | |
| }) | |
| return nil | |
| } | |
| // Check connection-level flow control. | |
| cc.mu.Lock() | |
| if cs.inflow.available() >= int32(f.Length) { | |
| cs.inflow.take(int32(f.Length)) | |
| } else { | |
| cc.mu.Unlock() | |
| return http2ConnectionError(http2ErrCodeFlowControl) | |
| } | |
| // Return any padded flow control now, since we won't | |
| // refund it later on body reads. | |
| var refund int | |
| if pad := int(f.Length) - len(data); pad > 0 { | |
| refund += pad | |
| } | |
| // Return len(data) now if the stream is already closed, | |
| // since data will never be read. | |
| didReset := cs.didReset | |
| if didReset { | |
| refund += len(data) | |
| } | |
| if refund > 0 { | |
| cc.inflow.add(int32(refund)) | |
| cc.wmu.Lock() | |
| cc.fr.WriteWindowUpdate(0, uint32(refund)) | |
| if !didReset { | |
| cs.inflow.add(int32(refund)) | |
| cc.fr.WriteWindowUpdate(cs.ID, uint32(refund)) | |
| } | |
| cc.bw.Flush() | |
| cc.wmu.Unlock() | |
| } | |
| cc.mu.Unlock() | |
| if len(data) > 0 && !didReset { | |
| if _, err := cs.bufPipe.Write(data); err != nil { | |
| rl.endStreamError(cs, err) | |
| return err | |
| } | |
| } | |
| } | |
| if f.StreamEnded() { | |
| rl.endStream(cs) | |
| } | |
| return nil | |
| } | |
| func (rl *http2clientConnReadLoop) endStream(cs *http2clientStream) { | |
| // TODO: check that any declared content-length matches, like | |
| // server.go's (*stream).endStream method. | |
| rl.endStreamError(cs, nil) | |
| } | |
| func (rl *http2clientConnReadLoop) endStreamError(cs *http2clientStream, err error) { | |
| var code func() | |
| if err == nil { | |
| err = io.EOF | |
| code = cs.copyTrailers | |
| } | |
| if http2isConnectionCloseRequest(cs.req) { | |
| rl.closeWhenIdle = true | |
| } | |
| cs.bufPipe.closeWithErrorAndCode(err, code) | |
| select { | |
| case cs.resc <- http2resAndError{err: err}: | |
| default: | |
| } | |
| } | |
| func (cs *http2clientStream) copyTrailers() { | |
| for k, vv := range cs.trailer { | |
| t := cs.resTrailer | |
| if *t == nil { | |
| *t = make(Header) | |
| } | |
| (*t)[k] = vv | |
| } | |
| } | |
| func (rl *http2clientConnReadLoop) processGoAway(f *http2GoAwayFrame) error { | |
| cc := rl.cc | |
| cc.t.connPool().MarkDead(cc) | |
| if f.ErrCode != 0 { | |
| // TODO: deal with GOAWAY more. particularly the error code | |
| cc.vlogf("transport got GOAWAY with error code = %v", f.ErrCode) | |
| } | |
| cc.setGoAway(f) | |
| return nil | |
| } | |
| func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error { | |
| cc := rl.cc | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| if f.IsAck() { | |
| if cc.wantSettingsAck { | |
| cc.wantSettingsAck = false | |
| return nil | |
| } | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| err := f.ForeachSetting(func(s http2Setting) error { | |
| switch s.ID { | |
| case http2SettingMaxFrameSize: | |
| cc.maxFrameSize = s.Val | |
| case http2SettingMaxConcurrentStreams: | |
| cc.maxConcurrentStreams = s.Val | |
| case http2SettingMaxHeaderListSize: | |
| cc.peerMaxHeaderListSize = uint64(s.Val) | |
| case http2SettingInitialWindowSize: | |
| // Values above the maximum flow-control | |
| // window size of 2^31-1 MUST be treated as a | |
| // connection error (Section 5.4.1) of type | |
| // FLOW_CONTROL_ERROR. | |
| if s.Val > math.MaxInt32 { | |
| return http2ConnectionError(http2ErrCodeFlowControl) | |
| } | |
| // Adjust flow control of currently-open | |
| // frames by the difference of the old initial | |
| // window size and this one. | |
| delta := int32(s.Val) - int32(cc.initialWindowSize) | |
| for _, cs := range cc.streams { | |
| cs.flow.add(delta) | |
| } | |
| cc.cond.Broadcast() | |
| cc.initialWindowSize = s.Val | |
| default: | |
| // TODO(bradfitz): handle more settings? SETTINGS_HEADER_TABLE_SIZE probably. | |
| cc.vlogf("Unhandled Setting: %v", s) | |
| } | |
| return nil | |
| }) | |
| if err != nil { | |
| return err | |
| } | |
| cc.wmu.Lock() | |
| defer cc.wmu.Unlock() | |
| cc.fr.WriteSettingsAck() | |
| cc.bw.Flush() | |
| return cc.werr | |
| } | |
| func (rl *http2clientConnReadLoop) processWindowUpdate(f *http2WindowUpdateFrame) error { | |
| cc := rl.cc | |
| cs := cc.streamByID(f.StreamID, false) | |
| if f.StreamID != 0 && cs == nil { | |
| return nil | |
| } | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| fl := &cc.flow | |
| if cs != nil { | |
| fl = &cs.flow | |
| } | |
| if !fl.add(int32(f.Increment)) { | |
| return http2ConnectionError(http2ErrCodeFlowControl) | |
| } | |
| cc.cond.Broadcast() | |
| return nil | |
| } | |
| func (rl *http2clientConnReadLoop) processResetStream(f *http2RSTStreamFrame) error { | |
| cs := rl.cc.streamByID(f.StreamID, true) | |
| if cs == nil { | |
| // TODO: return error if server tries to RST_STEAM an idle stream | |
| return nil | |
| } | |
| select { | |
| case <-cs.peerReset: | |
| // Already reset. | |
| // This is the only goroutine | |
| // which closes this, so there | |
| // isn't a race. | |
| default: | |
| err := http2streamError(cs.ID, f.ErrCode) | |
| cs.resetErr = err | |
| close(cs.peerReset) | |
| cs.bufPipe.CloseWithError(err) | |
| cs.cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl | |
| } | |
| return nil | |
| } | |
| // Ping sends a PING frame to the server and waits for the ack. | |
| func (cc *http2ClientConn) Ping(ctx context.Context) error { | |
| c := make(chan struct{}) | |
| // Generate a random payload | |
| var p [8]byte | |
| for { | |
| if _, err := rand.Read(p[:]); err != nil { | |
| return err | |
| } | |
| cc.mu.Lock() | |
| // check for dup before insert | |
| if _, found := cc.pings[p]; !found { | |
| cc.pings[p] = c | |
| cc.mu.Unlock() | |
| break | |
| } | |
| cc.mu.Unlock() | |
| } | |
| cc.wmu.Lock() | |
| if err := cc.fr.WritePing(false, p); err != nil { | |
| cc.wmu.Unlock() | |
| return err | |
| } | |
| if err := cc.bw.Flush(); err != nil { | |
| cc.wmu.Unlock() | |
| return err | |
| } | |
| cc.wmu.Unlock() | |
| select { | |
| case <-c: | |
| return nil | |
| case <-ctx.Done(): | |
| return ctx.Err() | |
| case <-cc.readerDone: | |
| // connection closed | |
| return cc.readerErr | |
| } | |
| } | |
| func (rl *http2clientConnReadLoop) processPing(f *http2PingFrame) error { | |
| if f.IsAck() { | |
| cc := rl.cc | |
| cc.mu.Lock() | |
| defer cc.mu.Unlock() | |
| // If ack, notify listener if any | |
| if c, ok := cc.pings[f.Data]; ok { | |
| close(c) | |
| delete(cc.pings, f.Data) | |
| } | |
| return nil | |
| } | |
| cc := rl.cc | |
| cc.wmu.Lock() | |
| defer cc.wmu.Unlock() | |
| if err := cc.fr.WritePing(true, f.Data); err != nil { | |
| return err | |
| } | |
| return cc.bw.Flush() | |
| } | |
| func (rl *http2clientConnReadLoop) processPushPromise(f *http2PushPromiseFrame) error { | |
| // We told the peer we don't want them. | |
| // Spec says: | |
| // "PUSH_PROMISE MUST NOT be sent if the SETTINGS_ENABLE_PUSH | |
| // setting of the peer endpoint is set to 0. An endpoint that | |
| // has set this setting and has received acknowledgement MUST | |
| // treat the receipt of a PUSH_PROMISE frame as a connection | |
| // error (Section 5.4.1) of type PROTOCOL_ERROR." | |
| return http2ConnectionError(http2ErrCodeProtocol) | |
| } | |
| func (cc *http2ClientConn) writeStreamReset(streamID uint32, code http2ErrCode, err error) { | |
| // TODO: map err to more interesting error codes, once the | |
| // HTTP community comes up with some. But currently for | |
| // RST_STREAM there's no equivalent to GOAWAY frame's debug | |
| // data, and the error codes are all pretty vague ("cancel"). | |
| cc.wmu.Lock() | |
| cc.fr.WriteRSTStream(streamID, code) | |
| cc.bw.Flush() | |
| cc.wmu.Unlock() | |
| } | |
| var ( | |
| http2errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit") | |
| http2errRequestHeaderListSize = errors.New("http2: request header list larger than peer's advertised limit") | |
| ) | |
| func (cc *http2ClientConn) logf(format string, args ...interface{}) { | |
| cc.t.logf(format, args...) | |
| } | |
| func (cc *http2ClientConn) vlogf(format string, args ...interface{}) { | |
| cc.t.vlogf(format, args...) | |
| } | |
| func (t *http2Transport) vlogf(format string, args ...interface{}) { | |
| if http2VerboseLogs { | |
| t.logf(format, args...) | |
| } | |
| } | |
| func (t *http2Transport) logf(format string, args ...interface{}) { | |
| log.Printf(format, args...) | |
| } | |
| var http2noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil)) | |
| func http2strSliceContains(ss []string, s string) bool { | |
| for _, v := range ss { | |
| if v == s { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| type http2erringRoundTripper struct{ err error } | |
| func (rt http2erringRoundTripper) RoundTripErr() error { return rt.err } | |
| func (rt http2erringRoundTripper) RoundTrip(*Request) (*Response, error) { return nil, rt.err } | |
| // gzipReader wraps a response body so it can lazily | |
| // call gzip.NewReader on the first call to Read | |
| type http2gzipReader struct { | |
| _ http2incomparable | |
| body io.ReadCloser // underlying Response.Body | |
| zr *gzip.Reader // lazily-initialized gzip reader | |
| zerr error // sticky error | |
| } | |
| func (gz *http2gzipReader) Read(p []byte) (n int, err error) { | |
| if gz.zerr != nil { | |
| return 0, gz.zerr | |
| } | |
| if gz.zr == nil { | |
| gz.zr, err = gzip.NewReader(gz.body) | |
| if err != nil { | |
| gz.zerr = err | |
| return 0, err | |
| } | |
| } | |
| return gz.zr.Read(p) | |
| } | |
| func (gz *http2gzipReader) Close() error { | |
| return gz.body.Close() | |
| } | |
| type http2errorReader struct{ err error } | |
| func (r http2errorReader) Read(p []byte) (int, error) { return 0, r.err } | |
| // bodyWriterState encapsulates various state around the Transport's writing | |
| // of the request body, particularly regarding doing delayed writes of the body | |
| // when the request contains "Expect: 100-continue". | |
| type http2bodyWriterState struct { | |
| cs *http2clientStream | |
| timer *time.Timer // if non-nil, we're doing a delayed write | |
| fnonce *sync.Once // to call fn with | |
| fn func() // the code to run in the goroutine, writing the body | |
| resc chan error // result of fn's execution | |
| delay time.Duration // how long we should delay a delayed write for | |
| } | |
| func (t *http2Transport) getBodyWriterState(cs *http2clientStream, body io.Reader) (s http2bodyWriterState) { | |
| s.cs = cs | |
| if body == nil { | |
| return | |
| } | |
| resc := make(chan error, 1) | |
| s.resc = resc | |
| s.fn = func() { | |
| cs.cc.mu.Lock() | |
| cs.startedWrite = true | |
| cs.cc.mu.Unlock() | |
| resc <- cs.writeRequestBody(body, cs.req.Body) | |
| } | |
| s.delay = t.expectContinueTimeout() | |
| if s.delay == 0 || | |
| !httpguts.HeaderValuesContainsToken( | |
| cs.req.Header["Expect"], | |
| "100-continue") { | |
| return | |
| } | |
| s.fnonce = new(sync.Once) | |
| // Arm the timer with a very large duration, which we'll | |
| // intentionally lower later. It has to be large now because | |
| // we need a handle to it before writing the headers, but the | |
| // s.delay value is defined to not start until after the | |
| // request headers were written. | |
| const hugeDuration = 365 * 24 * time.Hour | |
| s.timer = time.AfterFunc(hugeDuration, func() { | |
| s.fnonce.Do(s.fn) | |
| }) | |
| return | |
| } | |
| func (s http2bodyWriterState) cancel() { | |
| if s.timer != nil { | |
| if s.timer.Stop() { | |
| s.resc <- nil | |
| } | |
| } | |
| } | |
| func (s http2bodyWriterState) on100() { | |
| if s.timer == nil { | |
| // If we didn't do a delayed write, ignore the server's | |
| // bogus 100 continue response. | |
| return | |
| } | |
| s.timer.Stop() | |
| go func() { s.fnonce.Do(s.fn) }() | |
| } | |
| // scheduleBodyWrite starts writing the body, either immediately (in | |
| // the common case) or after the delay timeout. It should not be | |
| // called until after the headers have been written. | |
| func (s http2bodyWriterState) scheduleBodyWrite() { | |
| if s.timer == nil { | |
| // We're not doing a delayed write (see | |
| // getBodyWriterState), so just start the writing | |
| // goroutine immediately. | |
| go s.fn() | |
| return | |
| } | |
| http2traceWait100Continue(s.cs.trace) | |
| if s.timer.Stop() { | |
| s.timer.Reset(s.delay) | |
| } | |
| } | |
| // isConnectionCloseRequest reports whether req should use its own | |
| // connection for a single request and then close the connection. | |
| func http2isConnectionCloseRequest(req *Request) bool { | |
| return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close") | |
| } | |
| // registerHTTPSProtocol calls Transport.RegisterProtocol but | |
| // converting panics into errors. | |
| func http2registerHTTPSProtocol(t *Transport, rt http2noDialH2RoundTripper) (err error) { | |
| defer func() { | |
| if e := recover(); e != nil { | |
| err = fmt.Errorf("%v", e) | |
| } | |
| }() | |
| t.RegisterProtocol("https", rt) | |
| return nil | |
| } | |
| // noDialH2RoundTripper is a RoundTripper which only tries to complete the request | |
| // if there's already has a cached connection to the host. | |
| // (The field is exported so it can be accessed via reflect from net/http; tested | |
| // by TestNoDialH2RoundTripperType) | |
| type http2noDialH2RoundTripper struct{ *http2Transport } | |
| func (rt http2noDialH2RoundTripper) RoundTrip(req *Request) (*Response, error) { | |
| res, err := rt.http2Transport.RoundTrip(req) | |
| if http2isNoCachedConnError(err) { | |
| return nil, ErrSkipAltProtocol | |
| } | |
| return res, err | |
| } | |
| func (t *http2Transport) idleConnTimeout() time.Duration { | |
| if t.t1 != nil { | |
| return t.t1.IdleConnTimeout | |
| } | |
| return 0 | |
| } | |
| func http2traceGetConn(req *Request, hostPort string) { | |
| trace := httptrace.ContextClientTrace(req.Context()) | |
| if trace == nil || trace.GetConn == nil { | |
| return | |
| } | |
| trace.GetConn(hostPort) | |
| } | |
| func http2traceGotConn(req *Request, cc *http2ClientConn, reused bool) { | |
| trace := httptrace.ContextClientTrace(req.Context()) | |
| if trace == nil || trace.GotConn == nil { | |
| return | |
| } | |
| ci := httptrace.GotConnInfo{Conn: cc.tconn} | |
| ci.Reused = reused | |
| cc.mu.Lock() | |
| ci.WasIdle = len(cc.streams) == 0 && reused | |
| if ci.WasIdle && !cc.lastActive.IsZero() { | |
| ci.IdleTime = time.Now().Sub(cc.lastActive) | |
| } | |
| cc.mu.Unlock() | |
| trace.GotConn(ci) | |
| } | |
| func http2traceWroteHeaders(trace *httptrace.ClientTrace) { | |
| if trace != nil && trace.WroteHeaders != nil { | |
| trace.WroteHeaders() | |
| } | |
| } | |
| func http2traceGot100Continue(trace *httptrace.ClientTrace) { | |
| if trace != nil && trace.Got100Continue != nil { | |
| trace.Got100Continue() | |
| } | |
| } | |
| func http2traceWait100Continue(trace *httptrace.ClientTrace) { | |
| if trace != nil && trace.Wait100Continue != nil { | |
| trace.Wait100Continue() | |
| } | |
| } | |
| func http2traceWroteRequest(trace *httptrace.ClientTrace, err error) { | |
| if trace != nil && trace.WroteRequest != nil { | |
| trace.WroteRequest(httptrace.WroteRequestInfo{Err: err}) | |
| } | |
| } | |
| func http2traceFirstResponseByte(trace *httptrace.ClientTrace) { | |
| if trace != nil && trace.GotFirstResponseByte != nil { | |
| trace.GotFirstResponseByte() | |
| } | |
| } | |
| // writeFramer is implemented by any type that is used to write frames. | |
| type http2writeFramer interface { | |
| writeFrame(http2writeContext) error | |
| // staysWithinBuffer reports whether this writer promises that | |
| // it will only write less than or equal to size bytes, and it | |
| // won't Flush the write context. | |
| staysWithinBuffer(size int) bool | |
| } | |
| // writeContext is the interface needed by the various frame writer | |
| // types below. All the writeFrame methods below are scheduled via the | |
| // frame writing scheduler (see writeScheduler in writesched.go). | |
| // | |
| // This interface is implemented by *serverConn. | |
| // | |
| // TODO: decide whether to a) use this in the client code (which didn't | |
| // end up using this yet, because it has a simpler design, not | |
| // currently implementing priorities), or b) delete this and | |
| // make the server code a bit more concrete. | |
| type http2writeContext interface { | |
| Framer() *http2Framer | |
| Flush() error | |
| CloseConn() error | |
| // HeaderEncoder returns an HPACK encoder that writes to the | |
| // returned buffer. | |
| HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) | |
| } | |
| // writeEndsStream reports whether w writes a frame that will transition | |
| // the stream to a half-closed local state. This returns false for RST_STREAM, | |
| // which closes the entire stream (not just the local half). | |
| func http2writeEndsStream(w http2writeFramer) bool { | |
| switch v := w.(type) { | |
| case *http2writeData: | |
| return v.endStream | |
| case *http2writeResHeaders: | |
| return v.endStream | |
| case nil: | |
| // This can only happen if the caller reuses w after it's | |
| // been intentionally nil'ed out to prevent use. Keep this | |
| // here to catch future refactoring breaking it. | |
| panic("writeEndsStream called on nil writeFramer") | |
| } | |
| return false | |
| } | |
| type http2flushFrameWriter struct{} | |
| func (http2flushFrameWriter) writeFrame(ctx http2writeContext) error { | |
| return ctx.Flush() | |
| } | |
| func (http2flushFrameWriter) staysWithinBuffer(max int) bool { return false } | |
| type http2writeSettings []http2Setting | |
| func (s http2writeSettings) staysWithinBuffer(max int) bool { | |
| const settingSize = 6 // uint16 + uint32 | |
| return http2frameHeaderLen+settingSize*len(s) <= max | |
| } | |
| func (s http2writeSettings) writeFrame(ctx http2writeContext) error { | |
| return ctx.Framer().WriteSettings([]http2Setting(s)...) | |
| } | |
| type http2writeGoAway struct { | |
| maxStreamID uint32 | |
| code http2ErrCode | |
| } | |
| func (p *http2writeGoAway) writeFrame(ctx http2writeContext) error { | |
| err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil) | |
| ctx.Flush() // ignore error: we're hanging up on them anyway | |
| return err | |
| } | |
| func (*http2writeGoAway) staysWithinBuffer(max int) bool { return false } // flushes | |
| type http2writeData struct { | |
| streamID uint32 | |
| p []byte | |
| endStream bool | |
| } | |
| func (w *http2writeData) String() string { | |
| return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream) | |
| } | |
| func (w *http2writeData) writeFrame(ctx http2writeContext) error { | |
| return ctx.Framer().WriteData(w.streamID, w.endStream, w.p) | |
| } | |
| func (w *http2writeData) staysWithinBuffer(max int) bool { | |
| return http2frameHeaderLen+len(w.p) <= max | |
| } | |
| // handlerPanicRST is the message sent from handler goroutines when | |
| // the handler panics. | |
| type http2handlerPanicRST struct { | |
| StreamID uint32 | |
| } | |
| func (hp http2handlerPanicRST) writeFrame(ctx http2writeContext) error { | |
| return ctx.Framer().WriteRSTStream(hp.StreamID, http2ErrCodeInternal) | |
| } | |
| func (hp http2handlerPanicRST) staysWithinBuffer(max int) bool { return http2frameHeaderLen+4 <= max } | |
| func (se http2StreamError) writeFrame(ctx http2writeContext) error { | |
| return ctx.Framer().WriteRSTStream(se.StreamID, se.Code) | |
| } | |
| func (se http2StreamError) staysWithinBuffer(max int) bool { return http2frameHeaderLen+4 <= max } | |
| type http2writePingAck struct{ pf *http2PingFrame } | |
| func (w http2writePingAck) writeFrame(ctx http2writeContext) error { | |
| return ctx.Framer().WritePing(true, w.pf.Data) | |
| } | |
| func (w http2writePingAck) staysWithinBuffer(max int) bool { | |
| return http2frameHeaderLen+len(w.pf.Data) <= max | |
| } | |
| type http2writeSettingsAck struct{} | |
| func (http2writeSettingsAck) writeFrame(ctx http2writeContext) error { | |
| return ctx.Framer().WriteSettingsAck() | |
| } | |
| func (http2writeSettingsAck) staysWithinBuffer(max int) bool { return http2frameHeaderLen <= max } | |
| // splitHeaderBlock splits headerBlock into fragments so that each fragment fits | |
| // in a single frame, then calls fn for each fragment. firstFrag/lastFrag are true | |
| // for the first/last fragment, respectively. | |
| func http2splitHeaderBlock(ctx http2writeContext, headerBlock []byte, fn func(ctx http2writeContext, frag []byte, firstFrag, lastFrag bool) error) error { | |
| // For now we're lazy and just pick the minimum MAX_FRAME_SIZE | |
| // that all peers must support (16KB). Later we could care | |
| // more and send larger frames if the peer advertised it, but | |
| // there's little point. Most headers are small anyway (so we | |
| // generally won't have CONTINUATION frames), and extra frames | |
| // only waste 9 bytes anyway. | |
| const maxFrameSize = 16384 | |
| first := true | |
| for len(headerBlock) > 0 { | |
| frag := headerBlock | |
| if len(frag) > maxFrameSize { | |
| frag = frag[:maxFrameSize] | |
| } | |
| headerBlock = headerBlock[len(frag):] | |
| if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil { | |
| return err | |
| } | |
| first = false | |
| } | |
| return nil | |
| } | |
| // writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames | |
| // for HTTP response headers or trailers from a server handler. | |
| type http2writeResHeaders struct { | |
| streamID uint32 | |
| httpResCode int // 0 means no ":status" line | |
| h Header // may be nil | |
| trailers []string // if non-nil, which keys of h to write. nil means all. | |
| endStream bool | |
| date string | |
| contentType string | |
| contentLength string | |
| } | |
| func http2encKV(enc *hpack.Encoder, k, v string) { | |
| if http2VerboseLogs { | |
| log.Printf("http2: server encoding header %q = %q", k, v) | |
| } | |
| enc.WriteField(hpack.HeaderField{Name: k, Value: v}) | |
| } | |
| func (w *http2writeResHeaders) staysWithinBuffer(max int) bool { | |
| // TODO: this is a common one. It'd be nice to return true | |
| // here and get into the fast path if we could be clever and | |
| // calculate the size fast enough, or at least a conservative | |
| // upper bound that usually fires. (Maybe if w.h and | |
| // w.trailers are nil, so we don't need to enumerate it.) | |
| // Otherwise I'm afraid that just calculating the length to | |
| // answer this question would be slower than the ~2µs benefit. | |
| return false | |
| } | |
| func (w *http2writeResHeaders) writeFrame(ctx http2writeContext) error { | |
| enc, buf := ctx.HeaderEncoder() | |
| buf.Reset() | |
| if w.httpResCode != 0 { | |
| http2encKV(enc, ":status", http2httpCodeString(w.httpResCode)) | |
| } | |
| http2encodeHeaders(enc, w.h, w.trailers) | |
| if w.contentType != "" { | |
| http2encKV(enc, "content-type", w.contentType) | |
| } | |
| if w.contentLength != "" { | |
| http2encKV(enc, "content-length", w.contentLength) | |
| } | |
| if w.date != "" { | |
| http2encKV(enc, "date", w.date) | |
| } | |
| headerBlock := buf.Bytes() | |
| if len(headerBlock) == 0 && w.trailers == nil { | |
| panic("unexpected empty hpack") | |
| } | |
| return http2splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock) | |
| } | |
| func (w *http2writeResHeaders) writeHeaderBlock(ctx http2writeContext, frag []byte, firstFrag, lastFrag bool) error { | |
| if firstFrag { | |
| return ctx.Framer().WriteHeaders(http2HeadersFrameParam{ | |
| StreamID: w.streamID, | |
| BlockFragment: frag, | |
| EndStream: w.endStream, | |
| EndHeaders: lastFrag, | |
| }) | |
| } else { | |
| return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag) | |
| } | |
| } | |
| // writePushPromise is a request to write a PUSH_PROMISE and 0+ CONTINUATION frames. | |
| type http2writePushPromise struct { | |
| streamID uint32 // pusher stream | |
| method string // for :method | |
| url *url.URL // for :scheme, :authority, :path | |
| h Header | |
| // Creates an ID for a pushed stream. This runs on serveG just before | |
| // the frame is written. The returned ID is copied to promisedID. | |
| allocatePromisedID func() (uint32, error) | |
| promisedID uint32 | |
| } | |
| func (w *http2writePushPromise) staysWithinBuffer(max int) bool { | |
| // TODO: see writeResHeaders.staysWithinBuffer | |
| return false | |
| } | |
| func (w *http2writePushPromise) writeFrame(ctx http2writeContext) error { | |
| enc, buf := ctx.HeaderEncoder() | |
| buf.Reset() | |
| http2encKV(enc, ":method", w.method) | |
| http2encKV(enc, ":scheme", w.url.Scheme) | |
| http2encKV(enc, ":authority", w.url.Host) | |
| http2encKV(enc, ":path", w.url.RequestURI()) | |
| http2encodeHeaders(enc, w.h, nil) | |
| headerBlock := buf.Bytes() | |
| if len(headerBlock) == 0 { | |
| panic("unexpected empty hpack") | |
| } | |
| return http2splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock) | |
| } | |
| func (w *http2writePushPromise) writeHeaderBlock(ctx http2writeContext, frag []byte, firstFrag, lastFrag bool) error { | |
| if firstFrag { | |
| return ctx.Framer().WritePushPromise(http2PushPromiseParam{ | |
| StreamID: w.streamID, | |
| PromiseID: w.promisedID, | |
| BlockFragment: frag, | |
| EndHeaders: lastFrag, | |
| }) | |
| } else { | |
| return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag) | |
| } | |
| } | |
| type http2write100ContinueHeadersFrame struct { | |
| streamID uint32 | |
| } | |
| func (w http2write100ContinueHeadersFrame) writeFrame(ctx http2writeContext) error { | |
| enc, buf := ctx.HeaderEncoder() | |
| buf.Reset() | |
| http2encKV(enc, ":status", "100") | |
| return ctx.Framer().WriteHeaders(http2HeadersFrameParam{ | |
| StreamID: w.streamID, | |
| BlockFragment: buf.Bytes(), | |
| EndStream: false, | |
| EndHeaders: true, | |
| }) | |
| } | |
| func (w http2write100ContinueHeadersFrame) staysWithinBuffer(max int) bool { | |
| // Sloppy but conservative: | |
| return 9+2*(len(":status")+len("100")) <= max | |
| } | |
| type http2writeWindowUpdate struct { | |
| streamID uint32 // or 0 for conn-level | |
| n uint32 | |
| } | |
| func (wu http2writeWindowUpdate) staysWithinBuffer(max int) bool { return http2frameHeaderLen+4 <= max } | |
| func (wu http2writeWindowUpdate) writeFrame(ctx http2writeContext) error { | |
| return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n) | |
| } | |
| // encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k]) | |
| // is encoded only if k is in keys. | |
| func http2encodeHeaders(enc *hpack.Encoder, h Header, keys []string) { | |
| if keys == nil { | |
| sorter := http2sorterPool.Get().(*http2sorter) | |
| // Using defer here, since the returned keys from the | |
| // sorter.Keys method is only valid until the sorter | |
| // is returned: | |
| defer http2sorterPool.Put(sorter) | |
| keys = sorter.Keys(h) | |
| } | |
| for _, k := range keys { | |
| vv := h[k] | |
| k = http2lowerHeader(k) | |
| if !http2validWireHeaderFieldName(k) { | |
| // Skip it as backup paranoia. Per | |
| // golang.org/issue/14048, these should | |
| // already be rejected at a higher level. | |
| continue | |
| } | |
| isTE := k == "transfer-encoding" | |
| for _, v := range vv { | |
| if !httpguts.ValidHeaderFieldValue(v) { | |
| // TODO: return an error? golang.org/issue/14048 | |
| // For now just omit it. | |
| continue | |
| } | |
| // TODO: more of "8.1.2.2 Connection-Specific Header Fields" | |
| if isTE && v != "trailers" { | |
| continue | |
| } | |
| http2encKV(enc, k, v) | |
| } | |
| } | |
| } | |
| // WriteScheduler is the interface implemented by HTTP/2 write schedulers. | |
| // Methods are never called concurrently. | |
| type http2WriteScheduler interface { | |
| // OpenStream opens a new stream in the write scheduler. | |
| // It is illegal to call this with streamID=0 or with a streamID that is | |
| // already open -- the call may panic. | |
| OpenStream(streamID uint32, options http2OpenStreamOptions) | |
| // CloseStream closes a stream in the write scheduler. Any frames queued on | |
| // this stream should be discarded. It is illegal to call this on a stream | |
| // that is not open -- the call may panic. | |
| CloseStream(streamID uint32) | |
| // AdjustStream adjusts the priority of the given stream. This may be called | |
| // on a stream that has not yet been opened or has been closed. Note that | |
| // RFC 7540 allows PRIORITY frames to be sent on streams in any state. See: | |
| // https://tools.ietf.org/html/rfc7540#section-5.1 | |
| AdjustStream(streamID uint32, priority http2PriorityParam) | |
| // Push queues a frame in the scheduler. In most cases, this will not be | |
| // called with wr.StreamID()!=0 unless that stream is currently open. The one | |
| // exception is RST_STREAM frames, which may be sent on idle or closed streams. | |
| Push(wr http2FrameWriteRequest) | |
| // Pop dequeues the next frame to write. Returns false if no frames can | |
| // be written. Frames with a given wr.StreamID() are Pop'd in the same | |
| // order they are Push'd. No frames should be discarded except by CloseStream. | |
| Pop() (wr http2FrameWriteRequest, ok bool) | |
| } | |
| // OpenStreamOptions specifies extra options for WriteScheduler.OpenStream. | |
| type http2OpenStreamOptions struct { | |
| // PusherID is zero if the stream was initiated by the client. Otherwise, | |
| // PusherID names the stream that pushed the newly opened stream. | |
| PusherID uint32 | |
| } | |
| // FrameWriteRequest is a request to write a frame. | |
| type http2FrameWriteRequest struct { | |
| // write is the interface value that does the writing, once the | |
| // WriteScheduler has selected this frame to write. The write | |
| // functions are all defined in write.go. | |
| write http2writeFramer | |
| // stream is the stream on which this frame will be written. | |
| // nil for non-stream frames like PING and SETTINGS. | |
| stream *http2stream | |
| // done, if non-nil, must be a buffered channel with space for | |
| // 1 message and is sent the return value from write (or an | |
| // earlier error) when the frame has been written. | |
| done chan error | |
| } | |
| // StreamID returns the id of the stream this frame will be written to. | |
| // 0 is used for non-stream frames such as PING and SETTINGS. | |
| func (wr http2FrameWriteRequest) StreamID() uint32 { | |
| if wr.stream == nil { | |
| if se, ok := wr.write.(http2StreamError); ok { | |
| // (*serverConn).resetStream doesn't set | |
| // stream because it doesn't necessarily have | |
| // one. So special case this type of write | |
| // message. | |
| return se.StreamID | |
| } | |
| return 0 | |
| } | |
| return wr.stream.id | |
| } | |
| // isControl reports whether wr is a control frame for MaxQueuedControlFrames | |
| // purposes. That includes non-stream frames and RST_STREAM frames. | |
| func (wr http2FrameWriteRequest) isControl() bool { | |
| return wr.stream == nil | |
| } | |
| // DataSize returns the number of flow control bytes that must be consumed | |
| // to write this entire frame. This is 0 for non-DATA frames. | |
| func (wr http2FrameWriteRequest) DataSize() int { | |
| if wd, ok := wr.write.(*http2writeData); ok { | |
| return len(wd.p) | |
| } | |
| return 0 | |
| } | |
| // Consume consumes min(n, available) bytes from this frame, where available | |
| // is the number of flow control bytes available on the stream. Consume returns | |
| // 0, 1, or 2 frames, where the integer return value gives the number of frames | |
| // returned. | |
| // | |
| // If flow control prevents consuming any bytes, this returns (_, _, 0). If | |
| // the entire frame was consumed, this returns (wr, _, 1). Otherwise, this | |
| // returns (consumed, rest, 2), where 'consumed' contains the consumed bytes and | |
| // 'rest' contains the remaining bytes. The consumed bytes are deducted from the | |
| // underlying stream's flow control budget. | |
| func (wr http2FrameWriteRequest) Consume(n int32) (http2FrameWriteRequest, http2FrameWriteRequest, int) { | |
| var empty http2FrameWriteRequest | |
| // Non-DATA frames are always consumed whole. | |
| wd, ok := wr.write.(*http2writeData) | |
| if !ok || len(wd.p) == 0 { | |
| return wr, empty, 1 | |
| } | |
| // Might need to split after applying limits. | |
| allowed := wr.stream.flow.available() | |
| if n < allowed { | |
| allowed = n | |
| } | |
| if wr.stream.sc.maxFrameSize < allowed { | |
| allowed = wr.stream.sc.maxFrameSize | |
| } | |
| if allowed <= 0 { | |
| return empty, empty, 0 | |
| } | |
| if len(wd.p) > int(allowed) { | |
| wr.stream.flow.take(allowed) | |
| consumed := http2FrameWriteRequest{ | |
| stream: wr.stream, | |
| write: &http2writeData{ | |
| streamID: wd.streamID, | |
| p: wd.p[:allowed], | |
| // Even if the original had endStream set, there | |
| // are bytes remaining because len(wd.p) > allowed, | |
| // so we know endStream is false. | |
| endStream: false, | |
| }, | |
| // Our caller is blocking on the final DATA frame, not | |
| // this intermediate frame, so no need to wait. | |
| done: nil, | |
| } | |
| rest := http2FrameWriteRequest{ | |
| stream: wr.stream, | |
| write: &http2writeData{ | |
| streamID: wd.streamID, | |
| p: wd.p[allowed:], | |
| endStream: wd.endStream, | |
| }, | |
| done: wr.done, | |
| } | |
| return consumed, rest, 2 | |
| } | |
| // The frame is consumed whole. | |
| // NB: This cast cannot overflow because allowed is <= math.MaxInt32. | |
| wr.stream.flow.take(int32(len(wd.p))) | |
| return wr, empty, 1 | |
| } | |
| // String is for debugging only. | |
| func (wr http2FrameWriteRequest) String() string { | |
| var des string | |
| if s, ok := wr.write.(fmt.Stringer); ok { | |
| des = s.String() | |
| } else { | |
| des = fmt.Sprintf("%T", wr.write) | |
| } | |
| return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des) | |
| } | |
| // replyToWriter sends err to wr.done and panics if the send must block | |
| // This does nothing if wr.done is nil. | |
| func (wr *http2FrameWriteRequest) replyToWriter(err error) { | |
| if wr.done == nil { | |
| return | |
| } | |
| select { | |
| case wr.done <- err: | |
| default: | |
| panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write)) | |
| } | |
| wr.write = nil // prevent use (assume it's tainted after wr.done send) | |
| } | |
| // writeQueue is used by implementations of WriteScheduler. | |
| type http2writeQueue struct { | |
| s []http2FrameWriteRequest | |
| } | |
| func (q *http2writeQueue) empty() bool { return len(q.s) == 0 } | |
| func (q *http2writeQueue) push(wr http2FrameWriteRequest) { | |
| q.s = append(q.s, wr) | |
| } | |
| func (q *http2writeQueue) shift() http2FrameWriteRequest { | |
| if len(q.s) == 0 { | |
| panic("invalid use of queue") | |
| } | |
| wr := q.s[0] | |
| // TODO: less copy-happy queue. | |
| copy(q.s, q.s[1:]) | |
| q.s[len(q.s)-1] = http2FrameWriteRequest{} | |
| q.s = q.s[:len(q.s)-1] | |
| return wr | |
| } | |
| // consume consumes up to n bytes from q.s[0]. If the frame is | |
| // entirely consumed, it is removed from the queue. If the frame | |
| // is partially consumed, the frame is kept with the consumed | |
| // bytes removed. Returns true iff any bytes were consumed. | |
| func (q *http2writeQueue) consume(n int32) (http2FrameWriteRequest, bool) { | |
| if len(q.s) == 0 { | |
| return http2FrameWriteRequest{}, false | |
| } | |
| consumed, rest, numresult := q.s[0].Consume(n) | |
| switch numresult { | |
| case 0: | |
| return http2FrameWriteRequest{}, false | |
| case 1: | |
| q.shift() | |
| case 2: | |
| q.s[0] = rest | |
| } | |
| return consumed, true | |
| } | |
| type http2writeQueuePool []*http2writeQueue | |
| // put inserts an unused writeQueue into the pool. | |
| // put inserts an unused writeQueue into the pool. | |
| func (p *http2writeQueuePool) put(q *http2writeQueue) { | |
| for i := range q.s { | |
| q.s[i] = http2FrameWriteRequest{} | |
| } | |
| q.s = q.s[:0] | |
| *p = append(*p, q) | |
| } | |
| // get returns an empty writeQueue. | |
| func (p *http2writeQueuePool) get() *http2writeQueue { | |
| ln := len(*p) | |
| if ln == 0 { | |
| return new(http2writeQueue) | |
| } | |
| x := ln - 1 | |
| q := (*p)[x] | |
| (*p)[x] = nil | |
| *p = (*p)[:x] | |
| return q | |
| } | |
| // RFC 7540, Section 5.3.5: the default weight is 16. | |
| const http2priorityDefaultWeight = 15 // 16 = 15 + 1 | |
| // PriorityWriteSchedulerConfig configures a priorityWriteScheduler. | |
| type http2PriorityWriteSchedulerConfig struct { | |
| // MaxClosedNodesInTree controls the maximum number of closed streams to | |
| // retain in the priority tree. Setting this to zero saves a small amount | |
| // of memory at the cost of performance. | |
| // | |
| // See RFC 7540, Section 5.3.4: | |
| // "It is possible for a stream to become closed while prioritization | |
| // information ... is in transit. ... This potentially creates suboptimal | |
| // prioritization, since the stream could be given a priority that is | |
| // different from what is intended. To avoid these problems, an endpoint | |
| // SHOULD retain stream prioritization state for a period after streams | |
| // become closed. The longer state is retained, the lower the chance that | |
| // streams are assigned incorrect or default priority values." | |
| MaxClosedNodesInTree int | |
| // MaxIdleNodesInTree controls the maximum number of idle streams to | |
| // retain in the priority tree. Setting this to zero saves a small amount | |
| // of memory at the cost of performance. | |
| // | |
| // See RFC 7540, Section 5.3.4: | |
| // Similarly, streams that are in the "idle" state can be assigned | |
| // priority or become a parent of other streams. This allows for the | |
| // creation of a grouping node in the dependency tree, which enables | |
| // more flexible expressions of priority. Idle streams begin with a | |
| // default priority (Section 5.3.5). | |
| MaxIdleNodesInTree int | |
| // ThrottleOutOfOrderWrites enables write throttling to help ensure that | |
| // data is delivered in priority order. This works around a race where | |
| // stream B depends on stream A and both streams are about to call Write | |
| // to queue DATA frames. If B wins the race, a naive scheduler would eagerly | |
| // write as much data from B as possible, but this is suboptimal because A | |
| // is a higher-priority stream. With throttling enabled, we write a small | |
| // amount of data from B to minimize the amount of bandwidth that B can | |
| // steal from A. | |
| ThrottleOutOfOrderWrites bool | |
| } | |
| // NewPriorityWriteScheduler constructs a WriteScheduler that schedules | |
| // frames by following HTTP/2 priorities as described in RFC 7540 Section 5.3. | |
| // If cfg is nil, default options are used. | |
| func http2NewPriorityWriteScheduler(cfg *http2PriorityWriteSchedulerConfig) http2WriteScheduler { | |
| if cfg == nil { | |
| // For justification of these defaults, see: | |
| // https://docs.google.com/document/d/1oLhNg1skaWD4_DtaoCxdSRN5erEXrH-KnLrMwEpOtFY | |
| cfg = &http2PriorityWriteSchedulerConfig{ | |
| MaxClosedNodesInTree: 10, | |
| MaxIdleNodesInTree: 10, | |
| ThrottleOutOfOrderWrites: false, | |
| } | |
| } | |
| ws := &http2priorityWriteScheduler{ | |
| nodes: make(map[uint32]*http2priorityNode), | |
| maxClosedNodesInTree: cfg.MaxClosedNodesInTree, | |
| maxIdleNodesInTree: cfg.MaxIdleNodesInTree, | |
| enableWriteThrottle: cfg.ThrottleOutOfOrderWrites, | |
| } | |
| ws.nodes[0] = &ws.root | |
| if cfg.ThrottleOutOfOrderWrites { | |
| ws.writeThrottleLimit = 1024 | |
| } else { | |
| ws.writeThrottleLimit = math.MaxInt32 | |
| } | |
| return ws | |
| } | |
| type http2priorityNodeState int | |
| const ( | |
| http2priorityNodeOpen http2priorityNodeState = iota | |
| http2priorityNodeClosed | |
| http2priorityNodeIdle | |
| ) | |
| // priorityNode is a node in an HTTP/2 priority tree. | |
| // Each node is associated with a single stream ID. | |
| // See RFC 7540, Section 5.3. | |
| type http2priorityNode struct { | |
| q http2writeQueue // queue of pending frames to write | |
| id uint32 // id of the stream, or 0 for the root of the tree | |
| weight uint8 // the actual weight is weight+1, so the value is in [1,256] | |
| state http2priorityNodeState // open | closed | idle | |
| bytes int64 // number of bytes written by this node, or 0 if closed | |
| subtreeBytes int64 // sum(node.bytes) of all nodes in this subtree | |
| // These links form the priority tree. | |
| parent *http2priorityNode | |
| kids *http2priorityNode // start of the kids list | |
| prev, next *http2priorityNode // doubly-linked list of siblings | |
| } | |
| func (n *http2priorityNode) setParent(parent *http2priorityNode) { | |
| if n == parent { | |
| panic("setParent to self") | |
| } | |
| if n.parent == parent { | |
| return | |
| } | |
| // Unlink from current parent. | |
| if parent := n.parent; parent != nil { | |
| if n.prev == nil { | |
| parent.kids = n.next | |
| } else { | |
| n.prev.next = n.next | |
| } | |
| if n.next != nil { | |
| n.next.prev = n.prev | |
| } | |
| } | |
| // Link to new parent. | |
| // If parent=nil, remove n from the tree. | |
| // Always insert at the head of parent.kids (this is assumed by walkReadyInOrder). | |
| n.parent = parent | |
| if parent == nil { | |
| n.next = nil | |
| n.prev = nil | |
| } else { | |
| n.next = parent.kids | |
| n.prev = nil | |
| if n.next != nil { | |
| n.next.prev = n | |
| } | |
| parent.kids = n | |
| } | |
| } | |
| func (n *http2priorityNode) addBytes(b int64) { | |
| n.bytes += b | |
| for ; n != nil; n = n.parent { | |
| n.subtreeBytes += b | |
| } | |
| } | |
| // walkReadyInOrder iterates over the tree in priority order, calling f for each node | |
| // with a non-empty write queue. When f returns true, this function returns true and the | |
| // walk halts. tmp is used as scratch space for sorting. | |
| // | |
| // f(n, openParent) takes two arguments: the node to visit, n, and a bool that is true | |
| // if any ancestor p of n is still open (ignoring the root node). | |
| func (n *http2priorityNode) walkReadyInOrder(openParent bool, tmp *[]*http2priorityNode, f func(*http2priorityNode, bool) bool) bool { | |
| if !n.q.empty() && f(n, openParent) { | |
| return true | |
| } | |
| if n.kids == nil { | |
| return false | |
| } | |
| // Don't consider the root "open" when updating openParent since | |
| // we can't send data frames on the root stream (only control frames). | |
| if n.id != 0 { | |
| openParent = openParent || (n.state == http2priorityNodeOpen) | |
| } | |
| // Common case: only one kid or all kids have the same weight. | |
| // Some clients don't use weights; other clients (like web browsers) | |
| // use mostly-linear priority trees. | |
| w := n.kids.weight | |
| needSort := false | |
| for k := n.kids.next; k != nil; k = k.next { | |
| if k.weight != w { | |
| needSort = true | |
| break | |
| } | |
| } | |
| if !needSort { | |
| for k := n.kids; k != nil; k = k.next { | |
| if k.walkReadyInOrder(openParent, tmp, f) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // Uncommon case: sort the child nodes. We remove the kids from the parent, | |
| // then re-insert after sorting so we can reuse tmp for future sort calls. | |
| *tmp = (*tmp)[:0] | |
| for n.kids != nil { | |
| *tmp = append(*tmp, n.kids) | |
| n.kids.setParent(nil) | |
| } | |
| sort.Sort(http2sortPriorityNodeSiblings(*tmp)) | |
| for i := len(*tmp) - 1; i >= 0; i-- { | |
| (*tmp)[i].setParent(n) // setParent inserts at the head of n.kids | |
| } | |
| for k := n.kids; k != nil; k = k.next { | |
| if k.walkReadyInOrder(openParent, tmp, f) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| type http2sortPriorityNodeSiblings []*http2priorityNode | |
| func (z http2sortPriorityNodeSiblings) Len() int { return len(z) } | |
| func (z http2sortPriorityNodeSiblings) Swap(i, k int) { z[i], z[k] = z[k], z[i] } | |
| func (z http2sortPriorityNodeSiblings) Less(i, k int) bool { | |
| // Prefer the subtree that has sent fewer bytes relative to its weight. | |
| // See sections 5.3.2 and 5.3.4. | |
| wi, bi := float64(z[i].weight+1), float64(z[i].subtreeBytes) | |
| wk, bk := float64(z[k].weight+1), float64(z[k].subtreeBytes) | |
| if bi == 0 && bk == 0 { | |
| return wi >= wk | |
| } | |
| if bk == 0 { | |
| return false | |
| } | |
| return bi/bk <= wi/wk | |
| } | |
| type http2priorityWriteScheduler struct { | |
| // root is the root of the priority tree, where root.id = 0. | |
| // The root queues control frames that are not associated with any stream. | |
| root http2priorityNode | |
| // nodes maps stream ids to priority tree nodes. | |
| nodes map[uint32]*http2priorityNode | |
| // maxID is the maximum stream id in nodes. | |
| maxID uint32 | |
| // lists of nodes that have been closed or are idle, but are kept in | |
| // the tree for improved prioritization. When the lengths exceed either | |
| // maxClosedNodesInTree or maxIdleNodesInTree, old nodes are discarded. | |
| closedNodes, idleNodes []*http2priorityNode | |
| // From the config. | |
| maxClosedNodesInTree int | |
| maxIdleNodesInTree int | |
| writeThrottleLimit int32 | |
| enableWriteThrottle bool | |
| // tmp is scratch space for priorityNode.walkReadyInOrder to reduce allocations. | |
| tmp []*http2priorityNode | |
| // pool of empty queues for reuse. | |
| queuePool http2writeQueuePool | |
| } | |
| func (ws *http2priorityWriteScheduler) OpenStream(streamID uint32, options http2OpenStreamOptions) { | |
| // The stream may be currently idle but cannot be opened or closed. | |
| if curr := ws.nodes[streamID]; curr != nil { | |
| if curr.state != http2priorityNodeIdle { | |
| panic(fmt.Sprintf("stream %d already opened", streamID)) | |
| } | |
| curr.state = http2priorityNodeOpen | |
| return | |
| } | |
| // RFC 7540, Section 5.3.5: | |
| // "All streams are initially assigned a non-exclusive dependency on stream 0x0. | |
| // Pushed streams initially depend on their associated stream. In both cases, | |
| // streams are assigned a default weight of 16." | |
| parent := ws.nodes[options.PusherID] | |
| if parent == nil { | |
| parent = &ws.root | |
| } | |
| n := &http2priorityNode{ | |
| q: *ws.queuePool.get(), | |
| id: streamID, | |
| weight: http2priorityDefaultWeight, | |
| state: http2priorityNodeOpen, | |
| } | |
| n.setParent(parent) | |
| ws.nodes[streamID] = n | |
| if streamID > ws.maxID { | |
| ws.maxID = streamID | |
| } | |
| } | |
| func (ws *http2priorityWriteScheduler) CloseStream(streamID uint32) { | |
| if streamID == 0 { | |
| panic("violation of WriteScheduler interface: cannot close stream 0") | |
| } | |
| if ws.nodes[streamID] == nil { | |
| panic(fmt.Sprintf("violation of WriteScheduler interface: unknown stream %d", streamID)) | |
| } | |
| if ws.nodes[streamID].state != http2priorityNodeOpen { | |
| panic(fmt.Sprintf("violation of WriteScheduler interface: stream %d already closed", streamID)) | |
| } | |
| n := ws.nodes[streamID] | |
| n.state = http2priorityNodeClosed | |
| n.addBytes(-n.bytes) | |
| q := n.q | |
| ws.queuePool.put(&q) | |
| n.q.s = nil | |
| if ws.maxClosedNodesInTree > 0 { | |
| ws.addClosedOrIdleNode(&ws.closedNodes, ws.maxClosedNodesInTree, n) | |
| } else { | |
| ws.removeNode(n) | |
| } | |
| } | |
| func (ws *http2priorityWriteScheduler) AdjustStream(streamID uint32, priority http2PriorityParam) { | |
| if streamID == 0 { | |
| panic("adjustPriority on root") | |
| } | |
| // If streamID does not exist, there are two cases: | |
| // - A closed stream that has been removed (this will have ID <= maxID) | |
| // - An idle stream that is being used for "grouping" (this will have ID > maxID) | |
| n := ws.nodes[streamID] | |
| if n == nil { | |
| if streamID <= ws.maxID || ws.maxIdleNodesInTree == 0 { | |
| return | |
| } | |
| ws.maxID = streamID | |
| n = &http2priorityNode{ | |
| q: *ws.queuePool.get(), | |
| id: streamID, | |
| weight: http2priorityDefaultWeight, | |
| state: http2priorityNodeIdle, | |
| } | |
| n.setParent(&ws.root) | |
| ws.nodes[streamID] = n | |
| ws.addClosedOrIdleNode(&ws.idleNodes, ws.maxIdleNodesInTree, n) | |
| } | |
| // Section 5.3.1: A dependency on a stream that is not currently in the tree | |
| // results in that stream being given a default priority (Section 5.3.5). | |
| parent := ws.nodes[priority.StreamDep] | |
| if parent == nil { | |
| n.setParent(&ws.root) | |
| n.weight = http2priorityDefaultWeight | |
| return | |
| } | |
| // Ignore if the client tries to make a node its own parent. | |
| if n == parent { | |
| return | |
| } | |
| // Section 5.3.3: | |
| // "If a stream is made dependent on one of its own dependencies, the | |
| // formerly dependent stream is first moved to be dependent on the | |
| // reprioritized stream's previous parent. The moved dependency retains | |
| // its weight." | |
| // | |
| // That is: if parent depends on n, move parent to depend on n.parent. | |
| for x := parent.parent; x != nil; x = x.parent { | |
| if x == n { | |
| parent.setParent(n.parent) | |
| break | |
| } | |
| } | |
| // Section 5.3.3: The exclusive flag causes the stream to become the sole | |
| // dependency of its parent stream, causing other dependencies to become | |
| // dependent on the exclusive stream. | |
| if priority.Exclusive { | |
| k := parent.kids | |
| for k != nil { | |
| next := k.next | |
| if k != n { | |
| k.setParent(n) | |
| } | |
| k = next | |
| } | |
| } | |
| n.setParent(parent) | |
| n.weight = priority.Weight | |
| } | |
| func (ws *http2priorityWriteScheduler) Push(wr http2FrameWriteRequest) { | |
| var n *http2priorityNode | |
| if id := wr.StreamID(); id == 0 { | |
| n = &ws.root | |
| } else { | |
| n = ws.nodes[id] | |
| if n == nil { | |
| // id is an idle or closed stream. wr should not be a HEADERS or | |
| // DATA frame. However, wr can be a RST_STREAM. In this case, we | |
| // push wr onto the root, rather than creating a new priorityNode, | |
| // since RST_STREAM is tiny and the stream's priority is unknown | |
| // anyway. See issue #17919. | |
| if wr.DataSize() > 0 { | |
| panic("add DATA on non-open stream") | |
| } | |
| n = &ws.root | |
| } | |
| } | |
| n.q.push(wr) | |
| } | |
| func (ws *http2priorityWriteScheduler) Pop() (wr http2FrameWriteRequest, ok bool) { | |
| ws.root.walkReadyInOrder(false, &ws.tmp, func(n *http2priorityNode, openParent bool) bool { | |
| limit := int32(math.MaxInt32) | |
| if openParent { | |
| limit = ws.writeThrottleLimit | |
| } | |
| wr, ok = n.q.consume(limit) | |
| if !ok { | |
| return false | |
| } | |
| n.addBytes(int64(wr.DataSize())) | |
| // If B depends on A and B continuously has data available but A | |
| // does not, gradually increase the throttling limit to allow B to | |
| // steal more and more bandwidth from A. | |
| if openParent { | |
| ws.writeThrottleLimit += 1024 | |
| if ws.writeThrottleLimit < 0 { | |
| ws.writeThrottleLimit = math.MaxInt32 | |
| } | |
| } else if ws.enableWriteThrottle { | |
| ws.writeThrottleLimit = 1024 | |
| } | |
| return true | |
| }) | |
| return wr, ok | |
| } | |
| func (ws *http2priorityWriteScheduler) addClosedOrIdleNode(list *[]*http2priorityNode, maxSize int, n *http2priorityNode) { | |
| if maxSize == 0 { | |
| return | |
| } | |
| if len(*list) == maxSize { | |
| // Remove the oldest node, then shift left. | |
| ws.removeNode((*list)[0]) | |
| x := (*list)[1:] | |
| copy(*list, x) | |
| *list = (*list)[:len(x)] | |
| } | |
| *list = append(*list, n) | |
| } | |
| func (ws *http2priorityWriteScheduler) removeNode(n *http2priorityNode) { | |
| for k := n.kids; k != nil; k = k.next { | |
| k.setParent(n.parent) | |
| } | |
| n.setParent(nil) | |
| delete(ws.nodes, n.id) | |
| } | |
| // NewRandomWriteScheduler constructs a WriteScheduler that ignores HTTP/2 | |
| // priorities. Control frames like SETTINGS and PING are written before DATA | |
| // frames, but if no control frames are queued and multiple streams have queued | |
| // HEADERS or DATA frames, Pop selects a ready stream arbitrarily. | |
| func http2NewRandomWriteScheduler() http2WriteScheduler { | |
| return &http2randomWriteScheduler{sq: make(map[uint32]*http2writeQueue)} | |
| } | |
| type http2randomWriteScheduler struct { | |
| // zero are frames not associated with a specific stream. | |
| zero http2writeQueue | |
| // sq contains the stream-specific queues, keyed by stream ID. | |
| // When a stream is idle, closed, or emptied, it's deleted | |
| // from the map. | |
| sq map[uint32]*http2writeQueue | |
| // pool of empty queues for reuse. | |
| queuePool http2writeQueuePool | |
| } | |
| func (ws *http2randomWriteScheduler) OpenStream(streamID uint32, options http2OpenStreamOptions) { | |
| // no-op: idle streams are not tracked | |
| } | |
| func (ws *http2randomWriteScheduler) CloseStream(streamID uint32) { | |
| q, ok := ws.sq[streamID] | |
| if !ok { | |
| return | |
| } | |
| delete(ws.sq, streamID) | |
| ws.queuePool.put(q) | |
| } | |
| func (ws *http2randomWriteScheduler) AdjustStream(streamID uint32, priority http2PriorityParam) { | |
| // no-op: priorities are ignored | |
| } | |
| func (ws *http2randomWriteScheduler) Push(wr http2FrameWriteRequest) { | |
| id := wr.StreamID() | |
| if id == 0 { | |
| ws.zero.push(wr) | |
| return | |
| } | |
| q, ok := ws.sq[id] | |
| if !ok { | |
| q = ws.queuePool.get() | |
| ws.sq[id] = q | |
| } | |
| q.push(wr) | |
| } | |
| func (ws *http2randomWriteScheduler) Pop() (http2FrameWriteRequest, bool) { | |
| // Control frames first. | |
| if !ws.zero.empty() { | |
| return ws.zero.shift(), true | |
| } | |
| // Iterate over all non-idle streams until finding one that can be consumed. | |
| for streamID, q := range ws.sq { | |
| if wr, ok := q.consume(math.MaxInt32); ok { | |
| if q.empty() { | |
| delete(ws.sq, streamID) | |
| ws.queuePool.put(q) | |
| } | |
| return wr, true | |
| } | |
| } | |
| return http2FrameWriteRequest{}, false | |
| } |