diff --git a/amalg.py b/amalg.py index 0d6afda..9a999ff 100644 --- a/amalg.py +++ b/amalg.py @@ -29,8 +29,6 @@ header = Amalgamator() header.append_text("#ifndef CWEB_AMALGAMATION\n") header.append_text("#define CWEB_AMALGAMATION\n") header.append_text(desc) -header.append_file("3p/wl.h") -header.append_file("3p/chttp.h") header.append_file("src/main.h") header.append_text("#endif // CWEB_AMALGAMATION\n") header.save("cweb.h") @@ -38,6 +36,8 @@ header.save("cweb.h") source = Amalgamator() source.append_text("#include \"cweb.h\"\n") source.append_text("#define HTTP_NOINCLUDE\n") +source.append_file("3p/wl.h") +source.append_file("3p/chttp.h") source.append_file("3p/chttp.c") source.append_file("3p/crypt_blowfish.h") source.append_file("3p/crypt_blowfish.c") diff --git a/cweb.c b/cweb.c index effbdb9..fb45766 100644 --- a/cweb.c +++ b/cweb.c @@ -1,12 +1,642 @@ #include "cweb.h" #define HTTP_NOINCLUDE +//////////////////////////////////////////////////////////////////////////////////////// +// 3p/wl.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "3p/wl.h" +#include +#include + +typedef struct WL_Runtime WL_Runtime; +typedef struct WL_Compiler WL_Compiler; + +typedef struct { + char *ptr; + int len; +} WL_String; + +typedef struct { + char *ptr; + int len; + int cur; +} WL_Arena; + +typedef struct { + char *ptr; + int len; +} WL_Program; + +typedef enum { + WL_ADD_ERROR, + WL_ADD_AGAIN, + WL_ADD_LINK, +} WL_AddResultType; + +typedef struct { + WL_AddResultType type; + WL_String path; +} WL_AddResult; + +typedef enum { + WL_EVAL_NONE, + WL_EVAL_DONE, + WL_EVAL_ERROR, + WL_EVAL_OUTPUT, + WL_EVAL_SYSVAR, + WL_EVAL_SYSCALL, +} WL_EvalResultType; + +typedef struct { + WL_EvalResultType type; + WL_String str; +} WL_EvalResult; + +WL_Compiler* wl_compiler_init (WL_Arena *arena); +WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content); +int wl_compiler_link (WL_Compiler *compiler, WL_Program *program); +WL_String wl_compiler_error (WL_Compiler *compiler); +int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap); +void wl_dump_program (WL_Program program); + +WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program); +WL_EvalResult wl_runtime_eval (WL_Runtime *rt); +WL_String wl_runtime_error (WL_Runtime *rt); +void wl_runtime_dump (WL_Runtime *rt); + +bool wl_streq (WL_String a, char *b, int blen); +int wl_arg_count (WL_Runtime *rt); +bool wl_arg_none (WL_Runtime *rt, int idx); +bool wl_arg_bool (WL_Runtime *rt, int idx, bool *x); +bool wl_arg_s64 (WL_Runtime *rt, int idx, int64_t *x); +bool wl_arg_f64 (WL_Runtime *rt, int idx, double *x); +bool wl_arg_str (WL_Runtime *rt, int idx, WL_String *x); +bool wl_arg_array (WL_Runtime *rt, int idx); +bool wl_arg_map (WL_Runtime *rt, int idx); +bool wl_peek_none (WL_Runtime *rt, int off); +bool wl_peek_bool (WL_Runtime *rt, int off, bool *x); +bool wl_peek_s64 (WL_Runtime *rt, int off, int64_t *x); +bool wl_peek_f64 (WL_Runtime *rt, int off, double *x); +bool wl_peek_str (WL_Runtime *rt, int off, WL_String *x); +bool wl_pop_any (WL_Runtime *rt); +bool wl_pop_none (WL_Runtime *rt); +bool wl_pop_bool (WL_Runtime *rt, bool *x); +bool wl_pop_s64 (WL_Runtime *rt, int64_t *x); +bool wl_pop_f64 (WL_Runtime *rt, double *x); +bool wl_pop_str (WL_Runtime *rt, WL_String *x); +void wl_push_none (WL_Runtime *rt); +void wl_push_true (WL_Runtime *rt); +void wl_push_false (WL_Runtime *rt); +void wl_push_s64 (WL_Runtime *rt, int64_t x); +void wl_push_f64 (WL_Runtime *rt, double x); +void wl_push_str (WL_Runtime *rt, WL_String x); +void wl_push_array (WL_Runtime *rt, int cap); +void wl_push_map (WL_Runtime *rt, int cap); +void wl_push_arg (WL_Runtime *rt, int idx); +void wl_insert (WL_Runtime *rt); +void wl_append (WL_Runtime *rt); + +//////////////////////////////////////////////////////////////////////////////////////// +// 3p/chttp.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "3p/chttp.h" +#ifndef HTTP_AMALGAMATION +#define HTTP_AMALGAMATION + +// This file was generated automatically. Do not modify directly! + +//////////////////////////////////////////////////////////////////////////////////////// +// src/basic.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/basic.h" +#ifndef CHTTP_BASIC_INCLUDED +#define CHTTP_BASIC_INCLUDED + +#include + +// String type used throughout cHTTP. +typedef struct { + char *ptr; + int len; +} HTTP_String; + +// Compare two strings and return true iff they have +// the same contents. +bool http_streq(HTTP_String s1, HTTP_String s2); + +// Compre two strings case-insensitively (uppercase and +// lowercase versions of a letter are considered the same) +// and return true iff they have the same contents. +bool http_streqcase(HTTP_String s1, HTTP_String s2); + +// Remove spaces and tabs from the start and the end of +// a string. This doesn't change the original string and +// the new one references the contents of the original one. +HTTP_String http_trim(HTTP_String s); + +// TODO: comment +void print_bytes(HTTP_String prefix, HTTP_String src); + +// Macro to simplify converting string literals to +// HTTP_String. +// +// Instead of doing this: +// +// char *s = "some string"; +// +// You do this: +// +// HTTP_String s = HTTP_STR("some string") +// +// This is a bit cumbersome, but better than null-terminated +// strings, having a pointer and length variable pairs whenever +// a function operates on a string. If this wasn't a library +// I would have done for +// +// #define S(X) ... +// +// But I don't want to cause collisions with user code. +#define HTTP_STR(X) ((HTTP_String) {(X), sizeof(X)-1}) + +// Returns the number of items of a static array. +#define HTTP_COUNT(X) (sizeof(X) / sizeof((X)[0])) + +// TODO: comment +#define HTTP_UNPACK(X) (X).len, (X).ptr + +// Macro used to make invariants of the code more explicit. +// +// Say you have some function that operates on two integers +// and that by design their sum is always 100. This macro is +// useful to make that explicit: +// +// void func(int a, int b) +// { +// HTTP_ASSERT(a + b == 100); +// ... +// } +// +// Assertions are about documentation, *not* error management. +// +// In non-release builds (where NDEBUG is not defined) asserted +// expressions are evaluated and if not true, the program is halted. +// This is quite nice as they offer a way to document code in +// a way that can be checked at runtime, unlike regular comments +// like this one. +#ifdef NDEBUG +#define HTTP_ASSERT(X) ((void) 0) +#else +#define HTTP_ASSERT(X) {if (!(X)) { __builtin_trap(); }} +#endif + +#endif // CHTTP_BASIC_INCLUDED + +//////////////////////////////////////////////////////////////////////////////////////// +// src/parse.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/parse.h" +#ifndef PARSE_INCLUDED +#define PARSE_INCLUDED + +#ifndef HTTP_AMALGAMATION +#include "basic.h" +#endif + +#define HTTP_MAX_HEADERS 32 + +typedef struct { + unsigned int data; +} HTTP_IPv4; + +typedef struct { + unsigned short data[8]; +} HTTP_IPv6; + +typedef enum { + HTTP_HOST_MODE_VOID = 0, + HTTP_HOST_MODE_NAME, + HTTP_HOST_MODE_IPV4, + HTTP_HOST_MODE_IPV6, +} HTTP_HostMode; + +typedef struct { + HTTP_HostMode mode; + HTTP_String text; + union { + HTTP_String name; + HTTP_IPv4 ipv4; + HTTP_IPv6 ipv6; + }; +} HTTP_Host; + +typedef struct { + HTTP_String userinfo; + HTTP_Host host; + int port; +} HTTP_Authority; + +// ZII +typedef struct { + HTTP_String scheme; + HTTP_Authority authority; + HTTP_String path; + HTTP_String query; + HTTP_String fragment; +} HTTP_URL; + +typedef enum { + HTTP_METHOD_GET, + HTTP_METHOD_HEAD, + HTTP_METHOD_POST, + HTTP_METHOD_PUT, + HTTP_METHOD_DELETE, + HTTP_METHOD_CONNECT, + HTTP_METHOD_OPTIONS, + HTTP_METHOD_TRACE, + HTTP_METHOD_PATCH, +} HTTP_Method; + +typedef struct { + HTTP_String name; + HTTP_String value; +} HTTP_Header; + +typedef struct { + bool secure; + HTTP_Method method; + HTTP_URL url; + int minor; + int num_headers; + HTTP_Header headers[HTTP_MAX_HEADERS]; + HTTP_String body; +} HTTP_Request; + +typedef struct { + void* context; + int minor; + int status; + HTTP_String reason; + int num_headers; + HTTP_Header headers[HTTP_MAX_HEADERS]; + HTTP_String body; +} HTTP_Response; + +int http_parse_ipv4 (char *src, int len, HTTP_IPv4 *ipv4); +int http_parse_ipv6 (char *src, int len, HTTP_IPv6 *ipv6); +int http_parse_url (char *src, int len, HTTP_URL *url); +int http_parse_request (char *src, int len, HTTP_Request *req); +int http_parse_response (char *src, int len, HTTP_Response *res); + +int http_find_header (HTTP_Header *headers, int num_headers, HTTP_String name); + +HTTP_String http_get_cookie (HTTP_Request *req, HTTP_String name); +HTTP_String http_get_param (HTTP_String body, HTTP_String str, char *mem, int cap); +int http_get_param_i (HTTP_String body, HTTP_String str); + + +#endif // PARSE_INCLUDED + +//////////////////////////////////////////////////////////////////////////////////////// +// src/engine.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/engine.h" +#ifndef HTTP_ENGINE_INCLUDED +#define HTTP_ENGINE_INCLUDED + +#ifndef HTTP_AMALGAMATION +#include "parse.h" +#endif + +typedef enum { + HTTP_MEMFUNC_MALLOC, + HTTP_MEMFUNC_FREE, +} HTTP_MemoryFuncTag; + +typedef void*(*HTTP_MemoryFunc)(HTTP_MemoryFuncTag tag, + void *ptr, int len, void *data); + +typedef struct { + + HTTP_MemoryFunc memfunc; + void *memfuncdata; + + unsigned long long curs; + + char* data; + unsigned int head; + unsigned int size; + unsigned int used; + unsigned int limit; + + char* read_target; + unsigned int read_target_size; + + int flags; +} HTTP_ByteQueue; + +typedef unsigned long long HTTP_ByteQueueOffset; + +#define HTTP_ENGINE_STATEBIT_CLIENT (1 << 0) +#define HTTP_ENGINE_STATEBIT_CLOSED (1 << 1) +#define HTTP_ENGINE_STATEBIT_RECV_BUF (1 << 2) +#define HTTP_ENGINE_STATEBIT_RECV_ACK (1 << 3) +#define HTTP_ENGINE_STATEBIT_SEND_BUF (1 << 4) +#define HTTP_ENGINE_STATEBIT_SEND_ACK (1 << 5) +#define HTTP_ENGINE_STATEBIT_REQUEST (1 << 6) +#define HTTP_ENGINE_STATEBIT_RESPONSE (1 << 7) +#define HTTP_ENGINE_STATEBIT_PREP (1 << 8) +#define HTTP_ENGINE_STATEBIT_PREP_HEADER (1 << 9) +#define HTTP_ENGINE_STATEBIT_PREP_BODY_BUF (1 << 10) +#define HTTP_ENGINE_STATEBIT_PREP_BODY_ACK (1 << 11) +#define HTTP_ENGINE_STATEBIT_PREP_ERROR (1 << 12) +#define HTTP_ENGINE_STATEBIT_PREP_URL (1 << 13) +#define HTTP_ENGINE_STATEBIT_PREP_STATUS (1 << 14) +#define HTTP_ENGINE_STATEBIT_CLOSING (1 << 15) + +typedef enum { + HTTP_ENGINE_STATE_NONE = 0, + HTTP_ENGINE_STATE_CLIENT_PREP_URL = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_URL, + HTTP_ENGINE_STATE_CLIENT_PREP_HEADER = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_HEADER, + HTTP_ENGINE_STATE_CLIENT_PREP_BODY_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_BUF, + HTTP_ENGINE_STATE_CLIENT_PREP_BODY_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_ACK, + HTTP_ENGINE_STATE_CLIENT_PREP_ERROR = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_ERROR, + HTTP_ENGINE_STATE_CLIENT_SEND_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_SEND_BUF, + HTTP_ENGINE_STATE_CLIENT_SEND_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_SEND_ACK, + HTTP_ENGINE_STATE_CLIENT_RECV_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RECV_BUF, + HTTP_ENGINE_STATE_CLIENT_RECV_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RECV_ACK, + HTTP_ENGINE_STATE_CLIENT_READY = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RESPONSE, + HTTP_ENGINE_STATE_CLIENT_CLOSED = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_CLOSED, + HTTP_ENGINE_STATE_SERVER_RECV_BUF = HTTP_ENGINE_STATEBIT_RECV_BUF, + HTTP_ENGINE_STATE_SERVER_RECV_ACK = HTTP_ENGINE_STATEBIT_RECV_ACK, + HTTP_ENGINE_STATE_SERVER_PREP_STATUS = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_STATUS, + HTTP_ENGINE_STATE_SERVER_PREP_HEADER = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_HEADER, + HTTP_ENGINE_STATE_SERVER_PREP_BODY_BUF = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_BUF, + HTTP_ENGINE_STATE_SERVER_PREP_BODY_ACK = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_ACK, + HTTP_ENGINE_STATE_SERVER_PREP_ERROR = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_ERROR, + HTTP_ENGINE_STATE_SERVER_SEND_BUF = HTTP_ENGINE_STATEBIT_SEND_BUF, + HTTP_ENGINE_STATE_SERVER_SEND_ACK = HTTP_ENGINE_STATEBIT_SEND_ACK, + HTTP_ENGINE_STATE_SERVER_CLOSED = HTTP_ENGINE_STATEBIT_CLIENT, +} HTTP_EngineState; + +typedef struct { + HTTP_EngineState state; + HTTP_ByteQueue input; + HTTP_ByteQueue output; + int numexch; + int reqsize; + int closing; + int keepalive; + HTTP_ByteQueueOffset response_offset; + HTTP_ByteQueueOffset content_length_offset; + HTTP_ByteQueueOffset content_length_value_offset; + union { + HTTP_Request req; + HTTP_Response res; + } result; +} HTTP_Engine; + +void http_engine_init (HTTP_Engine *eng, int client, HTTP_MemoryFunc memfunc, void *memfuncdata); +void http_engine_free (HTTP_Engine *eng); + +void http_engine_close (HTTP_Engine *eng); +HTTP_EngineState http_engine_state (HTTP_Engine *eng); + +const char* http_engine_statestr(HTTP_EngineState state); // TODO: remove + +char* http_engine_recvbuf (HTTP_Engine *eng, int *cap); +void http_engine_recvack (HTTP_Engine *eng, int num); +char* http_engine_sendbuf (HTTP_Engine *eng, int *len); +void http_engine_sendack (HTTP_Engine *eng, int num); + +HTTP_Request* http_engine_getreq (HTTP_Engine *eng); +HTTP_Response* http_engine_getres (HTTP_Engine *eng); + +void http_engine_url (HTTP_Engine *eng, HTTP_Method method, HTTP_String url, int minor); +void http_engine_status (HTTP_Engine *eng, int status); +void http_engine_header (HTTP_Engine *eng, HTTP_String str); +void http_engine_body (HTTP_Engine *eng, HTTP_String str); +void http_engine_bodycap (HTTP_Engine *eng, int mincap); +char* http_engine_bodybuf (HTTP_Engine *eng, int *cap); +void http_engine_bodyack (HTTP_Engine *eng, int num); +void http_engine_done (HTTP_Engine *eng); +void http_engine_undo (HTTP_Engine *eng); + +#endif // HTTP_ENGINE_INCLUDED + +//////////////////////////////////////////////////////////////////////////////////////// +// src/cert.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/cert.h" +#ifndef CERT_INCLUDED +#define CERT_INCLUDED + +#ifndef HTTP_AMALGAMATION +#include "basic.h" +#endif + +// This is an utility to create self-signed certificates +// useful when testing HTTPS servers locally. This is only +// meant to be used by people starting out with a library +// and simplifying the zero to one phase. +// +// The C, O, and CN are respectively country name, organization name, +// and common name of the certificate. For instance: +// +// C="IT" +// O="My Organization" +// CN="my_website.com" +// +// The output is a certificate file in PEM format and a private +// key file with the key used to sign the certificate. +int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, + HTTP_String cert_file, HTTP_String key_file); + +#endif // CERT_INCLUDED + +//////////////////////////////////////////////////////////////////////////////////////// +// src/client.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/client.h" +#ifndef CLIENT_INCLUDED +#define CLIENT_INCLUDED + +#include + +#ifndef HTTP_AMALGAMATION +#include "parse.h" +#endif + +// Initialize the global state of cHTTP. +// +// cHTTP tries to avoid global state. What this function +// does is call the global initialization functions of +// its dependencies (OpenSSL and Winsock) +int http_global_init(void); + +// Free the global state of cHTTP. +void http_global_free(void); + +// Opaque type describing an "HTTP client". Any request +// that is started must always be associated to an HTTP +// client object. +typedef struct HTTP_Client HTTP_Client; + +// Handle for a pending request. This should be considered +// opaque. Don't read or modify its fields! +typedef struct { + void *data0; + int data1; + int data2; +} HTTP_RequestBuilder; + +// Initialize a client object. If something goes wrong, +// NULL is returned. +HTTP_Client *http_client_init(void); + +// Deinitialize a client object +void http_client_free(HTTP_Client *client); + +// Create a request object associated to the given client. +// On success, 0 is returned and the handle is initialized. +// On error, -1 is returned. +int http_client_get_builder(HTTP_Client *client, HTTP_RequestBuilder *builder); + +void http_request_builder_user_data(HTTP_RequestBuilder builder, void *user_data); + +// Enable/disable I/O tracing for the specified request. +// This must be done when the request is in the initialization +// phase. +void http_request_builder_trace(HTTP_RequestBuilder builder, bool trace); + +// Set the method and URL of the specified request object. +// This must be the first thing you do after http_client_request +// is called (you may http_request_trace before, but nothing +// else!) +void http_request_builder_line(HTTP_RequestBuilder builder, HTTP_Method method, HTTP_String url); + +// Append a header to the specified request. You must call +// this after http_request_line and may do so multiple times. +void http_request_builder_header(HTTP_RequestBuilder builder, HTTP_String str); + +// Append some data to the request's body. You must call +// this after either http_request_line or http_request_header. +void http_request_builder_body(HTTP_RequestBuilder builder, HTTP_String str); + +// Mark the initialization of the request as completed and +// perform the request. +void http_request_builder_submit(HTTP_RequestBuilder builder); + +// Free resources associated to a request. This must be called +// after the request has completed. +// +// TODO: allow aborting pending requests +void http_response_free(HTTP_Response *res); + +// Wait for the completion of one request associated to +// the client. The handle of the resolved request is returned +// through the handle output parameter. If you're not +// interested in which request completed (like when you +// have only one pending request), you can pass NULL. +// +// On error -1 is retutned, else 0 is returned and the +// handle is initialized. +// +// Note that calling this function when no requests are +// pending is considered an error. +int http_client_wait(HTTP_Client *client, HTTP_Response **res, void **user_data); + +// TODO: comment +HTTP_Response *http_get(HTTP_String url, + HTTP_String *headers, int num_headers); + +// TODO: comment +HTTP_Response *http_post(HTTP_String url, + HTTP_String *headers, int num_headers, + HTTP_String body); + +#endif // CLIENT_INCLUDED + +//////////////////////////////////////////////////////////////////////////////////////// +// src/server.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/server.h" +#ifndef HTTP_SERVER_INCLUDED +#define HTTP_SERVER_INCLUDED + +#include + +#ifndef HTTP_AMALGAMATION +#include "parse.h" +#endif + +typedef struct { + void *data0; + int data1; + int data2; +} HTTP_ResponseBuilder; + +typedef struct HTTP_Server HTTP_Server; + +HTTP_Server *http_server_init(HTTP_String addr, uint16_t port); + +HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port, + uint16_t secure_port, HTTP_String cert_key, HTTP_String private_key); + +void http_server_free (HTTP_Server *server); +int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseBuilder *handle); +int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); +void http_response_builder_status (HTTP_ResponseBuilder res, int status); +void http_response_builder_header (HTTP_ResponseBuilder res, HTTP_String str); +void http_response_builder_body (HTTP_ResponseBuilder res, HTTP_String str); +void http_response_builder_bodycap (HTTP_ResponseBuilder res, int mincap); +char* http_response_builder_bodybuf (HTTP_ResponseBuilder res, int *cap); +void http_response_builder_bodyack (HTTP_ResponseBuilder res, int num); +void http_response_builder_undo (HTTP_ResponseBuilder res); +void http_response_builder_done (HTTP_ResponseBuilder res); + +#endif // HTTP_SERVER_INCLUDED + +//////////////////////////////////////////////////////////////////////////////////////// +// src/router.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/router.h" +#ifndef HTTP_ROUTER_INCLUDED +#define HTTP_ROUTER_INCLUDED + +#ifndef HTTP_AMALGAMATION +#include "server.h" +#endif + +typedef struct HTTP_Router HTTP_Router; +typedef void (*HTTP_RouterFunc)(HTTP_Request*, HTTP_ResponseBuilder, void*);; + +HTTP_Router* http_router_init (void); +void http_router_free (HTTP_Router *router); +void http_router_resolve (HTTP_Router *router, HTTP_Request *req, HTTP_ResponseBuilder res); +void http_router_dir (HTTP_Router *router, HTTP_String endpoint, HTTP_String path); +void http_router_func (HTTP_Router *router, HTTP_Method method, HTTP_String endpoint, HTTP_RouterFunc func, void*); +int http_serve (char *addr, int port, HTTP_Router *router); + +#endif // HTTP_ROUTER_INCLUDED +#endif // HTTP_AMALGAMATION + //////////////////////////////////////////////////////////////////////////////////////// // 3p/chttp.c //////////////////////////////////////////////////////////////////////////////////////// #line 1 "3p/chttp.c" +#ifndef HTTP_NOINCLUDE #include "chttp.h" +#endif //////////////////////////////////////////////////////////////////////////////////////// // src/sec.h @@ -6515,102 +7145,6 @@ char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count, return output; } -//////////////////////////////////////////////////////////////////////////////////////// -// 3p/wl.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "3p/wl.h" -#include -#include - -typedef struct WL_Runtime WL_Runtime; -typedef struct WL_Compiler WL_Compiler; - -typedef struct { - char *ptr; - int len; -} WL_String; - -typedef struct { - char *ptr; - int len; - int cur; -} WL_Arena; - -typedef struct { - char *ptr; - int len; -} WL_Program; - -typedef enum { - WL_ADD_ERROR, - WL_ADD_AGAIN, - WL_ADD_LINK, -} WL_AddResultType; - -typedef struct { - WL_AddResultType type; - WL_String path; -} WL_AddResult; - -typedef enum { - WL_EVAL_NONE, - WL_EVAL_DONE, - WL_EVAL_ERROR, - WL_EVAL_OUTPUT, - WL_EVAL_SYSVAR, - WL_EVAL_SYSCALL, -} WL_EvalResultType; - -typedef struct { - WL_EvalResultType type; - WL_String str; -} WL_EvalResult; - -WL_Compiler* wl_compiler_init (WL_Arena *arena); -WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content); -int wl_compiler_link (WL_Compiler *compiler, WL_Program *program); -WL_String wl_compiler_error (WL_Compiler *compiler); -int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap); -void wl_dump_program (WL_Program program); - -WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program); -WL_EvalResult wl_runtime_eval (WL_Runtime *rt); -WL_String wl_runtime_error (WL_Runtime *rt); -void wl_runtime_dump (WL_Runtime *rt); - -bool wl_streq (WL_String a, char *b, int blen); -int wl_arg_count (WL_Runtime *rt); -bool wl_arg_none (WL_Runtime *rt, int idx); -bool wl_arg_bool (WL_Runtime *rt, int idx, bool *x); -bool wl_arg_s64 (WL_Runtime *rt, int idx, int64_t *x); -bool wl_arg_f64 (WL_Runtime *rt, int idx, double *x); -bool wl_arg_str (WL_Runtime *rt, int idx, WL_String *x); -bool wl_arg_array (WL_Runtime *rt, int idx); -bool wl_arg_map (WL_Runtime *rt, int idx); -bool wl_peek_none (WL_Runtime *rt, int off); -bool wl_peek_bool (WL_Runtime *rt, int off, bool *x); -bool wl_peek_s64 (WL_Runtime *rt, int off, int64_t *x); -bool wl_peek_f64 (WL_Runtime *rt, int off, double *x); -bool wl_peek_str (WL_Runtime *rt, int off, WL_String *x); -bool wl_pop_any (WL_Runtime *rt); -bool wl_pop_none (WL_Runtime *rt); -bool wl_pop_bool (WL_Runtime *rt, bool *x); -bool wl_pop_s64 (WL_Runtime *rt, int64_t *x); -bool wl_pop_f64 (WL_Runtime *rt, double *x); -bool wl_pop_str (WL_Runtime *rt, WL_String *x); -void wl_push_none (WL_Runtime *rt); -void wl_push_true (WL_Runtime *rt); -void wl_push_false (WL_Runtime *rt); -void wl_push_s64 (WL_Runtime *rt, int64_t x); -void wl_push_f64 (WL_Runtime *rt, double x); -void wl_push_str (WL_Runtime *rt, WL_String x); -void wl_push_array (WL_Runtime *rt, int cap); -void wl_push_map (WL_Runtime *rt, int cap); -void wl_push_arg (WL_Runtime *rt, int idx); -void wl_insert (WL_Runtime *rt); -void wl_append (WL_Runtime *rt); - //////////////////////////////////////////////////////////////////////////////////////// // 3p/wl.c //////////////////////////////////////////////////////////////////////////////////////// @@ -12453,6 +12987,8 @@ void wl_runtime_dump(WL_Runtime *rt) #line 1 "src/main.c" #include "sqlite3.h" +#include "wl.h" +#include "chttp.h" #include "cweb.h" #ifdef __linux__ @@ -12509,9 +13045,27 @@ struct CWEB { SQLiteCache *dbcache; // Template - TemplateCache *tcpcache; + TemplateCache *tpcache; bool allow_insecure_login; + + CWEB_Request req; +}; + +struct CWEB_Request { + + CWEB *cweb; + + WL_Arena arena; + + HTTP_Request *req; + HTTP_ResponseBuilder builder; + + // Session + bool just_created_session; + int user_id; + CWEB_String sess; + CWEB_String csrf; }; /////////////////////////// @@ -12722,7 +13276,7 @@ static void session_storage_free(SessionStorage *storage) free(storage); } -static Session *lookup_session_slot(SessionStorage *storage, HTTP_String sess, bool find_unused) +static Session *lookup_session_slot(SessionStorage *storage, CWEB_String sess, bool find_unused) { if (find_unused && 2 * storage->count + 2 > storage->capacity) return NULL; @@ -12775,7 +13329,7 @@ static Session *lookup_session_slot(SessionStorage *storage, HTTP_String sess, b return NULL; } -static int create_session(SessionStorage *storage, int user, HTTP_String *psess, HTTP_String *pcsrf) +static int create_session(SessionStorage *storage, int user, CWEB_String *psess, CWEB_String *pcsrf) { int ret; char raw_sess[SESS_RAW_TOKEN_SIZE]; @@ -12792,21 +13346,21 @@ static int create_session(SessionStorage *storage, int user, HTTP_String *psess, unpack_token(raw_sess, SESS_RAW_TOKEN_SIZE, sess, SESS_TOKEN_SIZE); unpack_token(raw_csrf, CSRF_RAW_TOKEN_SIZE, csrf, CSRF_TOKEN_SIZE); - Session *found = lookup_session_slot(storage, (HTTP_String) { sess, SESS_TOKEN_SIZE }, true); + Session *found = lookup_session_slot(storage, (CWEB_String) { sess, SESS_TOKEN_SIZE }, true); if (found == NULL) return -1; found->user = user; memcpy(found->sess, sess, SESS_TOKEN_SIZE); memcpy(found->csrf, csrf, CSRF_TOKEN_SIZE); - *psess = (HTTP_String) { found->sess, SESS_TOKEN_SIZE }; - *pcsrf = (HTTP_String) { found->csrf, CSRF_TOKEN_SIZE }; + *psess = (CWEB_String) { found->sess, SESS_TOKEN_SIZE }; + *pcsrf = (CWEB_String) { found->csrf, CSRF_TOKEN_SIZE }; storage->count++; return 0; } -static int delete_session(SessionStorage *storage, HTTP_String sess) +static int delete_session(SessionStorage *storage, CWEB_String sess) { char raw_sess[SESS_RAW_TOKEN_SIZE]; if (sess.len != SESS_TOKEN_SIZE || pack_token(sess.ptr, sess.len, raw_sess, (int) sizeof(raw_sess)) < 0) @@ -12820,13 +13374,13 @@ static int delete_session(SessionStorage *storage, HTTP_String sess) return 0; } -static int find_session(SessionStorage *storage, HTTP_String sess, HTTP_String *pcsrf, int *puser) +static int find_session(SessionStorage *storage, CWEB_String sess, CWEB_String *pcsrf, int *puser) { Session *found = lookup_session_slot(storage, sess, false); if (found == NULL) return -1; assert(found->user >= 0); - *pcsrf = (HTTP_String) { found->csrf, CSRF_TOKEN_SIZE }; + *pcsrf = (CWEB_String) { found->csrf, CSRF_TOKEN_SIZE }; *puser = found->user; return 0; } @@ -12947,7 +13501,7 @@ static int sqlite3utils_prepare(SQLiteCache *cache, sqlite3_stmt **pstmt, char * } static int sqlite3utils_prepare_and_bind_impl(SQLiteCache *cache, - sqlite3_stmt **pstmt, char *fmt, VArgs args) + sqlite3_stmt **pstmt, char *fmt, CWEB_VArgs args) { sqlite3_stmt *stmt; int ret = sqlite3utils_prepare(cache, &stmt, fmt, strlen(fmt)); @@ -12987,7 +13541,7 @@ static int sqlite3utils_prepare_and_bind_impl(SQLiteCache *cache, return SQLITE_OK; } -int64_t cweb_database_insert_impl(CWEB *cweb, const char *fmt, VArgs args) +int64_t cweb_database_insert_impl(CWEB *cweb, const char *fmt, CWEB_VArgs args) { sqlite3_stmt *stmt; int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args); @@ -13092,7 +13646,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args) case CWEB_VARG_TYPE_PSTR: { - *args.ptr[i].pstr = (HTTP_String) { + *args.ptr[i].pstr = (CWEB_String) { sqlite3_column_text(res->handle, i), sqlite3_column_bytes(res->handle, i), }; @@ -13127,7 +13681,7 @@ void cweb_global_free(void) http_global_free(); } -int cweb_init(CWEB *cweb, CWEB_String addr, uint16_t port) +CWEB *web_init(CWEB_String addr, uint16_t port) { // If set, allows logins and signups over HTTP, which is highly insecure. // This allows compiling the application without TLS when developing. @@ -13136,29 +13690,38 @@ int cweb_init(CWEB *cweb, CWEB_String addr, uint16_t port) if (allow_insecure_login) printf("WARNING: allow_insecure_login is true\n"); + CWEB *cweb = malloc(sizeof(CWEB)); + if (cweb == NULL) + return -1; + cweb->pool_cap = 1<<20; cweb->pool = malloc(cweb->pool_cap); - if (cweb->pool == NULL) + if (cweb->pool == NULL) { + free(cweb); return -1; + } cweb->tpcache = template_cache_init(4); if (cweb->tpcache == NULL) { free(cweb->pool); + free(cweb); return -1; } cweb->session_storage = session_storage_init(1024); if (cweb->session_storage == NULL) { - template_cache_free(cweb->tcpcache); + template_cache_free(cweb->tpcache); free(cweb->pool); + free(cweb); return -1; } - cweb->server = http_server_init(addr, port); + cweb->server = http_server_init((HTTP_String) { addr.ptr, addr.len }, port); if (cweb->server == NULL) { session_storage_free(cweb->session_storage); - template_cache_free(cweb->tcpcache); + template_cache_free(cweb->tpcache); free(cweb->pool); + free(cweb); return -1; } @@ -13179,6 +13742,7 @@ void cweb_free(CWEB *cweb) sqlite_cache_free(cweb->dbcache); sqlite3_close(cweb->db); } + free(cweb); } void cweb_version(void) @@ -13232,25 +13796,21 @@ int cweb_enable_database(CWEB *cweb, CWEB_String file) return 0; } -CWEB_Request cweb_wait(CWEB *cweb) +CWEB_Request *cweb_wait(CWEB *cweb) { - CWEB_Request req; + CWEB_Request *req = &cweb->req; - int ret = http_server_wait(cweb->server, &req.req, &req.builder); + int ret = http_server_wait(cweb->server, &req->req, &req->builder); if (ret < 0) return -1; - req.arena = (WL_Arena) { cweb->pool, cweb->pool_cap, 0 }; + req->arena = (WL_Arena) { cweb->pool, cweb->pool_cap, 0 }; - int user_id; - HTTP_String sess; - HTTP_String csrf; - - req.just_created_session = false; - req.sess = http_getcookie(req.req, HTTP_STR("sess_token")); - if (find_session(cweb->session_storage, sess, &csrf, &user_id) < 0) { - req.user_id = -1; - req.sess = (HTTP_String) { NULL, 0 }; - req.csrf = (HTTP_String) { NULL, 0 }; + req->just_created_session = false; + req->sess = http_get_cookie(req->req, HTTP_STR("sess_token")); + if (find_session(cweb->session_storage, req->sess, &req->csrf, &req->user_id) < 0) { + req->user_id = -1; + req->sess = (CWEB_String) { NULL, 0 }; + req->csrf = (CWEB_String) { NULL, 0 }; } return req; diff --git a/cweb.h b/cweb.h index e0cb4d3..e25a7a0 100644 --- a/cweb.h +++ b/cweb.h @@ -3,545 +3,15 @@ // This file was generated automatically. Do not modify directly! -//////////////////////////////////////////////////////////////////////////////////////// -// 3p/chttp.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "3p/chttp.h" -#ifndef HTTP_AMALGAMATION -#define HTTP_AMALGAMATION - -// This file was generated automatically. Do not modify directly! - -//////////////////////////////////////////////////////////////////////////////////////// -// src/basic.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "src/basic.h" -#ifndef CHTTP_BASIC_INCLUDED -#define CHTTP_BASIC_INCLUDED - -#include - -// String type used throughout cHTTP. -typedef struct { - char *ptr; - int len; -} HTTP_String; - -// Compare two strings and return true iff they have -// the same contents. -bool http_streq(HTTP_String s1, HTTP_String s2); - -// Compre two strings case-insensitively (uppercase and -// lowercase versions of a letter are considered the same) -// and return true iff they have the same contents. -bool http_streqcase(HTTP_String s1, HTTP_String s2); - -// Remove spaces and tabs from the start and the end of -// a string. This doesn't change the original string and -// the new one references the contents of the original one. -HTTP_String http_trim(HTTP_String s); - -// TODO: comment -void print_bytes(HTTP_String prefix, HTTP_String src); - -// Macro to simplify converting string literals to -// HTTP_String. -// -// Instead of doing this: -// -// char *s = "some string"; -// -// You do this: -// -// HTTP_String s = HTTP_STR("some string") -// -// This is a bit cumbersome, but better than null-terminated -// strings, having a pointer and length variable pairs whenever -// a function operates on a string. If this wasn't a library -// I would have done for -// -// #define S(X) ... -// -// But I don't want to cause collisions with user code. -#define HTTP_STR(X) ((HTTP_String) {(X), sizeof(X)-1}) - -// Returns the number of items of a static array. -#define HTTP_COUNT(X) (sizeof(X) / sizeof((X)[0])) - -// TODO: comment -#define HTTP_UNPACK(X) (X).len, (X).ptr - -// Macro used to make invariants of the code more explicit. -// -// Say you have some function that operates on two integers -// and that by design their sum is always 100. This macro is -// useful to make that explicit: -// -// void func(int a, int b) -// { -// HTTP_ASSERT(a + b == 100); -// ... -// } -// -// Assertions are about documentation, *not* error management. -// -// In non-release builds (where NDEBUG is not defined) asserted -// expressions are evaluated and if not true, the program is halted. -// This is quite nice as they offer a way to document code in -// a way that can be checked at runtime, unlike regular comments -// like this one. -#ifdef NDEBUG -#define HTTP_ASSERT(X) ((void) 0) -#else -#define HTTP_ASSERT(X) {if (!(X)) { __builtin_trap(); }} -#endif - -#endif // CHTTP_BASIC_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/parse.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "src/parse.h" -#ifndef PARSE_INCLUDED -#define PARSE_INCLUDED - -#ifndef HTTP_AMALGAMATION -#include "basic.h" -#endif - -#define HTTP_MAX_HEADERS 32 - -typedef struct { - unsigned int data; -} HTTP_IPv4; - -typedef struct { - unsigned short data[8]; -} HTTP_IPv6; - -typedef enum { - HTTP_HOST_MODE_VOID = 0, - HTTP_HOST_MODE_NAME, - HTTP_HOST_MODE_IPV4, - HTTP_HOST_MODE_IPV6, -} HTTP_HostMode; - -typedef struct { - HTTP_HostMode mode; - HTTP_String text; - union { - HTTP_String name; - HTTP_IPv4 ipv4; - HTTP_IPv6 ipv6; - }; -} HTTP_Host; - -typedef struct { - HTTP_String userinfo; - HTTP_Host host; - int port; -} HTTP_Authority; - -// ZII -typedef struct { - HTTP_String scheme; - HTTP_Authority authority; - HTTP_String path; - HTTP_String query; - HTTP_String fragment; -} HTTP_URL; - -typedef enum { - HTTP_METHOD_GET, - HTTP_METHOD_HEAD, - HTTP_METHOD_POST, - HTTP_METHOD_PUT, - HTTP_METHOD_DELETE, - HTTP_METHOD_CONNECT, - HTTP_METHOD_OPTIONS, - HTTP_METHOD_TRACE, - HTTP_METHOD_PATCH, -} HTTP_Method; - -typedef struct { - HTTP_String name; - HTTP_String value; -} HTTP_Header; - -typedef struct { - bool secure; - HTTP_Method method; - HTTP_URL url; - int minor; - int num_headers; - HTTP_Header headers[HTTP_MAX_HEADERS]; - HTTP_String body; -} HTTP_Request; - -typedef struct { - void* context; - int minor; - int status; - HTTP_String reason; - int num_headers; - HTTP_Header headers[HTTP_MAX_HEADERS]; - HTTP_String body; -} HTTP_Response; - -int http_parse_ipv4 (char *src, int len, HTTP_IPv4 *ipv4); -int http_parse_ipv6 (char *src, int len, HTTP_IPv6 *ipv6); -int http_parse_url (char *src, int len, HTTP_URL *url); -int http_parse_request (char *src, int len, HTTP_Request *req); -int http_parse_response (char *src, int len, HTTP_Response *res); - -int http_find_header (HTTP_Header *headers, int num_headers, HTTP_String name); - -HTTP_String http_get_cookie (HTTP_Request *req, HTTP_String name); -HTTP_String http_get_param (HTTP_String body, HTTP_String str, char *mem, int cap); -int http_get_param_i (HTTP_String body, HTTP_String str); - - -#endif // PARSE_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/engine.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "src/engine.h" -#ifndef HTTP_ENGINE_INCLUDED -#define HTTP_ENGINE_INCLUDED - -#ifndef HTTP_AMALGAMATION -#include "parse.h" -#endif - -typedef enum { - HTTP_MEMFUNC_MALLOC, - HTTP_MEMFUNC_FREE, -} HTTP_MemoryFuncTag; - -typedef void*(*HTTP_MemoryFunc)(HTTP_MemoryFuncTag tag, - void *ptr, int len, void *data); - -typedef struct { - - HTTP_MemoryFunc memfunc; - void *memfuncdata; - - unsigned long long curs; - - char* data; - unsigned int head; - unsigned int size; - unsigned int used; - unsigned int limit; - - char* read_target; - unsigned int read_target_size; - - int flags; -} HTTP_ByteQueue; - -typedef unsigned long long HTTP_ByteQueueOffset; - -#define HTTP_ENGINE_STATEBIT_CLIENT (1 << 0) -#define HTTP_ENGINE_STATEBIT_CLOSED (1 << 1) -#define HTTP_ENGINE_STATEBIT_RECV_BUF (1 << 2) -#define HTTP_ENGINE_STATEBIT_RECV_ACK (1 << 3) -#define HTTP_ENGINE_STATEBIT_SEND_BUF (1 << 4) -#define HTTP_ENGINE_STATEBIT_SEND_ACK (1 << 5) -#define HTTP_ENGINE_STATEBIT_REQUEST (1 << 6) -#define HTTP_ENGINE_STATEBIT_RESPONSE (1 << 7) -#define HTTP_ENGINE_STATEBIT_PREP (1 << 8) -#define HTTP_ENGINE_STATEBIT_PREP_HEADER (1 << 9) -#define HTTP_ENGINE_STATEBIT_PREP_BODY_BUF (1 << 10) -#define HTTP_ENGINE_STATEBIT_PREP_BODY_ACK (1 << 11) -#define HTTP_ENGINE_STATEBIT_PREP_ERROR (1 << 12) -#define HTTP_ENGINE_STATEBIT_PREP_URL (1 << 13) -#define HTTP_ENGINE_STATEBIT_PREP_STATUS (1 << 14) -#define HTTP_ENGINE_STATEBIT_CLOSING (1 << 15) - -typedef enum { - HTTP_ENGINE_STATE_NONE = 0, - HTTP_ENGINE_STATE_CLIENT_PREP_URL = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_URL, - HTTP_ENGINE_STATE_CLIENT_PREP_HEADER = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_HEADER, - HTTP_ENGINE_STATE_CLIENT_PREP_BODY_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_BUF, - HTTP_ENGINE_STATE_CLIENT_PREP_BODY_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_ACK, - HTTP_ENGINE_STATE_CLIENT_PREP_ERROR = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_ERROR, - HTTP_ENGINE_STATE_CLIENT_SEND_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_SEND_BUF, - HTTP_ENGINE_STATE_CLIENT_SEND_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_SEND_ACK, - HTTP_ENGINE_STATE_CLIENT_RECV_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RECV_BUF, - HTTP_ENGINE_STATE_CLIENT_RECV_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RECV_ACK, - HTTP_ENGINE_STATE_CLIENT_READY = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RESPONSE, - HTTP_ENGINE_STATE_CLIENT_CLOSED = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_CLOSED, - HTTP_ENGINE_STATE_SERVER_RECV_BUF = HTTP_ENGINE_STATEBIT_RECV_BUF, - HTTP_ENGINE_STATE_SERVER_RECV_ACK = HTTP_ENGINE_STATEBIT_RECV_ACK, - HTTP_ENGINE_STATE_SERVER_PREP_STATUS = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_STATUS, - HTTP_ENGINE_STATE_SERVER_PREP_HEADER = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_HEADER, - HTTP_ENGINE_STATE_SERVER_PREP_BODY_BUF = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_BUF, - HTTP_ENGINE_STATE_SERVER_PREP_BODY_ACK = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_ACK, - HTTP_ENGINE_STATE_SERVER_PREP_ERROR = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_ERROR, - HTTP_ENGINE_STATE_SERVER_SEND_BUF = HTTP_ENGINE_STATEBIT_SEND_BUF, - HTTP_ENGINE_STATE_SERVER_SEND_ACK = HTTP_ENGINE_STATEBIT_SEND_ACK, - HTTP_ENGINE_STATE_SERVER_CLOSED = HTTP_ENGINE_STATEBIT_CLIENT, -} HTTP_EngineState; - -typedef struct { - HTTP_EngineState state; - HTTP_ByteQueue input; - HTTP_ByteQueue output; - int numexch; - int reqsize; - int closing; - int keepalive; - HTTP_ByteQueueOffset response_offset; - HTTP_ByteQueueOffset content_length_offset; - HTTP_ByteQueueOffset content_length_value_offset; - union { - HTTP_Request req; - HTTP_Response res; - } result; -} HTTP_Engine; - -void http_engine_init (HTTP_Engine *eng, int client, HTTP_MemoryFunc memfunc, void *memfuncdata); -void http_engine_free (HTTP_Engine *eng); - -void http_engine_close (HTTP_Engine *eng); -HTTP_EngineState http_engine_state (HTTP_Engine *eng); - -const char* http_engine_statestr(HTTP_EngineState state); // TODO: remove - -char* http_engine_recvbuf (HTTP_Engine *eng, int *cap); -void http_engine_recvack (HTTP_Engine *eng, int num); -char* http_engine_sendbuf (HTTP_Engine *eng, int *len); -void http_engine_sendack (HTTP_Engine *eng, int num); - -HTTP_Request* http_engine_getreq (HTTP_Engine *eng); -HTTP_Response* http_engine_getres (HTTP_Engine *eng); - -void http_engine_url (HTTP_Engine *eng, HTTP_Method method, HTTP_String url, int minor); -void http_engine_status (HTTP_Engine *eng, int status); -void http_engine_header (HTTP_Engine *eng, HTTP_String str); -void http_engine_body (HTTP_Engine *eng, HTTP_String str); -void http_engine_bodycap (HTTP_Engine *eng, int mincap); -char* http_engine_bodybuf (HTTP_Engine *eng, int *cap); -void http_engine_bodyack (HTTP_Engine *eng, int num); -void http_engine_done (HTTP_Engine *eng); -void http_engine_undo (HTTP_Engine *eng); - -#endif // HTTP_ENGINE_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/cert.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "src/cert.h" -#ifndef CERT_INCLUDED -#define CERT_INCLUDED - -#ifndef HTTP_AMALGAMATION -#include "basic.h" -#endif - -// This is an utility to create self-signed certificates -// useful when testing HTTPS servers locally. This is only -// meant to be used by people starting out with a library -// and simplifying the zero to one phase. -// -// The C, O, and CN are respectively country name, organization name, -// and common name of the certificate. For instance: -// -// C="IT" -// O="My Organization" -// CN="my_website.com" -// -// The output is a certificate file in PEM format and a private -// key file with the key used to sign the certificate. -int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN, - HTTP_String cert_file, HTTP_String key_file); - -#endif // CERT_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/client.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "src/client.h" -#ifndef CLIENT_INCLUDED -#define CLIENT_INCLUDED - -#include - -#ifndef HTTP_AMALGAMATION -#include "parse.h" -#endif - -// Initialize the global state of cHTTP. -// -// cHTTP tries to avoid global state. What this function -// does is call the global initialization functions of -// its dependencies (OpenSSL and Winsock) -int http_global_init(void); - -// Free the global state of cHTTP. -void http_global_free(void); - -// Opaque type describing an "HTTP client". Any request -// that is started must always be associated to an HTTP -// client object. -typedef struct HTTP_Client HTTP_Client; - -// Handle for a pending request. This should be considered -// opaque. Don't read or modify its fields! -typedef struct { - void *data0; - int data1; - int data2; -} HTTP_RequestBuilder; - -// Initialize a client object. If something goes wrong, -// NULL is returned. -HTTP_Client *http_client_init(void); - -// Deinitialize a client object -void http_client_free(HTTP_Client *client); - -// Create a request object associated to the given client. -// On success, 0 is returned and the handle is initialized. -// On error, -1 is returned. -int http_client_get_builder(HTTP_Client *client, HTTP_RequestBuilder *builder); - -void http_request_builder_user_data(HTTP_RequestBuilder builder, void *user_data); - -// Enable/disable I/O tracing for the specified request. -// This must be done when the request is in the initialization -// phase. -void http_request_builder_trace(HTTP_RequestBuilder builder, bool trace); - -// Set the method and URL of the specified request object. -// This must be the first thing you do after http_client_request -// is called (you may http_request_trace before, but nothing -// else!) -void http_request_builder_line(HTTP_RequestBuilder builder, HTTP_Method method, HTTP_String url); - -// Append a header to the specified request. You must call -// this after http_request_line and may do so multiple times. -void http_request_builder_header(HTTP_RequestBuilder builder, HTTP_String str); - -// Append some data to the request's body. You must call -// this after either http_request_line or http_request_header. -void http_request_builder_body(HTTP_RequestBuilder builder, HTTP_String str); - -// Mark the initialization of the request as completed and -// perform the request. -void http_request_builder_submit(HTTP_RequestBuilder builder); - -// Free resources associated to a request. This must be called -// after the request has completed. -// -// TODO: allow aborting pending requests -void http_response_free(HTTP_Response *res); - -// Wait for the completion of one request associated to -// the client. The handle of the resolved request is returned -// through the handle output parameter. If you're not -// interested in which request completed (like when you -// have only one pending request), you can pass NULL. -// -// On error -1 is retutned, else 0 is returned and the -// handle is initialized. -// -// Note that calling this function when no requests are -// pending is considered an error. -int http_client_wait(HTTP_Client *client, HTTP_Response **res, void **user_data); - -// TODO: comment -HTTP_Response *http_get(HTTP_String url, - HTTP_String *headers, int num_headers); - -// TODO: comment -HTTP_Response *http_post(HTTP_String url, - HTTP_String *headers, int num_headers, - HTTP_String body); - -#endif // CLIENT_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/server.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "src/server.h" -#ifndef HTTP_SERVER_INCLUDED -#define HTTP_SERVER_INCLUDED - -#include - -#ifndef HTTP_AMALGAMATION -#include "parse.h" -#endif - -typedef struct { - void *data0; - int data1; - int data2; -} HTTP_ResponseBuilder; - -typedef struct HTTP_Server HTTP_Server; - -HTTP_Server *http_server_init(HTTP_String addr, uint16_t port); - -HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port, - uint16_t secure_port, HTTP_String cert_key, HTTP_String private_key); - -void http_server_free (HTTP_Server *server); -int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseBuilder *handle); -int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); -void http_response_builder_status (HTTP_ResponseBuilder res, int status); -void http_response_builder_header (HTTP_ResponseBuilder res, HTTP_String str); -void http_response_builder_body (HTTP_ResponseBuilder res, HTTP_String str); -void http_response_builder_bodycap (HTTP_ResponseBuilder res, int mincap); -char* http_response_builder_bodybuf (HTTP_ResponseBuilder res, int *cap); -void http_response_builder_bodyack (HTTP_ResponseBuilder res, int num); -void http_response_builder_undo (HTTP_ResponseBuilder res); -void http_response_builder_done (HTTP_ResponseBuilder res); - -#endif // HTTP_SERVER_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/router.h -//////////////////////////////////////////////////////////////////////////////////////// - -#line 1 "src/router.h" -#ifndef HTTP_ROUTER_INCLUDED -#define HTTP_ROUTER_INCLUDED - -#ifndef HTTP_AMALGAMATION -#include "server.h" -#endif - -typedef struct HTTP_Router HTTP_Router; -typedef void (*HTTP_RouterFunc)(HTTP_Request*, HTTP_ResponseBuilder, void*);; - -HTTP_Router* http_router_init (void); -void http_router_free (HTTP_Router *router); -void http_router_resolve (HTTP_Router *router, HTTP_Request *req, HTTP_ResponseBuilder res); -void http_router_dir (HTTP_Router *router, HTTP_String endpoint, HTTP_String path); -void http_router_func (HTTP_Router *router, HTTP_Method method, HTTP_String endpoint, HTTP_RouterFunc func, void*); -int http_serve (char *addr, int port, HTTP_Router *router); - -#endif // HTTP_ROUTER_INCLUDED -#endif // HTTP_AMALGAMATION - //////////////////////////////////////////////////////////////////////////////////////// // src/main.h //////////////////////////////////////////////////////////////////////////////////////// #line 1 "src/main.h" +#include +#include -#define CWEB_STR(X) (CWEB_String) { (X), SIZEOF(X)-1 } +#define CWEB_STR(X) (CWEB_String) { (X), (int) sizeof(X)-1 } typedef struct { char *ptr; @@ -730,33 +200,17 @@ typedef struct { #define CWEB_VARGS(...) CWEB_DISPATCH__(__VA_ARGS__, CWEB_VARGS_5, CWEB_VARGS_4, CWEB_VARGS_3, CWEB_VARGS_2, CWEB_VARGS_1)(__VA_ARGS__) typedef struct CWEB CWEB; - -typedef struct { - - CWEB *cweb; - - WL_Arena arena; - - HTTP_Request *req; - HTTP_ResponseBuilder builder; - - // Session - bool just_created_session; - int user_id; - CWEB_String sess; - CWEB_String csrf; - -} CWEB_Request; +typedef struct CWEB_Request CWEB_Request; int cweb_global_init(void); void cweb_global_free(void); -int cweb_init(CWEB *cweb, CWEB_String addr, uint16_t port); -void cweb_free(CWEB *cweb); +CWEB *cweb_init(CWEB_String addr, uint16_t port); +void cweb_free(CWEB *cweb); int cweb_enable_database(CWEB *cweb, CWEB_String file); -CWEB_Request cweb_wait(CWEB *cweb); +CWEB_Request *cweb_wait(CWEB *cweb); ////////////////////////////////////// // Session diff --git a/demo/main.c b/demo/main.c index a675843..3383b73 100644 --- a/demo/main.c +++ b/demo/main.c @@ -107,6 +107,8 @@ bool valid_comment_content(CWEB_String str) return true; } +// TODO: check for the correct methods at each endpoint + static int user_exists(CWEB *cweb, CWEB_String name, CWEB_String pass) { CWEB_QueryResult res = cweb_database_select(cweb, "SELECT id, hash FROM Users WHERE username=?", name); @@ -136,7 +138,7 @@ static int user_exists(CWEB *cweb, CWEB_String name, CWEB_String pass) return user_id; } -static void endpoint_api_login(CWEB_Request *req) +static void endpoint_api_login(CWEB *cweb, CWEB_Request *req) { if (cweb_get_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); @@ -150,7 +152,7 @@ static void endpoint_api_login(CWEB_Request *req) return; } - int ret = user_exists(req->cweb, name, pass); + int ret = user_exists(cweb, name, pass); if (ret < 0) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid credentials
")); return; @@ -162,7 +164,7 @@ static void endpoint_api_login(CWEB_Request *req) cweb_respond_redirect(req, CWEB_STR("/index")); } -static void endpoint_api_signup(CWEB_Request *req) +static void endpoint_api_signup(CWEB *cweb, CWEB_Request *req) { if (cweb_get_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); @@ -191,7 +193,7 @@ static void endpoint_api_signup(CWEB_Request *req) return; } - int64_t insert_id = cweb_database_insert(req->cweb, "INSERT INTO Users(username, email, hash) VALUES (?, ?, ?)", name, email, hash); + int64_t insert_id = cweb_database_insert(cweb, "INSERT INTO Users(username, email, hash) VALUES (?, ?, ?)", name, email, hash); if (insert_id < 0) { // TODO: What if the user exists? cweb_respond_basic(req, 400, CWEB_STR("
Internal error
")); @@ -202,13 +204,13 @@ static void endpoint_api_signup(CWEB_Request *req) cweb_respond_redirect(req, CWEB_STR("/index")); } -static void endpoint_api_logout(CWEB_Request *req) +static void endpoint_api_logout(CWEB *cweb, CWEB_Request *req) { - cweb_set_current_user_id(req, -1); + cweb_set_session_user_id(req, -1); cweb_respond_redirect(req, CWEB_STR("/index")); } -static void endpoint_api_post(CWEB_Request *req) +static void endpoint_api_post(CWEB *cweb, CWEB_Request *req) { int user_id = cweb_get_session_user_id(req); if (user_id == -1) { @@ -247,7 +249,7 @@ static void endpoint_api_post(CWEB_Request *req) content = link; } - int64_t insert_id = cweb_database_insert(req->cweb, + int64_t insert_id = cweb_database_insert(cweb, "INSERT INTO Posts(author, title, is_link, content) VALUES (?, ?, ?, ?)", user_id, title, is_link, content); @@ -260,7 +262,7 @@ static void endpoint_api_post(CWEB_Request *req) cweb_respond_redirect(req, "/post?id=%d", post_id); } -static void endpoint_api_comment(CWEB_Request *req) +static void endpoint_api_comment(CWEB *cweb, CWEB_Request *req) { int user_id = cweb_get_session_user_id(req); if (user_id == -1) { @@ -285,8 +287,8 @@ static void endpoint_api_comment(CWEB_Request *req) } int64_t insert_id; - if (parent_comment == -1) insert_id = cweb_database_insert(req->cweb, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post); - else insert_id = cweb_database_insert(req->cweb, "INSERT INTO Comments(author, content, parent_post, parent_comment) VALUES (?, ?, ?, ?)", user_id, content, parent_post, parent_comment); + if (parent_comment == -1) insert_id = cweb_database_insert(cweb, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post); + else insert_id = cweb_database_insert(cweb, "INSERT INTO Comments(author, content, parent_post, parent_comment) VALUES (?, ?, ?, ?)", user_id, content, parent_post, parent_comment); if (insert_id < 0) { cweb_respond_basic(req, 200, CWEB_STR("
Internal error
")); return; @@ -295,12 +297,12 @@ static void endpoint_api_comment(CWEB_Request *req) cweb_respond_redirect(req, "/post?id=%d", parent_post); } -static void endpoint_index(CWEB_Request *req) +static void endpoint_index(CWEB *cweb, CWEB_Request *req) { cweb_respond_template(req, 200, CWEB_STR("pages/index.wl"), -1); } -static void endpoint_write(CWEB_Request *req) +static void endpoint_write(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) == -1) { cweb_respond_redirect(req, CWEB_STR("/index")); @@ -310,7 +312,7 @@ static void endpoint_write(CWEB_Request *req) cweb_respond_template(req, 200, CWEB_STR("pages/write.wl"), -1); } -static void endpoint_login(CWEB_Request *req) +static void endpoint_login(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); @@ -320,7 +322,7 @@ static void endpoint_login(CWEB_Request *req) cweb_respond_template(req, 200, CWEB_STR("pages/login.wl"), -1); } -static void endpoint_signup(CWEB_Request *req) +static void endpoint_signup(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); @@ -330,15 +332,15 @@ static void endpoint_signup(CWEB_Request *req) cweb_respond_template(req, 200, CWEB_STR("pages/signup.wl"), -1); } -static void endpoint_post(CWEB_Request *req) +static void endpoint_post(CWEB *cweb, CWEB_Request *req) { - int post_id = cweb_request_get_parami(req, CWEB_STR("id")); + int post_id = cweb_get_param_i(req, CWEB_STR("id")); if (post_id < 0) { cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1); return; } - CWEB_QueryResult res = cweb_database_select(req->cweb, + CWEB_QueryResult res = cweb_database_select(cweb, "SELECT COUNT(*) FROM Posts WHERE id=?", post_id); int num; @@ -362,7 +364,7 @@ static void endpoint_post(CWEB_Request *req) cweb_respond_template(req, 200, CWEB_STR("pages/post.wl"), post_id); } -static void endpoint_fallback(CWEB_Request *req) +static void endpoint_fallback(CWEB *cweb, CWEB_Request *req) { cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1); } @@ -371,31 +373,33 @@ int main(void) { CWEB_String addr = CWEB_STR("127.0.0.1"); uint16_t port = 8080; - CWEB *cweb; if (cweb_global_init() < 0) return -1; - if (cweb_init(&cweb, addr, port) < 0) + CWEB *cweb = cweb_init(addr, port); + if (cweb == NULL) { + cweb_global_free(); return -1; - - for (;;) { - CWEB_Request request = cweb_wait(&cweb); - cweb_add_endpoint(cweb, CWEB_STR("/api/login"), endpoint_api_login, CWEB_ENDPOINT_POST | CWEB_ENDPOINT_SECURE); - cweb_add_endpoint(cweb, CWEB_STR("/api/signup"), endpoint_api_signup, CWEB_ENDPOINT_POST | CWEB_ENDPOINT_SECURE); - cweb_add_endpoint(cweb, CWEB_STR("/api/logout"), endpoint_api_logout, CWEB_ENDPOINT_POST | CWEB_ENDPOINT_SECURE); - cweb_add_endpoint(cweb, CWEB_STR("/api/post"), endpoint_api_post, CWEB_ENDPOINT_POST); - cweb_add_endpoint(cweb, CWEB_STR("/api/comment"), endpoint_api_comment, CWEB_ENDPOINT_POST); - cweb_add_endpoint(cweb, CWEB_STR("/index"), endpoint_index, CWEB_ENDPOINT_GET); - cweb_add_endpoint(cweb, CWEB_STR("/write"), endpoint_write, CWEB_ENDPOINT_GET); - cweb_add_endpoint(cweb, CWEB_STR("/login"), endpoint_login, CWEB_ENDPOINT_GET); - cweb_add_endpoint(cweb, CWEB_STR("/signup"), endpoint_signup, CWEB_ENDPOINT_GET); - cweb_add_endpoint(cweb, CWEB_STR("/post"), endpoint_post, CWEB_ENDPOINT_GET); - cweb_fallback_endpoint(cweb, endpoint_fallback); } - cweb_run(&cweb); - cweb_free(&cweb); + for (;;) { + CWEB_Request *req = cweb_wait(cweb); + if (0) {} + else if (cweb_match_endpoint(req, CWEB_STR("/api/login"))) endpoint_api_login(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/api/signup"))) endpoint_api_signup(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/api/logout"))) endpoint_api_logout(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/api/post"))) endpoint_api_post(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/api/comment"))) endpoint_api_comment(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/index"))) endpoint_index(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/write"))) endpoint_write(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/login"))) endpoint_login(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/signup"))) endpoint_signup(cweb, req); + else if (cweb_match_endpoint(req, CWEB_STR("/post"))) endpoint_post(cweb, req); + else endpoint_fallback(cweb, req); + } + + cweb_free(cweb); cweb_global_free(); return 0; } diff --git a/src/main.c b/src/main.c index 78d4d0f..172b5b4 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,6 @@ #include "sqlite3.h" +#include "wl.h" +#include "chttp.h" #include "cweb.h" #ifdef __linux__ @@ -55,9 +57,27 @@ struct CWEB { SQLiteCache *dbcache; // Template - TemplateCache *tcpcache; + TemplateCache *tpcache; bool allow_insecure_login; + + CWEB_Request req; +}; + +struct CWEB_Request { + + CWEB *cweb; + + WL_Arena arena; + + HTTP_Request *req; + HTTP_ResponseBuilder builder; + + // Session + bool just_created_session; + int user_id; + CWEB_String sess; + CWEB_String csrf; }; /////////////////////////// @@ -268,7 +288,7 @@ static void session_storage_free(SessionStorage *storage) free(storage); } -static Session *lookup_session_slot(SessionStorage *storage, HTTP_String sess, bool find_unused) +static Session *lookup_session_slot(SessionStorage *storage, CWEB_String sess, bool find_unused) { if (find_unused && 2 * storage->count + 2 > storage->capacity) return NULL; @@ -321,7 +341,7 @@ static Session *lookup_session_slot(SessionStorage *storage, HTTP_String sess, b return NULL; } -static int create_session(SessionStorage *storage, int user, HTTP_String *psess, HTTP_String *pcsrf) +static int create_session(SessionStorage *storage, int user, CWEB_String *psess, CWEB_String *pcsrf) { int ret; char raw_sess[SESS_RAW_TOKEN_SIZE]; @@ -338,21 +358,21 @@ static int create_session(SessionStorage *storage, int user, HTTP_String *psess, unpack_token(raw_sess, SESS_RAW_TOKEN_SIZE, sess, SESS_TOKEN_SIZE); unpack_token(raw_csrf, CSRF_RAW_TOKEN_SIZE, csrf, CSRF_TOKEN_SIZE); - Session *found = lookup_session_slot(storage, (HTTP_String) { sess, SESS_TOKEN_SIZE }, true); + Session *found = lookup_session_slot(storage, (CWEB_String) { sess, SESS_TOKEN_SIZE }, true); if (found == NULL) return -1; found->user = user; memcpy(found->sess, sess, SESS_TOKEN_SIZE); memcpy(found->csrf, csrf, CSRF_TOKEN_SIZE); - *psess = (HTTP_String) { found->sess, SESS_TOKEN_SIZE }; - *pcsrf = (HTTP_String) { found->csrf, CSRF_TOKEN_SIZE }; + *psess = (CWEB_String) { found->sess, SESS_TOKEN_SIZE }; + *pcsrf = (CWEB_String) { found->csrf, CSRF_TOKEN_SIZE }; storage->count++; return 0; } -static int delete_session(SessionStorage *storage, HTTP_String sess) +static int delete_session(SessionStorage *storage, CWEB_String sess) { char raw_sess[SESS_RAW_TOKEN_SIZE]; if (sess.len != SESS_TOKEN_SIZE || pack_token(sess.ptr, sess.len, raw_sess, (int) sizeof(raw_sess)) < 0) @@ -366,13 +386,13 @@ static int delete_session(SessionStorage *storage, HTTP_String sess) return 0; } -static int find_session(SessionStorage *storage, HTTP_String sess, HTTP_String *pcsrf, int *puser) +static int find_session(SessionStorage *storage, CWEB_String sess, CWEB_String *pcsrf, int *puser) { Session *found = lookup_session_slot(storage, sess, false); if (found == NULL) return -1; assert(found->user >= 0); - *pcsrf = (HTTP_String) { found->csrf, CSRF_TOKEN_SIZE }; + *pcsrf = (CWEB_String) { found->csrf, CSRF_TOKEN_SIZE }; *puser = found->user; return 0; } @@ -493,7 +513,7 @@ static int sqlite3utils_prepare(SQLiteCache *cache, sqlite3_stmt **pstmt, char * } static int sqlite3utils_prepare_and_bind_impl(SQLiteCache *cache, - sqlite3_stmt **pstmt, char *fmt, VArgs args) + sqlite3_stmt **pstmt, char *fmt, CWEB_VArgs args) { sqlite3_stmt *stmt; int ret = sqlite3utils_prepare(cache, &stmt, fmt, strlen(fmt)); @@ -533,7 +553,7 @@ static int sqlite3utils_prepare_and_bind_impl(SQLiteCache *cache, return SQLITE_OK; } -int64_t cweb_database_insert_impl(CWEB *cweb, const char *fmt, VArgs args) +int64_t cweb_database_insert_impl(CWEB *cweb, const char *fmt, CWEB_VArgs args) { sqlite3_stmt *stmt; int ret = sqlite3utils_prepare_and_bind_impl(cweb->dbcache, &stmt, fmt, args); @@ -638,7 +658,7 @@ int cweb_next_query_row_impl(CWEB_QueryResult *res, CWEB_VArgs args) case CWEB_VARG_TYPE_PSTR: { - *args.ptr[i].pstr = (HTTP_String) { + *args.ptr[i].pstr = (CWEB_String) { sqlite3_column_text(res->handle, i), sqlite3_column_bytes(res->handle, i), }; @@ -673,7 +693,7 @@ void cweb_global_free(void) http_global_free(); } -int cweb_init(CWEB *cweb, CWEB_String addr, uint16_t port) +CWEB *web_init(CWEB_String addr, uint16_t port) { // If set, allows logins and signups over HTTP, which is highly insecure. // This allows compiling the application without TLS when developing. @@ -682,29 +702,38 @@ int cweb_init(CWEB *cweb, CWEB_String addr, uint16_t port) if (allow_insecure_login) printf("WARNING: allow_insecure_login is true\n"); + CWEB *cweb = malloc(sizeof(CWEB)); + if (cweb == NULL) + return -1; + cweb->pool_cap = 1<<20; cweb->pool = malloc(cweb->pool_cap); - if (cweb->pool == NULL) + if (cweb->pool == NULL) { + free(cweb); return -1; + } cweb->tpcache = template_cache_init(4); if (cweb->tpcache == NULL) { free(cweb->pool); + free(cweb); return -1; } cweb->session_storage = session_storage_init(1024); if (cweb->session_storage == NULL) { - template_cache_free(cweb->tcpcache); + template_cache_free(cweb->tpcache); free(cweb->pool); + free(cweb); return -1; } - cweb->server = http_server_init(addr, port); + cweb->server = http_server_init((HTTP_String) { addr.ptr, addr.len }, port); if (cweb->server == NULL) { session_storage_free(cweb->session_storage); - template_cache_free(cweb->tcpcache); + template_cache_free(cweb->tpcache); free(cweb->pool); + free(cweb); return -1; } @@ -725,6 +754,7 @@ void cweb_free(CWEB *cweb) sqlite_cache_free(cweb->dbcache); sqlite3_close(cweb->db); } + free(cweb); } void cweb_version(void) @@ -778,30 +808,31 @@ int cweb_enable_database(CWEB *cweb, CWEB_String file) return 0; } -CWEB_Request cweb_wait(CWEB *cweb) +CWEB_Request *cweb_wait(CWEB *cweb) { - CWEB_Request req; + CWEB_Request *req = &cweb->req; - int ret = http_server_wait(cweb->server, &req.req, &req.builder); + int ret = http_server_wait(cweb->server, &req->req, &req->builder); if (ret < 0) return -1; - req.arena = (WL_Arena) { cweb->pool, cweb->pool_cap, 0 }; + req->arena = (WL_Arena) { cweb->pool, cweb->pool_cap, 0 }; - int user_id; - HTTP_String sess; - HTTP_String csrf; - - req.just_created_session = false; - req.sess = http_getcookie(req.req, HTTP_STR("sess_token")); - if (find_session(cweb->session_storage, sess, &csrf, &user_id) < 0) { - req.user_id = -1; - req.sess = (HTTP_String) { NULL, 0 }; - req.csrf = (HTTP_String) { NULL, 0 }; + req->just_created_session = false; + req->sess = http_get_cookie(req->req, HTTP_STR("sess_token")); + if (find_session(cweb->session_storage, req->sess, &req->csrf, &req->user_id) < 0) { + req->user_id = -1; + req->sess = (CWEB_String) { NULL, 0 }; + req->csrf = (CWEB_String) { NULL, 0 }; } return req; } +bool cweb_match_endpoint(CWEB_Request *req, CWEB_String str) +{ + return http_streq(req->req->url.path, (HTTP_String) { str.ptr, str.len }); +} + CWEB_String cweb_get_param_s(CWEB_Request *req, CWEB_String name) { HTTP_String res = http_get_param(req->req->body, diff --git a/src/main.h b/src/main.h index 2713e99..46c4d31 100644 --- a/src/main.h +++ b/src/main.h @@ -1,11 +1,6 @@ #include #include -#ifndef CWEB_AMALGAMATION -#include "wl.h" -#include "chttp.h" -#endif - #define CWEB_STR(X) (CWEB_String) { (X), (int) sizeof(X)-1 } typedef struct { @@ -195,33 +190,17 @@ typedef struct { #define CWEB_VARGS(...) CWEB_DISPATCH__(__VA_ARGS__, CWEB_VARGS_5, CWEB_VARGS_4, CWEB_VARGS_3, CWEB_VARGS_2, CWEB_VARGS_1)(__VA_ARGS__) typedef struct CWEB CWEB; - -typedef struct { - - CWEB *cweb; - - WL_Arena arena; - - HTTP_Request *req; - HTTP_ResponseBuilder builder; - - // Session - bool just_created_session; - int user_id; - CWEB_String sess; - CWEB_String csrf; - -} CWEB_Request; +typedef struct CWEB_Request CWEB_Request; int cweb_global_init(void); void cweb_global_free(void); -int cweb_init(CWEB *cweb, CWEB_String addr, uint16_t port); -void cweb_free(CWEB *cweb); +CWEB *cweb_init(CWEB_String addr, uint16_t port); +void cweb_free(CWEB *cweb); int cweb_enable_database(CWEB *cweb, CWEB_String file); -CWEB_Request cweb_wait(CWEB *cweb); +CWEB_Request *cweb_wait(CWEB *cweb); ////////////////////////////////////// // Session @@ -233,6 +212,7 @@ int cweb_set_session_user_id(CWEB_Request *req, int user_id); ////////////////////////////////////// // Request +bool cweb_match_endpoint(CWEB_Request *req, CWEB_String str); CWEB_String cweb_get_param_s(CWEB_Request *req, CWEB_String name); int cweb_get_param_i(CWEB_Request *req, CWEB_String name);