diff options
-rw-r--r-- | fhp.c | 117 | ||||
-rw-r--r-- | include/fhp/fhp.h | 23 |
2 files changed, 114 insertions, 26 deletions
@@ -235,6 +235,7 @@ fhp_errors[] = { "invalid character after carriage return", "invalid character in HTTP header name", "invalid error code", + "invalid body type", "buffer too small", }; @@ -343,6 +344,8 @@ fhp_strings[] = { "x-gzip", "deflate", "x-deflate", + "compress", + "x-compress", "chunked", NULL }; @@ -362,6 +365,8 @@ typedef enum { FHP_STR_X_GZIP, FHP_STR_DEFLATE, FHP_STR_X_DEFLATE, + FHP_STR_COMPRESS, + FHP_STR_X_COMPRESS, FHP_STR_CHUNKED, FHP_STR_LAST } fhp_str_t; @@ -399,6 +404,9 @@ static const fhp_t DEFAULT_CONTEXT = { .buf_len = 0, .is_hashing = false, .header_name_hash = 0, + .body_type = FHP_BODY_TYPE_NONE, + .content_length = 0, + .num_tes = 0, }; fhp_err_t @@ -452,7 +460,7 @@ fhp_buf_push( uint8_t byte ) { // flush buffer - if (fhp->buf_len + 1 >= FHP_BUF_SIZE) { + if (fhp->buf_len + 1 >= FHP_MAX_BUF_SIZE) { if (!fhp_buf_flush(fhp, token)) return false; } @@ -466,6 +474,77 @@ fhp_buf_push( } static fhp_err_t +fhp_handle_method(fhp_t * const fhp) { + // get method token + fhp->http_method = FHP_TOKEN_METHOD_OTHER; + if (fhp->buf_hash == fhp->env->hashes[FHP_STR_GET]) { + fhp->http_method = FHP_TOKEN_METHOD_GET; + } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_POST]) { + fhp->http_method = FHP_TOKEN_METHOD_POST; + } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_HEAD]) { + fhp->http_method = FHP_TOKEN_METHOD_HEAD; + } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_PUT]) { + fhp->http_method = FHP_TOKEN_METHOD_PUT; + } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_DELETE]) { + fhp->http_method = FHP_TOKEN_METHOD_DELETE; + } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_OPTIONS]) { + fhp->http_method = FHP_TOKEN_METHOD_OPTIONS; + } + + // send method token + if (!fhp->cb(fhp, fhp->http_method, 0, 0)) + return FHP_ERR_CB; + + // return success + return FHP_OK; +} + +// FIXME: follow RFC7230 3.3.3 +// https://tools.ietf.org/html/rfc7230#section-3.3.3 +static fhp_err_t +fhp_handle_header_name(fhp_t * const fhp) { + uint32_t hash = fhp->header_name_hash; + + if (hash == fhp->env->hashes[FHP_STR_CONTENT_LENGTH]) { + switch (fhp->body_type) { + case FHP_BODY_TYPE_CONTENT_LENGTH: + // duplicate content-length, ignore first one + // FIXME: should print warning here + case FHP_BODY_TYPE_NONE: + fhp->body_type = FHP_BODY_TYPE_CONTENT_LENGTH; + break; + case FHP_BODY_TYPE_TRANSFER_ENCODING: + // ignore content-length + // FIXME: should print warnings + break; + default: + // never reached + return FHP_ERR_INVALID_BODY_TYPE; + } + } else if (hash == fhp->env->hashes[FHP_STR_TRANSFER_ENCODING]) { + switch (fhp->body_type) { + case FHP_BODY_TYPE_CONTENT_LENGTH: + // duplicate content-length, ignore first one + // FIXME: should print warning here + case FHP_BODY_TYPE_NONE: + fhp->body_type = FHP_BODY_TYPE_TRANSFER_ENCODING; + break; + case FHP_BODY_TYPE_TRANSFER_ENCODING: + // ignore content-length + // FIXME: should print warnings + break; + default: + // never reached + return FHP_ERR_INVALID_BODY_TYPE; + } + } + + // return success + return FHP_OK; +} + + +static fhp_err_t fhp_push_byte( fhp_t * const fhp, uint8_t byte @@ -513,25 +592,10 @@ retry: if (!fhp->cb(fhp, FHP_TOKEN_METHOD_END, 0, 0)) return FHP_ERR_CB; - // get method token - fhp->http_method = FHP_TOKEN_METHOD_OTHER; - if (fhp->buf_hash == fhp->env->hashes[FHP_STR_GET]) { - fhp->http_method = FHP_TOKEN_METHOD_GET; - } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_POST]) { - fhp->http_method = FHP_TOKEN_METHOD_POST; - } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_HEAD]) { - fhp->http_method = FHP_TOKEN_METHOD_HEAD; - } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_PUT]) { - fhp->http_method = FHP_TOKEN_METHOD_PUT; - } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_DELETE]) { - fhp->http_method = FHP_TOKEN_METHOD_DELETE; - } else if (fhp->buf_hash == fhp->env->hashes[FHP_STR_OPTIONS]) { - fhp->http_method = FHP_TOKEN_METHOD_OPTIONS; - } - - // send method token - if (!fhp->cb(fhp, fhp->http_method, 0, 0)) - return FHP_ERR_CB; + // handle method + fhp_err_t err = fhp_handle_method(fhp); + if (err != FHP_OK) + return err; // set state fhp->state = FHP_STATE_METHOD_END; @@ -769,14 +833,19 @@ retry: if (!fhp_buf_flush(fhp, FHP_TOKEN_HEADER_NAME_FRAGMENT)) return FHP_ERR_CB; + // disable buffer hashing and cache header name hash + fhp->is_hashing = false; + fhp->header_name_hash = fhp->buf_hash; + + // handle header name + fhp_err_t err = fhp_handle_header_name(fhp); + if (err != FHP_OK) + return err; + // send end token if (!fhp->cb(fhp, FHP_TOKEN_HEADER_NAME_END, 0, 0)) return FHP_ERR_CB; - // disable buffer hashing - fhp->is_hashing = false; - fhp->header_name_hash = fhp->buf_hash; - // set state fhp->state = FHP_STATE_HEADER_NAME_END; diff --git a/include/fhp/fhp.h b/include/fhp/fhp.h index 3337cbe..9d680a9 100644 --- a/include/fhp/fhp.h +++ b/include/fhp/fhp.h @@ -33,6 +33,7 @@ typedef enum { FHP_ERR_INVALID_CHAR_IN_HEADER_NAME, FHP_ERR_INVALID_ERROR, FHP_ERR_BUFFER_TOO_SMALL, + FHP_ERR_INVALID_BODY_TYPE, FHP_ERR_LAST } fhp_err_t; @@ -82,6 +83,13 @@ typedef enum { fhp_err_t fhp_strtoken(fhp_token_t, char * const, size_t); +typedef enum { + FHP_BODY_TYPE_NONE, + FHP_BODY_TYPE_CONTENT_LENGTH, + FHP_BODY_TYPE_TRANSFER_ENCODING, + FHP_BODY_TYPE_LAST +} fhp_body_type_t; + // // env functions // @@ -133,7 +141,8 @@ typedef enum { FHP_STATE_LAST } fhp_state_t; -#define FHP_BUF_SIZE 1024 +#define FHP_MAX_BUF_SIZE 1024 +#define FHP_MAX_TRANSFER_ENCODINGS 4 struct fhp_t_ { // env pointer @@ -155,7 +164,7 @@ struct fhp_t_ { uint64_t ofs; // fragment data buffer - uint8_t buf[FHP_BUF_SIZE]; + uint8_t buf[FHP_MAX_BUF_SIZE]; size_t buf_len; // buffer hashing state @@ -168,6 +177,16 @@ struct fhp_t_ { // hash of last header name uint32_t header_name_hash; + // request body type + fhp_body_type_t body_type; + + // content length + uint64_t content_length; + + // transfer encodings + uint32_t tes[FHP_MAX_TRANSFER_ENCODINGS]; + size_t num_tes; + // state for url hex decoder uint32_t hex; }; |