forked from Telodendria/Telodendria
Jordan Bancino
3a683dbb70
These are installed to the system and some compilers may not find the headers in the current directory if we don't do this, even though according to the C standard, either should work.
233 lines
8.4 KiB
C
233 lines
8.4 KiB
C
/*
|
|
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
|
*
|
|
* 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 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.
|
|
*/
|
|
#ifndef CYTOPLASM_HTTPSERVER_H
|
|
#define CYTOPLASM_HTTPSERVER_H
|
|
|
|
/***
|
|
* @Nm HttpServer
|
|
* @Nd Extremely simple HTTP server.
|
|
* @Dd December 13 2022
|
|
* @Xr Http HttpClient
|
|
*
|
|
* .Nm
|
|
* builds on the
|
|
* .Xr Http 3
|
|
* API, and provides a very simple, yet very functional API for
|
|
* creating an HTTP server. It aims at being easy to use and minimal,
|
|
* yet also efficient. It uses non-blocking I/O, is fully
|
|
* multi-threaded, and is very configurable. It can be set up in just
|
|
* two function calls and minimal supporting code.
|
|
* .Pp
|
|
* This API should be familar to those that have dealt with the HTTP
|
|
* server libraries of other programming languages, particularly Java.
|
|
* In fact, much of the terminology used in this API came from Java,
|
|
* and you'll notice that the way responses are sent and received very
|
|
* closely resembles Java.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "Http.h"
|
|
#include "HashMap.h"
|
|
#include "Stream.h"
|
|
|
|
/**
|
|
* The functions on this API operate on an opaque structure.
|
|
*/
|
|
typedef struct HttpServer HttpServer;
|
|
|
|
/**
|
|
* Each request receives a context structure. It is opaque, so the
|
|
* functions defined in this API should be used to fetch data from
|
|
* it. These functions allow the handler to figure out the context of
|
|
* the request, which includes the path requested, any parameters,
|
|
* and the headers and method used to make the request. The context
|
|
* also provides the means by which the handler responds to the
|
|
* request, allowing it to set the status code, headers, and body.
|
|
*/
|
|
typedef struct HttpServerContext HttpServerContext;
|
|
|
|
/**
|
|
* The request handler function is executed when an HTTP request is
|
|
* received. It takes a request context, and a pointer as specified
|
|
* in the server configuration.
|
|
*/
|
|
typedef void (HttpHandler) (HttpServerContext *, void *);
|
|
|
|
/**
|
|
* The number of arguments to
|
|
* .Fn HttpServerCreate
|
|
* has grown so large that arguments are now stuffed into a
|
|
* configuration structure, which is in turn passed to
|
|
* .Fn HttpServerCreate .
|
|
* This configuration is copied by value into the internal
|
|
* structures of the server. It is copied with very minimal
|
|
* validation, so ensure that all values are sensible. It may
|
|
* make sense to use
|
|
* .Fn memset
|
|
* to zero out everything in here before assigning values.
|
|
*/
|
|
typedef struct HttpServerConfig
|
|
{
|
|
unsigned short port;
|
|
unsigned int threads;
|
|
unsigned int maxConnections;
|
|
|
|
int flags; /* Http(3) flags */
|
|
char *tlsCert; /* File path */
|
|
char *tlsKey; /* File path */
|
|
|
|
HttpHandler *handler;
|
|
void *handlerArgs;
|
|
} HttpServerConfig;
|
|
|
|
/**
|
|
* Create a new HTTP server using the specified configuration.
|
|
* This will set up all internal structures used by the server,
|
|
* and bind the socket and start listening for connections. However,
|
|
* it will not start accepting connections.
|
|
*/
|
|
extern HttpServer * HttpServerCreate(HttpServerConfig *);
|
|
|
|
/**
|
|
* Retrieve the configuration that was used to instantiate the given
|
|
* server. Note that this configuration is not necessarily the exact
|
|
* one that was provided; even though its values are the same, it
|
|
* should be treated as an entirely separate configuration with no
|
|
* connection to the original.
|
|
*/
|
|
extern HttpServerConfig * HttpServerConfigGet(HttpServer *);
|
|
|
|
/**
|
|
* Free the resources associated with the given HTTP server. Note that
|
|
* the server can only be freed after it has been stopped. Calling this
|
|
* function while the server is still running results in undefined
|
|
* behavior.
|
|
*/
|
|
extern void HttpServerFree(HttpServer *);
|
|
|
|
/**
|
|
* Attempt to start the HTTP server, and return immediately with the
|
|
* status. This API is fully multi-threaded and asynchronous, so the
|
|
* caller can continue working while the HTTP server is running in a
|
|
* separate thread and managing a pool of threads to handle responses.
|
|
*/
|
|
extern int HttpServerStart(HttpServer *);
|
|
|
|
/**
|
|
* Typically, at some point after calling
|
|
* .Fn HttpServerStart ,
|
|
* the program will have no more work to do, so it will want to wait
|
|
* for the HTTP server to finish. This is accomplished via this
|
|
* function, which joins the HTTP worker thread to the calling thread,
|
|
* pausing the calling thread until the HTTP server has stopped.
|
|
*/
|
|
extern void HttpServerJoin(HttpServer *);
|
|
|
|
/**
|
|
* Stop the HTTP server. Only the execution of this function will
|
|
* cause the proper shutdown of the HTTP server. If the main program
|
|
* is joined to the HTTP thread, then either another thread or a
|
|
* signal handler will have to stop the server using this function.
|
|
* The typical use case is to install a signal handler that executes
|
|
* this function on a global HTTP server.
|
|
*/
|
|
extern void HttpServerStop(HttpServer *);
|
|
|
|
/**
|
|
* Get the request headers for the request represented by the given
|
|
* context. The data in the returned hash map should be treated as
|
|
* read only and should not be freed; it is managed entirely by the
|
|
* server.
|
|
*/
|
|
extern HashMap * HttpRequestHeaders(HttpServerContext *);
|
|
|
|
/**
|
|
* Get the request method used to make the request represented by
|
|
* the given context.
|
|
*/
|
|
extern HttpRequestMethod HttpRequestMethodGet(HttpServerContext *);
|
|
|
|
/**
|
|
* Get the request path for the request represented by the given
|
|
* context. The return value of this function should be treated as
|
|
* read-only, and should not be freed; it is managed entirely by the
|
|
* server.
|
|
*/
|
|
extern char * HttpRequestPath(HttpServerContext *);
|
|
|
|
/**
|
|
* Retrieve the parsed GET parameters for the request represented by
|
|
* the given context. The returned hash map should be treated as
|
|
* read-only, and should not be freed; it is managed entirely by the
|
|
* server.
|
|
*/
|
|
extern HashMap * HttpRequestParams(HttpServerContext *);
|
|
|
|
/**
|
|
* Set a response header to return to the client. The old value for
|
|
* the given header is returned, if any, otherwise NULL is returned.
|
|
*/
|
|
extern char * HttpResponseHeader(HttpServerContext *, char *, char *);
|
|
|
|
/**
|
|
* Set the response status to return to the client.
|
|
*/
|
|
extern void HttpResponseStatus(HttpServerContext *, HttpStatus);
|
|
|
|
/**
|
|
* Get the current response status that will be sent to the client
|
|
* making the request represented by the given context.
|
|
*/
|
|
extern HttpStatus HttpResponseStatusGet(HttpServerContext *);
|
|
|
|
/**
|
|
* Send the response headers to the client that made the request
|
|
* represented by the specified context. This function must be called
|
|
* before the response body can be written, otherwise a malformed
|
|
* response will be sent.
|
|
*/
|
|
extern void HttpSendHeaders(HttpServerContext *);
|
|
|
|
/**
|
|
* Get a stream that is both readable and writable. Reading from the
|
|
* stream reads the request body that the client sent, if there is one.
|
|
* Note that the rquest headers have already been read, so the stream
|
|
* is correctly positioned at the beginning of the body of the request.
|
|
* .Fn HttpSendHeaders
|
|
* must be called before the stream is written, otherwise a malformed
|
|
* HTTP response will be sent. An HTTP handler should properly set all
|
|
* the headers it itends to send, send those headers, and then write
|
|
* the response body to this stream.
|
|
* .Pp
|
|
* Note that the stream does not need to be closed by the HTTP
|
|
* handler; in fact doing so results in undefined behavior. The stream
|
|
* is managed entirely by the server itself, so it will close it when
|
|
* necessary. This allows the underlying protocol to differ: for
|
|
* instance, an HTTP/1.1 connection may stay for multiple requests and
|
|
* responses.
|
|
*/
|
|
extern Stream * HttpServerStream(HttpServerContext *);
|
|
|
|
#endif /* CYTOPLASM_HTTPSERVER_H */
|