48 unsigned char in[65536];
51 unsigned char out[262144];
57static ssize_t
peer_read(
void* io,
void* buf,
size_t len) {
73static ssize_t
peer_write(
void* io,
const void* buf,
size_t len) {
84 if (p->
in_len + len >
sizeof(p->
in)) {
93static void peer_frame(
PEER* p,
int type,
int flags, uint32_t sid,
const unsigned char* payload,
size_t len) {
109 unsigned char block[4096];
122 b[0] = (
unsigned char)((inc >> 24) & 0x7F);
123 b[1] = (
unsigned char)((inc >> 16) & 0xFF);
124 b[2] = (
unsigned char)((inc >> 8) & 0xFF);
125 b[3] = (
unsigned char)(inc & 0xFF);
131 size_t off = 0, count = 0;
134 while (off < p->out_len) {
142 *total_payload += f.
length;
151 size_t off = 0, seen = 0;
152 while (off < p->out_len) {
157 if (f.
type ==
type && seen++ == nth) {
201 h[0].
name = (
char*)
":status";
203 h[1].
name = (
char*)
"content-type";
204 h[1].
value = (
char*)
"text/plain";
221 memset(&p, 0,
sizeof(p));
239 for (i = 0; i < n; i++)
253 memset(&p, 0,
sizeof(p));
254 peer_put(&p,
"GET / HTTP/1.1\r\nHost: x\r\n\r\n\r\n\r\n\r\n", 33);
260 memset(&p, 0,
sizeof(p));
268 memset(&p, 0,
sizeof(p));
283 unsigned char body[] =
"hello h2";
285 memset(&p, 0,
sizeof(p));
286 memset(&seen, 0,
sizeof(seen));
289 req[0].
name = (
char*)
":method";
290 req[0].
value = (
char*)
"GET";
291 req[1].
name = (
char*)
":scheme";
292 req[1].
value = (
char*)
"https";
293 req[2].
name = (
char*)
":authority";
294 req[2].
value = (
char*)
"example.test";
295 req[3].
name = (
char*)
":path";
296 req[3].
value = (
char*)
"/a?b=c";
331 memset(&p, 0,
sizeof(p));
332 memset(&seen, 0,
sizeof(seen));
333 req[0].
name = (
char*)
":method";
334 req[0].
value = (
char*)
"POST";
335 req[1].
name = (
char*)
":scheme";
336 req[1].
value = (
char*)
"https";
337 req[2].
name = (
char*)
":authority";
338 req[2].
value = (
char*)
"example.test";
339 req[3].
name = (
char*)
":path";
340 req[3].
value = (
char*)
"/submit";
341 req[4].
name = (
char*)
"content-type";
342 req[4].
value = (
char*)
"application/x-www-form-urlencoded";
360 size_t off = 0, conn_inc = 0;
367 uint32_t inc = ((uint32_t)(f.
payload[0] & 0x7F) << 24) | ((uint32_t)f.
payload[1] << 16) | ((uint32_t)f.
payload[2] << 8) | (uint32_t)f.
payload[3];
385 unsigned char block[4096];
388 req[0].
name = (
char*)
":method";
389 req[0].
value = (
char*)
"GET";
390 req[1].
name = (
char*)
":scheme";
391 req[1].
value = (
char*)
"https";
392 req[2].
name = (
char*)
":authority";
393 req[2].
value = (
char*)
"example.test";
394 req[3].
name = (
char*)
":path";
395 req[3].
value = (
char*)
"/split";
403 memset(&p, 0,
sizeof(p));
404 memset(&seen, 0,
sizeof(seen));
416 memset(&p, 0,
sizeof(p));
417 memset(&seen, 0,
sizeof(seen));
430 memset(&p, 0,
sizeof(p));
431 memset(&seen, 0,
sizeof(seen));
449 memset(&p, 0,
sizeof(p));
450 memset(&seen, 0,
sizeof(seen));
451 req[0].
name = (
char*)
":method";
452 req[0].
value = (
char*)
"GET";
453 req[1].
name = (
char*)
":scheme";
454 req[1].
value = (
char*)
"https";
455 req[2].
name = (
char*)
":authority";
456 req[2].
value = (
char*)
"example.test";
457 req[3].
name = (
char*)
":path";
458 req[3].
value = (
char*)
"/one";
461 req[3].
value = (
char*)
"/two";
463 req[3].
value = (
char*)
"/three";
485 unsigned char goaway[8];
487 memset(&p, 0,
sizeof(p));
488 memset(&seen, 0,
sizeof(seen));
489 memset(goaway, 0,
sizeof(goaway));
510 size_t data_bytes = 0;
511 const size_t reply_len = 100000;
512 unsigned char* reply = malloc(reply_len);
518 memset(reply,
'A', reply_len);
519 memset(&p, 0,
sizeof(p));
520 memset(&seen, 0,
sizeof(seen));
523 req[0].
name = (
char*)
":method";
524 req[0].
value = (
char*)
"GET";
525 req[1].
name = (
char*)
":scheme";
526 req[1].
value = (
char*)
"https";
527 req[2].
name = (
char*)
":authority";
528 req[2].
value = (
char*)
"example.test";
529 req[3].
name = (
char*)
":path";
530 req[3].
value = (
char*)
"/big";
542 if (data_bytes != reply_len)
failures++;
555 const size_t reply_len = 100000;
556 unsigned char* reply = malloc(reply_len);
562 memset(reply,
'B', reply_len);
563 memset(&p, 0,
sizeof(p));
564 memset(&seen, 0,
sizeof(seen));
568 req[0].
name = (
char*)
":method";
569 req[0].
value = (
char*)
"GET";
570 req[1].
name = (
char*)
":scheme";
571 req[1].
value = (
char*)
"https";
572 req[2].
name = (
char*)
":authority";
573 req[2].
value = (
char*)
"example.test";
574 req[3].
name = (
char*)
":path";
575 req[3].
value = (
char*)
"/big";
600 memset(&p, 0,
sizeof(p));
601 memset(&seen, 0,
sizeof(seen));
602 req[0].
name = (
char*)
":method";
603 req[0].
value = (
char*)
"GET";
604 req[1].
name = (
char*)
":scheme";
605 req[1].
value = (
char*)
"https";
606 req[2].
name = (
char*)
":authority";
607 req[2].
value = (
char*)
"example.test";
608 req[3].
name = (
char*)
":path";
609 req[3].
value = (
char*)
"/gone";
630 memset(&p, 0,
sizeof(p));
631 memset(&seen, 0,
sizeof(seen));
633 req[0].
name = (
char*)
":method";
634 req[0].
value = (
char*)
"GET";
635 req[1].
name = (
char*)
":scheme";
636 req[1].
value = (
char*)
"https";
637 req[2].
name = (
char*)
":authority";
638 req[2].
value = (
char*)
"example.test";
639 req[3].
name = (
char*)
":path";
640 req[3].
value = (
char*)
"/one";
661 memset(&p, 0,
sizeof(p));
662 memset(&seen, 0,
sizeof(seen));
664 req[0].
name = (
char*)
":method";
665 req[0].
value = (
char*)
"GET";
666 req[1].
name = (
char*)
":scheme";
667 req[1].
value = (
char*)
"https";
668 req[2].
name = (
char*)
":authority";
669 req[2].
value = (
char*)
"example.test";
670 req[3].
name = (
char*)
":path";
671 req[3].
value = (
char*)
"/dribble";
695 memset(&p, 0,
sizeof(p));
696 memset(&seen, 0,
sizeof(seen));
706 while ((opt = getopt(argc, argv,
"hvV:")) != EOF) {
709 if (!strncmp(
"LOG_NULL", optarg, 8))
711 else if (!strncmp(
"LOG_NOTICE", optarg, 10))
713 else if (!strncmp(
"LOG_INFO", optarg, 8))
715 else if (!strncmp(
"LOG_ERR", optarg, 7))
717 else if (!strncmp(
"LOG_DEBUG", optarg, 9))
721 fprintf(stderr,
"ex_http2_server\n");
729int main(
int argc,
char** argv) {
737 int before = failures; \
739 if (failures != before) \
740 n_log(LOG_ERR, "%s: %d failure(s)", #f, failures - before); \
void process_args(int argc, char **argv)
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
static void peer_frame(PEER *p, int type, int flags, uint32_t sid, const unsigned char *payload, size_t len)
static void test_send_window_stall(void)
static int on_request(void *ud, N_H2_SERVER *srv, const N_H2_REQUEST *req)
static void peer_put(PEER *p, const void *buf, size_t len)
static void peer_window_update(PEER *p, uint32_t sid, uint32_t inc)
static void test_dribbled_reads(void)
static void peer_headers(PEER *p, uint32_t sid, const N_H2_HEADER *h, size_t n, int end_stream)
static void test_send_flow_control(void)
unsigned char out[262144]
static void test_callback_stop(void)
static void test_handshake_errors(void)
static void test_post_body(void)
static ssize_t peer_write(void *io, const void *buf, size_t len)
static void test_guards(void)
static ssize_t peer_read(void *io, void *buf, size_t len)
static void test_continuation(void)
static void test_rst_stream(void)
static void peer_prologue(PEER *p)
static void test_handshake(void)
static void test_get(void)
static void test_ping_goaway(void)
static int out_find(const PEER *p, int type, size_t nth, N_H2_FRAME *out)
static void test_multiplex(void)
static size_t out_count(const PEER *p, int type, size_t *total_payload)
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_NOTICE
normal but significant condition
#define LOG_NULL
no log output
#define LOG_INFO
informational
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 ...
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.
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_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.
char * name
header field name (owned after decode)
#define N_H2_SETTINGS_INITIAL_WINDOW_SIZE
#define N_H2_WINDOW_UPDATE
WINDOW_UPDATE.
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
SETTINGS.
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_GOAWAY
GOAWAY.
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)
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)
int n_http2_server_run(N_H2_SERVER *s, n_h2_srv_request_cb cb, void *user_data)
serve the connection until it ends
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
void n_http2_server_free(N_H2_SERVER **srv)
free a server connection and NULL the caller's pointer
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
int n_http2_server_handshake(N_H2_SERVER *s)
read and validate the client connection preface, then send ours
HTTP/2 (RFC 7540) server-side connection and stream state machine.
const char * authority
the :authority pseudo-header, or NULL when absent
uint32_t stream_id
the stream the request arrived on
const char * method
the :method pseudo-header, or NULL when absent
size_t body_len
the body length in bytes
#define N_H2_ERR_CANCEL
stream cancelled
const char * scheme
the :scheme pseudo-header, or NULL when absent
const char * path
the :path pseudo-header, or NULL when absent
const unsigned char * body
the request body, or NULL when there is none
one complete request received on a stream; every pointer is owned by the server and valid only for th...