| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 1 | // Copyright 2014 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | // Package http2 implements the HTTP/2 protocol. |
| 6 | // |
| 7 | // This package is low-level and intended to be used directly by very |
| 8 | // few people. Most users will use it indirectly through the automatic |
| 9 | // use by the net/http package (from Go 1.6 and later). |
| 10 | // For use in earlier Go versions see ConfigureServer. (Transport support |
| 11 | // requires Go 1.6 or later) |
| 12 | // |
| 13 | // See https://http2.github.io/ for more information on HTTP/2. |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 14 | package http2 // import "golang.org/x/net/http2" |
| 15 | |
| 16 | import ( |
| 17 | "bufio" |
| 18 | "crypto/tls" |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 19 | "errors" |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 20 | "fmt" |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 21 | "net" |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 22 | "net/http" |
| 23 | "os" |
| 24 | "sort" |
| 25 | "strconv" |
| 26 | "strings" |
| 27 | "sync" |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 28 | "time" |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 29 | |
| 30 | "golang.org/x/net/http/httpguts" |
| 31 | ) |
| 32 | |
| 33 | var ( |
| 34 | VerboseLogs bool |
| 35 | logFrameWrites bool |
| 36 | logFrameReads bool |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 37 | |
| 38 | // Enabling extended CONNECT by causes browsers to attempt to use |
| 39 | // WebSockets-over-HTTP/2. This results in problems when the server's websocket |
| 40 | // package doesn't support extended CONNECT. |
| 41 | // |
| 42 | // Disable extended CONNECT by default for now. |
| 43 | // |
| 44 | // Issue #71128. |
| 45 | disableExtendedConnectProtocol = true |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 46 | ) |
| 47 | |
| 48 | func init() { |
| 49 | e := os.Getenv("GODEBUG") |
| 50 | if strings.Contains(e, "http2debug=1") { |
| 51 | VerboseLogs = true |
| 52 | } |
| 53 | if strings.Contains(e, "http2debug=2") { |
| 54 | VerboseLogs = true |
| 55 | logFrameWrites = true |
| 56 | logFrameReads = true |
| 57 | } |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 58 | if strings.Contains(e, "http2xconnect=1") { |
| 59 | disableExtendedConnectProtocol = false |
| 60 | } |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | const ( |
| 64 | // ClientPreface is the string that must be sent by new |
| 65 | // connections from clients. |
| 66 | ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" |
| 67 | |
| 68 | // SETTINGS_MAX_FRAME_SIZE default |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 69 | // https://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2 |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 70 | initialMaxFrameSize = 16384 |
| 71 | |
| 72 | // NextProtoTLS is the NPN/ALPN protocol negotiated during |
| 73 | // HTTP/2's TLS setup. |
| 74 | NextProtoTLS = "h2" |
| 75 | |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 76 | // https://httpwg.org/specs/rfc7540.html#SettingValues |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 77 | initialHeaderTableSize = 4096 |
| 78 | |
| 79 | initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size |
| 80 | |
| 81 | defaultMaxReadFrameSize = 1 << 20 |
| 82 | ) |
| 83 | |
| 84 | var ( |
| 85 | clientPreface = []byte(ClientPreface) |
| 86 | ) |
| 87 | |
| 88 | type streamState int |
| 89 | |
| 90 | // HTTP/2 stream states. |
| 91 | // |
| 92 | // See http://tools.ietf.org/html/rfc7540#section-5.1. |
| 93 | // |
| 94 | // For simplicity, the server code merges "reserved (local)" into |
| 95 | // "half-closed (remote)". This is one less state transition to track. |
| 96 | // The only downside is that we send PUSH_PROMISEs slightly less |
| 97 | // liberally than allowable. More discussion here: |
| 98 | // https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0599.html |
| 99 | // |
| 100 | // "reserved (remote)" is omitted since the client code does not |
| 101 | // support server push. |
| 102 | const ( |
| 103 | stateIdle streamState = iota |
| 104 | stateOpen |
| 105 | stateHalfClosedLocal |
| 106 | stateHalfClosedRemote |
| 107 | stateClosed |
| 108 | ) |
| 109 | |
| 110 | var stateName = [...]string{ |
| 111 | stateIdle: "Idle", |
| 112 | stateOpen: "Open", |
| 113 | stateHalfClosedLocal: "HalfClosedLocal", |
| 114 | stateHalfClosedRemote: "HalfClosedRemote", |
| 115 | stateClosed: "Closed", |
| 116 | } |
| 117 | |
| 118 | func (st streamState) String() string { |
| 119 | return stateName[st] |
| 120 | } |
| 121 | |
| 122 | // Setting is a setting parameter: which setting it is, and its value. |
| 123 | type Setting struct { |
| 124 | // ID is which setting is being set. |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 125 | // See https://httpwg.org/specs/rfc7540.html#SettingFormat |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 126 | ID SettingID |
| 127 | |
| 128 | // Val is the value. |
| 129 | Val uint32 |
| 130 | } |
| 131 | |
| 132 | func (s Setting) String() string { |
| 133 | return fmt.Sprintf("[%v = %d]", s.ID, s.Val) |
| 134 | } |
| 135 | |
| 136 | // Valid reports whether the setting is valid. |
| 137 | func (s Setting) Valid() error { |
| 138 | // Limits and error codes from 6.5.2 Defined SETTINGS Parameters |
| 139 | switch s.ID { |
| 140 | case SettingEnablePush: |
| 141 | if s.Val != 1 && s.Val != 0 { |
| 142 | return ConnectionError(ErrCodeProtocol) |
| 143 | } |
| 144 | case SettingInitialWindowSize: |
| 145 | if s.Val > 1<<31-1 { |
| 146 | return ConnectionError(ErrCodeFlowControl) |
| 147 | } |
| 148 | case SettingMaxFrameSize: |
| 149 | if s.Val < 16384 || s.Val > 1<<24-1 { |
| 150 | return ConnectionError(ErrCodeProtocol) |
| 151 | } |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 152 | case SettingEnableConnectProtocol: |
| 153 | if s.Val != 1 && s.Val != 0 { |
| 154 | return ConnectionError(ErrCodeProtocol) |
| 155 | } |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 156 | } |
| 157 | return nil |
| 158 | } |
| 159 | |
| 160 | // A SettingID is an HTTP/2 setting as defined in |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 161 | // https://httpwg.org/specs/rfc7540.html#iana-settings |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 162 | type SettingID uint16 |
| 163 | |
| 164 | const ( |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 165 | SettingHeaderTableSize SettingID = 0x1 |
| 166 | SettingEnablePush SettingID = 0x2 |
| 167 | SettingMaxConcurrentStreams SettingID = 0x3 |
| 168 | SettingInitialWindowSize SettingID = 0x4 |
| 169 | SettingMaxFrameSize SettingID = 0x5 |
| 170 | SettingMaxHeaderListSize SettingID = 0x6 |
| 171 | SettingEnableConnectProtocol SettingID = 0x8 |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 172 | ) |
| 173 | |
| 174 | var settingName = map[SettingID]string{ |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 175 | SettingHeaderTableSize: "HEADER_TABLE_SIZE", |
| 176 | SettingEnablePush: "ENABLE_PUSH", |
| 177 | SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", |
| 178 | SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", |
| 179 | SettingMaxFrameSize: "MAX_FRAME_SIZE", |
| 180 | SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", |
| 181 | SettingEnableConnectProtocol: "ENABLE_CONNECT_PROTOCOL", |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 182 | } |
| 183 | |
| 184 | func (s SettingID) String() string { |
| 185 | if v, ok := settingName[s]; ok { |
| 186 | return v |
| 187 | } |
| 188 | return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s)) |
| 189 | } |
| 190 | |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 191 | // validWireHeaderFieldName reports whether v is a valid header field |
| 192 | // name (key). See httpguts.ValidHeaderName for the base rules. |
| 193 | // |
| 194 | // Further, http2 says: |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 195 | // |
| 196 | // "Just as in HTTP/1.x, header field names are strings of ASCII |
| 197 | // characters that are compared in a case-insensitive |
| 198 | // fashion. However, header field names MUST be converted to |
| 199 | // lowercase prior to their encoding in HTTP/2. " |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 200 | func validWireHeaderFieldName(v string) bool { |
| 201 | if len(v) == 0 { |
| 202 | return false |
| 203 | } |
| 204 | for _, r := range v { |
| 205 | if !httpguts.IsTokenRune(r) { |
| 206 | return false |
| 207 | } |
| 208 | if 'A' <= r && r <= 'Z' { |
| 209 | return false |
| 210 | } |
| 211 | } |
| 212 | return true |
| 213 | } |
| 214 | |
| 215 | func httpCodeString(code int) string { |
| 216 | switch code { |
| 217 | case 200: |
| 218 | return "200" |
| 219 | case 404: |
| 220 | return "404" |
| 221 | } |
| 222 | return strconv.Itoa(code) |
| 223 | } |
| 224 | |
| 225 | // from pkg io |
| 226 | type stringWriter interface { |
| 227 | WriteString(s string) (n int, err error) |
| 228 | } |
| 229 | |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 230 | // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed). |
| 231 | type closeWaiter chan struct{} |
| 232 | |
| 233 | // Init makes a closeWaiter usable. |
| 234 | // It exists because so a closeWaiter value can be placed inside a |
| 235 | // larger struct and have the Mutex and Cond's memory in the same |
| 236 | // allocation. |
| 237 | func (cw *closeWaiter) Init() { |
| 238 | *cw = make(chan struct{}) |
| 239 | } |
| 240 | |
| 241 | // Close marks the closeWaiter as closed and unblocks any waiters. |
| 242 | func (cw closeWaiter) Close() { |
| 243 | close(cw) |
| 244 | } |
| 245 | |
| 246 | // Wait waits for the closeWaiter to become closed. |
| 247 | func (cw closeWaiter) Wait() { |
| 248 | <-cw |
| 249 | } |
| 250 | |
| 251 | // bufferedWriter is a buffered writer that writes to w. |
| 252 | // Its buffered writer is lazily allocated as needed, to minimize |
| 253 | // idle memory usage with many connections. |
| 254 | type bufferedWriter struct { |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 255 | _ incomparable |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 256 | conn net.Conn // immutable |
| 257 | bw *bufio.Writer // non-nil when data is buffered |
| 258 | byteTimeout time.Duration // immutable, WriteByteTimeout |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 259 | } |
| 260 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 261 | func newBufferedWriter(conn net.Conn, timeout time.Duration) *bufferedWriter { |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 262 | return &bufferedWriter{ |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 263 | conn: conn, |
| 264 | byteTimeout: timeout, |
| 265 | } |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | // bufWriterPoolBufferSize is the size of bufio.Writer's |
| 269 | // buffers created using bufWriterPool. |
| 270 | // |
| 271 | // TODO: pick a less arbitrary value? this is a bit under |
| 272 | // (3 x typical 1500 byte MTU) at least. Other than that, |
| 273 | // not much thought went into it. |
| 274 | const bufWriterPoolBufferSize = 4 << 10 |
| 275 | |
| 276 | var bufWriterPool = sync.Pool{ |
| 277 | New: func() interface{} { |
| 278 | return bufio.NewWriterSize(nil, bufWriterPoolBufferSize) |
| 279 | }, |
| 280 | } |
| 281 | |
| 282 | func (w *bufferedWriter) Available() int { |
| 283 | if w.bw == nil { |
| 284 | return bufWriterPoolBufferSize |
| 285 | } |
| 286 | return w.bw.Available() |
| 287 | } |
| 288 | |
| 289 | func (w *bufferedWriter) Write(p []byte) (n int, err error) { |
| 290 | if w.bw == nil { |
| 291 | bw := bufWriterPool.Get().(*bufio.Writer) |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 292 | bw.Reset((*bufferedWriterTimeoutWriter)(w)) |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 293 | w.bw = bw |
| 294 | } |
| 295 | return w.bw.Write(p) |
| 296 | } |
| 297 | |
| 298 | func (w *bufferedWriter) Flush() error { |
| 299 | bw := w.bw |
| 300 | if bw == nil { |
| 301 | return nil |
| 302 | } |
| 303 | err := bw.Flush() |
| 304 | bw.Reset(nil) |
| 305 | bufWriterPool.Put(bw) |
| 306 | w.bw = nil |
| 307 | return err |
| 308 | } |
| 309 | |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 310 | type bufferedWriterTimeoutWriter bufferedWriter |
| 311 | |
| 312 | func (w *bufferedWriterTimeoutWriter) Write(p []byte) (n int, err error) { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 313 | return writeWithByteTimeout(w.conn, w.byteTimeout, p) |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 314 | } |
| 315 | |
| 316 | // writeWithByteTimeout writes to conn. |
| 317 | // If more than timeout passes without any bytes being written to the connection, |
| 318 | // the write fails. |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 319 | func writeWithByteTimeout(conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 320 | if timeout <= 0 { |
| 321 | return conn.Write(p) |
| 322 | } |
| 323 | for { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 324 | conn.SetWriteDeadline(time.Now().Add(timeout)) |
| mgouda | 64be882 | 2025-05-30 10:44:00 +0530 | [diff] [blame] | 325 | nn, err := conn.Write(p[n:]) |
| 326 | n += nn |
| 327 | if n == len(p) || nn == 0 || !errors.Is(err, os.ErrDeadlineExceeded) { |
| 328 | // Either we finished the write, made no progress, or hit the deadline. |
| 329 | // Whichever it is, we're done now. |
| 330 | conn.SetWriteDeadline(time.Time{}) |
| 331 | return n, err |
| 332 | } |
| 333 | } |
| 334 | } |
| 335 | |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 336 | func mustUint31(v int32) uint32 { |
| 337 | if v < 0 || v > 2147483647 { |
| 338 | panic("out of range") |
| 339 | } |
| 340 | return uint32(v) |
| 341 | } |
| 342 | |
| 343 | // bodyAllowedForStatus reports whether a given response status code |
| 344 | // permits a body. See RFC 7230, section 3.3. |
| 345 | func bodyAllowedForStatus(status int) bool { |
| 346 | switch { |
| 347 | case status >= 100 && status <= 199: |
| 348 | return false |
| 349 | case status == 204: |
| 350 | return false |
| 351 | case status == 304: |
| 352 | return false |
| 353 | } |
| 354 | return true |
| 355 | } |
| 356 | |
| 357 | type httpError struct { |
| Andrea Campanella | 3614a92 | 2021-02-25 12:40:42 +0100 | [diff] [blame] | 358 | _ incomparable |
| khenaidoo | ac63710 | 2019-01-14 15:44:34 -0500 | [diff] [blame] | 359 | msg string |
| 360 | timeout bool |
| 361 | } |
| 362 | |
| 363 | func (e *httpError) Error() string { return e.msg } |
| 364 | func (e *httpError) Timeout() bool { return e.timeout } |
| 365 | func (e *httpError) Temporary() bool { return true } |
| 366 | |
| 367 | var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true} |
| 368 | |
| 369 | type connectionStater interface { |
| 370 | ConnectionState() tls.ConnectionState |
| 371 | } |
| 372 | |
| 373 | var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }} |
| 374 | |
| 375 | type sorter struct { |
| 376 | v []string // owned by sorter |
| 377 | } |
| 378 | |
| 379 | func (s *sorter) Len() int { return len(s.v) } |
| 380 | func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] } |
| 381 | func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] } |
| 382 | |
| 383 | // Keys returns the sorted keys of h. |
| 384 | // |
| 385 | // The returned slice is only valid until s used again or returned to |
| 386 | // its pool. |
| 387 | func (s *sorter) Keys(h http.Header) []string { |
| 388 | keys := s.v[:0] |
| 389 | for k := range h { |
| 390 | keys = append(keys, k) |
| 391 | } |
| 392 | s.v = keys |
| 393 | sort.Sort(s) |
| 394 | return keys |
| 395 | } |
| 396 | |
| 397 | func (s *sorter) SortStrings(ss []string) { |
| 398 | // Our sorter works on s.v, which sorter owns, so |
| 399 | // stash it away while we sort the user's buffer. |
| 400 | save := s.v |
| 401 | s.v = ss |
| 402 | sort.Sort(s) |
| 403 | s.v = save |
| 404 | } |
| 405 | |
| Andrea Campanella | 3614a92 | 2021-02-25 12:40:42 +0100 | [diff] [blame] | 406 | // incomparable is a zero-width, non-comparable type. Adding it to a struct |
| 407 | // makes that struct also non-comparable, and generally doesn't add |
| 408 | // any size (as long as it's first). |
| 409 | type incomparable [0]func() |