Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_http2_server.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
27
29#include "nilorea/n_common.h"
30#include "nilorea/n_log.h"
31
32#include <stdlib.h>
33#include <string.h>
34
36#define H2_WIN_MAX 0x7FFFFFFF
38#define H2_SRV_RECV_WINDOW H2_WIN_MAX
40#define H2_DEFAULT_WINDOW 65535
42#define H2_DEFAULT_MAX_FRAME 16384
43
45typedef struct H2BUF {
46 unsigned char* data;
47 size_t len;
48 size_t cap;
49} H2BUF;
50
52typedef struct H2_STREAM {
53 uint32_t id;
54 int in_use;
58 int reset;
60 size_t nhdrs;
62 int64_t send_win;
63} H2_STREAM;
64
87
88/* ------------------------------------------------------------------ */
89/* buffer helpers */
90/* ------------------------------------------------------------------ */
91
93static int h2buf_append(H2BUF* b, const unsigned char* p, size_t n) {
94 if (n == 0)
95 return 0;
96 if (b->len + n > b->cap) {
97 size_t nc = b->cap ? b->cap : 512;
98 unsigned char* nd;
99 while (b->len + n > nc)
100 nc *= 2;
101 nd = realloc(b->data, nc);
102 if (!nd)
103 return -1;
104 b->data = nd;
105 b->cap = nc;
106 }
107 memcpy(b->data + b->len, p, n);
108 b->len += n;
109 return 0;
110}
111
113static void h2buf_shift(H2BUF* b, size_t n) {
114 if (n >= b->len) {
115 b->len = 0;
116 return;
117 }
118 memmove(b->data, b->data + n, b->len - n);
119 b->len -= n;
120}
121
123static void h2buf_free(H2BUF* b) {
124 free(b->data);
125 b->data = NULL;
126 b->len = 0;
127 b->cap = 0;
128}
129
130/* ------------------------------------------------------------------ */
131/* frame I/O */
132/* ------------------------------------------------------------------ */
133
136static int srv_send(N_H2_SERVER* s, int type, int flags, uint32_t sid, const unsigned char* payload, size_t len) {
137 unsigned char hdr[N_H2_FRAME_HEADER_LEN];
138 if (n_http2_frame_build_header(hdr, sizeof(hdr), (uint32_t)len, type, flags, sid) == 0) {
139 s->err = "HTTP/2: frame too large to build";
140 return -1;
141 }
142 if (s->wr(s->io, hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) {
143 s->io_failed = 1;
144 s->err = "HTTP/2: write failed";
145 return -1;
146 }
147 if (len > 0 && s->wr(s->io, payload, len) != (ssize_t)len) {
148 s->io_failed = 1;
149 s->err = "HTTP/2: write failed";
150 return -1;
151 }
152 return 0;
153}
154
156static int srv_window_update(N_H2_SERVER* s, uint32_t sid, uint32_t inc) {
157 unsigned char p[4];
158 if (inc == 0)
159 return 0; /* a zero increment is a protocol error, and means nothing */
160 p[0] = (unsigned char)((inc >> 24) & 0x7F);
161 p[1] = (unsigned char)((inc >> 16) & 0xFF);
162 p[2] = (unsigned char)((inc >> 8) & 0xFF);
163 p[3] = (unsigned char)(inc & 0xFF);
164 return srv_send(s, N_H2_WINDOW_UPDATE, 0, sid, p, sizeof(p));
165}
166
168static void srv_put_setting(unsigned char* out, uint16_t id, uint32_t value) {
169 out[0] = (unsigned char)((id >> 8) & 0xFF);
170 out[1] = (unsigned char)(id & 0xFF);
171 out[2] = (unsigned char)((value >> 24) & 0xFF);
172 out[3] = (unsigned char)((value >> 16) & 0xFF);
173 out[4] = (unsigned char)((value >> 8) & 0xFF);
174 out[5] = (unsigned char)(value & 0xFF);
175}
176
179static int srv_fill(N_H2_SERVER* s) {
180 unsigned char tmp[N_H2_SRV_READ_CHUNK];
181 ssize_t n = s->rd(s->io, tmp, sizeof(tmp));
182 if (n == 0) {
183 s->eof = 1;
184 return 0;
185 }
186 if (n < 0) {
187 s->io_failed = 1;
188 s->err = "HTTP/2: read failed";
189 return -1;
190 }
191 if (h2buf_append(&s->rb, tmp, (size_t)n) != 0) {
192 s->err = "HTTP/2: out of memory";
193 return -1;
194 }
195 return 1;
196}
197
200static int srv_need(N_H2_SERVER* s, size_t n) {
201 while (s->rb.len < n) {
202 int r = srv_fill(s);
203 if (r <= 0)
204 return r;
205 }
206 return 1;
207}
208
211static int srv_next_frame(N_H2_SERVER* s, N_H2_FRAME* fr, size_t* consumed) {
212 for (;;) {
213 int pr = (s->rb.len > 0) ? n_http2_frame_parse(s->rb.data, s->rb.len, fr, consumed) : 0;
214 int r;
215 if (pr == 1)
216 return 1;
217 if (pr < 0) {
218 s->err = "HTTP/2: frame exceeds the maximum frame size";
219 return -1;
220 }
221 r = srv_fill(s);
222 if (r <= 0)
223 return r;
224 }
225}
226
227/* ------------------------------------------------------------------ */
228/* stream table */
229/* ------------------------------------------------------------------ */
230
232static H2_STREAM* srv_stream_find(N_H2_SERVER* s, uint32_t id) {
233 size_t i;
234 for (i = 0; i < N_H2_SRV_MAX_STREAMS; i++)
235 if (s->streams[i].in_use && s->streams[i].id == id)
236 return &s->streams[i];
237 return NULL;
238}
239
241static H2_STREAM* srv_stream_open(N_H2_SERVER* s, uint32_t id) {
242 size_t i;
243 for (i = 0; i < N_H2_SRV_MAX_STREAMS; i++) {
244 H2_STREAM* st = &s->streams[i];
245 if (!st->in_use) {
246 memset(st, 0, sizeof(*st));
247 st->in_use = 1;
248 st->id = id;
249 st->send_win = s->peer_initial_win;
250 return st;
251 }
252 }
253 return NULL;
254}
255
257static void srv_stream_close(H2_STREAM* st) {
258 if (!st->in_use)
259 return;
260 if (st->hdrs) {
262 free(st->hdrs);
263 }
264 h2buf_free(&st->body);
265 memset(st, 0, sizeof(*st));
266}
267
270 H2_STREAM* best = NULL;
271 size_t i;
272 for (i = 0; i < N_H2_SRV_MAX_STREAMS; i++) {
273 H2_STREAM* st = &s->streams[i];
274 if (st->in_use && st->headers_done && st->end_stream && !st->delivered && !st->reset)
275 if (!best || st->id < best->id)
276 best = st;
277 }
278 return best;
279}
280
281/* ------------------------------------------------------------------ */
282/* header block handling */
283/* ------------------------------------------------------------------ */
284
289 size_t count = 0;
290 H2_STREAM* st;
291
292 if (n_http2_hpack_decode(s->dec, s->hblock.data, s->hblock.len, tmp, N_H2_SRV_MAX_HEADERS, &count) != 0) {
293 s->err = "HTTP/2: HPACK decode failed";
294 return -1;
295 }
296 st = srv_stream_find(s, s->hblock_sid);
297 s->hblock.len = 0;
298 s->hblock_sid = 0;
299 if (!st || st->reset) {
300 n_http2_hpack_headers_free(tmp, count); /* the stream went away: drop them, the
301 HPACK state is still updated, which is
302 what keeps the connection decodable */
303 return 0;
304 }
305 if (st->headers_done) {
306 /* a second block on the same stream is a trailer section: it carries no
307 request line, so it is accounted for and dropped */
308 n_http2_hpack_headers_free(tmp, count);
309 } else {
310 st->hdrs = malloc(count ? count * sizeof(N_H2_HEADER) : sizeof(N_H2_HEADER));
311 if (!st->hdrs) {
312 n_http2_hpack_headers_free(tmp, count);
313 s->err = "HTTP/2: out of memory";
314 return -1;
315 }
316 if (count)
317 memcpy(st->hdrs, tmp, count * sizeof(N_H2_HEADER)); /* ownership moves */
318 st->nhdrs = count;
319 st->headers_done = 1;
320 }
321 if (s->hblock_end_stream)
322 st->end_stream = 1;
323 return 0;
324}
325
328static int srv_headers_payload(const N_H2_FRAME* fr, const unsigned char** p_out, size_t* len_out) {
329 const unsigned char* p = fr->payload;
330 size_t plen = fr->length;
331 size_t padlen = 0;
332 if (fr->flags & N_H2_FLAG_PADDED) {
333 if (plen < 1)
334 return -1;
335 padlen = p[0];
336 p++;
337 plen--;
338 }
339 if (fr->flags & N_H2_FLAG_PRIORITY) {
340 if (plen < 5)
341 return -1;
342 p += 5;
343 plen -= 5;
344 }
345 if (padlen > plen)
346 return -1;
347 plen -= padlen;
348 *p_out = p;
349 *len_out = plen;
350 return 0;
351}
352
353/* ------------------------------------------------------------------ */
354/* frame dispatch */
355/* ------------------------------------------------------------------ */
356
358static int srv_apply_settings(N_H2_SERVER* s, const N_H2_FRAME* fr) {
359 N_H2_SETTING set[32];
360 size_t n = 0, i;
361 if (n_http2_settings_parse(fr->payload, fr->length, set, 32, &n) != 0) {
362 s->err = "HTTP/2: malformed SETTINGS";
363 return -1;
364 }
365 for (i = 0; i < n; i++) {
366 if (set[i].id == N_H2_SETTINGS_INITIAL_WINDOW_SIZE) {
367 int64_t nv = (int64_t)set[i].value;
368 int64_t delta;
369 size_t k;
370 if (nv > H2_WIN_MAX) {
371 s->err = "HTTP/2: SETTINGS_INITIAL_WINDOW_SIZE out of range";
372 return -1;
373 }
374 delta = nv - s->peer_initial_win;
375 s->peer_initial_win = nv;
376 /* the change applies to every open stream (RFC 7540 6.9.2); the
377 connection window is not affected */
378 for (k = 0; k < N_H2_SRV_MAX_STREAMS; k++)
379 if (s->streams[k].in_use)
380 s->streams[k].send_win += delta;
381 } else if (set[i].id == N_H2_SETTINGS_MAX_FRAME_SIZE) {
382 size_t v = set[i].value;
383 if (v < H2_DEFAULT_MAX_FRAME)
387 s->peer_max_frame = v;
388 }
389 }
390 return srv_send(s, N_H2_SETTINGS, N_H2_FLAG_ACK, 0, NULL, 0);
391}
392
395 uint32_t inc;
396 if (fr->length != 4) {
397 s->err = "HTTP/2: malformed WINDOW_UPDATE";
398 return -1;
399 }
400 inc = ((uint32_t)(fr->payload[0] & 0x7F) << 24) | ((uint32_t)fr->payload[1] << 16) | ((uint32_t)fr->payload[2] << 8) | (uint32_t)fr->payload[3];
401 if (fr->stream_id == 0) {
402 s->send_win_conn += (int64_t)inc;
403 if (s->send_win_conn > H2_WIN_MAX)
405 } else {
406 H2_STREAM* st = srv_stream_find(s, fr->stream_id);
407 if (st) {
408 st->send_win += (int64_t)inc;
409 if (st->send_win > H2_WIN_MAX)
410 st->send_win = H2_WIN_MAX;
411 }
412 }
413 return 0;
414}
415
417static int srv_apply_data(N_H2_SERVER* s, const N_H2_FRAME* fr) {
418 const unsigned char* p = fr->payload;
419 size_t plen = fr->length;
420 size_t padlen = 0;
421 H2_STREAM* st = srv_stream_find(s, fr->stream_id);
422
423 if (fr->flags & N_H2_FLAG_PADDED) {
424 if (plen < 1) {
425 s->err = "HTTP/2: bad padded DATA";
426 return -1;
427 }
428 padlen = p[0];
429 p++;
430 plen--;
431 }
432 if (padlen > plen) {
433 s->err = "HTTP/2: DATA padding too large";
434 return -1;
435 }
436 plen -= padlen;
437
438 /* the whole frame payload counts against the connection window whatever
439 happens to the stream, so it is always given back: withholding it on the
440 last DATA frame of each request slowly starves a long-lived connection */
441 if (fr->length > 0 && srv_window_update(s, 0, fr->length) != 0)
442 return -1;
443
444 if (!st || st->reset) {
445 return 0; /* unknown or reset stream: the bytes are accounted for and dropped */
446 }
447 if (st->body.len + plen > N_H2_SRV_MAX_BODY) {
448 st->reset = 1;
450 }
451 if (plen > 0 && h2buf_append(&st->body, p, plen) != 0) {
452 s->err = "HTTP/2: out of memory";
453 return -1;
454 }
455 if (fr->flags & N_H2_FLAG_END_STREAM)
456 st->end_stream = 1;
457 else if (fr->length > 0 && srv_window_update(s, fr->stream_id, fr->length) != 0)
458 return -1;
459 return 0;
460}
461
463static int srv_apply_headers(N_H2_SERVER* s, const N_H2_FRAME* fr) {
464 const unsigned char* p = fr->payload;
465 size_t plen = fr->length;
466
467 if (fr->type == N_H2_HEADERS) {
468 const H2_STREAM* st;
469 if (fr->stream_id == 0 || (fr->stream_id & 1) == 0) {
470 s->err = "HTTP/2: HEADERS on an invalid stream id";
471 return -1;
472 }
473 if (srv_headers_payload(fr, &p, &plen) != 0) {
474 s->err = "HTTP/2: bad HEADERS padding or priority";
475 return -1;
476 }
477 st = srv_stream_find(s, fr->stream_id);
478 if (!st) {
479 if (fr->stream_id <= s->last_stream_id) {
480 s->err = "HTTP/2: HEADERS reopening a closed stream";
481 return -1;
482 }
483 st = srv_stream_open(s, fr->stream_id);
484 s->last_stream_id = fr->stream_id;
485 if (!st) {
486 /* over the concurrency limit: refuse the stream but keep the
487 connection, and still decode the block so HPACK stays in sync */
489 return -1;
490 }
491 }
492 s->hblock_sid = fr->stream_id;
493 s->hblock_end_stream = (fr->flags & N_H2_FLAG_END_STREAM) ? 1 : 0;
494 s->hblock.len = 0;
495 } else if (s->hblock_sid != fr->stream_id) {
496 /* a CONTINUATION must follow its HEADERS on the same stream with nothing
497 in between: anything else leaves the peer's HPACK state undefined */
498 s->err = "HTTP/2: CONTINUATION on the wrong stream";
499 return -1;
500 }
501 if (plen > 0 && h2buf_append(&s->hblock, p, plen) != 0) {
502 s->err = "HTTP/2: out of memory";
503 return -1;
504 }
506 return srv_finish_headers(s);
507 return 0;
508}
509
512static int srv_handle_frame(N_H2_SERVER* s, const N_H2_FRAME* fr) {
513 /* while a header block is open the only legal frame is its CONTINUATION */
514 if (s->hblock_sid != 0 && !(fr->type == N_H2_CONTINUATION && fr->stream_id == s->hblock_sid)) {
515 s->err = "HTTP/2: frame interleaved inside a header block";
516 return -1;
517 }
518 switch (fr->type) {
519 case N_H2_HEADERS:
521 return srv_apply_headers(s, fr);
522 case N_H2_DATA:
523 return srv_apply_data(s, fr);
524 case N_H2_SETTINGS:
525 if (fr->flags & N_H2_FLAG_ACK) {
526 if (!s->settings_acked) {
527 s->settings_acked = 1;
528 n_log(LOG_DEBUG, "HTTP/2: the peer acknowledged our SETTINGS");
529 }
530 return 0;
531 }
532 return srv_apply_settings(s, fr);
533 case N_H2_PING:
534 if (fr->flags & N_H2_FLAG_ACK)
535 return 0;
536 if (fr->length != 8) {
537 s->err = "HTTP/2: malformed PING";
538 return -1;
539 }
540 return srv_send(s, N_H2_PING, N_H2_FLAG_ACK, 0, fr->payload, fr->length);
542 return srv_apply_window_update(s, fr);
543 case N_H2_RST_STREAM: {
544 H2_STREAM* st = srv_stream_find(s, fr->stream_id);
545 if (st)
546 st->reset = 1; /* the slot is freed by the serving loop, so a
547 response in flight never dangles */
548 return 0;
549 }
550 case N_H2_GOAWAY:
551 s->goaway_recv = 1;
552 return 0;
554 s->err = "HTTP/2: PUSH_PROMISE from a client";
555 return -1;
556 case N_H2_PRIORITY:
557 default:
558 return 0; /* PRIORITY carries no state here, unknown types are ignored */
559 }
560}
561
564static int srv_pump_one(N_H2_SERVER* s) {
565 N_H2_FRAME fr;
566 size_t consumed = 0;
567 int r = srv_next_frame(s, &fr, &consumed);
568 if (r <= 0)
569 return r;
570 r = srv_handle_frame(s, &fr);
571 h2buf_shift(&s->rb, consumed); /* the frame aliases the buffer: shift after use */
572 return (r == 0) ? 1 : -1;
573}
574
575/* ------------------------------------------------------------------ */
576/* public API */
577/* ------------------------------------------------------------------ */
578
580 N_H2_SERVER* s;
581 __n_assert(rd, return NULL);
582 __n_assert(wr, return NULL);
583 s = calloc(1, sizeof(*s));
584 if (!s) {
585 n_log(LOG_ERR, "could not allocate an HTTP/2 server context");
586 return NULL;
587 }
588 s->io = io;
589 s->rd = rd;
590 s->wr = wr;
591 s->dec = n_http2_hpack_new(0);
592 if (!s->dec) {
593 free(s);
594 return NULL;
595 }
599 return s;
600}
601
603 N_H2_SERVER* s;
604 size_t i;
605 if (!srv || !*srv)
606 return;
607 s = *srv;
608 for (i = 0; i < N_H2_SRV_MAX_STREAMS; i++)
611 h2buf_free(&s->rb);
612 h2buf_free(&s->hblock);
613 free(s);
614 *srv = NULL;
615}
616
618 unsigned char settings[18];
619 N_H2_FRAME fr;
620 size_t consumed = 0;
621 int r;
622
623 __n_assert(s, return -1);
624 r = srv_need(s, strlen(N_H2_PREFACE));
625 if (r <= 0) {
626 s->err = s->err ? s->err : "HTTP/2: connection closed before the preface";
627 return -1;
628 }
629 if (memcmp(s->rb.data, N_H2_PREFACE, strlen(N_H2_PREFACE)) != 0) {
630 s->err = "HTTP/2: bad client connection preface";
631 return -1;
632 }
633 h2buf_shift(&s->rb, strlen(N_H2_PREFACE));
634
635 srv_put_setting(settings + 0, N_H2_SETTINGS_ENABLE_PUSH, 0); /* no server push */
637 srv_put_setting(settings + 12, N_H2_SETTINGS_INITIAL_WINDOW_SIZE, H2_SRV_RECV_WINDOW); /* stream receive window */
638 if (srv_send(s, N_H2_SETTINGS, 0, 0, settings, sizeof(settings)) != 0)
639 return -1;
640 /* the connection receive window starts at 65535 whatever SETTINGS says, so it
641 is opened explicitly to the maximum a window may hold */
643 return -1;
644
645 /* the client's first frame must be its SETTINGS (RFC 7540 3.5) */
646 r = srv_next_frame(s, &fr, &consumed);
647 if (r <= 0) {
648 s->err = s->err ? s->err : "HTTP/2: connection closed before the client SETTINGS";
649 return -1;
650 }
651 if (fr.type != N_H2_SETTINGS || (fr.flags & N_H2_FLAG_ACK) || fr.stream_id != 0) {
652 s->err = "HTTP/2: the first client frame was not a SETTINGS";
653 h2buf_shift(&s->rb, consumed);
654 return -1;
655 }
656 r = srv_handle_frame(s, &fr);
657 h2buf_shift(&s->rb, consumed);
658 return r;
659}
660
662 __n_assert(s, return -1);
663 __n_assert(cb, return -1);
664
665 for (;;) {
666 H2_STREAM* st;
667 int r;
668 while ((st = srv_next_ready(s)) != NULL) {
669 N_H2_REQUEST req;
670 size_t i;
671 int stop;
672 memset(&req, 0, sizeof(req));
673 req.stream_id = st->id;
674 req.headers = st->hdrs;
675 req.nheaders = st->nhdrs;
676 for (i = 0; i < st->nhdrs; i++) {
677 const char* n = st->hdrs[i].name;
678 if (!n || n[0] != ':')
679 continue;
680 if (strcmp(n, ":method") == 0)
681 req.method = st->hdrs[i].value;
682 else if (strcmp(n, ":scheme") == 0)
683 req.scheme = st->hdrs[i].value;
684 else if (strcmp(n, ":authority") == 0)
685 req.authority = st->hdrs[i].value;
686 else if (strcmp(n, ":path") == 0)
687 req.path = st->hdrs[i].value;
688 }
689 req.body = st->body.len ? st->body.data : NULL;
690 req.body_len = st->body.len;
691 st->delivered = 1;
692 s->nrequests++;
693 stop = cb(user_data, s, &req);
694 /* the slot is released only now: the callback held pointers into it */
695 st = srv_stream_find(s, req.stream_id);
696 if (st)
698 if (s->io_failed)
699 return -1;
700 if (stop != 0) {
702 return 0;
703 }
704 }
705 if (s->goaway_recv || s->eof)
706 break;
707 r = srv_pump_one(s);
708 if (r < 0)
709 return -1;
710 if (r == 0)
711 break;
712 /* reclaim the slots of streams the client gave up on, so a long-lived
713 connection does not fill its stream table with cancelled requests */
714 {
715 size_t i;
716 for (i = 0; i < N_H2_SRV_MAX_STREAMS; i++)
717 if (s->streams[i].in_use && s->streams[i].reset)
719 }
720 }
722 return 0;
723}
724
725int n_http2_server_respond(N_H2_SERVER* s, uint32_t stream_id, const N_H2_HEADER* headers, size_t nheaders, const unsigned char* body, size_t body_len) {
726 size_t cap = 64, size, off = 0, i;
727 unsigned char* block;
728 int first = 1, rc = 0;
729 H2_STREAM* st;
730
731 __n_assert(s, return -1);
732 __n_assert(headers, return -1);
733 st = srv_stream_find(s, stream_id);
734 if (!st || st->reset) {
735 s->err = "HTTP/2: response for a stream that is gone";
736 return -1;
737 }
738
739 for (i = 0; i < nheaders; i++)
740 cap += (headers[i].name ? strlen(headers[i].name) : 0) + (headers[i].value ? strlen(headers[i].value) : 0) + 8;
741 block = malloc(cap);
742 if (!block) {
743 s->err = "HTTP/2: out of memory";
744 return -1;
745 }
746 size = n_http2_hpack_encode(headers, nheaders, block, cap);
747 if (size == 0) {
748 free(block);
749 s->err = "HTTP/2: HPACK encode failed";
750 return -1;
751 }
752 do {
753 size_t chunk = size - off;
754 int flags = 0;
755 if (chunk > s->peer_max_frame)
756 chunk = s->peer_max_frame;
757 if (off + chunk >= size)
758 flags |= N_H2_FLAG_END_HEADERS;
759 if (first && body_len == 0)
760 flags |= N_H2_FLAG_END_STREAM;
761 if (srv_send(s, first ? N_H2_HEADERS : N_H2_CONTINUATION, flags, stream_id, block + off, chunk) != 0) {
762 rc = -1;
763 break;
764 }
765 off += chunk;
766 first = 0;
767 } while (off < size);
768 free(block);
769 if (rc != 0 || body_len == 0)
770 return rc;
771
772 off = 0;
773 while (off < body_len) {
774 int64_t win;
775 size_t chunk;
776 st = srv_stream_find(s, stream_id);
777 if (!st || st->reset) {
778 s->err = "HTTP/2: the stream was reset while the response was being written";
779 return -1;
780 }
781 win = (s->send_win_conn < st->send_win) ? s->send_win_conn : st->send_win;
782 if (win <= 0) {
783 /* both windows are the peer's to reopen: read until it does, which is
784 the difference between a server that streams a large body and one
785 that hangs on it */
786 int r = srv_pump_one(s);
787 if (r <= 0) {
788 s->err = s->err ? s->err : "HTTP/2: the peer never reopened the send window";
789 return -1;
790 }
791 continue;
792 }
793 chunk = body_len - off;
794 if (chunk > s->peer_max_frame)
795 chunk = s->peer_max_frame;
796 if ((int64_t)chunk > win)
797 chunk = (size_t)win;
798 if (srv_send(s, N_H2_DATA, (off + chunk >= body_len) ? N_H2_FLAG_END_STREAM : 0, stream_id, body + off, chunk) != 0)
799 return -1;
800 s->send_win_conn -= (int64_t)chunk;
801 st->send_win -= (int64_t)chunk;
802 off += chunk;
803 }
804 return 0;
805}
806
807int n_http2_server_rst_stream(N_H2_SERVER* s, uint32_t stream_id, uint32_t error_code) {
808 unsigned char p[4];
809 __n_assert(s, return -1);
810 p[0] = (unsigned char)((error_code >> 24) & 0xFF);
811 p[1] = (unsigned char)((error_code >> 16) & 0xFF);
812 p[2] = (unsigned char)((error_code >> 8) & 0xFF);
813 p[3] = (unsigned char)(error_code & 0xFF);
814 return srv_send(s, N_H2_RST_STREAM, 0, stream_id, p, sizeof(p));
815}
816
817int n_http2_server_goaway(N_H2_SERVER* s, uint32_t error_code) {
818 unsigned char p[8];
819 __n_assert(s, return -1);
820 if (s->io_failed)
821 return -1; /* the connection is already gone: do not log a second failure */
822 p[0] = (unsigned char)((s->last_stream_id >> 24) & 0x7F);
823 p[1] = (unsigned char)((s->last_stream_id >> 16) & 0xFF);
824 p[2] = (unsigned char)((s->last_stream_id >> 8) & 0xFF);
825 p[3] = (unsigned char)(s->last_stream_id & 0xFF);
826 p[4] = (unsigned char)((error_code >> 24) & 0xFF);
827 p[5] = (unsigned char)((error_code >> 16) & 0xFF);
828 p[6] = (unsigned char)((error_code >> 8) & 0xFF);
829 p[7] = (unsigned char)(error_code & 0xFF);
830 return srv_send(s, N_H2_GOAWAY, 0, 0, p, sizeof(p));
831}
832
833const char* n_http2_server_error(const N_H2_SERVER* s) {
834 return s ? s->err : NULL;
835}
836
838 return s ? s->nrequests : 0;
839}
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
static void stop(int sig)
Definition ex_kafka.c:75
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
Common headers and low-level functions & define.
int n_http2_hpack_decode(N_H2_HPACK *ctx, const unsigned char *block, size_t len, N_H2_HEADER *out, size_t max_out, size_t *count)
decode an HPACK header block into out[] (owned name/value strings).
Definition n_http2.c:472
size_t n_http2_hpack_encode(const N_H2_HEADER *headers, size_t n, unsigned char *out, size_t out_cap)
encode a header list into a block using literal representations without indexing and without Huffman ...
Definition n_http2.c:554
N_H2_HPACK * n_http2_hpack_new(size_t max_table_size)
create an HPACK codec context with the given dynamic table size limit (0 uses N_H2_HPACK_DEFAULT_TABL...
Definition n_http2.c:353
void n_http2_hpack_free(N_H2_HPACK **pctx)
free an HPACK codec context and NULL the caller's pointer
Definition n_http2.c:375
void n_http2_hpack_headers_free(N_H2_HEADER *headers, size_t n)
free the owned name/value strings of a decoded header array (does not free the array itself)
Definition n_http2.c:460
int n_http2_settings_parse(const unsigned char *payload, size_t len, N_H2_SETTING *out, size_t max, size_t *count)
parse a SETTINGS frame payload (6-byte id/value entries) into out.
Definition n_http2.c:70
int n_http2_frame_parse(const unsigned char *buf, size_t len, N_H2_FRAME *frame, size_t *consumed)
parse one HTTP/2 frame from a byte buffer (stateless).
Definition n_http2.c:32
size_t n_http2_frame_build_header(unsigned char *out, size_t out_cap, uint32_t length, int type, int flags, uint32_t stream_id)
write a 9-byte HTTP/2 frame header into out.
Definition n_http2.c:52
HPACK codec context: the dynamic table (index 0 = most recently added).
Definition n_http2.c:182
char * value
header field value (owned after decode)
Definition n_http2.h:125
#define N_H2_PREFACE
the client connection preface sent before any HTTP/2 frame (RFC 7540 3.5)
Definition n_http2.h:51
#define N_H2_FLAG_END_HEADERS
HEADERS/PUSH_PROMISE/CONTINUATION: header block complete.
Definition n_http2.h:75
#define N_H2_FLAG_PADDED
DATA/HEADERS/PUSH_PROMISE: a pad-length prefix is present.
Definition n_http2.h:76
#define N_H2_RST_STREAM
RST_STREAM.
Definition n_http2.h:62
#define N_H2_FRAME_HEADER_LEN
length of the fixed frame header in bytes
Definition n_http2.h:53
#define N_H2_FLAG_ACK
SETTINGS/PING: acknowledgement.
Definition n_http2.h:74
uint32_t value
parameter value
Definition n_http2.h:102
char * name
header field name (owned after decode)
Definition n_http2.h:124
#define N_H2_SETTINGS_INITIAL_WINDOW_SIZE
Definition n_http2.h:85
#define N_H2_WINDOW_UPDATE
WINDOW_UPDATE.
Definition n_http2.h:67
#define N_H2_PRIORITY
PRIORITY.
Definition n_http2.h:61
#define N_H2_DATA
DATA.
Definition n_http2.h:59
#define N_H2_SETTINGS_MAX_FRAME_SIZE
Definition n_http2.h:86
uint32_t length
payload length (24-bit)
Definition n_http2.h:92
#define N_H2_CONTINUATION
CONTINUATION.
Definition n_http2.h:68
const unsigned char * payload
pointer into the input buffer (may be NULL when length is 0)
Definition n_http2.h:96
int type
frame type (N_H2_*)
Definition n_http2.h:93
#define N_H2_SETTINGS_ENABLE_PUSH
Definition n_http2.h:83
int flags
frame flags
Definition n_http2.h:94
#define N_H2_SETTINGS
SETTINGS.
Definition n_http2.h:63
#define N_H2_FLAG_PRIORITY
HEADERS: priority fields are present.
Definition n_http2.h:77
uint32_t stream_id
stream identifier (31-bit; the reserved bit is cleared)
Definition n_http2.h:95
#define N_H2_HEADERS
HEADERS.
Definition n_http2.h:60
#define N_H2_FLAG_END_STREAM
DATA/HEADERS: last frame of the stream.
Definition n_http2.h:73
#define N_H2_PUSH_PROMISE
PUSH_PROMISE.
Definition n_http2.h:64
#define N_H2_SETTINGS_MAX_CONCURRENT_STREAMS
Definition n_http2.h:84
#define N_H2_GOAWAY
GOAWAY.
Definition n_http2.h:66
#define N_H2_MAX_FRAME_PAYLOAD
largest frame payload the parser accepts (the default SETTINGS_MAX_FRAME_SIZE)
Definition n_http2.h:55
#define N_H2_PING
PING.
Definition n_http2.h:65
a parsed HTTP/2 frame; payload aliases the input buffer
Definition n_http2.h:91
one HPACK header field; after a decode both strings are owned (heap)
Definition n_http2.h:123
one SETTINGS parameter (identifier and value)
Definition n_http2.h:100
static int srv_apply_settings(N_H2_SERVER *s, const N_H2_FRAME *fr)
apply a peer SETTINGS payload to our send-side state
size_t cap
bytes allocated
int eof
the peer closed the connection
H2BUF rb
the wire read buffer
n_h2_srv_read_fn rd
read callback
static H2_STREAM * srv_next_ready(N_H2_SERVER *s)
the lowest-numbered stream whose request is complete and undelivered
static void h2buf_free(H2BUF *b)
release a buffer's storage
static void h2buf_shift(H2BUF *b, size_t n)
drop the first n bytes of b
int io_failed
a read or write callback failed
int delivered
the request was handed to the callback
static int srv_next_frame(N_H2_SERVER *s, N_H2_FRAME *fr, size_t *consumed)
read the next complete frame into fr (aliasing the read buffer).
int n_http2_server_respond(N_H2_SERVER *s, uint32_t stream_id, const N_H2_HEADER *headers, size_t nheaders, const unsigned char *body, size_t body_len)
write a complete response on a stream (HEADERS then DATA, END_STREAM)
#define H2_SRV_RECV_WINDOW
the connection and stream receive window we advertise
int in_use
1 when the slot holds a stream
int n_http2_server_run(N_H2_SERVER *s, n_h2_srv_request_cb cb, void *user_data)
serve the connection until it ends
static int srv_finish_headers(N_H2_SERVER *s)
HPACK-decode the accumulated header block onto its stream.
n_h2_srv_write_fn wr
write callback
const char * err
the reason the connection failed, or NULL
#define H2_DEFAULT_WINDOW
the initial connection flow-control window, fixed by RFC 7540 6.9.2
int settings_acked
the peer acknowledged our SETTINGS
int goaway_recv
the peer sent GOAWAY
uint32_t id
the stream identifier
H2BUF hblock
header-block fragment accumulator
int64_t peer_initial_win
the peer's SETTINGS_INITIAL_WINDOW_SIZE
static void srv_put_setting(unsigned char *out, uint16_t id, uint32_t value)
write one 6-byte SETTINGS entry (identifier then value) into out
void * io
opaque I/O handle for the callbacks
int headers_done
END_HEADERS seen: the header block decoded.
int reset
RST_STREAM received, or the request was refused.
H2_STREAM streams[128]
the stream table
static H2_STREAM * srv_stream_find(N_H2_SERVER *s, uint32_t id)
the stream with this id, or NULL
static int srv_window_update(N_H2_SERVER *s, uint32_t sid, uint32_t inc)
send a WINDOW_UPDATE for sid with the given increment
static int srv_need(N_H2_SERVER *s, size_t n)
make sure at least n bytes are buffered.
static int srv_apply_headers(N_H2_SERVER *s, const N_H2_FRAME *fr)
take one HEADERS or CONTINUATION frame
N_H2_SERVER * n_http2_server_new(void *io, n_h2_srv_read_fn rd, n_h2_srv_write_fn wr)
create a server connection over the given I/O callbacks
static int h2buf_append(H2BUF *b, const unsigned char *p, size_t n)
append n bytes to b, growing it as needed.
#define H2_WIN_MAX
the largest value a flow-control window may hold (RFC 7540 6.9.1)
H2BUF body
accumulated request body
int n_http2_server_goaway(N_H2_SERVER *s, uint32_t error_code)
send GOAWAY, naming the last stream we processed
void n_http2_server_free(N_H2_SERVER **srv)
free a server connection and NULL the caller's pointer
int n_http2_server_rst_stream(N_H2_SERVER *s, uint32_t stream_id, uint32_t error_code)
abort one stream with RST_STREAM
int64_t send_win
our remaining send window for this stream
size_t len
bytes held
static H2_STREAM * srv_stream_open(N_H2_SERVER *s, uint32_t id)
open a stream, or NULL when the table is full
size_t nhdrs
number of decoded headers
static int srv_pump_one(N_H2_SERVER *s)
read and handle one frame.
unsigned char * data
storage, or NULL when empty
#define H2_DEFAULT_MAX_FRAME
the initial SETTINGS_MAX_FRAME_SIZE, fixed by RFC 7540 6.5.2
size_t nrequests
requests delivered to the callback
static int srv_headers_payload(const N_H2_FRAME *fr, const unsigned char **p_out, size_t *len_out)
strip the pad-length prefix and priority fields of a HEADERS payload.
static int srv_send(N_H2_SERVER *s, int type, int flags, uint32_t sid, const unsigned char *payload, size_t len)
write one frame (header then payload).
const char * n_http2_server_error(const N_H2_SERVER *s)
the reason the connection failed, or NULL when none was recorded
size_t n_http2_server_request_count(const N_H2_SERVER *s)
the number of requests delivered to the callback so far
N_H2_HPACK * dec
HPACK decoder for the client-to-server direction.
uint32_t last_stream_id
the highest client stream id seen
int hblock_end_stream
END_STREAM of the HEADERS that opened the block.
static int srv_apply_window_update(N_H2_SERVER *s, const N_H2_FRAME *fr)
add a WINDOW_UPDATE increment to the matching send window
size_t peer_max_frame
the peer's SETTINGS_MAX_FRAME_SIZE
int n_http2_server_handshake(N_H2_SERVER *s)
read and validate the client connection preface, then send ours
int end_stream
END_STREAM seen: the request is complete.
static int srv_apply_data(N_H2_SERVER *s, const N_H2_FRAME *fr)
take one DATA frame onto its stream and replenish the receive windows
static int srv_handle_frame(N_H2_SERVER *s, const N_H2_FRAME *fr)
handle one received frame.
int64_t send_win_conn
our remaining connection send window
static void srv_stream_close(H2_STREAM *st)
release a stream slot and everything it owns
uint32_t hblock_sid
the stream whose header block is in progress, 0 = none
static int srv_fill(N_H2_SERVER *s)
pull one chunk from the peer into the read buffer.
N_H2_HEADER * hdrs
decoded request headers (owned), or NULL
one client-initiated stream
a growable byte buffer for the read stream and the per-stream accumulators
server connection state
HTTP/2 (RFC 7540) server-side connection and stream state machine.
const char * authority
the :authority pseudo-header, or NULL when absent
size_t nheaders
number of header fields
uint32_t stream_id
the stream the request arrived on
ssize_t(* n_h2_srv_read_fn)(void *io, void *buf, size_t len)
read up to len bytes into buf: returns the bytes read (>0), 0 on a clean end of stream,...
#define N_H2_ERR_REFUSED_STREAM
stream not processed
#define N_H2_ERR_NO_ERROR
graceful shutdown
#define N_H2_ERR_ENHANCE_YOUR_CALM
processing capacity exceeded
#define N_H2_SRV_MAX_BODY
largest request body accumulated for one stream
ssize_t(* n_h2_srv_write_fn)(void *io, const void *buf, size_t len)
write exactly len bytes from buf: returns len on success, -1 on error
#define N_H2_SRV_MAX_STREAMS
largest number of streams tracked at once on one connection
const char * method
the :method pseudo-header, or NULL when absent
N_H2_HEADER * headers
the decoded header fields, pseudo-headers included
size_t body_len
the body length in bytes
#define N_H2_SRV_MAX_HEADERS
largest number of header fields decoded from one request
const char * scheme
the :scheme pseudo-header, or NULL when absent
int(* n_h2_srv_request_cb)(void *user_data, N_H2_SERVER *srv, const N_H2_REQUEST *req)
called with each complete request; answer it with n_http2_server_respond() (or n_http2_server_rst_str...
const char * path
the :path pseudo-header, or NULL when absent
const unsigned char * body
the request body, or NULL when there is none
#define N_H2_SRV_READ_CHUNK
bytes pulled from the read callback per call
one complete request received on a stream; every pointer is owned by the server and valid only for th...
Generic log system.