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 --- ctx.c | 638 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 638 insertions(+) create mode 100644 ctx.c (limited to 'ctx.c') 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; +} -- cgit v1.2.3