diff options
-rw-r--r-- | fhp.c | 217 | ||||
-rw-r--r-- | include/fhp/fhp.h | 45 | ||||
-rw-r--r-- | test.c | 42 |
3 files changed, 301 insertions, 3 deletions
@@ -160,6 +160,10 @@ CASE_TOKEN_CHARS \ case '/': +#define CASE_TE_CHARS \ + CASE_ALNUM_CHARS \ + case '-': + // // rfc7230, Appendix B // https://tools.ietf.org/html/rfc7230 @@ -235,8 +239,13 @@ fhp_errors[] = { "invalid character after carriage return", "invalid character in HTTP header name", "invalid error code", - "invalid body type", "buffer too small", + "invalid body type", + "too many transfer encodings", + "invalid character in transfer encoding name", + "invalid character in transfer encoding", + "bad transfer encoding state", + "transfer encoding parser already done", }; fhp_err_t @@ -392,10 +401,212 @@ fhp_get_default_env(void) { } // +// transfer encoding buffer functions +// + +static void +fhp_te_parser_buf_flush(fhp_te_parser_t * const te) { + te->buf_hash = fhp_lc_hash_push(te->buf_hash, te->buf, te->len); + te->len = 0; +} + +static void +fhp_te_parser_buf_push( + fhp_te_parser_t * const te, + uint8_t byte +) { + // flush buffer (if necessary) + if (te->len + 1 >= FHP_TE_MAX_BUF_SIZE) + fhp_te_parser_buf_flush(te); + + // push to buffer + te->buf[te->len] = byte; + te->len++; +} + +// +// transfer encoding parser functions +// + +static const fhp_te_parser_t FHP_DEFAULT_TE_PARSER = { + .state = FHP_TE_STATE_INIT, + .len = 0, + .num_tes = 0, + .err = FHP_OK, +}; + +fhp_err_t +fhp_te_parser_init(fhp_te_parser_t * const te) { + // init parser + *te = FHP_DEFAULT_TE_PARSER; + + // return succes + return FHP_OK; +} + +static fhp_err_t +fhp_te_parser_push_byte( + fhp_te_parser_t * const te, + uint8_t byte +) { +retry: + switch (te->state) { + case FHP_TE_STATE_INIT: + // set state + te->state = FHP_TE_STATE_SPACE; + goto retry; + + break; + case FHP_TE_STATE_NAME: + switch (byte) { + CASE_TE_CHARS + // add to buffer + fhp_te_parser_buf_push(te, byte); + + break; + CASE_OWS_CHARS + // flush buffer (if necessary) + if (te->len > 0) + fhp_te_parser_buf_flush(te); + + // check number of encodings + if (te->num_tes + 1 >= FHP_TE_MAX_TES) + return FHP_ERR_TOO_MANY_TES; + + // add to list of transfer encodings + // FIXME: check length + te->tes[te->num_tes] = te->buf_hash; + te->num_tes++; + + // set state + te->state = FHP_TE_STATE_IGNORE_UNTIL_COMMA; + + break; + case ',': + // flush buffer (if necessary) + if (te->len > 0) + fhp_te_parser_buf_flush(te); + + // check number of encodings + if (te->num_tes + 1 >= FHP_TE_MAX_TES) + return FHP_ERR_TOO_MANY_TES; + + // add to list of transfer encodings + // FIXME: check length + te->tes[te->num_tes] = te->buf_hash; + te->num_tes++; + + // set state + te->state = FHP_TE_STATE_SPACE; + + break; + default: + // invalid transfer encoding character + return FHP_ERR_INVALID_CHAR_IN_TE_NAME; + } + + break; + case FHP_TE_STATE_IGNORE_UNTIL_COMMA: + switch (byte) { + case ',': + // set state + te->state = FHP_TE_STATE_SPACE; + + break; + default: + // do nothing (ignore) + NULL; + } + + break; + case FHP_TE_STATE_SPACE: + switch (byte) { + CASE_OWS_CHARS + // ignore leading spaces + break; + CASE_TE_CHARS + // clear buffer, init hash + te->len = 0; + te->buf_hash = fhp_hash_init(); + + // set state + te->state = FHP_TE_STATE_NAME; + goto retry; + + break; + default: + // return error + return FHP_ERR_INVALID_CHAR_IN_TE; + } + + break; + case FHP_TE_STATE_ERROR: + // return last error + return te->err; + + break; + case FHP_TE_STATE_DONE: + // shouldn't be reached + return FHP_ERR_TE_PARSER_DONE; + + break; + default: + // never reached + return FHP_ERR_BAD_TE_STATE; + } + + // return succes + return FHP_OK; +} + +fhp_err_t +fhp_te_parser_push( + fhp_te_parser_t *te, + uint8_t * const buf, + size_t len +) { + for (size_t i = 0; i < len; i++) { + // push byte + fhp_err_t err = fhp_te_parser_push_byte(te, buf[len]); + + // check error + if (err != FHP_OK) { + // cache error + te->err = err; + + // set state + te->state = FHP_TE_STATE_ERROR; + + // return error + return err; + } + } + + // return succes + return FHP_OK; +} + +fhp_err_t +fhp_te_parser_done(fhp_te_parser_t * const te) { + uint8_t buf[1] = { 20 }; + + // flush data, check for error + fhp_err_t err = fhp_te_parser_push(te, buf, 1); + if (err != FHP_OK) + return err; + + // set state + te->state = FHP_TE_STATE_DONE; + + // return success + return FHP_OK; +} + +// // context functions // -static const fhp_t DEFAULT_CONTEXT = { +static const fhp_t FHP_DEFAULT_CONTEXT = { .state = FHP_STATE_INIT, .user_data = NULL, .cb = NULL, @@ -416,7 +627,7 @@ fhp_init( fhp_cb_t cb, void * const user_data ) { - *fhp = DEFAULT_CONTEXT; + *fhp = FHP_DEFAULT_CONTEXT; fhp->env = env ? env : fhp_get_default_env(); fhp->user_data = user_data; fhp->cb = cb; diff --git a/include/fhp/fhp.h b/include/fhp/fhp.h index 9d680a9..d537a6e 100644 --- a/include/fhp/fhp.h +++ b/include/fhp/fhp.h @@ -34,6 +34,11 @@ typedef enum { FHP_ERR_INVALID_ERROR, FHP_ERR_BUFFER_TOO_SMALL, FHP_ERR_INVALID_BODY_TYPE, + FHP_ERR_TOO_MANY_TES, + FHP_ERR_INVALID_CHAR_IN_TE_NAME, + FHP_ERR_INVALID_CHAR_IN_TE, + FHP_ERR_BAD_TE_STATE, + FHP_ERR_TE_PARSER_DONE, FHP_ERR_LAST } fhp_err_t; @@ -104,6 +109,46 @@ void fhp_env_init(fhp_env_t * const env); fhp_env_t *fhp_get_default_env(void); // +// transfer encoding parser functions +// + +typedef enum { + FHP_TE_STATE_INIT, + FHP_TE_STATE_NAME, + FHP_TE_STATE_IGNORE_UNTIL_COMMA, + FHP_TE_STATE_SPACE, + FHP_TE_STATE_ERROR, + FHP_TE_STATE_DONE, + FHP_TE_STATE_LAST +} fhp_te_state_t; + +#define FHP_TE_MAX_BUF_SIZE 16 +#define FHP_TE_MAX_TES 4 + +typedef struct { + fhp_te_state_t state; + + uint8_t buf[FHP_TE_MAX_BUF_SIZE]; + size_t len; + + uint32_t buf_hash; + + uint32_t tes[FHP_TE_MAX_TES]; + size_t num_tes; + + fhp_err_t err; +} fhp_te_parser_t; + +fhp_err_t +fhp_te_parser_init(fhp_te_parser_t * const te); + +fhp_err_t +fhp_te_parser_push(fhp_te_parser_t *, uint8_t * const, size_t); + +fhp_err_t +fhp_te_parser_done(fhp_te_parser_t * const); + +// // context functions // @@ -145,12 +145,54 @@ test_percent(void) { } } +/******************/ +/* te parser test */ +/******************/ + +static const char * +te_parser_tests[] = { + "gzip", + "deflate", + "gzip,deflate,chunked", + "gzip, deflate, chunked", + "deflate , gzip , chunked ", + " deflate , gzip , chunked ", + NULL +}; + +static void +test_te_parser(void) { + for (size_t i = 0; te_parser_tests[i]; i++) { + fhp_err_t err; + + // get test string + const char *s = te_parser_tests[i]; + + // init parser + fhp_te_parser_t p; + fhp_te_parser_init(&p); + + // parse data + if ((err = fhp_te_parser_push(&p, s, strlen(s))) != FHP_OK) { + die("test_te_parser", "fhp_te_parser_push", err); + } + + // finish parsing + if ((err = fhp_te_parser_done(&p)) != FHP_OK) { + die("test_te_parser", "fhp_te_parser_push", err); + } + + fprintf(stderr, "te test: \"%s\", p->num_tes = %u\n", s, p.num_tes); + } +} + int main(int argc, char *argv[]) { UNUSED(argc); UNUSED(argv); test_basic(); test_percent(); + test_te_parser(); return EXIT_SUCCESS; } |