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
ctx->buf_pos += ret;
struct http_stream stream = {
- .id = stream_id
+ .id = stream_id,
+ .headers = ctx->headers
};
queue_push(ctx->streams, stream);
return 0;
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;
+ }
}
/*
} 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;
}
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);
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);
}
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);
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);
struct http_stream {
int32_t id;
- trie_t *headers;
+ kr_http_header_array_t *headers;
};
typedef queue_t(struct http_stream) queue_http_stream;
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;
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;
struct kr_request_qsource_flags flags;
size_t size;
int32_t stream_id;
+ kr_http_header_array_t *headers;
} qsource;
struct {
unsigned int rtt;
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
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. */
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
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 */
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, ...);