summaryrefslogtreecommitdiff
path: root/kit/http1.h
diff options
context:
space:
mode:
Diffstat (limited to 'kit/http1.h')
-rw-r--r--kit/http1.h511
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