NEWS: Welcome to my new homepage! <3

Initial implementation - libhttp - A basic HTTP Framework

libhttp

A basic HTTP Framework
git clone git://192.168.2.2/libhttp
Log | Files | Refs | README

commit 76c0a03c0b506c8d17058f8f66219b50797d7dba
Author: typable <contact@typable.dev>
Date:   Wed,  3 Apr 2024 13:51:31 +0200

Initial implementation

Diffstat:
AMakefile | 18++++++++++++++++++
AREADME.md | 3+++
Alibhttp.c | 565+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibhttp.h | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 710 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,18 @@ +default: build + +build: + cc -o libhttp.so -shared -fPIC -lpthread -lssl -Wall -Wextra libhttp.c + +install: + cc -o libhttp.so -shared -fPIC libhttp.c + sudo cp libhttp.h /usr/local/include/ + sudo cp libhttp.so /usr/local/lib/ + sudo ldconfig + rm libhttp.so + +uninstall: + sudo rm /usr/local/include/libhttp.h + sudo rm /usr/local/lib/libhttp.so + +clean: + rm libhttp.so diff --git a/README.md b/README.md @@ -0,0 +1,3 @@ +# libhttp + +A basic HTTP Framework diff --git a/libhttp.c b/libhttp.c @@ -0,0 +1,565 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <pthread.h> +#include <netdb.h> +#include <errno.h> +#include <poll.h> +#include <openssl/ssl.h> + +#include "libhttp.h" + +#define MAX_REQUEST_HEAD_SIZE 40960 + +int http_sockfd; +pthread_t http_thread; +bool http_quit = false; +bool http_error = false; +SSL_CTX *http_ctx = NULL; + +http_response_t (*on_request_fn)(http_request_t *) = NULL; + +void http_on_request(http_response_t fn(http_request_t *)) { + on_request_fn = fn; +} + +char *http_read_file(char *filename) { + FILE *fp = fopen(filename, "r"); + fseek(fp, 0, SEEK_END); + long len = ftell(fp); + fseek(fp, 0, SEEK_SET); + char *content = malloc(sizeof(char) * (len + 1)); + fread(content, len, sizeof(char), fp); + content[len] = '\0'; + fclose(fp); + return content; +} + +char *http_status_str(http_status_t status) { + switch (status) { + case HTTP_STATUS_CONTINUE: + return "Continue"; + case HTTP_STATUS_SWITCHING_PROTOCOLS: + return "Switching Protocols"; + case HTTP_STATUS_PROCESSING: + return "Processing"; + case HTTP_STATUS_EARLY_HINTS: + return "Early Hints"; + case HTTP_STATUS_OK: + return "OK"; + case HTTP_STATUS_CREATED: + return "Created"; + case HTTP_STATUS_ACCEPTED: + return "Accepted"; + case HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION: + return "Non-Authoritative Information"; + case HTTP_STATUS_NO_CONTENT: + return "No Content"; + case HTTP_STATUS_RESET_CONTENT: + return "Reset Content"; + case HTTP_STATUS_PARTIAL_CONTENT: + return "Partial Content"; + case HTTP_STATUS_MULTI_STATUS: + return "Multi-Status"; + case HTTP_STATUS_ALREADY_REPORTED: + return "Already Reported"; + case HTTP_STATUS_IM_USED: + return "IM Used"; + case HTTP_STATUS_MULTIPLE_CHOICES: + return "Multiple Choices"; + case HTTP_STATUS_MOVED_PERMANENTLY: + return "Moved Permanently"; + case HTTP_STATUS_FOUND: + return "Found"; + case HTTP_STATUS_SEE_OTHER: + return "See Other"; + case HTTP_STATUS_NOT_MODIFIED: + return "Not Modified"; + case HTTP_STATUS_TEMPORARY_REDIRECT: + return "Temporary Redirect"; + case HTTP_STATUS_PERMANENT_REDIRECT: + return "Permanent Redirect"; + case HTTP_STATUS_BAD_REQUEST: + return "Bad Request"; + case HTTP_STATUS_UNAUTHORIZED: + return "Unauthorized"; + case HTTP_STATUS_PAYMENT_REQUIRED: + return "Payment Required"; + case HTTP_STATUS_FORBIDDEN: + return "Forbidden"; + case HTTP_STATUS_NOT_FOUND: + return "Not Found"; + case HTTP_STATUS_METHOD_NOT_ALLOWED: + return "Method Not Allowed"; + case HTTP_STATUS_NOT_ACCEPTABLE: + return "Not Acceptable"; + case HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED: + return "Proxy Authentication Required"; + case HTTP_STATUS_REQUEST_TIMEOUT: + return "Request Timeout"; + case HTTP_STATUS_CONFLICT: + return "Conflict"; + case HTTP_STATUS_GONE: + return "Gone"; + case HTTP_STATUS_LENGTH_REQUIRED: + return "Length Required"; + case HTTP_STATUS_PRECONDITION_FAILED: + return "Precondition Failed"; + case HTTP_STATUS_PAYLOAD_TOO_LARGE: + return "Payload Too Large"; + case HTTP_STATUS_URI_TOO_LONG: + return "URI Too Long"; + case HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE: + return "Unsupported Media Type"; + case HTTP_STATUS_RANGE_NOT_SATISFIABLE: + return "Range Not Satisfiable"; + case HTTP_STATUS_EXPECTATION_FAILED: + return "Expectation Failed"; + case HTTP_STATUS_I_AM_A_TEAPOT: + return "I'm a teapot"; + case HTTP_STATUS_MISDIRECTED_REQUEST: + return "Misdirected Request"; + case HTTP_STATUS_UNPROCESSABLE_CONTENT: + return "Unprocessable Content"; + case HTTP_STATUS_LOCKED: + return "Locked"; + case HTTP_STATUS_FAILED_DEPENDENCY: + return "Failed Dependency"; + case HTTP_STATUS_TOO_EARLY: + return "Too Early"; + case HTTP_STATUS_UPGRADE_REQUIRED: + return "Upgrade Required"; + case HTTP_STATUS_PRECONDITION_REQUIRED: + return "Precondition Required"; + case HTTP_STATUS_TOO_MANY_REQUESTS: + return "Too Many Requests"; + case HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE: + return "Request Header Fields Too Large"; + case HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS: + return "Unavailable For Legal Reasons"; + case HTTP_STATUS_INTERNAL_SERVER_ERROR: + return "Internal Server Error"; + case HTTP_STATUS_NOT_IMPLEMENTED: + return "Not Implemented"; + case HTTP_STATUS_BAD_GATEWAY: + return "Bad Gateway"; + case HTTP_STATUS_SERVICE_UNAVAILABLE: + return "Service Unavailable"; + case HTTP_STATUS_GATEWAY_TIMEOUT: + return "Gateway Timeout"; + case HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED: + return "HTTP Version Not Supported"; + case HTTP_STATUS_VARIANT_ALSO_NEGOTITATES: + return "Variant Also Negotitates"; + case HTTP_STATUS_INSUFFICIENT_STORAGE: + return "Insufficient Storage"; + case HTTP_STATUS_LOOP_DETECTED: + return "Loop Detected"; + case HTTP_STATUS_NOT_EXTENDED: + return "Not Extended"; + case HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED: + return "Network Authentication Required"; + default: + return NULL; + } +} + +ssize_t http_read(conn_t *conn, void *buffer, size_t len) { + if (conn->ssl != NULL) { + return SSL_read(conn->ssl, buffer, len); + } + return read(conn->sockfd, buffer, len); +} + +ssize_t http_write(conn_t *conn, void *buffer, size_t len) { + if (conn->ssl != NULL) { + return SSL_write(conn->ssl, buffer, len); + } + return write(conn->sockfd, buffer, len); +} + +char *http_header_get(http_request_t *request, char *key) { + if (request->headers_len > 0) { + for (int i = 0; i < request->headers_len; i++) { + http_header_t *header = &request->headers[i]; + if (strcmp(header->key, key) == 0) { + return header->value; + } + } + } + return NULL; +} + +void http_header_set(http_response_t *response, char *key, char *value) { + http_header_t *new_header = NULL; + if (response->headers_len > 0) { + for (int i = 0; i < response->headers_len; i++) { + http_header_t *header = &response->headers[i]; + if (strcmp(header->key, key) == 0) { + new_header = header; + break; + } + } + } + if (new_header == NULL) { + response->headers = realloc(response->headers, sizeof(http_header_t) * (response->headers_len + 1)); + new_header = &response->headers[response->headers_len]; + response->headers_len++; + } + int key_len = strlen(key); + new_header->key = malloc(sizeof(char) * (key_len + 1)); + memcpy(new_header->key, key, key_len); + new_header->key[key_len] = '\0'; + int value_len = strlen(value); + new_header->value = malloc(sizeof(char) * (value_len + 1)); + memcpy(new_header->value, value, value_len); + new_header->value[value_len] = '\0'; +} + +http_response_t http_response_create(http_status_t status) { + char *version = "HTTP/1.1"; + int version_len = strlen(version); + http_response_t response = { NULL, status, NULL, 0, NULL, 0 }; + response.version = malloc(sizeof(char) * (version_len + 1)); + memcpy(response.version, version, version_len); + response.version[version_len] = '\0'; + return response; +} + +void http_response_body(http_response_t *response, char *body, long len) { + if (response != NULL && body != NULL) { + response->body = malloc(sizeof(char) * len); + memcpy(response->body, body, len); + response->body_len = len; + char content_length[50]; + snprintf(content_length, 50, "%ld", len); + http_header_set(response, "Content-Length", content_length); + } +} + +void http_response_file(http_response_t *response, char *filename) { + char *content = http_read_file(filename); + if (content != NULL) { + http_response_body(response, content, strlen(content)); + free(content); + } +} + +void http_request_free(http_request_t *request) { + if (request->method != NULL) { + free(request->method); + } + if (request->url != NULL) { + free(request->url); + } + if (request->version != NULL) { + free(request->version); + } + if (request->headers_len > 0) { + for (int i = 0; i < request->headers_len; i++) { + http_header_t *header = &request->headers[i]; + if (header->key != NULL) { + free(header->key); + } + if (header->value != NULL) { + free(header->value); + } + } + } + if (request->body != NULL) { + free(request->body); + } +} + +void http_response_free(http_response_t *response) { + if (response->version != NULL) { + free(response->version); + } + if (response->headers_len > 0) { + for (int i = 0; i < response->headers_len; i++) { + http_header_t *header = &response->headers[i]; + if (header->key != NULL) { + free(header->key); + } + if (header->value != NULL) { + free(header->value); + } + } + } + if (response->body != NULL) { + free(response->body); + } +} + +http_request_t http_read_request(conn_t *conn) { + int len = 0; + int ln = 0; + int start = 0; + char buffer[10240]; + http_request_t request = { conn, NULL, NULL, NULL, NULL, 0, NULL, 0 }; + while (true) { + char c; + http_read(conn, &c, 1); + // break if too long + if (len == MAX_REQUEST_HEAD_SIZE) { + perror("request is too long"); + break; + } + buffer[len] = c; + len++; + // read head + if (len >= 2) { + char *slice = buffer + (len - 2); + if (strncmp(slice, "\r\n", 2) == 0) { + int line_len = (len - 2) - start; + if (line_len > 0) { + // read line + char line[line_len + 1]; + memcpy(line, buffer + start, line_len); + line[line_len] = '\0'; + // read method, url, and version + if (ln == 0) { + int arg = 0; + int start = 0; + for (int i = 0; i <= line_len; i++) { + if (line[i] == ' ' || i == line_len) { + // read method + if (arg == 0) { + int method_length = i - start; + request.method = malloc(sizeof(char) * (method_length + 1)); + memcpy(request.method, line + start, method_length); + request.method[method_length] = '\0'; + } + // read url + if (arg == 1) { + int url_length = i - start; + request.url = malloc(sizeof(char) * (url_length + 1)); + memcpy(request.url, line + start, url_length); + request.url[url_length] = '\0'; + } + // read version + if (arg == 2) { + int version_length = i - start; + request.version = malloc(sizeof(char) * (version_length + 1)); + memcpy(request.version, line + start, version_length); + request.version[version_length] = '\0'; + } + start = i + 1; + arg++; + } + } + } + // read headers + else { + int sep = 0; + request.headers = realloc(request.headers, sizeof(http_header_t) * (request.headers_len + 1)); + http_header_t *header = &request.headers[request.headers_len]; + request.headers_len++; + for (int i = 0; i < line_len; i++) { + if (line[i] == ':') { + int key_len = i; + header->key = malloc(sizeof(char) * (key_len + 1)); + memcpy(header->key, line, key_len); + header->key[key_len] = '\0'; + sep = i + 2; + break; + } + } + int value_len = line_len - sep; + header->value = malloc(sizeof(char) * (value_len + 1)); + memcpy(header->value, line + sep, value_len); + header->value[value_len] = '\0'; + } + } + start = len; + ln++; + } + } + // read body + if (len >= 4) { + char *slice = buffer + (len - 4); + if (strncmp(slice, "\r\n\r\n", 4) == 0) { + char *content_length = http_header_get(&request, "Content-Length"); + if (content_length != NULL) { + request.body_len = strtol(content_length, NULL, 10); + http_read(conn, request.body, request.body_len); + } + break; + } + } + } + return request; +} + +void http_write_response(conn_t *conn, http_response_t response) { + http_write(conn, response.version, strlen(response.version)); + http_write(conn, " ", 1); + char status[4]; + snprintf(status, 4, "%d", response.status); + http_write(conn, status, strlen(status)); + http_write(conn, " ", 1); + char *status_message = http_status_str(response.status); + http_write(conn, status_message, strlen(status_message)); + http_write(conn, "\r\n", 2); + if (response.headers_len > 0) { + for (int i = 0; i < response.headers_len; i++) { + http_header_t *header = &response.headers[i]; + http_write(conn, header->key, strlen(header->key)); + http_write(conn, ": ", 2); + http_write(conn, header->value, strlen(header->value)); + http_write(conn, "\r\n", 2); + } + } + http_write(conn, "\r\n", 2); + if (response.body != NULL) { + http_write(conn, response.body, response.body_len); + } +} + +void *http_handle_client(void *ptr) { + if (!ptr) { + pthread_exit(0); + } + conn_t *conn = (conn_t *) ptr; + if (http_ctx != NULL) { + conn->ssl = SSL_new(http_ctx); + SSL_set_fd(conn->ssl, conn->sockfd); + if (SSL_accept(conn->ssl) <= 0) { + perror("cannot SSL handshake"); + pthread_exit(0); + } + } + http_request_t request = http_read_request(conn); + if (request.method != NULL && request.url != NULL) { + long addr = ((struct sockaddr_in *) &request.conn->addr)->sin_addr.s_addr; + printf( + "%ld.%ld.%ld.%ld -> %s %s\n", + (addr ) & 0xff, + (addr >> 8) & 0xff, + (addr >> 16) & 0xff, + (addr >> 24) & 0xff, + request.method, + request.url + ); + if (on_request_fn != NULL) { + http_response_t response = on_request_fn(&request); + http_write_response(conn, response); + http_response_free(&response); + } + } + http_request_free(&request); + if (http_ctx != NULL) { + SSL_shutdown(conn->ssl); + SSL_free(conn->ssl); + } + close(conn->sockfd); + free(conn); + pthread_exit(0); +} + +void *http_handle_server(void *ptr) { + if (!ptr) { + pthread_exit(0); + } + int sockfd = *((int *) ptr); + struct pollfd fds[1]; + fds[0].fd = sockfd; + fds[0].events = POLLIN | POLLPRI; + while (!http_quit) { + usleep(100000); + if (poll(fds, 1, 100)) { + conn_t *conn = malloc(sizeof(conn_t)); + conn->addr_len = sizeof(conn->addr); + conn->sockfd = accept(sockfd, &conn->addr, (socklen_t *) &conn->addr_len); + conn->ssl = NULL; + if (conn->sockfd <= 0) { + free(conn); + } + else { + pthread_t thread; + pthread_create(&thread, 0, http_handle_client, (void *) conn); + pthread_detach(thread); + } + } + } + pthread_exit(0); +} + +void http_tls_enable(char *certificate, char *private_key) { + const SSL_METHOD *method = TLS_server_method(); + http_ctx = SSL_CTX_new(method); + if (!http_ctx) { + perror("cannot create SSL context"); + errno = HTTP_ERR_NO_SSL_CONTEXT; + http_error = true; + return; + } + if (SSL_CTX_use_certificate_file(http_ctx, certificate, SSL_FILETYPE_PEM) <= 0) { + perror("cannot use certificate file"); + errno = HTTP_ERR_INVALID_CERTIFICATE; + http_error = true; + } + if (SSL_CTX_use_PrivateKey_file(http_ctx, private_key, SSL_FILETYPE_PEM) <= 0) { + perror("cannot use private key file"); + errno = HTTP_ERR_INVALID_PRIVATE_KEY; + http_error = true; + } +} + +void http_listen(char *hostname, int port) { + if (http_error) { + return; + } + errno = 0; + http_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (http_sockfd <= 0) { + perror("cannot create socket"); + errno = HTTP_ERR_NO_SOCKET; + http_error = true; + return; + } + struct hostent *host = gethostbyname(hostname); + if (!host) { + perror("unknown host"); + errno = HTTP_ERR_UNKNOWN_HOST; + http_error = true; + return; + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + memcpy(&addr.sin_addr, host->h_addr_list[0], host->h_length); + addr.sin_port = htons(port); + int enable = 1; + if (setsockopt(http_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + perror("cannot reuse address"); + errno = HTTP_ERR_NO_ADDR_REUSE; + http_error = true; + return; + } + if (bind(http_sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) { + perror("cannot bind socket to port"); + errno = HTTP_ERR_ADDR_IN_USE; + http_error = true; + return; + } + if (listen(http_sockfd, 5) < 0) { + perror("cannot listen on port"); + errno = HTTP_ERR_NO_LISTEN; + http_error = true; + return; + } + pthread_create(&http_thread, 0, http_handle_server, (void *) &http_sockfd); +} + +void http_close() { + http_quit = true; + if (!http_error) { + pthread_join(http_thread, NULL); + } + close(http_sockfd); + if (http_ctx != NULL) { + SSL_CTX_free(http_ctx); + } +} diff --git a/libhttp.h b/libhttp.h @@ -0,0 +1,124 @@ +#pragma once + +#include "sys/socket.h" +#include "openssl/ssl.h" + +typedef enum { + HTTP_ERR_NO_SOCKET = 1, + HTTP_ERR_UNKNOWN_HOST = 2, + HTTP_ERR_NO_ADDR_REUSE = 3, + HTTP_ERR_ADDR_IN_USE = 4, + HTTP_ERR_NO_LISTEN = 5, + HTTP_ERR_NO_SSL_CONTEXT = 6, + HTTP_ERR_INVALID_CERTIFICATE = 7, + HTTP_ERR_INVALID_PRIVATE_KEY = 8, +} http_error_t; + +typedef enum { + HTTP_STATUS_CONTINUE = 100, + HTTP_STATUS_SWITCHING_PROTOCOLS = 101, + HTTP_STATUS_PROCESSING = 102, + HTTP_STATUS_EARLY_HINTS = 103, + HTTP_STATUS_OK = 200, + HTTP_STATUS_CREATED = 201, + HTTP_STATUS_ACCEPTED = 202, + HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + HTTP_STATUS_NO_CONTENT = 204, + HTTP_STATUS_RESET_CONTENT = 205, + HTTP_STATUS_PARTIAL_CONTENT = 206, + HTTP_STATUS_MULTI_STATUS = 207, + HTTP_STATUS_ALREADY_REPORTED = 208, + HTTP_STATUS_IM_USED = 226, + HTTP_STATUS_MULTIPLE_CHOICES = 300, + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + HTTP_STATUS_NOT_MODIFIED = 304, + HTTP_STATUS_TEMPORARY_REDIRECT = 307, + HTTP_STATUS_PERMANENT_REDIRECT = 308, + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED = 401, + HTTP_STATUS_PAYMENT_REQUIRED = 402, + HTTP_STATUS_FORBIDDEN = 403, + HTTP_STATUS_NOT_FOUND = 404, + HTTP_STATUS_METHOD_NOT_ALLOWED = 405, + HTTP_STATUS_NOT_ACCEPTABLE = 406, + HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + HTTP_STATUS_REQUEST_TIMEOUT = 408, + HTTP_STATUS_CONFLICT = 409, + HTTP_STATUS_GONE = 410, + HTTP_STATUS_LENGTH_REQUIRED = 411, + HTTP_STATUS_PRECONDITION_FAILED = 412, + HTTP_STATUS_PAYLOAD_TOO_LARGE = 413, + HTTP_STATUS_URI_TOO_LONG = 414, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, + HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416, + HTTP_STATUS_EXPECTATION_FAILED = 417, + HTTP_STATUS_I_AM_A_TEAPOT = 418, + HTTP_STATUS_MISDIRECTED_REQUEST = 421, + HTTP_STATUS_UNPROCESSABLE_CONTENT = 422, + HTTP_STATUS_LOCKED = 423, + HTTP_STATUS_FAILED_DEPENDENCY = 424, + HTTP_STATUS_TOO_EARLY = 425, + HTTP_STATUS_UPGRADE_REQUIRED = 426, + HTTP_STATUS_PRECONDITION_REQUIRED = 428, + HTTP_STATUS_TOO_MANY_REQUESTS = 429, + HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451, + HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + HTTP_STATUS_NOT_IMPLEMENTED = 501, + HTTP_STATUS_BAD_GATEWAY = 502, + HTTP_STATUS_SERVICE_UNAVAILABLE = 503, + HTTP_STATUS_GATEWAY_TIMEOUT = 504, + HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505, + HTTP_STATUS_VARIANT_ALSO_NEGOTITATES = 506, + HTTP_STATUS_INSUFFICIENT_STORAGE = 507, + HTTP_STATUS_LOOP_DETECTED = 508, + HTTP_STATUS_NOT_EXTENDED = 510, + HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511, +} http_status_t; + +typedef struct { + int sockfd; + struct sockaddr addr; + int addr_len; + SSL *ssl; +} conn_t; + +typedef struct { + char *key; + char *value; +} http_header_t; + +typedef struct { + conn_t *conn; + char *method; + char *url; + char *version; + http_header_t *headers; + int headers_len; + char *body; + long body_len; +} http_request_t; + +typedef struct { + char *version; + http_status_t status; + http_header_t *headers; + int headers_len; + char *body; + long body_len; +} http_response_t; + +void http_on_request(http_response_t fn(http_request_t *)); + +char *http_header_get(http_request_t *request, char *key); +void http_header_set(http_response_t *response, char *key, char *value); + +http_response_t http_response_create(http_status_t status); +void http_response_body(http_response_t *response, char *body, long len); +void http_response_file(http_response_t *response, char *filename); + +void http_tls_enable(char *certificate, char *private_key); +void http_listen(char *hostname, int port); +void http_close();