340 lines
9.6 KiB
C
340 lines
9.6 KiB
C
////////////////////////////////////////////////////////////////////////////
|
|
// LICENSE //
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2025 Francesco Cozzuto
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the “Software”),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <stddef.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef TINYHTTP_CUSTOMCONFIG
|
|
#include "tinyhttp_config.h"
|
|
#else
|
|
#define TINYHTTP_SERVER_ENABLE 1
|
|
#define TINYHTTP_ROUTER_ENABLE 0
|
|
#define TINYHTTP_HTTPS_ENABLE 0
|
|
#define TINYHTTP_ROUTER_MAX_PATH_COMPONENTS 32
|
|
#define TINYHTTP_HEADER_LIMIT 32
|
|
#define TINYHTTP_SERVER_CONN_LIMIT (1<<10)
|
|
#define TINYHTTP_SERVER_EPOLL_BATCH_SIZE (1<<10)
|
|
#endif
|
|
|
|
// Opaque types
|
|
typedef struct TinyHTTPServer TinyHTTPServer;
|
|
typedef struct TinyHTTPRouter TinyHTTPRouter;
|
|
|
|
typedef struct {
|
|
const char *ptr;
|
|
ptrdiff_t len;
|
|
} TinyHTTPString;
|
|
|
|
#define TINYHTTP_STRING(X) ((TinyHTTPString) {(X), sizeof(X)-1})
|
|
|
|
typedef enum {
|
|
TINYHTTP_METHOD_GET,
|
|
TINYHTTP_METHOD_POST,
|
|
} TinyHTTPMethod;
|
|
|
|
typedef struct {
|
|
TinyHTTPString name;
|
|
TinyHTTPString value;
|
|
} TinyHTTPHeader;
|
|
|
|
typedef struct {
|
|
TinyHTTPMethod method;
|
|
int minor;
|
|
TinyHTTPString path;
|
|
int num_headers;
|
|
TinyHTTPHeader headers[TINYHTTP_HEADER_LIMIT];
|
|
char* body;
|
|
ptrdiff_t body_len;
|
|
} TinyHTTPRequest;
|
|
|
|
typedef struct {
|
|
TinyHTTPServer *server;
|
|
unsigned short idx;
|
|
unsigned short gen;
|
|
} TinyHTTPResponse;
|
|
|
|
typedef struct {
|
|
|
|
int reuse;
|
|
|
|
// HTTP
|
|
const char *plain_addr;
|
|
int plain_port;
|
|
int plain_backlog;
|
|
|
|
// HTTPS
|
|
int secure;
|
|
const char *secure_addr;
|
|
int secure_port;
|
|
int secure_backlog;
|
|
const char *cert_file;
|
|
const char *private_key_file;
|
|
|
|
} TinyHTTPServerConfig;
|
|
|
|
typedef enum {
|
|
TINYHTTP_MEM_MALLOC,
|
|
TINYHTTP_MEM_FREE,
|
|
} TinyHTTPMemoryFuncTag;
|
|
|
|
typedef void*(*TinyHTTPMemoryFunc)(TinyHTTPMemoryFuncTag tag,
|
|
void *ptr, int len, void *data);
|
|
|
|
// See [tinyhttp_stream_state]
|
|
enum {
|
|
TINYHTTP_STREAM_DIED = 1 << 0,
|
|
TINYHTTP_STREAM_READY = 1 << 1,
|
|
TINYHTTP_STREAM_RECV = 1 << 2,
|
|
TINYHTTP_STREAM_SEND = 1 << 3,
|
|
TINYHTTP_STREAM_CLOSE = 1 << 4,
|
|
TINYHTTP_STREAM_REUSE = 1 << 5,
|
|
TINYHTTP_STREAM_SEND_STARTED = 1 << 6,
|
|
TINYHTTP_STREAM_RECV_STARTED = 1 << 7,
|
|
};
|
|
|
|
// Internal use
|
|
typedef enum {
|
|
TINYHTTP_OUTPUT_STATE_NONE,
|
|
TINYHTTP_OUTPUT_STATE_STATUS,
|
|
TINYHTTP_OUTPUT_STATE_HEADER,
|
|
TINYHTTP_OUTPUT_STATE_BODY,
|
|
TINYHTTP_OUTPUT_STATE_ERROR,
|
|
} TinyHTTPConnectionOutputState;
|
|
|
|
// Internal use
|
|
enum {
|
|
BYTE_QUEUE_ERROR = 1 << 0,
|
|
BYTE_QUEUE_LOCK = 1 << 1,
|
|
BYTE_QUEUE_READ = 1 << 2,
|
|
BYTE_QUEUE_WRITE = 1 << 3,
|
|
};
|
|
|
|
// Internal use
|
|
typedef struct {
|
|
|
|
TinyHTTPMemoryFunc memfunc;
|
|
void *memfuncdata;
|
|
|
|
unsigned long long curs;
|
|
unsigned int lock; // TODO: Should this be u64?
|
|
|
|
char* data;
|
|
unsigned int head;
|
|
unsigned int size;
|
|
unsigned int used;
|
|
unsigned int limit;
|
|
|
|
char* read_target;
|
|
unsigned int read_target_size;
|
|
|
|
int flags;
|
|
} TinyHTTPByteQueue;
|
|
|
|
// Internal use
|
|
typedef unsigned long long TinyHTTPByteQueueOffset;
|
|
|
|
typedef struct {
|
|
int state;
|
|
int numexch;
|
|
int chunked;
|
|
int keepalive;
|
|
ptrdiff_t reqsize;
|
|
unsigned long long bodylimit;
|
|
TinyHTTPConnectionOutputState output_state;
|
|
TinyHTTPByteQueueOffset content_length_value_offset;
|
|
TinyHTTPByteQueueOffset content_length_offset;
|
|
TinyHTTPByteQueue in;
|
|
TinyHTTPByteQueue out;
|
|
TinyHTTPRequest req;
|
|
} TinyHTTPStream;
|
|
|
|
int tinyhttp_streq(TinyHTTPString s1, TinyHTTPString s2);
|
|
int tinyhttp_streqcase(TinyHTTPString s1, TinyHTTPString s2);
|
|
|
|
// Initializes an HTTP stream
|
|
//
|
|
// TODO: Comment on memfunc
|
|
void
|
|
tinyhttp_stream_init(TinyHTTPStream *stream,
|
|
TinyHTTPMemoryFunc memfunc, void *memfuncdata);
|
|
|
|
// Deinitializes an HTTP stream
|
|
//
|
|
// This function may be called at any time on a memory
|
|
// region where [tinyhttp_stream_init] was called at
|
|
// least once.
|
|
//
|
|
// From the moment this function is called until the
|
|
// next call to [tinyhttp_stream_init], all [tinyhttp_stream_*]
|
|
// calls become no-ops.
|
|
void tinyhttp_stream_free(TinyHTTPStream *stream);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_kill(TinyHTTPStream *stream);
|
|
|
|
// Returns the current state of the connection
|
|
//
|
|
// TODO: List the possible states
|
|
//
|
|
// Note:
|
|
// - Calling this function is always valid on a
|
|
// memory region where [tinyhttp_stream_init] was
|
|
// called at least once.
|
|
int tinyhttp_stream_state(TinyHTTPStream *stream);
|
|
|
|
// Returns the pointer and capacity of a memory region
|
|
// where data should be read from the network.
|
|
//
|
|
// You can write at least [*cap] bytes at the returned
|
|
// pointer, and need to call [tinyhttp_stream_net_recv_ack]
|
|
// when the write is complete.
|
|
//
|
|
// Note:
|
|
// - Due to sticky errors, this function may return
|
|
// the null pointer and a capacity of 0. The caller
|
|
// must not write to the pointer but act as if
|
|
// everything went okay.
|
|
char *tinyhttp_stream_recv_buf(TinyHTTPStream *stream, ptrdiff_t *cap);
|
|
|
|
// Acknowledge the bytes written from the network
|
|
//
|
|
// The write must have been initiated by calling
|
|
// [tinyhttp_stream_net_recv_buf].
|
|
void tinyhttp_stream_recv_ack(TinyHTTPStream *stream, ptrdiff_t num);
|
|
|
|
// Returns the pointer and capacity of a memory region
|
|
// where data should be written to the network.
|
|
//
|
|
// When the write is complete, [tinyhttp_stream_net_send_ack]
|
|
// should be called with the number of consumed bytes.
|
|
char *tinyhttp_stream_send_buf(TinyHTTPStream *stream, ptrdiff_t *len);
|
|
|
|
// See [tinyhttp_stream_net_send_buf]
|
|
void tinyhttp_stream_send_ack(TinyHTTPStream *stream, ptrdiff_t num);
|
|
|
|
// Enable/disable connection reuse for this HTTP
|
|
// connection. This change takes effect at the start
|
|
// of the next request.
|
|
//
|
|
// Note that even if the reuse option is set, the
|
|
// connection may still be closed due to errors or
|
|
// the client deciding otherwise.
|
|
//
|
|
// On the other hand, if this option is set the next
|
|
// request will definitely be the last.
|
|
void tinyhttp_stream_setreuse(TinyHTTPStream *stream, int value);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_setbodylimit(TinyHTTPStream *stream, unsigned long long value);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_setinbuflimit(TinyHTTPStream *stream, unsigned int value);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_setoutbuflimit(TinyHTTPStream *stream, unsigned int value);
|
|
|
|
// TODO: Comment
|
|
TinyHTTPRequest *tinyhttp_stream_request(TinyHTTPStream *stream);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_status(TinyHTTPStream *stream, int status);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_header(TinyHTTPStream *stream, const char *fmt, ...);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_header_fmt(TinyHTTPStream *stream, const char *fmt, va_list args);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_body(TinyHTTPStream *stream, const char *src, int len);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_body_setmincap(TinyHTTPStream *stream, ptrdiff_t mincap);
|
|
|
|
// TODO: Comment
|
|
char *tinyhttp_stream_response_body_buf(TinyHTTPStream *stream, ptrdiff_t *cap);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_body_ack(TinyHTTPStream *stream, ptrdiff_t num);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_send(TinyHTTPStream *stream);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_stream_response_undo(TinyHTTPStream *stream);
|
|
|
|
// TODO: Comment
|
|
TinyHTTPServer *tinyhttp_server_init(TinyHTTPServerConfig config);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_server_free(TinyHTTPServer *server);
|
|
|
|
// TODO: Comment
|
|
int tinyhttp_server_wait(TinyHTTPServer *server, TinyHTTPRequest **req,
|
|
TinyHTTPResponse *res, int timeout);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_status(TinyHTTPResponse res, int status);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_header(TinyHTTPResponse res,
|
|
const char *fmt, ...);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_header_fmt(TinyHTTPResponse res,
|
|
const char *fmt, va_list args);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_body_setmincap(TinyHTTPResponse res,
|
|
ptrdiff_t mincap);
|
|
|
|
// TODO: Comment
|
|
char *tinyhttp_response_body_buf(TinyHTTPResponse res, ptrdiff_t *cap);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_body_ack(TinyHTTPResponse res, ptrdiff_t num);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_body(TinyHTTPResponse res, char *src, int len);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_send(TinyHTTPResponse res);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_response_undo(TinyHTTPResponse res);
|
|
|
|
// TODO: Comment
|
|
TinyHTTPRouter *tinyhttp_router_init(void);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_router_free(TinyHTTPRouter *router);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_router_resolve(TinyHTTPRouter *router, TinyHTTPServer *server, TinyHTTPRequest *request, TinyHTTPResponse response);
|
|
|
|
// TODO: Comment
|
|
void tinyhttp_router_dir(TinyHTTPRouter *router, const char *endpoint, const char *path, int dir_listing);
|