mirror of
https://github.com/emqx/neuron.git
synced 2026-10-17 04:40:42 +08:00
http check body length
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) \
|
||||
@@ -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,
|
||||
|
||||
@@ -255,25 +255,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,
|
||||
@@ -394,12 +397,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,
|
||||
@@ -436,6 +441,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,
|
||||
@@ -502,12 +508,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,
|
||||
@@ -526,6 +534,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,
|
||||
@@ -730,6 +739,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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import copy
|
||||
|
||||
import requests
|
||||
|
||||
import neuron.api as api
|
||||
import neuron.error as error
|
||||
import neuron.config as config
|
||||
@@ -649,6 +651,23 @@ class TestHttp:
|
||||
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')
|
||||
|
||||
@@ -16,8 +16,8 @@ NEU_ERR_DUPLICATE_PASSWORD = 1013
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user