Skip to content

Commit

Permalink
More HTTP1.1 refactors (#65)
Browse files Browse the repository at this point in the history
* Move decoder vtable to http_impl
* Migrate h1 decoder API to use byte cursors
  • Loading branch information
ColdenCullen authored May 29, 2019
1 parent 9b542da commit 4224916
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 331 deletions.
40 changes: 2 additions & 38 deletions include/aws/http/private/h1_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,6 @@

#include <aws/http/private/http_impl.h>

/**
* Called from `aws_h1_decode` when an http header has been received.
* All pointers are strictly *read only*; any data that needs to persist must be copied out into user-owned memory.
*/
typedef int(aws_h1_decoder_on_header_fn)(const struct aws_http_decoded_header *header, void *user_data);

/**
* Called from `aws_h1_decode` when a portion of the http body has been received.
* `finished` is true if this is the last section of the http body, and false if more body data is yet to be received.
* All pointers are strictly *read only*; any data that needs to persist must be copied out into user-owned memory.
*/
typedef int(aws_h1_decoder_on_body_fn)(const struct aws_byte_cursor *data, bool finished, void *user_data);

typedef int(aws_h1_decoder_on_request_fn)(
enum aws_http_method method_enum,
const struct aws_byte_cursor *method_str,
const struct aws_byte_cursor *uri,
void *user_data);

typedef int(aws_h1_decoder_on_response_fn)(int status_code, void *user_data);

typedef int(aws_h1_decoder_done_fn)(void *user_data);

struct aws_h1_decoder_vtable {
aws_h1_decoder_on_header_fn *on_header;
aws_h1_decoder_on_body_fn *on_body;

/* Only needed for requests, can be NULL for responses. */
aws_h1_decoder_on_request_fn *on_request;

/* Only needed for responses, can be NULL for requests. */
aws_h1_decoder_on_response_fn *on_response;

aws_h1_decoder_done_fn *on_done;
};

/**
* Structure used to initialize an `aws_h1_decoder`.
*/
Expand All @@ -63,7 +27,7 @@ struct aws_h1_decoder_params {
/* Set false if decoding responses */
bool is_decoding_requests;
void *user_data;
struct aws_h1_decoder_vtable vtable;
struct aws_http_decoder_vtable vtable;
};

struct aws_h1_decoder;
Expand All @@ -72,7 +36,7 @@ AWS_EXTERN_C_BEGIN

AWS_HTTP_API struct aws_h1_decoder *aws_h1_decoder_new(struct aws_h1_decoder_params *params);
AWS_HTTP_API void aws_h1_decoder_destroy(struct aws_h1_decoder *decoder);
AWS_HTTP_API int aws_h1_decode(struct aws_h1_decoder *decoder, const void *data, size_t data_bytes, size_t *bytes_read);
AWS_HTTP_API int aws_h1_decode(struct aws_h1_decoder *decoder, struct aws_byte_cursor *data);

AWS_HTTP_API void aws_h1_decoder_set_logging_id(struct aws_h1_decoder *decoder, void *id);

Expand Down
36 changes: 36 additions & 0 deletions include/aws/http/private/http_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,42 @@ struct aws_http_decoded_header {
struct aws_byte_cursor data;
};

/**
* Called from `aws_h*_decode` when an http header has been received.
* All pointers are strictly *read only*; any data that needs to persist must be copied out into user-owned memory.
*/
typedef int(aws_http_decoder_on_header_fn)(const struct aws_http_decoded_header *header, void *user_data);

/**
* Called from `aws_h*_decode` when a portion of the http body has been received.
* `finished` is true if this is the last section of the http body, and false if more body data is yet to be received.
* All pointers are strictly *read only*; any data that needs to persist must be copied out into user-owned memory.
*/
typedef int(aws_http_decoder_on_body_fn)(const struct aws_byte_cursor *data, bool finished, void *user_data);

typedef int(aws_http_decoder_on_request_fn)(
enum aws_http_method method_enum,
const struct aws_byte_cursor *method_str,
const struct aws_byte_cursor *uri,
void *user_data);

typedef int(aws_http_decoder_on_response_fn)(int status_code, void *user_data);

typedef int(aws_http_decoder_done_fn)(void *user_data);

struct aws_http_decoder_vtable {
aws_http_decoder_on_header_fn *on_header;
aws_http_decoder_on_body_fn *on_body;

/* Only needed for requests, can be NULL for responses. */
aws_http_decoder_on_request_fn *on_request;

/* Only needed for responses, can be NULL for requests. */
aws_http_decoder_on_response_fn *on_response;

aws_http_decoder_done_fn *on_done;
};

AWS_EXTERN_C_BEGIN

AWS_HTTP_API void aws_http_fatal_assert_library_initialized(void);
Expand Down
13 changes: 3 additions & 10 deletions source/h1_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
#include <aws/http/private/request_response_impl.h>
#include <aws/io/logging.h>

#include <stdio.h>

#if _MSC_VER
# pragma warning(disable : 4204) /* non-constant aggregate initializer */
#endif
Expand Down Expand Up @@ -95,7 +93,7 @@ static const struct aws_http_stream_vtable s_stream_vtable = {
.update_window = s_stream_update_window,
};

static const struct aws_h1_decoder_vtable s_decoder_vtable = {
static const struct aws_http_decoder_vtable s_h1_decoder_vtable = {
.on_request = s_decoder_on_request,
.on_response = s_decoder_on_response,
.on_header = s_decoder_on_header,
Expand Down Expand Up @@ -1201,7 +1199,7 @@ static struct h1_connection *s_connection_new(struct aws_allocator *alloc) {
.alloc = alloc,
.is_decoding_requests = connection->base.server_data != NULL,
.user_data = connection,
.vtable = s_decoder_vtable,
.vtable = s_h1_decoder_vtable,
.scratch_space_initial_size = DECODER_INITIAL_SCRATCH_SIZE,
};
connection->thread_data.incoming_stream_decoder = aws_h1_decoder_new(&options);
Expand Down Expand Up @@ -1316,9 +1314,7 @@ static int s_handler_process_read_message(
aws_h1_decoder_set_logging_id(
connection->thread_data.incoming_stream_decoder, connection->thread_data.incoming_stream);

size_t decoded_len = 0;
err = aws_h1_decode(
connection->thread_data.incoming_stream_decoder, message_cursor.ptr, message_cursor.len, &decoded_len);
err = aws_h1_decode(connection->thread_data.incoming_stream_decoder, &message_cursor);
if (err) {
AWS_LOGF_ERROR(
AWS_LS_HTTP_CONNECTION,
Expand All @@ -1329,9 +1325,6 @@ static int s_handler_process_read_message(

goto error;
}

AWS_FATAL_ASSERT(decoded_len > 0);
aws_byte_cursor_advance(&message_cursor, decoded_len);
}

AWS_LOGF_TRACE(AWS_LS_HTTP_CONNECTION, "id=%p: Done processing message.", (void *)&connection->base);
Expand Down
71 changes: 27 additions & 44 deletions source/h1_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ AWS_STATIC_STRING_FROM_LITERAL(s_transfer_coding_x_gzip, "x-gzip");
* A common state is the "line state", which handles consuming one line ending in CRLF
* and feeding the line to a linestate_fn, which should process data and set the next state.
*/
typedef int(state_fn)(struct aws_h1_decoder *decoder, struct aws_byte_cursor input, size_t *bytes_processed);
typedef int(state_fn)(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input);
typedef int(linestate_fn)(struct aws_h1_decoder *decoder, struct aws_byte_cursor input);

struct aws_h1_decoder {
Expand All @@ -49,7 +49,7 @@ struct aws_h1_decoder {
void *logging_id;

/* User callbacks and settings. */
struct aws_h1_decoder_vtable vtable;
struct aws_http_decoder_vtable vtable;
bool is_decoding_requests;
void *user_data;
};
Expand Down Expand Up @@ -116,9 +116,8 @@ static bool s_scan_for_crlf(struct aws_h1_decoder *decoder, struct aws_byte_curs
return false;
}

static int s_cat(struct aws_h1_decoder *decoder, uint8_t *data, size_t len) {
static int s_cat(struct aws_h1_decoder *decoder, struct aws_byte_cursor to_append) {
struct aws_byte_buf *buffer = &decoder->scratch_space;
struct aws_byte_cursor to_append = aws_byte_cursor_from_array(data, len);
int op = AWS_OP_ERR;
if (buffer->buffer != NULL) {
if ((aws_byte_buf_append(buffer, &to_append) == AWS_OP_SUCCESS)) {
Expand All @@ -133,7 +132,7 @@ static int s_cat(struct aws_h1_decoder *decoder, uint8_t *data, size_t len) {
if (new_size == 0) { /* check for overflow */
return aws_raise_error(AWS_ERROR_OOM);
}
} while (new_size < (buffer->len + len));
} while (new_size < (buffer->len + to_append.len));

uint8_t *new_data = aws_mem_acquire(buffer->allocator, new_size);
if (!new_data) {
Expand Down Expand Up @@ -200,16 +199,20 @@ static int s_read_size_hex(struct aws_byte_cursor cursor, size_t *size) {
}

/* This state consumes an entire line, then calls a linestate_fn to process the line. */
static int s_state_getline(struct aws_h1_decoder *decoder, struct aws_byte_cursor input, size_t *bytes_processed) {
static int s_state_getline(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input) {
/* If preceding runs of this state failed to find CRLF, their data is stored in the scratch_space
* and new data needs to be combined with the old data for processing. */
bool has_prev_data = decoder->scratch_space.len;

bool found_crlf = s_scan_for_crlf(decoder, input, bytes_processed);
size_t line_length = 0;
bool found_crlf = s_scan_for_crlf(decoder, *input, &line_length);

/* Found end of line! Run the line processor on it */
struct aws_byte_cursor line = aws_byte_cursor_advance(input, line_length);

bool use_scratch = !found_crlf | has_prev_data;
if (AWS_UNLIKELY(use_scratch)) {
int err = s_cat(decoder, input.ptr, *bytes_processed);
int err = s_cat(decoder, line);
if (err) {
AWS_LOGF_ERROR(
AWS_LS_HTTP_STREAM,
Expand All @@ -220,17 +223,11 @@ static int s_state_getline(struct aws_h1_decoder *decoder, struct aws_byte_curso

return AWS_OP_ERR;
}
/* Line is actually the entire scratch buffer now */
line = aws_byte_cursor_from_buf(&decoder->scratch_space);
}

if (AWS_LIKELY(found_crlf)) {
/* Found end of line! Run the line processor on it */
struct aws_byte_cursor line;
if (use_scratch) {
line = aws_byte_cursor_from_buf(&decoder->scratch_space);
} else {
line = aws_byte_cursor_from_array(input.ptr, *bytes_processed);
}

/* Backup so "\r\n" is not included. */
/* RFC-7230 section 3 Message Format */
AWS_ASSERT(line.len >= 2);
Expand Down Expand Up @@ -327,24 +324,21 @@ static void s_reset_state(struct aws_h1_decoder *decoder) {
decoder->is_done = false;
}

static int s_state_unchunked_body(
struct aws_h1_decoder *decoder,
struct aws_byte_cursor input,
size_t *bytes_processed) {
static int s_state_unchunked_body(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input) {

size_t processed_bytes = 0;
AWS_FATAL_ASSERT(decoder->content_processed < decoder->content_length); /* shouldn't be possible */

if ((decoder->content_processed + input.len) > decoder->content_length) {
if ((decoder->content_processed + input->len) > decoder->content_length) {
processed_bytes = decoder->content_length - decoder->content_processed;
} else {
processed_bytes = input.len;
processed_bytes = input->len;
}

decoder->content_processed += processed_bytes;

bool finished = decoder->content_processed == decoder->content_length;
struct aws_byte_cursor body = aws_byte_cursor_from_array(input.ptr, processed_bytes);
struct aws_byte_cursor body = aws_byte_cursor_advance(input, processed_bytes);
int err = decoder->vtable.on_body(&body, finished, decoder->user_data);
if (err) {
return AWS_OP_ERR;
Expand All @@ -357,8 +351,6 @@ static int s_state_unchunked_body(
}
}

*bytes_processed = processed_bytes;

return AWS_OP_SUCCESS;
}

Expand All @@ -377,20 +369,20 @@ static int s_linestate_chunk_terminator(struct aws_h1_decoder *decoder, struct a
return AWS_OP_SUCCESS;
}

static int s_state_chunk(struct aws_h1_decoder *decoder, struct aws_byte_cursor input, size_t *bytes_processed) {
static int s_state_chunk(struct aws_h1_decoder *decoder, struct aws_byte_cursor *input) {
size_t processed_bytes = 0;
AWS_ASSERT(decoder->chunk_processed < decoder->chunk_size);

if ((decoder->chunk_processed + input.len) > decoder->chunk_size) {
if ((decoder->chunk_processed + input->len) > decoder->chunk_size) {
processed_bytes = decoder->chunk_size - decoder->chunk_processed;
} else {
processed_bytes = input.len;
processed_bytes = input->len;
}

decoder->chunk_processed += processed_bytes;

bool finished = decoder->chunk_processed == decoder->chunk_size;
struct aws_byte_cursor body = aws_byte_cursor_from_array(input.ptr, decoder->chunk_size);
struct aws_byte_cursor body = aws_byte_cursor_advance(input, decoder->chunk_size);
int err = decoder->vtable.on_body(&body, false, decoder->user_data);
if (err) {
return AWS_OP_ERR;
Expand All @@ -400,8 +392,6 @@ static int s_state_chunk(struct aws_h1_decoder *decoder, struct aws_byte_cursor
s_set_line_state(decoder, s_linestate_chunk_terminator);
}

*bytes_processed = processed_bytes;

return AWS_OP_SUCCESS;
}

Expand Down Expand Up @@ -758,26 +748,19 @@ void aws_h1_decoder_destroy(struct aws_h1_decoder *decoder) {
aws_mem_release(decoder->alloc, decoder);
}

int aws_h1_decode(struct aws_h1_decoder *decoder, const void *data, size_t data_bytes, size_t *bytes_read) {
int aws_h1_decode(struct aws_h1_decoder *decoder, struct aws_byte_cursor *data) {
AWS_ASSERT(decoder);
AWS_ASSERT(data);

struct aws_byte_cursor input = aws_byte_cursor_from_array(data, data_bytes);
size_t total_bytes_processed = 0;
struct aws_byte_cursor backup = *data;

while (data_bytes && !decoder->is_done) {
size_t bytes_processed = 0;
int err = decoder->run_state(decoder, input, &bytes_processed);
while (data->len && !decoder->is_done) {
int err = decoder->run_state(decoder, data);
if (err) {
/* Reset the data param to how we found it */
*data = backup;
return AWS_OP_ERR;
}
data_bytes -= bytes_processed;
total_bytes_processed += bytes_processed;
aws_byte_cursor_advance(&input, bytes_processed);
}

if (bytes_read) {
*bytes_read = total_bytes_processed;
}

if (decoder->is_done) {
Expand Down
Loading

0 comments on commit 4224916

Please sign in to comment.