Skip to content
Merged
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
4 changes: 4 additions & 0 deletions include/neuron/define.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
#define NEU_MSG_MAX_SIZE 2048
#define NEU_PATH_LEN 128

#define NEU_HTTP_BODY_MAX_SIZE (1 * 1024 * 1024)
#define NEU_HTTP_BODY_MAX_SIZE_LARGE (4 * 1024 * 1024)
#define NEU_HTTP_BODY_TOO_BIG (-2)

#define NEU_TAG_META_LENGTH 20
#define NEU_TAG_META_SIZE 32
#define NEU_TAG_FORMAT_LENGTH 16
Expand Down
4 changes: 4 additions & 0 deletions include/neuron/utils/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ extern "C" {
// buffer overflow.
ssize_t neu_url_decode(const char *s, size_t len, char *buf, size_t size);

// Get a NUL terminated copy of the request body, which the caller must free.
// Returns 0 on success, NEU_HTTP_BODY_TOO_BIG if the body exceeds
// NEU_HTTP_BODY_MAX_SIZE_LARGE, and -1 if the body is empty or on allocation
// failure. On failure `*data` is NULL and `*data_size` is zero.
int neu_http_get_body(nng_aio *aio, void **data, size_t *data_size);

// Find query parameter value of the given name.
Expand Down
35 changes: 20 additions & 15 deletions include/neuron/utils/http_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,27 @@
#include <nng/nng.h>
#include <nng/supplemental/http/http.h>

#include "define.h"
#include "utils/neu_jwt.h"
#include "json/neu_json_error.h"

#define NEU_PROCESS_HTTP_REQUEST(aio, req_type, decode_fun, func) \
{ \
char * req_data = NULL; \
size_t req_data_size = 0; \
req_type *req = NULL; \
\
if (neu_http_get_body((aio), (void **) &req_data, &req_data_size) == \
0 && \
decode_fun(req_data, &req) == 0) { \
{ func }; \
decode_fun##_free(req); \
} else { \
neu_http_bad_request(aio, "{\"error\": 1002}"); \
} \
free(req_data); \
#define NEU_PROCESS_HTTP_REQUEST(aio, req_type, decode_fun, func) \
{ \
char * req_data = NULL; \
size_t req_data_size = 0; \
req_type *req = NULL; \
int body_rv = \
neu_http_get_body((aio), (void **) &req_data, &req_data_size); \
\
if (0 == body_rv && decode_fun(req_data, &req) == 0) { \
{ func }; \
decode_fun##_free(req); \
} else if (NEU_HTTP_BODY_TOO_BIG == body_rv) { \
neu_http_bad_request(aio, "{\"error\": 1017}"); \
} else { \
neu_http_bad_request(aio, "{\"error\": 1002}"); \
} \
free(req_data); \
}

#define NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT(aio, req_type, decode_fun, func) \
Expand Down Expand Up @@ -119,6 +122,8 @@ struct neu_http_handler {
char *path;
char *dst_url;
} value;

size_t max_body;
};

int neu_http_add_handler(nng_http_server * server,
Expand Down
12 changes: 11 additions & 1 deletion plugins/restful/handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,25 +269,28 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/tags",
.value.handler = handle_add_tags,

.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/gtags",
.value.handler = handle_add_gtags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/tags/import",
.value.handler = handle_import_tags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_PUT,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/tags",
.value.handler = handle_update_tags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
Expand Down Expand Up @@ -408,12 +411,14 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/write/tags",
.value.handler = handle_write_tags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/write/gtags",
.value.handler = handle_write_gtags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
Expand Down Expand Up @@ -450,6 +455,7 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/subscribes",
.value.handler = handle_grp_subscribes,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
Expand Down Expand Up @@ -516,12 +522,14 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/global/config",
.value.handler = handle_put_global_config,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_PUT,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/global/drivers",
.value.handler = handle_put_drivers,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
Expand All @@ -540,6 +548,7 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/global/apps",
.value.handler = handle_put_apps,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
Expand Down Expand Up @@ -746,6 +755,7 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/simulator/config",
.value.handler = handle_simulator_set_config,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
Expand Down
10 changes: 10 additions & 0 deletions src/utils/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,18 @@ int neu_http_get_body(nng_aio *aio, void **data, size_t *data_size)
nng_http_req_get_data(req, data, data_size);
if (*data_size == 0) {
return -1;
} else if (*data_size > NEU_HTTP_BODY_MAX_SIZE_LARGE) {
nlog_error("<%p> request body too big: %zu", aio, *data_size);
*data = NULL;
*data_size = 0;
return NEU_HTTP_BODY_TOO_BIG;
} else {
char *buf = calloc(*data_size + 1, sizeof(char));
if (NULL == buf) {
*data = NULL;
*data_size = 0;
return -1;
}
memcpy(buf, *data, *data_size);
*data = buf;
return 0;
Expand Down
7 changes: 7 additions & 0 deletions src/utils/http_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <nng/nng.h>
#include <nng/supplemental/http/http.h>

#include "define.h"
#include "utils/http_handler.h"
#include "utils/log.h"

Expand Down Expand Up @@ -53,6 +54,12 @@ int neu_http_add_handler(nng_http_server * server,
return -1;
}

if (NEU_HTTP_HANDLER_FUNCTION == http_handler->type) {
size_t max_body = http_handler->max_body > 0 ? http_handler->max_body
: NEU_HTTP_BODY_MAX_SIZE;
nng_http_handler_collect_body(handler, true, max_body);
}

switch (http_handler->method) {
case NEU_HTTP_METHOD_GET:
ret = nng_http_handler_set_method(handler, "GET");
Expand Down
19 changes: 19 additions & 0 deletions tests/ft/http_api/test_http_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import copy

import requests

import neuron.api as api
import neuron.error as error
import neuron.config as config
Expand Down Expand Up @@ -649,6 +651,23 @@ def test_get_node_or_group_by_filter(self):
assert 1 == len(response.json()["groups"])


@description(given="running neuron", when="post an over sized body", then="should be rejected before parsing")
def test_body_too_big(self):
# far above the 1MB cap of an ordinary endpoint, but below the 4MB
# cap of the bulk endpoints
payload = '{"name": "' + 'a' * (2 * 1024 * 1024) + '"}'
headers = {"Authorization": config.default_jwt,
"Content-Type": "application/json"}

response = requests.post(
url=config.BASE_URL + '/api/v2/node', headers=headers, data=payload)
assert 400 == response.status_code
assert error.NEU_ERR_BODY_TOO_BIG == response.json()['error']

# neuron is still alive and serving after the rejection
response = api.get_version()
assert 200 == response.status_code

@description(given="running neuron", when="test jwt error", then="failed")
def test_jwt_err(self):
response = api.change_password(new_password='123456', jwt='invalid')
Expand Down
4 changes: 2 additions & 2 deletions tests/ft/neuron/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
NEU_ERR_COMMAND_EXECUTION_FAILED = 1014
NEU_ERR_IP_ADDRESS_INVALID = 1015
NEU_ERR_IP_ADDRESS_IN_USE = 1016
NEU_ERR_INVALID_USER = 1017
NEU_ERR_INVALID_PASSWORD = 1018
NEU_ERR_BODY_TOO_BIG = 1017
NEU_ERR_INVALID_CID = 1018

NEU_ERR_NODE_EXIST = 2002
NEU_ERR_NODE_NOT_EXIST = 2003
Expand Down
Loading