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/nxt_conf.c | |
parent | 8bb88aaf5186721db20dd07e1976d52a1bc8332a (diff) | |
download | unit-80deee3903e70287f18e0d296d53caae077e6f99.tar.gz unit-80deee3903e70287f18e0d296d53caae077e6f99.tar.bz2 |
Controller: more HTTP headers and detailed JSON parsing errors.
Diffstat (limited to '')
-rw-r--r-- | src/nxt_conf.c | 357 |
1 files changed, 320 insertions, 37 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; + } +} |