hunting/external/httpserver.h-master/src/api.h
2024-02-27 22:08:38 -08:00

490 lines
16 KiB
C

#ifndef HS_API_H
#define HS_API_H
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @file api.h
*
* MIT License
*
* Copyright (c) 2019 Jeremy Williams
*
* 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.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* httpserver.h (0.9.0)
*
* Description:
*
* A single header C library for building non-blocking event driven HTTP
* servers
*
* Usage:
*
* Do this:
* #define HTTPSERVER_IMPL
* before you include this file in *one* C or C++ file to create the
* implementation.
*
* // i.e. it should look like this:
* #include ...
* #include ...
* #include ...
* #define HTTPSERVER_IMPL
* #include "httpserver.h"
*
* There are some #defines that can be configured. This must be done in the
* same file that you define HTTPSERVER_IMPL These defines have default values
* and will need to be #undef'd and redefined to configure them.
*
* HTTP_REQUEST_BUF_SIZE - default 1024 - The initial size in bytes of the
* read buffer for the request. This buffer grows automatically if it's
* capacity is reached but it certain environments it may be optimal to
* change this value.
*
* HTTP_RESPONSE_BUF_SIZE - default 1024 - Same as above except for the
* response buffer.
*
* HTTP_REQUEST_TIMEOUT - default 20 - The amount of seconds the request
* will wait for activity on the socket before closing. This only applies mid
* request. For the amount of time to hold onto keep-alive connections see
* below.
*
* HTTP_KEEP_ALIVE_TIMEOUT - default 120 - The amount of seconds to keep a
* connection alive a keep-alive request has completed.
*
* HTTP_MAX_TOTAL_EST_MEM_USAGE - default 4294967296 (4GB) - This is the
* amount of read/write buffer space that is allowed to be allocated
* across all requests before new requests will get 503 responses.
*
* HTTP_MAX_TOKEN_LENGTH - default 8192 (8KB) - This is the max size of any
* non body http tokens. i.e: header names, header values, url length,
* etc.
*
* HTTP_MAX_REQUEST_BUF_SIZE - default 8388608 (8MB) - This is the maximum
* amount of bytes that the request buffer will grow to. If the body of
* the request + headers cannot fit in this size the request body will be
* streamed in.
*
* For more details see the documentation of the interface and the example
* below.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifdef __cplusplus
extern "C" {
#endif
// String type used to read the request details. The char pointer is NOT null
// terminated.
struct http_string_s;
struct http_server_s;
struct http_request_s;
struct http_response_s;
/**
* Get the event loop descriptor that the server is running on.
*
* This will be an epoll fd when running on Linux or a kqueue on BSD. This can
* be used to listen for activity on sockets, etc. The only caveat is that the
* user data must be set to a struct where the first member is the function
* pointer to a callback that will handle the event. i.e:
*
* For kevent:
*
* struct foo {
* void (*handler)(struct kevent*);
* ...
* }
*
* // Set ev.udata to a foo pointer when registering the event.
*
* For epoll:
*
* struct foo {
* void (*handler)(struct epoll_event*);
* ...
* }
*
* // Set ev.data.ptr to a foo pointer when registering the event.
*
* @param server The server.
*
* @return The descriptor of the event loop.
*/
int http_server_loop(struct http_server_s *server);
/**
* Allocates and initializes the http server.
*
* @param port The port to listen on.
* @param handler The callback that will fire to handle requests.
*
* @return Pointer to the allocated server.
*/
struct http_server_s *
http_server_init(int port, void (*handler)(struct http_request_s *));
/**
* Listens on the server socket and starts an event loop.
*
* During normal operation this function will not return.
*
* @param server The server.
* @param ipaddr The ip to bind to if NULL binds to all interfaces.
*
* @return Error code if the server fails.
*/
int http_server_listen_addr(struct http_server_s *server, const char *ipaddr);
/**
* See http_server_listen_addr
*/
int http_server_listen(struct http_server_s *server);
/**
* Poll the server socket on specific interface.
*
* Use this listen call in place of the one above when you want to integrate
* an http server into an existing application that has a loop already and you
* want to use the polling functionality instead. This works well for
* applications like games that have a constant update loop.
*
* @param server The server.
* @param ipaddr The ip to bind to if NULL bind to all.
*
* @return Error code if the poll fails.
*/
int http_server_listen_addr_poll(struct http_server_s *server,
const char *ipaddr);
/**
* Poll the server socket on all interfaces. See http_server_listen_addr_poll
*
* @param server The server.
*
* @return Error code if the poll fails.
*/
int http_server_listen_poll(struct http_server_s *server);
/**
* Poll of the request sockets.
*
* Call this function in your update loop. It will trigger the request handler
* once if there is a request ready. It should be called in a loop until it
* returns 0.
*
* @param server The server.
*
* @return Returns 1 if a request was handled and 0 if no requests were handled.
*/
int http_server_poll(struct http_server_s *server);
/**
* Check if a request flag is set.
*
* The flags that can be queried are listed below:
*
* HTTP_FLG_STREAMED
*
* This flag will be set when the request body is chunked or the body is too
* large to fit in memory are once. This means that the
* http_request_read_chunk function must be used to read the body piece by
* piece.
*
* @param request The request.
* @param flag One of the flags listed above.
*
* @return 1 or 0 if the flag is set or not respectively.
*/
int http_request_has_flag(struct http_request_s *request, int flag);
/**
* Returns the request method as it was read from the HTTP request line.
*
* @param request The request.
*
* @return The HTTP method.
*/
struct http_string_s http_request_method(struct http_request_s *request);
/**
* Returns the full request target (url) from the HTTP request line.
*
* @param request The request.
*
* @return The target.
*/
struct http_string_s http_request_target(struct http_request_s *request);
/**
* Retrieves the request body.
*
* @param request The request.
*
* @return The request body. If no request body was sent buf and len of the
* string will be set to 0.
*/
struct http_string_s http_request_body(struct http_request_s *request);
/**
* Returns the request header value for the given header key.
*
* @param request The request.
* @param key The case insensitive header key to search for.
*
* @return The value for the header matching the key. Will be length 0 if not
* found.
*/
struct http_string_s http_request_header(struct http_request_s *request,
char const *key);
/**
* Iterate over the request headers.
*
* Each call will set key and val to the key and value of the next header.
*
* @param request The request.
* @param[out] key The key of the header.
* @param[out] value The key of the header.
* @param[inout] iter Should be initialized to 0 before calling. Pass back in
* with each consecutive call.
*
* @return 0 when there are no more headers.
*/
int http_request_iterate_headers(struct http_request_s *request,
struct http_string_s *key,
struct http_string_s *val, int *iter);
/**
* Stores an arbitrary userdata pointer for this request.
*
* This is not used by the library in any way and is strictly for you, the
* application programmer to make use of.
*
* @param request The request.
* @param data Opaque pointer to user data.
*/
void http_request_set_userdata(struct http_request_s *request, void *data);
/**
* Retrieve the opaque data pointer that was set with http_request_set_userdata.
*
* @param request The request.
*/
void *http_request_userdata(struct http_request_s *request);
/**
* Stores a server wide opaque pointer for future retrieval.
*
* This is not used by the library in any way and is strictly for you, the
* application programmer to make use of.
*
* @param server The server.
* @param data Opaque data pointer.
*/
void http_server_set_userdata(struct http_server_s *server, void *data);
/**
* Retrieve the server wide userdata pointer.
*
* @param request The request.
*/
void *http_request_server_userdata(struct http_request_s *request);
/**
* Sets how the request will handle it's connection
*
* By default the server will inspect the Connection header and the HTTP
* version to determine whether the connection should be kept alive or not.
* Use this function to override that behaviour to force the connection to
* keep-alive or close by passing in the HTTP_KEEP_ALIVE or HTTP_CLOSE
* directives respectively. This may provide a minor performance improvement
* in cases where you control client and server and want to always close or
* keep the connection alive.
*
* @param request The request.
* @param directive One of HTTP_KEEP_ALIVE or HTTP_CLOSE
*/
void http_request_connection(struct http_request_s *request, int directive);
/**
* Frees the buffer of a request.
*
* When reading in the HTTP request the server allocates a buffer to store
* the request details such as the headers, method, body, etc. By default this
* memory will be freed when http_respond is called. This function lets you
* free that memory before the http_respond call. This can be useful if you
* have requests that take a long time to complete and you don't require the
* request data. Accessing any http_string_s's will be invalid after this call.
*
* @param request The request to free the buffer of.
*/
void http_request_free_buffer(struct http_request_s *request);
/**
* Allocates an http response.
*
* This memory will be freed when http_respond is called.
*
* @return Allocated response.
*/
struct http_response_s *http_response_init();
/**
* Set the response status.
*
* Accepts values between 100 and 599 inclusive. Any other value will map to
* 500.
*
* @param response The response struct to set status on.
* @param status The HTTP status code.
*/
void http_response_status(struct http_response_s *response, int status);
/**
* Sets an HTTP response header.
*
* @param response The response struct to set the header on.
* @param key The null-terminated key of the header eg: Content-Type
* @param value The null-terminated value of the header eg: application/json
*/
void http_response_header(struct http_response_s *response, char const *key,
char const *value);
/**
* Set the response body.
*
* The caller is responsible for freeing any memory that
* may have been allocated for the body. It is safe to free this memory AFTER
* http_respond has been called. If responding with chunked transfer encoding
* this will become a single chunk. This procedure can be used again to set
* subsequent chunks.
*
* @param response The response struct to set the body for.
* @param body The body of the response.
* @param length The length of the body
*/
void http_response_body(struct http_response_s *response, char const *body,
int length);
/**
* Starts writing the response to the client.
*
* Any memory allocated for the response body or response headers is safe to
* free after this call. Adds the default HTTP response headers, Date and
* Connection.
*
* @param request The request to respond to.
* @param response The response to respond with.
*/
void http_respond(struct http_request_s *request,
struct http_response_s *response);
/**
* Writes a chunk to the client.
*
* The notify_done callback will be called when the write is complete. This call
* consumes the response so a new response will need to be initialized for each
* chunk. The response status of the request will be the response status that is
* set when http_respond_chunk is called the first time. Any headers set for the
* first call will be sent as the response headers. Transfer-Encoding header
* will automatically be set to chunked. Headers set for subsequent calls will
* be ignored.
*
* @param request The request to respond to.
* @param response The response to respond with.
* @param notify_done The callback that's used to signal user code that another
* chunk is ready to be written out.
*/
void http_respond_chunk(struct http_request_s *request,
struct http_response_s *response,
void (*notify_done)(struct http_request_s *));
/**
* Ends the chunked response.
*
* Used to signal end of transmission on chunked requests. Any headers set
* before this call will be included as what the HTTP spec refers to as
* 'trailers' which are essentially more response headers.
*
* @param request The request to respond to.
* @param response The response to respond with.
*/
void http_respond_chunk_end(struct http_request_s *request,
struct http_response_s *response);
/**
* Read a chunk of the request body.
*
* If a request has Transfer-Encoding: chunked or the body is too big to fit in
* memory all at once you cannot read the body in the typical way. Instead you
* need to call this function to read one chunk at a time. To check if the
* request requires this type of reading you can call the http_request_has_flag
* function to check if the HTTP_FLG_STREAMED flag is set. To read a streamed
* body you pass a callback that will be called when the chunk is ready. When
* the callback is called you can use 'http_request_chunk' to get the current
* chunk. When done with that chunk call this function again to request the
* next chunk. If the chunk has size 0 then the request body has been completely
* read and you can now respond.
*
* @param request The request.
* @param chunk_cb Callback for when the chunk is ready.
*/
void http_request_read_chunk(struct http_request_s *request,
void (*chunk_cb)(struct http_request_s *));
/**
* Returns the current chunk of the request body.
*
* This chunk is only valid until the next call to 'http_request_read_chunk'.
*
* @param request The request.
*
* @return The chunk data.
*/
struct http_string_s http_request_chunk(struct http_request_s *request);
#define http_request_read_body http_request_read_chunk
#ifdef __cplusplus
}
#endif
// Minimal example usage.
#ifdef HTTPSERVER_EXAMPLE
#define RESPONSE "Hello, World!"
void handle_request(struct http_request_s *request) {
struct http_response_s *response = http_response_init();
http_response_status(response, 200);
http_response_header(response, "Content-Type", "text/plain");
http_response_body(response, RESPONSE, sizeof(RESPONSE) - 1);
http_respond(request, response);
}
int main() {
struct http_server_s *server = http_server_init(8080, handle_request);
http_server_listen(server);
}
#endif
#endif