diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib-http/http-server.h | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/src/lib-http/http-server.h b/src/lib-http/http-server.h new file mode 100644 index 0000000..48708a3 --- /dev/null +++ b/src/lib-http/http-server.h @@ -0,0 +1,427 @@ +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +#include "http-common.h" +#include "http-auth.h" +#include "http-request.h" + +struct istream; +struct ostream; + +struct http_request; + +struct http_server; +struct http_server_resource; +struct http_server_request; +struct http_server_response; + +/* + * Server settings + */ + +struct http_server_settings { + const char *default_host; + + const char *rawlog_dir; + + /* SSL settings; if NULL, master_service_ssl_init() is used instead */ + const struct ssl_iostream_settings *ssl; + + /* The maximum time in milliseconds a client is allowed to be idle + before it is disconnected. */ + unsigned int max_client_idle_time_msecs; + + /* Maximum number of pipelined requests per connection (default = 1) */ + unsigned int max_pipelined_requests; + + /* Request limits */ + struct http_request_limits request_limits; + + /* The kernel send/receive buffer sizes used for the connection sockets. + Configuring this is mainly useful for the test suite. The kernel + defaults are used when these settings are 0. */ + size_t socket_send_buffer_size; + size_t socket_recv_buffer_size; + + /* Event to use for the http server. */ + struct event *event; + + /* Enable logging debug messages */ + bool debug; +}; + +/* + * Response + */ + +/* Connection data for an established HTTP tunnel */ +struct http_server_tunnel { + int fd_in, fd_out; + struct istream *input; + struct ostream *output; +}; + +typedef void +(*http_server_tunnel_callback_t)(void *context, + const struct http_server_tunnel *tunnel); + +/* Start creating the response for the request. This function can be called + only once for each request. */ +struct http_server_response * +http_server_response_create(struct http_server_request *req, + unsigned int status, const char *reason); + +/* Reference a server response */ +void http_server_response_ref(struct http_server_response *resp); +/* Unreference a server response. Returns TRUE if there are still more + references, FALSE if not. */ +bool http_server_response_unref(struct http_server_response **_resp); + +/* Add a custom header to the response. This can override headers that are + otherwise created implicitly. */ +void http_server_response_add_header(struct http_server_response *resp, + const char *key, const char *value); +/* Add a header permanently to the response. Even if another response is + created for the request, this header is kept. */ +void http_server_response_add_permanent_header(struct http_server_response *resp, + const char *key, const char *value); +/* Change the response code and text, cannot be used after submission */ +void http_server_response_update_status(struct http_server_response *resp, + unsigned int status, const char *reason); +/* Set the value of the "Date" header for the response using a time_t value. + Use this instead of setting it directly using + http_server_response_add_header() */ +void http_server_response_set_date(struct http_server_response *resp, + time_t date); +/* Assign an input stream for the outgoing payload of this response. The input + stream is read asynchronously while the response is sent to the client. */ +void http_server_response_set_payload(struct http_server_response *resp, + struct istream *input); +/* Assign payload data to the response. The data is copied to the request pool. + If your data is already durably allocated during the existence of the + response, you should consider using http_server_response_set_payload() with + a data input stream instead. This will avoid copying the data unnecessarily. + */ +void http_server_response_set_payload_data(struct http_server_response *resp, + const unsigned char *data, + size_t size); + +/* Get an output stream for the outgoing payload of this response. The output + stream operates asynchronously when blocking is FALSE. In that case the + flush callback is called once more data can be sent. When blocking is TRUE, + writing to the stream will block until all data is sent. In every respect, + it operates very similar to a normal file output stream. The response is + submitted implicitly when the stream is first used; e.g., when it is written, + flushed, or o_stream_set_flush_pending(ostream, TRUE) is called. */ +struct ostream * +http_server_response_get_payload_output(struct http_server_response *resp, + size_t max_buffer_size, bool blocking); + +/* Get the status code and reason string currently set for this response. */ +void http_server_response_get_status(struct http_server_response *resp, + int *status_r, const char **reason_r); +/* Get the total size of the response when sent over the connection. */ +uoff_t http_server_response_get_total_size(struct http_server_response *resp); +/* Add authentication challenge to the response. */ +void http_server_response_add_auth(struct http_server_response *resp, + const struct http_auth_challenge *chlng); +/* Add "Basic" authentication challenge to the response. */ +void http_server_response_add_auth_basic(struct http_server_response *resp, + const char *realm); + +/* Submit the response. It is queued for transmission to the client. */ +void http_server_response_submit(struct http_server_response *resp); +/* Submit the response and close the connection once it is sent. */ +void http_server_response_submit_close(struct http_server_response *resp); +/* Submit the response and turn the connection it is sent across into a tunnel + once it is sent successfully. The callback is called once that happens. */ +void http_server_response_submit_tunnel(struct http_server_response *resp, + http_server_tunnel_callback_t callback, + void *context); + +/* Submits response and blocks until provided payload is sent. Multiple calls + are allowed; payload is sent in chunks this way. Payload transmission is + finished with http_server_response_finish_payload(). If the sending fails, + returns -1 and sets resp=NULL to indicate that the response was freed, + otherwise returns 0 and resp is unchanged. + + An often more convenient ostream wrapper API is available as + http_server_response_get_payload_output() with blocking=TRUE. + */ +int http_server_response_send_payload(struct http_server_response **resp, + const unsigned char *data, size_t size); +/* Finish sending the payload. Always frees resp and sets it to NULL. + Returns 0 on success, -1 on error. */ +int http_server_response_finish_payload(struct http_server_response **resp); +/* Abort response payload transmission prematurely. This closes the associated + connection */ +void http_server_response_abort_payload(struct http_server_response **resp); + +/* + * Request + */ + +/* Get the parsed HTTP request information for this request. */ +const struct http_request * +http_server_request_get(struct http_server_request *req); + +/* Reference a server request */ +void http_server_request_ref(struct http_server_request *req); +/* Unreference a server request. Returns TRUE if there are still more + references, FALSE if not. */ +bool http_server_request_unref(struct http_server_request **_req); + +/* Set flag that determines whether the connection is closed after the + request is handled. */ +void http_server_request_connection_close(struct http_server_request *req, + bool close); + +/* Get the pool for this request. */ +pool_t http_server_request_get_pool(struct http_server_request *req); +/* Returns the response created for the request with + http_server_response_create(), or NULL if none. */ +struct http_server_response * +http_server_request_get_response(struct http_server_request *req); +/* Returns TRUE if request is finished either because a response was sent + or because the request was aborted. */ +bool http_server_request_is_finished(struct http_server_request *req); + +/* Add a header to any HTTP response created for the HTTP request. */ +void http_server_request_add_response_header(struct http_server_request *req, + const char *key, const char *value); + +/* Return input stream for the request's payload. Optionally, this stream + can be made blocking. Do *NOT* meddle with the FD of the http_request + payload to achieve the same, because protocol violations will result. + */ +struct istream * +http_server_request_get_payload_input(struct http_server_request *req, + bool blocking); + +/* Forward the incoming request payload to the provided output stream in the + background. Calls the provided callback once the payload was forwarded + successfully. If forwarding fails, the client is presented with an + appropriate error. If the payload size exceeds max_size, the client will + get a 413 error. Before the callback finishes, the application must either + have added a reference to the request or have submitted a response. */ +void http_server_request_forward_payload(struct http_server_request *req, + struct ostream *output, + uoff_t max_size, + void (*callback)(void *), + void *context); +#define http_server_request_forward_payload(req, output, max_size, \ + callback, context) \ + http_server_request_forward_payload(req, output, max_size, \ + (void(*)(void*))callback, TRUE ? context : \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context)))) +/* Forward the incoming request payload to the provided buffer in the + background. Behaves identical to http_server_request_forward_payload() + otherwise. */ +void http_server_request_buffer_payload(struct http_server_request *req, + buffer_t *buffer, uoff_t max_size, + void (*callback)(void *), + void *context); +#define http_server_request_buffer_payload(req, buffer, max_size, \ + callback, context) \ + http_server_request_buffer_payload(req, buffer, max_size, \ + (void(*)(void*))callback, TRUE ? context : \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context)))) +/* Handle the incoming request payload by calling the callback each time + more data is available. Payload reading automatically finishes when the + request payload is fully read. Before the final callback finishes, the + application must either have added a reference to the request or have + submitted a response. */ +void http_server_request_handle_payload(struct http_server_request *req, + void (*callback)(void *context), + void *context); +#define http_server_request_handle_payload(req, callback, context) \ + http_server_request_handle_payload(req,\ + (void(*)(void*))callback, TRUE ? context : \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context)))) + +/* Get the authentication credentials provided in this request. Returns 0 if + the Authorization header is absent, returns -1 when that header cannot be + parsed, and returns 1 otherwise */ +int http_server_request_get_auth(struct http_server_request *req, + struct http_auth_credentials *credentials); + +/* Send a failure response for the request with given status/reason. */ +void http_server_request_fail(struct http_server_request *req, + unsigned int status, const char *reason); +/* Send a failure response for the request with given status/reason + and close the connection. */ +void http_server_request_fail_close(struct http_server_request *req, + unsigned int status, const char *reason); +/* Send a failure response for the request with given status/reason/text. + The text is sent as the response payload, if appropriate. */ +void http_server_request_fail_text(struct http_server_request *req, + unsigned int status, const char *reason, + const char *format, ...) ATTR_FORMAT(4, 5); +/* Send an authentication failure response for the request with given reason. + The provided challenge is set in the WWW-Authenticate header of the + response. */ +void http_server_request_fail_auth(struct http_server_request *req, + const char *reason, + const struct http_auth_challenge *chlng) + ATTR_NULL(2); +/* Send a authentication failure response for the request with given reason. + The provided realm is used to construct an Basic challenge in the + WWW-Authenticate header of the response. */ +void http_server_request_fail_auth_basic(struct http_server_request *req, + const char *reason, const char *realm) + ATTR_NULL(2); + +/* Send a 405 failure response for a request with an unknown method. The allow + parameter is the value used for the mandatory "Allow" header in the response. + */ +void http_server_request_fail_bad_method(struct http_server_request *req, + const char *allow); + +/* Call the specified callback when HTTP request is destroyed. This happens + after one of the following: + + a) Response and its payload is fully sent, + b) Response was submitted, but it couldn't be sent due to disconnection or + some other error, + c) http_server_deinit() was called and the request was aborted + + Note client disconnection before response is submitted isn't visible to this. + The request payload reading is the responsibility of the caller, which also + must handle the read errors by submitting a failure response. */ +void http_server_request_set_destroy_callback(struct http_server_request *req, + void (*callback)(void *), + void *context); +#define http_server_request_set_destroy_callback(req, callback, context) \ + http_server_request_set_destroy_callback( \ + req, (void(*)(void*))callback, \ + (TRUE ? context : \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context))))) + +/* + * Connection + */ + +/* Connection statistics */ +struct http_server_stats { + /* The number of requests received and responses sent */ + unsigned int request_count, response_count; + /* Bytes sent and received accross the connection */ + uoff_t input, output; +}; + +/* Connection callbacks */ +struct http_server_callbacks { + /* Handle the server request. All requests must be sent back a response. + The response is sent either with http_server_request_fail*() or + http_server_response_submit*(). For simple requests you can send the + response back immediately. If you can't do that, you'll need to + reference the request (or the request payload input stream). Then the + code flow usually goes like this: + + - http_server_request_set_destroy_callback(destroy_callback) + - http_server_request_ref() + - <do whatever is needed to handle the request> + - http_server_response_create() + - http_server_response_set_payload() can be used especially with + istream-callback to create a large response without temp files. + - http_server_response_submit() triggers the destroy_callback + after it has finished sending the response and its payload. + - In destroy_callback: http_server_request_unref() and any other + necessary cleanup - the request handling is now fully finished. + */ + void (*handle_request)(void *context, struct http_server_request *req); + void (*handle_connect_request)(void *context, + struct http_server_request *req, + struct http_url *target); + + /* Called once the connection is destroyed. */ + void (*connection_destroy)(void *context, const char *reason); +}; + +/* Create a HTTP server connection object for the provided fd pair. The + callbacks structure is described above. */ +struct http_server_connection * +http_server_connection_create(struct http_server *server, + int fd_in, int fd_out, bool ssl, + const struct http_server_callbacks *callbacks, + void *context); +/* Reference the connection */ +void http_server_connection_ref(struct http_server_connection *conn); +/* Dereference the connection. Returns FALSE if unrefing destroyed the + connection entirely */ +bool http_server_connection_unref(struct http_server_connection **_conn); +/* Dereference and close the connection. The provided reason is passed to the + connection_destroy() callback. */ +void http_server_connection_close(struct http_server_connection **_conn, + const char *reason); +/* Get the current statistics for this connection */ +const struct http_server_stats * +http_server_connection_get_stats(struct http_server_connection *conn); + +/* Switch connection to a specific ioloop. */ +struct ioloop * +http_server_connection_switch_ioloop_to(struct http_server_connection *conn, + struct ioloop *ioloop); +/* Switch connection to the current ioloop. */ +struct ioloop * +http_server_connection_switch_ioloop(struct http_server_connection *conn); + +/* + * Resource + */ + +typedef void +(http_server_resource_callback_t)(void *context, + struct http_server_request *req, + const char *sub_path); + +struct http_server_resource * +http_server_resource_create(struct http_server *server, pool_t pool, + http_server_resource_callback_t *callback, + void *context); +#define http_server_resource_create(server, pool, callback, context) \ + http_server_resource_create(server, pool, \ + (http_server_resource_callback_t *)callback, \ + (TRUE ? context : \ + CALLBACK_TYPECHECK(callback, void (*)( \ + typeof(context), struct http_server_request *req, \ + const char *sub_path)))) +/* Resources are freed upon http_server_deinit(), so calling + http_server_resource_free() is only necessary when the resource needs to + disappear somewhere in the middle of the server lifetime. */ +void http_server_resource_free(struct http_server_resource **_res); + +pool_t http_server_resource_get_pool(struct http_server_resource *res) + ATTR_PURE; +const char * +http_server_resource_get_path(struct http_server_resource *res) ATTR_PURE; +struct event * +http_server_resource_get_event(struct http_server_resource *res) ATTR_PURE; + +void http_server_resource_add_location(struct http_server_resource *res, + const char *path); + +/* Call the specified callback when HTTP resource is destroyed. */ +void http_server_resource_set_destroy_callback(struct http_server_resource *res, + void (*callback)(void *), + void *context); +#define http_server_resource_set_destroy_callback(req, callback, context) \ + http_server_resource_set_destroy_callback(req, \ + (void(*)(void*))callback, context - \ + CALLBACK_TYPECHECK(callback, void (*)(typeof(context)))) + +/* + * Server + */ + +struct http_server *http_server_init(const struct http_server_settings *set); +void http_server_deinit(struct http_server **_server); + +/* Shut down the server; accept no new requests and drop connections once + they become idle */ +void http_server_shut_down(struct http_server *server); + +/* Switch this server to the current ioloop */ +void http_server_switch_ioloop(struct http_server *server); + +#endif |