Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/gateway/asap_http_body.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ int asap_http_body_parse_content_length(const char *cl_buf, long *cl_out)
return 0;
}

int asap_http_body_exceeds_static_cap(const asap_http_body_t *body, long content_length)
{
if (!body || body->use_dyn_body)
return 0;
return content_length > (long)BODY_BUF_SIZE;
}

int asap_http_body_init_from_request(struct lws *wsi, asap_http_body_t *body)
{
char cl_buf[32] = {0};
Expand Down
12 changes: 12 additions & 0 deletions src/gateway/asap_http_body.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ typedef struct asap_http_body {
*/
int asap_http_body_parse_content_length(const char *cl_buf, long *cl_out);

/**
* True when Content-Length exceeds the static POST/PUT cap (BODY_BUF_SIZE).
* POST /asap uses a 1 MiB dynamic buffer (use_dyn_body); skip the static cap
* so envelopes between 64 KiB and ASAP_BODY_MAX are not 413'd.
*
* Example: asap_http_body_exceeds_static_cap(&body, 70000) is 0 when
* body.use_dyn_body is set, and 1 for the default static buffer.
*
* @return 1 if the static cap applies and is exceeded; 0 otherwise.
*/
int asap_http_body_exceeds_static_cap(const asap_http_body_t *body, long content_length);

/**
* For POST /asap: validate Content-Length and allocate dynamic buffer.
* @return 0 ok, -1 body too large, -2 allocation failure; non-/asap returns 0.
Expand Down
3 changes: 2 additions & 1 deletion src/gateway/http_lws.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ int http_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user,
lws_callback_on_writable(wsi);
} else {
long cl = 0;
if (http_body_content_length(wsi, &cl) == 0 && cl > (long)BODY_BUF_SIZE)
if (http_body_content_length(wsi, &cl) == 0 &&
asap_http_body_exceeds_static_cap(&conn->body, cl))
conn->body.body_too_large = 1;
conn->body.body[0] = '\0';
conn->body.body_len = 0;
Expand Down
18 changes: 18 additions & 0 deletions tests/test_asap_http_body.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "gateway/asap_http_body.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ASSERT(c) do { \
Expand Down Expand Up @@ -67,6 +68,19 @@ static int test_static_append_sets_too_large(void)
return 0;
}

static int test_exceeds_static_cap(void)
{
asap_http_body_t body;

memset(&body, 0, sizeof(body));
ASSERT(asap_http_body_exceeds_static_cap(NULL, (long)BODY_BUF_SIZE + 1) == 0);
ASSERT(asap_http_body_exceeds_static_cap(&body, (long)BODY_BUF_SIZE) == 0);
ASSERT(asap_http_body_exceeds_static_cap(&body, (long)BODY_BUF_SIZE + 1) == 1);
body.use_dyn_body = 1;
ASSERT(asap_http_body_exceeds_static_cap(&body, (long)BODY_BUF_SIZE + 1) == 0);
return 0;
}

int main(void)
{
int failed = 0;
Expand All @@ -86,6 +100,10 @@ int main(void)
fprintf(stderr, "test_static_append_sets_too_large failed\n");
failed++;
}
if (test_exceeds_static_cap() != 0) {
fprintf(stderr, "test_exceeds_static_cap failed\n");
failed++;
}
if (failed == 0)
printf("test_asap_http_body: all tests passed\n");
return failed ? 1 : 0;
Expand Down
47 changes: 46 additions & 1 deletion tests/test_gateway_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ static int test_asap_body_over_max(void)
long code;
char *body = NULL;
const char payload[] = "{}";
int r = http_post_raw(gw_url("/asap"), payload, sizeof(payload) - 1, "1000001",
/* ASAP_BODY_MAX is 1 MiB (1048576). 1000001 is still under that cap. */
int r = http_post_raw(gw_url("/asap"), payload, sizeof(payload) - 1, "1048577",
&code, &body);
ASSERT(r == 0);
ASSERT(code == 413);
Expand All @@ -477,6 +478,46 @@ static int test_asap_body_over_max(void)
return 0;
}

/*
* POST /asap allocates a 1 MiB dynamic buffer. A leftover Content-Length check
* still compared against the 64 KiB static cap used by PUT /api/config, so a
* valid state.query just over 64 KiB was 413'd before parse.
*/
static int test_asap_body_over_static_cap_accepted(void)
{
enum { PAD = 70000 };
const char prefix[] =
"{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"asap.send\",\"params\":{"
"\"id\":\"e1\",\"asap_version\":\"2.1\","
"\"sender\":\"urn:from\",\"recipient\":\"urn:to\","
"\"payload_type\":\"state.query\",\"payload\":{\"pad\":\"";
const char suffix[] = "\"}}}";
size_t prefix_len = strlen(prefix);
size_t suffix_len = strlen(suffix);
size_t total = prefix_len + (size_t)PAD + suffix_len;
char *payload;
long code = 0;
char *body = NULL;
int r;

payload = malloc(total + 1U);
ASSERT(payload != NULL);
memcpy(payload, prefix, prefix_len);
memset(payload + prefix_len, 'A', (size_t)PAD);
memcpy(payload + prefix_len + (size_t)PAD, suffix, suffix_len + 1U);
ASSERT(total > 65536U);
ASSERT(total < (1024U * 1024U));
r = http_post(gw_url("/asap"), payload, &code, &body);
free(payload);
ASSERT(r == 0);
ASSERT(code == 200);
ASSERT(body != NULL);
ASSERT(strstr(body, "\"result\"") != NULL);
ASSERT(strstr(body, "sessions") != NULL);
free(body);
return 0;
}

static int test_health_wellknown(void)
{
long code;
Expand Down Expand Up @@ -1054,6 +1095,10 @@ int main(int argc, char **argv)
fprintf(stderr, "test_asap_body_over_max failed\n");
failed++;
}
if (test_asap_body_over_static_cap_accepted() != 0) {
fprintf(stderr, "test_asap_body_over_static_cap_accepted failed\n");
failed++;
}
if (test_asap_invalid_body() != 0) { fprintf(stderr, "test_asap_invalid_body failed\n"); failed++; }
if (test_asap_missing_fields() != 0) { fprintf(stderr, "test_asap_missing_fields failed\n"); failed++; }
if (test_api_asap_log_401() != 0) { fprintf(stderr, "test_api_asap_log_401 failed\n"); failed++; }
Expand Down
Loading