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_outbound_item_init(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_trailer(nghttp2_session *session, int32_t stream_id,
159 const nghttp2_nv *nva, size_t nvlen) {
160 return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id,
161 NULL, nva, nvlen, NULL, NULL, 0);
164 int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
166 const nghttp2_priority_spec *pri_spec,
167 const nghttp2_nv *nva, size_t nvlen,
168 void *stream_user_data) {
169 flags &= NGHTTP2_FLAG_END_STREAM;
171 if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) {
172 flags |= NGHTTP2_FLAG_PRIORITY;
177 return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
178 nvlen, NULL, stream_user_data, 0);
181 int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_,
182 const uint8_t *opaque_data) {
183 return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data);
186 int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_,
188 const nghttp2_priority_spec *pri_spec) {
190 nghttp2_outbound_item *item;
191 nghttp2_frame *frame;
192 nghttp2_priority_spec copy_pri_spec;
197 if (stream_id == 0 || pri_spec == NULL) {
198 return NGHTTP2_ERR_INVALID_ARGUMENT;
201 if (stream_id == pri_spec->stream_id) {
202 return NGHTTP2_ERR_INVALID_ARGUMENT;
205 copy_pri_spec = *pri_spec;
207 adjust_priority_spec_weight(©_pri_spec);
209 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
212 return NGHTTP2_ERR_NOMEM;
215 nghttp2_outbound_item_init(item);
217 frame = &item->frame;
219 nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec);
221 rv = nghttp2_session_add_item(session, item);
224 nghttp2_frame_priority_free(&frame->priority);
225 nghttp2_mem_free(mem, item);
233 int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_,
234 int32_t stream_id, uint32_t error_code) {
235 if (stream_id == 0) {
236 return NGHTTP2_ERR_INVALID_ARGUMENT;
239 return nghttp2_session_add_rst_stream(session, stream_id, error_code);
242 int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_,
243 int32_t last_stream_id, uint32_t error_code,
244 const uint8_t *opaque_data, size_t opaque_data_len) {
245 if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) {
248 return nghttp2_session_add_goaway(session, last_stream_id, error_code,
249 opaque_data, opaque_data_len,
250 NGHTTP2_GOAWAY_AUX_NONE);
253 int nghttp2_submit_shutdown_notice(nghttp2_session *session) {
254 if (!session->server) {
255 return NGHTTP2_ERR_INVALID_STATE;
257 if (session->goaway_flags) {
260 return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR,
262 NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE);
265 int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_,
266 const nghttp2_settings_entry *iv, size_t niv) {
267 return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv);
270 int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_,
271 int32_t stream_id, const nghttp2_nv *nva,
273 void *promised_stream_user_data) {
274 nghttp2_outbound_item *item;
275 nghttp2_frame *frame;
276 nghttp2_nv *nva_copy;
278 int32_t promised_stream_id;
284 if (stream_id == 0 || nghttp2_session_is_my_stream_id(session, stream_id)) {
285 return NGHTTP2_ERR_INVALID_ARGUMENT;
288 if (!session->server) {
289 return NGHTTP2_ERR_PROTO;
292 /* All 32bit signed stream IDs are spent. */
293 if (session->next_stream_id > INT32_MAX) {
294 return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE;
297 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
299 return NGHTTP2_ERR_NOMEM;
302 nghttp2_outbound_item_init(item);
304 item->aux_data.headers.stream_user_data = promised_stream_user_data;
306 frame = &item->frame;
308 rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem);
310 nghttp2_mem_free(mem, item);
314 flags_copy = NGHTTP2_FLAG_END_HEADERS;
316 promised_stream_id = session->next_stream_id;
317 session->next_stream_id += 2;
319 nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id,
320 promised_stream_id, nva_copy, nvlen);
322 rv = nghttp2_session_add_item(session, item);
325 nghttp2_frame_push_promise_free(&frame->push_promise, mem);
326 nghttp2_mem_free(mem, item);
331 return promised_stream_id;
334 int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
336 int32_t window_size_increment) {
338 nghttp2_stream *stream = 0;
339 if (window_size_increment == 0) {
343 if (stream_id == 0) {
344 rv = nghttp2_adjust_local_window_size(
345 &session->local_window_size, &session->recv_window_size,
346 &session->recv_reduction, &window_size_increment);
351 stream = nghttp2_session_get_stream(session, stream_id);
356 rv = nghttp2_adjust_local_window_size(
357 &stream->local_window_size, &stream->recv_window_size,
358 &stream->recv_reduction, &window_size_increment);
364 if (window_size_increment > 0) {
365 if (stream_id == 0) {
366 session->consumed_size =
367 nghttp2_max(0, session->consumed_size - window_size_increment);
369 stream->consumed_size =
370 nghttp2_max(0, stream->consumed_size - window_size_increment);
373 return nghttp2_session_add_window_update(session, flags, stream_id,
374 window_size_increment);
379 static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
380 const nghttp2_data_provider *data_prd) {
381 uint8_t flags = NGHTTP2_FLAG_NONE;
382 if (data_prd == NULL || data_prd->read_callback == NULL) {
383 flags |= NGHTTP2_FLAG_END_STREAM;
387 flags |= NGHTTP2_FLAG_PRIORITY;
393 int32_t nghttp2_submit_request(nghttp2_session *session,
394 const nghttp2_priority_spec *pri_spec,
395 const nghttp2_nv *nva, size_t nvlen,
396 const nghttp2_data_provider *data_prd,
397 void *stream_user_data) {
400 if (pri_spec && nghttp2_priority_spec_check_default(pri_spec)) {
404 flags = set_request_flags(pri_spec, data_prd);
406 return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
407 data_prd, stream_user_data, 0);
410 static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) {
411 uint8_t flags = NGHTTP2_FLAG_NONE;
412 if (data_prd == NULL || data_prd->read_callback == NULL) {
413 flags |= NGHTTP2_FLAG_END_STREAM;
418 int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,
419 const nghttp2_nv *nva, size_t nvlen,
420 const nghttp2_data_provider *data_prd) {
421 uint8_t flags = set_response_flags(data_prd);
422 return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
426 int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
428 const nghttp2_data_provider *data_prd) {
430 nghttp2_outbound_item *item;
431 nghttp2_frame *frame;
432 nghttp2_data_aux_data *aux_data;
433 uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM;
438 if (stream_id == 0) {
439 return NGHTTP2_ERR_INVALID_ARGUMENT;
442 item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
444 return NGHTTP2_ERR_NOMEM;
447 nghttp2_outbound_item_init(item);
449 frame = &item->frame;
450 aux_data = &item->aux_data.data;
451 aux_data->data_prd = *data_prd;
453 aux_data->flags = nflags;
455 /* flags are sent on transmission */
456 nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id);
458 rv = nghttp2_session_add_item(session, item);
460 nghttp2_frame_data_free(&frame->data);
461 nghttp2_mem_free(mem, item);
467 ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen,
468 const nghttp2_settings_entry *iv,
470 if (!nghttp2_iv_check(iv, niv)) {
471 return NGHTTP2_ERR_INVALID_ARGUMENT;
474 if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) {
475 return NGHTTP2_ERR_INSUFF_BUFSIZE;
478 return nghttp2_frame_pack_settings_payload(buf, iv, niv);