From 845ddb43da8aa2a8c80a9d9638d63386ddf97d7e Mon Sep 17 00:00:00 2001 From: Paul Duncan Date: Sun, 28 Aug 2016 20:32:55 -0400 Subject: add content-length handling and partial chunked transfer-encoding support (still need footers and compression) --- ctx.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) (limited to 'ctx.c') diff --git a/ctx.c b/ctx.c index 405223f..d2d7245 100644 --- a/ctx.c +++ b/ctx.c @@ -391,6 +391,7 @@ retry: case '\n': // set state ctx->state = FHP_STATE_HEADERS_END; + goto retry; break; } @@ -401,6 +402,7 @@ retry: case '\n': // set state ctx->state = FHP_STATE_HEADERS_END; + goto retry; break; default: @@ -568,6 +570,7 @@ retry: // set state ctx->state = FHP_STATE_HEADERS_END; + goto retry; break; default: @@ -579,6 +582,7 @@ retry: switch (byte) { case '\n': ctx->state = FHP_STATE_HEADERS_END; + goto retry; break; default: @@ -587,7 +591,236 @@ retry: break; case FHP_STATE_HEADERS_END: + // send end headers token + if (!ctx->cb(ctx, FHP_TOKEN_HEADERS_END, 0, 0)) + return FHP_ERR_CB; + + switch (ctx->body_type) { + case FHP_BODY_TYPE_NONE: + // no body + + // send request end token + if (!ctx->cb(ctx, FHP_TOKEN_REQUEST_END, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_REQUEST_END; + + break; + case FHP_BODY_TYPE_CONTENT_LENGTH: + // send body start token + if (!ctx->cb(ctx, FHP_TOKEN_BODY_START, 0, 0)) + return FHP_ERR_CB; + + if (ctx->content_length > 0) { + // grab content length and clear buffer + ctx->bytes_left = ctx->content_length; + ctx->buf_len = 0; + + // set state + ctx->state = FHP_STATE_CL_BODY; + } else { + // empty body + + // send body end token + if (!ctx->cb(ctx, FHP_TOKEN_BODY_END, 0, 0)) + return FHP_ERR_CB; + + // send request end token + if (!ctx->cb(ctx, FHP_TOKEN_REQUEST_END, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_REQUEST_END; + } + + break; + case FHP_BODY_TYPE_TRANSFER_ENCODING: + // send body start token + if (!ctx->cb(ctx, FHP_TOKEN_BODY_START, 0, 0)) + return FHP_ERR_CB; + + // clear chunk size + ctx->chunk_len = 0; + + // set state + ctx->state = FHP_STATE_CHUNK_LEN; + + break; + default: + // invalid body type (bug?) + return FHP_ERR_INVALID_BODY_TYPE; + } + + break; + case FHP_STATE_CL_BODY: + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_BODY_FRAGMENT, byte)) != FHP_OK) + return err; + + // decriment remaining bytes + ctx->bytes_left--; + + if (!ctx->bytes_left) { + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_BODY_FRAGMENT)) != FHP_OK) + return err; + + // send body end token + if (!ctx->cb(ctx, FHP_TOKEN_BODY_END, 0, 0)) + return FHP_ERR_CB; + + // send request end token + if (!ctx->cb(ctx, FHP_TOKEN_REQUEST_END, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_REQUEST_END; + } + + break; + case FHP_STATE_CHUNK_LEN: + { + uint64_t old_chunk_len = ctx->chunk_len; + + switch (byte) { + case '\r': + // set state + ctx->state = FHP_STATE_CHUNK_LEN_CR; + + break; + case '\n': + // set state + ctx->state = FHP_STATE_CHUNK_LEN_CR; + goto retry; + + break; + CASE_DIGIT_CHARS + // update chunk length + ctx->chunk_len = (ctx->chunk_len << 4) + (byte - '0'); + + break; + CASE_HEX_LC_ALPHA_CHARS + // update chunk length + ctx->chunk_len = (ctx->chunk_len << 4) + (byte - 'a'); + + break; + CASE_HEX_UC_ALPHA_CHARS + // update chunk length + ctx->chunk_len = (ctx->chunk_len << 4) + (byte - 'A'); + + break; + default: + // invalid character + return FHP_ERR_INVALID_CHAR_IN_CHUNK_LEN; + } + + // check for overflow + if (ctx->chunk_len < old_chunk_len) { + // overflow in chunk length + return FHP_ERR_CHUNK_LEN_OVERFLOW; + } + } + + break; + case FHP_STATE_CHUNK_LEN_CR: + switch (byte) { + case '\n': + // TODO: check chunk len + + if (ctx->chunk_len > 0) { + ctx->bytes_left = ctx->chunk_len; + ctx->buf_len = 0; + + // send chunk start token + if (!ctx->cb(ctx, FHP_TOKEN_CHUNK_START, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_CHUNK_BODY; + } else { + // last chunk + + // send chunk start token + if (!ctx->cb(ctx, FHP_TOKEN_CHUNK_LAST, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_TE_FOOTER; + } + + break; + default: + return FHP_ERR_INVALID_CHAR_AFTER_CHUNK_LEN; + } + + break; + case FHP_STATE_CHUNK_BODY: + // add to buffer + if ((err = fhp_ctx_buf_push(ctx, FHP_TOKEN_BODY_FRAGMENT, byte)) != FHP_OK) + return err; + + // decriment remaining bytes + ctx->bytes_left--; + + if (!ctx->bytes_left) { + // flush buffer + if ((err = fhp_ctx_buf_flush(ctx, FHP_TOKEN_BODY_FRAGMENT)) != FHP_OK) + return err; + + // send body end token + if (!ctx->cb(ctx, FHP_TOKEN_CHUNK_END, 0, 0)) + return FHP_ERR_CB; + + // set state + ctx->state = FHP_STATE_CHUNK_BODY_END; + } + break; + case FHP_STATE_CHUNK_BODY_END: + switch (byte) { + case '\r': + // set state + ctx->state = FHP_STATE_CHUNK_BODY_END_CR; + + break; + case '\n': + // set state + ctx->state = FHP_STATE_CHUNK_BODY_END_CR; + goto retry; + + break; + default: + return FHP_ERR_INVALID_CHAR_AFTER_CHUNK_BODY; + } + + break; + case FHP_STATE_CHUNK_BODY_END_CR: + switch (byte) { + case '\n': + // clear chunk len, set state + ctx->chunk_len = 0; + ctx->state = FHP_STATE_CHUNK_LEN; + + break; + default: + return FHP_ERR_INVALID_CHAR_AFTER_CHUNK_BODY; + } + + break; + case FHP_STATE_TE_FOOTER: // TODO + break; + case FHP_STATE_REQUEST_END: + switch (byte) { + case '\r': + case '\n': + // eat newlines + break; + default: + // set state + ctx->state = FHP_STATE_INIT; + goto retry; + } break; default: -- cgit v1.2.3