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
97 size_t nc = b->
cap ? b->
cap : 512;
99 while (b->
len + n > nc)
101 nd = realloc(b->
data, nc);
107 memcpy(b->
data + b->
len, p, n);
139 s->
err =
"HTTP/2: frame too large to build";
142 if (s->
wr(s->
io, hdr,
sizeof(hdr)) != (ssize_t)
sizeof(hdr)) {
144 s->
err =
"HTTP/2: write failed";
147 if (len > 0 && s->
wr(s->
io, payload, len) != (ssize_t)len) {
149 s->
err =
"HTTP/2: write failed";
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);
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);
181 ssize_t n = s->
rd(s->
io, tmp,
sizeof(tmp));
188 s->
err =
"HTTP/2: read failed";
192 s->
err =
"HTTP/2: out of memory";
201 while (s->
rb.
len < n) {
218 s->
err =
"HTTP/2: frame exceeds the maximum frame size";
246 memset(st, 0,
sizeof(*st));
265 memset(st, 0,
sizeof(*st));
275 if (!best || st->
id < best->
id)
293 s->
err =
"HTTP/2: HPACK decode failed";
299 if (!st || st->
reset) {
313 s->
err =
"HTTP/2: out of memory";
329 const unsigned char* p = fr->
payload;
362 s->
err =
"HTTP/2: malformed SETTINGS";
365 for (i = 0; i < n; i++) {
367 int64_t nv = (int64_t)set[i].value;
371 s->
err =
"HTTP/2: SETTINGS_INITIAL_WINDOW_SIZE out of range";
382 size_t v = set[i].
value;
397 s->
err =
"HTTP/2: malformed WINDOW_UPDATE";
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];
418 const unsigned char* p = fr->
payload;
425 s->
err =
"HTTP/2: bad padded DATA";
433 s->
err =
"HTTP/2: DATA padding too large";
444 if (!st || st->
reset) {
452 s->
err =
"HTTP/2: out of memory";
464 const unsigned char* p = fr->
payload;
470 s->
err =
"HTTP/2: HEADERS on an invalid stream id";
474 s->
err =
"HTTP/2: bad HEADERS padding or priority";
480 s->
err =
"HTTP/2: HEADERS reopening a closed stream";
498 s->
err =
"HTTP/2: CONTINUATION on the wrong stream";
502 s->
err =
"HTTP/2: out of memory";
515 s->
err =
"HTTP/2: frame interleaved inside a header block";
537 s->
err =
"HTTP/2: malformed PING";
554 s->
err =
"HTTP/2: PUSH_PROMISE from a client";
572 return (r == 0) ? 1 : -1;
583 s = calloc(1,
sizeof(*s));
585 n_log(
LOG_ERR,
"could not allocate an HTTP/2 server context");
618 unsigned char settings[18];
626 s->
err = s->
err ? s->
err :
"HTTP/2: connection closed before the preface";
630 s->
err =
"HTTP/2: bad client connection preface";
648 s->
err = s->
err ? s->
err :
"HTTP/2: connection closed before the client SETTINGS";
652 s->
err =
"HTTP/2: the first client frame was not a SETTINGS";
672 memset(&req, 0,
sizeof(req));
676 for (i = 0; i < st->
nhdrs; i++) {
678 if (!n || n[0] !=
':')
680 if (strcmp(n,
":method") == 0)
682 else if (strcmp(n,
":scheme") == 0)
684 else if (strcmp(n,
":authority") == 0)
686 else if (strcmp(n,
":path") == 0)
693 stop = cb(user_data, s, &req);
726 size_t cap = 64, size, off = 0, i;
727 unsigned char* block;
728 int first = 1, rc = 0;
734 if (!st || st->
reset) {
735 s->
err =
"HTTP/2: response for a stream that is gone";
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;
743 s->
err =
"HTTP/2: out of memory";
749 s->
err =
"HTTP/2: HPACK encode failed";
753 size_t chunk = size - off;
757 if (off + chunk >= size)
759 if (first && body_len == 0)
767 }
while (off < size);
769 if (rc != 0 || body_len == 0)
773 while (off < body_len) {
777 if (!st || st->
reset) {
778 s->
err =
"HTTP/2: the stream was reset while the response was being written";
788 s->
err = s->
err ? s->
err :
"HTTP/2: the peer never reopened the send window";
793 chunk = body_len - off;
796 if ((int64_t)chunk > win)
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);
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);
834 return s ? s->
err : NULL;
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
static void stop(int sig)
#define __n_assert(__ptr, __ret)
macro to assert things
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
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).
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 ...
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...
void n_http2_hpack_free(N_H2_HPACK **pctx)
free an HPACK codec context and NULL the caller's pointer
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)
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.
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).
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.
HPACK codec context: the dynamic table (index 0 = most recently added).
char * value
header field value (owned after decode)
#define N_H2_PREFACE
the client connection preface sent before any HTTP/2 frame (RFC 7540 3.5)
#define N_H2_FLAG_END_HEADERS
HEADERS/PUSH_PROMISE/CONTINUATION: header block complete.
#define N_H2_FLAG_PADDED
DATA/HEADERS/PUSH_PROMISE: a pad-length prefix is present.
#define N_H2_RST_STREAM
RST_STREAM.
#define N_H2_FRAME_HEADER_LEN
length of the fixed frame header in bytes
#define N_H2_FLAG_ACK
SETTINGS/PING: acknowledgement.
uint32_t value
parameter value
char * name
header field name (owned after decode)
#define N_H2_SETTINGS_INITIAL_WINDOW_SIZE
#define N_H2_WINDOW_UPDATE
WINDOW_UPDATE.
#define N_H2_PRIORITY
PRIORITY.
#define N_H2_SETTINGS_MAX_FRAME_SIZE
uint32_t length
payload length (24-bit)
#define N_H2_CONTINUATION
CONTINUATION.
const unsigned char * payload
pointer into the input buffer (may be NULL when length is 0)
int type
frame type (N_H2_*)
#define N_H2_SETTINGS_ENABLE_PUSH
#define N_H2_SETTINGS
SETTINGS.
#define N_H2_FLAG_PRIORITY
HEADERS: priority fields are present.
uint32_t stream_id
stream identifier (31-bit; the reserved bit is cleared)
#define N_H2_HEADERS
HEADERS.
#define N_H2_FLAG_END_STREAM
DATA/HEADERS: last frame of the stream.
#define N_H2_PUSH_PROMISE
PUSH_PROMISE.
#define N_H2_SETTINGS_MAX_CONCURRENT_STREAMS
#define N_H2_GOAWAY
GOAWAY.
#define N_H2_MAX_FRAME_PAYLOAD
largest frame payload the parser accepts (the default SETTINGS_MAX_FRAME_SIZE)
a parsed HTTP/2 frame; payload aliases the input buffer
one HPACK header field; after a decode both strings are owned (heap)
one SETTINGS parameter (identifier and value)
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
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
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...