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