diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/json/json-defs.h | 44 | ||||
-rw-r--r-- | src/core/json/json-reader-defs.h | 95 | ||||
-rw-r--r-- | src/core/json/json-reader-impl.h | 630 | ||||
-rw-r--r-- | src/core/json/json-string.c | 378 | ||||
-rw-r--r-- | src/core/json/json-writer-defs.h | 46 | ||||
-rw-r--r-- | src/core/json/json-writer-impl.h | 233 | ||||
-rw-r--r-- | src/core/json/json.c | 64 | ||||
-rw-r--r-- | src/core/json/json.h | 78 | ||||
-rw-r--r-- | src/core/security/credentials.c | 46 | ||||
-rw-r--r-- | src/core/security/json_token.c | 84 |
10 files changed, 1645 insertions, 53 deletions
diff --git a/src/core/json/json-defs.h b/src/core/json/json-defs.h new file mode 100644 index 0000000000..decf35e4a2 --- /dev/null +++ b/src/core/json/json-defs.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* The various json types. "NONE" may only used on top-level. */ +enum grpc_json_type_t { + GRPC_JSON_OBJECT, + GRPC_JSON_ARRAY, + GRPC_JSON_STRING, + GRPC_JSON_NUMBER, + GRPC_JSON_TRUE, + GRPC_JSON_FALSE, + GRPC_JSON_NULL, + GRPC_JSON_NONE +}; diff --git a/src/core/json/json-reader-defs.h b/src/core/json/json-reader-defs.h new file mode 100644 index 0000000000..10c84738b3 --- /dev/null +++ b/src/core/json/json-reader-defs.h @@ -0,0 +1,95 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* the following need to be pre-defined: + * grpc_json_reader_opaque_t // A type you can use to keep track of your + * // own stuff. + * grpc_json_wchar_t // A type that can hold a unicode character + * // unsigned is good enough. + * grpc_json_string_t // A type that can hold a growable string. + */ + +enum grpc_json_reader_state_t { + GRPC_JSON_STATE_OBJECT_KEY_BEGIN, + GRPC_JSON_STATE_OBJECT_KEY_STRING, + GRPC_JSON_STATE_OBJECT_KEY_END, + GRPC_JSON_STATE_VALUE_BEGIN, + GRPC_JSON_STATE_VALUE_STRING, + GRPC_JSON_STATE_STRING_ESCAPE, + GRPC_JSON_STATE_STRING_ESCAPE_U1, + GRPC_JSON_STATE_STRING_ESCAPE_U2, + GRPC_JSON_STATE_STRING_ESCAPE_U3, + GRPC_JSON_STATE_STRING_ESCAPE_U4, + GRPC_JSON_STATE_VALUE_NUMBER, + GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL, + GRPC_JSON_STATE_VALUE_NUMBER_ZERO, + GRPC_JSON_STATE_VALUE_NUMBER_DOT, + GRPC_JSON_STATE_VALUE_NUMBER_E, + GRPC_JSON_STATE_VALUE_NUMBER_EPM, + GRPC_JSON_STATE_VALUE_TRUE_R, + GRPC_JSON_STATE_VALUE_TRUE_U, + GRPC_JSON_STATE_VALUE_TRUE_E, + GRPC_JSON_STATE_VALUE_FALSE_A, + GRPC_JSON_STATE_VALUE_FALSE_L, + GRPC_JSON_STATE_VALUE_FALSE_S, + GRPC_JSON_STATE_VALUE_FALSE_E, + GRPC_JSON_STATE_VALUE_NULL_U, + GRPC_JSON_STATE_VALUE_NULL_L1, + GRPC_JSON_STATE_VALUE_NULL_L2, + GRPC_JSON_STATE_VALUE_END, + GRPC_JSON_STATE_END +}; + +struct grpc_json_reader_t { + /* You are responsible for the initialization of the following. */ + grpc_json_reader_opaque_t opaque; + + /* Everything down here is private, + and initialized by grpc_json_reader_init. */ + int depth; + int in_object; + int in_array; + int escaped_string_was_key; + int container_just_begun; + grpc_json_wchar_t unicode; + enum grpc_json_reader_state_t state; +}; + +/* The return type of the parser. */ +typedef enum { + GRPC_JSON_DONE, /* The parser finished successfully. */ + GRPC_JSON_EAGAIN, /* The parser yields to get more data. */ + GRPC_JSON_READ_ERROR, /* The parser passes through a read error. */ + GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */ + GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */ +} grpc_json_reader_ret_t; diff --git a/src/core/json/json-reader-impl.h b/src/core/json/json-reader-impl.h new file mode 100644 index 0000000000..2fe56959d0 --- /dev/null +++ b/src/core/json/json-reader-impl.h @@ -0,0 +1,630 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* the following need to be pre-defined: + * grpc_json_static_inline // A macro to declare a static inline + * // function + * grpc_json_eof // A macro that can be used in a switch + * // statement, that grpc_json_read_char + * // can return + * grpc_json_eagain // A macro that can be used in a switch + * // statement, that grpc_json_read_char + * // can return + * grpc_json_error // A macro that can be used in a switch + * // statement, that grpc_json_read_char + * // can return + * + * // A macro or a function that clears your internal scratchpad. + * grpc_json_reader_string_clear(struct grpc_json_reader_t*); + * // A macro or a function that adds a character to your internal + * // scratchpad. + * grpc_json_reader_string_add_char(struct grpc_json_reader_t*, int); + * // A macro or a function that adds a unicode character to your internal + * // scratchpad. + * grpc_json_reader_string_add_wchar(struct grpc_json_reader_t*, + * grpc_json_wchar_t); + * + * // A macro or a function that returns the next character from the input. + * // It can return: + * // . an actual character into an int - unicode, wchar_t, whatever, as + * // long as it's going to work in a switch statement, and can be tested + * // against typical json tokens, such as '{', '[', ',', '}', ']', digits + * // and whitespaces. + * // . grpc_json_eof, which means the end of the input has been reached. + * // . grpc_json_eagain, which means the parser needs to yield before + * // getting more input. + * // . grpc_json_error, which means the parser needs to exit with an error. + * int grpc_json_reader_read_char(struct grpc_json_reader_t*); + * + * // A macro or a function that will be called to signal the beginning of a + * // container. + * // The argument "type" can be either GRPC_JSON_OBJECT, or GRPC_JSON_ARRAY. + * void grpc_json_reader_container_begins(struct grpc_json_reader_t*, + * enum *grpc_json_type_t type) + * // A macro or a function that will be called to signal the end of the + * // current container. It must return GRPC_JSON_OBJECT or GRPC_JSON_ARRAY + * // to signal what is the new current container, or GRPC_JSON_NONE if the + * // stack of containers is now empty. + * enum grpc_json_type_t + * grpc_json_reader_container_ends(struct grpc_json_reader_t*); + * + * // A macro or a function that will be called to signal that json->string + * // contains the string of a object's key that is being added. + * void grpc_json_reader_object_set_key(struct grpc_json_reader_t*); + * + * // A set of macro or functions that will be called to signal that the + * // current container is getting a new value. set_string and set_number + * // are reading their value from your internal scratchpad. set_number + * // must return a boolean to signal if the number parsing succeeded or + * // not. There is little reason for it not to. + * void grpc_json_reader_container_set_string(struct grpc_json_reader_t*); + * int grpc_json_reader_container_set_number(struct grpc_json_reader_t*); + * void grpc_json_reader_container_set_true(struct grpc_json_reader_t*); + * void grpc_json_reader_container_set_false(struct grpc_json_reader_t*); + * void grpc_json_reader_container_set_null(struct grpc_json_reader_t*); + */ + +/* Call this function to initialize the reader structure. */ +grpc_json_static_inline void grpc_json_reader_init( + struct grpc_json_reader_t* reader) { + reader->depth = 0; + reader->in_object = 0; + reader->in_array = 0; + grpc_json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; +} + +/* Call this function to start parsing the input. It will return the following: + * . GRPC_JSON_DONE if the input got eof, and the parsing finished + * successfully. + * . GRPC_JSON_EAGAIN if the read_char function returned again. Call the + * parser again as needed. It is okay to call the parser in polling mode, + * although a bit dull. + * . GRPC_JSON_READ_ERROR if the read_char function returned an error. The + * state isn't broken however, and the function can be called again if the + * error has been corrected. But please use the EAGAIN feature instead for + * consistency. + * . GRPC_JSON_PARSE_ERROR if the input was somehow invalid. + * . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid + * internal state. + */ + +grpc_json_static_inline grpc_json_reader_ret_t +grpc_json_reader_run(struct grpc_json_reader_t* reader) { + int c, success; + + /* This state-machine is a strict implementation of http://json.org/ */ + for (;;) { + c = grpc_json_reader_read_char(reader); + switch (c) { + /* Let's process the error cases first. */ + case grpc_json_error: + return GRPC_JSON_READ_ERROR; + + case grpc_json_eagain: + return GRPC_JSON_EAGAIN; + + case grpc_json_eof: + if ((reader->depth == 0) && + ((reader->state == GRPC_JSON_STATE_END) || + (reader->state == GRPC_JSON_STATE_VALUE_END))) { + return GRPC_JSON_DONE; + } else { + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Processing whitespaces. */ + case ' ': + case '\t': + case '\n': + case '\r': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_OBJECT_KEY_END: + case GRPC_JSON_STATE_VALUE_BEGIN: + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_END: + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + grpc_json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + success = grpc_json_reader_container_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + grpc_json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Value, object or array terminations. */ + case ',': + case '}': + case ']': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + grpc_json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + success = grpc_json_reader_container_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + grpc_json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + /* The missing break here is intentional. */ + + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_VALUE_BEGIN: + if (c == ',') { + if (reader->state != GRPC_JSON_STATE_VALUE_END) { + return GRPC_JSON_PARSE_ERROR; + } + if (reader->in_object) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + } else { + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + } + } else { + if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR; + if ((c == '}') && !reader->in_object) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == '}') && + (reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR; + if ((c == ']') && + (reader->state == GRPC_JSON_STATE_VALUE_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + reader->state = GRPC_JSON_STATE_VALUE_END; + switch (grpc_json_reader_container_ends(reader)) { + case GRPC_JSON_OBJECT: + reader->in_object = 1; + reader->in_array = 0; + break; + case GRPC_JSON_ARRAY: + reader->in_object = 0; + reader->in_array = 1; + break; + case GRPC_JSON_NONE: + if (reader->depth != 0) return GRPC_JSON_INTERNAL_ERROR; + reader->in_object = 0; + reader->in_array = 0; + reader->state = GRPC_JSON_STATE_END; + break; + default: + return GRPC_JSON_INTERNAL_ERROR; + } + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* In-string escaping. */ + case '\\': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + reader->escaped_string_was_key = 1; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + case GRPC_JSON_STATE_VALUE_STRING: + reader->escaped_string_was_key = 0; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + /* This is the \\ case. */ + case GRPC_JSON_STATE_STRING_ESCAPE: + grpc_json_reader_string_add_char(reader, '\\'); + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + default: + reader->container_just_begun = 0; + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + if (c != '"') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + if (c == '"') { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; + grpc_json_reader_object_set_key(reader); + grpc_json_reader_string_clear(reader); + } else { + grpc_json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_VALUE_STRING: + if (c == '"') { + reader->state = GRPC_JSON_STATE_VALUE_END; + grpc_json_reader_container_set_string(reader); + grpc_json_reader_string_clear(reader); + } else { + grpc_json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_OBJECT_KEY_END: + if (c != ':') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + break; + + case GRPC_JSON_STATE_VALUE_BEGIN: + switch (c) { + case 't': + reader->state = GRPC_JSON_STATE_VALUE_TRUE_R; + break; + + case 'f': + reader->state = GRPC_JSON_STATE_VALUE_FALSE_A; + break; + + case 'n': + reader->state = GRPC_JSON_STATE_VALUE_NULL_U; + break; + + case '"': + reader->state = GRPC_JSON_STATE_VALUE_STRING; + break; + + case '0': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO; + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + grpc_json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER; + break; + + case '{': + reader->container_just_begun = 1; + grpc_json_reader_container_begins(reader, GRPC_JSON_OBJECT); + reader->depth++; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + reader->in_object = 1; + reader->in_array = 0; + break; + + case '[': + reader->container_just_begun = 1; + grpc_json_reader_container_begins(reader, GRPC_JSON_ARRAY); + reader->depth++; + reader->in_object = 0; + reader->in_array = 1; + break; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + switch (c) { + case '"': + case '/': + grpc_json_reader_string_add_char(reader, c); + break; + case 'b': + grpc_json_reader_string_add_char(reader, '\b'); + break; + case 'f': + grpc_json_reader_string_add_char(reader, '\f'); + break; + case 'n': + grpc_json_reader_string_add_char(reader, '\n'); + break; + case 'r': + grpc_json_reader_string_add_char(reader, '\r'); + break; + case 't': + grpc_json_reader_string_add_char(reader, '\t'); + break; + case 'u': + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1; + reader->unicode = 0; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + if ((c >= '0') && (c <= '9')) { + c -= '0'; + } else if ((c >= 'A') && (c <= 'F')) { + c -= 'A' - 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a' - 10; + } else { + return GRPC_JSON_PARSE_ERROR; + } + reader->unicode <<= 4; + reader->unicode |= c; + + switch (reader->state) { + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + grpc_json_reader_string_add_wchar(reader, reader->unicode); + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + grpc_json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case '.': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + grpc_json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'e': + case 'E': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + if (c != '.') return GRPC_JSON_PARSE_ERROR; + grpc_json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_DOT: + grpc_json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_E: + grpc_json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + grpc_json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + + case GRPC_JSON_STATE_VALUE_TRUE_R: + if (c != 'r') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_U; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_E; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + grpc_json_reader_container_set_true(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_A: + if (c != 'a') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_L; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_L: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_S; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_S: + if (c != 's') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_E; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + grpc_json_reader_container_set_false(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_NULL_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L1; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L1: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L2; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L2: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + grpc_json_reader_container_set_null(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + /* All of the VALUE_END cases are handled in the specialized case + * above. */ + case GRPC_JSON_STATE_VALUE_END: + switch (c) { + case ',': + case '}': + case ']': + return GRPC_JSON_INTERNAL_ERROR; + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_END: + return GRPC_JSON_PARSE_ERROR; + } + } + } + + return GRPC_JSON_INTERNAL_ERROR; +} diff --git a/src/core/json/json-string.c b/src/core/json/json-string.c new file mode 100644 index 0000000000..771c2cc85e --- /dev/null +++ b/src/core/json/json-string.c @@ -0,0 +1,378 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/json/json.h" + +/* This json-writer will put everything in a big string. + * The point is that we allocate that string in chunks of 256 bytes. + */ +typedef struct { + char *output; + size_t free_space, string_len, allocated; +} grpc_json_writer_opaque_t; + +#include "src/core/json/json-writer-defs.h" + +/* The json-reader will construct a bunch of grpc_json objects and + * link them all up together in a tree-like structure that will represent + * the json data in memory. + * + * It also uses its own input as a scratchpad to store all of the decoded, + * unescaped strings. So we need to keep track of all these pointers in + * that opaque structure the reader will carry for us. + * + * Note that this works because the act of parsing json always reduces its + * input size, and never expands it. + */ +typedef struct { + grpc_json *top; + grpc_json *current_container; + grpc_json *current_value; + char *input; + char *key; + char *string; + char *string_ptr; + size_t remaining_input; +} grpc_json_reader_opaque_t; + +typedef unsigned grpc_json_wchar_t; + +#include "src/core/json/json-reader-defs.h" + +/* Next up, the definitions needed for the implementation. */ +#define grpc_json_static_inline static +#define grpc_json_eof -1 +#define grpc_json_eagain -2 +#define grpc_json_error -3 + +/* This functions checks if there's enough space left in the output buffer, + * and will enlarge it if necessary. We're only allocating chunks of 256 + * bytes at a time (or multiples thereof). + */ +static void grpc_json_writer_output_check(struct grpc_json_writer_t *writer, + size_t needed) { + if (writer->opaque.free_space >= needed) return; + needed = (needed - writer->opaque.free_space + 0xff) & ~0xff; + writer->opaque.output = (char *)gpr_realloc( + writer->opaque.output, writer->opaque.allocated + needed); + writer->opaque.free_space += needed; + writer->opaque.allocated += needed; +} + +/* These are needed by the writer's implementation. */ +static void grpc_json_writer_output_char(struct grpc_json_writer_t *writer, + char c) { + grpc_json_writer_output_check(writer, 1); + writer->opaque.output[writer->opaque.string_len++] = c; + writer->opaque.free_space--; +} + +static void grpc_json_writer_output_string_with_len( + struct grpc_json_writer_t *writer, const char *str, size_t len) { + grpc_json_writer_output_check(writer, len); + memcpy(writer->opaque.output + writer->opaque.string_len, str, len); + writer->opaque.string_len += len; + writer->opaque.free_space -= len; +} + +static void grpc_json_writer_output_string(struct grpc_json_writer_t *writer, + const char *str) { + size_t len = strlen(str); + grpc_json_writer_output_string_with_len(writer, str, len); +} + +#include "src/core/json/json-writer-impl.h" + +/* The reader asks us to clear our scratchpad. In our case, we'll simply mark + * the end of the current string, and advance our output pointer. + */ +static void grpc_json_reader_string_clear(struct grpc_json_reader_t *reader) { + if (reader->opaque.string) { + GPR_ASSERT(reader->opaque.string_ptr < reader->opaque.input); + *reader->opaque.string_ptr++ = 0; + } + reader->opaque.string = reader->opaque.string_ptr; +} + +static void grpc_json_reader_string_add_char(struct grpc_json_reader_t *reader, + int c) { + GPR_ASSERT(reader->opaque.string_ptr < reader->opaque.input); + *reader->opaque.string_ptr++ = (char)c; +} + +/* We are converting a unicode character into utf-8 here. */ +/* The unicode escape encoding of json can only hold 16-bits values. + * So the the 4th case, as well as the last test aren't techically + * necessary, but I wrote them anyway for completion. + */ +static void grpc_json_reader_string_add_wchar(struct grpc_json_reader_t *reader, + unsigned int c) { + if (c <= 0x7f) { + grpc_json_reader_string_add_char(reader, c); + } else if (c <= 0x7ff) { + int b1 = 0xc0 | ((c >> 6) & 0x1f); + int b2 = 0x80 | (c & 0x3f); + grpc_json_reader_string_add_char(reader, b1); + grpc_json_reader_string_add_char(reader, b2); + } else if (c <= 0xffff) { + int b1 = 0xe0 | ((c >> 12) & 0x0f); + int b2 = 0x80 | ((c >> 6) & 0x3f); + int b3 = 0x80 | (c & 0x3f); + grpc_json_reader_string_add_char(reader, b1); + grpc_json_reader_string_add_char(reader, b2); + grpc_json_reader_string_add_char(reader, b3); + } else if (c <= 0x1fffff) { + int b1 = 0xf0 | ((c >> 18) & 0x07); + int b2 = 0x80 | ((c >> 12) & 0x3f); + int b3 = 0x80 | ((c >> 6) & 0x3f); + int b4 = 0x80 | (c & 0x3f); + grpc_json_reader_string_add_char(reader, b1); + grpc_json_reader_string_add_char(reader, b2); + grpc_json_reader_string_add_char(reader, b3); + grpc_json_reader_string_add_char(reader, b4); + } +} + +/* We consider that the input may be a zero-terminated string. So we + * can end up hitting eof before the end of the alleged string length. + */ +static int grpc_json_reader_read_char(struct grpc_json_reader_t *reader) { + int r; + + if (reader->opaque.remaining_input == 0) { + return grpc_json_eof; + } + + r = *reader->opaque.input++; + reader->opaque.remaining_input--; + + if (r == 0) { + reader->opaque.remaining_input = 0; + return grpc_json_eof; + } + + return r; +} + +/* Helper function to create a new grpc_json object and link it into + * our tree-in-progress inside our opaque structure. + */ +static grpc_json *grpc_json_new_and_link(struct grpc_json_reader_t *reader, + enum grpc_json_type_t type) { + grpc_json *json = grpc_json_new(type); + + json->parent = reader->opaque.current_container; + json->prev = reader->opaque.current_value; + reader->opaque.current_value = json; + + if (json->prev) { + json->prev->next = json; + } + if (json->parent) { + if (!json->parent->child) { + json->parent->child = json; + } + if (json->parent->type == GRPC_JSON_OBJECT) { + json->key = reader->opaque.key; + } + } + if (!reader->opaque.top) { + reader->opaque.top = json; + } + + return json; +} + +static void grpc_json_reader_container_begins(struct grpc_json_reader_t *reader, + enum grpc_json_type_t type) { + grpc_json *container; + + GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); + + container = grpc_json_new_and_link(reader, type); + reader->opaque.current_container = container; + reader->opaque.current_value = NULL; +} + +/* It's important to remember that the reader is mostly state-less, so it + * isn't trying to remember what was the container prior the one that just + * ends. Since we're keeping track of these for our own purpose, we are + * able to return that information back, which is useful for it to validate + * the input json stream. + * + * Also note that if we're at the top of the tree, and the last container + * ends, we have to return GRPC_JSON_NONE. + */ +static enum grpc_json_type_t grpc_json_reader_container_ends( + struct grpc_json_reader_t *reader) { + enum grpc_json_type_t container_type = GRPC_JSON_NONE; + + GPR_ASSERT(reader->opaque.current_container); + + reader->opaque.current_value = reader->opaque.current_container; + reader->opaque.current_container = reader->opaque.current_container->parent; + + if (reader->opaque.current_container) { + container_type = reader->opaque.current_container->type; + } + + return container_type; +} + +/* The next 3 functions basically are the reader asking us to use our string + * scratchpad for one of these 3 purposes. + * + * Note that in the set_number case, we're not going to try interpreting it. + * We'll keep it as a string, and leave it to the caller to evaluate it. + */ +static void grpc_json_reader_object_set_key(struct grpc_json_reader_t *reader) { + reader->opaque.key = reader->opaque.string; +} + +static void grpc_json_reader_container_set_string( + struct grpc_json_reader_t *reader) { + grpc_json *json = grpc_json_new_and_link(reader, GRPC_JSON_STRING); + json->value = reader->opaque.string; +} + +static int grpc_json_reader_container_set_number( + struct grpc_json_reader_t *reader) { + grpc_json *json = grpc_json_new_and_link(reader, GRPC_JSON_NUMBER); + json->value = reader->opaque.string; + return 1; +} + +/* The object types true, false and null are self-sufficient, and don't need + * any more information beside their type. + */ +static void grpc_json_reader_container_set_true( + struct grpc_json_reader_t *reader) { + grpc_json_new_and_link(reader, GRPC_JSON_TRUE); +} + +static void grpc_json_reader_container_set_false( + struct grpc_json_reader_t *reader) { + grpc_json_new_and_link(reader, GRPC_JSON_FALSE); +} + +static void grpc_json_reader_container_set_null( + struct grpc_json_reader_t *reader) { + grpc_json_new_and_link(reader, GRPC_JSON_NULL); +} + +/* Now that we've defined all that's needed for the parser's implementation, + * let's include its file. */ +#include "json-reader-impl.h" + +/* And finally, let's define our public API. */ +grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { + struct grpc_json_reader_t reader; + grpc_json *json = NULL; + grpc_json_reader_ret_t status; + + if (!input) return NULL; + + reader.opaque.top = reader.opaque.current_container = + reader.opaque.current_value = NULL; + reader.opaque.string = reader.opaque.key = NULL; + reader.opaque.string_ptr = reader.opaque.input = input; + reader.opaque.remaining_input = size; + grpc_json_reader_init(&reader); + + status = grpc_json_reader_run(&reader); + json = reader.opaque.top; + + if ((status != GRPC_JSON_DONE) && json) { + grpc_json_delete(json); + json = NULL; + } + + return json; +} + +grpc_json *grpc_json_parse_string(char *input) { + return grpc_json_parse_string_with_len(input, 0x7fffffff); +} + +static void grpc_json_dump_recursive(struct grpc_json_writer_t *writer, + grpc_json *json, int in_object) { + while (json) { + if (in_object) grpc_json_writer_object_key(writer, json->key); + + switch (json->type) { + case GRPC_JSON_OBJECT: + case GRPC_JSON_ARRAY: + grpc_json_writer_container_begins(writer, json->type); + if (json->child) + grpc_json_dump_recursive(writer, json->child, + json->type == GRPC_JSON_OBJECT); + grpc_json_writer_container_ends(writer, json->type); + break; + case GRPC_JSON_STRING: + grpc_json_writer_value_string(writer, json->value); + break; + case GRPC_JSON_NUMBER: + grpc_json_writer_value_raw(writer, json->value); + break; + case GRPC_JSON_TRUE: + grpc_json_writer_value_raw_with_len(writer, "true", 4); + break; + case GRPC_JSON_FALSE: + grpc_json_writer_value_raw_with_len(writer, "false", 5); + break; + case GRPC_JSON_NULL: + grpc_json_writer_value_raw_with_len(writer, "null", 4); + break; + default: + abort(); + } + json = json->next; + } +} + +char *grpc_json_dump_to_string(grpc_json *json, int indent) { + struct grpc_json_writer_t writer; + writer.opaque.output = NULL; + writer.opaque.free_space = writer.opaque.string_len = + writer.opaque.allocated = 0; + grpc_json_writer_init(&writer, indent); + grpc_json_dump_recursive(&writer, json, 0); + grpc_json_writer_output_char(&writer, 0); + + return writer.opaque.output; +} diff --git a/src/core/json/json-writer-defs.h b/src/core/json/json-writer-defs.h new file mode 100644 index 0000000000..f8ab9885a2 --- /dev/null +++ b/src/core/json/json-writer-defs.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* the following need to be pre-defined: + * grpc_json_writer_opaque_t // A type you can use to keep track of your + * // own stuff. + */ + +struct grpc_json_writer_t { + grpc_json_writer_opaque_t opaque; + + int indent; + int depth; + int container_empty; + int got_key; +}; diff --git a/src/core/json/json-writer-impl.h b/src/core/json/json-writer-impl.h new file mode 100644 index 0000000000..f3ef968bb0 --- /dev/null +++ b/src/core/json/json-writer-impl.h @@ -0,0 +1,233 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* The idea of the writer is basically symmetrical of the reader. While the + * reader emits various calls to your code, the writer takes basically the + * same calls and emit json out of it. It doesn't try to make any check on + * the order of the calls you do on it. + * + * Also, unlike the reader, the writer expects UTF-8 encoded input strings. + * + * The following need to be defined: + * + * // Adds a character to the output stream. + * void grpc_json_writer_output_char(struct grpc_json_writer_t *, char); + * // Adds a zero-terminated string to the output stream. + * void grpc_json_writer_output_string( + * struct grpc_json_writer_t *writer, const char *str); + * // Adds a fixed-length string to the output stream. + * void grpc_json_writer_output_string_with_len( + * struct grpc_json_writer_t *writer, const char *str, size_t len); + + */ + +/* Call this function to initialize the writer structure. */ +grpc_json_static_inline void grpc_json_writer_init( + struct grpc_json_writer_t* writer, int indent) { + writer->depth = 0; + writer->container_empty = 1; + writer->got_key = 0; + writer->indent = indent; +} + +/* This function is fully private. */ +grpc_json_static_inline void grpc_json_writer_output_indent( + struct grpc_json_writer_t* writer) { + static const char spacesstr[] = + " " + " " + " " + " "; + + int spaces = writer->depth * writer->indent; + + if (writer->got_key) { + grpc_json_writer_output_char(writer, ' '); + return; + } + + while (spaces >= (sizeof(spacesstr) - 1)) { + grpc_json_writer_output_string_with_len(writer, spacesstr, + sizeof(spacesstr) - 1); + spaces -= (sizeof(spacesstr) - 1); + } + + if (!spaces) return; + + grpc_json_writer_output_string_with_len( + writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); +} + +/* This function is fully private. */ +grpc_json_static_inline void grpc_json_writer_value_end( + struct grpc_json_writer_t* writer) { + if (writer->container_empty) { + writer->container_empty = 0; + if (!writer->indent || !writer->depth) return; + grpc_json_writer_output_char(writer, '\n'); + } else { + grpc_json_writer_output_char(writer, ','); + if (!writer->indent) return; + grpc_json_writer_output_char(writer, '\n'); + } +} + +/* This function is fully private. */ +grpc_json_static_inline void grpc_json_writer_escape_string( + struct grpc_json_writer_t* writer, const char* string) { + static const char hex[] = "0123456789abcdef"; + grpc_json_writer_output_char(writer, '"'); + + for (;;) { + unsigned char c = (unsigned char)*string++; + if (!c) { + break; + } else if ((c >= 32) && (c <= 127)) { + if ((c == '\\') || (c == '"')) grpc_json_writer_output_char(writer, '\\'); + grpc_json_writer_output_char(writer, c); + } else if (c < 32) { + grpc_json_writer_output_char(writer, '\\'); + switch (c) { + case '\b': + grpc_json_writer_output_char(writer, 'b'); + break; + case '\f': + grpc_json_writer_output_char(writer, 'f'); + break; + case '\n': + grpc_json_writer_output_char(writer, 'n'); + break; + case '\r': + grpc_json_writer_output_char(writer, 'r'); + break; + case '\t': + grpc_json_writer_output_char(writer, 't'); + break; + default: + grpc_json_writer_output_string_with_len(writer, "u00", 3); + grpc_json_writer_output_char(writer, c >= 16 ? '1' : '0'); + grpc_json_writer_output_char(writer, hex[c & 15]); + break; + } + } else { + unsigned unicode = 0; + if ((c & 0xe0) == 0xc0) { + unicode = c & 0x1f; + unicode <<= 6; + c = *string++; + if ((c & 0xc0) != 0x80) break; + unicode |= c & 0x3f; + } else if ((c & 0xf0) == 0xe0) { + unicode = c & 0x0f; + unicode <<= 6; + c = *string++; + if ((c & 0xc0) != 0x80) break; + unicode |= c & 0x3f; + unicode <<= 6; + c = *string++; + if ((c & 0xc0) != 0x80) break; + unicode |= c & 0x3f; + } else { + break; + } + grpc_json_writer_output_string_with_len(writer, "\\u", 2); + grpc_json_writer_output_char(writer, hex[(unicode >> 12) & 0x0f]); + grpc_json_writer_output_char(writer, hex[(unicode >> 8) & 0x0f]); + grpc_json_writer_output_char(writer, hex[(unicode >> 4) & 0x0f]); + grpc_json_writer_output_char(writer, hex[(unicode) & 0x0f]); + } + } + + grpc_json_writer_output_char(writer, '"'); +} + +/* Call that function to start a new json container. */ +grpc_json_static_inline void grpc_json_writer_container_begins( + struct grpc_json_writer_t* writer, enum grpc_json_type_t type) { + if (!writer->got_key) grpc_json_writer_value_end(writer); + grpc_json_writer_output_indent(writer); + grpc_json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '['); + writer->container_empty = 1; + writer->got_key = 0; + writer->depth++; +} + +/* Call that function to end the current json container. */ +grpc_json_static_inline void grpc_json_writer_container_ends( + struct grpc_json_writer_t* writer, enum grpc_json_type_t type) { + if (writer->indent && !writer->container_empty) + grpc_json_writer_output_char(writer, '\n'); + writer->depth--; + if (!writer->container_empty) grpc_json_writer_output_indent(writer); + grpc_json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']'); + writer->container_empty = 0; + writer->got_key = 0; +} + +/* If you are in a GRPC_JSON_OBJECT container, call this to set up a key. */ +grpc_json_static_inline void grpc_json_writer_object_key( + struct grpc_json_writer_t* writer, const char* string) { + grpc_json_writer_value_end(writer); + grpc_json_writer_output_indent(writer); + grpc_json_writer_escape_string(writer, string); + grpc_json_writer_output_char(writer, ':'); + writer->got_key = 1; +} + +/* Sets a raw value - use it for numbers. */ +grpc_json_static_inline void grpc_json_writer_value_raw( + struct grpc_json_writer_t* writer, const char* string) { + if (!writer->got_key) grpc_json_writer_value_end(writer); + grpc_json_writer_output_indent(writer); + grpc_json_writer_output_string(writer, string); + writer->got_key = 0; +} + +/* Sets a raw value with a known length - use it for true, false and null. */ +grpc_json_static_inline void grpc_json_writer_value_raw_with_len( + struct grpc_json_writer_t* writer, const char* string, unsigned len) { + if (!writer->got_key) grpc_json_writer_value_end(writer); + grpc_json_writer_output_indent(writer); + grpc_json_writer_output_string_with_len(writer, string, len); + writer->got_key = 0; +} + +/* Outputs a string value. This will add double quotes, and escape it. */ +grpc_json_static_inline void grpc_json_writer_value_string( + struct grpc_json_writer_t* writer, const char* string) { + if (!writer->got_key) grpc_json_writer_value_end(writer); + grpc_json_writer_output_indent(writer); + grpc_json_writer_escape_string(writer, string); + writer->got_key = 0; +} diff --git a/src/core/json/json.c b/src/core/json/json.c new file mode 100644 index 0000000000..2905abadcf --- /dev/null +++ b/src/core/json/json.c @@ -0,0 +1,64 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <grpc/support/alloc.h> + +#include "src/core/json/json.h" + +grpc_json *grpc_json_new(enum grpc_json_type_t type) { + grpc_json *json = (grpc_json *)gpr_malloc(sizeof(grpc_json)); + json->parent = json->child = json->next = json->prev = NULL; + json->type = type; + + json->value = json->key = NULL; + + return json; +} + +void grpc_json_delete(grpc_json *json) { + while (json->child) { + grpc_json_delete(json->child); + } + + if (json->next) { + json->next->prev = json->prev; + } + + if (json->prev) { + json->prev->next = json->next; + } else if (json->parent) { + json->parent->child = json->next; + } + + gpr_free(json); +} diff --git a/src/core/json/json.h b/src/core/json/json.h new file mode 100644 index 0000000000..a0bc4dfffc --- /dev/null +++ b/src/core/json/json.h @@ -0,0 +1,78 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __GRPC_SRC_CORE_JSON_JSON_H__ +#define __GRPC_SRC_CORE_JSON_JSON_H__ + +#include <stdlib.h> + +#include "src/core/json/json-defs.h" + +typedef struct grpc_json_t { + struct grpc_json_t* next, *prev, *child, *parent; + enum grpc_json_type_t type; + + const char* key; + const char* value; +} grpc_json; + +/* The next two functions are going to parse the input string, and + * destroy it in the process, in order to use its space to store + * all of the keys and values for the returned object tree. + * + * They assume UTF-8 input stream, and will output UTF-8 encoded + * strings in the tree. + * + * Delete the allocated tree afterward using grpc_json_delete(). + */ +grpc_json* grpc_json_parse_string_with_len(char* input, size_t size); +grpc_json* grpc_json_parse_string(char* input); + +/* This function will create a new string using gpr_realloc, and will + * deserialize the grpc_json tree into it. It'll be zero-terminated, + * but will be allocated in chunks of 256 bytes. + * + * The indent parameter controls the way the output is formatted. + * If indent is 0, then newlines will be suppressed as well, and the + * output will be condensed at its maximum. + */ +char* grpc_json_dump_to_string(grpc_json* json, int indent); + +/* Use these to create or delete a grpc_json object. + * Deletion is recursive. We will not attempt to free any of the strings + * in any of the objects of that tree. + */ +grpc_json* grpc_json_new(enum grpc_json_type_t type); +void grpc_json_delete(grpc_json* json); + +#endif /* __GRPC_SRC_CORE_JSON_JSON_H__ */ diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index 628963e46c..cc48623ea4 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -42,7 +42,7 @@ #include <grpc/support/sync.h> #include <grpc/support/time.h> -#include "third_party/cJSON/cJSON.h" +#include "src/core/json/json.h" #include <string.h> #include <stdio.h> @@ -157,7 +157,7 @@ static void ssl_server_destroy(grpc_server_credentials *creds) { if (c->config.pem_private_keys[i] != NULL) { gpr_free(c->config.pem_private_keys[i]); } - if (c->config.pem_cert_chains[i]!= NULL) { + if (c->config.pem_cert_chains[i] != NULL) { gpr_free(c->config.pem_cert_chains[i]); } } @@ -336,7 +336,7 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( char *null_terminated_body = NULL; char *new_access_token = NULL; grpc_credentials_status status = GRPC_CREDENTIALS_OK; - cJSON *json = NULL; + grpc_json *json = NULL; if (response->body_length > 0) { null_terminated_body = gpr_malloc(response->body_length + 1); @@ -351,46 +351,52 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( status = GRPC_CREDENTIALS_ERROR; goto end; } else { - cJSON *access_token = NULL; - cJSON *token_type = NULL; - cJSON *expires_in = NULL; + grpc_json *access_token = NULL; + grpc_json *token_type = NULL; + grpc_json *expires_in = NULL; + grpc_json *ptr; size_t new_access_token_size = 0; - json = cJSON_Parse(null_terminated_body); + json = grpc_json_parse_string(null_terminated_body); if (json == NULL) { gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body); status = GRPC_CREDENTIALS_ERROR; goto end; } - if (json->type != cJSON_Object) { + if (json->type != GRPC_JSON_OBJECT) { gpr_log(GPR_ERROR, "Response should be a JSON object"); status = GRPC_CREDENTIALS_ERROR; goto end; } - access_token = cJSON_GetObjectItem(json, "access_token"); - if (access_token == NULL || access_token->type != cJSON_String) { + for (ptr = json->child; ptr; ptr = ptr->next) { + if (strcmp(ptr->key, "access_token") == 0) { + access_token = ptr; + } else if (strcmp(ptr->key, "token_type") == 0) { + token_type = ptr; + } else if (strcmp(ptr->key, "expires_in") == 0) { + expires_in = ptr; + } + } + if (access_token == NULL || access_token->type != GRPC_JSON_STRING) { gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON."); status = GRPC_CREDENTIALS_ERROR; goto end; } - token_type = cJSON_GetObjectItem(json, "token_type"); - if (token_type == NULL || token_type->type != cJSON_String) { + if (token_type == NULL || token_type->type != GRPC_JSON_STRING) { gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON."); status = GRPC_CREDENTIALS_ERROR; goto end; } - expires_in = cJSON_GetObjectItem(json, "expires_in"); - if (expires_in == NULL || expires_in->type != cJSON_Number) { + if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) { gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON."); status = GRPC_CREDENTIALS_ERROR; goto end; } - new_access_token_size = strlen(token_type->valuestring) + 1 + - strlen(access_token->valuestring) + 1; + new_access_token_size = + strlen(token_type->value) + 1 + strlen(access_token->value) + 1; new_access_token = gpr_malloc(new_access_token_size); /* C89 does not have snprintf :(. */ - sprintf(new_access_token, "%s %s", token_type->valuestring, - access_token->valuestring); - token_lifetime->tv_sec = expires_in->valueint; + sprintf(new_access_token, "%s %s", token_type->value, access_token->value); + token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10); token_lifetime->tv_nsec = 0; if (*token_elem != NULL) grpc_mdelem_unref(*token_elem); *token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY, @@ -405,7 +411,7 @@ end: } if (null_terminated_body != NULL) gpr_free(null_terminated_body); if (new_access_token != NULL) gpr_free(new_access_token); - if (json != NULL) cJSON_Delete(json); + if (json != NULL) grpc_json_delete(json); return status; } diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c index 14ee758e8b..010e34780a 100644 --- a/src/core/security/json_token.c +++ b/src/core/security/json_token.c @@ -44,7 +44,8 @@ #include <openssl/bio.h> #include <openssl/evp.h> #include <openssl/pem.h> -#include "third_party/cJSON/cJSON.h" + +#include "src/core/json/json.h" /* --- Constants. --- */ @@ -64,18 +65,20 @@ static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL; /* --- grpc_auth_json_key. --- */ -static const char *json_get_string_property(cJSON *json, +static const char *json_get_string_property(grpc_json *json, const char *prop_name) { - cJSON *child = NULL; - child = cJSON_GetObjectItem(json, prop_name); - if (child == NULL || child->type != cJSON_String) { + grpc_json *child; + for (child = json->child; child; child = child->next) { + if (strcmp(child->key, prop_name) == 0) break; + } + if (child == NULL || child->type != GRPC_JSON_STRING) { gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name); return NULL; } - return child->valuestring; + return child->value; } -static int set_json_key_string_property(cJSON *json, const char *prop_name, +static int set_json_key_string_property(grpc_json *json, const char *prop_name, char **json_key_field) { const char *prop_value = json_get_string_property(json, prop_name); if (prop_value == NULL) return 0; @@ -91,7 +94,8 @@ int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) { grpc_auth_json_key grpc_auth_json_key_create_from_string( const char *json_string) { grpc_auth_json_key result; - cJSON *json = cJSON_Parse(json_string); + char *scratchpad = gpr_strdup(json_string); + grpc_json *json = grpc_json_parse_string(scratchpad); BIO *bio = NULL; const char *prop_value; int success = 0; @@ -100,7 +104,7 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string( result.type = GRPC_AUTH_JSON_KEY_TYPE_INVALID; if (json == NULL) { gpr_log(GPR_ERROR, "Invalid json string %s", json_string); - return result; + goto end; } prop_value = json_get_string_property(json, "type"); @@ -136,8 +140,9 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string( end: if (bio != NULL) BIO_free(bio); - if (json != NULL) cJSON_Delete(json); + if (json != NULL) grpc_json_delete(json); if (!success) grpc_auth_json_key_destruct(&result); + gpr_free(scratchpad); return result; } @@ -164,49 +169,62 @@ void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) { /* --- jwt encoding and signature. --- */ +static void create_child(grpc_json **brother, grpc_json *parent, + const char *key, const char *value, + enum grpc_json_type_t type) { + grpc_json *child = grpc_json_new(type); + if (*brother) (*brother)->next = child; + if (!parent->child) parent->child = child; + child->parent = parent; + child->value = value; + child->key = key; + *brother = child; +} + static char *encoded_jwt_header(const char *algorithm) { - cJSON *json = cJSON_CreateObject(); - cJSON *child = cJSON_CreateString(algorithm); + grpc_json *json = grpc_json_new(GRPC_JSON_OBJECT); + grpc_json *brother = NULL; char *json_str = NULL; char *result = NULL; - cJSON_AddItemToObject(json, "alg", child); - child = cJSON_CreateString(GRPC_JWT_TYPE); - cJSON_AddItemToObject(json, "typ", child); - json_str = cJSON_PrintUnformatted(json); + + create_child(&brother, json, "alg", algorithm, GRPC_JSON_STRING); + create_child(&brother, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING); + + json_str = grpc_json_dump_to_string(json, 0); result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); free(json_str); - cJSON_Delete(json); + grpc_json_delete(json); return result; } static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, const char *scope, gpr_timespec token_lifetime) { - cJSON *json = cJSON_CreateObject(); - cJSON *child = NULL; + grpc_json *json = grpc_json_new(GRPC_JSON_OBJECT); + grpc_json *brother = NULL; char *json_str = NULL; char *result = NULL; gpr_timespec now = gpr_now(); gpr_timespec expiration = gpr_time_add(now, token_lifetime); + /* log10(2^64) ~= 20 */ + char now_str[24]; + char expiration_str[24]; if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime) > 0) { gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value."); expiration = gpr_time_add(now, grpc_max_auth_token_lifetime); } - child = cJSON_CreateString(json_key->client_email); - cJSON_AddItemToObject(json, "iss", child); - child = cJSON_CreateString(scope); - cJSON_AddItemToObject(json, "scope", child); - child = cJSON_CreateString(GRPC_JWT_AUDIENCE); - cJSON_AddItemToObject(json, "aud", child); - child = cJSON_CreateNumber(now.tv_sec); - cJSON_SetIntValue(child, now.tv_sec); - cJSON_AddItemToObject(json, "iat", child); - child = cJSON_CreateNumber(expiration.tv_sec); - cJSON_SetIntValue(child, expiration.tv_sec); - cJSON_AddItemToObject(json, "exp", child); - json_str = cJSON_PrintUnformatted(json); + sprintf(now_str, "%ld", now.tv_sec); + sprintf(expiration_str, "%ld", expiration.tv_sec); + + create_child(&brother, json, "iss", json_key->client_email, GRPC_JSON_STRING); + create_child(&brother, json, "scope", scope, GRPC_JSON_STRING); + create_child(&brother, json, "aud", GRPC_JWT_AUDIENCE, GRPC_JSON_STRING); + create_child(&brother, json, "iat", now_str, GRPC_JSON_NUMBER); + create_child(&brother, json, "exp", expiration_str, GRPC_JSON_NUMBER); + + json_str = grpc_json_dump_to_string(json, 0); result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); free(json_str); - cJSON_Delete(json); + grpc_json_delete(json); return result; } |