From: Tomas Krizek Date: Mon, 17 Aug 2020 14:22:06 +0000 (+0200) Subject: daemon/http: refactor code style X-Git-Tag: v5.2.0~15^2~36 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=27975d505d340675878da43ffa99a9e84226c6d4;p=thirdparty%2Fknot-resolver.git daemon/http: refactor code style --- diff --git a/daemon/http.c b/daemon/http.c index 8c6748615..0837b867a 100644 --- a/daemon/http.c +++ b/daemon/http.c @@ -39,6 +39,9 @@ struct http_data_buffer { size_t pos; }; +/* + * Write HTTP/2 data to underlying transport layer. + */ static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) { @@ -98,9 +101,8 @@ static int header_callback(nghttp2_session *session, const nghttp2_frame *frame, /* If the HEADERS don't have END_STREAM set, there are some DATA frames, * which implies POST method. Skip header processing for POST. */ - if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) return 0; - } /* If there is incomplete data in the buffer, we can't process the new stream. */ if (ctx->incomplete_stream) { @@ -116,55 +118,66 @@ static int header_callback(nghttp2_session *session, const nghttp2_frame *frame, return 0; } -/* This method is called for data received via POST. */ -static int data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) +/* + * Process DATA chunk sent by the client (by POST method). + * + * We use a single DNS message buffer for the entire connection. Therefore, we + * don't support interweaving DATA chunks from different streams. To successfully + * parse multiple subsequent streams, each one must be fully received before + * processing a new stream. + */ +static int data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, + const uint8_t *data, size_t len, void *user_data) { struct http_ctx *ctx = (struct http_ctx *)user_data; + ssize_t remaining; + ssize_t required; if (ctx->incomplete_stream) { if (queue_len(ctx->streams) <= 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } else if (queue_tail(ctx->streams) != stream_id) { - /* If the received DATA chunk is from a new stream and the previous - * one still has unfinished DATA, refuse the new stream. */ - kr_log_verbose("[http] refusing http DATA chunk, other stream has incomplete DATA\n"); - nghttp2_submit_rst_stream( - session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_REFUSED_STREAM); + kr_log_verbose("[http] previous stream incomplete, refusing\n"); + refuse_stream(session, stream_id); return 0; } } - /* Check message and its length can still fit into the wire buffer. */ - ssize_t remaining = ctx->buf_size - ctx->submitted - ctx->buf_pos; - ssize_t required = len + sizeof(uint16_t); + remaining = ctx->buf_size - ctx->submitted - ctx->buf_pos; + required = len + sizeof(uint16_t); if (required > remaining) { - kr_log_error( - "[http] insufficient space in buffer: remaining %zd B, required %zd B\n", - remaining, required); + kr_log_error("[http] insufficient space in buffer\n"); return NGHTTP2_ERR_CALLBACK_FAILURE; } if (!ctx->incomplete_stream) { ctx->incomplete_stream = true; + ctx->buf_pos = sizeof(uint16_t); /* Reserve 2B for dnsmsg len. */ queue_push(ctx->streams, stream_id); - - /* 2B at the start of buffer is reserved for message length. */ - ctx->buf_pos = sizeof(uint16_t); } + memcpy(ctx->buf + ctx->buf_pos, data, len); ctx->buf_pos += len; - return 0; } +/* + * Finalize existing buffer upon receiving the last frame in the stream. + * + * For GET, this would be HEADERS frame. + * For POST, it is a DATA frame. + * + * Unrelated frames (such as SETTINGS) are ignored (no data was buffered). + */ static int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { struct http_ctx *ctx = (struct http_ctx *)user_data; + ssize_t len; if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) && ctx->buf_pos != 0) { ctx->incomplete_stream = false; - ssize_t len = ctx->buf_pos - sizeof(uint16_t); + len = ctx->buf_pos - sizeof(uint16_t); if (len <= 0 || len > KNOT_WIRE_MAX_PKTSIZE) { kr_log_verbose("[http] invalid dnsmsg size: %zd B\n", len); return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -179,18 +192,28 @@ static int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame return 0; } +/* + * Setup and initialize connection with new HTTP/2 context. + */ struct http_ctx* http_new(http_send_callback cb, void *user_ctx) { assert(cb != NULL); nghttp2_session_callbacks *callbacks; + struct http_ctx *ctx; + static const nghttp2_settings_entry iv[] = { + { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, HTTP_MAX_CONCURRENT_STREAMS } + }; + nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, header_callback); - nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, data_chunk_recv_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, data_chunk_recv_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, on_frame_recv_callback); - struct http_ctx *ctx = calloc(1UL, sizeof(struct http_ctx)); + ctx = calloc(1UL, sizeof(struct http_ctx)); ctx->send_cb = cb; ctx->user_ctx = user_ctx; queue_init(ctx->streams); @@ -200,42 +223,53 @@ struct http_ctx* http_new(http_send_callback cb, void *user_ctx) nghttp2_session_server_new(&ctx->session, callbacks, ctx); nghttp2_session_callbacks_del(callbacks); - static const nghttp2_settings_entry iv[] = { - { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, HTTP_MAX_CONCURRENT_STREAMS } - }; - - nghttp2_submit_settings(ctx->session, NGHTTP2_FLAG_NONE, iv, sizeof(iv)/sizeof(*iv) ); + nghttp2_submit_settings(ctx->session, NGHTTP2_FLAG_NONE, + iv, sizeof(iv)/sizeof(*iv)); return ctx; } +/* + * Process inbound HTTP/2 data and return number of bytes read into session wire buffer. + * + * This function may trigger outgoing HTTP/2 data, such as stream resets, window updates etc. + */ ssize_t http_process_input_data(struct session *s, const uint8_t *in_buf, ssize_t in_buf_len) { - struct http_ctx *http_p = session_http_get_server_ctx(s); - if (!http_p->session) { + struct http_ctx *ctx = session_http_get_server_ctx(s); + ssize_t ret = 0; + + if (!ctx->session) // TODO session vs h2; assert session equals return kr_error(ENOSYS); - } - http_p->submitted = 0; - http_p->buf = session_wirebuf_get_free_start(s); - http_p->buf_pos = 0; - http_p->buf_size = session_wirebuf_get_free_size(s); + ctx->submitted = 0; + ctx->buf = session_wirebuf_get_free_start(s); + ctx->buf_pos = 0; + ctx->buf_size = session_wirebuf_get_free_size(s); - ssize_t ret = 0; - if ((ret = nghttp2_session_mem_recv(http_p->session, in_buf, in_buf_len)) < 0) { - kr_log_error("[http] nghttp2_session_mem_recv failed: %s (%zd)\n", nghttp2_strerror(ret), ret); + ret = nghttp2_session_mem_recv(ctx->session, in_buf, in_buf_len); + if (ret < 0) { + kr_log_error("[http] nghttp2_session_mem_recv failed: %s (%zd)\n", + nghttp2_strerror(ret), ret); return kr_error(EIO); } - if ((ret = nghttp2_session_send(http_p->session)) < 0) { - kr_log_error("[http] nghttp2_session_send failed: %s (%zd)\n", nghttp2_strerror(ret), ret); + ret = nghttp2_session_send(ctx->session); + if (ret < 0) { + kr_log_error("[http] nghttp2_session_send failed: %s (%zd)\n", + nghttp2_strerror(ret), ret); return kr_error(EIO); } - return http_p->submitted; + return ctx->submitted; } -static ssize_t send_response_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) +/* + * + */ +static ssize_t send_response_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { struct http_data_buffer *buffer = (struct http_data_buffer *)source->ptr; assert(buffer != NULL); @@ -309,12 +343,15 @@ int http_write(uv_write_t *req, uv_handle_t *handle, int32_t stream_id, knot_pkt return kr_ok(); } +/* + * Release HTTP/2 context. + */ void http_free(struct http_ctx *ctx) { - if (ctx == NULL || ctx->session == NULL) { + if (!ctx) return; - } + queue_deinit(ctx->streams); nghttp2_session_del(ctx->session); - ctx->session = NULL; + free(ctx); }