diff options
Diffstat (limited to 'kit/http1.h')
-rw-r--r-- | kit/http1.h | 511 |
1 files changed, 0 insertions, 511 deletions
diff --git a/kit/http1.h b/kit/http1.h deleted file mode 100644 index 0a58f96..0000000 --- a/kit/http1.h +++ /dev/null @@ -1,511 +0,0 @@ -// TODO -// - Error handling -// - HTTPS support - -#ifndef KIT_HTTP1_H -#define KIT_HTTP1_H - -#include "types.h" -#include "string_builder.h" -#include "sockets.h" - -#include <stdio.h> -#include <assert.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define KIT_HTTP1_NEWLINE "\r\n" -#define KIT_HTTP1_SPACE " " -#define KIT_HTTP1_HEADER_SEPARATOR ": " -#define KIT_HTTP1_BUFFER_SIZE 8192 - -enum { - KIT_HTTP1_OPTIONS, - KIT_HTTP1_GET, - KIT_HTTP1_HEAD, - KIT_HTTP1_POST, - KIT_HTTP1_PUT, - KIT_HTTP1_DELETE, - KIT_HTTP1_TRACE, - KIT_HTTP1_CONNECT, -}; - -typedef struct { - kit_str_t key; - kit_str_t value; -} kit_http1_str_pair_t; - -typedef KIT_DA(kit_http1_str_pair_t) kit_http1_str_map_t; - -typedef struct { - kit_str_t str; - i64 position; -} kit_http1_tok_t; - -typedef struct { - kit_str_t protocol; - kit_str_t host; - kit_str_t port; - kit_str_t address; - kit_str_t query_string; - kit_str_t hash; - kit_http1_str_map_t parameters; -} kit_http1_uri_t; - -typedef struct { - i8 success; - kit_str_builder_t protocol; - kit_str_builder_t response; - kit_str_builder_t response_str; - kit_http1_str_map_t header; - kit_str_builder_t header_str; - kit_str_builder_t body; -} kit_http1_response_t; - -#ifdef __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-function" -# pragma GCC diagnostic ignored "-Wunknown-pragmas" -#endif - -static kit_str_t kit_http1_tok_tail(kit_http1_tok_t *tok) { - assert(tok != NULL); - - if (tok == NULL) - return (kit_str_t) { .size = 0, .values = NULL }; - - i64 previous_position = tok->position; - tok->position = tok->str.size; - - return (kit_str_t) { .size = tok->position - previous_position, - .values = tok->str.values + - previous_position }; -} - -static kit_str_t kit_http1_tok_next(kit_http1_tok_t *tok, - kit_str_t search, - i8 return_tail) { - assert(tok != NULL); - if (tok == NULL) - return (kit_str_t) { .size = 0, .values = NULL }; - - i64 hit = tok->position; - - while (hit + search.size <= tok->str.size) { - kit_str_t s = { .size = search.size, - .values = tok->str.values + hit }; - if (KIT_AR_EQUAL(s, search)) - break; - } - - if (hit + search.size > tok->str.size) { - if (return_tail) - return kit_http1_tok_tail(tok); - else - return (kit_str_t) { .size = 0, .values = NULL }; - } - - i64 previous_position = tok->position; - tok->position = hit + search.size; - - return (kit_str_t) { .size = hit - previous_position, - .values = tok->str.values + - previous_position }; -} - -// TODO -// - Return error status -static void kit_http1_uri_init(kit_http1_uri_t *uri, kit_str_t input, - kit_allocator_t *alloc) { - static const kit_str_t port_80 = { .size = 2, .values = "80" }; - - assert(uri != NULL); - if (uri == NULL) - return; - - memset(uri, 0, sizeof *uri); - - kit_http1_tok_t input_tok = { .str = input, .position = 0 }; - - uri->protocol = kit_http1_tok_next(&input_tok, SZ("://"), 0); - kit_str_t host_port_str = kit_http1_tok_next(&input_tok, SZ("/"), - 0); - - // FIXME - // Handle invalid host port error - assert(host_port_str.size > 0); - if (host_port_str.size == 0) - return; - - kit_http1_tok_t host_port_tok = { .str = host_port_str, - .position = 0 }; - - uri->host = kit_http1_tok_next( - &host_port_tok, - host_port_str.values[0] == '[' ? SZ("]:") : SZ(":"), 1); - - if (uri->host.values[0] == '[') - uri->host = (kit_str_t) { .size = uri->host.size - 1, - .values = uri->host.values + 1 }; - - // TODO - // - HTTPS default port 443 - uri->port = kit_http1_tok_tail(&host_port_tok); - if (uri->port.size == 0) - uri->port = port_80; - - uri->address = kit_http1_tok_next(&input_tok, SZ("?"), 1); - uri->query_string = kit_http1_tok_next(&input_tok, SZ("#"), 1); - - uri->hash = kit_http1_tok_tail(&input_tok); - - KIT_DA_INIT(uri->parameters, 0, alloc); - - kit_http1_tok_t query_tok = { - .str = { .size = uri->query_string.size, - .values = uri->query_string.values }, - .position = 0 - }; - - for (;;) { - kit_str_t key = kit_http1_tok_next(&query_tok, SZ("="), 0); - if (key.size == 0) - break; - kit_str_t value = kit_http1_tok_next(&query_tok, SZ("&"), 1); - - i64 index = uri->parameters.size; - - KIT_DA_RESIZE(uri->parameters, index + 1); - - assert(uri->parameters.size == index + 1); - if (uri->parameters.size != index + 1) - // FIXME - // Handle bad alloc error - break; - - uri->parameters.values[index] = (kit_http1_str_pair_t) { - .key = key, .value = value - }; - } -} - -static void kit_http1_uri_destroy(kit_http1_uri_t *uri) { - assert(uri != NULL); - if (uri == NULL) - return; - - KIT_DA_DESTROY(uri->parameters); - - memset(uri, 0, sizeof *uri); -} - -static kit_str_t kit_http1_method_to_str(i32 method) { - static kit_str_t methods[] = { { .size = 7, .values = "OPTIONS" }, - { .size = 3, .values = "GET" }, - { .size = 4, .values = "HEAD" }, - { .size = 4, .values = "POST" }, - { .size = 3, .values = "PUT" }, - { .size = 6, .values = "DELETE" }, - { .size = 5, .values = "TRACE" }, - { .size = 7, .values = "CONNECT" } }; - - assert(method >= 0 && method < (i32) (sizeof methods / sizeof *methods)); - if (method < 0 || method >= (i32) (sizeof methods / sizeof *methods)) - return (kit_str_t) { .size = 0, .values = NULL }; - - return methods[method]; -} - -// TODO -// - Return error status -static socket_t kit_http1_connect_to_uri(kit_http1_uri_t *uri) { - assert(uri != NULL); - if (uri == NULL) - return INVALID_SOCKET; - - struct addrinfo hints, *result, *rp; - - memset(&hints, 0, sizeof hints); - - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - char host_str[128]; - char port_str[128]; - - assert(uri->host.size < (i64) sizeof host_str); - memcpy(host_str, uri->host.values, uri->host.size); - host_str[uri->host.size] = '\0'; - - assert(uri->port.size < (i64) sizeof port_str); - memcpy(port_str, uri->port.values, uri->port.size); - port_str[uri->port.size] = '\0'; - - i32 getaddrinfo_result = getaddrinfo(host_str, port_str, &hints, - &result); - - if (getaddrinfo_result != 0) - return INVALID_SOCKET; - - socket_t fd = INVALID_SOCKET; - - for (rp = result; rp != NULL; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - - if (fd == INVALID_SOCKET) - continue; - - i32 connect_result = connect(fd, rp->ai_addr, - (socklen_t) rp->ai_addrlen); - - if (connect_result == -1) { - closesocket(fd); - fd = INVALID_SOCKET; - continue; - } - - break; - } - - freeaddrinfo(result); - - return fd; -} - -// TODO -// - Return error status -static kit_str_builder_t kit_http1_buffered_read( - socket_t fd, kit_allocator_t *alloc) { - kit_str_builder_t buf; - KIT_DA_INIT(buf, KIT_HTTP1_BUFFER_SIZE, alloc); - - // FIXME - // Handle bad alloc error - assert(buf.size == KIT_HTTP1_BUFFER_SIZE); - if (buf.size != KIT_HTTP1_BUFFER_SIZE) - return buf; - - i64 offset = 0; - - for (;;) { - i64 chunk = buf.size - offset; - i64 n = recv(fd, buf.values + offset, (socklen_t) chunk, 0); - if (n < chunk) - break; - offset += n; - KIT_DA_RESIZE(buf, offset + KIT_HTTP1_BUFFER_SIZE); - - // FIXME - // Handle bad alloc error - assert(buf.size == offset + KIT_HTTP1_BUFFER_SIZE); - if (buf.size != offset + KIT_HTTP1_BUFFER_SIZE) - break; - } - - return buf; -} - -// TODO -// - Return error status -static kit_http1_response_t kit_http1_request( - i32 method, kit_http1_uri_t *uri, kit_str_t body, - kit_allocator_t *alloc) { - - socket_t fd = kit_http1_connect_to_uri(uri); - - assert(fd != INVALID_SOCKET); - if (fd == INVALID_SOCKET) { - kit_http1_response_t res; - memset(&res, 0, sizeof res); - res.success = 0; - return res; - } - - kit_str_builder_t req; - - // reserve - KIT_DA_INIT(req, 512, alloc); - - assert(req.size == 512); - if (req.size != 512) { - // bad alloc - // - - closesocket(fd); - - kit_http1_response_t res; - memset(&res, 0, sizeof res); - res.success = 0; - return res; - } - - req.size = 0; - - // FIXME - // Handle errors - // - - kit_str_append(&req, kit_http1_method_to_str(method)); - kit_str_append(&req, SZ(" /")); - kit_str_append(&req, uri->address); - kit_str_append(&req, - uri->query_string.size == 0 ? SZ("") : SZ("?")); - kit_str_append(&req, uri->query_string); - kit_str_append(&req, SZ(" HTTP/1.1")); - kit_str_append(&req, SZ(KIT_HTTP1_NEWLINE)); - - kit_str_append(&req, SZ("Host: ")); - kit_str_append(&req, uri->host); - kit_str_append(&req, SZ(":")); - kit_str_append(&req, uri->port); - kit_str_append(&req, SZ(KIT_HTTP1_NEWLINE)); - - kit_str_append(&req, SZ("Accept: */*")); - kit_str_append(&req, SZ(KIT_HTTP1_NEWLINE)); - - kit_str_append( - &req, - SZ("Content-Type: text/plain; version=0.0.4; charset=utf-8")); - kit_str_append(&req, SZ(KIT_HTTP1_NEWLINE)); - - // FIXME - // Use our own print instead - char content_length_str[11]; - sprintf(content_length_str, "%lld", body.size); - - kit_str_append(&req, SZ("Content-Length: ")); - kit_str_append(&req, - (kit_str_t) { .size = strlen(content_length_str), - .values = content_length_str }); - kit_str_append(&req, SZ(KIT_HTTP1_NEWLINE)); - - kit_str_append(&req, SZ(KIT_HTTP1_NEWLINE)); - kit_str_append(&req, body); - - // Append '\0' at the end - // - { - i64 n = req.size; - KIT_DA_RESIZE(req, n + 1); - - assert(req.size == n + 1); - if (req.size != n + 1) { - KIT_DA_DESTROY(req); - closesocket(fd); - - kit_http1_response_t res; - memset(&res, 0, sizeof res); - res.success = 0; - return res; - } - - req.size = n; - req.values[n] = '\0'; - } - - i64 req_size = req.size; - i64 n = send(fd, req.values, (socklen_t) req.size, 0); - - KIT_DA_DESTROY(req); - - if (n != req_size) { - // send failed - // - - closesocket(fd); - - kit_http1_response_t res; - memset(&res, 0, sizeof res); - res.success = 0; - return res; - } - - kit_str_builder_t buffer = kit_http1_buffered_read(fd, alloc); - - closesocket(fd); - - kit_http1_response_t res; - memset(&res, 0, sizeof res); - res.success = 1; - - kit_http1_tok_t buf_tok = { .str = { .size = buffer.size, - .values = buffer.values }, - .position = 0 }; - - res.protocol = kit_str_build( - kit_http1_tok_next(&buf_tok, SZ(KIT_HTTP1_SPACE), 0), alloc); - res.response = kit_str_build( - kit_http1_tok_next(&buf_tok, SZ(KIT_HTTP1_SPACE), 0), alloc); - res.response_str = kit_str_build( - kit_http1_tok_next(&buf_tok, SZ(KIT_HTTP1_NEWLINE), 0), alloc); - - res.header_str = kit_str_build( - kit_http1_tok_next(&buf_tok, - SZ(KIT_HTTP1_NEWLINE KIT_HTTP1_NEWLINE), 0), - alloc); - - res.body = kit_str_build(kit_http1_tok_tail(&buf_tok), alloc); - - kit_http1_tok_t header_tok = { - .str = (kit_str_t) { .size = res.header_str.size, - .values = res.header_str.values }, - .position = 0 - }; - - KIT_DA_INIT(res.header, 0, alloc); - - for (;;) { - kit_str_t key = kit_http1_tok_next( - &header_tok, SZ(KIT_HTTP1_HEADER_SEPARATOR), 0); - if (key.size == 0) - break; - kit_str_t value = kit_http1_tok_next(&header_tok, - SZ(KIT_HTTP1_NEWLINE), 1); - - i64 index = res.header.size; - - KIT_DA_RESIZE(res.header, index + 1); - - assert(res.header.size == index + 1); - if (res.header.size != index + 1) { - // FIXME - // Handle bad alloc error - res.success = 0; - break; - } - - res.header.values[index] = (kit_http1_str_pair_t) { - .key = key, .value = value - }; - } - - return res; -} - -static void kit_http1_response_destroy( - kit_http1_response_t *response) { - assert(response != NULL); - if (response == NULL) - return; - - KIT_DA_DESTROY(response->protocol); - KIT_DA_DESTROY(response->response); - KIT_DA_DESTROY(response->response_str); - KIT_DA_DESTROY(response->header); - KIT_DA_DESTROY(response->header_str); - KIT_DA_DESTROY(response->body); - - memset(response, 0, sizeof *response); -} - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif - -#ifdef __cplusplus -} -#endif - -#endif |