1 /* 2 * nghttp2 - HTTP/2 C Library 3 * 4 * Copyright (c) 2012 Tatsuhiro Tsujikawa 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sublicense, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be 15 * included in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 */ 25 #ifndef NGHTTP2_STREAM_H 26 #define NGHTTP2_STREAM_H 27 28 #ifdef HAVE_CONFIG_H 29 # include <config.h> 30 #endif /* HAVE_CONFIG_H */ 31 32 #include <nghttp2/nghttp2.h> 33 #include "nghttp2_outbound_item.h" 34 #include "nghttp2_map.h" 35 #include "nghttp2_pq.h" 36 #include "nghttp2_int.h" 37 38 /* 39 * If local peer is stream initiator: 40 * NGHTTP2_STREAM_OPENING : upon sending request HEADERS 41 * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS 42 * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM 43 * 44 * If remote peer is stream initiator: 45 * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS 46 * NGHTTP2_STREAM_OPENED : upon sending response HEADERS 47 * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM 48 */ 49 typedef enum { 50 /* Initial state */ 51 NGHTTP2_STREAM_INITIAL, 52 /* For stream initiator: request HEADERS has been sent, but response 53 HEADERS has not been received yet. For receiver: request HEADERS 54 has been received, but it does not send response HEADERS yet. */ 55 NGHTTP2_STREAM_OPENING, 56 /* For stream initiator: response HEADERS is received. For receiver: 57 response HEADERS is sent. */ 58 NGHTTP2_STREAM_OPENED, 59 /* RST_STREAM is received, but somehow we need to keep stream in 60 memory. */ 61 NGHTTP2_STREAM_CLOSING, 62 /* PUSH_PROMISE is received or sent */ 63 NGHTTP2_STREAM_RESERVED, 64 /* Stream is created in this state if it is used as anchor in 65 dependency tree. */ 66 NGHTTP2_STREAM_IDLE 67 } nghttp2_stream_state; 68 69 typedef enum { 70 NGHTTP2_SHUT_NONE = 0, 71 /* Indicates further receptions will be disallowed. */ 72 NGHTTP2_SHUT_RD = 0x01, 73 /* Indicates further transmissions will be disallowed. */ 74 NGHTTP2_SHUT_WR = 0x02, 75 /* Indicates both further receptions and transmissions will be 76 disallowed. */ 77 NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR 78 } nghttp2_shut_flag; 79 80 typedef enum { 81 NGHTTP2_STREAM_FLAG_NONE = 0, 82 /* Indicates that this stream is pushed stream and not opened 83 yet. */ 84 NGHTTP2_STREAM_FLAG_PUSH = 0x01, 85 /* Indicates that this stream was closed */ 86 NGHTTP2_STREAM_FLAG_CLOSED = 0x02, 87 /* Indicates the item is deferred due to flow control. */ 88 NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, 89 /* Indicates the item is deferred by user callback */ 90 NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, 91 /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and 92 NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ 93 NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c, 94 /* Indicates that this stream is not subject to RFC7540 95 priorities scheme. */ 96 NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10, 97 /* Ignore client RFC 9218 priority signal. */ 98 NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20, 99 /* Indicates that RFC 9113 leading and trailing white spaces 100 validation against a field value is not performed. */ 101 NGHTTP2_STREAM_FLAG_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 0x40, 102 } nghttp2_stream_flag; 103 104 /* HTTP related flags to enforce HTTP semantics */ 105 typedef enum { 106 NGHTTP2_HTTP_FLAG_NONE = 0, 107 /* header field seen so far */ 108 NGHTTP2_HTTP_FLAG__AUTHORITY = 1, 109 NGHTTP2_HTTP_FLAG__PATH = 1 << 1, 110 NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, 111 NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, 112 /* host is not pseudo header, but we require either host or 113 :authority */ 114 NGHTTP2_HTTP_FLAG_HOST = 1 << 4, 115 NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, 116 /* required header fields for HTTP request except for CONNECT 117 method. */ 118 NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | 119 NGHTTP2_HTTP_FLAG__PATH | 120 NGHTTP2_HTTP_FLAG__SCHEME, 121 NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, 122 /* HTTP method flags */ 123 NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, 124 NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, 125 NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, 126 NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10, 127 NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | 128 NGHTTP2_HTTP_FLAG_METH_HEAD | 129 NGHTTP2_HTTP_FLAG_METH_OPTIONS | 130 NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND, 131 /* :path category */ 132 /* path starts with "/" */ 133 NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11, 134 /* path "*" */ 135 NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12, 136 /* scheme */ 137 /* "http" or "https" scheme */ 138 NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13, 139 /* set if final response is expected */ 140 NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, 141 NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15, 142 /* set if priority header field is received */ 143 NGHTTP2_HTTP_FLAG_PRIORITY = 1 << 16, 144 /* set if an error is encountered while parsing priority header 145 field */ 146 NGHTTP2_HTTP_FLAG_BAD_PRIORITY = 1 << 17, 147 } nghttp2_http_flag; 148 149 struct nghttp2_stream { 150 /* Entry for dep_prev->obq */ 151 nghttp2_pq_entry pq_entry; 152 /* Priority Queue storing direct descendant (nghttp2_stream). Only 153 streams which itself has some data to send, or has a descendant 154 which has some data to sent. */ 155 nghttp2_pq obq; 156 /* Content-Length of request/response body. -1 if unknown. */ 157 int64_t content_length; 158 /* Received body so far */ 159 int64_t recv_content_length; 160 /* Base last_cycle for direct descendent streams. */ 161 uint64_t descendant_last_cycle; 162 /* Next scheduled time to sent item */ 163 uint64_t cycle; 164 /* Next seq used for direct descendant streams */ 165 uint64_t descendant_next_seq; 166 /* Secondary key for prioritization to break a tie for cycle. This 167 value is monotonically increased for single parent stream. */ 168 uint64_t seq; 169 /* pointers to form dependency tree. If multiple streams depend on 170 a stream, only one stream (left most) has non-NULL dep_prev which 171 points to the stream it depends on. The remaining streams are 172 linked using sib_prev and sib_next. The stream which has 173 non-NULL dep_prev always NULL sib_prev. The right most stream 174 has NULL sib_next. If this stream is a root of dependency tree, 175 dep_prev and sib_prev are NULL. */ 176 nghttp2_stream *dep_prev, *dep_next; 177 nghttp2_stream *sib_prev, *sib_next; 178 /* When stream is kept after closure, it may be kept in doubly 179 linked list pointed by nghttp2_session closed_stream_head. 180 closed_next points to the next stream object if it is the element 181 of the list. */ 182 nghttp2_stream *closed_prev, *closed_next; 183 /* The arbitrary data provided by user for this stream. */ 184 void *stream_user_data; 185 /* Item to send */ 186 nghttp2_outbound_item *item; 187 /* Last written length of frame payload */ 188 size_t last_writelen; 189 /* stream ID */ 190 int32_t stream_id; 191 /* Current remote window size. This value is computed against the 192 current initial window size of remote endpoint. */ 193 int32_t remote_window_size; 194 /* Keep track of the number of bytes received without 195 WINDOW_UPDATE. This could be negative after submitting negative 196 value to WINDOW_UPDATE */ 197 int32_t recv_window_size; 198 /* The number of bytes consumed by the application and now is 199 subject to WINDOW_UPDATE. This is only used when auto 200 WINDOW_UPDATE is turned off. */ 201 int32_t consumed_size; 202 /* The amount of recv_window_size cut using submitting negative 203 value to WINDOW_UPDATE */ 204 int32_t recv_reduction; 205 /* window size for local flow control. It is initially set to 206 NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by 207 submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ 208 int32_t local_window_size; 209 /* weight of this stream */ 210 int32_t weight; 211 /* This is unpaid penalty (offset) when calculating cycle. */ 212 uint32_t pending_penalty; 213 /* sum of weight of direct descendants */ 214 int32_t sum_dep_weight; 215 nghttp2_stream_state state; 216 /* status code from remote server */ 217 int16_t status_code; 218 /* Bitwise OR of zero or more nghttp2_http_flag values */ 219 uint32_t http_flags; 220 /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ 221 uint8_t flags; 222 /* Bitwise OR of zero or more nghttp2_shut_flag values */ 223 uint8_t shut_flags; 224 /* Nonzero if this stream has been queued to stream pointed by 225 dep_prev. We maintain the invariant that if a stream is queued, 226 then its ancestors, except for root, are also queued. This 227 invariant may break in fatal error condition. */ 228 uint8_t queued; 229 /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to 230 this stream. The nonzero does not necessarily mean WINDOW_UPDATE 231 is not queued. */ 232 uint8_t window_update_queued; 233 /* extpri is a stream priority produced by nghttp2_extpri_to_uint8 234 used by RFC 9218 extensible priorities. */ 235 uint8_t extpri; 236 /* http_extpri is a stream priority received in HTTP request header 237 fields and produced by nghttp2_extpri_to_uint8. */ 238 uint8_t http_extpri; 239 }; 240 241 void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, 242 uint8_t flags, nghttp2_stream_state initial_state, 243 int32_t weight, int32_t remote_initial_window_size, 244 int32_t local_initial_window_size, 245 void *stream_user_data, nghttp2_mem *mem); 246 247 void nghttp2_stream_free(nghttp2_stream *stream); 248 249 /* 250 * Disallow either further receptions or transmissions, or both. 251 * |flag| is bitwise OR of one or more of nghttp2_shut_flag. 252 */ 253 void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); 254 255 /* 256 * Defer |stream->item|. We won't call this function in the situation 257 * where |stream->item| == NULL. The |flags| is bitwise OR of zero or 258 * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and 259 * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates 260 * the reason of this action. 261 * 262 * This function returns 0 if it succeeds, or one of the following 263 * negative error codes: 264 * 265 * NGHTTP2_ERR_NOMEM 266 * Out of memory 267 */ 268 int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); 269 270 /* 271 * Put back deferred data in this stream to active state. The |flags| 272 * are one or more of bitwise OR of the following values: 273 * NGHTTP2_STREAM_FLAG_DEFERRED_USER and 274 * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are 275 * cleared if they are set. So even if this function is called, if 276 * one of flag is still set, data does not become active. 277 * 278 * This function returns 0 if it succeeds, or one of the following 279 * negative error codes: 280 * 281 * NGHTTP2_ERR_NOMEM 282 * Out of memory 283 */ 284 int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags); 285 286 /* 287 * Returns nonzero if item is deferred by whatever reason. 288 */ 289 int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); 290 291 /* 292 * Returns nonzero if item is deferred by flow control. 293 */ 294 int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); 295 296 /* 297 * Updates the remote window size with the new value 298 * |new_initial_window_size|. The |old_initial_window_size| is used to 299 * calculate the current window size. 300 * 301 * This function returns 0 if it succeeds or -1. The failure is due to 302 * overflow. 303 */ 304 int nghttp2_stream_update_remote_initial_window_size( 305 nghttp2_stream *stream, int32_t new_initial_window_size, 306 int32_t old_initial_window_size); 307 308 /* 309 * Updates the local window size with the new value 310 * |new_initial_window_size|. The |old_initial_window_size| is used to 311 * calculate the current window size. 312 * 313 * This function returns 0 if it succeeds or -1. The failure is due to 314 * overflow. 315 */ 316 int nghttp2_stream_update_local_initial_window_size( 317 nghttp2_stream *stream, int32_t new_initial_window_size, 318 int32_t old_initial_window_size); 319 320 /* 321 * Call this function if promised stream |stream| is replied with 322 * HEADERS. This function makes the state of the |stream| to 323 * NGHTTP2_STREAM_OPENED. 324 */ 325 void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); 326 327 /* 328 * Returns nonzero if |target| is an ancestor of |stream|. 329 */ 330 int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, 331 nghttp2_stream *target); 332 333 /* 334 * Computes distributed weight of a stream of the |weight| under the 335 * |stream| if |stream| is removed from a dependency tree. 336 */ 337 int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, 338 int32_t weight); 339 340 /* 341 * Makes the |stream| depend on the |dep_stream|. This dependency is 342 * exclusive. All existing direct descendants of |dep_stream| become 343 * the descendants of the |stream|. This function assumes 344 * |stream->item| is NULL. 345 * 346 * This function returns 0 if it succeeds, or one of the following 347 * negative error codes: 348 * 349 * NGHTTP2_ERR_NOMEM 350 * Out of memory 351 */ 352 int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, 353 nghttp2_stream *stream); 354 355 /* 356 * Makes the |stream| depend on the |dep_stream|. This dependency is 357 * not exclusive. This function assumes |stream->item| is NULL. 358 */ 359 void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); 360 361 /* 362 * Removes the |stream| from the current dependency tree. This 363 * function assumes |stream->item| is NULL. 364 */ 365 int nghttp2_stream_dep_remove(nghttp2_stream *stream); 366 367 /* 368 * Attaches |item| to |stream|. 369 * 370 * This function returns 0 if it succeeds, or one of the following 371 * negative error codes: 372 * 373 * NGHTTP2_ERR_NOMEM 374 * Out of memory 375 */ 376 int nghttp2_stream_attach_item(nghttp2_stream *stream, 377 nghttp2_outbound_item *item); 378 379 /* 380 * Detaches |stream->item|. This function does not free 381 * |stream->item|. The caller must free it. 382 * 383 * This function returns 0 if it succeeds, or one of the following 384 * negative error codes: 385 * 386 * NGHTTP2_ERR_NOMEM 387 * Out of memory 388 */ 389 int nghttp2_stream_detach_item(nghttp2_stream *stream); 390 391 /* 392 * Makes the |stream| depend on the |dep_stream|. This dependency is 393 * exclusive. 394 * 395 * This function returns 0 if it succeeds, or one of the following 396 * negative error codes: 397 * 398 * NGHTTP2_ERR_NOMEM 399 * Out of memory 400 */ 401 int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, 402 nghttp2_stream *stream); 403 404 /* 405 * Makes the |stream| depend on the |dep_stream|. This dependency is 406 * not exclusive. 407 * 408 * This function returns 0 if it succeeds, or one of the following 409 * negative error codes: 410 * 411 * NGHTTP2_ERR_NOMEM 412 * Out of memory 413 */ 414 int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, 415 nghttp2_stream *stream); 416 417 /* 418 * Removes subtree whose root stream is |stream|. The 419 * effective_weight of streams in removed subtree is not updated. 420 * 421 * This function returns 0 if it succeeds, or one of the following 422 * negative error codes: 423 * 424 * NGHTTP2_ERR_NOMEM 425 * Out of memory 426 */ 427 void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); 428 429 /* 430 * Returns nonzero if |stream| is in any dependency tree. 431 */ 432 int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); 433 434 /* 435 * Schedules transmission of |stream|'s item, assuming stream->item is 436 * attached, and stream->last_writelen was updated. 437 */ 438 void nghttp2_stream_reschedule(nghttp2_stream *stream); 439 440 /* 441 * Changes |stream|'s weight to |weight|. If |stream| is queued, it 442 * will be rescheduled based on new weight. 443 */ 444 void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight); 445 446 /* 447 * Returns a stream which has highest priority, updating 448 * descendant_last_cycle of selected stream's ancestors. 449 */ 450 nghttp2_outbound_item * 451 nghttp2_stream_next_outbound_item(nghttp2_stream *stream); 452 453 #endif /* NGHTTP2_STREAM */ 454