From 843b3f2ced993fab157cfdd6e55b10ce8dfa13a7 Mon Sep 17 00:00:00 2001 From: Paul Duncan Date: Sun, 28 Aug 2016 15:32:27 -0400 Subject: mv fhp.c ctx.c --- Makefile | 2 +- ctx.c | 638 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fhp.c | 638 --------------------------------------------------------------- 3 files changed, 639 insertions(+), 639 deletions(-) create mode 100644 ctx.c delete mode 100644 fhp.c diff --git a/Makefile b/Makefile index 0de2951..29e9424 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC ?= cc CFLAGS=-std=c99 -W -Wall -pedantic -O2 -Iinclude -fPIC -OBJS=fhp.o hash.o error.o token.o env.o te-parser.o cl-parser.o header-value-parser.o +OBJS=ctx.o hash.o error.o token.o env.o te-parser.o cl-parser.o header-value-parser.o SONAME=libfhp.so LIB=libfhp.so STATIC_LIB=libfhp.a diff --git a/ctx.c b/ctx.c new file mode 100644 index 0000000..e73fba8 --- /dev/null +++ b/ctx.c @@ -0,0 +1,638 @@ +#include "internal.h" + +// +// context functions +// + +static const fhp_ctx_t FHP_DEFAULT_CONTEXT = { + .state = FHP_STATE_INIT, + .user_data = NULL, + .cb = NULL, + .err = FHP_OK, + .ofs = 0, + .buf_len = 0, + .is_hashing = false, + .header_name_hash = 0, + .header_value_parser = FHP_HEADER_VALUE_PARSER_NONE, + .body_type = FHP_BODY_TYPE_NONE, + .content_length = 0, + .num_tes = 0, +}; + +fhp_err_t +fhp_ctx_init( + fhp_ctx_t * const ctx, + fhp_env_t * const env, + fhp_cb_t cb, + void * const user_data +) { + *ctx = FHP_DEFAULT_CONTEXT; + ctx->env = env ? env : fhp_get_default_env(); + ctx->user_data = user_data; + ctx->cb = cb; + + /* return success */ + return FHP_OK; +} + +static void +fhp_ctx_buf_clear(fhp_ctx_t * const ctx) { + // clear buffer + ctx->buf_len = 0; +} + +static fhp_err_t +fhp_ctx_buf_flush( + fhp_ctx_t * const ctx, + fhp_token_t token +) { + if (ctx->buf_len > 0) { + // push data + if (!ctx->cb(ctx, token, ctx->buf, ctx->buf_len)) + return FHP_ERR_CB; + + // update buffer hash + if (ctx->is_hashing) + ctx->buf_hash = fhp_lc_hash_push(ctx->buf_hash, ctx->buf, ctx->buf_len); + + // push to header value parser + fhp_err_t err; + if ((err = fhp_header_value_parser_push(ctx, ctx->buf, ctx->buf_len)) != FHP_OK) + return err; + + // clear buffer + fhp_ctx_buf_clear(ctx); + } + + // return success + return FHP_OK; +} + +static fhp_err_t +fhp_ctx_buf_push( + fhp_ctx_t * const ctx, + fhp_token_t token, + uint8_t byte +) { + // flush buffer + if (ctx->buf_len + 1 >= FHP_CTX_MAX_BUF_SIZE) { + fhp_err_t err; + if ((err = fhp_ctx_buf_flush(ctx, token)) != FHP_OK) + return err; + } + + // append to buffer + ctx->buf[ctx->buf_len] = byte; + ctx->buf_len++; + + // return success + return FHP_OK; +} + +static fhp_err_t +fhp_ctx_handle_method(fhp_ctx_t * const ctx) { + // get method token + ctx->http_method = FHP_TOKEN_METHOD_OTHER; + if (ctx->buf_hash == ctx->env->hashes[FHP_STR_GET]) { + ctx->http_method = FHP_TOKEN_METHOD_GET; + } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_POST]) { + ctx->http_method = FHP_TOKEN_METHOD_POST; + } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_HEAD]) { + ctx->http_method = FHP_TOKEN_METHOD_HEAD; + } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_PUT]) { + ctx->http_method = FHP_TOKEN_METHOD_PUT; + } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_DELETE]) { + ctx->http_method = FHP_TOKEN_METHOD_DELETE; + } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_OPTIONS]) { + ctx->http_method = FHP_TOKEN_METHOD_OPTIONS; + } + + // send method token + if (!ctx->cb(ctx, ctx->http_method, 0, 0)) + return FHP_ERR_CB; + + // return success + return FHP_OK; +} + +static fhp_err_t +fhp_ctx_push_byte( + fhp_ctx_t * const ctx, + uint8_t byte +) { + fhp_err_t err; + +retry: + switch (ctx->state) { + case FHP_STATE_INIT: + switch (byte) { + CASE_TOKEN_CHARS + // send start token + if (!ctx->cb(ctx, FHP_TOKEN_METHOD_START, 0, 0)) + return FHP_ERR_CB; + + // enable buffer hashing + ctx->is_hashing = true; + ctx->buf_hash = fhp_hash_init(); + + // set state + ctx->state = FHP_STATE_METHOD; + goto retry; + + break; + default: + return FHP_ERR_INVALID_CHAR_IN_METHOD; + } + + break; + case FHP_STATE_METHOD: + switch (byte) { + CASE_TOKEN_CHARS + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_METHOD_FRAGMENT, byte)) != FHP_OK) + return err; + + break; + case ' ': + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_METHOD_FRAGMENT)) != FHP_OK) + return err; + + // disable buffer hashing + ctx->is_hashing = false; + + // send end token + if (!ctx->cb(ctx, FHP_TOKEN_METHOD_END, 0, 0)) + return FHP_ERR_CB; + + // handle method + fhp_err_t err = fhp_ctx_handle_method(ctx); + if (err != FHP_OK) + return err; + + // set state + ctx->state = FHP_STATE_METHOD_END; + goto retry; + + break; + default: + return FHP_ERR_INVALID_CHAR_IN_METHOD; + } + + break; + case FHP_STATE_METHOD_END: + switch (byte) { + case ' ': + // FIXME: do we want to allow more than one whitespace? + // ignore + break; + CASE_URL_CHARS + if (!ctx->cb(ctx, FHP_TOKEN_URL_START, 0, 0)) + return FHP_ERR_CB; + + ctx->state = FHP_STATE_URL; + goto retry; + default: + return FHP_ERR_INVALID_CHAR_IN_URL; + } + + break; + case FHP_STATE_URL: + switch (byte) { + case ' ': + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_URL_FRAGMENT)) != FHP_OK) + return err; + + // send end token + if (!ctx->cb(ctx, FHP_TOKEN_URL_END, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_URL_END; + goto retry; + + break; + case '%': + // set state + ctx->state = FHP_STATE_URL_PERCENT; + + break; + CASE_URL_CHARS + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_URL_FRAGMENT, byte)) != FHP_OK) + return err; + } + + break; + case FHP_STATE_URL_PERCENT: + switch (byte) { + CASE_DIGIT_CHARS + ctx->hex = byte - '0'; + ctx->state = FHP_STATE_URL_PERCENT_LAST; + + break; + CASE_HEX_ALPHA_CHARS + ctx->hex = 10 + byte - ((byte >= 'a') ? 'a' : 'A'); + ctx->state = FHP_STATE_URL_PERCENT_LAST; + + break; + default: + return FHP_ERR_INVALID_CHAR_IN_URL_PERCENT; + } + + break; + case FHP_STATE_URL_PERCENT_LAST: + switch (byte) { + CASE_DIGIT_CHARS + ctx->hex = (ctx->hex << 4) + (byte - '0'); + break; + CASE_HEX_ALPHA_CHARS + ctx->hex = (ctx->hex << 4) + (10 + byte - ((byte >= 'a') ? 'a' : 'A')); + break; + default: + return FHP_ERR_INVALID_CHAR_IN_URL_PERCENT; + } + + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_URL_FRAGMENT, ctx->hex)) != FHP_OK) + return err; + + // set state + ctx->state = FHP_STATE_URL; + + break; + case FHP_STATE_URL_END: + switch (byte) { + case ' ': + // ignore + break; + default: + if (!ctx->cb(ctx, FHP_TOKEN_VERSION_START, 0, 0)) + return FHP_ERR_CB; + + // enable buffer hashing + ctx->is_hashing = true; + ctx->buf_hash = fhp_hash_init(); + + // set state + ctx->state = FHP_STATE_VERSION; + goto retry; + } + + break; + case FHP_STATE_VERSION: + switch (byte) { + case '\r': + case '\n': + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_VERSION_FRAGMENT)) != FHP_OK) + return err; + + // send end token + if (!ctx->cb(ctx, FHP_TOKEN_VERSION_END, 0, 0)) + return FHP_ERR_CB; + + // disable buffer hashing + ctx->is_hashing = false; + + // get version token + ctx->http_version = FHP_TOKEN_VERSION_OTHER; + if (ctx->buf_hash == ctx->env->hashes[FHP_STR_HTTP_10]) { + ctx->http_version = FHP_TOKEN_VERSION_HTTP_10; + } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_HTTP_11]) { + ctx->http_version = FHP_TOKEN_VERSION_HTTP_11; + } + + // send version token + if (!ctx->cb(ctx, ctx->http_version, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_VERSION_END; + goto retry; + + break; + CASE_VERSION_CHARS + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_VERSION_FRAGMENT, byte)) != FHP_OK) + return err; + + break; + default: + return FHP_ERR_INVALID_CHAR_IN_VERSION; + } + + break; + case FHP_STATE_VERSION_END: + switch (byte) { + case '\r': + ctx->state = FHP_STATE_VERSION_END_CR; + + break; + case '\n': + ctx->state = FHP_STATE_STATUS_END; + + break; + default: + // invalid character + // (should never be reached) + return FHP_ERR_INVALID_CHAR; + }; + + break; + case FHP_STATE_VERSION_END_CR: + switch (byte) { + case '\n': + ctx->state = FHP_STATE_STATUS_END; + + break; + default: + return FHP_ERR_INVALID_CHAR_AFTER_CR; + }; + + break; + case FHP_STATE_STATUS_END: + switch (byte) { + CASE_TOKEN_CHARS + // send start token + if (!ctx->cb(ctx, FHP_TOKEN_HEADER_NAME_START, 0, 0)) + return FHP_ERR_CB; + + // enable buffer hashing + ctx->is_hashing = true; + ctx->buf_hash = fhp_hash_init(); + + // set state + ctx->state = FHP_STATE_HEADER_NAME; + goto retry; + + break; + case '\r': + // set state + ctx->state = FHP_STATE_STATUS_END_CR; + + break; + case '\n': + // set state + ctx->state = FHP_STATE_HEADERS_END; + + break; + } + + break; + case FHP_STATE_STATUS_END_CR: + switch (byte) { + case '\n': + // set state + ctx->state = FHP_STATE_HEADERS_END; + + break; + default: + return FHP_ERR_INVALID_CHAR_AFTER_CR; + } + + break; + case FHP_STATE_HEADER_NAME: + switch (byte) { + CASE_TOKEN_CHARS + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_NAME_FRAGMENT, byte)) != FHP_OK) + return err; + + break; + case ':': + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_NAME_FRAGMENT)) != FHP_OK) + return err; + + // disable buffer hashing and cache header name hash + ctx->is_hashing = false; + ctx->header_name_hash = ctx->buf_hash; + + // send end token + if (!ctx->cb(ctx, FHP_TOKEN_HEADER_NAME_END, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_HEADER_NAME_END; + + break; + default: + return FHP_ERR_INVALID_CHAR_IN_HEADER_NAME; + } + + break; + case FHP_STATE_HEADER_NAME_END: + switch (byte) { + CASE_OWS_CHARS + // ignore leading spaces + + break; + default: + // send start token + if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_START, 0, 0)) + return FHP_ERR_CB; + + // init header value parser + fhp_err_t err; + if ((err = fhp_header_value_parser_init(ctx)) != FHP_OK) + return err; + + // set state + ctx->state = FHP_STATE_HEADER_VALUE; + goto retry; + + break; + } + + break; + case FHP_STATE_HEADER_VALUE: + switch (byte) { + case '\r': + ctx->state = FHP_STATE_HEADER_VALUE_END_CR; + + break; + case '\n': + ctx->state = FHP_STATE_HEADER_VALUE_END; + break; + default: + // FIXME: need more limits on valid octets + + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT, byte)) != FHP_OK) + return err; + + break; + } + + break; + case FHP_STATE_HEADER_VALUE_END_CR: + switch (byte) { + case '\n': + ctx->state = FHP_STATE_HEADER_VALUE_END; + break; + default: + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADER_VALUE_END: + switch (byte) { + CASE_OWS_CHARS + // ows-fold + + // add space to buffer + // folding to ' ', as per RFC7230 3.2.4 + // https://tools.ietf.org/html/rfc7230#section-3.2.4 + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT, ' ')) != FHP_OK) + return err; + + // set state + ctx->state = FHP_STATE_HEADER_VALUE; + + break; + CASE_TOKEN_CHARS + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) != FHP_OK) + return err; + + // end header value + if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) + return FHP_ERR_CB; + + // end header value parser + if ((err = fhp_header_value_parser_done(ctx)) != FHP_OK) + return err; + + // send start token + if (!ctx->cb(ctx, FHP_TOKEN_HEADER_NAME_START, 0, 0)) + return FHP_ERR_CB; + + // enable buffer hashing + ctx->is_hashing = true; + ctx->buf_hash = fhp_hash_init(); + + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_NAME_FRAGMENT, byte)) != FHP_OK) + return err; + + // set state + ctx->state = FHP_STATE_HEADER_NAME; + + break; + case '\r': + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) != FHP_OK) + return err; + + // end header value + if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) + return FHP_ERR_CB; + + // end header value parser + if ((err = fhp_header_value_parser_done(ctx)) != FHP_OK) + return err; + + // set state + ctx->state = FHP_STATE_HEADERS_END_CR; + + break; + case '\n': + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) != FHP_OK) + return err; + + // end header value + if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) + return FHP_ERR_CB; + + // end header value parser + if ((err = fhp_header_value_parser_done(ctx)) != FHP_OK) + return err; + + // set state + ctx->state = FHP_STATE_HEADERS_END; + + break; + default: + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADERS_END_CR: + switch (byte) { + case '\n': + ctx->state = FHP_STATE_HEADERS_END; + + break; + default: + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADERS_END: + // TODO + + break; + default: + // invalid state + // (should never be reached) + return FHP_ERR_BAD_STATE; + } + + // increment byte offset + ctx->ofs++; + + /* return success */ + return FHP_OK; +} + +fhp_err_t +fhp_ctx_push( + fhp_ctx_t * const ctx, + uint8_t * const buf, + size_t len +) { + switch (ctx->state) { + case FHP_STATE_ERROR: + return ctx->err; + + break; + default: + for (size_t i = 0; i < len; i++) { + // push byte + fhp_err_t err = fhp_ctx_push_byte(ctx, buf[i]); + + // check result + if (err != FHP_OK) { + ctx->state = FHP_STATE_ERROR; + ctx->err = err; + return err; + } + } + } + + // return success + return FHP_OK; +} + +fhp_env_t * +fhp_ctx_get_env(fhp_ctx_t * const ctx) { + return ctx->env; +} + +void * +fhp_ctx_get_user_data(fhp_ctx_t * const ctx) { + return ctx->user_data; +} + +size_t +fhp_ctx_get_num_tes(fhp_ctx_t * const ctx) { + return ctx->num_tes; +} + +uint64_t +fhp_ctx_get_content_length(fhp_ctx_t * const ctx) { + return ctx->content_length; +} diff --git a/fhp.c b/fhp.c deleted file mode 100644 index e73fba8..0000000 --- a/fhp.c +++ /dev/null @@ -1,638 +0,0 @@ -#include "internal.h" - -// -// context functions -// - -static const fhp_ctx_t FHP_DEFAULT_CONTEXT = { - .state = FHP_STATE_INIT, - .user_data = NULL, - .cb = NULL, - .err = FHP_OK, - .ofs = 0, - .buf_len = 0, - .is_hashing = false, - .header_name_hash = 0, - .header_value_parser = FHP_HEADER_VALUE_PARSER_NONE, - .body_type = FHP_BODY_TYPE_NONE, - .content_length = 0, - .num_tes = 0, -}; - -fhp_err_t -fhp_ctx_init( - fhp_ctx_t * const ctx, - fhp_env_t * const env, - fhp_cb_t cb, - void * const user_data -) { - *ctx = FHP_DEFAULT_CONTEXT; - ctx->env = env ? env : fhp_get_default_env(); - ctx->user_data = user_data; - ctx->cb = cb; - - /* return success */ - return FHP_OK; -} - -static void -fhp_ctx_buf_clear(fhp_ctx_t * const ctx) { - // clear buffer - ctx->buf_len = 0; -} - -static fhp_err_t -fhp_ctx_buf_flush( - fhp_ctx_t * const ctx, - fhp_token_t token -) { - if (ctx->buf_len > 0) { - // push data - if (!ctx->cb(ctx, token, ctx->buf, ctx->buf_len)) - return FHP_ERR_CB; - - // update buffer hash - if (ctx->is_hashing) - ctx->buf_hash = fhp_lc_hash_push(ctx->buf_hash, ctx->buf, ctx->buf_len); - - // push to header value parser - fhp_err_t err; - if ((err = fhp_header_value_parser_push(ctx, ctx->buf, ctx->buf_len)) != FHP_OK) - return err; - - // clear buffer - fhp_ctx_buf_clear(ctx); - } - - // return success - return FHP_OK; -} - -static fhp_err_t -fhp_ctx_buf_push( - fhp_ctx_t * const ctx, - fhp_token_t token, - uint8_t byte -) { - // flush buffer - if (ctx->buf_len + 1 >= FHP_CTX_MAX_BUF_SIZE) { - fhp_err_t err; - if ((err = fhp_ctx_buf_flush(ctx, token)) != FHP_OK) - return err; - } - - // append to buffer - ctx->buf[ctx->buf_len] = byte; - ctx->buf_len++; - - // return success - return FHP_OK; -} - -static fhp_err_t -fhp_ctx_handle_method(fhp_ctx_t * const ctx) { - // get method token - ctx->http_method = FHP_TOKEN_METHOD_OTHER; - if (ctx->buf_hash == ctx->env->hashes[FHP_STR_GET]) { - ctx->http_method = FHP_TOKEN_METHOD_GET; - } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_POST]) { - ctx->http_method = FHP_TOKEN_METHOD_POST; - } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_HEAD]) { - ctx->http_method = FHP_TOKEN_METHOD_HEAD; - } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_PUT]) { - ctx->http_method = FHP_TOKEN_METHOD_PUT; - } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_DELETE]) { - ctx->http_method = FHP_TOKEN_METHOD_DELETE; - } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_OPTIONS]) { - ctx->http_method = FHP_TOKEN_METHOD_OPTIONS; - } - - // send method token - if (!ctx->cb(ctx, ctx->http_method, 0, 0)) - return FHP_ERR_CB; - - // return success - return FHP_OK; -} - -static fhp_err_t -fhp_ctx_push_byte( - fhp_ctx_t * const ctx, - uint8_t byte -) { - fhp_err_t err; - -retry: - switch (ctx->state) { - case FHP_STATE_INIT: - switch (byte) { - CASE_TOKEN_CHARS - // send start token - if (!ctx->cb(ctx, FHP_TOKEN_METHOD_START, 0, 0)) - return FHP_ERR_CB; - - // enable buffer hashing - ctx->is_hashing = true; - ctx->buf_hash = fhp_hash_init(); - - // set state - ctx->state = FHP_STATE_METHOD; - goto retry; - - break; - default: - return FHP_ERR_INVALID_CHAR_IN_METHOD; - } - - break; - case FHP_STATE_METHOD: - switch (byte) { - CASE_TOKEN_CHARS - // add to buffer - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_METHOD_FRAGMENT, byte)) != FHP_OK) - return err; - - break; - case ' ': - // flush buffer - if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_METHOD_FRAGMENT)) != FHP_OK) - return err; - - // disable buffer hashing - ctx->is_hashing = false; - - // send end token - if (!ctx->cb(ctx, FHP_TOKEN_METHOD_END, 0, 0)) - return FHP_ERR_CB; - - // handle method - fhp_err_t err = fhp_ctx_handle_method(ctx); - if (err != FHP_OK) - return err; - - // set state - ctx->state = FHP_STATE_METHOD_END; - goto retry; - - break; - default: - return FHP_ERR_INVALID_CHAR_IN_METHOD; - } - - break; - case FHP_STATE_METHOD_END: - switch (byte) { - case ' ': - // FIXME: do we want to allow more than one whitespace? - // ignore - break; - CASE_URL_CHARS - if (!ctx->cb(ctx, FHP_TOKEN_URL_START, 0, 0)) - return FHP_ERR_CB; - - ctx->state = FHP_STATE_URL; - goto retry; - default: - return FHP_ERR_INVALID_CHAR_IN_URL; - } - - break; - case FHP_STATE_URL: - switch (byte) { - case ' ': - // flush buffer - if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_URL_FRAGMENT)) != FHP_OK) - return err; - - // send end token - if (!ctx->cb(ctx, FHP_TOKEN_URL_END, 0, 0)) - return FHP_ERR_CB; - - // set state - ctx->state = FHP_STATE_URL_END; - goto retry; - - break; - case '%': - // set state - ctx->state = FHP_STATE_URL_PERCENT; - - break; - CASE_URL_CHARS - // add to buffer - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_URL_FRAGMENT, byte)) != FHP_OK) - return err; - } - - break; - case FHP_STATE_URL_PERCENT: - switch (byte) { - CASE_DIGIT_CHARS - ctx->hex = byte - '0'; - ctx->state = FHP_STATE_URL_PERCENT_LAST; - - break; - CASE_HEX_ALPHA_CHARS - ctx->hex = 10 + byte - ((byte >= 'a') ? 'a' : 'A'); - ctx->state = FHP_STATE_URL_PERCENT_LAST; - - break; - default: - return FHP_ERR_INVALID_CHAR_IN_URL_PERCENT; - } - - break; - case FHP_STATE_URL_PERCENT_LAST: - switch (byte) { - CASE_DIGIT_CHARS - ctx->hex = (ctx->hex << 4) + (byte - '0'); - break; - CASE_HEX_ALPHA_CHARS - ctx->hex = (ctx->hex << 4) + (10 + byte - ((byte >= 'a') ? 'a' : 'A')); - break; - default: - return FHP_ERR_INVALID_CHAR_IN_URL_PERCENT; - } - - // add to buffer - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_URL_FRAGMENT, ctx->hex)) != FHP_OK) - return err; - - // set state - ctx->state = FHP_STATE_URL; - - break; - case FHP_STATE_URL_END: - switch (byte) { - case ' ': - // ignore - break; - default: - if (!ctx->cb(ctx, FHP_TOKEN_VERSION_START, 0, 0)) - return FHP_ERR_CB; - - // enable buffer hashing - ctx->is_hashing = true; - ctx->buf_hash = fhp_hash_init(); - - // set state - ctx->state = FHP_STATE_VERSION; - goto retry; - } - - break; - case FHP_STATE_VERSION: - switch (byte) { - case '\r': - case '\n': - // flush buffer - if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_VERSION_FRAGMENT)) != FHP_OK) - return err; - - // send end token - if (!ctx->cb(ctx, FHP_TOKEN_VERSION_END, 0, 0)) - return FHP_ERR_CB; - - // disable buffer hashing - ctx->is_hashing = false; - - // get version token - ctx->http_version = FHP_TOKEN_VERSION_OTHER; - if (ctx->buf_hash == ctx->env->hashes[FHP_STR_HTTP_10]) { - ctx->http_version = FHP_TOKEN_VERSION_HTTP_10; - } else if (ctx->buf_hash == ctx->env->hashes[FHP_STR_HTTP_11]) { - ctx->http_version = FHP_TOKEN_VERSION_HTTP_11; - } - - // send version token - if (!ctx->cb(ctx, ctx->http_version, 0, 0)) - return FHP_ERR_CB; - - // set state - ctx->state = FHP_STATE_VERSION_END; - goto retry; - - break; - CASE_VERSION_CHARS - // add to buffer - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_VERSION_FRAGMENT, byte)) != FHP_OK) - return err; - - break; - default: - return FHP_ERR_INVALID_CHAR_IN_VERSION; - } - - break; - case FHP_STATE_VERSION_END: - switch (byte) { - case '\r': - ctx->state = FHP_STATE_VERSION_END_CR; - - break; - case '\n': - ctx->state = FHP_STATE_STATUS_END; - - break; - default: - // invalid character - // (should never be reached) - return FHP_ERR_INVALID_CHAR; - }; - - break; - case FHP_STATE_VERSION_END_CR: - switch (byte) { - case '\n': - ctx->state = FHP_STATE_STATUS_END; - - break; - default: - return FHP_ERR_INVALID_CHAR_AFTER_CR; - }; - - break; - case FHP_STATE_STATUS_END: - switch (byte) { - CASE_TOKEN_CHARS - // send start token - if (!ctx->cb(ctx, FHP_TOKEN_HEADER_NAME_START, 0, 0)) - return FHP_ERR_CB; - - // enable buffer hashing - ctx->is_hashing = true; - ctx->buf_hash = fhp_hash_init(); - - // set state - ctx->state = FHP_STATE_HEADER_NAME; - goto retry; - - break; - case '\r': - // set state - ctx->state = FHP_STATE_STATUS_END_CR; - - break; - case '\n': - // set state - ctx->state = FHP_STATE_HEADERS_END; - - break; - } - - break; - case FHP_STATE_STATUS_END_CR: - switch (byte) { - case '\n': - // set state - ctx->state = FHP_STATE_HEADERS_END; - - break; - default: - return FHP_ERR_INVALID_CHAR_AFTER_CR; - } - - break; - case FHP_STATE_HEADER_NAME: - switch (byte) { - CASE_TOKEN_CHARS - // add to buffer - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_NAME_FRAGMENT, byte)) != FHP_OK) - return err; - - break; - case ':': - // flush buffer - if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_NAME_FRAGMENT)) != FHP_OK) - return err; - - // disable buffer hashing and cache header name hash - ctx->is_hashing = false; - ctx->header_name_hash = ctx->buf_hash; - - // send end token - if (!ctx->cb(ctx, FHP_TOKEN_HEADER_NAME_END, 0, 0)) - return FHP_ERR_CB; - - // set state - ctx->state = FHP_STATE_HEADER_NAME_END; - - break; - default: - return FHP_ERR_INVALID_CHAR_IN_HEADER_NAME; - } - - break; - case FHP_STATE_HEADER_NAME_END: - switch (byte) { - CASE_OWS_CHARS - // ignore leading spaces - - break; - default: - // send start token - if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_START, 0, 0)) - return FHP_ERR_CB; - - // init header value parser - fhp_err_t err; - if ((err = fhp_header_value_parser_init(ctx)) != FHP_OK) - return err; - - // set state - ctx->state = FHP_STATE_HEADER_VALUE; - goto retry; - - break; - } - - break; - case FHP_STATE_HEADER_VALUE: - switch (byte) { - case '\r': - ctx->state = FHP_STATE_HEADER_VALUE_END_CR; - - break; - case '\n': - ctx->state = FHP_STATE_HEADER_VALUE_END; - break; - default: - // FIXME: need more limits on valid octets - - // add to buffer - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT, byte)) != FHP_OK) - return err; - - break; - } - - break; - case FHP_STATE_HEADER_VALUE_END_CR: - switch (byte) { - case '\n': - ctx->state = FHP_STATE_HEADER_VALUE_END; - break; - default: - return FHP_ERR_INVALID_CHAR; - } - - break; - case FHP_STATE_HEADER_VALUE_END: - switch (byte) { - CASE_OWS_CHARS - // ows-fold - - // add space to buffer - // folding to ' ', as per RFC7230 3.2.4 - // https://tools.ietf.org/html/rfc7230#section-3.2.4 - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT, ' ')) != FHP_OK) - return err; - - // set state - ctx->state = FHP_STATE_HEADER_VALUE; - - break; - CASE_TOKEN_CHARS - // flush buffer - if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) != FHP_OK) - return err; - - // end header value - if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) - return FHP_ERR_CB; - - // end header value parser - if ((err = fhp_header_value_parser_done(ctx)) != FHP_OK) - return err; - - // send start token - if (!ctx->cb(ctx, FHP_TOKEN_HEADER_NAME_START, 0, 0)) - return FHP_ERR_CB; - - // enable buffer hashing - ctx->is_hashing = true; - ctx->buf_hash = fhp_hash_init(); - - // add to buffer - if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_HEADER_NAME_FRAGMENT, byte)) != FHP_OK) - return err; - - // set state - ctx->state = FHP_STATE_HEADER_NAME; - - break; - case '\r': - // flush buffer - if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) != FHP_OK) - return err; - - // end header value - if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) - return FHP_ERR_CB; - - // end header value parser - if ((err = fhp_header_value_parser_done(ctx)) != FHP_OK) - return err; - - // set state - ctx->state = FHP_STATE_HEADERS_END_CR; - - break; - case '\n': - // flush buffer - if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) != FHP_OK) - return err; - - // end header value - if (!ctx->cb(ctx, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) - return FHP_ERR_CB; - - // end header value parser - if ((err = fhp_header_value_parser_done(ctx)) != FHP_OK) - return err; - - // set state - ctx->state = FHP_STATE_HEADERS_END; - - break; - default: - return FHP_ERR_INVALID_CHAR; - } - - break; - case FHP_STATE_HEADERS_END_CR: - switch (byte) { - case '\n': - ctx->state = FHP_STATE_HEADERS_END; - - break; - default: - return FHP_ERR_INVALID_CHAR; - } - - break; - case FHP_STATE_HEADERS_END: - // TODO - - break; - default: - // invalid state - // (should never be reached) - return FHP_ERR_BAD_STATE; - } - - // increment byte offset - ctx->ofs++; - - /* return success */ - return FHP_OK; -} - -fhp_err_t -fhp_ctx_push( - fhp_ctx_t * const ctx, - uint8_t * const buf, - size_t len -) { - switch (ctx->state) { - case FHP_STATE_ERROR: - return ctx->err; - - break; - default: - for (size_t i = 0; i < len; i++) { - // push byte - fhp_err_t err = fhp_ctx_push_byte(ctx, buf[i]); - - // check result - if (err != FHP_OK) { - ctx->state = FHP_STATE_ERROR; - ctx->err = err; - return err; - } - } - } - - // return success - return FHP_OK; -} - -fhp_env_t * -fhp_ctx_get_env(fhp_ctx_t * const ctx) { - return ctx->env; -} - -void * -fhp_ctx_get_user_data(fhp_ctx_t * const ctx) { - return ctx->user_data; -} - -size_t -fhp_ctx_get_num_tes(fhp_ctx_t * const ctx) { - return ctx->num_tes; -} - -uint64_t -fhp_ctx_get_content_length(fhp_ctx_t * const ctx) { - return ctx->content_length; -} -- cgit v1.2.3