2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
25 #include "nghttp2_submit.h"
30 #include "nghttp2_session.h"
31 #include "nghttp2_frame.h"
32 #include "nghttp2_helper.h"
33 #include "nghttp2_priority_spec.h"
35 /* This function takes ownership of |nva_copy|. Regardless of the
36 return value, the caller must not free |nva_copy| after this
38 static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
40 const nghttp2_priority_spec *pri_spec,
41 nghttp2_nv *nva_copy, size_t nvlen,
42 const nghttp2_data_provider *data_prd,
43 void *stream_user_data,
44 uint8_t attach_stream) {
47 nghttp2_outbound_item *item = NULL;
48 nghttp2_frame *frame = NULL;
49 nghttp2_headers_category hcat;
55 rv = NGHTTP2_ERR_INVALID_ARGUMENT;
59 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
61 rv = NGHTTP2_ERR_NOMEM;
65 nghttp2_session_outbound_item_init(session, item);
67 if (data_prd != NULL && data_prd->read_callback != NULL) {
68 item->aux_data.headers.data_prd = *data_prd;
71 item->aux_data.headers.stream_user_data = stream_user_data;
72 item->aux_data.headers.attach_stream = attach_stream;
74 flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) |
75 NGHTTP2_FLAG_END_HEADERS;
77 if (stream_id == -1) {
78 if (session->next_stream_id > INT32_MAX) {
79 rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
83 stream_id = session->next_stream_id;
84 session->next_stream_id += 2;
86 hcat = NGHTTP2_HCAT_REQUEST;
88 /* More specific categorization will be done later. */
89 hcat = NGHTTP2_HCAT_HEADERS;
94 nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,
95 pri_spec, nva_copy, nvlen);
97 rv = nghttp2_session_add_item(session, item);
100 nghttp2_frame_headers_free(&frame->headers, mem);
104 if (hcat == NGHTTP2_HCAT_REQUEST) {
111 /* nghttp2_frame_headers_init() takes ownership of nva_copy. */
112 nghttp2_nv_array_del(nva_copy, mem);
114 nghttp2_mem_free(mem, item);
119 static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec) {
120 if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) {
121 pri_spec->weight = NGHTTP2_MIN_WEIGHT;
122 } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) {
123 pri_spec->weight = NGHTTP2_MAX_WEIGHT;
127 static int32_t submit_headers_shared_nva(nghttp2_session *session,
128 uint8_t flags, int32_t stream_id,
129 const nghttp2_priority_spec *pri_spec,
130 const nghttp2_nv *nva, size_t nvlen,
131 const nghttp2_data_provider *data_prd,
132 void *stream_user_data,
133 uint8_t attach_stream) {
135 nghttp2_nv *nva_copy;
136 nghttp2_priority_spec copy_pri_spec;
142 copy_pri_spec = *pri_spec;
143 adjust_priority_spec_weight(©_pri_spec);
145 nghttp2_priority_spec_default_init(©_pri_spec);
148 rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
153 return submit_headers_shared(session, flags, stream_id, ©_pri_spec,
154 nva_copy, nvlen, data_prd, stream_user_data,
158 int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
160 const nghttp2_priority_spec *pri_spec,
161 const nghttp2_nv *nva, size_t nvlen,
162 void *stream_user_data) {
163 flags &= NGHTTP2_FLAG_END_STREAM;
165 if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
166 flags |= NGHTTP2_FLAG_PRIORITY;
171 return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
172 nvlen, NULL, stream_user_data, 0);
175 int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_,
176 const uint8_t *opaque_data) {
177 return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data);
180 int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
182 const nghttp2_priority_spec *pri_spec) {
184 nghttp2_outbound_item *item;
185 nghttp2_frame *frame;
186 nghttp2_priority_spec copy_pri_spec;
191 if (stream_id == 0 || pri_spec == NULL) {
192 return NGHTTP2_ERR_INVALID_ARGUMENT;
195 if (stream_id == pri_spec->stream_id) {
196 return NGHTTP2_ERR_INVALID_ARGUMENT;
199 copy_pri_spec = *pri_spec;
201 adjust_priority_spec_weight(©_pri_spec);
203 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
206 return NGHTTP2_ERR_NOMEM;
209 nghttp2_session_outbound_item_init(session, item);
211 frame = &item->frame;
213 nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec);
215 rv = nghttp2_session_add_item(session, item);
218 nghttp2_frame_priority_free(&frame->priority);
219 nghttp2_mem_free(mem, item);
227 int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_,
228 int32_t stream_id, uint32_t error_code) {
229 if (stream_id == 0) {
230 return NGHTTP2_ERR_INVALID_ARGUMENT;
233 return nghttp2_session_add_rst_stream(session, stream_id, error_code);
236 int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_,
237 int32_t last_stream_id, uint32_t error_code,
238 const uint8_t *opaque_data, size_t opaque_data_len) {
239 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
242 return nghttp2_session_add_goaway(session, last_stream_id, error_code,
243 opaque_data, opaque_data_len,
244 NGHTTP2_GOAWAY_AUX_NONE);
247 int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
248 if (!session->server) {
249 return NGHTTP2_ERR_INVALID_STATE;
251 if (session->goaway_flags) {
254 return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
256 NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
259 int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_,
260 const nghttp2_settings_entry *iv, size_t niv) {
261 return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
264 int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
265 int32_t stream_id, const nghttp2_nv *nva,
267 void *promised_stream_user_data) {
268 nghttp2_outbound_item *item;
269 nghttp2_frame *frame;
270 nghttp2_nv *nva_copy;
272 int32_t promised_stream_id;
278 if (stream_id == 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
279 return NGHTTP2_ERR_INVALID_ARGUMENT;
282 if (!session->server) {
283 return NGHTTP2_ERR_PROTO;
286 /* All 32bit signed stream IDs are spent. */
287 if (session->next_stream_id > INT32_MAX) {
288 return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
291 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
293 return NGHTTP2_ERR_NOMEM;
296 nghttp2_session_outbound_item_init(session, item);
298 item->aux_data.headers.stream_user_data = promised_stream_user_data;
300 frame = &item->frame;
302 rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
304 nghttp2_mem_free(mem, item);
308 flags_copy = NGHTTP2_FLAG_END_HEADERS;
310 promised_stream_id = session->next_stream_id;
311 session->next_stream_id += 2;
313 nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
314 promised_stream_id, nva_copy, nvlen);
316 rv = nghttp2_session_add_item(session, item);
319 nghttp2_frame_push_promise_free(&frame->push_promise, mem);
320 nghttp2_mem_free(mem, item);
325 return promised_stream_id;
328 int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
330 int32_t window_size_increment) {
332 nghttp2_stream *stream = 0;
333 if (window_size_increment == 0) {
337 if (stream_id == 0) {
338 rv = nghttp2_adjust_local_window_size(
339 &session->local_window_size, &session->recv_window_size,
340 &session->recv_reduction, &window_size_increment);
345 stream = nghttp2_session_get_stream(session, stream_id);
350 rv = nghttp2_adjust_local_window_size(
351 &stream->local_window_size, &stream->recv_window_size,
352 &stream->recv_reduction, &window_size_increment);
358 if (window_size_increment > 0) {
359 if (stream_id == 0) {
360 session->consumed_size =
361 nghttp2_max(0, session->consumed_size - window_size_increment);
363 stream->consumed_size =
364 nghttp2_max(0, stream->consumed_size - window_size_increment);
367 return nghttp2_session_add_window_update(session, flags, stream_id,
368 window_size_increment);
373 int nghttp2_submit_altsvc(nghttp2_session *session _U_, uint8_t flags _U_,
374 int32_t stream_id _U_, uint32_t max_age _U_,
375 uint16_t port _U_, const uint8_t *protocol_id _U_,
376 size_t protocol_id_len _U_, const uint8_t *host _U_,
377 size_t host_len _U_, const uint8_t *origin _U_,
378 size_t origin_len _U_) {
382 static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
383 const nghttp2_data_provider *data_prd) {
384 uint8_t flags = NGHTTP2_FLAG_NONE;
385 if (data_prd == NULL || data_prd->read_callback == NULL) {
386 flags |= NGHTTP2_FLAG_END_STREAM;
390 flags |= NGHTTP2_FLAG_PRIORITY;
396 int32_t nghttp2_submit_request(nghttp2_session *session,
397 const nghttp2_priority_spec *pri_spec,
398 const nghttp2_nv *nva, size_t nvlen,
399 const nghttp2_data_provider *data_prd,
400 void *stream_user_data) {
403 if (pri_spec && nghttp2_priority_spec_check_default(pri_spec)) {
407 flags = set_request_flags(pri_spec, data_prd);
409 return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
410 data_prd, stream_user_data, 0);
413 static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
414 uint8_t flags = NGHTTP2_FLAG_NONE;
415 if (data_prd == NULL || data_prd->read_callback == NULL) {
416 flags |= NGHTTP2_FLAG_END_STREAM;
421 int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
422 const nghttp2_nv *nva, size_t nvlen,
423 const nghttp2_data_provider *data_prd) {
424 uint8_t flags = set_response_flags(data_prd);
425 return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
429 int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
431 const nghttp2_data_provider *data_prd) {
433 nghttp2_outbound_item *item;
434 nghttp2_frame *frame;
435 nghttp2_data_aux_data *aux_data;
436 uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
441 if (stream_id == 0) {
442 return NGHTTP2_ERR_INVALID_ARGUMENT;
445 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
447 return NGHTTP2_ERR_NOMEM;
450 nghttp2_session_outbound_item_init(session, item);
452 frame = &item->frame;
453 aux_data = &item->aux_data.data;
454 aux_data->data_prd = *data_prd;
456 aux_data->flags = nflags;
458 /* flags are sent on transmission */
459 nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
461 rv = nghttp2_session_add_item(session, item);
463 nghttp2_frame_data_free(&frame->data);
464 nghttp2_mem_free(mem, item);
470 ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
471 const nghttp2_settings_entry *iv,
473 if (!nghttp2_iv_check(iv, niv)) {
474 return NGHTTP2_ERR_INVALID_ARGUMENT;
477 if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
478 return NGHTTP2_ERR_INSUFF_BUFSIZE;
481 return nghttp2_frame_pack_settings_payload(buf, iv, niv);