]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/http: expose HTTP headers to kr_request
authorTomas Krizek <tomas.krizek@nic.cz>
Mon, 5 Apr 2021 17:26:40 +0000 (19:26 +0200)
committerTomas Krizek <tomas.krizek@nic.cz>
Mon, 24 May 2021 12:20:15 +0000 (14:20 +0200)
Add HTTP headers to an optional kr_request.qsource.headers structure.
Headers are stored as name, value string pairs.

The following snippet can be used to access the headers in lua modules:

```
if (req.qsource.headers ~= nil) then
for i = 1, tonumber(req.qsource.headers.len) do
local name = ffi.string(req.qsource.headers.at[i - 1].name)
local value = ffi.string(req.qsource.headers.at[i - 1].value)
print(name, value)
end
end
```

Fixes #616

daemon/http.c
daemon/http.h
daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
daemon/worker.c
lib/resolve.h
lib/utils.h

index ae2c7a33b9e399d7629e941fc78de1144b3cc832..53ac3bb81c6be58d9d6cbc6038b1842669fd1b8e 100644 (file)
@@ -236,7 +236,8 @@ static int process_uri_path(struct http_ctx *ctx, const char* path, int32_t stre
        ctx->buf_pos += ret;
 
        struct http_stream stream = {
-               .id = stream_id
+               .id = stream_id,
+               .headers = ctx->headers
        };
        queue_push(ctx->streams, stream);
        return 0;
@@ -255,6 +256,15 @@ static void http_cleanup_stream(struct http_ctx *ctx)
        ctx->current_method = HTTP_METHOD_NONE;
        free(ctx->uri_path);
        ctx->uri_path = NULL;
+       if (ctx->headers != NULL) {
+               for (int i = 0; i < ctx->headers->len; i++) {
+                       free(ctx->headers->at[i].name);
+                       free(ctx->headers->at[i].value);
+               }
+               array_clear(*ctx->headers);
+               free(ctx->headers);
+               ctx->headers = NULL;
+       }
 }
 
 /*
@@ -282,6 +292,8 @@ static int begin_headers_callback(nghttp2_session *h2, const nghttp2_frame *fram
        } else {
                http_cleanup_stream(ctx);  // Free any leftover data and ensure pristine state
                ctx->incomplete_stream = stream_id;
+               ctx->headers = malloc(sizeof(kr_http_header_array_t));
+               array_init(*ctx->headers);
        }
        return 0;
 }
@@ -309,6 +321,16 @@ static int header_callback(nghttp2_session *h2, const nghttp2_frame *frame,
                return 0;
        }
 
+       /* Store header: TODO only selected. */
+       kr_http_header_array_entry_t header;
+       header.name = malloc(sizeof(*header.name) * (namelen + 1));
+       memcpy(header.name, name, namelen);
+       header.name[namelen] = '\0';
+       header.value = malloc(sizeof(*header.value) * (valuelen + 1));
+       memcpy(header.value, value, valuelen);
+       header.value[valuelen] = '\0';
+       array_push(*ctx->headers, header);
+
        if (!strcasecmp(":path", (const char *)name)) {
                if (check_uri((const char *)value) < 0) {
                        refuse_stream(h2, stream_id);
@@ -375,7 +397,8 @@ static int data_chunk_recv_callback(nghttp2_session *h2, uint8_t flags, int32_t
        if (is_first) {
                ctx->buf_pos = sizeof(uint16_t);  /* Reserve 2B for dnsmsg len. */
                struct http_stream stream = {
-                       .id = stream_id
+                       .id = stream_id,
+                       .headers = ctx->headers
                };
                queue_push(ctx->streams, stream);
        }
@@ -406,6 +429,7 @@ static int on_frame_recv_callback(nghttp2_session *h2, const nghttp2_frame *fram
                                refuse_stream(h2, stream_id);
                        }
                }
+               ctx->headers = NULL;  // Success -> transfer ownership to stream (waiting in wirebuffer)
                http_cleanup_stream(ctx);
 
                len = ctx->buf_pos - sizeof(uint16_t);
@@ -699,6 +723,7 @@ void http_free(struct http_ctx *ctx)
                return;
 
        http_cleanup_stream(ctx);
+       // TODO: queue_pop and check/free all headers (ownership may not have been transferred)
        queue_deinit(ctx->streams);
        nghttp2_session_del(ctx->h2);
        free(ctx);
index ebcd5045c51d6ec1ee5cc1bca9890e91f71ade7a..c5bbb528e15388a17a10318c8ad987c5332788c2 100644 (file)
@@ -26,7 +26,7 @@ typedef ssize_t(*http_send_callback)(const uint8_t *buffer,
 
 struct http_stream {
        int32_t id;
-       trie_t *headers;
+       kr_http_header_array_t *headers;
 };
 
 typedef queue_t(struct http_stream) queue_http_stream;
@@ -46,6 +46,7 @@ struct http_ctx {
        ssize_t submitted;
        http_method_t current_method;
        char *uri_path;
+       kr_http_header_array_t *headers;
        uint8_t *buf;  /* Part of the wire_buf that belongs to current HTTP/2 stream. */
        ssize_t buf_pos;
        ssize_t buf_size;
index 6ae0557b311b4f03427614477fea2d04027fee73..a5413aaad7ac18aff2b3e97af49cec810b63eebe 100644 (file)
@@ -145,6 +145,15 @@ typedef struct {
        size_t len;
        size_t cap;
 } ranked_rr_array_t;
+typedef struct kr_http_header_array_entry {
+       char *name;
+       char *value;
+} kr_http_header_array_entry_t;
+typedef struct {
+       kr_http_header_array_entry_t *at;
+       size_t len;
+       size_t cap;
+} kr_http_header_array_t;
 typedef struct {
        union inaddr *at;
        size_t len;
@@ -188,6 +197,7 @@ struct kr_request {
                struct kr_request_qsource_flags flags;
                size_t size;
                int32_t stream_id;
+               kr_http_header_array_t *headers;
        } qsource;
        struct {
                unsigned int rtt;
index e4745a8a5bf3df4d60bd9677401fca0a2a193b44..52539a5ed1389e88369385c7ba2db7f3bfccd51b 100755 (executable)
@@ -109,6 +109,8 @@ ${CDEFS} ${LIBKRES} types <<-EOF
        struct kr_qflags
        ranked_rr_array_entry_t
        ranked_rr_array_t
+       kr_http_header_array_entry_t
+       kr_http_header_array_t
        inaddr_array_t
        struct kr_zonecut
        kr_qarray_t
index ce511d3b48e99deccbaa0275fccfeb42366a4ecc..586d6e3035a1e90ff5c6b96c6e6d1d6953af6f77 100644 (file)
@@ -383,10 +383,17 @@ static struct request_ctx *request_create(struct worker_ctx *worker,
                req->qsource.flags.tls = session_flags(session)->has_tls;
                req->qsource.flags.http = session_flags(session)->has_http;
                req->qsource.stream_id = -1;
+               req->qsource.headers = NULL;
 #if ENABLE_DOH2
                if (req->qsource.flags.http) {
                        struct http_ctx *http_ctx = session_http_get_server_ctx(session);
-                       req->qsource.stream_id = queue_head(http_ctx->streams).id;
+                       struct http_stream stream = queue_head(http_ctx->streams);
+                       req->qsource.stream_id = stream.id;
+                       /* Take ownership of HTTP headers. */
+                       if (stream.headers) {
+                               req->qsource.headers = stream.headers;
+                               stream.headers = NULL;
+                       }
                }
 #endif
                /* We need to store a copy of peer address. */
@@ -456,6 +463,15 @@ static void request_free(struct request_ctx *ctx)
                lua_pop(L, 1);
                ctx->req.vars_ref = LUA_NOREF;
        }
+       /* Free HTTP/2 headers for DoH requests. */
+       if (ctx->req.qsource.headers) {  // TODO maybe refactor elsewhere into function
+               for(int i = 0; i < ctx->req.qsource.headers->len; i++) {
+                       free(ctx->req.qsource.headers->at[i].name);
+                       free(ctx->req.qsource.headers->at[i].value);
+               }
+               array_clear(*ctx->req.qsource.headers);
+               free(ctx->req.qsource.headers);
+       }
        /* Make sure to free XDP buffer in case it wasn't sent. */
        if (ctx->req.alloc_wire_cb) {
        #if ENABLE_XDP
index 481cf6b4d77cfdbacaa9613c72067ee594fc8537..0a6ee4cc83999280a9d01b1a8a3131246dac07a7 100644 (file)
@@ -211,6 +211,7 @@ struct kr_request {
                struct kr_request_qsource_flags flags; /**< See definition above. */
                size_t size; /**< query packet size */
                int32_t stream_id; /**< HTTP/2 stream ID for DoH requests */
+               kr_http_header_array_t *headers;  /**< HTTP/2 headers for DoH requests */
        } qsource;
        struct {
                unsigned rtt;                  /**< Current upstream RTT */
index c098bd5a68ee385f9687075dd7828d724bd5d636..cbd0403d8c653cb7f29e14a0e3ec00baa25260c0 100644 (file)
@@ -170,6 +170,14 @@ typedef struct ranked_rr_array_entry ranked_rr_array_entry_t;
 typedef array_t(ranked_rr_array_entry_t *) ranked_rr_array_t;
 /* @endcond */
 
+typedef struct kr_http_header_array_entry {
+       char* name;
+       char* value;
+} kr_http_header_array_entry_t;
+
+/** Array of HTTP headers for DoH. */
+typedef array_t(kr_http_header_array_entry_t) kr_http_header_array_t;
+
 /** Concatenate N strings. */
 KR_EXPORT
 char* kr_strcatdup(unsigned n, ...);