commit 76c0a03c0b506c8d17058f8f66219b50797d7dba
Author: typable <contact@typable.dev>
Date: Wed, 3 Apr 2024 13:51:31 +0200
Initial implementation
Diffstat:
A | Makefile | | | 18 | ++++++++++++++++++ |
A | README.md | | | 3 | +++ |
A | libhttp.c | | | 565 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | libhttp.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();