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