diff options
author | Valentin Bartenev <vbart@nginx.com> | 2017-08-11 19:54:40 +0300 |
---|---|---|
committer | Valentin Bartenev <vbart@nginx.com> | 2017-08-11 19:54:40 +0300 |
commit | 80deee3903e70287f18e0d296d53caae077e6f99 (patch) | |
tree | df447d1ea0e9da893de291a22d4dc10ea7e0475c /src | |
parent | 8bb88aaf5186721db20dd07e1976d52a1bc8332a (diff) | |
download | unit-80deee3903e70287f18e0d296d53caae077e6f99.tar.gz unit-80deee3903e70287f18e0d296d53caae077e6f99.tar.bz2 |
Controller: more HTTP headers and detailed JSON parsing errors.
Diffstat (limited to 'src')
-rw-r--r-- | src/nxt_conf.c | 357 | ||||
-rw-r--r-- | src/nxt_conf.h | 19 | ||||
-rw-r--r-- | src/nxt_controller.c | 354 | ||||
-rw-r--r-- | src/nxt_master_process.c | 2 | ||||
-rw-r--r-- | src/nxt_router.c | 2 |
5 files changed, 576 insertions, 158 deletions
diff --git a/src/nxt_conf.c b/src/nxt_conf.c index 4164bd5b..de9253d3 100644 --- a/src/nxt_conf.c +++ b/src/nxt_conf.c @@ -48,8 +48,8 @@ struct nxt_conf_value_s { double number; struct { - uint8_t length; u_char start[NXT_CONF_MAX_SHORT_STRING]; + uint8_t length; } str; struct { @@ -93,9 +93,9 @@ struct nxt_conf_op_s { static u_char *nxt_conf_json_skip_space(u_char *start, u_char *end); static u_char *nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, - u_char *start, u_char *end); + u_char *start, u_char *end, nxt_conf_json_error_t *error); static u_char *nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, - u_char *start, u_char *end); + u_char *start, u_char *end, nxt_conf_json_error_t *error); static nxt_int_t nxt_conf_object_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *lvlhsh, nxt_conf_object_member_t *member); static nxt_int_t nxt_conf_object_hash_test(nxt_lvlhsh_query_t *lhq, @@ -103,11 +103,13 @@ static nxt_int_t nxt_conf_object_hash_test(nxt_lvlhsh_query_t *lhq, static void *nxt_conf_object_hash_alloc(void *data, size_t size); static void nxt_conf_object_hash_free(void *data, void *p); static u_char *nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value, - u_char *start, u_char *end); + u_char *start, u_char *end, nxt_conf_json_error_t *error); static u_char *nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, - u_char *start, u_char *end); + u_char *start, u_char *end, nxt_conf_json_error_t *error); static u_char *nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value, - u_char *start, u_char *end); + u_char *start, u_char *end, nxt_conf_json_error_t *error); +static void nxt_conf_json_parse_error(nxt_conf_json_error_t *error, u_char *pos, + const char *detail); static nxt_int_t nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, nxt_conf_value_t *src); @@ -192,8 +194,8 @@ nxt_conf_create_object(nxt_mp_t *mp, nxt_uint_t count) } -nxt_int_t -nxt_conf_set_object_member(nxt_conf_value_t *object, nxt_str_t *name, +void +nxt_conf_set_member(nxt_conf_value_t *object, nxt_str_t *name, nxt_conf_value_t *value, uint32_t index) { nxt_conf_value_t *name_value; @@ -215,8 +217,71 @@ nxt_conf_set_object_member(nxt_conf_value_t *object, nxt_str_t *name, } member->value = *value; +} - return NXT_OK; + +void +nxt_conf_set_member_string(nxt_conf_value_t *object, nxt_str_t *name, + nxt_str_t *value, uint32_t index) +{ + nxt_conf_value_t *set; + nxt_conf_object_member_t *member; + + member = &object->u.object->members[index]; + set = &member->name; + + if (name->length > NXT_CONF_MAX_SHORT_STRING) { + set->type = NXT_CONF_VALUE_STRING; + set->u.string.length = name->length; + set->u.string.start = name->start; + + } else { + set->type = NXT_CONF_VALUE_SHORT_STRING; + set->u.str.length = name->length; + + nxt_memcpy(set->u.str.start, name->start, name->length); + } + + set = &member->value; + + if (value->length > NXT_CONF_MAX_SHORT_STRING) { + set->type = NXT_CONF_VALUE_STRING; + set->u.string.length = value->length; + set->u.string.start = value->start; + + } else { + set->type = NXT_CONF_VALUE_SHORT_STRING; + set->u.str.length = value->length; + + nxt_memcpy(set->u.str.start, value->start, value->length); + } +} + + +void +nxt_conf_set_member_integer(nxt_conf_value_t *object, nxt_str_t *name, + int64_t value, uint32_t index) +{ + nxt_conf_value_t *name_value; + nxt_conf_object_member_t *member; + + member = &object->u.object->members[index]; + name_value = &member->name; + + if (name->length > NXT_CONF_MAX_SHORT_STRING) { + name_value->type = NXT_CONF_VALUE_STRING; + name_value->u.string.length = name->length; + name_value->u.string.start = name->start; + + } else { + name_value->type = NXT_CONF_VALUE_SHORT_STRING; + name_value->u.str.length = name->length; + + nxt_memcpy(name_value->u.str.start, name->start, name->length); + } + + member->value.u.integer = value; + member->value.type = NXT_CONF_VALUE_INTEGER; } @@ -802,7 +867,8 @@ nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst, nxt_conf_value_t * -nxt_conf_json_parse(nxt_mp_t *mp, u_char *start, u_char *end) +nxt_conf_json_parse(nxt_mp_t *mp, u_char *start, u_char *end, + nxt_conf_json_error_t *error) { u_char *p; nxt_conf_value_t *value; @@ -815,10 +881,17 @@ nxt_conf_json_parse(nxt_mp_t *mp, u_char *start, u_char *end) p = nxt_conf_json_skip_space(start, end); if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, start, + "An empty JSON isn't allowed. It must be either literal " + "(null, true, or false), number, string (in double quotes \"\"), " + "array (with brackets []), or object (with braces {})." + ); + return NULL; } - p = nxt_conf_json_parse_value(mp, value, p, end); + p = nxt_conf_json_parse_value(mp, value, p, end, error); if (nxt_slow_path(p == NULL)) { return NULL; @@ -827,6 +900,11 @@ nxt_conf_json_parse(nxt_mp_t *mp, u_char *start, u_char *end) p = nxt_conf_json_skip_space(p, end); if (nxt_slow_path(p != end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected character after the end of valid JSON value." + ); + return NULL; } @@ -858,21 +936,21 @@ nxt_conf_json_skip_space(u_char *start, u_char *end) static u_char * nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, - u_char *end) + u_char *end, nxt_conf_json_error_t *error) { - u_char ch; + u_char ch, *p; ch = *start; switch (ch) { case '{': - return nxt_conf_json_parse_object(mp, value, start, end); + return nxt_conf_json_parse_object(mp, value, start, end, error); case '[': - return nxt_conf_json_parse_array(mp, value, start, end); + return nxt_conf_json_parse_array(mp, value, start, end, error); case '"': - return nxt_conf_json_parse_string(mp, value, start, end); + return nxt_conf_json_parse_string(mp, value, start, end, error); case 't': if (nxt_fast_path(end - start >= 4 @@ -884,7 +962,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, return start + 4; } - return NULL; + goto error; case 'f': if (nxt_fast_path(end - start >= 5 @@ -896,7 +974,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, return start + 5; } - return NULL; + goto error; case 'n': if (nxt_fast_path(end - start >= 4 @@ -906,13 +984,47 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, return start + 4; } - return NULL; + goto error; + + case '-': + if (nxt_fast_path(end - start > 1)) { + ch = start[1]; + break; + } + + goto error; } - if (nxt_fast_path(ch == '-' || (ch - '0') <= 9)) { - return nxt_conf_json_parse_number(mp, value, start, end); + if (nxt_fast_path((ch - '0') <= 9)) { + p = nxt_conf_json_parse_number(mp, value, start, end, error); + + if (p == end) { + return end; + } + + switch (*p) { + case ' ': + case '\t': + case '\r': + case '\n': + case ',': + case '}': + case ']': + case '{': + case '[': + case '"': + return p; + } } +error: + + nxt_conf_json_parse_error(error, start, + "A valid JSON value is expected here. It must be either literal " + "(null, true, or false), number, string (in double quotes \"\"), " + "array (with brackets []), or object (with braces {})." + ); + return NULL; } @@ -929,9 +1041,9 @@ static const nxt_lvlhsh_proto_t nxt_conf_object_hash_proto static u_char * nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, - u_char *end) + u_char *end, nxt_conf_json_error_t *error) { - u_char *p; + u_char *p, *name; nxt_mp_t *mp_temp; nxt_int_t rc; nxt_uint_t count; @@ -954,6 +1066,12 @@ nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, p = nxt_conf_json_skip_space(p + 1, end); if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected end of JSON. There's an object without closing " + "brace (})." + ); + goto error; } @@ -962,9 +1080,17 @@ nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, break; } + nxt_conf_json_parse_error(error, p, + "A double quote (\") is expected here. There must be a valid " + "JSON object member starts with a name, which is a string " + "enclosed in double quotes." + ); + goto error; } + name = p; + count++; member = nxt_mp_get(mp_temp, sizeof(nxt_conf_object_member_t)); @@ -972,7 +1098,7 @@ nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, goto error; } - p = nxt_conf_json_parse_string(mp, &member->name, p, end); + p = nxt_conf_json_parse_string(mp, &member->name, p, end, error); if (nxt_slow_path(p == NULL)) { goto error; @@ -981,22 +1107,52 @@ nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, rc = nxt_conf_object_hash_add(mp_temp, &hash, member); if (nxt_slow_path(rc != NXT_OK)) { + + if (rc == NXT_DECLINED) { + nxt_conf_json_parse_error(error, name, + "Duplicate object member. All JSON object members must " + "have unique names." + ); + } + goto error; } p = nxt_conf_json_skip_space(p, end); - if (nxt_slow_path(p == end || *p != ':')) { + if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected end of JSON. There's an object member without " + "value." + ); + + goto error; + } + + if (nxt_slow_path(*p != ':')) { + + nxt_conf_json_parse_error(error, p, + "A colon (:) is expected here. There must be a colon between " + "JSON member name and value." + ); + goto error; } p = nxt_conf_json_skip_space(p + 1, end); if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected end of JSON. There's an object member without " + "value." + ); + goto error; } - p = nxt_conf_json_parse_value(mp, &member->value, p, end); + p = nxt_conf_json_parse_value(mp, &member->value, p, end, error); if (nxt_slow_path(p == NULL)) { goto error; @@ -1005,6 +1161,12 @@ nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, p = nxt_conf_json_skip_space(p, end); if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected end of JSON. There's an object without closing " + "brace (})." + ); + goto error; } @@ -1013,6 +1175,12 @@ nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, break; } + nxt_conf_json_parse_error(error, p, + "Either a closing brace (}) or a comma (,) is expected here. " + "In JSON, all object members must be enclosed in braces and " + "separated by commas." + ); + goto error; } } @@ -1081,11 +1249,7 @@ nxt_conf_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data) nxt_conf_get_string(&member->name, &str); - if (nxt_strstr_eq(&lhq->key, &str)) { - return NXT_OK; - } - - return NXT_DECLINED; + return nxt_strstr_eq(&lhq->key, &str) ? NXT_OK : NXT_DECLINED; } @@ -1105,7 +1269,7 @@ nxt_conf_object_hash_free(void *data, void *p) static u_char * nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, - u_char *end) + u_char *end, nxt_conf_json_error_t *error) { u_char *p; nxt_mp_t *mp_temp; @@ -1131,6 +1295,12 @@ nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, p = nxt_conf_json_skip_space(p + 1, end); if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected end of JSON. There's an array without closing " + "bracket (])." + ); + goto error; } @@ -1145,7 +1315,7 @@ nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, goto error; } - p = nxt_conf_json_parse_value(mp, element, p, end); + p = nxt_conf_json_parse_value(mp, element, p, end, error); if (nxt_slow_path(p == NULL)) { goto error; @@ -1154,6 +1324,12 @@ nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, p = nxt_conf_json_skip_space(p, end); if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected end of JSON. There's an array without closing " + "bracket (])." + ); + goto error; } @@ -1162,6 +1338,12 @@ nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, break; } + nxt_conf_json_parse_error(error, p, + "Either a closing bracket (]) or a comma (,) is expected here. " + "In JSON, all array members must be enclosed in brackets and " + "separated by commas." + ); + goto error; } } @@ -1195,7 +1377,7 @@ error: static u_char * nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, - u_char *end) + u_char *end, nxt_conf_json_error_t *error) { u_char *p, ch, *last, *s; size_t size, surplus; @@ -1235,6 +1417,11 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, continue; } + nxt_conf_json_parse_error(error, p, + "Unexpected character in string. All control characters must " + "be escaped in JSON strings." + ); + return NULL; case sw_escape: @@ -1265,6 +1452,12 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, continue; } + nxt_conf_json_parse_error(error, p - 1, + "Unexpected reverse solidus in string. Reverse solidus in " + "JSON strings must be escaped with a second reverse solidus " + "(\\\\)." + ); + return NULL; case sw_encoded1: @@ -1280,6 +1473,12 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, continue; } + nxt_conf_json_parse_error(error, p, + "Invalid escape sequence. In JSON, escape sequences start " + "with a reverse solidus, followed by the lowercase letter u, " + "followed by four hexadecimal digits (\\uXXXX)." + ); + return NULL; } @@ -1287,6 +1486,12 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, } if (nxt_slow_path(p == end)) { + + nxt_conf_json_parse_error(error, p, + "Unexpected end of JSON. There's a string without ending double " + "quote (\")." + ); + return NULL; } @@ -1298,6 +1503,12 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, if (size > NXT_CONF_MAX_SHORT_STRING) { if (nxt_slow_path(size > NXT_CONF_MAX_STRING)) { + + nxt_conf_json_parse_error(error, start, + "Too long string. Such a big JSON string values are not " + "supported." + ); + return NULL; } @@ -1377,6 +1588,13 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, if (utf_high != 0) { if (nxt_slow_path(utf < 0xdc00 || utf > 0xdfff)) { + + nxt_conf_json_parse_error(error, p - 12, + "Invalid JSON encoding sequence. There's a 12 bytes " + "sequence that composes an illegal UTF-16 surrogate " + "pair." + ); + return NULL; } @@ -1390,6 +1608,12 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, } if (utf > 0xdbff || p[0] != '\\' || p[1] != 'u') { + + nxt_conf_json_parse_error(error, p - 6, + "Invalid JSON encoding sequence. There's a 6 bytes " + "sequence that doesn't represent a valid character." + ); + return NULL; } @@ -1416,7 +1640,7 @@ nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, static u_char * nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, - u_char *end) + u_char *end, nxt_conf_json_error_t *error) { u_char *p, ch; uint64_t integer; @@ -1454,13 +1678,23 @@ nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, if (nxt_slow_path(integer >= cutoff && (integer > cutoff || ch > cutlim))) { - return NULL; + nxt_conf_json_parse_error(error, start, + "Too big integer. Such a big JSON integer values are not " + "supported." + ); + + return NULL; } integer = integer * 10 + ch; } - if (nxt_slow_path(p == start || (p - start > 1 && *start == '0'))) { + if (nxt_slow_path(p - start > 1 && *start == '0')) { + + nxt_conf_json_parse_error(error, start, + "Leading zeros are not allowed in JSON numbers." + ); + return NULL; } @@ -1549,12 +1783,32 @@ nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, if (nxt_fast_path(isfinite(value->u.number))) { return p; } +#else + + nxt_conf_json_parse_error(error, start, + "Invalid number. Only integer JSON numbers without fraction and " + "exponent parts are supported." + ); + #endif return NULL; } +static void +nxt_conf_json_parse_error(nxt_conf_json_error_t *error, u_char *pos, + const char *detail) +{ + if (error == NULL) { + return; + } + + error->pos = pos; + error->detail = (u_char *) detail; +} + + size_t nxt_conf_json_length(nxt_conf_value_t *value, nxt_conf_json_pretty_t *pretty) { @@ -1983,3 +2237,32 @@ nxt_conf_json_escape(u_char *dst, u_char *src, size_t size) return dst; } + + +void +nxt_conf_json_position(u_char *start, u_char *pos, nxt_uint_t *line, + nxt_uint_t *column) +{ + u_char *p; + ssize_t symbols; + nxt_uint_t lines; + + lines = 1; + + for (p = start; p != pos; p++) { + + if (*p != '\n') { + continue; + } + + lines++; + start = p + 1; + } + + symbols = nxt_utf8_length(start, p - start); + + if (symbols != -1) { + *line = lines; + *column = 1 + symbols; + } +} diff --git a/src/nxt_conf.h b/src/nxt_conf.h index 7262d890..30863fbf 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -22,6 +22,12 @@ typedef struct nxt_conf_value_s nxt_conf_value_t; typedef struct nxt_conf_op_s nxt_conf_op_t; +typedef struct { + u_char *pos; + u_char *detail; +} nxt_conf_json_error_t; + + typedef enum { NXT_CONF_MAP_INT8, NXT_CONF_MAP_INT32, @@ -65,15 +71,18 @@ nxt_int_t nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *nxt_conf_clone(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *value); -nxt_conf_value_t *nxt_conf_json_parse(nxt_mp_t *mp, u_char *start, u_char *end); +nxt_conf_value_t *nxt_conf_json_parse(nxt_mp_t *mp, u_char *start, u_char *end, + nxt_conf_json_error_t *error); #define nxt_conf_json_parse_str(mp, str) \ - nxt_conf_json_parse(mp, (str)->start, (str)->start + (str)->length) + nxt_conf_json_parse(mp, (str)->start, (str)->start + (str)->length, NULL) size_t nxt_conf_json_length(nxt_conf_value_t *value, nxt_conf_json_pretty_t *pretty); u_char *nxt_conf_json_print(u_char *p, nxt_conf_value_t *value, nxt_conf_json_pretty_t *pretty); +void nxt_conf_json_position(u_char *start, u_char *pos, nxt_uint_t *line, + nxt_uint_t *column); nxt_int_t nxt_conf_validate(nxt_conf_value_t *value); @@ -82,8 +91,12 @@ void nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str); // FIXME reimplement and reorder functions below nxt_uint_t nxt_conf_object_members_count(nxt_conf_value_t *value); nxt_conf_value_t *nxt_conf_create_object(nxt_mp_t *mp, nxt_uint_t count); -nxt_int_t nxt_conf_set_object_member(nxt_conf_value_t *object, nxt_str_t *name, +void nxt_conf_set_member(nxt_conf_value_t *object, nxt_str_t *name, nxt_conf_value_t *value, uint32_t index); +void nxt_conf_set_member_string(nxt_conf_value_t *object, nxt_str_t *name, + nxt_str_t *value, uint32_t index); +void nxt_conf_set_member_integer(nxt_conf_value_t *object, nxt_str_t *name, + int64_t value, uint32_t index); #endif /* _NXT_CONF_INCLUDED_ */ diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 221393eb..876c037f 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -27,9 +27,14 @@ typedef struct { typedef struct { - nxt_str_t status_line; + nxt_uint_t status; nxt_conf_value_t *conf; - nxt_str_t json; + + u_char *title; + u_char *detail; + ssize_t offset; + nxt_uint_t line; + nxt_uint_t column; } nxt_controller_response_t; @@ -63,8 +68,8 @@ static nxt_int_t nxt_controller_conf_pass(nxt_task_t *task, nxt_conf_value_t *conf); static void nxt_controller_response(nxt_task_t *task, nxt_controller_request_t *req, nxt_controller_response_t *resp); -static nxt_buf_t *nxt_controller_response_body(nxt_controller_response_t *resp, - nxt_mp_t *pool); +static u_char *nxt_controller_date(u_char *buf, nxt_realtime_t *now, + struct tm *tm, size_t size, const char *format); static nxt_http_fields_hash_entry_t nxt_controller_request_fields[] = { @@ -561,10 +566,10 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) nxt_int_t rc; nxt_str_t path; nxt_conn_t *c; - nxt_uint_t status; nxt_buf_mem_t *mbuf; nxt_conf_op_t *ops; nxt_conf_value_t *value; + nxt_conf_json_error_t error; nxt_controller_response_t resp; static const nxt_str_t empty_obj = nxt_string("{}"); @@ -583,14 +588,14 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) value = nxt_conf_get_path(nxt_controller_conf.root, &path); if (value == NULL) { - status = 404; - goto done; + goto not_found; } + resp.status = 200; resp.conf = value; - status = 200; - goto done; + nxt_controller_response(task, req, &resp); + return; } if (nxt_str_eq(&req->parser.method, "PUT", 3)) { @@ -598,19 +603,32 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) mp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(mp == NULL)) { - status = 500; - goto done; + goto alloc_fail; } mbuf = &c->read->mem; - value = nxt_conf_json_parse(mp, mbuf->pos, mbuf->free); + nxt_memzero(&error, sizeof(nxt_conf_json_error_t)); + + value = nxt_conf_json_parse(mp, mbuf->pos, mbuf->free, &error); if (value == NULL) { nxt_mp_destroy(mp); - status = 400; - nxt_str_set(&resp.json, "{ \"error\": \"Invalid JSON.\" }"); - goto done; + + if (error.pos == NULL) { + goto alloc_fail; + } + + resp.status = 400; + resp.title = (u_char *) "Invalid JSON."; + resp.detail = error.detail; + resp.offset = error.pos - mbuf->pos; + + nxt_conf_json_position(mbuf->pos, error.pos, + &resp.line, &resp.column); + + nxt_controller_response(task, req, &resp); + return; } if (path.length != 1) { @@ -620,29 +638,23 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (rc != NXT_OK) { if (rc == NXT_DECLINED) { - status = 404; - goto done; + goto not_found; } - status = 500; - goto done; + goto alloc_fail; } value = nxt_conf_clone(mp, ops, nxt_controller_conf.root); if (nxt_slow_path(value == NULL)) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } } if (nxt_slow_path(nxt_conf_validate(value) != NXT_OK)) { nxt_mp_destroy(mp); - status = 400; - nxt_str_set(&resp.json, - "{ \"error\": \"Invalid configuration.\" }"); - goto done; + goto invalid_conf; } req->conf.root = value; @@ -650,8 +662,7 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (nxt_controller_conf_apply(task, req) != NXT_OK) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } return; @@ -663,8 +674,7 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) mp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(mp == NULL)) { - status = 500; - goto done; + goto alloc_fail; } value = nxt_conf_json_parse_str(mp, &empty_obj); @@ -676,19 +686,16 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (rc != NXT_OK) { if (rc == NXT_DECLINED) { - status = 404; - goto done; + goto not_found; } - status = 500; - goto done; + goto alloc_fail; } mp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(mp == NULL)) { - status = 500; - goto done; + goto alloc_fail; } value = nxt_conf_clone(mp, ops, nxt_controller_conf.root); @@ -696,16 +703,12 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (nxt_slow_path(value == NULL)) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } if (nxt_slow_path(nxt_conf_validate(value) != NXT_OK)) { nxt_mp_destroy(mp); - status = 400; - nxt_str_set(&resp.json, - "{ \"error\": \"Invalid configuration.\" }"); - goto done; + goto invalid_conf; } req->conf.root = value; @@ -713,44 +716,45 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) if (nxt_controller_conf_apply(task, req) != NXT_OK) { nxt_mp_destroy(mp); - status = 500; - goto done; + goto alloc_fail; } return; } - status = 405; + resp.status = 405; + resp.title = (u_char *) "Invalid method."; + resp.offset = -1; -done: + nxt_controller_response(task, req, &resp); + return; - switch (status) { +alloc_fail: - case 200: - nxt_str_set(&resp.status_line, "200 OK"); - break; + resp.status = 500; + resp.title = (u_char *) "Memory allocation failed."; + resp.offset = -1; - case 400: - nxt_str_set(&resp.status_line, "400 Bad Request"); - break; + nxt_controller_response(task, req, &resp); + return; - case 404: - nxt_str_set(&resp.status_line, "404 Not Found"); - nxt_str_set(&resp.json, "{ \"error\": \"Value doesn't exist.\" }"); - break; +not_found: - case 405: - nxt_str_set(&resp.status_line, "405 Method Not Allowed"); - nxt_str_set(&resp.json, "{ \"error\": \"Invalid method.\" }"); - break; + resp.status = 404; + resp.title = (u_char *) "Value doesn't exist."; + resp.offset = -1; - case 500: - nxt_str_set(&resp.status_line, "500 Internal Server Error"); - nxt_str_set(&resp.json, "{ \"error\": \"Memory allocation failed.\" }"); - break; - } + nxt_controller_response(task, req, &resp); + return; + +invalid_conf: + + resp.status = 400; + resp.title = (u_char *) "Invalid configuration."; + resp.offset = -1; nxt_controller_response(task, req, &resp); + return; } @@ -796,15 +800,15 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_controller_conf = req->conf; - nxt_str_set(&resp.status_line, "200 OK"); - nxt_str_set(&resp.json, "{ \"success\": \"Reconfiguration done.\" }"); + resp.status = 200; + resp.title = (u_char *) "Reconfiguration done."; } else { nxt_mp_destroy(req->conf.pool); - nxt_str_set(&resp.status_line, "500 Internal Server Error"); - nxt_str_set(&resp.json, - "{ \"error\": \"Failed to apply new configuration.\" }"); + resp.status = 500; + resp.title = (u_char *) "Failed to apply new configuration."; + resp.offset = -1; } nxt_controller_response(task, req, &resp); @@ -830,9 +834,11 @@ nxt_controller_process_waiting(nxt_task_t *task) nxt_mp_destroy(req->conf.pool); - nxt_str_set(&resp.status_line, "500 Internal Server Error"); - nxt_str_set(&resp.json, - "{ \"error\": \"Memory allocation failed.\" }"); + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + resp.status = 500; + resp.title = (u_char *) "Memory allocation failed."; + resp.offset = -1; nxt_controller_response(task, req, &resp); @@ -877,61 +883,111 @@ nxt_controller_conf_pass(nxt_task_t *task, nxt_conf_value_t *conf) } - static void nxt_controller_response(nxt_task_t *task, nxt_controller_request_t *req, nxt_controller_response_t *resp) { - size_t size; - nxt_buf_t *b; - nxt_conn_t *c; + size_t size; + nxt_str_t status_line, str; + nxt_buf_t *b, *body; + nxt_conn_t *c; + nxt_uint_t n; + nxt_conf_value_t *value, *location; + nxt_conf_json_pretty_t pretty; - c = req->conn; + static nxt_str_t success_str = nxt_string("success"); + static nxt_str_t error_str = nxt_string("error"); + static nxt_str_t detail_str = nxt_string("detail"); + static nxt_str_t location_str = nxt_string("location"); + static nxt_str_t offset_str = nxt_string("offset"); + static nxt_str_t line_str = nxt_string("line"); + static nxt_str_t column_str = nxt_string("column"); + + static nxt_time_string_t date_cache = { + (nxt_atomic_uint_t) -1, + nxt_controller_date, + "%s, %02d %s %4d %02d:%02d:%02d GMT", + sizeof("Wed, 31 Dec 1986 16:40:00 GMT") - 1, + NXT_THREAD_TIME_GMT, + NXT_THREAD_TIME_SEC, + }; + + switch (resp->status) { - size = sizeof("HTTP/1.0 " "\r\n\r\n") - 1 + resp->status_line.length; + case 200: + nxt_str_set(&status_line, "200 OK"); + break; - b = nxt_buf_mem_alloc(c->mem_pool, size, 0); - if (nxt_slow_path(b == NULL)) { - nxt_controller_conn_close(task, c, req); - return; + case 400: + nxt_str_set(&status_line, "400 Bad Request"); + break; + + case 404: + nxt_str_set(&status_line, "404 Not Found"); + break; + + case 405: + nxt_str_set(&status_line, "405 Method Not Allowed"); + break; + + case 500: + nxt_str_set(&status_line, "500 Internal Server Error"); + break; } - b->mem.free = nxt_cpymem(b->mem.free, "HTTP/1.0 ", sizeof("HTTP/1.0 ") - 1); - b->mem.free = nxt_cpymem(b->mem.free, resp->status_line.start, - resp->status_line.length); + c = req->conn; + value = resp->conf; - b->mem.free = nxt_cpymem(b->mem.free, "\r\n\r\n", sizeof("\r\n\r\n") - 1); + if (value == NULL) { + n = 1 + + (resp->detail != NULL) + + (resp->status >= 400 && resp->offset != -1); - b->next = nxt_controller_response_body(resp, c->mem_pool); + value = nxt_conf_create_object(c->mem_pool, n); - if (nxt_slow_path(b->next == NULL)) { - nxt_controller_conn_close(task, c, req); - return; - } + if (nxt_slow_path(value == NULL)) { + nxt_controller_conn_close(task, c, req); + return; + } - c->write = b; - c->write_state = &nxt_controller_conn_write_state; + str.length = nxt_strlen(resp->title); + str.start = resp->title; - nxt_conn_write(task->thread->engine, c); -} + if (resp->status < 400) { + nxt_conf_set_member_string(value, &success_str, &str, 0); + } else { + nxt_conf_set_member_string(value, &error_str, &str, 0); + } -static nxt_buf_t * -nxt_controller_response_body(nxt_controller_response_t *resp, nxt_mp_t *pool) -{ - size_t size; - nxt_buf_t *b; - nxt_conf_value_t *value; - nxt_conf_json_pretty_t pretty; + n = 0; - if (resp->conf) { - value = resp->conf; + if (resp->detail != NULL) { + str.length = nxt_strlen(resp->detail); + str.start = resp->detail; - } else { - value = nxt_conf_json_parse_str(pool, &resp->json); + n++; - if (nxt_slow_path(value == NULL)) { - return NULL; + nxt_conf_set_member_string(value, &detail_str, &str, n); + } + + if (resp->status >= 400 && resp->offset != -1) { + n++; + + location = nxt_conf_create_object(c->mem_pool, + resp->line != 0 ? 3 : 1); + + nxt_conf_set_member(value, &location_str, location, n); + + nxt_conf_set_member_integer(location, &offset_str, resp->offset, 0); + + if (resp->line != 0) { + nxt_conf_set_member_integer(location, &line_str, + resp->line, 1); + + nxt_conf_set_member_integer(location, &column_str, + resp->column, 2); + } } } @@ -939,17 +995,83 @@ nxt_controller_response_body(nxt_controller_response_t *resp, nxt_mp_t *pool) size = nxt_conf_json_length(value, &pretty) + 2; - b = nxt_buf_mem_alloc(pool, size, 0); - if (nxt_slow_path(b == NULL)) { - return NULL; + body = nxt_buf_mem_alloc(c->mem_pool, size, 0); + if (nxt_slow_path(body == NULL)) { + nxt_controller_conn_close(task, c, req); + return; } nxt_memzero(&pretty, sizeof(nxt_conf_json_pretty_t)); - b->mem.free = nxt_conf_json_print(b->mem.free, value, &pretty); + body->mem.free = nxt_conf_json_print(body->mem.free, value, &pretty); + + body->mem.free = nxt_cpymem(body->mem.free, "\r\n", 2); + + size = sizeof("HTTP/1.1 " "\r\n") - 1 + status_line.length + + sizeof("Server: nginext/0.1\r\n") - 1 + + sizeof("Date: Wed, 31 Dec 1986 16:40:00 GMT\r\n") - 1 + + sizeof("Content-Type: application/json\r\n") - 1 + + sizeof("Content-Length: " "\r\n") - 1 + NXT_SIZE_T_LEN + + sizeof("Connection: close\r\n") - 1 + + sizeof("\r\n") - 1; + + b = nxt_buf_mem_alloc(c->mem_pool, size, 0); + if (nxt_slow_path(b == NULL)) { + nxt_controller_conn_close(task, c, req); + return; + } + + b->next = body; + + nxt_str_set(&str, "HTTP/1.1 "); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + b->mem.free = nxt_cpymem(b->mem.free, status_line.start, + status_line.length); + + nxt_str_set(&str, "\r\n" + "Server: nginext/0.1\r\n" + "Date: "); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + + b->mem.free = nxt_thread_time_string(task->thread, &date_cache, + b->mem.free); + + nxt_str_set(&str, "\r\n" + "Content-Type: application/json\r\n" + "Content-Length: "); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + + b->mem.free = nxt_sprintf(b->mem.free, b->mem.end, "%uz", + nxt_buf_mem_used_size(&body->mem)); + + nxt_str_set(&str, "\r\n" + "Connection: close\r\n" + "\r\n"); + + b->mem.free = nxt_cpymem(b->mem.free, str.start, str.length); + + c->write = b; + c->write_state = &nxt_controller_conn_write_state; + + nxt_conn_write(task->thread->engine, c); +} + + +static u_char * +nxt_controller_date(u_char *buf, nxt_realtime_t *now, struct tm *tm, + size_t size, const char *format) +{ + static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", + "Sat" }; - *b->mem.free++ = '\r'; - *b->mem.free++ = '\n'; + static const char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - return b; + return nxt_sprintf(buf, buf + size, format, + week[tm->tm_wday], tm->tm_mday, + month[tm->tm_mon], tm->tm_year + 1900, + tm->tm_hour, tm->tm_min, tm->tm_sec); } diff --git a/src/nxt_master_process.c b/src/nxt_master_process.c index 0df32631..a1a44cd2 100644 --- a/src/nxt_master_process.c +++ b/src/nxt_master_process.c @@ -181,7 +181,7 @@ nxt_port_master_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) start += app_conf.name.length + 1; - conf = nxt_conf_json_parse(mp, start, b->mem.free); + conf = nxt_conf_json_parse(mp, start, b->mem.free, NULL); if (conf == NULL) { nxt_log(task, NXT_LOG_CRIT, "configuration parsing error"); diff --git a/src/nxt_router.c b/src/nxt_router.c index fa2e957d..e55f0fb0 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -661,7 +661,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_str_t applications_path = nxt_string("/applications"); static nxt_str_t listeners_path = nxt_string("/listeners"); - conf = nxt_conf_json_parse(tmcf->mem_pool, start, end); + conf = nxt_conf_json_parse(tmcf->mem_pool, start, end, NULL); if (conf == NULL) { nxt_log(task, NXT_LOG_CRIT, "configuration parsing error"); return NXT_ERROR; |