diff --git a/.dockerignore b/.dockerignore index 9bda86a..26ee61d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,14 +2,15 @@ !src/ !include/ +!Makefile +!config.mk !lsm/src/ !lsm/include/ !lsm/Makefile !lsm/config.mk -!thirdparty/include -!thirdparty/src - -!Makefile -!config.mk +!lnm/src/ +!lnm/include/ +!lnm/Makefile +!lnm/config.mk diff --git a/Makefile b/Makefile index 6e13422..cedc8b8 100644 --- a/Makefile +++ b/Makefile @@ -10,14 +10,13 @@ BIN := $(BUILD_DIR)/$(BIN_FILENAME) SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' -SRCS_THIRDPARTY != find '$(THIRDPARTY_DIR)/src' -iname '*.c' SRCS_H != find include -iname '*.h' SRCS_H_INTERNAL != find $(SRC_DIR) -iname '*.h' -OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) $(SRCS_THIRDPARTY:%=$(BUILD_DIR)/%.o) +OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o) -DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_THIRDPARTY:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) +DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) BINS_TEST := $(OBJS_TEST:%.c.o=%) TARGETS_TEST := $(BINS_TEST:%=test-%) @@ -50,10 +49,6 @@ $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) $(CC) $(_CFLAGS) -c $< -o $@ -$(BUILD_DIR)/$(THIRDPARTY_DIR)/%.c.o: $(THIRDPARTY_DIR)/%.c - mkdir -p $(dir $@) - $(CC) $(_CFLAGS) -c $< -o $@ - .PHONY: bin-docker bin-docker: docker build -t lander . diff --git a/include/event_loop.h b/include/event_loop.h deleted file mode 100644 index 24dd05a..0000000 --- a/include/event_loop.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef LANDER_EVENT_LOOP -#define LANDER_EVENT_LOOP - -#include -#include -#include - -// Size of the read and write buffers for each connection, in bytes -#define EVENT_LOOP_BUFFER_SIZE 2048 - -/** - * State of a connection - */ -typedef enum { - event_loop_conn_state_req = 0, - event_loop_conn_state_res = 1, - event_loop_conn_state_end = 2, -} event_loop_conn_state; - -/** - * Represents an active connection managed by the event loop - */ -typedef struct event_loop_conn { - int fd; - event_loop_conn_state state; - // Read buffer - size_t rbuf_size; - size_t rbuf_read; - uint8_t rbuf[EVENT_LOOP_BUFFER_SIZE]; - // Write buffer - size_t wbuf_size; - size_t wbuf_sent; - uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE]; - - // If true, the server will close the connection after the final write buffer - // has been written - bool close_after_write; - // Context for a request - void *ctx; -} event_loop_conn; - -/* - * Main struct object representing the event loop - */ -typedef struct event_loop { - event_loop_conn **connections; - size_t connection_count; - // Global context passed to every connection - void *gctx; - /** - * Function to initialize a connection context. - * - * @param gctx global context of the event loop - * @return pointer to the allocated object. - */ - void *(*ctx_init)(void *gctx); - /** - * Function to free a connection context object. - * - * @param ctx context to free - */ - void (*ctx_free)(void *ctx); - /** - * Function to process incoming data while in the req state. - * - * @param conn connection to process - * @return whether the function can be called again immediately in the same - * event loop cycle. This allows quicly processing multiple requests without - * waiting for I/O. - */ - bool (*handle_data)(event_loop_conn *conn); - /** - * Function to process outgoing data while in the res state. - * - * @param conn connection to proces - */ - void (*write_data)(event_loop_conn *conn); -} event_loop; - -/* - * Initialize a new connection struct - * - * @param el the event loop - * @return pointer to the newly allocated connection struct - */ -event_loop_conn *event_loop_conn_init(event_loop *el); - -/* - * Free a connection struct - * - * @param el the event loop - * @param conn connection struct to free - */ -void event_loop_conn_free(event_loop *el, event_loop_conn *conn); - -/* - * Handle I/O for a connection, be it reading input or writing output. - * - * @param el the event loop - * @param conn the connection to process - */ -void event_loop_conn_io(event_loop *el, event_loop_conn *conn); - -/* - * Initialize a new event loop struct - * - * @return pointer to the newly allocated event loop struct - */ -event_loop *event_loop_init(); - -/* - * Place a new connection into the event loop's internal array. - * - * @param el the event loop - * @param conn connection to insert - * @return 0 on success, -1 if the internal realloc failed. - */ -int event_loop_put(event_loop *el, event_loop_conn *conn); - -/** - * Accept a new connection for the given file descriptor. - * - * @param el the event loop - * @param fd file descriptor for the connection - * @return 0 if successful, negative value otherwise - */ -int event_loop_accept(event_loop *el, int fd); - -/* - * Run the event loop. This function never returns. - * - * @param el the event loop - * @param port on what port to listen - */ -void event_loop_run(event_loop *el, int port); - -#endif diff --git a/include/http/req.h b/include/http/req.h deleted file mode 100644 index ab8922b..0000000 --- a/include/http/req.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef LANDER_HTTP_REQ -#define LANDER_HTTP_REQ - -#include -#include -#include -#include - -#include "http/types.h" -#include "picohttpparser.h" - -#define HTTP_MAX_ALLOWED_HEADERS 32 -#define HTTP_MAX_REGEX_GROUPS 4 - -/** - * Struct representing the specific type of request - */ -typedef struct http_request { - size_t len; - int minor_version; - http_method method; - const char *path; - size_t path_len; - const char *query; - size_t query_len; - http_body body; - regmatch_t regex_groups[HTTP_MAX_REGEX_GROUPS]; - struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS]; - size_t num_headers; -} http_request; - -/** - * Result of the HTTP parse function - */ -typedef enum http_parse_error { - http_parse_error_ok = 0, - http_parse_error_incomplete = 1, - http_parse_error_invalid = 2, - http_parse_error_unknown_method = 3 -} http_parse_error; - -#endif diff --git a/include/http/res.h b/include/http/res.h deleted file mode 100644 index 729958f..0000000 --- a/include/http/res.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef LANDER_HTTP_RES -#define LANDER_HTTP_RES - -#include -#include - -#include "http/types.h" - -/** - * Struct describing a header for the response. - */ -typedef struct http_response_header { - http_header type; - const char *value; - bool owned; -} http_response_header; - -/** - * Struct representing an HTTP response. - */ -typedef struct http_response { - http_status status; - const char *head; - size_t head_len; - size_t head_written; - http_body body; - struct { - http_response_header *arr; - size_t len; - size_t cap; - } headers; -} http_response; - -/** - * Set the request body to the given buffer. - * - * @param res response to modify - * @param body pointer to the buf containing the body - * @param body_len length of the body - * @owned whether the body should be freed after processing the request - */ -void http_res_set_body_buf(http_response *res, const char *body, - size_t body_len, bool owned); - -/** - * Set the request body to the given filename. - * - * @param res response to modify - * @param filename path to the file to return - */ -void http_res_set_body_file(http_response *res, const char *filename); - -/** - * Add a header to the response. - * - * @param res response to modify - * @param type type of the header - * @param value value of the header - * @param owned whether the value should be freed after processing the request - */ -void http_res_add_header(http_response *res, http_header type, - const char *value, bool owned); - -/** - * Add a Content-Type header corresponding to the mime type. - * - * @param res response to modiy - * @param mime_type mime type of the response - */ -void http_res_set_mime_type(http_response *res, http_mime_type mime_type); - -#endif diff --git a/include/http/types.h b/include/http/types.h deleted file mode 100644 index fbbe160..0000000 --- a/include/http/types.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef LANDER_HTTP_TYPES -#define LANDER_HTTP_TYPES - -#include -#include -#include - -// Array mapping the http_request_method enum to strings -extern const char *http_method_names[]; -extern const size_t http_method_names_len; - -typedef enum http_method { - http_get = 0, - http_post = 1, - http_put = 2, - http_patch = 3, - http_delete = 4 -} http_method; - -// Array mapping the http_response_type enum to strings -extern const char *http_status_names[][32]; - -typedef enum http_status { - // 1xx - http_continue = 100, - http_switching_protocols = 101, - http_processing = 102, - http_early_hints = 103, - // 2xx - http_ok = 200, - http_created = 201, - http_accepted = 202, - http_non_authoritative_information = 203, - http_no_content = 204, - http_reset_content = 205, - http_partial_content = 206, - http_multi_status = 207, - http_already_reported = 208, - // 3xx - http_multiple_choices = 300, - http_moved_permanently = 301, - http_found = 302, - http_see_other = 303, - http_not_modified = 304, - http_temporary_redirect = 307, - http_permanent_redirect = 308, - // 4xx - http_bad_request = 400, - http_unauthorized = 401, - http_payment_required = 402, - http_forbidden = 403, - http_not_found = 404, - http_method_not_allowed = 405, - http_not_acceptable = 406, - http_proxy_authentication_required = 407, - http_request_timeout = 408, - http_conflict = 409, - http_gone = 410, - http_length_required = 411, - http_precondition_failed = 412, - http_content_too_large = 413, - http_uri_too_long = 414, - http_unsupported_media_type = 415, - http_range_not_satisfiable = 416, - http_expection_failed = 417, - http_im_a_teapot = 418, - http_misdirected_request = 421, - http_unprocessable_content = 422, - http_locked = 423, - http_failed_dependency = 424, - http_too_early = 425, - http_upgrade_required = 426, - http_precondition_required = 428, - http_too_many_requests = 429, - http_request_header_fields_too_large = 431, - // 5xx - http_internal_server_error = 500, - http_method_not_implemented = 501, - http_bad_gateway = 502, - http_service_unavailable = 503, - http_gateway_timeout = 504, - http_http_version_not_supported = 505, - http_variant_also_negotiates = 506, - http_insufficient_storage = 507, - http_loop_detected = 508, - http_not_extended = 510, - http_network_authentication_required = 511 -} http_status; - -// Array mapping the http_mime_type enum to strings -extern const char *http_mime_type_names[][2]; - -typedef enum http_mime_type { - http_mime_aac = 0, - http_mime_bz, - http_mime_bz2, - http_mime_css, - http_mime_csv, - http_mime_gz, - http_mime_gif, - http_mime_htm, - http_mime_html, - http_mime_jar, - http_mime_jpeg, - http_mime_jpg, - http_mime_js, - http_mime_json, - http_mime_mp3, - http_mime_mp4, - http_mime_png, - http_mime_pdf, - http_mime_rar, - http_mime_sh, - http_mime_svg, - http_mime_tar, - http_mime_txt, - http_mime_wav, - http_mime_7z -} http_mime_type; - -// Array mapping the http_header enum to strings -extern const char *http_header_names[]; - -typedef enum http_header { - http_header_connection = 0, - http_header_location, - http_header_content_type, - http_header_content_disposition, - http_header_server, - http_header_content_length -} http_header; - -typedef enum http_body_type { - http_body_buf = 0, - http_body_file = 1 -} http_body_type; - -typedef struct http_body { - http_body_type type; - char *buf; - bool buf_owned; - FILE *file; - char *fname; - bool fname_owned; - // In the context of a request, expected_len is the content of the request's - // Content-Length header, and len is how many bytes have already been - // received. - // In the context of a response, expected_len is the actual length of the - // body, and len is how many have been written. - size_t expected_len; - size_t len; -} http_body; - -/** - * Initialize a new body struct. - * - * @return pointer to the newly allocated object. - */ -http_body *http_body_init(); - -/** - * Reset a body, allowing it to be reused as if newly allocated. - * - * @param body body to reset - */ -void http_body_reset(http_body *body); - -/** - * Free a body. Internally, this calls http_body_reset. - * - * @param body body to free - */ -void http_body_free(http_body *body); - -#endif diff --git a/include/http_loop.h b/include/http_loop.h deleted file mode 100644 index 131bd6e..0000000 --- a/include/http_loop.h +++ /dev/null @@ -1,241 +0,0 @@ -#ifndef LANDER_HTTP_LOOP -#define LANDER_HTTP_LOOP - -#include - -#include "event_loop.h" -#include "http/req.h" -#include "http/res.h" -#include "http/types.h" - -// Max amount of steps a route can use -#define HTTP_LOOP_MAX_STEPS 17 - -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) - -/** - * Type of a route - */ -typedef enum http_route_type { - http_route_literal = 0, - http_route_regex = 1, -} http_route_type; - -/** - * Function describing a step in a route's processing. - * - * @param conn connection to process - * @return whether processing can proceed to the next step without performing - * I/O first. For a request step, `false` means more data needs to be read - * before the step can finish its processing. For response steps, `false` means - * there's new data in the write buffer that needs to be written. - */ -typedef bool (*http_step)(event_loop_conn *conn); - -extern const http_step http_default_res_steps[HTTP_LOOP_MAX_STEPS]; - -/** - * Struct describing a route a request can take. - */ -typedef struct http_route { - http_route_type type; - http_method method; - char *path; - // Compiled regex for a regex route. This value gets set at runtime when - // starting the http loop - regex_t *regex; - const http_step steps[HTTP_LOOP_MAX_STEPS]; - const http_step steps_res[HTTP_LOOP_MAX_STEPS]; -} http_route; - -/** - * Global context passed to every connection using the same pointer - */ -typedef struct http_loop_gctx { - http_route *routes; - size_t route_count; - void *(*custom_ctx_init)(); - void (*custom_ctx_reset)(void *); - void (*custom_ctx_free)(void *); - const char *api_key; - // Custom global context - void *c; -} http_loop_gctx; - -/** - * Initialize a new global context - * - * @return pointer to the newly allocated object. - */ -http_loop_gctx *http_loop_gctx_init(); - -/** - * Invidivual context initialized for every connection - */ -typedef struct http_loop_ctx { - http_request req; - http_response res; - http_route *route; - size_t current_step; - http_loop_gctx *g; - void *c; -} http_loop_ctx; - -/** - * Initialize a context struct - * - * @param g global context - * @return pointer to the newly allocated context - */ -http_loop_ctx *http_loop_ctx_init(http_loop_gctx *g); - -/** - * Resets an already allocated context so that it can be reused for a new - * request. - * - * @param ctx context to reset - */ -void http_loop_ctx_reset(http_loop_ctx *ctx); - -/** - * Free a context struct. Internally this first calls http_loop_ctx_reset. - * - * @param ctx context to free - */ -void http_loop_ctx_free(http_loop_ctx *ctx); - -/** - * Represents an HTTP loop - */ -typedef struct event_loop http_loop; - -/** - * Process incoming data as an HTTP request. This is the "handle_data" function - * for the event loop. - * - * @param conn connection to process - * @return whether another request can be processed immediately. - */ -bool http_loop_handle_request(event_loop_conn *conn); - -/** - * Try to parse the incoming data as an HTTP request. - * - * @param conn connection to process - * @return result of the parse - */ -http_parse_error http_loop_parse_request(event_loop_conn *conn); - -/** - * Try to match the parsed request with one of the defined routes, aka route the - * request. - * - * @param conn connection to process - */ -void http_loop_route_request(event_loop_conn *conn); - -/** - * Advance the processing of the routed request's processing by cycling through - * the request's various steps. - * - * @param conn connection to process - */ -void http_loop_process_request(event_loop_conn *conn); - -/** - * Handles the response processing. This is the `write_data` function for the - * event loop. - * - * @param conn connection to process - */ -void http_loop_handle_response(event_loop_conn *conn); - -/** - * Request step that consumes the request body and stores it in a buffer. - * - * @param conn connection to process - * @return true if the body has been fully received, false otherwise - */ -bool http_loop_step_body_to_buf(event_loop_conn *conn); - -/** - * Request step that consumes the request body and stores it in a file. - * - * @param conn connection to process - * @return true if the body has been fully received, false otherwise - */ -bool http_loop_step_body_to_file(event_loop_conn *conn); - -/** - * Try to parse the Content-Length header. - * - * @param conn connection to process - */ -bool http_loop_step_parse_content_length(event_loop_conn *conn); - -/** - * Authenticate the request using the X-Api-Key header. - * - * @param conn connection to check - * @return always true - */ -bool http_loop_step_auth(event_loop_conn *conn); - -/** - * A step that simply sets the connection's state to res. - * - * @param conn connection to process - * @return always true - */ -bool http_loop_step_switch_res(event_loop_conn *conn); - -/** - * Write the HTTP header back to the connection. If `res->head` is not set, a - * header will be generated for you. - * - * @param conn connection to process - */ -bool http_loop_step_write_header(event_loop_conn *conn); - -/** - * Write the HTTP body back to the connection. - * - * @param conn connection to process - */ -bool http_loop_step_write_body(event_loop_conn *conn); - -/** - * Initialize a new http loop. - * - * @param routes array of routes that should be served - * @param route_count how many elements are in `routes` - * @param custom_gctx the application's custom global context; can be NULL - * @param custom_ctx_init function to initialize a new custom context - * @param custom_ctx_reset function to reset a custom context - * @param custom_ctx_free function to free a custom context; will always be run - * after a reset - * @return pointer to the newly allocated object - */ -http_loop *http_loop_init(http_route *routes, size_t route_count, - void *custom_gctx, void *(*custom_ctx_init)(), - void(custom_ctx_reset)(void *), - void(custom_ctx_free)(void *)); - -/** - * Set the API key the authentication steps should use. - * - * @param hl HTTP loop to set key in - * @param api_key API key to use - */ -void http_loop_set_api_key(http_loop *hl, const char *api_key); - -/** - * Run the HTTP loop. This function never returns. - * - * @param el the event loop - * @param port on what port to listen - */ -void http_loop_run(http_loop *hl, int port); - -#endif diff --git a/include/lander.h b/include/lander.h index c61b7b6..ae2f714 100644 --- a/include/lander.h +++ b/include/lander.h @@ -5,9 +5,6 @@ #include "lnm/http/loop.h" #include "lsm/store.h" -#include "http_loop.h" - -extern http_route lander_routes[6]; extern const char lander_key_charset[]; typedef struct lander_gctx { @@ -50,8 +47,6 @@ lnm_http_step_err lander_post_paste(lnm_http_conn *conn); lnm_http_step_err lander_stream_body_to_entry(lnm_http_conn *conn); -bool lander_stream_body_to_client(event_loop_conn *conn); - lnm_http_step_err lander_post_redirect_body_to_attr(lnm_http_conn *conn); lnm_http_step_err lander_remove_entry(lnm_http_conn *conn); diff --git a/lnm/Makefile b/lnm/Makefile index 7371c9f..38b2b4a 100644 --- a/lnm/Makefile +++ b/lnm/Makefile @@ -9,11 +9,10 @@ SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find include -iname '*.h' SRCS_H_INTERNAL != find $(SRC_DIR) -iname '*.h' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' -SRCS_THIRDPARTY != find '$(THIRDPARTY_DIR)/src' -iname '*.c' -OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) $(SRCS_THIRDPARTY:%=$(BUILD_DIR)/%.o) +OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o) -DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) $(SRCS_THIRDPARTY:%=$(BUILD_DIR)/%.d) +DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) BINS_TEST := $(OBJS_TEST:%.c.o=%) TARGETS_TEST := $(BINS_TEST:%=test-%) @@ -39,10 +38,6 @@ $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) $(CC) -c $(_CFLAGS) $< -o $@ -$(BUILD_DIR)/$(THIRDPARTY_DIR)/%.c.o: $(THIRDPARTY_DIR)/%.c - mkdir -p $(dir $@) - $(CC) $(_CFLAGS) -c $< -o $@ - # =====TESTING===== .PHONY: test test: $(TARGETS_TEST) @@ -91,11 +86,17 @@ $(BUILD_DIR)/$(EXAMPLE_DIR)/%.c.o: $(EXAMPLE_DIR)/%.c # =====MAINTENANCE===== .PHONY: lint lint: - clang-format -n --Werror $(SRCS) $(SRCS_H) $(SRCS_H_INTERNAL) + clang-format -n --Werror \ + $(filter-out $(THIRDPARTY),$(SRCS)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H_INTERNAL)) .PHONY: fmt fmt: - clang-format -i $(SRCS) $(SRCS_H) $(SRCS_H_INTERNAL) + clang-format -i \ + $(filter-out $(THIRDPARTY),$(SRCS)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H)) \ + $(filter-out $(THIRDPARTY),$(SRCS_H_INTERNAL)) .PHONY: check check: @@ -109,11 +110,11 @@ check: --check-level=exhaustive \ --quiet \ -j$(shell nproc) \ - $(SRCS) + $(filter-out $(THIRDPARTY),$(SRCS)) .PHONY: clean clean: - rm -rf $(BUILD_DIR) + rm -rf '$(BUILD_DIR)' .PHONY: bear diff --git a/lnm/config.mk b/lnm/config.mk index fde7e0e..7e55aff 100644 --- a/lnm/config.mk +++ b/lnm/config.mk @@ -3,10 +3,10 @@ LIB_FILENAME = liblnm.a BUILD_DIR = build SRC_DIR = src TEST_DIR = test -THIRDPARTY_DIR = thirdparty +THIRDPARTY = src/picohttpparser.c include/picohttpparser.h PUB_INC_DIR = include -INC_DIRS = $(PUB_INC_DIR) src/_include $(THIRDPARTY_DIR)/include +INC_DIRS = $(PUB_INC_DIR) src/_include # -MMD: generate a .d file for every source file. This file can be imported by # make and makes make aware that a header file has been changed, ensuring an diff --git a/lnm/thirdparty/include/picohttpparser.h b/lnm/include/picohttpparser.h similarity index 100% rename from lnm/thirdparty/include/picohttpparser.h rename to lnm/include/picohttpparser.h diff --git a/lnm/thirdparty/src/picohttpparser.c b/lnm/src/picohttpparser.c similarity index 100% rename from lnm/thirdparty/src/picohttpparser.c rename to lnm/src/picohttpparser.c diff --git a/src/lander/lander.c b/src/lander/lander.c index d6bf3f5..ff44b5c 100644 --- a/src/lander/lander.c +++ b/src/lander/lander.c @@ -4,8 +4,6 @@ #include "lnm/common.h" #include "lsm/store.h" -#include "http/types.h" -#include "http_loop.h" #include "lander.h" const char lander_key_charset[] = diff --git a/src/lander/lander_get.c b/src/lander/lander_get.c index 1bf4da0..4b42221 100644 --- a/src/lander/lander_get.c +++ b/src/lander/lander_get.c @@ -3,13 +3,10 @@ #include "lnm/http/consts.h" #include "lnm/http/loop.h" #include "lnm/loop.h" +#include "lsm/store.h" -#include "event_loop.h" -#include "http/res.h" -#include "http/types.h" #include "lander.h" #include "log.h" -#include "lsm/store.h" static const char index_page[] = "\n" @@ -156,25 +153,3 @@ lnm_http_step_err lander_get_entry(lnm_http_conn *conn) { return res; } - -bool lander_stream_body_to_client(event_loop_conn *conn) { - http_loop_ctx *ctx = conn->ctx; - lander_ctx *c_ctx = ctx->c; - - if ((c_ctx->entry == NULL) || - (ctx->res.body.expected_len == ctx->res.body.len)) { - return true; - } - - uint64_t to_write = MIN(EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size, - ctx->res.body.expected_len - ctx->res.body.len); - - uint64_t read = 0; - lsm_entry_data_read(&read, (char *)&conn->wbuf[conn->wbuf_size], c_ctx->entry, - to_write); - - ctx->res.body.len += read; - conn->wbuf_size += read; - - return false; -} diff --git a/src/lander/lander_post.c b/src/lander/lander_post.c index 882cb04..4bda832 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -1,10 +1,9 @@ -#include "http/res.h" -#include "http/types.h" -#include "lander.h" #include "lnm/loop.h" -#include "log.h" #include "lsm/store.h" +#include "lander.h" +#include "log.h" + static void randomize_key(char *key, int len) { size_t charset_len = strlen(lander_key_charset); diff --git a/src/lander/lander_steps.c b/src/lander/lander_steps.c index 6021dbd..00c86ba 100644 --- a/src/lander/lander_steps.c +++ b/src/lander/lander_steps.c @@ -1,16 +1,17 @@ #include -#include "lander.h" #include "lnm/http/loop.h" #include "lnm/loop.h" +#include "lander.h" + lnm_http_step_err lander_stream_body_to_entry(lnm_http_conn *conn) { lnm_http_loop_ctx *ctx = conn->ctx; lander_ctx *c_ctx = ctx->c; uint64_t to_append = - MIN(conn->r.size - conn->r.read, - ctx->req.body.expected_len - lsm_entry_data_len(c_ctx->entry)); + LNM_MIN(conn->r.size - conn->r.read, + ctx->req.body.expected_len - lsm_entry_data_len(c_ctx->entry)); lsm_str *data; lsm_str_init_copy_n(&data, (char *)&conn->r.buf[conn->r.read], to_append); diff --git a/thirdparty/include/picohttpparser.h b/thirdparty/include/picohttpparser.h deleted file mode 100644 index 07537cf..0000000 --- a/thirdparty/include/picohttpparser.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, - * Shigeo Mitsunari - * - * The software is licensed under either the MIT License (below) or the Perl - * license. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef picohttpparser_h -#define picohttpparser_h - -#include - -#ifdef _MSC_VER -#define ssize_t intptr_t -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* contains name and value of a header (name == NULL if is a continuing line - * of a multiline header */ -struct phr_header { - const char *name; - size_t name_len; - const char *value; - size_t value_len; -}; - -/* returns number of bytes consumed if successful, -2 if request is partial, - * -1 if failed */ -int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, - int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* ditto */ -int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, - struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* ditto */ -int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* should be zero-filled before start */ -struct phr_chunked_decoder { - size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ - char consume_trailer; /* if trailing headers should be consumed */ - char _hex_count; - char _state; -}; - -/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- - * encoding headers. When the function returns without an error, bufsz is - * updated to the length of the decoded data available. Applications should - * repeatedly call the function while it returns -2 (incomplete) every time - * supplying newly arrived data. If the end of the chunked-encoded data is - * found, the function returns a non-negative number indicating the number of - * octets left undecoded, that starts from the offset returned by `*bufsz`. - * Returns -1 on error. - */ -ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz); - -/* returns if the chunked decoder is in middle of chunked data */ -int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/thirdparty/src/picohttpparser.c b/thirdparty/src/picohttpparser.c deleted file mode 100644 index 5e5783a..0000000 --- a/thirdparty/src/picohttpparser.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, - * Shigeo Mitsunari - * - * The software is licensed under either the MIT License (below) or the Perl - * license. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include -#include -#ifdef __SSE4_2__ -#ifdef _MSC_VER -#include -#else -#include -#endif -#endif -#include "picohttpparser.h" - -#if __GNUC__ >= 3 -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#else -#define likely(x) (x) -#define unlikely(x) (x) -#endif - -#ifdef _MSC_VER -#define ALIGNED(n) _declspec(align(n)) -#else -#define ALIGNED(n) __attribute__((aligned(n))) -#endif - -#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) - -#define CHECK_EOF() \ - if (buf == buf_end) { \ - *ret = -2; \ - return NULL; \ - } - -#define EXPECT_CHAR_NO_CHECK(ch) \ - if (*buf++ != ch) { \ - *ret = -1; \ - return NULL; \ - } - -#define EXPECT_CHAR(ch) \ - CHECK_EOF(); \ - EXPECT_CHAR_NO_CHECK(ch); - -#define ADVANCE_TOKEN(tok, toklen) \ - do { \ - const char *tok_start = buf; \ - static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ - int found2; \ - buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ - if (!found2) { \ - CHECK_EOF(); \ - } \ - while (1) { \ - if (*buf == ' ') { \ - break; \ - } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ - if ((unsigned char)*buf < '\040' || *buf == '\177') { \ - *ret = -1; \ - return NULL; \ - } \ - } \ - ++buf; \ - CHECK_EOF(); \ - } \ - tok = tok_start; \ - toklen = buf - tok_start; \ - } while (0) - -static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" - "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" - "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - -static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) -{ - *found = 0; -#if __SSE4_2__ - if (likely(buf_end - buf >= 16)) { - __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); - - size_t left = (buf_end - buf) & ~15; - do { - __m128i b16 = _mm_loadu_si128((const __m128i *)buf); - int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); - if (unlikely(r != 16)) { - buf += r; - *found = 1; - break; - } - buf += 16; - left -= 16; - } while (likely(left != 0)); - } -#else - /* suppress unused parameter warning */ - (void)buf_end; - (void)ranges; - (void)ranges_size; -#endif - return buf; -} - -static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) -{ - const char *token_start = buf; - -#ifdef __SSE4_2__ - static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ - "\012\037" /* allow SP and up to but not including DEL */ - "\177\177"; /* allow chars w. MSB set */ - int found; - buf = findchar_fast(buf, buf_end, ranges1, 6, &found); - if (found) - goto FOUND_CTL; -#else - /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ - while (likely(buf_end - buf >= 8)) { -#define DOIT() \ - do { \ - if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ - goto NonPrintable; \ - ++buf; \ - } while (0) - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); -#undef DOIT - continue; - NonPrintable: - if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { - goto FOUND_CTL; - } - ++buf; - } -#endif - for (;; ++buf) { - CHECK_EOF(); - if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { - if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { - goto FOUND_CTL; - } - } - } -FOUND_CTL: - if (likely(*buf == '\015')) { - ++buf; - EXPECT_CHAR('\012'); - *token_len = buf - 2 - token_start; - } else if (*buf == '\012') { - *token_len = buf - token_start; - ++buf; - } else { - *ret = -1; - return NULL; - } - *token = token_start; - - return buf; -} - -static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) -{ - int ret_cnt = 0; - buf = last_len < 3 ? buf : buf + last_len - 3; - - while (1) { - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - CHECK_EOF(); - EXPECT_CHAR('\012'); - ++ret_cnt; - } else if (*buf == '\012') { - ++buf; - ++ret_cnt; - } else { - ++buf; - ret_cnt = 0; - } - if (ret_cnt == 2) { - return buf; - } - } - - *ret = -2; - return NULL; -} - -#define PARSE_INT(valp_, mul_) \ - if (*buf < '0' || '9' < *buf) { \ - buf++; \ - *ret = -1; \ - return NULL; \ - } \ - *(valp_) = (mul_) * (*buf++ - '0'); - -#define PARSE_INT_3(valp_) \ - do { \ - int res_ = 0; \ - PARSE_INT(&res_, 100) \ - *valp_ = res_; \ - PARSE_INT(&res_, 10) \ - *valp_ += res_; \ - PARSE_INT(&res_, 1) \ - *valp_ += res_; \ - } while (0) - -/* returned pointer is always within [buf, buf_end), or null */ -static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char, - int *ret) -{ - /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128 - * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */ - static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */ - "\"\"" /* 0x22 */ - "()" /* 0x28,0x29 */ - ",," /* 0x2c */ - "//" /* 0x2f */ - ":@" /* 0x3a-0x40 */ - "[]" /* 0x5b-0x5d */ - "{\xff"; /* 0x7b-0xff */ - const char *buf_start = buf; - int found; - buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found); - if (!found) { - CHECK_EOF(); - } - while (1) { - if (*buf == next_char) { - break; - } else if (!token_char_map[(unsigned char)*buf]) { - *ret = -1; - return NULL; - } - ++buf; - CHECK_EOF(); - } - *token = buf_start; - *token_len = buf - buf_start; - return buf; -} - -/* returned pointer is always within [buf, buf_end), or null */ -static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret) -{ - /* we want at least [HTTP/1.] to try to parse */ - if (buf_end - buf < 9) { - *ret = -2; - return NULL; - } - EXPECT_CHAR_NO_CHECK('H'); - EXPECT_CHAR_NO_CHECK('T'); - EXPECT_CHAR_NO_CHECK('T'); - EXPECT_CHAR_NO_CHECK('P'); - EXPECT_CHAR_NO_CHECK('/'); - EXPECT_CHAR_NO_CHECK('1'); - EXPECT_CHAR_NO_CHECK('.'); - PARSE_INT(minor_version, 1); - return buf; -} - -static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, - size_t max_headers, int *ret) -{ - for (;; ++*num_headers) { - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - break; - } else if (*buf == '\012') { - ++buf; - break; - } - if (*num_headers == max_headers) { - *ret = -1; - return NULL; - } - if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { - /* parsing name, but do not discard SP before colon, see - * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ - if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) { - return NULL; - } - if (headers[*num_headers].name_len == 0) { - *ret = -1; - return NULL; - } - ++buf; - for (;; ++buf) { - CHECK_EOF(); - if (!(*buf == ' ' || *buf == '\t')) { - break; - } - } - } else { - headers[*num_headers].name = NULL; - headers[*num_headers].name_len = 0; - } - const char *value; - size_t value_len; - if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { - return NULL; - } - /* remove trailing SPs and HTABs */ - const char *value_end = value + value_len; - for (; value_end != value; --value_end) { - const char c = *(value_end - 1); - if (!(c == ' ' || c == '\t')) { - break; - } - } - headers[*num_headers].value = value; - headers[*num_headers].value_len = value_end - value; - } - return buf; -} - -static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, - size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, - size_t max_headers, int *ret) -{ - /* skip first empty line (some clients add CRLF after POST content) */ - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - } else if (*buf == '\012') { - ++buf; - } - - /* parse request line */ - if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) { - return NULL; - } - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - ADVANCE_TOKEN(*path, *path_len); - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - if (*method_len == 0 || *path_len == 0) { - *ret = -1; - return NULL; - } - if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { - return NULL; - } - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - } else if (*buf == '\012') { - ++buf; - } else { - *ret = -1; - return NULL; - } - - return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); -} - -int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, - size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf_start + len; - size_t max_headers = *num_headers; - int r; - - *method = NULL; - *method_len = 0; - *path = NULL; - *path_len = 0; - *minor_version = -1; - *num_headers = 0; - - /* if last_len != 0, check if the request is complete (a fast countermeasure - againt slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers, - &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg, - size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret) -{ - /* parse "HTTP/1.x" */ - if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { - return NULL; - } - /* skip space */ - if (*buf != ' ') { - *ret = -1; - return NULL; - } - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - /* parse status code, we want at least [:digit:][:digit:][:digit:] to try to parse */ - if (buf_end - buf < 4) { - *ret = -2; - return NULL; - } - PARSE_INT_3(status); - - /* get message including preceding space */ - if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { - return NULL; - } - if (*msg_len == 0) { - /* ok */ - } else if (**msg == ' ') { - /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP - * before running past the end of the given buffer. */ - do { - ++*msg; - --*msg_len; - } while (**msg == ' '); - } else { - /* garbage found after status code */ - *ret = -1; - return NULL; - } - - return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); -} - -int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, - struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf + len; - size_t max_headers = *num_headers; - int r; - - *minor_version = -1; - *status = 0; - *msg = NULL; - *msg_len = 0; - *num_headers = 0; - - /* if last_len != 0, check if the response is complete (a fast countermeasure - against slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf + len; - size_t max_headers = *num_headers; - int r; - - *num_headers = 0; - - /* if last_len != 0, check if the response is complete (a fast countermeasure - against slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -enum { - CHUNKED_IN_CHUNK_SIZE, - CHUNKED_IN_CHUNK_EXT, - CHUNKED_IN_CHUNK_DATA, - CHUNKED_IN_CHUNK_CRLF, - CHUNKED_IN_TRAILERS_LINE_HEAD, - CHUNKED_IN_TRAILERS_LINE_MIDDLE -}; - -static int decode_hex(int ch) -{ - if ('0' <= ch && ch <= '9') { - return ch - '0'; - } else if ('A' <= ch && ch <= 'F') { - return ch - 'A' + 0xa; - } else if ('a' <= ch && ch <= 'f') { - return ch - 'a' + 0xa; - } else { - return -1; - } -} - -ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) -{ - size_t dst = 0, src = 0, bufsz = *_bufsz; - ssize_t ret = -2; /* incomplete */ - - while (1) { - switch (decoder->_state) { - case CHUNKED_IN_CHUNK_SIZE: - for (;; ++src) { - int v; - if (src == bufsz) - goto Exit; - if ((v = decode_hex(buf[src])) == -1) { - if (decoder->_hex_count == 0) { - ret = -1; - goto Exit; - } - break; - } - if (decoder->_hex_count == sizeof(size_t) * 2) { - ret = -1; - goto Exit; - } - decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; - ++decoder->_hex_count; - } - decoder->_hex_count = 0; - decoder->_state = CHUNKED_IN_CHUNK_EXT; - /* fallthru */ - case CHUNKED_IN_CHUNK_EXT: - /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] == '\012') - break; - } - ++src; - if (decoder->bytes_left_in_chunk == 0) { - if (decoder->consume_trailer) { - decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; - break; - } else { - goto Complete; - } - } - decoder->_state = CHUNKED_IN_CHUNK_DATA; - /* fallthru */ - case CHUNKED_IN_CHUNK_DATA: { - size_t avail = bufsz - src; - if (avail < decoder->bytes_left_in_chunk) { - if (dst != src) - memmove(buf + dst, buf + src, avail); - src += avail; - dst += avail; - decoder->bytes_left_in_chunk -= avail; - goto Exit; - } - if (dst != src) - memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); - src += decoder->bytes_left_in_chunk; - dst += decoder->bytes_left_in_chunk; - decoder->bytes_left_in_chunk = 0; - decoder->_state = CHUNKED_IN_CHUNK_CRLF; - } - /* fallthru */ - case CHUNKED_IN_CHUNK_CRLF: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] != '\015') - break; - } - if (buf[src] != '\012') { - ret = -1; - goto Exit; - } - ++src; - decoder->_state = CHUNKED_IN_CHUNK_SIZE; - break; - case CHUNKED_IN_TRAILERS_LINE_HEAD: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] != '\015') - break; - } - if (buf[src++] == '\012') - goto Complete; - decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; - /* fallthru */ - case CHUNKED_IN_TRAILERS_LINE_MIDDLE: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] == '\012') - break; - } - ++src; - decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; - break; - default: - assert(!"decoder is corrupt"); - } - } - -Complete: - ret = bufsz - src; -Exit: - if (dst != src) - memmove(buf + dst, buf + src, bufsz - src); - *_bufsz = dst; - return ret; -} - -int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) -{ - return decoder->_state == CHUNKED_IN_CHUNK_DATA; -} - -#undef CHECK_EOF -#undef EXPECT_CHAR -#undef ADVANCE_TOKEN