diff options
Diffstat (limited to 'src/include/taler_mhd_lib.h')
-rw-r--r-- | src/include/taler_mhd_lib.h | 585 |
1 files changed, 561 insertions, 24 deletions
diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h index 7100ad95c..57d041758 100644 --- a/src/include/taler_mhd_lib.h +++ b/src/include/taler_mhd_lib.h @@ -27,6 +27,15 @@ #include <jansson.h> #include <microhttpd.h> #include "taler_error_codes.h" +#include "taler_util.h" +#include <gnunet/gnunet_mhd_compat.h> + + +/** + * Maximum POST request size. + */ +#define TALER_MHD_REQUEST_BUFFER_MAX (1024 * 1024 * 16) + /** * Global options for response generation. @@ -79,7 +88,7 @@ TALER_MHD_add_global_headers (struct MHD_Response *response); * @param[in,out] buf_size pointer to initial size of @a buf * @return #MHD_YES if @a buf was compressed */ -int +MHD_RESULT TALER_MHD_body_compress (void **buf, size_t *buf_size); @@ -90,11 +99,25 @@ TALER_MHD_body_compress (void **buf, * @param connection connection to check * @return #MHD_YES if 'deflate' compression is allowed */ -int +MHD_RESULT TALER_MHD_can_compress (struct MHD_Connection *connection); /** + * Check if @a mime matches the @a accept_pattern. For this function, the @a + * accept_pattern may include multiple values separated by ";". + * + * @param accept_pattern a mime pattern like "text/plain" + * or "image/STAR" or "text/plain; text/xml" + * @param mime the mime type to match + * @return true if @a mime matches the @a accept_pattern + */ +bool +TALER_MHD_xmime_matches (const char *accept_pattern, + const char *mime); + + +/** * Send JSON object as response. * * @param connection the MHD connection @@ -102,13 +125,28 @@ TALER_MHD_can_compress (struct MHD_Connection *connection); * @param response_code the http response code * @return MHD result code */ -int +MHD_RESULT TALER_MHD_reply_json (struct MHD_Connection *connection, const json_t *json, unsigned int response_code); /** + * Send JSON object as response, and free the @a json + * object. + * + * @param connection the MHD connection + * @param json the json object (freed!) + * @param response_code the http response code + * @return MHD result code + */ +MHD_RESULT +TALER_MHD_reply_json_steal (struct MHD_Connection *connection, + json_t *json, + unsigned int response_code); + + +/** * Function to call to handle the request by building a JSON * reply from a format string and varargs. * @@ -118,7 +156,7 @@ TALER_MHD_reply_json (struct MHD_Connection *connection, * @param ... varargs * @return MHD result code */ -int +MHD_RESULT TALER_MHD_reply_json_pack (struct MHD_Connection *connection, unsigned int response_code, const char *fmt, @@ -126,19 +164,60 @@ TALER_MHD_reply_json_pack (struct MHD_Connection *connection, /** + * Function to call to handle the request by building a JSON + * reply from varargs. + * + * @param connection the MHD connection to handle + * @param response_code HTTP response code to use + * @param ... varargs of JSON pack specification + * @return MHD result code + */ +#define TALER_MHD_REPLY_JSON_PACK(connection,response_code,...) \ + TALER_MHD_reply_json_steal (connection, GNUNET_JSON_PACK (__VA_ARGS__), \ + response_code) + + +/** * Send a response indicating an error. * * @param connection the MHD connection to use * @param ec error code uniquely identifying the error * @param http_status HTTP status code to use - * @param hint human readable hint about the error + * @param detail additional optional detail about the error * @return a MHD result code */ -int +MHD_RESULT TALER_MHD_reply_with_error (struct MHD_Connection *connection, unsigned int http_status, enum TALER_ErrorCode ec, - const char *hint); + const char *detail); + + +/** + * Send a response indicating an error. The HTTP status code is + * to be derived from the @a ec. + * + * @param connection the MHD connection to use + * @param ec error code uniquely identifying the error + * @param detail additional optional detail about the error + * @return a MHD result code + */ +MHD_RESULT +TALER_MHD_reply_with_ec (struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + const char *detail); + + +/** + * Produce HTTP "Date:" header. + * + * @param at time to write to @a date + * @param[out] date where to write the header, with + * at least 128 bytes available space. + */ +void +TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at, + char date[128]); /** @@ -152,6 +231,16 @@ TALER_MHD_make_json (const json_t *json); /** + * Make JSON response object and free @a json. + * + * @param json the json object, freed. + * @return MHD response object + */ +struct MHD_Response * +TALER_MHD_make_json_steal (json_t *json); + + +/** * Make JSON response object. * * @param fmt format string for pack @@ -164,15 +253,36 @@ TALER_MHD_make_json_pack (const char *fmt, /** + * Make JSON response object. + * + * @param ... varargs + * @return MHD response object + */ +#define TALER_MHD_MAKE_JSON_PACK(...) \ + TALER_MHD_make_json_steal (GNUNET_JSON_PACK (__VA_ARGS__)) + + +/** + * Pack Taler error code @a ec and associated hint into a + * JSON object. + * + * @param ec error code to pack + * @return packer array entries (two!) + */ +#define TALER_MHD_PACK_EC(ec) \ + GNUNET_JSON_pack_uint64 ("code", ec), \ + GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec)) + +/** * Create a response indicating an internal error. * * @param ec error code to return - * @param hint hint about the internal error's nature + * @param detail additional optional detail about the error, can be NULL * @return a MHD response object */ struct MHD_Response * TALER_MHD_make_error (enum TALER_ErrorCode ec, - const char *hint); + const char *detail); /** @@ -181,7 +291,7 @@ TALER_MHD_make_error (enum TALER_ErrorCode ec, * @param connection the MHD connection to use * @return a MHD result code */ -int +MHD_RESULT TALER_MHD_reply_request_too_large (struct MHD_Connection *connection); @@ -193,7 +303,7 @@ TALER_MHD_reply_request_too_large (struct MHD_Connection *connection); * @param url where to redirect for the sources * @return MHD result code */ -int +MHD_RESULT TALER_MHD_reply_agpl (struct MHD_Connection *connection, const char *url); @@ -209,7 +319,7 @@ TALER_MHD_reply_agpl (struct MHD_Connection *connection, * @param body_size number of bytes in @a body * @return MHD result code */ -int +MHD_RESULT TALER_MHD_reply_static (struct MHD_Connection *connection, unsigned int http_status, const char *mime_type, @@ -241,7 +351,7 @@ TALER_MHD_reply_static (struct MHD_Connection *connection, * (we could not even queue an error message, * close HTTP session with MHD_NO) */ -int +enum GNUNET_GenericReturnValue TALER_MHD_parse_post_json (struct MHD_Connection *connection, void **con_cls, const char *upload_data, @@ -262,7 +372,8 @@ TALER_MHD_parse_post_cleanup_callback (void *con_cls); /** * Parse JSON object into components based on the given field - * specification. + * specification. If parsing fails, we return an HTTP + * status code of 400 (#MHD_HTTP_BAD_REQUEST). * * @param connection the connection to send an error response to * @param root the JSON node to start the navigation at. @@ -274,13 +385,37 @@ TALER_MHD_parse_post_cleanup_callback (void *con_cls); * #GNUNET_NO if json is malformed, error response was generated * #GNUNET_SYSERR on internal error */ -int +enum GNUNET_GenericReturnValue TALER_MHD_parse_json_data (struct MHD_Connection *connection, const json_t *root, struct GNUNET_JSON_Specification *spec); /** + * Parse JSON object that we (the server!) generated into components based on + * the given field specification. The difference to + * #TALER_MHD_parse_json_data() is that this function will fail + * with an HTTP failure of 500 (internal server error) in case + * parsing fails, instead of blaming it on the client with a + * 400 (#MHD_HTTP_BAD_REQUEST). + * + * @param connection the connection to send an error response to + * @param root the JSON node to start the navigation at. + * @param spec field specification for the parser + * @return + * #GNUNET_YES if navigation was successful (caller is responsible + * for freeing allocated variable-size data using + * GNUNET_JSON_parse_free() when done) + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error + */ +enum GNUNET_GenericReturnValue +TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_JSON_Specification *spec); + + +/** * Parse JSON array into components based on the given field * specification. Generates error response on parse errors. * @@ -295,7 +430,7 @@ TALER_MHD_parse_json_data (struct MHD_Connection *connection, * #GNUNET_NO if json is malformed, error response was generated * #GNUNET_SYSERR on internal error */ -int +enum GNUNET_GenericReturnValue TALER_MHD_parse_json_array (struct MHD_Connection *connection, const json_t *root, struct GNUNET_JSON_Specification *spec, @@ -303,7 +438,201 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection, /** - * Extraxt fixed-size base32crockford encoded data from request. + * Extract optional "timeout_ms" argument from request. + * + * @param connection the MHD connection + * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, + * the current time plus the value given under "timeout_ms" otherwise + * @return #GNUNET_OK on success, #GNUNET_NO if an + * error was returned on @a connection (caller should return #MHD_YES) and + * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) + */ +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection, + struct GNUNET_TIME_Absolute *expiration); + + +/** + * Extract optional "timeout_ms" argument from request. + * Macro that *returns* #MHD_YES/#MHD_NO if the "timeout_ms" + * argument existed but failed to parse. + * + * @param connection the MHD connection + * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, + * the current time plus the value given under "timeout_ms" otherwise + */ +#define TALER_MHD_parse_request_timeout(connection,expiration) \ + do { \ + switch (TALER_MHD_parse_request_arg_timeout (connection, \ + expiration)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + case GNUNET_OK: \ + break; \ + } \ + } while (0) + + +/** + * Extract optional numeric limit argument from request. + * + * @param connection the MHD connection + * @param name name of the query parameter + * @param[out] off set to the offset, unchanged if the + * option was not given + * @return #GNUNET_OK on success, + * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and + * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) + */ +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection, + const char *name, + uint64_t *off); + + +/** + * Extract optional numeric argument from request. + * Macro that *returns* #MHD_YES/#MHD_NO if the + * requested argument existed but failed to parse. + * + * @param connection the MHD connection + * @param name name of the argument to parse + * @param[out] off set to the given numeric value, + * unchanged if value was not specified + */ +#define TALER_MHD_parse_request_number(connection,name,off) \ + do { \ + switch (TALER_MHD_parse_request_arg_number (connection, \ + name, \ + off)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + case GNUNET_OK: \ + break; \ + } \ + } while (0) + + +/** + * Extract optional signed numeric limit argument from request. + * + * @param connection the MHD connection + * @param name name of the query parameter + * @param[out] val set to the signed value, unchanged if the + * option was not given + * @return #GNUNET_OK on success, + * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and + * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) + */ +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_arg_snumber (struct MHD_Connection *connection, + const char *name, + int64_t *val); + + +/** + * Extract optional numeric argument from request. + * Macro that *returns* #MHD_YES/#MHD_NO if the + * requested argument existed but failed to parse. + * + * @param connection the MHD connection + * @param name name of the argument to parse + * @param[out] val set to the given numeric value, + * unchanged if value was not specified + */ +#define TALER_MHD_parse_request_snumber(connection,name,val) \ + do { \ + switch (TALER_MHD_parse_request_arg_snumber (connection, \ + name, \ + val)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + case GNUNET_OK: \ + break; \ + } \ + } while (0) + + +/** + * Extract optional amount argument from request. + * + * @param connection the MHD connection + * @param name name of the query parameter + * @param[out] val set to the amount, unchanged if the + * option was not given + * @return #GNUNET_OK on success, + * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and + * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) + */ +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection, + const char *name, + struct TALER_Amount *val); + + +/** + * Extract optional amount argument from request. + * Macro that *returns* #MHD_YES/#MHD_NO if the + * requested argument existed but failed to parse. + * + * @param connection the MHD connection + * @param name name of the argument to parse + * @param[out] val set to the given amount, + * unchanged if value was not specified + */ +#define TALER_MHD_parse_request_amount(connection,name,val) \ + do { \ + switch (TALER_MHD_parse_request_arg_amount (connection, \ + name, \ + val)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + case GNUNET_OK: \ + break; \ + } \ + } while (0) + + +/** + * Determines which of the given choices is preferred + * by the client. Parses an HTTP header such as + * "Accept:" or "Language:" to find entries matching + * the list of strings given in the variadic argument + * list. Returns the index of the preferred choice. + * + * @param connection HTTP request handle + * @param header type of HTTP header to evaluate + * @param ... NULL-terminated list of choices to + * check for in the header + * @return -1 if none of the given choices is in + * the header, -2 if the header is missing, + * otherwise index of preferred choice in + * the varargs list + */ +int +TALER_MHD_check_accept (struct MHD_Connection *connection, + const char *header, + ...); + + +/** + * Extract fixed-size base32crockford encoded data from request argument. * * Queues an error response to the connection if the parameter is missing or * invalid. @@ -312,16 +641,195 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection, * @param param_name the name of the parameter with the key * @param[out] out_data pointer to store the result * @param out_size expected size of @a out_data + * @param[out] present set to true if argument was found * @return * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_NO if the argument is malformed * #GNUNET_SYSERR on internal error (error response could not be sent) */ -int +enum GNUNET_GenericReturnValue TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, const char *param_name, void *out_data, - size_t out_size); + size_t out_size, + bool *present); + + +/** + * Extract fixed-size base32crockford encoded data from request header. + * + * Queues an error response to the connection if the parameter is missing or + * invalid. + * + * @param connection the MHD connection + * @param header_name the name of the HTTP header with the value + * @param[out] out_data pointer to store the result + * @param out_size expected size of @a out_data + * @param[out] present set to true if argument was found + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +enum GNUNET_GenericReturnValue +TALER_MHD_parse_request_header_data (struct MHD_Connection *connection, + const char *header_name, + void *out_data, + size_t out_size, + bool *present); + +/** + * Extract fixed-size base32crockford encoded data from request. + * + * @param connection the MHD connection + * @param name the name of the parameter with the key + * @param[out] val pointer to store the result, type must determine size + * @param[in,out] required pass true to require presence of this argument; if 'false' + * set to true if the argument was found + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +#define TALER_MHD_parse_request_arg_auto(connection,name,val,required) \ + do { \ + bool p; \ + switch (TALER_MHD_parse_request_arg_data (connection, name, \ + val, sizeof (*val), &p)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + return MHD_YES; \ + case GNUNET_OK: \ + if (required & (! p)) \ + return TALER_MHD_reply_with_error ( \ + connection, \ + MHD_HTTP_BAD_REQUEST, \ + TALER_EC_GENERIC_PARAMETER_MISSING, \ + name); \ + required = p; \ + break; \ + } \ + } while (0) + + +/** + * Extract required fixed-size base32crockford encoded data from request. + * + * @param connection the MHD connection + * @param name the name of the parameter with the key + * @param[out] val pointer to store the result, type must determine size + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +#define TALER_MHD_parse_request_arg_auto_t(connection,name,val) \ + do { \ + bool b = true; \ + TALER_MHD_parse_request_arg_auto (connection,name,val,b); \ + } while (0) + +/** + * Extract fixed-size base32crockford encoded data from request. + * + * @param connection the MHD connection + * @param name the name of the header with the key + * @param[out] val pointer to store the result, type must determine size + * @param[in,out] required pass true to require presence of this argument; if 'false' + * set to true if the argument was found + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +#define TALER_MHD_parse_request_header_auto(connection,name,val,required) \ + do { \ + bool p; \ + switch (TALER_MHD_parse_request_header_data (connection, name, \ + val, sizeof (*val), &p)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + return MHD_YES; \ + case GNUNET_OK: \ + if (required & (! p)) \ + return TALER_MHD_reply_with_error ( \ + connection, \ + MHD_HTTP_BAD_REQUEST, \ + TALER_EC_GENERIC_PARAMETER_MISSING, \ + name); \ + required = p; \ + break; \ + } \ + } while (0) + + +/** + * Extract required fixed-size base32crockford encoded data from request. + * + * @param connection the MHD connection + * @param name the name of the header with the key + * @param[out] val pointer to store the result, type must determine size + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +#define TALER_MHD_parse_request_header_auto_t(connection,name,val) \ + do { \ + bool b = true; \ + TALER_MHD_parse_request_header_auto (connection,name,val,b); \ + } while (0) + + +/** + * Check that the 'Content-Length' header is giving + * a length below @a max_len. If not, return an + * appropriate error response and return the + * correct #MHD_YES/#MHD_NO value from this function. + * + * @param connection the MHD connection + * @param max_len maximum allowed content length + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +enum GNUNET_GenericReturnValue +TALER_MHD_check_content_length_ (struct MHD_Connection *connection, + unsigned long long max_len); + + +/** + * Check that the 'Content-Length' header is giving + * a length below @a max_len. If not, return an + * appropriate error response and return the + * correct #MHD_YES/#MHD_NO value from this function. + * + * @param connection the MHD connection + * @param max_len maximum allowed content length + */ +#define TALER_MHD_check_content_length(connection,max_len) \ + do { \ + switch (TALER_MHD_check_content_length_ (connection, max_len)) \ + { \ + case GNUNET_SYSERR: \ + GNUNET_break (0); \ + return MHD_NO; \ + case GNUNET_NO: \ + GNUNET_break_op (0); \ + return MHD_YES; \ + case GNUNET_OK: \ + break; \ + } \ + } while (0) /** @@ -335,7 +843,7 @@ TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, * @param[out] unix_mode set to the mode to be used for @a unix_path * @return #GNUNET_OK on success */ -int +enum GNUNET_GenericReturnValue TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, uint16_t *rport, @@ -388,6 +896,35 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg, /** + * Start to run an event loop for @a daemon. + * Only one daemon can be running per process + * using this API. + * + * @param daemon the MHD service to run + */ +void +TALER_MHD_daemon_start (struct MHD_Daemon *daemon); + + +/** + * Stop running the event loop for MHD. + * + * @return the daemon that we were previously running, + * or NULL if none was active + */ +struct MHD_Daemon * +TALER_MHD_daemon_stop (void); + + +/** + * Trigger MHD daemon that is running. Needed when + * a connection was resumed. + */ +void +TALER_MHD_daemon_trigger (void); + + +/** * Prepared responses for legal documents * (terms of service, privacy policy). */ @@ -396,7 +933,7 @@ struct TALER_MHD_Legal; /** * Load set of legal documents as specified in @a cfg in section @a section - * where the Etag is given under the @param tagoption and the directory under + * where the Etag is given under the @a tagoption and the directory under * the @a diroption. * * @param cfg configuration to use @@ -431,7 +968,7 @@ TALER_MHD_legal_free (struct TALER_MHD_Legal *legal); * @param legal legal document to serve * @return MHD result code */ -int +MHD_RESULT TALER_MHD_reply_legal (struct MHD_Connection *conn, struct TALER_MHD_Legal *legal); @@ -443,7 +980,7 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn, * @param connection the MHD connection * @return MHD result code */ -int +MHD_RESULT TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection); #endif |