Browse Source

net: http_server: fix header capture on concurrent http2 streams

Concurrent HTTP POST requests on different HTTP2 concurrent streams
require that the client's header_capture_context is re-used to capture
headers on a second stream before all of the body data has been received
(and sent to the application) on the first stream.

As a result, any captured headers must be sent to the application
callback before any headers can be received on a different stream. In
practice this means that for HTTP2 the application callback is called
for the first time on receiving a headers frame, before any data frames
are received. All subsequent application callbacks will not include the
request header data.

While this mechanism is not necessary for HTTP1, it is also updated to
only send headers in the first application callback for consistency.

Fixes #82273

Signed-off-by: Matt Rodgers <mrodgers@witekio.com>
pull/82574/head
Matt Rodgers 7 months ago committed by Anas Nashif
parent
commit
ddaeb1379a
  1. 69
      include/zephyr/net/http/server.h
  2. 4
      samples/net/prometheus/src/main.c
  3. 4
      samples/net/prometheus/src/stats.c
  4. 32
      samples/net/sockets/http_server/src/main.c
  5. 3
      subsys/net/lib/http/headers/server_internal.h
  6. 34
      subsys/net/lib/http/http_server_core.c
  7. 46
      subsys/net/lib/http/http_server_http1.c
  8. 69
      subsys/net/lib/http/http_server_http2.c
  9. 232
      tests/net/lib/http_server/core/src/main.c

69
include/zephyr/net/http/server.h

@ -49,6 +49,14 @@ extern "C" { @@ -49,6 +49,14 @@ extern "C" {
#define HTTP_SERVER_MAX_HEADER_LEN 0
#endif
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
#define HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE CONFIG_HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE
#define HTTP_SERVER_CAPTURE_HEADER_COUNT CONFIG_HTTP_SERVER_CAPTURE_HEADER_COUNT
#else
#define HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE 0
#define HTTP_SERVER_CAPTURE_HEADER_COUNT 0
#endif
#define HTTP2_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
/** @endcond */
@ -162,20 +170,36 @@ enum http_data_status { @@ -162,20 +170,36 @@ enum http_data_status {
HTTP_SERVER_DATA_FINAL = 1,
};
/** @brief Status of captured request headers */
enum http_header_status {
HTTP_HEADER_STATUS_OK, /**< All available headers were successfully captured. */
HTTP_HEADER_STATUS_DROPPED, /**< One or more headers were dropped due to lack of space. */
HTTP_HEADER_STATUS_NONE, /**< No header status is available. */
};
/** @brief HTTP header representation */
struct http_header {
const char *name; /**< Pointer to header name NULL-terminated string. */
const char *value; /**< Pointer to header value NULL-terminated string. */
};
/** @brief HTTP request context */
struct http_request_ctx {
uint8_t *data; /**< HTTP request data */
size_t data_len; /**< Length of HTTP request data */
struct http_header *headers; /**< Array of HTTP request headers */
size_t header_count; /**< Array length of HTTP request headers */
enum http_header_status headers_status; /**< Status of HTTP request headers */
};
/** @brief HTTP response context */
struct http_response_ctx {
enum http_status status; /** HTTP status code to include in response */
const struct http_header *headers; /** Array of HTTP headers */
size_t header_count; /** Length of headers array */
const uint8_t *body; /** Pointer to body data */
size_t body_len; /** Length of body data */
bool final_chunk; /** Flag set to true when the application has no more data to send */
enum http_status status; /**< HTTP status code to include in response */
const struct http_header *headers; /**< Array of HTTP headers */
size_t header_count; /**< Length of headers array */
const uint8_t *body; /**< Pointer to body data */
size_t body_len; /**< Length of body data */
bool final_chunk; /**< Flag set to true when the application has no more data to send */
};
/**
@ -185,20 +209,16 @@ struct http_response_ctx { @@ -185,20 +209,16 @@ struct http_response_ctx {
*
* @param client HTTP context information for this client connection.
* @param status HTTP data status, indicate whether more data is expected or not.
* @param data_buffer Data received.
* @param data_len Amount of data received.
* @param response_ctx
* @param request_ctx Request context structure containing HTTP request data that was received.
* @param response_ctx Response context structure for application to populate with response data.
* @param user_data User specified data.
*
* @return >0 amount of data to be sent to client, let server to call this
* function again when new data is received.
* 0 nothing to sent to client, close the connection
* @return 0 success, server can send any response data provided in the response_ctx.
* <0 error, close the connection.
*/
typedef int (*http_resource_dynamic_cb_t)(struct http_client_ctx *client,
enum http_data_status status,
uint8_t *data_buffer,
size_t data_len,
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx,
void *user_data);
@ -341,20 +361,14 @@ struct http2_frame { @@ -341,20 +361,14 @@ struct http2_frame {
uint8_t padding_len; /**< Frame padding length. */
};
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
/** @brief Status of captured headers */
enum http_header_status {
HTTP_HEADER_STATUS_OK, /**< All available headers were successfully captured. */
HTTP_HEADER_STATUS_DROPPED, /**< One or more headers were dropped due to lack of space. */
};
/** @cond INTERNAL_HIDDEN */
/** @brief Context for capturing HTTP headers */
struct http_header_capture_ctx {
/** Buffer for HTTP headers captured for application use */
unsigned char buffer[CONFIG_HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE];
unsigned char buffer[HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE];
/** Descriptor of each captured HTTP header */
struct http_header headers[CONFIG_HTTP_SERVER_CAPTURE_HEADER_COUNT];
struct http_header headers[HTTP_SERVER_CAPTURE_HEADER_COUNT];
/** Status of captured headers */
enum http_header_status status;
@ -365,15 +379,18 @@ struct http_header_capture_ctx { @@ -365,15 +379,18 @@ struct http_header_capture_ctx {
/** Current position in buffer */
size_t cursor;
/** The HTTP2 stream associated with the current headers */
struct http2_stream_ctx *current_stream;
/** The next HTTP header value should be stored */
bool store_next_value;
};
/** @endcond */
/** @brief HTTP header name representation */
struct http_header_name {
const char *name; /**< Pointer to header name NULL-terminated string. */
};
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
/**
* @brief Representation of an HTTP client connected to the server.
@ -418,10 +435,8 @@ struct http_client_ctx { @@ -418,10 +435,8 @@ struct http_client_ctx {
/** HTTP/1 parser context. */
struct http_parser parser;
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
/** Header capture context */
struct http_header_capture_ctx header_capture_ctx;
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
/** Request URL. */
unsigned char url_buffer[HTTP_SERVER_MAX_URL_LENGTH];
@ -478,7 +493,6 @@ struct http_client_ctx { @@ -478,7 +493,6 @@ struct http_client_ctx {
bool expect_continuation : 1;
};
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
/**
* @brief Register an HTTP request header to be captured by the server
*
@ -493,7 +507,6 @@ struct http_client_ctx { @@ -493,7 +507,6 @@ struct http_client_ctx {
static const STRUCT_SECTION_ITERABLE(http_header_name, _id) = { \
.name = _id##_str, \
}
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
/** @brief Start the HTTP2 server.
*

4
samples/net/prometheus/src/main.c

@ -43,8 +43,8 @@ HTTP_SERVICE_DEFINE(test_http_service, CONFIG_NET_CONFIG_MY_IPV4_ADDR, &test_htt @@ -43,8 +43,8 @@ HTTP_SERVICE_DEFINE(test_http_service, CONFIG_NET_CONFIG_MY_IPV4_ADDR, &test_htt
10, NULL);
static int dyn_handler(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len, struct http_response_ctx *response_ctx,
void *user_data)
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
int ret;
static uint8_t prom_buffer[256];

4
samples/net/prometheus/src/stats.c

@ -33,8 +33,8 @@ static struct prometheus_collector *stats_collector; @@ -33,8 +33,8 @@ static struct prometheus_collector *stats_collector;
static struct prometheus_collector_walk_context walk_ctx;
static int stats_handler(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len, struct http_response_ctx *response_ctx,
void *user_data)
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
int ret;
static uint8_t prom_buffer[1024];

32
samples/net/sockets/http_server/src/main.c

@ -68,8 +68,8 @@ static struct http_resource_detail_static main_js_gz_resource_detail = { @@ -68,8 +68,8 @@ static struct http_resource_detail_static main_js_gz_resource_detail = {
};
static int echo_handler(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len, struct http_response_ctx *response_ctx,
void *user_data)
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
#define MAX_TEMP_PRINT_LEN 32
static char print_str[MAX_TEMP_PRINT_LEN];
@ -84,11 +84,11 @@ static int echo_handler(struct http_client_ctx *client, enum http_data_status st @@ -84,11 +84,11 @@ static int echo_handler(struct http_client_ctx *client, enum http_data_status st
__ASSERT_NO_MSG(buffer != NULL);
processed += len;
processed += request_ctx->data_len;
snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)",
http_method_str(method), len);
LOG_HEXDUMP_DBG(buffer, len, print_str);
snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)", http_method_str(method),
request_ctx->data_len);
LOG_HEXDUMP_DBG(request_ctx->data, request_ctx->data_len, print_str);
if (status == HTTP_SERVER_DATA_FINAL) {
LOG_DBG("All data received (%zd bytes).", processed);
@ -96,8 +96,8 @@ static int echo_handler(struct http_client_ctx *client, enum http_data_status st @@ -96,8 +96,8 @@ static int echo_handler(struct http_client_ctx *client, enum http_data_status st
}
/* Echo data back to client */
response_ctx->body = buffer;
response_ctx->body_len = len;
response_ctx->body = request_ctx->data;
response_ctx->body_len = request_ctx->data_len;
response_ctx->final_chunk = (status == HTTP_SERVER_DATA_FINAL);
return 0;
@ -113,8 +113,8 @@ static struct http_resource_detail_dynamic echo_resource_detail = { @@ -113,8 +113,8 @@ static struct http_resource_detail_dynamic echo_resource_detail = {
};
static int uptime_handler(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len, struct http_response_ctx *response_ctx,
void *user_data)
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
int ret;
static uint8_t uptime_buf[sizeof(STRINGIFY(INT64_MAX))];
@ -172,20 +172,20 @@ static void parse_led_post(uint8_t *buf, size_t len) @@ -172,20 +172,20 @@ static void parse_led_post(uint8_t *buf, size_t len)
}
static int led_handler(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len, struct http_response_ctx *response_ctx,
void *user_data)
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
static uint8_t post_payload_buf[32];
static size_t cursor;
LOG_DBG("LED handler status %d, size %zu", status, len);
LOG_DBG("LED handler status %d, size %zu", status, request_ctx->data_len);
if (status == HTTP_SERVER_DATA_ABORTED) {
cursor = 0;
return 0;
}
if (len + cursor > sizeof(post_payload_buf)) {
if (request_ctx->data_len + cursor > sizeof(post_payload_buf)) {
cursor = 0;
return -ENOMEM;
}
@ -194,8 +194,8 @@ static int led_handler(struct http_client_ctx *client, enum http_data_status sta @@ -194,8 +194,8 @@ static int led_handler(struct http_client_ctx *client, enum http_data_status sta
* chunks (e.g. if the header size was such that the whole HTTP request exceeds the size of
* the client buffer).
*/
memcpy(post_payload_buf + cursor, buffer, len);
cursor += len;
memcpy(post_payload_buf + cursor, request_ctx->data, request_ctx->data_len);
cursor += request_ctx->data_len;
if (status == HTTP_SERVER_DATA_FINAL) {
parse_led_post(post_payload_buf, cursor);

3
subsys/net/lib/http/headers/server_internal.h

@ -51,4 +51,7 @@ int parse_http_frame_header(struct http_client_ctx *client, const uint8_t *buffe @@ -51,4 +51,7 @@ int parse_http_frame_header(struct http_client_ctx *client, const uint8_t *buffe
size_t buflen);
const char *get_frame_type_name(enum http2_frame_type type);
void populate_request_ctx(struct http_request_ctx *req_ctx, uint8_t *data, size_t len,
struct http_header_capture_ctx *header_ctx);
#endif /* HTTP_SERVER_INTERNAL_H_ */

34
subsys/net/lib/http/http_server_core.c

@ -305,6 +305,7 @@ static void client_release_resources(struct http_client_ctx *client) @@ -305,6 +305,7 @@ static void client_release_resources(struct http_client_ctx *client)
{
struct http_resource_detail *detail;
struct http_resource_detail_dynamic *dynamic_detail;
struct http_request_ctx request_ctx;
struct http_response_ctx response_ctx;
HTTP_SERVICE_FOREACH(service) {
@ -331,8 +332,10 @@ static void client_release_resources(struct http_client_ctx *client) @@ -331,8 +332,10 @@ static void client_release_resources(struct http_client_ctx *client)
continue;
}
dynamic_detail->cb(client, HTTP_SERVER_DATA_ABORTED, NULL, 0, &response_ctx,
dynamic_detail->user_data);
populate_request_ctx(&request_ctx, NULL, 0, NULL);
dynamic_detail->cb(client, HTTP_SERVER_DATA_ABORTED, &request_ctx,
&response_ctx, dynamic_detail->user_data);
}
}
}
@ -421,11 +424,11 @@ static int handle_http_preface(struct http_client_ctx *client) @@ -421,11 +424,11 @@ static int handle_http_preface(struct http_client_ctx *client)
return -EAGAIN;
}
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
client->header_capture_ctx.count = 0;
client->header_capture_ctx.cursor = 0;
client->header_capture_ctx.status = HTTP_HEADER_STATUS_OK;
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)) {
client->header_capture_ctx.count = 0;
client->header_capture_ctx.cursor = 0;
client->header_capture_ctx.status = HTTP_HEADER_STATUS_OK;
}
if (strncmp(client->cursor, HTTP2_PREFACE, sizeof(HTTP2_PREFACE) - 1) != 0) {
return enter_http1_request(client);
@ -829,6 +832,23 @@ bool http_response_is_provided(struct http_response_ctx *rsp) @@ -829,6 +832,23 @@ bool http_response_is_provided(struct http_response_ctx *rsp)
return false;
}
void populate_request_ctx(struct http_request_ctx *req_ctx, uint8_t *data, size_t len,
struct http_header_capture_ctx *header_ctx)
{
req_ctx->data = data;
req_ctx->data_len = len;
if (NULL == header_ctx || header_ctx->status == HTTP_HEADER_STATUS_NONE) {
req_ctx->headers = NULL;
req_ctx->header_count = 0;
req_ctx->headers_status = HTTP_HEADER_STATUS_NONE;
} else {
req_ctx->headers = header_ctx->headers;
req_ctx->header_count = header_ctx->count;
req_ctx->headers_status = header_ctx->status;
}
}
int http_server_start(void)
{
if (server_running) {

46
subsys/net/lib/http/http_server_http1.c

@ -260,6 +260,7 @@ static int dynamic_get_req(struct http_resource_detail_dynamic *dynamic_detail, @@ -260,6 +260,7 @@ static int dynamic_get_req(struct http_resource_detail_dynamic *dynamic_detail,
int ret, len;
char *ptr;
enum http_data_status status;
struct http_request_ctx request_ctx;
struct http_response_ctx response_ctx;
/* Start of GET params */
@ -269,8 +270,9 @@ static int dynamic_get_req(struct http_resource_detail_dynamic *dynamic_detail, @@ -269,8 +270,9 @@ static int dynamic_get_req(struct http_resource_detail_dynamic *dynamic_detail,
do {
memset(&response_ctx, 0, sizeof(response_ctx));
populate_request_ctx(&request_ctx, ptr, len, &client->header_capture_ctx);
ret = dynamic_detail->cb(client, status, ptr, len, &response_ctx,
ret = dynamic_detail->cb(client, status, &request_ctx, &response_ctx,
dynamic_detail->user_data);
if (ret < 0) {
return ret;
@ -302,6 +304,7 @@ static int dynamic_post_req(struct http_resource_detail_dynamic *dynamic_detail, @@ -302,6 +304,7 @@ static int dynamic_post_req(struct http_resource_detail_dynamic *dynamic_detail,
int ret;
char *ptr = client->cursor;
enum http_data_status status;
struct http_request_ctx request_ctx;
struct http_response_ctx response_ctx;
if (ptr == NULL) {
@ -315,13 +318,19 @@ static int dynamic_post_req(struct http_resource_detail_dynamic *dynamic_detail, @@ -315,13 +318,19 @@ static int dynamic_post_req(struct http_resource_detail_dynamic *dynamic_detail,
}
memset(&response_ctx, 0, sizeof(response_ctx));
populate_request_ctx(&request_ctx, ptr, client->data_len, &client->header_capture_ctx);
ret = dynamic_detail->cb(client, status, ptr, client->data_len, &response_ctx,
ret = dynamic_detail->cb(client, status, &request_ctx, &response_ctx,
dynamic_detail->user_data);
if (ret < 0) {
return ret;
}
/* Only send request headers in first callback to application. This is not strictly
* necessary for http1, but is done for consistency with the http2 behaviour.
*/
client->header_capture_ctx.status = HTTP_HEADER_STATUS_NONE;
/* For POST the application might not send a response until all data has been received.
* Don't send a default response until the application has had a chance to respond.
*/
@ -335,8 +344,9 @@ static int dynamic_post_req(struct http_resource_detail_dynamic *dynamic_detail, @@ -335,8 +344,9 @@ static int dynamic_post_req(struct http_resource_detail_dynamic *dynamic_detail,
/* Once all data is transferred to application, repeat cb until response is complete */
while (!http_response_is_final(&response_ctx, status) && status == HTTP_SERVER_DATA_FINAL) {
memset(&response_ctx, 0, sizeof(response_ctx));
populate_request_ctx(&request_ctx, ptr, 0, &client->header_capture_ctx);
ret = dynamic_detail->cb(client, status, ptr, 0, &response_ctx,
ret = dynamic_detail->cb(client, status, &request_ctx, &response_ctx,
dynamic_detail->user_data);
if (ret < 0) {
return ret;
@ -544,7 +554,6 @@ not_supported: @@ -544,7 +554,6 @@ not_supported:
return 0;
}
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
static void check_user_request_headers(struct http_header_capture_ctx *ctx, const char *buf)
{
size_t header_len;
@ -579,7 +588,6 @@ static void check_user_request_headers(struct http_header_capture_ctx *ctx, cons @@ -579,7 +588,6 @@ static void check_user_request_headers(struct http_header_capture_ctx *ctx, cons
}
}
}
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
static int on_header_field(struct http_parser *parser, const char *at,
size_t length)
@ -602,9 +610,10 @@ static int on_header_field(struct http_parser *parser, const char *at, @@ -602,9 +610,10 @@ static int on_header_field(struct http_parser *parser, const char *at,
/* This means that the header field is fully parsed,
* and we can use it directly.
*/
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
check_user_request_headers(&ctx->header_capture_ctx, ctx->header_buffer);
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)) {
check_user_request_headers(&ctx->header_capture_ctx,
ctx->header_buffer);
}
if (strcasecmp(ctx->header_buffer, "Upgrade") == 0) {
ctx->has_upgrade_header = true;
@ -621,7 +630,6 @@ static int on_header_field(struct http_parser *parser, const char *at, @@ -621,7 +630,6 @@ static int on_header_field(struct http_parser *parser, const char *at,
return 0;
}
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
static void populate_user_request_header(struct http_header_capture_ctx *ctx, const char *buf)
{
char *dest;
@ -650,7 +658,6 @@ static void populate_user_request_header(struct http_header_capture_ctx *ctx, co @@ -650,7 +658,6 @@ static void populate_user_request_header(struct http_header_capture_ctx *ctx, co
ctx->headers[ctx->count].value = dest;
ctx->count++;
}
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
static int on_header_value(struct http_parser *parser,
const char *at, size_t length)
@ -664,21 +671,22 @@ static int on_header_value(struct http_parser *parser, @@ -664,21 +671,22 @@ static int on_header_value(struct http_parser *parser,
LOG_DBG("Header %s too long (by %zu bytes)", "value",
offset + length - sizeof(ctx->header_buffer) - 1U);
ctx->header_buffer[0] = '\0';
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
if (ctx->header_capture_ctx.store_next_value) {
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) &&
ctx->header_capture_ctx.store_next_value) {
ctx->header_capture_ctx.store_next_value = false;
ctx->header_capture_ctx.status = HTTP_HEADER_STATUS_DROPPED;
}
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
} else {
memcpy(ctx->header_buffer + offset, at, length);
offset += length;
ctx->header_buffer[offset] = '\0';
if (parser->state == s_header_almost_done) {
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
populate_user_request_header(&ctx->header_capture_ctx, ctx->header_buffer);
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)) {
populate_user_request_header(&ctx->header_capture_ctx,
ctx->header_buffer);
}
if (ctx->has_upgrade_header) {
if (strcasecmp(ctx->header_buffer, "h2c") == 0) {
@ -777,9 +785,9 @@ int enter_http1_request(struct http_client_ctx *client) @@ -777,9 +785,9 @@ int enter_http1_request(struct http_client_ctx *client)
client->parser_state = HTTP1_INIT_HEADER_STATE;
client->http1_headers_sent = false;
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
client->header_capture_ctx.store_next_value = false;
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)) {
client->header_capture_ctx.store_next_value = false;
}
memset(client->header_buffer, 0, sizeof(client->header_buffer));
memset(client->url_buffer, 0, sizeof(client->url_buffer));

69
subsys/net/lib/http/http_server_http2.c

@ -563,6 +563,7 @@ static int dynamic_get_req_v2(struct http_resource_detail_dynamic *dynamic_detai @@ -563,6 +563,7 @@ static int dynamic_get_req_v2(struct http_resource_detail_dynamic *dynamic_detai
char *ptr;
struct http2_frame *frame = &client->current_frame;
enum http_data_status status;
struct http_request_ctx request_ctx;
struct http_response_ctx response_ctx;
if (client->current_stream == NULL) {
@ -576,8 +577,9 @@ static int dynamic_get_req_v2(struct http_resource_detail_dynamic *dynamic_detai @@ -576,8 +577,9 @@ static int dynamic_get_req_v2(struct http_resource_detail_dynamic *dynamic_detai
do {
memset(&response_ctx, 0, sizeof(response_ctx));
populate_request_ctx(&request_ctx, ptr, len, &client->header_capture_ctx);
ret = dynamic_detail->cb(client, status, ptr, len, &response_ctx,
ret = dynamic_detail->cb(client, status, &request_ctx, &response_ctx,
dynamic_detail->user_data);
if (ret < 0) {
return ret;
@ -607,14 +609,17 @@ static int dynamic_get_req_v2(struct http_resource_detail_dynamic *dynamic_detai @@ -607,14 +609,17 @@ static int dynamic_get_req_v2(struct http_resource_detail_dynamic *dynamic_detai
}
static int dynamic_post_req_v2(struct http_resource_detail_dynamic *dynamic_detail,
struct http_client_ctx *client)
struct http_client_ctx *client, bool headers_only)
{
int ret = 0;
char *ptr = client->cursor;
size_t data_len;
enum http_data_status status;
struct http2_frame *frame = &client->current_frame;
struct http_request_ctx request_ctx;
struct http_response_ctx response_ctx;
struct http_header_capture_ctx *request_headers_ctx =
headers_only ? &client->header_capture_ctx : NULL;
if (dynamic_detail == NULL) {
return -ENOENT;
@ -624,20 +629,26 @@ static int dynamic_post_req_v2(struct http_resource_detail_dynamic *dynamic_deta @@ -624,20 +629,26 @@ static int dynamic_post_req_v2(struct http_resource_detail_dynamic *dynamic_deta
return -ENOENT;
}
data_len = MIN(frame->length, client->data_len);
frame->length -= data_len;
client->cursor += data_len;
client->data_len -= data_len;
if (headers_only) {
data_len = 0;
} else {
data_len = MIN(frame->length, client->data_len);
frame->length -= data_len;
client->cursor += data_len;
client->data_len -= data_len;
}
if (frame->length == 0 && is_header_flag_set(frame->flags, HTTP2_FLAG_END_STREAM)) {
if (frame->length == 0 && is_header_flag_set(frame->flags, HTTP2_FLAG_END_STREAM) &&
!headers_only) {
status = HTTP_SERVER_DATA_FINAL;
} else {
status = HTTP_SERVER_DATA_MORE;
}
memset(&response_ctx, 0, sizeof(response_ctx));
populate_request_ctx(&request_ctx, ptr, data_len, request_headers_ctx);
ret = dynamic_detail->cb(client, status, ptr, data_len, &response_ctx,
ret = dynamic_detail->cb(client, status, &request_ctx, &response_ctx,
dynamic_detail->user_data);
if (ret < 0) {
return ret;
@ -656,8 +667,9 @@ static int dynamic_post_req_v2(struct http_resource_detail_dynamic *dynamic_deta @@ -656,8 +667,9 @@ static int dynamic_post_req_v2(struct http_resource_detail_dynamic *dynamic_deta
/* Once all data is transferred to application, repeat cb until response is complete */
while (!http_response_is_final(&response_ctx, status) && status == HTTP_SERVER_DATA_FINAL) {
memset(&response_ctx, 0, sizeof(response_ctx));
populate_request_ctx(&request_ctx, ptr, 0, request_headers_ctx);
ret = dynamic_detail->cb(client, status, ptr, 0, &response_ctx,
ret = dynamic_detail->cb(client, status, &request_ctx, &response_ctx,
dynamic_detail->user_data);
if (ret < 0) {
return ret;
@ -736,6 +748,17 @@ static int handle_http2_dynamic_resource( @@ -736,6 +748,17 @@ static int handle_http2_dynamic_resource(
if (user_method & BIT(HTTP_POST)) {
client->current_stream->current_detail =
(struct http_resource_detail *)dynamic_detail;
/* If there are any header fields to pass to the application, call the
* dynamic handler now with the header data so that this may be cleared to
* re-use for any other concurrent streams
*/
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)) {
ret = dynamic_post_req_v2(dynamic_detail, client, true);
if (ret < 0) {
return ret;
}
}
break;
}
@ -840,6 +863,14 @@ static int enter_http_frame_headers_state(struct http_client_ctx *client) @@ -840,6 +863,14 @@ static int enter_http_frame_headers_state(struct http_client_ctx *client)
client->expect_continuation = false;
}
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)) {
/* Reset header capture state for new headers frame */
client->header_capture_ctx.count = 0;
client->header_capture_ctx.cursor = 0;
client->header_capture_ctx.status = HTTP_HEADER_STATUS_OK;
client->header_capture_ctx.current_stream = stream;
}
client->server_state = HTTP_SERVER_FRAME_HEADERS_STATE;
return 0;
@ -1022,8 +1053,8 @@ int handle_http1_to_http2_upgrade(struct http_client_ctx *client) @@ -1022,8 +1053,8 @@ int handle_http1_to_http2_upgrade(struct http_client_ctx *client)
if (client->method == HTTP_POST) {
ret = dynamic_post_req_v2(
(struct http_resource_detail_dynamic *)detail,
client);
(struct http_resource_detail_dynamic *)detail, client,
false);
if (ret < 0) {
goto error;
}
@ -1131,7 +1162,7 @@ int handle_http_frame_data(struct http_client_ctx *client) @@ -1131,7 +1162,7 @@ int handle_http_frame_data(struct http_client_ctx *client)
ret = dynamic_post_req_v2(
(struct http_resource_detail_dynamic *)client->current_stream->current_detail,
client);
client, false);
if (ret < 0 && ret == -ENOENT) {
ret = send_http2_404(client, frame);
}
@ -1176,7 +1207,6 @@ int handle_http_frame_data(struct http_client_ctx *client) @@ -1176,7 +1207,6 @@ int handle_http_frame_data(struct http_client_ctx *client)
return 0;
}
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
static void check_user_request_headers_http2(struct http_header_capture_ctx *ctx,
struct http_hpack_header_buf *hdr_buf)
{
@ -1223,14 +1253,13 @@ static void check_user_request_headers_http2(struct http_header_capture_ctx *ctx @@ -1223,14 +1253,13 @@ static void check_user_request_headers_http2(struct http_header_capture_ctx *ctx
}
}
}
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
static int process_header(struct http_client_ctx *client,
struct http_hpack_header_buf *header)
{
#if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
check_user_request_headers_http2(&client->header_capture_ctx, header);
#endif /* defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS) */
if (IS_ENABLED(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)) {
check_user_request_headers_http2(&client->header_capture_ctx, header);
}
if (header->name_len == (sizeof(":method") - 1) &&
memcmp(header->name, ":method", header->name_len) == 0) {
@ -1344,6 +1373,7 @@ static int handle_incomplete_http_header(struct http_client_ctx *client) @@ -1344,6 +1373,7 @@ static int handle_incomplete_http_header(struct http_client_ctx *client)
static int handle_http_frame_headers_end_stream(struct http_client_ctx *client)
{
struct http2_frame *frame = &client->current_frame;
struct http_request_ctx request_ctx;
struct http_response_ctx response_ctx;
int ret = 0;
@ -1361,9 +1391,10 @@ static int handle_http_frame_headers_end_stream(struct http_client_ctx *client) @@ -1361,9 +1391,10 @@ static int handle_http_frame_headers_end_stream(struct http_client_ctx *client)
client->current_stream->current_detail;
memset(&response_ctx, 0, sizeof(response_ctx));
populate_request_ctx(&request_ctx, NULL, 0, NULL);
ret = dynamic_detail->cb(client, HTTP_SERVER_DATA_FINAL, NULL, 0, &response_ctx,
dynamic_detail->user_data);
ret = dynamic_detail->cb(client, HTTP_SERVER_DATA_FINAL, &request_ctx,
&response_ctx, dynamic_detail->user_data);
if (ret < 0) {
dynamic_detail->holder = NULL;
goto out;

232
tests/net/lib/http_server/core/src/main.c

@ -44,7 +44,18 @@ BUILD_ASSERT(sizeof(long_payload) - 1 > CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE, @@ -44,7 +44,18 @@ BUILD_ASSERT(sizeof(long_payload) - 1 > CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE,
"long_payload should be longer than client buffer to test payload being sent to "
"application across multiple calls to dynamic resource callback");
/* Individual HTTP2 frames, used to compose requests. */
/* Individual HTTP2 frames, used to compose requests.
*
* Headers and data frames can be composed based on a "real" request by copying the frame from a
* wireshark capture (Copy --> ...as a hex stream) and formatting into a C array initializer using
* xxd:
*
* echo "<frame_as_hex_stream>" | xxd -r -p | xxd -i
*
* For example:
* $ echo "01234567" | xxd -r -p | xxd -i
* 0x01, 0x23, 0x45, 0x67
*/
#define TEST_HTTP2_MAGIC \
0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32, \
0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
@ -106,6 +117,22 @@ BUILD_ASSERT(sizeof(long_payload) - 1 > CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE, @@ -106,6 +117,22 @@ BUILD_ASSERT(sizeof(long_payload) - 1 > CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE,
0x9f, 0x87, 0x49, 0x50, 0x98, 0xbb, 0x8e, 0x8b, 0x4b, 0x40, 0x88, 0x49, \
0x50, 0x95, 0xa7, 0x28, 0xe4, 0x2d, 0x82, 0x88, 0x49, 0x50, 0x98, 0xbb, \
0x8e, 0x8b, 0x4a, 0x2f
#define TEST_HTTP2_HEADERS_POST_HEADER_CAPTURE_WITH_TESTHEADER_STREAM_1 \
0x00, 0x00, 0x4b, 0x01, 0x04, 0x00, 0x00, 0x00, TEST_STREAM_ID_1, \
0x83, 0x04, 0x8b, 0x62, 0x72, 0x8e, 0x42, 0xd9, 0x11, 0x07, 0x5a, 0x6d, \
0xb0, 0xbf, 0x86, 0x41, 0x87, 0x0b, 0xe2, 0x5c, 0x0b, 0x89, 0x70, 0xff, \
0x7a, 0x88, 0x25, 0xb6, 0x50, 0xc3, 0xab, 0xbc, 0x15, 0xc1, 0x53, 0x03, \
0x2a, 0x2f, 0x2a, 0x40, 0x88, 0x49, 0x50, 0x95, 0xa7, 0x28, 0xe4, 0x2d, \
0x9f, 0x87, 0x49, 0x50, 0x98, 0xbb, 0x8e, 0x8b, 0x4b, 0x5f, 0x8b, 0x1d, \
0x75, 0xd0, 0x62, 0x0d, 0x26, 0x3d, 0x4c, 0x74, 0x41, 0xea, 0x0f, 0x0d, \
0x02, 0x31, 0x30
#define TEST_HTTP2_HEADERS_POST_HEADER_CAPTURE2_NO_TESTHEADER_STREAM_2 \
0x00, 0x00, 0x39, 0x01, 0x04, 0x00, 0x00, 0x00, TEST_STREAM_ID_2, \
0x83, 0x04, 0x8b, 0x62, 0x72, 0x8e, 0x42, 0xd9, 0x11, 0x07, 0x5a, 0x6d, \
0xb0, 0xa2, 0x86, 0x41, 0x87, 0x0b, 0xe2, 0x5c, 0x0b, 0x89, 0x70, 0xff, \
0x7a, 0x88, 0x25, 0xb6, 0x50, 0xc3, 0xab, 0xbc, 0x15, 0xc1, 0x53, 0x03, \
0x2a, 0x2f, 0x2a, 0x5f, 0x8b, 0x1d, 0x75, 0xd0, 0x62, 0x0d, 0x26, 0x3d, \
0x4c, 0x74, 0x41, 0xea, 0x0f, 0x0d, 0x02, 0x31, 0x30
#define TEST_HTTP2_HEADERS_GET_RESPONSE_HEADERS_STREAM_1 \
0x00, 0x00, 0x28, 0x01, 0x05, 0x00, 0x00, 0x00, TEST_STREAM_ID_1, \
0x82, 0x04, 0x8c, 0x62, 0xc2, 0xa2, 0xb3, 0xd4, 0x82, 0xc5, 0x39, 0x47, \
@ -163,6 +190,12 @@ BUILD_ASSERT(sizeof(long_payload) - 1 > CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE, @@ -163,6 +190,12 @@ BUILD_ASSERT(sizeof(long_payload) - 1 > CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, TEST_STREAM_ID_1, \
0x54, 0x65, 0x73, 0x74, 0x20, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, \
0x20, 0x50, 0x4f, 0x53, 0x54
#define TEST_HTTP2_DATA_POST_HEADER_CAPTURE_STREAM_1 \
0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, TEST_STREAM_ID_1, \
0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x31, 0x7d
#define TEST_HTTP2_DATA_POST_HEADER_CAPTURE_STREAM_2 \
0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, TEST_STREAM_ID_2, \
0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x31, 0x7d
#define TEST_HTTP2_TRAILING_HEADER_STREAM_1 \
0x00, 0x00, 0x0c, 0x01, 0x05, 0x00, 0x00, 0x00, TEST_STREAM_ID_1, \
0x40, 0x84, 0x92, 0xda, 0x69, 0xf5, 0x85, 0x9c, 0xa3, 0x90, 0xb6, 0x7f
@ -190,8 +223,9 @@ HTTP_RESOURCE_DEFINE(static_resource, test_http_service, "/", @@ -190,8 +223,9 @@ HTTP_RESOURCE_DEFINE(static_resource, test_http_service, "/",
static uint8_t dynamic_payload[32];
static size_t dynamic_payload_len = sizeof(dynamic_payload);
static int dynamic_cb(struct http_client_ctx *client, enum http_data_status status, uint8_t *buffer,
size_t len, struct http_response_ctx *response_ctx, void *user_data)
static int dynamic_cb(struct http_client_ctx *client, enum http_data_status status,
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
static size_t offset;
@ -207,13 +241,13 @@ static int dynamic_cb(struct http_client_ctx *client, enum http_data_status stat @@ -207,13 +241,13 @@ static int dynamic_cb(struct http_client_ctx *client, enum http_data_status stat
response_ctx->final_chunk = true;
break;
case HTTP_POST:
if (len + offset > sizeof(dynamic_payload)) {
if (request_ctx->data_len + offset > sizeof(dynamic_payload)) {
return -ENOMEM;
}
if (len > 0) {
memcpy(dynamic_payload + offset, buffer, len);
offset += len;
if (request_ctx->data_len > 0) {
memcpy(dynamic_payload + offset, request_ctx->data, request_ctx->data_len);
offset += request_ctx->data_len;
}
if (status == HTTP_SERVER_DATA_FINAL) {
@ -244,36 +278,43 @@ struct http_resource_detail_dynamic dynamic_detail = { @@ -244,36 +278,43 @@ struct http_resource_detail_dynamic dynamic_detail = {
HTTP_RESOURCE_DEFINE(dynamic_resource, test_http_service, "/dynamic",
&dynamic_detail);
static struct http_header_capture_ctx header_capture_ctx_clone;
struct test_headers_clone {
uint8_t buffer[CONFIG_HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE];
struct http_header headers[CONFIG_HTTP_SERVER_CAPTURE_HEADER_COUNT];
size_t count;
enum http_header_status status;
};
static int dynamic_request_headers_cb(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len,
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
ptrdiff_t offset;
struct http_header *hdrs_src;
struct http_header *hdrs_dst;
struct test_headers_clone *clone = (struct test_headers_clone *)user_data;
if (status == HTTP_SERVER_DATA_FINAL) {
if (request_ctx->header_count != 0) {
/* Copy the captured header info to static buffer for later assertions in testcase.
* Don't assume that the buffer inside client context remains valid after return
* from the callback - this is currently the case at the time of writing but could
* change. Also need to update pointers within structure with an offset to point at
* new buffer.
* from the callback. Also need to update pointers within structure with an offset
* to point at new buffer.
*/
memcpy(&header_capture_ctx_clone, &client->header_capture_ctx,
sizeof(header_capture_ctx_clone));
memcpy(clone->buffer, &client->header_capture_ctx, sizeof(clone->buffer));
hdrs_src = client->header_capture_ctx.headers;
hdrs_dst = header_capture_ctx_clone.headers;
offset = header_capture_ctx_clone.buffer - client->header_capture_ctx.buffer;
clone->count = request_ctx->header_count;
clone->status = request_ctx->headers_status;
for (int i = 0; i < CONFIG_HTTP_SERVER_CAPTURE_HEADER_COUNT; i++) {
hdrs_src = request_ctx->headers;
hdrs_dst = clone->headers;
offset = clone->buffer - client->header_capture_ctx.buffer;
for (int i = 0; i < request_ctx->header_count; i++) {
if (hdrs_src[i].name != NULL) {
hdrs_dst[i].name = hdrs_src[i].name + offset;
}
if (hdrs_dst[i].value != NULL) {
if (hdrs_src[i].value != NULL) {
hdrs_dst[i].value = hdrs_src[i].value + offset;
}
}
@ -282,6 +323,10 @@ static int dynamic_request_headers_cb(struct http_client_ctx *client, enum http_ @@ -282,6 +323,10 @@ static int dynamic_request_headers_cb(struct http_client_ctx *client, enum http_
return 0;
}
/* Define two resources for testing header capture, so that we can check concurrent streams */
static struct test_headers_clone request_headers_clone;
static struct test_headers_clone request_headers_clone2;
struct http_resource_detail_dynamic dynamic_request_headers_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_DYNAMIC,
@ -289,12 +334,25 @@ struct http_resource_detail_dynamic dynamic_request_headers_detail = { @@ -289,12 +334,25 @@ struct http_resource_detail_dynamic dynamic_request_headers_detail = {
.content_type = "text/plain",
},
.cb = dynamic_request_headers_cb,
.user_data = NULL
.user_data = &request_headers_clone,
};
struct http_resource_detail_dynamic dynamic_request_headers_detail2 = {
.common = {
.type = HTTP_RESOURCE_TYPE_DYNAMIC,
.bitmask_of_supported_http_methods = BIT(HTTP_GET) | BIT(HTTP_POST),
.content_type = "text/plain",
},
.cb = dynamic_request_headers_cb,
.user_data = &request_headers_clone2,
};
HTTP_RESOURCE_DEFINE(dynamic_request_headers_resource, test_http_service, "/header_capture",
&dynamic_request_headers_detail);
HTTP_RESOURCE_DEFINE(dynamic_request_headers_resource2, test_http_service, "/header_capture2",
&dynamic_request_headers_detail2);
HTTP_SERVER_REGISTER_HEADER_CAPTURE(capture_user_agent, "User-Agent");
HTTP_SERVER_REGISTER_HEADER_CAPTURE(capture_test_header, "Test-Header");
HTTP_SERVER_REGISTER_HEADER_CAPTURE(capture_test_header2, "Test-Header2");
@ -326,7 +384,7 @@ static uint8_t dynamic_response_headers_variant; @@ -326,7 +384,7 @@ static uint8_t dynamic_response_headers_variant;
static uint8_t dynamic_response_headers_buffer[sizeof(long_payload)];
static int dynamic_response_headers_cb(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len,
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
static bool request_continuation;
@ -340,6 +398,14 @@ static int dynamic_response_headers_cb(struct http_client_ctx *client, enum http @@ -340,6 +398,14 @@ static int dynamic_response_headers_cb(struct http_client_ctx *client, enum http
{.name = "Content-Type", .value = "application/json"},
};
if (status != HTTP_SERVER_DATA_FINAL &&
dynamic_response_headers_variant != DYNAMIC_RESPONSE_HEADERS_VARIANT_BODY_LONG) {
/* Long body variant is the only one which needs to take some action before final
* data has been received from server
*/
return 0;
}
switch (dynamic_response_headers_variant) {
case DYNAMIC_RESPONSE_HEADERS_VARIANT_NONE:
break;
@ -400,10 +466,12 @@ static int dynamic_response_headers_cb(struct http_client_ctx *client, enum http @@ -400,10 +466,12 @@ static int dynamic_response_headers_cb(struct http_client_ctx *client, enum http
}
} else if (client->method == HTTP_POST) {
/* Copy POST payload into buffer for later comparison */
zassert(offset + len <= sizeof(dynamic_response_headers_buffer),
zassert(offset + request_ctx->data_len <=
sizeof(dynamic_response_headers_buffer),
"POST data too long for buffer");
memcpy(dynamic_response_headers_buffer + offset, buffer, len);
offset += len;
memcpy(dynamic_response_headers_buffer + offset, request_ctx->data,
request_ctx->data_len);
offset += request_ctx->data_len;
if (status == HTTP_SERVER_DATA_FINAL) {
offset = 0;
@ -544,10 +612,12 @@ static void expect_http2_headers_frame(size_t *offset, int stream_id, uint8_t fl @@ -544,10 +612,12 @@ static void expect_http2_headers_frame(size_t *offset, int stream_id, uint8_t fl
test_get_frame_header(offset, &frame);
zassert_equal(frame.type, HTTP2_HEADERS_FRAME, "Expected headers frame");
zassert_equal(frame.type, HTTP2_HEADERS_FRAME, "Expected headers frame, got frame type %u",
frame.type);
zassert_equal(frame.stream_identifier, stream_id,
"Invalid headers frame stream ID");
zassert_equal(frame.flags, flags, "Unexpected flags received");
zassert_equal(frame.flags, flags, "Unexpected flags received (expected %x got %x)", flags,
frame.flags);
/* Consume headers payload */
test_read_data(offset, frame.length);
@ -588,7 +658,8 @@ static void expect_http2_window_update_frame(size_t *offset, int stream_id) @@ -588,7 +658,8 @@ static void expect_http2_window_update_frame(size_t *offset, int stream_id)
zassert_equal(frame.type, HTTP2_WINDOW_UPDATE_FRAME,
"Expected window update frame");
zassert_equal(frame.stream_identifier, stream_id,
"Invalid window update frame stream ID");
"Invalid window update frame stream ID (expected %d got %d)", stream_id,
frame.stream_identifier);
zassert_equal(frame.flags, 0, "Unexpected flags received");
zassert_equal(frame.length, sizeof(uint32_t),
"Unexpected window update frame length");
@ -1173,14 +1244,14 @@ ZTEST(server_function_tests, test_http1_header_capture) @@ -1173,14 +1244,14 @@ ZTEST(server_function_tests, test_http1_header_capture)
"Accept: */*\r\n"
"Accept-Encoding: deflate, gzip, br\r\n"
"\r\n";
struct http_header *hdrs = header_capture_ctx_clone.headers;
struct http_header *hdrs = request_headers_clone.headers;
int ret;
test_http1_header_capture_common(request);
zassert_equal(header_capture_ctx_clone.count, 2,
zassert_equal(request_headers_clone.count, 2,
"Didn't capture the expected number of headers");
zassert_equal(header_capture_ctx_clone.status, HTTP_HEADER_STATUS_OK,
zassert_equal(request_headers_clone.status, HTTP_HEADER_STATUS_OK,
"Header capture status was not OK");
zassert_not_equal(hdrs[0].name, NULL, "First header name is NULL");
@ -1207,14 +1278,14 @@ ZTEST(server_function_tests, test_http1_header_too_long) @@ -1207,14 +1278,14 @@ ZTEST(server_function_tests, test_http1_header_too_long)
"Accept: */*\r\n"
"Accept-Encoding: deflate, gzip, br\r\n"
"\r\n";
struct http_header *hdrs = header_capture_ctx_clone.headers;
struct http_header *hdrs = request_headers_clone.headers;
int ret;
test_http1_header_capture_common(request);
zassert_equal(header_capture_ctx_clone.count, 1,
zassert_equal(request_headers_clone.count, 1,
"Didn't capture the expected number of headers");
zassert_equal(header_capture_ctx_clone.status, HTTP_HEADER_STATUS_DROPPED,
zassert_equal(request_headers_clone.status, HTTP_HEADER_STATUS_DROPPED,
"Header capture status was OK, but should not have been");
/* First header too long should not stop second header being captured into first slot */
@ -1236,14 +1307,14 @@ ZTEST(server_function_tests, test_http1_header_too_many) @@ -1236,14 +1307,14 @@ ZTEST(server_function_tests, test_http1_header_too_many)
"Accept: */*\r\n"
"Accept-Encoding: deflate, gzip, br\r\n"
"\r\n";
struct http_header *hdrs = header_capture_ctx_clone.headers;
struct http_header *hdrs = request_headers_clone.headers;
int ret;
test_http1_header_capture_common(request);
zassert_equal(header_capture_ctx_clone.count, 2,
zassert_equal(request_headers_clone.count, 2,
"Didn't capture the expected number of headers");
zassert_equal(header_capture_ctx_clone.status, HTTP_HEADER_STATUS_DROPPED,
zassert_equal(request_headers_clone.status, HTTP_HEADER_STATUS_DROPPED,
"Header capture status OK, but should not have been");
zassert_not_equal(hdrs[0].name, NULL, "First header name is NULL");
@ -1290,14 +1361,14 @@ ZTEST(server_function_tests, test_http2_header_capture) @@ -1290,14 +1361,14 @@ ZTEST(server_function_tests, test_http2_header_capture)
TEST_HTTP2_HEADERS_GET_HEADER_CAPTURE1_STREAM_1,
TEST_HTTP2_GOAWAY,
};
struct http_header *hdrs = header_capture_ctx_clone.headers;
struct http_header *hdrs = request_headers_clone.headers;
int ret;
common_verify_http2_get_header_capture_request(request, sizeof(request));
zassert_equal(header_capture_ctx_clone.count, 2,
zassert_equal(request_headers_clone.count, 2,
"Didn't capture the expected number of headers");
zassert_equal(header_capture_ctx_clone.status, HTTP_HEADER_STATUS_OK,
zassert_equal(request_headers_clone.status, HTTP_HEADER_STATUS_OK,
"Header capture status was not OK");
zassert_not_equal(hdrs[0].name, NULL, "First header name is NULL");
@ -1324,14 +1395,14 @@ ZTEST(server_function_tests, test_http2_header_too_long) @@ -1324,14 +1395,14 @@ ZTEST(server_function_tests, test_http2_header_too_long)
TEST_HTTP2_HEADERS_GET_HEADER_CAPTURE2_STREAM_1,
TEST_HTTP2_GOAWAY,
};
struct http_header *hdrs = header_capture_ctx_clone.headers;
struct http_header *hdrs = request_headers_clone.headers;
int ret;
common_verify_http2_get_header_capture_request(request, sizeof(request));
zassert_equal(header_capture_ctx_clone.count, 1,
zassert_equal(request_headers_clone.count, 1,
"Didn't capture the expected number of headers");
zassert_equal(header_capture_ctx_clone.status, HTTP_HEADER_STATUS_DROPPED,
zassert_equal(request_headers_clone.status, HTTP_HEADER_STATUS_DROPPED,
"Header capture status was OK, but should not have been");
/* First header too long should not stop second header being captured into first slot */
@ -1353,14 +1424,14 @@ ZTEST(server_function_tests, test_http2_header_too_many) @@ -1353,14 +1424,14 @@ ZTEST(server_function_tests, test_http2_header_too_many)
TEST_HTTP2_HEADERS_GET_HEADER_CAPTURE3_STREAM_1,
TEST_HTTP2_GOAWAY,
};
struct http_header *hdrs = header_capture_ctx_clone.headers;
struct http_header *hdrs = request_headers_clone.headers;
int ret;
common_verify_http2_get_header_capture_request(request, sizeof(request));
zassert_equal(header_capture_ctx_clone.count, 2,
zassert_equal(request_headers_clone.count, 2,
"Didn't capture the expected number of headers");
zassert_equal(header_capture_ctx_clone.status, HTTP_HEADER_STATUS_DROPPED,
zassert_equal(request_headers_clone.status, HTTP_HEADER_STATUS_DROPPED,
"Header capture status OK, but should not have been");
zassert_not_equal(hdrs[0].name, NULL, "First header name is NULL");
@ -1378,6 +1449,73 @@ ZTEST(server_function_tests, test_http2_header_too_many) @@ -1378,6 +1449,73 @@ ZTEST(server_function_tests, test_http2_header_too_many)
zassert_equal(0, ret, "Header strings did not match");
}
ZTEST(server_function_tests, test_http2_header_concurrent)
{
/* Two POST requests which are concurrent, ie. headers1, headers2, data1, data2 */
static const uint8_t request[] = {
TEST_HTTP2_MAGIC,
TEST_HTTP2_SETTINGS,
TEST_HTTP2_SETTINGS_ACK,
TEST_HTTP2_HEADERS_POST_HEADER_CAPTURE_WITH_TESTHEADER_STREAM_1,
TEST_HTTP2_HEADERS_POST_HEADER_CAPTURE2_NO_TESTHEADER_STREAM_2,
TEST_HTTP2_DATA_POST_HEADER_CAPTURE_STREAM_1,
TEST_HTTP2_DATA_POST_HEADER_CAPTURE_STREAM_2,
TEST_HTTP2_GOAWAY,
};
struct http_header *hdrs = request_headers_clone.headers;
struct http_header *hdrs2 = request_headers_clone2.headers;
int ret;
size_t offset = 0;
ret = zsock_send(client_fd, request, sizeof(request), 0);
zassert_not_equal(ret, -1, "send() failed (%d)", errno);
/* Wait for response on both resources before checking captured headers */
expect_http2_settings_frame(&offset, false);
expect_http2_settings_frame(&offset, true);
expect_http2_headers_frame(&offset, TEST_STREAM_ID_1,
HTTP2_FLAG_END_HEADERS | HTTP2_FLAG_END_STREAM, NULL, 0);
expect_http2_window_update_frame(&offset, TEST_STREAM_ID_1);
expect_http2_window_update_frame(&offset, 0);
expect_http2_headers_frame(&offset, TEST_STREAM_ID_2,
HTTP2_FLAG_END_HEADERS | HTTP2_FLAG_END_STREAM, NULL, 0);
/* Headers captured on /header_capture path should have two headers including the
* Test-Header
*/
zassert_equal(request_headers_clone.count, 2,
"Didn't capture the expected number of headers");
zassert_not_equal(hdrs[0].name, NULL, "First header name is NULL");
zassert_not_equal(hdrs[0].value, NULL, "First header value is NULL");
zassert_not_equal(hdrs[1].name, NULL, "Second header name is NULL");
zassert_not_equal(hdrs[1].value, NULL, "Second header value is NULL");
ret = strcmp(hdrs[0].name, "User-Agent");
zassert_equal(0, ret, "Header strings did not match");
ret = strcmp(hdrs[0].value, "curl/7.81.0");
zassert_equal(0, ret, "Header strings did not match");
ret = strcmp(hdrs[1].name, "Test-Header");
zassert_equal(0, ret, "Header strings did not match");
ret = strcmp(hdrs[1].value, "test_value");
zassert_equal(0, ret, "Header strings did not match");
/* Headers captured on the /header_capture2 path should have only one header, not including
* the Test-Header
*/
zassert_equal(request_headers_clone2.count, 1,
"Didn't capture the expected number of headers");
zassert_not_equal(hdrs2[0].name, NULL, "First header name is NULL");
zassert_not_equal(hdrs2[0].value, NULL, "First header value is NULL");
ret = strcmp(hdrs2[0].name, "User-Agent");
zassert_equal(0, ret, "Header strings did not match");
ret = strcmp(hdrs2[0].value, "curl/7.81.0");
zassert_equal(0, ret, "Header strings did not match");
}
static void test_http1_dynamic_response_headers(const char *request, const char *expected_response)
{
int ret;
@ -1982,6 +2120,8 @@ static void http_server_tests_before(void *fixture) @@ -1982,6 +2120,8 @@ static void http_server_tests_before(void *fixture)
memset(dynamic_payload, 0, sizeof(dynamic_payload));
memset(dynamic_response_headers_buffer, 0, sizeof(dynamic_response_headers_buffer));
memset(&request_headers_clone, 0, sizeof(request_headers_clone));
memset(&request_headers_clone2, 0, sizeof(request_headers_clone2));
dynamic_payload_len = 0;
ret = http_server_start();

Loading…
Cancel
Save