pax_global_header00006660000000000000000000000064134101702230014503gustar00rootroot0000000000000052 comment=47554d5b41e830350d2b7775e9419ded027ba23a ulfius-2.5.2/000077500000000000000000000000001341017022300130205ustar00rootroot00000000000000ulfius-2.5.2/.gitignore000066400000000000000000000015621341017022300150140ustar00rootroot00000000000000*.o *.so *.so.* *.a ulfius-cfg.h example_programs/auth_example/auth_client example_programs/auth_example/auth_server example_programs/injection_example/injection_example example_programs/proxy_example/proxy example_programs/request_example/client example_programs/request_example/mail example_programs/request_example/server example_programs/simple_example/simple_example example_programs/stream_example/stream_example example_programs/stream_example/stream_client example_programs/test_u_map/test_u_map example_programs/multiple_callbacks_example/multiple_callbacks_example example_programs/sheep_counter/sheep_counter example_programs/websocket_example/websocket_server example_programs/websocket_example/websocket_client example_programs/*/valgrind*.txt test/valgrind*.txt test/*.o test/core test/u_map test/framework test/websocket tools/uwsc/uwsc tools/uwsc/valgrind.txt *.log ulfius-2.5.2/.travis.yml000066400000000000000000000142711341017022300151360ustar00rootroot00000000000000# travis configuration file # Copyright 2018 Ilya Shipitsin # Nicolas Mora sudo: required language: c addons: apt: packages: [ libmicrohttpd-dev, libjansson-dev, check, libsubunit-dev, cppcheck ] matrix: include: - os: linux compiler: gcc - os: linux compiler: clang - env: LABEL=cppcheck os: linux script: - cppcheck --force --enable=warning,missingInclude --error-exitcode=1 . >build.log 2>&1 || (cat build.log && exit 1) script: - mkdir build - mkdir example_programs/build - cd build - cmake -DWITH_JOURNALD=off -DBUILD_ULFIUS_TESTING=on .. - make test || (cat Testing/Temporary/LastTest.log && false) - make package - sudo make install - cd ../example_programs/build - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=off -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=off -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=on -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=on -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=off -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=off -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=on -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=off -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=on -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=off -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=off -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=on -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=off -DWITH_JANSSON=on -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=off -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=off -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=on -DWITH_CURL=off .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cmake .. - make - cd ../../build - sudo make uninstall && rm -rf * - cmake -DWITH_JOURNALD=off -DWITH_YDER=on -DBUILD_ULFIUS_TESTING=on -DWITH_WEBSOCKET=on -DWITH_JANSSON=on -DWITH_CURL=on .. - make package - sudo make install - cd ../example_programs/build - rm -rf * - cd ../../ - make - make clean debug CURLFLAG=1 JANSSONFLAG=1 WEBSOCKETFLAG=1 YDERFLAG=1 - make clean debug CURLFLAG=1 JANSSONFLAG=1 YDERFLAG=1 - make clean debug CURLFLAG=1 WEBSOCKETFLAG=1 YDERFLAG=1 - make clean debug CURLFLAG=1 YDERFLAG=1 - make clean debug JANSSONFLAG=1 WEBSOCKETFLAG=1 YDERFLAG=1 - make clean debug CURLFLAG=1 JANSSONFLAG=1 YDERFLAG=1 - make clean debug CURLFLAG=1 WEBSOCKETFLAG=1 YDERFLAG=1 - make clean debug CURLFLAG=1 YDERFLAG=1 - make clean debug CURLFLAG=1 JANSSONFLAG=1 WEBSOCKETFLAG=1 - make clean debug CURLFLAG=1 JANSSONFLAG=1 - make clean debug CURLFLAG=1 WEBSOCKETFLAG=1 - make clean debug CURLFLAG=1 - make clean debug JANSSONFLAG=1 WEBSOCKETFLAG=1 - make clean debug CURLFLAG=1 JANSSONFLAG=1 - make clean debug CURLFLAG=1 WEBSOCKETFLAG=1 - make clean debug CURLFLAG=1 - make clean debug check ulfius-2.5.2/API.md000066400000000000000000002501601341017022300137570ustar00rootroot00000000000000# Ulfius API Documentation - [Header file](#header-file) - [Return values](#return-values) - [Memory management](#memory-management) - [Webservice initialization](#webservice-initialization) - [Instance structure](#instance-structure) - [Endpoint structure](#endpoint-structure) - [Multiple callback functions](#multiple-callback-functions) - [Multiple urls with similar pattern](#multiple-urls-with-similar-pattern) - [Start and stop webservice](#start-and-stop-webservice) - [Callback functions management](#callback-functions-management) - [Request structure](#request-structure) - [Response structure](#response-structure) - [Callback functions return value](#callback-functions-return-value) - [Use JSON in request and response body](#use-json-in-request-and-response-body) - [Additional functions](#additional-functions) - [Memory management](#memory-management) - [Character encoding](#character-encoding) - [Cookie management](#cookie-management) - [File upload](#file-upload) - [Streaming data](#streaming-data) - [Websockets communication](#websockets-communication) - [Websocket management](#websocket-management) - [Messages manipulation](#messages-manipulation) - [Server-side websocket](#server-side-websocket) - [Starting a websocket communication](#starting-a-websocket-communication) - [Websocket status](#websocket-status) - [Client-side websocket](#client-side-websocket) - [Prepare the request](#prepare-the-request) - [Open the websocket](#open-the-websocket) - [Websocket status](#websocket-status-1) - [Outgoing request functions](#outgoing-request-functions) - [Send HTTP request API](#send-http-request-api) - [Send SMTP request API](#send-http-request-api) - [struct _u_map API](#struct-_u_map-api) - [What's new in Ulfius 2.5?](#whats-new-in-ulfius-25) - [What's new in Ulfius 2.4?](#whats-new-in-ulfius-24) - [What's new in Ulfius 2.3?](#whats-new-in-ulfius-23) - [What's new in Ulfius 2.2?](#whats-new-in-ulfius-22) - [What's new in Ulfius 2.1?](#whats-new-in-ulfius-21) - [What's new in Ulfius 2.0?](#whats-new-in-ulfius-20) - [Multiple callback functions](#multiple-callback-functions-1) - [Keep only binary_body in struct _u_request and struct _u_response](#keep-only-binary_body-in-struct-_u_request-and-struct-_u_response) - [Websocket service](#websocket-service) - [Remove libjansson and libcurl hard dependency](#remove-libjansson-and-libcurl-hard-dependency) - [Ready-to-use callback functions](#ready-to-use-callback-functions) - [Update existing programs from Ulfius 2.0 to 2.1](#update-existing-programs-from-ulfius-20-to-21) - [Update existing programs from Ulfius 1.x to 2.0](#update-existing-programs-from-ulfius-1x-to-20) ## Header file Include file `ulfius.h` in your source file: ```C #include ``` On your linker command, add ulfius as a dependency library, e.g. `-lulfius` for gcc. ### Return values When specified, some functions return `U_OK` on success, and other values otherwise. `U_OK` is 0, other values are non-0 values. The defined return value list is the following: ```C #define U_OK 0 // No error #define U_ERROR 1 // Error #define U_ERROR_MEMORY 2 // Error in memory allocation #define U_ERROR_PARAMS 3 // Error in input parameters #define U_ERROR_LIBMHD 4 // Error in libmicrohttpd execution #define U_ERROR_LIBCURL 5 // Error in libcurl execution #define U_ERROR_NOT_FOUND 6 // Something was not found ``` ### Memory management Ulfius uses the memory allocation functions `malloc/realloc/calloc/free` by default, but you can overwrite this and use any other memory allocation functions of your choice. Use Orcania's functions `o_set_alloc_funcs` and `o_get_alloc_funcs` to set and get memory allocation functions. ```C void o_set_alloc_funcs(o_malloc_t malloc_fn, o_realloc_t realloc_fn, o_free_t free_fn); void o_get_alloc_funcs(o_malloc_t * malloc_fn, o_realloc_t * realloc_fn, o_free_t * free_fn); ``` To link your program with Orcania library, add `-lorcania` to your linking command. Data structures allocated have their specific cleanup functions. To free pointer allocated, you should use the function `u_free` that is intended to use your memory management functions. ```c /** * ulfius_clean_instance * * Clean memory allocated by a struct _u_instance * */ void ulfius_clean_instance(struct _u_instance * u_instance); /** * ulfius_clean_request * clean the specified request's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_request_full * return U_OK on success */ int ulfius_clean_request(struct _u_request * request); /** * ulfius_clean_response * clean the specified response's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_response_full * return U_OK on success */ int ulfius_clean_response(struct _u_response * response); /** * free the struct _u_map's inner components * return U_OK on success */ int u_map_clean(struct _u_map * u_map); /** * free data allocated by ulfius functions */ void u_free(void * data); ``` ### Webservice initialization Ulfius framework runs as an async task in the background. When initialized, a thread is executed in the background. This thread will listen to the specified port and dispatch the calls to the specified callback functions. Ulfius allows adding and removing new endpoints during the instance execution. To run a webservice, you must initialize a `struct _u_instance` and add your endpoints. #### Instance structure The `struct _u_instance` is defined as: ```C /** * * Structure of an instance * * Contains the needed data for an ulfius instance to work * * mhd_daemon: pointer to the libmicrohttpd daemon * status: status of the current instance, status are U_STATUS_STOP, U_STATUS_RUNNING or U_STATUS_ERROR * port: port number to listen to * bind_address: ip address to listen to (optional) * timeout: Timeout to close the connection because of inactivity between the client and the server * nb_endpoints: Number of available endpoints * default_auth_realm: Default realm on authentication error * endpoint_list: List of available endpoints * default_endpoint: Default endpoint if no other endpoint match the current url * default_headers: Default headers that will be added to all response->map_header * max_post_param_size: maximum size for a post parameter, 0 means no limit, default 0 * max_post_body_size: maximum size for the entire post body, 0 means no limit, default 0 * websocket_handler: handler for the websocket structure * file_upload_callback: callback function to manage file upload by blocks * file_upload_cls: any pointer to pass to the file_upload_callback function * mhd_response_copy_data: to choose between MHD_RESPMEM_MUST_COPY and MHD_RESPMEM_MUST_FREE * check_utf8: check that all parameters values in the request (url, header and post_body) * are valid utf8 strings, if a parameter value has non utf8 character, the value * will be ignored, default 1 * use_client_cert_auth: Internal variable use to indicate if the instance uses client certificate authentication * Do not change this value, available only if websocket support is enabled * */ struct _u_instance { struct MHD_Daemon * mhd_daemon; int status; unsigned int port; struct sockaddr_in * bind_address; unsigned int timeout; int nb_endpoints; char * default_auth_realm; struct _u_endpoint * endpoint_list; struct _u_endpoint * default_endpoint; struct _u_map * default_headers; size_t max_post_param_size; size_t max_post_body_size; void * websocket_handler; int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls); void * file_upload_cls; int mhd_response_copy_data; int check_utf8; #ifndef U_DISABLE_WEBSOCKET int use_client_cert_auth; #endif }; ``` In the `struct _u_instance` structure, the element `port` must be set to the port number you want to listen to, the element `bind_address` is used if you want to listen only to a specific IP address. The element `mhd_daemon` is used by the framework, don't modify it. You can use the functions `ulfius_init_instance` and `ulfius_clean_instance` to facilitate the manipulation of the structure: ```C /** * ulfius_init_instance * * Initialize a struct _u_instance * with default values * return U_OK on success */ int ulfius_init_instance(struct _u_instance * u_instance, int port, struct sockaddr_in * bind_address, const char * default_auth_realm); /** * ulfius_clean_instance * * Clean memory allocated by a struct _u_instance * */ void ulfius_clean_instance(struct _u_instance * u_instance); ``` #### Endpoint structure The `struct _u_endpoint` is defined as: ```C /** * * Structure of an endpoint * * Contains all informations needed for an endpoint * http_method: http verb (GET, POST, PUT, etc.) in upper case * url_prefix: prefix for the url (optional) * url_format: string used to define the endpoint format * separate words with / * to define a variable in the url, prefix it with @ or : * example: /test/resource/:name/elements * on an url_format that ends with '*', the rest of the url will not be tested * priority: endpoint priority in descending order (0 is the higher priority) * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in callback_function * */ struct _u_endpoint { char * http_method; char * url_prefix; char * url_format; uint priority; int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) struct _u_response * response, // Output parameters (set by the user) void * user_data); void * user_data; }; ``` Some functions help you facilitate endpoints manipulation: ```C /** * Add a struct _u_endpoint * to the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint: pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list * return U_OK on success */ int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint); /** * Add a struct _u_endpoint * to the specified u_instance with its values specified * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * http_method: http verb (GET, POST, PUT, etc.) in upper case * url_prefix: prefix for the url (optional) * url_format: string used to define the endpoint format * separate words with / * to define a variable in the url, prefix it with @ or : * example: /test/resource/:name/elements * on an url_format that ends with '*', the rest of the url will not be tested * priority: endpoint priority in descending order (0 is the higher priority) * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in callback_function * return U_OK on success */ int ulfius_add_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format, uint priority, int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) struct _u_response * response, // Output parameters (set by the user) void * user_data), void * user_data); /** * Add a struct _u_endpoint * list to the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint_list: pointer to an array of struct _u_endpoint ending with a ulfius_empty_endpoint() that will be copied in the u_instance endpoint_list * return U_OK on success */ int ulfius_add_endpoint_list(struct _u_instance * u_instance, const struct _u_endpoint ** u_endpoint_list); /** * Remove a struct _u_endpoint * from the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint: pointer to a struct _u_endpoint that will be removed in the u_instance endpoint_list * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match * If no endpoint is found, return U_ERROR_NOT_FOUND * return U_OK on success */ int ulfius_remove_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint); /** * Remove a struct _u_endpoint * from the specified u_instance * using the specified values used to identify an endpoint * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * http_method: http_method used by the endpoint * url_prefix: url_prefix used by the endpoint * url_format: url_format used by the endpoint * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match * If no endpoint is found, return U_ERROR_NOT_FOUND * return U_OK on success */ int ulfius_remove_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format); /** * ulfius_set_default_callback_function * Set the default callback function * This callback will be called if no endpoint match the url called * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in the callback function * to remove a default callback function, call ulfius_set_default_callback_function with NULL parameter for callback_function * return U_OK on success */ int ulfius_set_default_callback_function(struct _u_instance * u_instance, int (* callback_function)(const struct _u_request * request, struct _u_response * response, void * user_data), void * user_data); ``` HTTP Method can be an existing or not existing method, or `*` for any method. You must specify a url_prefix, a url_format or both, callback_function is mandatory, user_data is optional. If you fill your array of endoints manually, your `struct _u_endpoint` array **MUST** end with an empty `struct _u_endpoint`. You can manually declare an endpoint or use the dedicated functions as `int ulfius_add_endpoint` or `int ulfius_add_endpoint_by_val`. It's recommended to use the dedicated functions to fill this array though. If you manipulate the attribute `u_instance.endpoint_list`, you must end the list with an empty endpoint (see `const struct _u_endpoint * ulfius_empty_endpoint()`), and you must set the attribute `u_instance.nb_endpoints` accordingly. Also, you must use dynamically allocated values (`malloc`) for attributes `http_method`, `url_prefix` and `url_format`. #### Multiple callback functions Ulfius allows multiple callbacks for the same endpoint. This is helpful when you need to execute several actions in sequence, for example check authentication, get resource, set cookie, then gzip response body. That's also why a priority must be set for each callback. The priority is in descending order, which means that it starts with 0 (highest priority) and priority decreases when priority number increases. There is no more signification to the priority number, which means you can use any incrementation of your choice. `Warning`: Having 2 callback functions with the same priority number will result in an undefined execution order result. To help passing parameters between callback functions of the same request, the value `struct _u_response.shared_data` can bse used. But it will not be allocated or freed by the framework, the program using this variable must free by itself. #### Multiple urls with similar pattern If you need to differentiate multiple urls with similar pattern, you can use priorities among multiple callback function. For example, if you have 2 endpoints with the following patterns: 1- `/example/:id` 2- `/example/findByStatus` You'll probably need the callback referred in 2- to be called and the callback referred in 1- not when the url called is the exact pattern as in 2-. Nevertheless, you'll need callback referred in 1- in all the other cases. In that case, you'll have to set a higher priority to the endpoint with the url 2- and return its callback function with the value `U_CALLBACK_COMPLETE`. Remember, if the first callback returns `U_CALLBACK_CONTINUE`, the second callback will be called afterwards. ```C int callback_example_find_by_status(const struct _u_request * request, struct _u_response * response, void * user_data) { /* do something here... */ return U_CALLBACK_COMPLETE; } int callback_example_by_id(const struct _u_request * request, struct _u_response * response, void * user_data) { /* do something else there... */ return U_CALLBACK_CONTINUE; } int main() { /* initialize program and instance */ ulfius_add_endpoint_by_val(instance, "GET", NULL, "/example/:id", 1, &callback_example_by_id, my_user_data); ulfius_add_endpoint_by_val(instance, "GET", NULL, "/example/findByStatus", 0, &callback_example_find_by_status, my_user_data); /* start instance and run program */ } ``` ### Start and stop webservice #### Start webservice The starting point function are `ulfius_start_framework`, `ulfius_start_secure_framework` or `ulfius_start_secure_ca_trust_framework`: ```C /** * ulfius_start_framework * Initializes the framework and run the webservice based on the parameters given * return truze if no error * * u_instance: pointer to a struct _u_instance that describe its port and bind address * return U_OK on success */ int ulfius_start_framework(struct _u_instance * u_instance); /** * ulfius_start_secure_framework * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection * * u_instance: pointer to a struct _u_instance that describe its port and bind address * key_pem: private key for the server * cert_pem: server certificate * return U_OK on success */ int ulfius_start_secure_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem); /** * ulfius_start_secure_ca_trust_framework * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection * And using a root server to authenticate client connections * * u_instance: pointer to a struct _u_instance that describe its port and bind address * key_pem: private key for the server * cert_pem: server certificate * root_ca_pem: client root CA you're willing to trust for this instance * return U_OK on success */ int ulfius_start_secure_ca_trust_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem, const char * root_ca_pem); ``` In your program, where you want to start the web server, execute the function `ulfius_start_framework(struct _u_instance * u_instance)` for a non-secure http connection. Use the function `ulfius_start_secure_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem)` for a secure https connection, using a valid private key and a valid corresponding server certificate, see openssl documentation for certificate generation. Finally, use the function `int ulfius_start_secure_ca_trust_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem, const char * root_ca_pem)` to start a secure https connection and be able to authenticate clients with a certificate. Those function accept the previously declared `instance` as first parameter. You can reuse the same callback function as much as you want for different endpoints. On success, these functions returns `U_OK`, otherwise an error code. Note: for security concerns, after running `ulfius_start_secure_framework` or `ulfius_start_secure_ca_trust_framework`, you can free the parameters `key_pem`, `cert_pem` and `root_ca_pem` if you want to. #### Stop webservice To stop the webservice, call the following function: ```C /** * ulfius_stop_framework * * Stop the webservice * u_instance: pointer to a struct _u_instance that describe its port and bind address * return U_OK on success */ int ulfius_stop_framework(struct _u_instance * u_instance); ``` ### Callback functions management The callback function is the function executed when a user calls an endpoint managed by your webservice (as defined in your `struct _u_endpoint` list). The callback function has the following signature: ```C int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) struct _u_response * response, // Output parameters (set by the user) void * user_data); ``` In the callback function definition, the variables `request` and `response` will be initialized by the framework, and the `user_data` variable will be assigned to the user_data defined in your endpoint list definition. #### Request structure The request variable is defined as: ```C /** * * Structure of request parameters * * Contains request data * http_protocol: http protocol used (1.0 or 1.1) * http_verb: http method (GET, POST, PUT, DELETE, etc.), use '*' to match all http methods * http_url: url used to call this callback function or full url to call when used in a ulfius_send_http_request * proxy: proxy address to use for outgoing connections, used by ulfius_send_http_request * check_server_certificate: do not check server certificate and hostname if false (default true), used by ulfius_send_http_request * timeout connection timeout used by ulfius_send_http_request, default is 0 * client_address: IP address of the client * auth_basic_user: basic authtication username * auth_basic_password: basic authtication password * map_url: map containing the url variables, both from the route and the ?key=value variables * map_header: map containing the header variables * map_cookie: map containing the cookie variables * map_post_body: map containing the post body variables (if available) * binary_body: pointer to raw body * binary_body_length: length of raw body * client_cert: x509 certificate of the client if the instance uses client certificate authentication and the client is authenticated * available only if websocket support is enabled * client_cert_file: path to client certificate file for sending http requests with certificate authentication * available only if websocket support is enabled * client_key_file: path to client key file for sending http requests with certificate authentication * available only if websocket support is enabled * client_key_password: password to unlock client key file * available only if websocket support is enabled */ struct _u_request { char * http_protocol; char * http_verb; char * http_url; char * proxy; int check_server_certificate; long timeout; struct sockaddr * client_address; char * auth_basic_user; char * auth_basic_password; struct _u_map * map_url; struct _u_map * map_header; struct _u_map * map_cookie; struct _u_map * map_post_body; void * binary_body; size_t binary_body_length; #ifndef U_DISABLE_WEBSOCKET gnutls_x509_crt_t client_cert; char * client_cert_file; char * client_key_file; char * client_key_password; #endif }; ``` #### Response structure The response variable is defined as: ```C /** * * Structure of response parameters * * Contains response data that must be set by the user * status: HTTP status code (200, 404, 500, etc) * protocol: HTTP Protocol sent * map_header: map containing the header variables * nb_cookies: number of cookies sent * map_cookie: array of cookies sent * auth_realm: realm to send to the client on authenticationb failed * binary_body: a void * containing a raw binary content * binary_body_length: the length of the binary_body * stream_callback: callback function to stream data in response body * stream_callback_free: callback function to free data allocated for streaming * stream_size: size of the streamed data (U_STREAM_SIZE_UNKOWN if unknown) * stream_block_size: size of each block to be streamed, set according to your system * stream_user_data: user defined data that will be available in your callback stream functions * websocket_handle: handle for websocket extension * shared_data: any data shared between callback functions, must be allocated and freed by the callback functions * timeout: Timeout in seconds to close the connection because of inactivity between the client and the server * */ struct _u_response { long status; char * protocol; struct _u_map * map_header; unsigned int nb_cookies; struct _u_cookie * map_cookie; char * auth_realm; void * binary_body; size_t binary_body_length; ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max); void (* stream_callback_free) (void * stream_user_data); uint64_t stream_size; size_t stream_block_size; void * stream_user_data; void * websocket_handle; void * shared_data; unsigned int timeout; }; ``` In the response variable set by the framework to the callback function, the structure is initialized with no data. The user can set the `binary_body` before the return statement, or no response body at all if no need. If a `binary_body` is set, its size must be set to `binary_body_length`. `binary_body` is free'd by the framework when the response has been sent to the client, so you must use dynamically allocated values. If no status is set, status 200 will be sent to the client. Some functions are dedicated to handle the response: ```C /** * ulfius_add_header_to_response * add a header to the response * return U_OK on success */ int ulfius_add_header_to_response(struct _u_response * response, const char * key, const char * value); /** * ulfius_set_string_body_response * Add a string body to a response * body must end with a '\0' character * return U_OK on success */ int ulfius_set_string_body_response(struct _u_response * response, const uint status, const char * body); /** * ulfius_set_binary_response * Add a binary body to a response * return U_OK on success */ int ulfius_set_binary_response(struct _u_response * response, const uint status, const char * body, const size_t length); /** * ulfius_set_empty_body_response * Set an empty response with only a status * return U_OK on success */ int ulfius_set_empty_body_response(struct _u_response * response, const uint status); /** * ulfius_set_stream_response * Set an stream response with a status * return U_OK on success */ int ulfius_set_stream_response(struct _u_response * response, const uint status, ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max); void (* stream_callback_free) (void * stream_user_data), uint64_t stream_size, size_t stream_block_size, void * stream_user_data); /** * Set a websocket in the response * You must set at least websocket_manager_callback or websocket_incoming_message_callback * @Parameters * response: struct _u_response to send back the websocket initialization, mandatory * websocket_protocol: list of protocols, separated by a comma, or NULL if all protocols are accepted * websocket_extensions: list of extensions, separated by a comma, or NULL if all extensions are accepted * websocket_manager_callback: callback function called right after the handshake acceptance, optional * websocket_manager_user_data: any data that will be given to the websocket_manager_callback, optional * websocket_incoming_message_callback: callback function called on each incoming complete message, optional * websocket_incoming_user_data: any data that will be given to the websocket_incoming_message_callback, optional * websocket_onclose_callback: callback function called right before closing the websocket, must be complete for the websocket to close * websocket_onclose_user_data: any data that will be given to the websocket_onclose_callback, optional * @Return value: U_OK on success */ int ulfius_set_websocket_response(struct _u_response * response, const char * websocket_protocol, const char * websocket_extensions, void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data), void * websocket_manager_user_data, void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data), void * websocket_incoming_user_data, void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data), void * websocket_onclose_user_data); ``` #### Callback functions return value The callback returned value can have the following values: - `U_CALLBACK_CONTINUE`: The framework can transfer the request and the response to the next callback function in priority order if there is one, or complete the transaction and send back the response to the client. - `U_CALLBACK_COMPLETE`: The framework must complete the transaction and send the response to the client without calling any further callback function. - `U_CALLBACK_UNAUTHORIZED`: The framework must complete the transaction without calling any further callback function and send an unauthorized response to the client with the status 401, the body specified and the `auth_realm` value if specified. - `U_CALLBACK_ERROR`: An error occured during execution, the framework will complete the transaction without calling any further callback function and send an error 500 to the client. Except for the return values `U_CALLBACK_UNAUTHORIZED` and `U_CALLBACK_ERROR`, the callback return value isn't useful to specify the response sent back to the client. Use the `struct _u_response` variable in your callback function to set all values in the HTTP response. #### Use JSON in request and response body In Ulfius 2.0, hard dependency with `libjansson` has been removed, the jansson library is now optional but enabled by default. If you want to remove JSON dependency, build Ulfius library using Makefile with the flag `JANSSONFLAG=-DU_DISABLE_JANSSON` or with CMake with th option `-DWITH_WEBSOCKET=off`. ``` $ make JANSSONFLAG=-DU_DISABLE_JANSSON # Makefile $ cmake -DWITH_WEBSOCKET=off # CMake ``` if JSON is enabled, the following functions are available in Ulfius: ```C /** * ulfius_get_json_body_request * Get JSON structure from the request body if the request is valid * In case of an error in getting or parsing JSON data in the request, * the structure json_error_t * json_error will be filled with an error * message if json_error is not NULL */ json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error); /** * ulfius_set_json_body_request * Add a json_t body to a request * return U_OK on success */ int ulfius_set_json_body_request(struct _u_request * request, json_t * body); /** * ulfius_get_json_body_response * Get JSON structure from the response body if the request is valid * In case of an error in getting or parsing JSON data in the request, * the structure json_error_t * json_error will be filled with an error * message if json_error is not NULL */ json_t * ulfius_get_json_body_response(struct _u_response * response, json_error_t * json_error); /** * ulfius_set_json_body_response * Add a json_t body to a response * return U_OK on success */ int ulfius_set_json_body_response(struct _u_response * response, const uint status, const json_t * body); ``` The `jansson` api documentation is available at the following address: [Jansson documentation](https://jansson.readthedocs.org/). Note: According to the [JSON RFC section 6](https://tools.ietf.org/html/rfc4627#section-6), the MIME media type for JSON text is `application/json`. Thus, if there is no HTTP header specifying JSON content-type, the functions `ulfius_get_json_body_request` and `ulfius_get_json_body_response` will return NULL. #### Additional functions In addition with manipulating the raw parameters of the structures, you can use the `_u_request` and `_u_response` structures by using specific functions designed to facilitate their use and memory management: ```C /** * ulfius_init_request * Initialize a request structure by allocating inner elements * return U_OK on success */ int ulfius_init_request(struct _u_request * request); /** * ulfius_clean_request * clean the specified request's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_request_full * return U_OK on success */ int ulfius_clean_request(struct _u_request * request); /** * ulfius_clean_request_full * clean the specified request and all its elements * return U_OK on success */ int ulfius_clean_request_full(struct _u_request * request); /** * ulfius_init_response * Initialize a response structure by allocating inner elements * return U_OK on success */ int ulfius_init_response(struct _u_response * response); /** * ulfius_clean_response * clean the specified response's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_response_full * return U_OK on success */ int ulfius_clean_response(struct _u_response * response); /** * ulfius_clean_response_full * clean the specified response and all its elements * return U_OK on success */ int ulfius_clean_response_full(struct _u_response * response); /** * ulfius_copy_response * Copy the source response elements into the des response * return U_OK on success */ int ulfius_copy_response(struct _u_response * dest, const struct _u_response * source); /** * ulfius_clean_cookie * clean the cookie's elements * return U_OK on success */ int ulfius_clean_cookie(struct _u_cookie * cookie); /** * Copy the cookie source elements into dest elements * return U_OK on success */ int ulfius_copy_cookie(struct _u_cookie * dest, const struct _u_cookie * source); /** * create a new request based on the source elements * returned value must be free'd after use */ struct _u_request * ulfius_duplicate_request(const struct _u_request * request); /** * create a new response based on the source elements * return value must be free'd after use */ struct _u_response * ulfius_duplicate_response(const struct _u_response * response); ``` #### Memory management The Ulfius framework will automatically free the variables referenced by the request and responses structures, except for `struct _u_response.shared_data`, so you must use dynamically allocated values for the response pointers. #### Character encoding You may be careful with characters encoding if you use non UTF8 characters in your application or webservice source code, and especially if you use different encodings in the same application. Ulfius may not work properly. #### Cookie management The map_cookie structure will contain a set of key/values for the cookies. The cookie structure is defined as ```C /** * struct _u_cookie * the structure containing the response cookie parameters */ struct _u_cookie { char * key; char * value; char * expires; uint max_age; char * domain; char * path; int secure; int http_only; int same_site; }; ``` You can use the functions `ulfius_add_cookie_to_response` or `ulfius_add_same_site_cookie_to_response` in your callback function to facilitate cookies management. Thess functions are defined as: ```C /** * ulfius_add_cookie_to_response * add a cookie to the cookie map * return U_OK on success */ int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const uint max_age, const char * domain, const char * path, const int secure, const int http_only); /** * ulfius_add_same_site_cookie_to_response * add a cookie to the cookie map with a SameSite attribute * the same_site parameter must have one of the following values: * - U_COOKIE_SAME_SITE_NONE - No SameSite attribute * - U_COOKIE_SAME_SITE_STRICT - SameSite attribute set to 'Strict' * - U_COOKIE_SAME_SITE_LAX - SameSite attribute set to 'Lax' * return U_OK on success */ int ulfius_add_same_site_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age, const char * domain, const char * path, const int secure, const int http_only, const int same_site); ``` ### File upload Ulifius allows file upload to the server. Beware that an uploaded file will be stored in the request object in memory, so uploading large files may dramatically slow the application or even crash it, depending on your system. An uploaded file is stored in the `request->map_body` structure. You can use `u_map_get_length` to get the exact length of the file as it may not be a string format. If you want to limit the size of a post parameter, if you want to limit the file size for example, set the value `struct _u_instance.max_post_param_size`. Files or post data exceeding this size will be truncated to the size `struct _u_instance.max_post_param_size`. If this parameter is 0, then no limit is set. Default value is 0. If you want to handle file upload yourself, you can intercept the file upload process with your own callback function. Before running the webservice with `ulfius_start_framework`, you must call the function `ulfius_set_upload_file_callback_function` with a pointer to your file upload callback function. By using this method, the specified callback function will be executed as much as needed with a chunk of the file upload each time. This function `ulfius_set_upload_file_callback_function` has the following prototype: ```C /** * ulfius_set_upload_file_callback_function * * Set the callback function to handle file upload * Used to facilitate large files upload management * The callback function file_upload_callback will be called * multiple times, with the uploaded file in striped in parts * * Warning: If this function is used, all the uploaded files * for the instance will be managed via this function, and they * will no longer be available in the struct _u_request in the * ulfius callback function afterwards. * * Thanks to Thad Phetteplace for the help on this feature * * u_instance: pointer to a struct _u_instance that describe its port and bind address * file_upload_callback: Pointer to a callback function that will handle all file uploads * cls: a pointer that will be passed to file_upload_callback each tim it's called */ int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance, int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls), void * cls); ``` This callback function will be called before all the other callback functions, and be aware that not all parameters, especially url parameters, will be present during the file upload callback function executions. See `examples/sheep_counter` for a file upload example. ### Streaming data If you need to stream data, i.e. send a variable and potentially large amount of data, you can define and use `stream_callback_function` in the `struct _u_response`. Not that if you stream data to the client, any data that was in the `response->binary_body` will be ignored. You must at least set the function pointer `struct _u_response.stream_callback` to stream data. Set `stream_size` to U_STREAM_SIZE_UNKOWN if you don't know the size of the data you need to send, like in audio stream for example. Set `stream_block_size` according to you system resources to avoid out of memory errors, also, set `stream_callback_free` with a pointer to a function that will free values allocated by your stream callback function, as a `close()` file for example, and finally, you can set `stream_user_data` to a pointer. You can use the function `ulfius_set_stream_response` to set those parameters. The prototype of the `stream_callback` function is the following: ```C ssize_t stream_callback (void * stream_user_data, // Your predefined user_data uint64_t offset, // the position of the current data to send char * out_buf, // The output buffer to fill with data size_t max); // the max size of data to be put in the out_buf ``` The return value must be the size of the data put in `out_buf`. This function will be called over and over in loop as long as the client has the connection opened. If you want to close the stream from the server side, return `U_STREAM_END` in the `stream_callback` function. If a problem occured, you can close the connection with a `U_STREAM_ERROR` return value. While the `stream_callback_free` function is as simple as: ```C void stream_callback_free (void * stream_user_data); ``` Check the application `stream_example` in the example folder. ### Websockets communication The websocket protocol is defined in the [RFC6455](https://tools.ietf.org/html/rfc6455). A websocket is a full-duplex communication layer between a server and a client initiated by a HTTP request. Once the websocket handshake is complete between the client and the server, the tcp socket between them is kept open and messages in a specific format can be exchanged. Any side of the socket can send a message to the other side, which allows the server to push messages to the client. Ulfius implements websocket communication, both server-side and client-side. The following chapter will describe how to create a websocket service or a websocket client by using callback functions. The framework will handle sending and receiving messages with the clients, and your application will deal with high level functions to facilitate the communication process. #### Websocket management During the websocket connection, you can either send messages, read the incoming messages, close the connection, or wait until the connection is closed by the client or by a network problem. #### Messages manipulation A websocket message has the following structure: ```C /** * websocket message structure * contains all the data of a websocket message * and the timestamp of when it was sent of received */ struct _websocket_message { time_t datestamp; // datestamp when the message was transmitted uint8_t opcode; // opcode of the message: U_WEBSOCKET_OPCODE_TEXT, U_WEBSOCKET_OPCODE_BINARY, U_WEBSOCKET_OPCODE_PING, U_WEBSOCKET_OPCODE_PONG uint8_t has_mask; // Flag to specify if the message has a mask uint8_t mask[4]; // mask size_t data_len; // Length of the data payload char * data; // data payload }; ``` The different opcode values available are the following: ```C U_WEBSOCKET_OPCODE_TEXT U_WEBSOCKET_OPCODE_BINARY U_WEBSOCKET_OPCODE_CLOSE U_WEBSOCKET_OPCODE_PING ``` If you want to send a message to the client, you must use the dedicated functions `ulfius_websocket_send_message` or `ulfius_websocket_send_fragmented_message`: ```C /** * Send a message in the websocket * Return U_OK on success */ int ulfius_websocket_send_message(struct _websocket_manager * websocket_manager, const uint8_t opcode, const uint64_t data_len, const char * data); /** * Send a fragmented message in the websocket * each fragment size will be at most fragment_len * Return U_OK on success */ int ulfius_websocket_send_fragmented_message(struct _websocket_manager * websocket_manager, const uint8_t opcode, const uint64_t data_len, const char * data, const size_t fragment_len); ``` To get the first message of the incoming or outcoming if you need to with `ulfius_websocket_pop_first_message`, this will remove the first message of the list, and return it as a pointer. You must free the message using the function `ulfius_clear_websocket_message` after use: ```C /** * Return the first message of the message list * Return NULL if message_list has no message * Returned value must be cleared after use */ struct _websocket_message * ulfius_websocket_pop_first_message(struct _websocket_message_list * message_list); /** * Clear data of a websocket message */ void ulfius_clear_websocket_message(struct _websocket_message * message); ``` ##### Fragmented messages limitation in browsers It seems that some browsers like Firefox or Chromium don't like to receive fragmented messages, they will close the connection with a fragmented message is received. Use `ulfius_websocket_send_fragmented_message` with caution then. #### Server-side websocket ##### Opening a websocket communication To start a websocket communication between the client and your application, you must use the dedicated function `ulfius_start_websocket_cb` with proper values: ```C /** * Set a websocket in the response * You must set at least websocket_manager_callback or websocket_incoming_message_callback * @Parameters * response: struct _u_response to send back the websocket initialization, mandatory * websocket_protocol: list of protocols, separated by a comma, or NULL if all protocols are accepted * websocket_extensions: list of extensions, separated by a comma, or NULL if all extensions are accepted * websocket_manager_callback: callback function called right after the handshake acceptance, optional * websocket_manager_user_data: any data that will be given to the websocket_manager_callback, optional * websocket_incoming_message_callback: callback function called on each incoming complete message, optional * websocket_incoming_user_data: any data that will be given to the websocket_incoming_message_callback, optional * websocket_onclose_callback: callback function called right before closing the websocket, must be complete for the websocket to close * websocket_onclose_user_data: any data that will be given to the websocket_onclose_callback, optional * @Return value: U_OK on success */ int ulfius_set_websocket_response(struct _u_response * response, const char * websocket_protocol, const char * websocket_extensions, void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data), void * websocket_manager_user_data, void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data), void * websocket_incoming_user_data, void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data), void * websocket_onclose_user_data); ``` According to the Websockets RFC, parameters `websocket_protocol` and `websocket_extensions` are specific for your application. In Ulfius Implementation, if you specify a list of protocols as a string of protocol names, separated by a comma (`,`), Ulfius framework will check each one and see if they match the list of protocols specified by the client. The resulting protocol list will be sent back to the client. Likewise, the websocket extension is specific to your application, you can specify a list of websocket extension separated by a semicolon (`;`). If no protocol match your list, the connection will be closed by the framework and will return an error 400 to the client. If you set a `NULL` value for the protocol and/or the extension, Ulfius will accept any protocols and/or extension sent by the client. 3 callback functions are available for the websocket implementation: - `websocket_manager_callback`: This function will be called in a separate thread, and the websocket will remain open as long as this callback function is not completed. In this function, your program will have access to the websocket status (connected or not), and the list of messages sent and received. When this function ends, the websocket will close itself automatically. - `websocket_incoming_message_callback`: This function will be called every time a new message is sent by the client. Although, it is in synchronous mode, which means that you won't have 2 different `websocket_incoming_message_callback` of the same websocket executed at the same time. - `websocket_onclose_callback`: This optional function will be called right after the websocket connection is closed, but before the websocket structure is cleaned. You must specify at least one of the callback functions between `websocket_manager_callback` or `websocket_incoming_message_callback`. When the function `ulfius_stop_framework` is called, it will wait for all running websockets to end by themselves, there is no force close. So if you have a `websocket_manager_callback` function running, you *MUST* end this function in order to make a clean stop of the http daemon. For each of these callback function, you can specify a `*_user_data` pointer containing any data you need. ##### Closing a websocket communication To close a websocket communication from the server, you can do one of the following: - End the function `websocket_manager_callback`, it will result in closing the websocket connection - Send a message with the opcode `U_WEBSOCKET_OPCODE_CLOSE` - Call the function `ulfius_websocket_wait_close` or `ulfius_websocket_send_close_signal` described below If no `websocket_manager_callback` is specified, you can send a `U_WEBSOCKET_OPCODE_CLOSE` in the `websocket_incoming_message_callback` function when you need, or call the function `ulfius_websocket_send_close_signal`: ##### Websocket status The following functions allow the application to know if the the websocket is still open, to enforce closing the websocket or to wait until the websocket is closed by the client: ```C /** * Sets the websocket in closing mode * The websocket will not necessarily be closed at the return of this function, * it will process through the end of the `websocket_manager_callback` * and the `websocket_onclose_callback` calls first. * return U_OK on success * or U_ERROR on error */ int ulfius_websocket_send_close_signal(struct _websocket_manager * websocket_manager); /** * Returns the status of the websocket connection * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_status(struct _websocket_manager * websocket_manager); /** * Wait until the websocket connection is closed or the timeout in milliseconds is reached * if timeout is 0, no timeout is set * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_wait_close(struct _websocket_manager * websocket_manager, unsigned int timeout); ``` #### Client-side websocket Ulfius allows to create a websocket connection as a client. The behavior is quite similar to the server-side websocket. The application will open a websocket connection specified by a `struct _u_request`, and a set of callback functions to manage the websocket once connected. ##### Prepare the request You can manually fill the `struct _u_request` with your parameters or use the dedicated function `ulfius_set_websocket_request`: ```C /** * Set values for a struct _u_request to open a websocket * request must be previously initialized * Return U_OK on success */ int ulfius_set_websocket_request(struct _u_request * request, const char * url, const char * websocket_protocol, const char * websocket_extensions); ``` The `url` specified must have one of the following form: - `http://` - `ws://` - `https://` - `wss://` The `websocket_protocol` and `websocket_extensions` values are optional. To specify multiple protocol, you must separate them with the comma `,` character. To specify multiple extensions, you must separate them with the semicolon `;` character. You can also specify additional headers or cookies to the request. Any body parameter or body raw value will be ignored, the header `Content-Length` will be set to 0. The header `User-Agent` value will be `Ulfius Websocket Client Framework`, feel free to modify it afterwards if you need. ##### Opening the websocket connection Once the request is completed, you can open the websocket connection with `ulfius_open_websocket_client_connection`: ```C /** * Open a websocket client connection * Return U_OK on success * @parameters * struct _u_request * request: request used to specify the input parameters * websocket_manager_callback: main websocket callback function * websocket_manager_user_data: a pointer that will be available in websocket_manager_callback * websocket_incoming_message_callback: callback function that will be called each time a message is received * websocket_incoming_user_data: a pointer that will be available in websocket_incoming_message_callback * websocket_onclose_callback: callback function that will be called right after the websocket is closed * websocket_onclose_user_data: a pointer that will be available in websocket_onclose_callback * websocket_client_handler: The handler for the websocket * */ int ulfius_open_websocket_client_connection(struct _u_request * request, void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data), void * websocket_manager_user_data, void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data), void * websocket_incoming_user_data, void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data), void * websocket_onclose_user_data, struct _websocket_client_handler * websocket_client_handler); ``` If the websocket connection is established, `U_OK` will be returned and the websocket connection will be executed in a separate thread. ##### Closing a websocket communication To close a websocket communication, you can do one of the following: - End the function `websocket_manager_callback`, it will result in closing the websocket connection - Send a message with the opcode `U_WEBSOCKET_OPCODE_CLOSE` - Call the function `ulfius_websocket_wait_close` described below, this function will return U_OK when the websocket is closed - Call the function `ulfius_websocket_client_connection_send_close_signal` described below, this function is non-blocking, it will send a closing signal to the websocket and will return even if the websocket is still open. You can use `ulfius_websocket_wait_close` or `ulfius_websocket_client_connection_status` to check if the websocket is closed. ##### Websocket status The following functions allow the application to know if the the websocket is still open, to enforce closing the websocket or to wait until the websocket is closed by the server: ```C /** * Send a close signal to the websocket * return U_OK when the signal is sent * or U_ERROR on error */ int ulfius_websocket_client_connection_send_close_signal(struct _websocket_client_handler * websocket_client_handler); /** * Closes a websocket client connection * return U_OK when the websocket is closed * or U_ERROR on error */ int ulfius_websocket_client_connection_close(struct _websocket_client_handler * websocket_client_handler); /** * Returns the status of the websocket client connection * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_client_connection_status(struct _websocket_client_handler * websocket_client_handler); /** * Wait until the websocket client connection is closed or the timeout in milliseconds is reached * if timeout is 0, no timeout is set * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_client_connection_wait_close(struct _websocket_client_handler * websocket_client_handler, unsigned int timeout); ``` ## Outgoing request functions Ulfius allows output functions to send HTTP or SMTP requests. These functions use `libcurl`. You can disable these functions by appending the argument `CURLFLAG=-DU_DISABLE_CURL` when you build the library with Makefile or by disabling the flag in CMake build: ``` $ make CURLFLAG=-DU_DISABLE_CURL # Makefile $ cmake -DWITH_CURL=off # CMake ``` ### Send HTTP request API The functions `int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response)` and `int ulfius_send_http_streaming_request(const struct _u_request * request, struct _u_response * response, size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data), void * write_body_data)` are based on `libcurl` api. They allow to send an HTTP request with the parameters specified by the `_u_request` structure. Use the parameter `_u_request.http_url` to specify the distant url to call. You can fill the maps in the `_u_request` structure with parameters, they will be used to build the request. Note that if you fill `_u_request.map_post_body` with parameters, the content-type `application/x-www-form-urlencoded` will be use to encode the data. The response parameters is stored into the `_u_response` structure. If you specify NULL for the response structure, the http call will still be made but no response details will be returned. If you use `ulfius_send_http_request`, the response body will be stored in the parameter `response->*body*`, if you use `ulfius_send_http_streaming_request`, the response body will be available in the `write_body_function` specified in the call. The `ulfius_send_http_streaming_request` can be used for streaming data or large response. Return value is `U_OK` on success. This functions are defined as: ```C /** * ulfius_send_http_request * Send a HTTP request and store the result into a _u_response * return U_OK on success */ int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response); /** * ulfius_send_http_streaming_request * Send a HTTP request and store the result into a _u_response * Except for the body which will be available using write_body_function in the write_body_data * return U_OK on success */ int ulfius_send_http_streaming_request(const struct _u_request * request, struct _u_response * response, size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data), void * write_body_data); ``` ### Send SMTP request API The function `ulfius_send_smtp_email` is used to send emails using a smtp server. It is based on `libcurl` API. It's used to send raw emails via a smtp server. This function is defined as: ```C /** * Send an email * host: smtp server host name * port: tcp port number (optional, 0 for default) * use_tls: true if the connection is tls secured * verify_certificate: true if you want to disable the certificate verification on a tls server * user: connection user name (optional, NULL: no user name) * password: connection password (optional, NULL: no password) * from: from address (mandatory) * to: to recipient address (mandatory) * cc: cc recipient address (optional, NULL: no cc) * bcc: bcc recipient address (optional, NULL: no bcc) * subject: email subject (mandatory) * mail_body: email body (mandatory) * return U_OK on success */ int ulfius_send_smtp_email(const char * host, const int port, const int use_tls, const int verify_certificate, const char * user, const char * password, const char * from, const char * to, const char * cc, const char * bcc, const char * subject, const char * mail_body); ``` ### struct _u_map API The `struct _u_map` is a simple key/value mapping API used in the requests and the response for setting parameters. The available functions to use this structure are: ```C /** * initialize a struct _u_map * this function MUST be called after a declaration or allocation * return U_OK on success */ int u_map_init(struct _u_map * map); /** * free the struct _u_map's inner components * return U_OK on success */ int u_map_clean(struct _u_map * u_map); /** * free the struct _u_map and its components * return U_OK on success */ int u_map_clean_full(struct _u_map * u_map); /** * free an enum return by functions u_map_enum_keys or u_map_enum_values * return U_OK on success */ int u_map_clean_enum(char ** array); /** * returns an array containing all the keys in the struct _u_map * return an array of char * ending with a NULL element */ const char ** u_map_enum_keys(const struct _u_map * u_map); /** * returns an array containing all the values in the struct _u_map * return an array of char * ending with a NULL element */ const char ** u_map_enum_values(const struct _u_map * u_map); /** * return true if the sprcified u_map contains the specified key * false otherwise * search is case sensitive */ int u_map_has_key(const struct _u_map * u_map, const char * key); /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case sensitive */ int u_map_has_value(const struct _u_map * u_map, const char * value); /** * return true if the sprcified u_map contains the specified value up until the specified length * false otherwise * search is case sensitive */ int u_map_has_value_binary(const struct _u_map * u_map, const char * value, size_t length); /** * return true if the sprcified u_map contains the specified key * false otherwise * search is case insensitive */ int u_map_has_key_case(const struct _u_map * u_map, const char * key); /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case insensitive */ int u_map_has_value_case(const struct _u_map * u_map, const char * value); /** * add the specified key/value pair into the specified u_map * if the u_map already contains a pair with the same key, replace the value * return U_OK on success */ int u_map_put(struct _u_map * u_map, const char * key, const char * value); /** * add the specified key/binary value pair into the specified u_map * if the u_map already contains a pair with the same key, * replace the value at the specified offset with the specified length * return U_OK on success */ int u_map_put_binary(struct _u_map * u_map, const char * key, const char * value, uint64_t offset, size_t length); /** * get the value length corresponding to the specified key in the u_map * return -1 if no match found * search is case sensitive */ size_t u_map_get_length(const struct _u_map * u_map, const char * key); /** * get the value length corresponding to the specified key in the u_map * return -1 if no match found * search is case insensitive */ size_t u_map_get_case_length(const struct _u_map * u_map, const char * key); /** * get the value corresponding to the specified key in the u_map * return NULL if no match found * search is case sensitive */ const char * u_map_get(const struct _u_map * u_map, const char * key); /** * get the value corresponding to the specified key in the u_map * return NULL if no match found * search is case insensitive */ const char * u_map_get_case(const struct _u_map * u_map, const char * key); /** * remove an pair key/value that has the specified key * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_key(struct _u_map * u_map, const char * key); /** * remove all pairs key/value that has the specified key (case insensitive search) * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_key_case(struct _u_map * u_map, const char * key); /** * remove all pairs key/value that has the specified value * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value(struct _u_map * u_map, const char * value); /** * remove all pairs key/value that has the specified value (case insensitive search) * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value_case(struct _u_map * u_map, const char * value); /** * remove all pairs key/value that has the specified value up until the specified length * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value_binary(struct _u_map * u_map, const char * key, size_t length); /** * remove the pair key/value at the specified index * return U_OK on success, U_NOT_FOUND if index is out of bound, error otherwise */ int u_map_remove_at(struct _u_map * u_map, const int index); /** * Create an exact copy of the specified struct _u_map * return a reference to the copy, NULL otherwise * returned value must be free'd after use */ struct _u_map * u_map_copy(const struct _u_map * source); /** * Copy all key/values pairs of source into target * If key is already present in target, it's overwritten * return U_OK on success, error otherwise */ int u_map_copy_into(const struct _u_map * source, struct _u_map * target); /** * Return the number of key/values pair in the specified struct _u_map * Return -1 on error */ int u_map_count(const struct _u_map * source); ``` ## What's new in Ulfius 2.4? Improve websocket service features with lots of bugfixes and add the possibility to send a fragmented message. Add websocket client functionality. Allow to create a websocket client connection and exchange messages with the websocket service. In `http://`/`ws://` non-secure mode or `https://`/`wss://` secure mode. Add a command-line websocket client: `uwsc`. ## What's new in Ulfius 2.5? Add option to ignore non utf8 strings in incoming requests. Allow client certificate authentication ## What's new in Ulfius 2.4? Improve websockets support, add a websocket client API, and a new tool called `uwsc`: a CLI to connect to websockets services. ## What's new in Ulfius 2.3? Not much on the API, a lot on the build process. Install via CMake script. ## What's new in Ulfius 2.2? Allow to use your own callback function when uploading files with `ulfius_set_upload_file_callback_function`, so a large file can be uploaded, even with the option `struct _u_instance.max_post_param_size` set. ## What's new in Ulfius 2.1? I know it wasn't long since Ulfius 2.0 was released. But after some review and tests, I realized some adjustments had to be made to avoid bugs and to clean the framework a little bit more. Some of the adjustments made in the new release: - An annoying bug has been fixed that made streaming data a little buggy when used on raspbian. Now if you don't know the data size you're sending, use the macro U_STREAM_SIZE_UNKOWN instead of the previous value -1. There is some updates in the stream callback function parameter types. Check the [streaming data documentation](API.md#streaming-data). - Fix bug on `ulfius_send_http_request` that didn't send back all headers value with the same name (#19) - Fix websocket declaration structures to have them outside of the `ulfius.h`, because it could lead to horrifying bugs when you compile ulfius with websocket but add `#define U_DISABLE_WEBSOCKET` in your application. - Add proxy value for outgoing requests (#18) - Unify and update functions name `ulfius_set_[string|json|binary]_body`. You may have to update your legacy code. The minor version number has been incremented, from 2.0 to 2.1 because some of the changes may require changes in your own code. ## What's new in Ulfius 2.0? Ulfius 2.0 brings several changes that make the library incompatible with Ulfius 1.0.x branch. The goal of making Ulfius 2.0 is to make a spring cleaning of some functions, remove what is apparently useless, and should bring bugs and memory loss. The main new features are multiple callback functions and websockets implementation. ### Multiple callback functions Instead of having an authentication callback function, then a main callback function, you can now have as much callback functions as you want for the same endpoint. A `priority` number has been added in the `struct _u_endpoint` and the auth_callback function and its dependencies have been removed. For example, let's say you have the following endpoints defined: - `GET` `/api/tomato/:tomato` => `tomato_get_callback` function, priority 10 - `GET` `/api/potato/:potato` => `potato_get_callback` function, priority 10 - `GET` `/api/*` => `api_validate_callback` function, priority 5 - `*` `*` => `authentication_callback` function, priority 1 - `GET` `*` => `gzip_body_callback` function, priority 99 Then if the client calls the url `GET` `/api/potato/myPotato`, the following callback functions will be called in that order: - `authentication_callback` - `api_validate_callback` - `potato_get_callback` - `gzip_body_callback` *Warning:* In this example, the url parameter `myPotato` will be availabe only in the `potato_get_callback` function, because the other endpoints did not defined a url parameter after `/potato`. If you need to communicate between callback functions for any purpose, you can use the new parameter `struct _u_response.shared_data`. This is a `void *` pointer initialized to `NULL`. If you use it, remember to free it after use, because the framework won't. ### Keep only binary_body in struct _u_request and struct _u_response the values `string_body` and `json_body` have been removed from the structures `struct _u_request` and `struct _u_response`. This may be painless in the response if you used only the functions `ulfius_set_xxx_body_response`. Otherwise, you should make small arrangements to your code. ### Websocket service Ulfius now allows websockets communication between the client and the server. Check the [API.md](API.md#websockets-communication) file for implementation details. Using websocket requires [libgnutls](https://www.gnutls.org/). It also requires a recent version of [Libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/), at least 0.9.53. If you dont need or can't use this feature, you can disable it by adding the option `WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET` to the make command when you build Ulfius: ```shell $ make WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET ``` ### Remove libjansson and libcurl hard dependency In Ulfius 1.0, libjansson and libcurl were mandatory to build the library, but their usage was not in the core of the framework. Although they can be very useful, so the dependency is now optional. They are enabled by default, but if you don't need them, you can disable them when you build Ulfius library. #### libjansson dependency This dependency allows to use the following functions: ```c /** * ulfius_get_json_body_request * Get JSON structure from the request body if the request is valid */ json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error); /** * ulfius_set_json_body_request * Add a json_t body to a request * return U_OK on success */ int ulfius_set_json_body_request(struct _u_request * request, json_t * body); /** * ulfius_set_json_body_response * Add a json_t body to a response * return U_OK on success */ int ulfius_set_json_body_response(struct _u_response * response, const uint status, const json_t * body); /** * ulfius_get_json_body_response * Get JSON structure from the response body if the request is valid */ json_t * ulfius_get_json_body_response(struct _u_response * response, json_error_t * json_error); ``` If you want to disable these functions, append `JANSSONFLAG=-DU_DISABLE_JANSSON` when you build Ulfius library. ``` $ git clone https://github.com/babelouest/ulfius.git $ cd ulfius/ $ git submodule update --init $ make JANSSONFLAG=-DU_DISABLE_JANSSON $ sudo make install ``` #### libcurl dependency This dependency allows to use the following functions: ```c /** * ulfius_send_http_request * Send a HTTP request and store the result into a _u_response * return U_OK on success */ int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response); /** * ulfius_send_http_streaming_request * Send a HTTP request and store the result into a _u_response * Except for the body which will be available using write_body_function in the write_body_data * return U_OK on success */ int ulfius_send_http_streaming_request(const struct _u_request * request, struct _u_response * response, size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data), void * write_body_data); /** * ulfius_send_smtp_email * Send an email using libcurl * email is plain/text and UTF8 charset * host: smtp server host name * port: tcp port number (optional, 0 for default) * use_tls: true if the connection is tls secured * verify_certificate: true if you want to disable the certificate verification on a tls server * user: connection user name (optional, NULL: no user name) * password: connection password (optional, NULL: no password) * from: from address (mandatory) * to: to recipient address (mandatory) * cc: cc recipient address (optional, NULL: no cc) * bcc: bcc recipient address (optional, NULL: no bcc) * subject: email subject (mandatory) * mail_body: email body (mandatory) * return U_OK on success */ int ulfius_send_smtp_email(const char * host, const int port, const int use_tls, const int verify_certificate, const char * user, const char * password, const char * from, const char * to, const char * cc, const char * bcc, const char * subject, const char * mail_body); ``` If you want to disable these functions, append `CURLFLAG=-DU_DISABLE_CURL` when you build Ulfius library. ``` $ git clone https://github.com/babelouest/ulfius.git $ cd ulfius/ $ git submodule update --init $ make CURLFLAG=-DU_DISABLE_CURL $ sudo make install ``` If you wan to disable libjansson and libcurl, you can append both parameters. ``` $ git clone https://github.com/babelouest/ulfius.git $ cd ulfius/ $ git submodule update --init $ make CURLFLAG=-DU_DISABLE_CURL JANSSONFLAG=-DU_DISABLE_JANSSON $ sudo make install ``` ### Ready-to-use callback functions You can find some ready-to-use callback functions in the folder [example_callbacks](https://github.com/babelouest/ulfius/blob/master/example_callbacks). ## Update existing programs from Ulfius 2.0 to 2.1 - An annoying bug has been fixed that made streaming data a little buggy when used on raspbian. Now if you don't know the data size you're sending, use the macro U_STREAM_SIZE_UNKOWN instead of the previous value -1. - There are some updates in the stream callback function parameter types. Check the [streaming data documentation](#streaming-data). - The websocket data structures are no longer available directly in `struct _u_response` or `struct _u_instance`. But you shouldn't use them like this anyway so it won't be a problem. - Unify and update functions name `ulfius_set_*_body_response`. You may have to update your legacy code. The new functions names are: ```c int ulfius_set_string_body_response(struct _u_response * response, const uint status, const char * body); int ulfius_set_binary_body_response(struct _u_response * response, const uint status, const char * body, const size_t length); int ulfius_set_empty_body_response(struct _u_response * response, const uint status); ``` ## Update existing programs from Ulfius 1.x to 2.0 If you already have programs that use Ulfius 1.x and want to update them to the brand new fresh Ulfius 2.0, it may require the following minor changes. ### Endpoints definitions Endpoints structure have changed, `ulfius_add_endpoint_by_val` now requires only one callback function, but requires a priority number. If you don't use authentication callback functions, you can simply remove the `NULL, NULL, NULL` parameters corresponding to the former authentication callback function pointer, the authentication callback user data, and the realm value. Then add any number as a priority, 0 for example. If you use authentication callback functions, split your `ulfius_add_endpoint_by_val` call in 2 separate calls, one for the authentication function, one for the main callback function. For example: ```C // An Ulfius 1.x call ulfius_add_endpoint_by_val(&instance, "GET", "/", NULL, &auth_callback, my_auth_data, "my realm", &main_callback, my_main_data); // The same behaviour with Ulfius 2.0 ulfius_add_endpoint_by_val(&instance, "GET", "/", NULL, 0, &auth_callback, my_auth_data); ulfius_add_endpoint_by_val(&instance, "GET", "/", NULL, 1, &main_callback, my_main_data); // In this case, the realm value "my realm" must be specified in the response ``` ### Callback return value The return value for the callback functions must be adapted, instead of U_OK, U_ERROR or U_ERROR_UNAUTHORIZED, you must use one of the following: ```C #define U_CALLBACK_CONTINUE 0 // Will replace U_OK #define U_CALLBACK_COMPLETE 1 #define U_CALLBACK_UNAUTHORIZED 2 // Will replace U_ERROR_UNAUTHORIZED #define U_CALLBACK_ERROR 3 // Will replace U_ERROR ``` If you want more details on the multiple callback functions, check the [documentation](#callback-functions-return-value). Other functions may have change their name or signature, check the documentation for more information. ulfius-2.5.2/CHANGELOG.md000066400000000000000000000053271341017022300146400ustar00rootroot00000000000000# Ulfius Changelog ## 2.5.2 - Fix utf8 check on NULL value ## 2.5.1 - Fix uwsc crash on some systems ## 2.5.0 - Add struct _u_endpoint.check_utf8 to check all request parameters and values to be valid utf8 strings - Add client certificate authentication for webservice and send request library (issue #83) - Fix build config file bug when using -jxx option to Makefile (issue #84) - Allow to disable Yder logging library, to use Ulfius in embedded systems like FreeRTOS where console, syslog or journald are not available, and file logging is overkill - Add support for FreeRTOS and LWIP, Thanks to Dirk Uhlemann - Add support for SameSite attribute in response cookies (issue #88) ## 2.4.4 - CMake scripts improvements ## 2.4.3 - Add config file ulfius-cfg.h dynamically built with the options - Adapt examples with new ulfius-cfg.h file ## 2.4.2 - Fix #79 where yuarel should be hidden from the outside world ## 2.4.1 - Fix #78 where gnutls is not required if websocket is disabled ## 2.4.0 - Fix Websocket fragmented messages - Fix CMake script that installed Orcania twice - Fix cppcheck warnings - Add timeout for http connections - Allow to use MHD_RESPMEM_MUST_COPY for different memory manager, fix #63 - Add websocket client framework - Add uwsc - Ulfius WebSocket Client - A simple command-line websocket client program - Add Travis CI - Add RPM in CMake script package ## 2.3.8 - Fix CMake build when /usr/local is not present in default build path ## 2.3.7 - Improve documentation with summary - Yet another websocket fix, this one was binary messages not properly handled - At the same time, improve websocket_example to handle incoming binary messages ## 2.3.6 - Fix websocket bug that did not close a websocket properly after wrongly closed connections - Add last example_callbacks versions - Improve documentation on ulfius_get_json_body_request and ulfius_get_json_body_response ## 2.3.5 - Fix websocket bug that kept some connections open after being unproperly closed by the client ## 2.3.4 - Fix Makefile soname ## 2.3.3 - Add Debian hardening patch on Makefile ## 2.3.2 - Fix websocket_example that worked for Firefox onky due to minor bugs in websocket management and misunderstanding the RFC - Update oauth2_bearer/glewlwyd_resource to handle client tokens ## 2.3.1 - Sync version number on all places it's located ## 2.3 - Add CMake installation script - Various bugfixes ## 2.2.3 - Fix PTHREAD_MUTEX_RECURSIVE_NP bug ## 2.2.2 - Fix bug in websockets ## 2.2.1 - Added error informations inside ulfius_get_json_body_request function (#33) - code cleaning - small bugfixes ## 2.2 - Add large file upload support (#31) - fix `upload_data_size` and memory consumption - Make ldconfig harmless if not root (#26) ulfius-2.5.2/CMakeLists.txt000066400000000000000000000327461341017022300155740ustar00rootroot00000000000000# # Ulfius library # # CMake file used to build all programs # # Copyright 2018 Silvio Clecio # Copyright 2018 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # cmake_minimum_required(VERSION 3.5) project(ulfius C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror") # library info set(PROJECT_DESCRIPTION "Web Framework to build REST APIs, Webservices or any HTTP endpoint in C language. Can stream large amount of data, integrate JSON data with Jansson, and create websocket services") set(PROJECT_HOMEPAGE_URL "https://github.com/babelouest/ulfius/") set(PROJECT_BUGREPORT_PATH "https://github.com/babelouest/ulfius/issues") set(LIBRARY_VERSION_MAJOR "2") set(LIBRARY_VERSION_MINOR "5") set(LIBRARY_VERSION_PATCH "2") set(PROJECT_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}") set(LIBRARY_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}") set(LIBRARY_SOVERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}") set(ORCANIA_VERSION_DOWNLOAD "1.2.9") set(YDER_VERSION_DOWNLOAD "1.4.4") # cmake modules set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules) include(GNUInstallDirs) include(CheckSymbolExists) # check if _GNU_SOURCE is available if (NOT _GNU_SOURCE) check_symbol_exists(__GNU_LIBRARY__ "features.h" _GNU_SOURCE) if (NOT _GNU_SOURCE) unset(_GNU_SOURCE CACHE) check_symbol_exists(_GNU_SOURCE "features.h" _GNU_SOURCE) endif () endif () if (_GNU_SOURCE) add_definitions(-D_GNU_SOURCE) endif () # directories and source set(INC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) set(UWSC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools/uwsc) include_directories(${INC_DIR}) set(LIB_SRC ${INC_DIR}/ulfius.h ${INC_DIR}/u_private.h ${INC_DIR}/yuarel.h ${SRC_DIR}/u_map.c ${SRC_DIR}/u_request.c ${SRC_DIR}/u_response.c ${SRC_DIR}/u_send_request.c ${SRC_DIR}/u_websocket.c ${SRC_DIR}/yuarel.c ${SRC_DIR}/ulfius.c) # websocket support option(WITH_WEBSOCKET "Websocket support" ON) if (WIN32) set(WITH_WEBSOCKET OFF) endif () if (NOT WITH_WEBSOCKET) set(MHD_MIN_VERSION 0.9.51) else () set(MHD_MIN_VERSION 0.9.53) include(FindGnuTLS) find_package(GnuTLS REQUIRED) if (GNUTLS_FOUND) set(LIBS ${LIBS} ${GNUTLS_LIBRARIES}) endif () endif () include(FindMHD) find_package(MHD ${MHD_MIN_VERSION} REQUIRED) if (MHD_FOUND) set(LIBS ${LIBS} ${MHD_LIBRARIES}) include_directories(${MHD_INCLUDE_DIRS}) if (MHD_VERSION_STRING VERSION_LESS "0.9.53") set(WITH_WEBSOCKET OFF) endif () endif () if (WITH_WEBSOCKET) set(U_DISABLE_WEBSOCKET OFF) else () set(U_DISABLE_WEBSOCKET ON) endif () option(WITH_CURL "Use Curl library" ON) if (WITH_CURL) include(FindCURL) find_package(CURL REQUIRED) if (CURL_FOUND) set(LIBS ${LIBS} ${CURL_LIBRARIES}) include_directories(${CURL_INCLUDE_DIRS}) set(U_DISABLE_CURL OFF) endif () else () set(U_DISABLE_CURL ON) endif () option(WITH_JANSSON "Use jansson library" ON) if (WITH_JANSSON) include(FindJansson) set(JANSSON_MIN_VERSION 2.4) find_package(Jansson ${JANSSON_MIN_VERSION} REQUIRED) if (JANSSON_FOUND) include_directories(${JANSSON_INCLUDE_DIRS}) set(LIBS ${LIBS} ${JANSSON_LIBRARIES}) set(U_DISABLE_JANSSON OFF) endif () else () set(U_DISABLE_JANSSON ON) endif () # TO MY FUTURE SELF # The following 2 blocks are put BEFORE searching for Orcania and Yder by design # Otherwise it will lead to cmake errors # DON'T MOVE IT BELOW PLEASE! # static library option(BUILD_STATIC "Build static library." OFF) if (BUILD_STATIC) add_library(ulfius_static STATIC ${LIB_SRC}) target_compile_definitions(ulfius_static PUBLIC -DO_STATIC_LIBRARY) set_target_properties(ulfius_static PROPERTIES OUTPUT_NAME ulfius) endif () # shared library add_library(ulfius SHARED ${LIB_SRC}) set_target_properties(ulfius PROPERTIES COMPILE_OPTIONS -Wextra PUBLIC_HEADER "${INC_DIR}/ulfius.h;${PROJECT_BINARY_DIR}/ulfius-cfg.h" VERSION "${LIBRARY_VERSION}" SOVERSION "${LIBRARY_SOVERSION}") if (WIN32) set_target_properties(ulfius PROPERTIES SUFFIX "-${LIBRARY_VERSION_MAJOR}.dll") endif () target_link_libraries(ulfius ${LIBS}) # dependencies option(SEARCH_ORCANIA_U "Search for ORCANIA library" ON) if (SEARCH_ORCANIA_U) set(Orcania_FIND_QUIETLY ON) # force to find Orcania quietly include(FindOrcania) find_package(Orcania 1.1 QUIET) # try to find orcania if (NOT ORCANIA_FOUND) include(DownloadProject) download_project(PROJ orcania # ... otherwise, download archive URL "https://github.com/babelouest/orcania/archive/v${ORCANIA_VERSION_DOWNLOAD}.tar.gz" QUIET) add_subdirectory(${orcania_SOURCE_DIR} ${orcania_BINARY_DIR}) include_directories(${orcania_SOURCE_DIR}/include) add_dependencies(ulfius orcania) set(ORCANIA_LIBRARIES orcania) set(LIBS ${LIBS} ${ORCANIA_LIBRARIES}) include_directories(${orcania_BINARY_DIR}) else() set(LIBS ${LIBS} ${ORCANIA_LIBRARIES}) include_directories(${ORCANIA_INCLUDE_DIRS}) endif () endif () option(WITH_YDER "Use Yder library to log messages" ON) option(SEARCH_YDER "Search for Yder library" ON) if (WITH_YDER) set(U_DISABLE_YDER OFF) set(SEARCH_ORCANIA OFF CACHE BOOL "Force to false") # Avoid to search and download orcania during yder search and download if (SEARCH_YDER) set(Yder_FIND_QUIETLY ON) # force to find Yder quietly include(FindYder) find_package(Yder 1.1 QUIET) # try to find Yder if (NOT YDER_FOUND) include(DownloadProject) download_project(PROJ yder # ... otherwise, download archive URL "https://github.com/babelouest/yder/archive/v${YDER_VERSION_DOWNLOAD}.tar.gz" QUIET) add_subdirectory(${yder_SOURCE_DIR} ${yder_BINARY_DIR}) include_directories(${yder_SOURCE_DIR}/include) add_dependencies(ulfius yder) set(YDER_LIBRARIES yder orcania) include_directories(${yder_BINARY_DIR}) else() set(LIBS ${LIBS} ${YDER_LIBRARIES}) include_directories(${YDER_INCLUDE_DIRS}) endif () target_link_libraries(ulfius ${LIBS} ${YDER_LIBRARIES}) endif () else () set(U_DISABLE_YDER ON) endif () # build uwsc option(BUILD_UWSC "Build uwsc application." ON) if (WITH_WEBSOCKET AND BUILD_UWSC) add_executable(uwsc ${UWSC_DIR}/uwsc.c ${INC_DIR}/ulfius.h ${INC_DIR}/u_private.h ${PROJECT_BINARY_DIR}/ulfius-cfg.h) set_target_properties(uwsc PROPERTIES SKIP_BUILD_RPATH TRUE) add_dependencies(uwsc ulfius) target_link_libraries(uwsc ulfius ${LIBS}) install(TARGETS uwsc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif () set(PKGCONF_REQ "") set(PKGCONF_REQ_PRIVATE "liborcania, libyder") if (WITH_CURL) set (PKGCONF_REQ_PRIVATE "${PKGCONF_REQ_PRIVATE}, libcurl") endif () if (WITH_JANSSON) set (PKGCONF_REQ_PRIVATE "${PKGCONF_REQ_PRIVATE}, jansson") endif () if (WITH_WEBSOCKET) set (PKGCONF_REQ_PRIVATE "${PKGCONF_REQ_PRIVATE}, gnutls > 3.5.0, libmicrohttpd > 0.9.53") else () set (PKGCONF_REQ_PRIVATE "${PKGCONF_REQ_PRIVATE}, libmicrohttpd > 0.9.51") endif () # build ulfius-cfg.h file configure_file(${INC_DIR}/ulfius-cfg.h.in ${PROJECT_BINARY_DIR}/ulfius-cfg.h) set (CMAKE_EXTRA_INCLUDE_FILES ${PROJECT_BINARY_DIR}) include_directories(${PROJECT_BINARY_DIR}) # tests option(BUILD_ULFIUS_TESTING "Build the testing tree." OFF) # because we do not use include(CTest) if (BUILD_ULFIUS_TESTING) include(FindCheck) find_package(Check REQUIRED) if (CHECK_FOUND) if (NOT WIN32) include(FindSubunit) find_package(Subunit REQUIRED) endif () enable_testing() set(CMAKE_CTEST_COMMAND ctest -V) set(TST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test) set(LIBS ulfius ${LIBS} ${CHECK_LIBRARIES}) if (NOT WIN32) find_package(Threads REQUIRED) set(LIBS ${LIBS} ${SUBUNIT_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} m rt) endif () set(TESTS core u_map framework) if (WITH_WEBSOCKET) set(TESTS ${TESTS} websocket) endif () configure_file( "${CMAKE_MODULE_PATH}/CTestCustom.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake" @ONLY) foreach (t ${TESTS}) add_executable(${t} EXCLUDE_FROM_ALL ${TST_DIR}/${t}.c) target_include_directories(${t} PUBLIC ${TST_DIR}) target_link_libraries(${t} PUBLIC ${LIBS}) add_test(NAME ${t} WORKING_DIRECTORY ${TST_DIR} COMMAND ${t}) endforeach () endif () endif () # install target option(INSTALL_HEADER "Install the header files" ON) # Install ulfius.h or not configure_file(libulfius.pc.in libulfius.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libulfius.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) SET (TARGETS ulfius) if (BUILD_STATIC) SET (TARGETS ${TARGETS} ulfius_static) endif () if (INSTALL_HEADER) install(TARGETS ${TARGETS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY example_callbacks/ DESTINATION ${CMAKE_INSTALL_DOCDIR}/example_callbacks/ COMPONENT runtime) install(DIRECTORY example_programs/ DESTINATION ${CMAKE_INSTALL_DOCDIR}/example_programs/ COMPONENT runtime) install(FILES README.md DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT runtime) install(FILES INSTALL.md DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT runtime) install(FILES API.md DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT runtime) else () install(TARGETS ${TARGETS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif () # uninstall target if (NOT TARGET uninstall) configure_file( "${CMAKE_MODULE_PATH}/CMakeUninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif () # packaging set(CPACK_PACKAGE_VERSION_MAJOR ${LIBRARY_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${LIBRARY_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${LIBRARY_VERSION_PATCH}) if (INSTALL_HEADER) set(PACKAGE_FILE_NAME "lib${CMAKE_PROJECT_NAME}-dev_${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") else () set(PACKAGE_FILE_NAME "lib${CMAKE_PROJECT_NAME}_${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") endif () set(PACKAGE_IGNORED_FILES "${CMAKE_CURRENT_BINARY_DIR}/;/.git/;.gitignore;~$;${CPACK_SOURCE_IGNORE_FILES}") set(CPACK_PACKAGE_NAME "libulfius") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Web Framework for C programs") set(CPACK_GENERATOR "TGZ;DEB") set(CPACK_PACKAGE_VERSION_MAJOR ${LIBRARY_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${LIBRARY_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${LIBRARY_VERSION_PATCH}) option(BUILD_RPM "Build a RPM for your system" OFF) if (BUILD_RPM) set(CPACK_GENERATOR "TGZ;DEB;RPM") set(CPACK_RPM_PACKAGE_LICENSE "LGPL") set(CPACK_RPM_PACKAGE_URL "http://babelouest.github.io/ulfius/") endif () set(CPACK_DEBIAN_PACKAGE_MAINTAINER "mail@babelouest.org") set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${PROJECT_DESCRIPTION}) set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://babelouest.github.io/ulfius/") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.4), liborcania (>= 1.2), libyder (>= 1.3)") if (WITH_CURL) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libcurl3-gnutls (>= 7.16.2)") endif () if (WITH_JANSSON) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libjansson4 (>= 2.1)") endif () if (WITH_WEBSOCKET) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libgnutls30 (>= 3.5.0), libmicrohttpd12 (>= 0.9.53)") else () set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libmicrohttpd12 (>= 0.9.51)") endif () set(CPACK_PACKAGE_FILE_NAME ${PACKAGE_FILE_NAME}) set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_PACKAGE_FILE_NAME ${PACKAGE_FILE_NAME}) set(CPACK_SOURCE_IGNORE_FILES ${PACKAGE_IGNORED_FILES}) include(CPack) add_custom_target(dist_u COMMAND ${CMAKE_MAKE_PROGRAM} package_source) message(STATUS "Websocket support: ${WITH_WEBSOCKET}") message(STATUS "Outgoing requests support: ${WITH_CURL}") message(STATUS "Jansson library support: ${WITH_JANSSON}") message(STATUS "Yder support: ${WITH_YDER}") message(STATUS "Build uwsc application: ${BUILD_UWSC}") message(STATUS "Build static library: ${BUILD_STATIC}") message(STATUS "Build testing tree: ${BUILD_ULFIUS_TESTING}") message(STATUS "Install the header files: ${INSTALL_HEADER}") message(STATUS "Build RPM package: ${BUILD_RPM}") ulfius-2.5.2/INSTALL.md000066400000000000000000000154631341017022300144610ustar00rootroot00000000000000# Install Ulfius - [Distribution packages](#distribution-packages) - [Pre-compiled packages](#pre-compiled-packages) - [Manual install](#manual-install) - [Prerequisites](#prerequisites) - [CMake - Multi architecture](#cmake---multi-architecture) - [Good ol' Makefile](#good-ol-makefile) ## Distribution packages [![Packaging status](https://repology.org/badge/vertical-allrepos/ulfius.svg)](https://repology.org/metapackage/ulfius) Ulfius is available in multiple distributions as official package. Check out your distribution documentation to install the package automatically. ```shell $ # Example for Debian testing $ sudo apt install libulfius-dev # Or apt install libulfius2.3 if you don't need the development files ``` ## Pre-compiled packages You can install Ulfius with a pre-compiled package available in the [release pages](https://github.com/babelouest/ulfius/releases/latest/). `jansson`, `libmicrohttpd`, `gnutls` and `libcurl-gnutls` development files packages are required to install Ulfius. The packages files `ulfius-dev-full_*` contain the libraries `orcania`, `yder` and `ulfius`. For example, to install Ulfius with the `ulfius-dev-full_2.3.0_Debian_stretch_x86_64.tar.gz` package downloaded on the `releases` page, you must execute the following commands: ```shell $ sudo apt install -y libmicrohttpd-dev libjansson-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libsystemd-dev $ wget https://github.com/babelouest/ulfius/releases/download/v2.3.0/ulfius-dev-full_2.3.0_Debian_stretch_x86_64.tar.gz $ tar xf ulfius-dev-full_2.3.0_Debian_stretch_x86_64.tar.gz $ sudo dpkg -i liborcania-dev_1.2.0_Debian_stretch_x86_64.deb $ sudo dpkg -i libyder-dev_1.2.0_Debian_stretch_x86_64.deb $ sudo dpkg -i libulfius-dev_2.3.0_Debian_stretch_x86_64.deb ``` If there's no package available for your distribution, you can recompile it manually using `CMake` or `Makefile`. ## Manual install ### Prerequisites Ulfius requires the following dependencies. - libmicrohttpd (required), minimum 0.9.53 if you require Websockets support - libjansson (optional), minimum 2.4, required for json support - libgnutls, libgcrypt (optional), required for Websockets and https support - libcurl (optional), required to send http/smtp requests - libsystemd (optional), required for [yder](https://github.com/babelouest/yder) to log messages in journald Note: the build stacks require a compiler (`gcc` or `clang`), `make`, `cmake` (if using cmake build), and `pkg-config`. For example, to install all the external dependencies on Debian Stretch, run as root: ```shell # apt-get install libmicrohttpd-dev libjansson-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev ``` ### Good ol' Makefile Download Orcania, Yder and Ulfius source code from Github, compile and install Orcania, then Yder, then compile and install ulfius: ```shell $ git clone https://github.com/babelouest/orcania.git $ git clone https://github.com/babelouest/yder.git $ git clone https://github.com/babelouest/ulfius.git $ cd orcania/ $ make && sudo make install $ cd ../yder/ $ make && sudo make install $ cd ../ulfius/ $ make && sudo make install ``` #### Disable Ulfius dependencies To disable libcurl functions, append the option `CURLFLAG=1` to the make command when you build Ulfius: ```shell $ make CURLFLAG=1 ``` If libcurl functions are disabled, `libcurl4-gnutls-dev` is no longer mandatory for install. To disable libjansson functions, append the option `JANSSONFLAG=1` to the make command when you build Ulfius and Orcania: ```shell $ make JANSSONFLAG=1 ``` If libjansson functions are disabled, `libjansson-dev` is no longer mandatory for install. To disable websocket implementation and avoid installing libgnutls, append the option `WEBSOCKETFLAG=1` to the make command when you build Ulfius: ```shell $ make WEBSOCKETFLAG=1 ``` If websocket functions are disabled, `libgnutls-dev` is no longer mandatory for install. To disable yder library (you will no longer have log messages available!), append the option `YDERFLAG=1` to the make command when you build Ulfius: ```shell $ make YDERFLAG=1 ``` To disable two or more libraries, append options, example: ```shell $ make CURLFLAG=1 JANSSONFLAG=1 ``` #### Enable embedded systems flags To build Ulfius on [FreeRTOS](https://www.freertos.org/), append the option `FREERTOSFLAG=1` to the make command when you build Ulfius: ```shell $ make FREERTOSFLAG=1 ``` To build Ulfius with the [LWIP](https://savannah.nongnu.org/projects/lwip/) library, append the option `LWIPFLAG=1` to the make command when you build Ulfius: ```shell $ make LWIPFLAG=1 ``` Those two options are exclusive, you can't use them both at the same time. #### Installation directory By default, the shared libraries and the header files will be installed in the `/usr/local` location. To change this setting, you can modify the `DESTDIR` value in the `src/Makefile`, `lib/orcania/src/Makefile` and `lib/yder/src/Makefile` files. ```shell $ make DESTDIR=/tmp install # to install ulfius in /tmp/lib for example ``` You can install Ulfius without root permission if your user has write access to `$(DESTDIR)`. A `ldconfig` command is executed at the end of the install, it will probably fail if you don't have root permission, but this is harmless. If you choose to install Ulfius in another directory, you must set your environment variable `LD_LIBRARY_PATH` properly. #### Install uwsc uwsc is a small command-line tool to connect to websocket services. To install uwsc only, you can use the `Makefile` in the `uwsc/` directory: ```shell $ cd ulfius/uwsc $ make && sudo make install ``` This will compile and install uwsc in `/usr/local/bin`, to install it in another directory, you can change the value of `DESTDIR`. ### CMake - Multi architecture You can build Ulfius library using cmake, example: ```shell $ mkdir build $ cd build $ cmake .. $ make && sudo make install ``` The available options for cmake are: - `-DWITH_JANSSON=[on|off]` (default `on`): Build with Jansson dependency - `-DWITH_CURL=[on|off]` (default `on`): Build with libcurl dependency - `-DWITH_WEBSOCKET=[on|off]` (default `on`): Build with websocket functions, not available for Windows, requires libmicrohttpd 0.9.53 minimum and GnuTLS installed. - `-DWITH_JOURNALD=[on|off]` (default `on`): Build with journald (SystemD) support for logging - `-DWITH_YDER=[on|off]` (default `on`): Build with Yder library for logging messages - `-DBUILD_UWSC=[on|off]` (default `on`): Build uwsc - `-DBUILD_STATIC=[on|off]` (default `off`): Build the static archive in addition to the shared library - `-DBUILD_ULFIUS_TESTING=[on|off]` (default `off`): Build unit tests - `-DINSTALL_HEADER=[on|off]` (default `on`): Install header file `ulfius.h` - `-DBUILD_RPM=[on|off]` (default `off`): Build RPM package when running `make package` - `-DCMAKE_BUILD_TYPE=[Debug|Release]` (default `Release`): Compile with debugging symbols or not ulfius-2.5.2/LICENSE000066400000000000000000000635361341017022300140420ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. (This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.) Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. {signature of Ty Coon}, 1 April 1990 Ty Coon, President of Vice That's all there is to it! ulfius-2.5.2/Makefile000066400000000000000000000021461341017022300144630ustar00rootroot00000000000000# # Ulfius Framework # # Makefile used to build all programs # # Copyright 2014-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # LIBULFIUS_LOCATION=./src EXAMPLES_LOCATION=./example_programs UWSC_LOCATION=./tools/uwsc TESTS_LOCATION=./test all: cd $(LIBULFIUS_LOCATION) && $(MAKE) $* cd $(UWSC_LOCATION) && $(MAKE) $* debug: cd $(LIBULFIUS_LOCATION) && $(MAKE) debug $* cd $(UWSC_LOCATION) && $(MAKE) debug $* clean: cd $(LIBULFIUS_LOCATION) && $(MAKE) clean cd $(EXAMPLES_LOCATION) && $(MAKE) clean cd $(UWSC_LOCATION) && $(MAKE) clean cd $(TESTS_LOCATION) && $(MAKE) clean examples: cd $(EXAMPLES_LOCATION) && $(MAKE) $* install: cd $(LIBULFIUS_LOCATION) && $(MAKE) install cd $(UWSC_LOCATION) && $(MAKE) install uninstall: cd $(LIBULFIUS_LOCATION) && $(MAKE) uninstall check: cd $(TESTS_LOCATION) && $(MAKE) ulfius-2.5.2/README.md000066400000000000000000000111121341017022300142730ustar00rootroot00000000000000# Ulfius [![Build Status](https://travis-ci.com/babelouest/ulfius.svg?branch=master)](https://travis-ci.com/babelouest/ulfius) HTTP Framework for REST Applications in C. Based on [GNU Libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) for the backend web server, [Jansson](http://www.digip.org/jansson/) for the json manipulation library, and [Libcurl](http://curl.haxx.se/libcurl/) for the http/smtp client API. Used to facilitate creation of web applications in C programs with a small memory footprint, as in embedded systems applications. You can create webservices in HTTP or HTTPS mode, stream data, or implement server websockets. ## Hello World! example application The source code of a hello world using Ulfius is the following: ```c /** * test.c * Small Hello World! example * to compile with gcc, run the following command * gcc -o test test.c -lulfius */ #include #include #define PORT 8080 /** * Callback function for the web application on /helloworld url call */ int callback_hello_world (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World!"); return U_CALLBACK_CONTINUE; } /** * main function */ int main(void) { struct _u_instance instance; // Initialize instance with the port number if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { fprintf(stderr, "Error ulfius_init_instance, abort\n"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", "/helloworld", NULL, 0, &callback_hello_world, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { fprintf(stderr, "Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } ``` ## Main features ### Webservice - Create a webservice in a separate thread, the endpoint is identified by its method (ex: `GET`, `POST`, `PUT`, `DELETE`, etc.) and its url path with its optional parameters (ex: `/api/doc/@id`). The webservice is executed in a callback function. - Stream large amount of data with a reduced memory footprint. - Websocket service, the websocket messages exchange is executed in dedicated callback functions. ### Client requests - Client http[s] and smtp requests execution, the response is parsed in a dedicated structure. - Client websocket request execution, the websocket messages exchange is executed in dedicated callback functions. ### Websockets - Create a websocket service application - Create websocket client application - CLI to connect to a remote websocket: [uwsc](https://github.com/babelouest/ulfius/tree/master/tools/uwsc) ## Installation See [INSTALL.md](INSTALL.md) file for installation details ## Documentation See [API.md](API.md) file for API documentation details ## Example programs source code Example programs are available to understand the different functionalities available, see [example_programs](https://github.com/babelouest/ulfius/blob/master/example_programs) folder for detailed sample source codes and documentation. ## Example callback functions Example callback functions are available in the folder [example_callbacks](https://github.com/babelouest/ulfius/blob/master/example_callbacks). The example callback functions available are: - static file server: to provide static files of a specific folder - oauth2 bearer: to check the validity of a Oauth2 bearer jwt token. Requires [libjwt](https://github.com/benmcollins/libjwt). ## Projects using Ulfius framework - [Angharad](https://github.com/babelouest/angharad), House automation system for ZWave and other types of devices - [Glewlwyd](https://github.com/babelouest/glewlwyd), a lightweight OAuth2 authentication server that provides [JSON Web Tokens](https://jwt.io/) - [Hutch](https://github.com/babelouest/hutch), a safe locker for passwords and other secrets, using client side encryption only - [Taliesin](https://github.com/babelouest/taliesin), a lightweight audio streaming server - [Taulas Raspberry Pi Serial interface](https://github.com/babelouest/taulas/tree/master/taulas_raspberrypi_serial), an interface for Arduino devices that implent [Taulas](https://github.com/babelouest/taulas/) protocol, a house automation protocol ## Questions, problems ? I'm open for questions and suggestions, feel free to open an issue or send a pull request if you feel like it! ulfius-2.5.2/cmake-modules/000077500000000000000000000000001341017022300155465ustar00rootroot00000000000000ulfius-2.5.2/cmake-modules/CMakeUninstall.cmake.in000066400000000000000000000020641341017022300220310ustar00rootroot00000000000000if (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") endif (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach (file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if (IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if (NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif (NOT "${rm_retval}" STREQUAL 0) else (IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif (IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach (file) ulfius-2.5.2/cmake-modules/CTestCustom.cmake.in000066400000000000000000000001421341017022300213670ustar00rootroot00000000000000string(REPLACE ";" " " TESTS "@TESTS@") set(CTEST_CUSTOM_PRE_TEST "@CMAKE_MAKE_PROGRAM@ ${TESTS}")ulfius-2.5.2/cmake-modules/DownloadProject.CMakeLists.cmake.in000066400000000000000000000012041341017022300242460ustar00rootroot00000000000000# Distributed under the OSI-approved MIT License. See accompanying # file LICENSE or https://github.com/Crascit/DownloadProject for details. cmake_minimum_required(VERSION 2.8.2) project(${DL_ARGS_PROJ}-download NONE) include(ExternalProject) ExternalProject_Add(${DL_ARGS_PROJ}-download ${DL_ARGS_UNPARSED_ARGUMENTS} SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" BINARY_DIR "${DL_ARGS_BINARY_DIR}" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" ) ulfius-2.5.2/cmake-modules/DownloadProject.cmake000066400000000000000000000175531341017022300216610ustar00rootroot00000000000000# Distributed under the OSI-approved MIT License. See accompanying # file LICENSE or https://github.com/Crascit/DownloadProject for details. # # MODULE: DownloadProject # # PROVIDES: # download_project( PROJ projectName # [PREFIX prefixDir] # [DOWNLOAD_DIR downloadDir] # [SOURCE_DIR srcDir] # [BINARY_DIR binDir] # [QUIET] # ... # ) # # Provides the ability to download and unpack a tarball, zip file, git repository, # etc. at configure time (i.e. when the cmake command is run). How the downloaded # and unpacked contents are used is up to the caller, but the motivating case is # to download source code which can then be included directly in the build with # add_subdirectory() after the call to download_project(). Source and build # directories are set up with this in mind. # # The PROJ argument is required. The projectName value will be used to construct # the following variables upon exit (obviously replace projectName with its actual # value): # # projectName_SOURCE_DIR # projectName_BINARY_DIR # # The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically # need to be provided. They can be specified if you want the downloaded source # and build directories to be located in a specific place. The contents of # projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the # locations used whether you provide SOURCE_DIR/BINARY_DIR or not. # # The DOWNLOAD_DIR argument does not normally need to be set. It controls the # location of the temporary CMake build used to perform the download. # # The PREFIX argument can be provided to change the base location of the default # values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments # are provided, then PREFIX will have no effect. The default value for PREFIX is # CMAKE_BINARY_DIR. # # The QUIET option can be given if you do not want to show the output associated # with downloading the specified project. # # In addition to the above, any other options are passed through unmodified to # ExternalProject_Add() to perform the actual download, patch and update steps. # The following ExternalProject_Add() options are explicitly prohibited (they # are reserved for use by the download_project() command): # # CONFIGURE_COMMAND # BUILD_COMMAND # INSTALL_COMMAND # TEST_COMMAND # # Only those ExternalProject_Add() arguments which relate to downloading, patching # and updating of the project sources are intended to be used. Also note that at # least one set of download-related arguments are required. # # If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to # prevent a check at the remote end for changes every time CMake is run # after the first successful download. See the documentation of the ExternalProject # module for more information. It is likely you will want to use this option if it # is available to you. Note, however, that the ExternalProject implementation contains # bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when # using the URL download method or when specifying a SOURCE_DIR with no download # method. Fixes for these have been created, the last of which is scheduled for # inclusion in CMake 3.8.0. Details can be found here: # # https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c # https://gitlab.kitware.com/cmake/cmake/issues/16428 # # If you experience build errors related to the update step, consider avoiding # the use of UPDATE_DISCONNECTED. # # EXAMPLE USAGE: # # include(DownloadProject) # download_project(PROJ googletest # GIT_REPOSITORY https://github.com/google/googletest.git # GIT_TAG master # UPDATE_DISCONNECTED 1 # QUIET # ) # # add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) # #======================================================================================== set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}") include(CMakeParseArguments) function(download_project) set(options QUIET) set(oneValueArgs PROJ PREFIX DOWNLOAD_DIR SOURCE_DIR BINARY_DIR # Prevent the following from being passed through CONFIGURE_COMMAND BUILD_COMMAND INSTALL_COMMAND TEST_COMMAND ) set(multiValueArgs "") cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # Hide output if requested if (DL_ARGS_QUIET) set(OUTPUT_QUIET "OUTPUT_QUIET") else() unset(OUTPUT_QUIET) message(STATUS "Downloading/updating ${DL_ARGS_PROJ}") endif() # Set up where we will put our temporary CMakeLists.txt file and also # the base point below which the default source and binary dirs will be. # The prefix must always be an absolute path. if (NOT DL_ARGS_PREFIX) set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}") else() get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") endif() if (NOT DL_ARGS_DOWNLOAD_DIR) set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download") endif() # Ensure the caller can know where to find the source and build directories if (NOT DL_ARGS_SOURCE_DIR) set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src") endif() if (NOT DL_ARGS_BINARY_DIR) set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build") endif() set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE) set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE) # The way that CLion manages multiple configurations, it causes a copy of # the CMakeCache.txt to be copied across due to it not expecting there to # be a project within a project. This causes the hard-coded paths in the # cache to be copied and builds to fail. To mitigate this, we simply # remove the cache if it exists before we configure the new project. It # is safe to do so because it will be re-generated. Since this is only # executed at the configure step, it should not cause additional builds or # downloads. file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt") # Create and build a separate CMake project to carry out the download. # If we've already previously done these steps, they will not cause # anything to be updated, so extra rebuilds of the project won't occur. # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project # has this set to something not findable on the PATH. configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in" "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt") execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}" . RESULT_VARIABLE result ${OUTPUT_QUIET} WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" ) if(result) message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}") endif() execute_process(COMMAND ${CMAKE_COMMAND} --build . RESULT_VARIABLE result ${OUTPUT_QUIET} WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" ) if(result) message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}") endif() endfunction() ulfius-2.5.2/cmake-modules/FindCheck.cmake000066400000000000000000000050411341017022300203660ustar00rootroot00000000000000#.rst: # FindCheck # ----------- # # Find Check # # Find Check headers and libraries. # # :: # # CHECK_FOUND - True if Check found. # CHECK_INCLUDE_DIRS - Where to find check.h. # CHECK_LIBRARIES - List of libraries when using Check. # CHECK_VERSION_STRING - The version of Check found. #============================================================================= # Copyright 2018 Silvio Clecio # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= # Sat Jan 20 23:33:47 -03 2018 find_package(PkgConfig QUIET) pkg_check_modules(PC_CHECK QUIET check) find_path(CHECK_INCLUDE_DIR NAMES check.h HINTS ${PC_CHECK_INCLUDEDIR} ${PC_CHECK_INCLUDE_DIRS}) find_library(CHECK_LIBRARY NAMES check libcheck HINTS ${PC_CHECK_LIBDIR} ${PC_CHECK_LIBRARY_DIRS}) if (PC_CHECK_VERSION) set(CHECK_VERSION_STRING ${PC_CHECK_VERSION}) elseif (CHECK_INCLUDE_DIR AND EXISTS "${CHECK_INCLUDE_DIR}/check.h") set(check_version_list MAJOR MINOR MICRO) foreach (v ${check_version_list}) set(regex_check_version "^#define CHECK_${v}_VERSION +\\(?([0-9]+)\\)?$") file(STRINGS "${CHECK_INCLUDE_DIR}/check.h" check_version_${v} REGEX "${regex_check_version}") string(REGEX REPLACE "${regex_check_version}" "\\1" check_version_${v} "${check_version_${v}}") unset(regex_check_version) endforeach () set(CHECK_VERSION_STRING "${check_version_MAJOR}.${check_version_MINOR}.${check_version_MICRO}") foreach (v check_version_list) unset(check_version_${v}) endforeach () unset(check_version_list) endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Check REQUIRED_VARS CHECK_LIBRARY CHECK_INCLUDE_DIR VERSION_VAR CHECK_VERSION_STRING) if (CHECK_FOUND) set(CHECK_LIBRARIES ${CHECK_LIBRARY}) set(CHECK_INCLUDE_DIRS ${CHECK_INCLUDE_DIR}) endif () mark_as_advanced(CHECK_INCLUDE_DIR CHECK_LIBRARY)ulfius-2.5.2/cmake-modules/FindJansson.cmake000066400000000000000000000045071341017022300207720ustar00rootroot00000000000000#.rst: # FindJansson # ----------- # # Find Jansson # # Find Jansson headers and libraries. # # :: # # JANSSON_FOUND - True if Jansson found. # JANSSON_INCLUDE_DIRS - Where to find jansson.h. # JANSSON_LIBRARIES - List of libraries when using Jansson. # JANSSON_VERSION_STRING - The version of Jansson found. #============================================================================= # Copyright 2018 Silvio Clecio # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= # Sat Jan 20 12:32:26 -03 2018 find_package(PkgConfig QUIET) pkg_check_modules(PC_JANSSON QUIET jansson) find_path(JANSSON_INCLUDE_DIR NAMES jansson.h HINTS ${PC_JANSSON_INCLUDEDIR} ${PC_JANSSON_INCLUDE_DIRS}) find_library(JANSSON_LIBRARY NAMES jansson libjansson HINTS ${PC_JANSSON_LIBDIR} ${PC_JANSSON_LIBRARY_DIRS}) if (PC_JANSSON_VERSION) set(JANSSON_VERSION_STRING ${PC_JANSSON_VERSION}) elseif (JANSSON_INCLUDE_DIR AND EXISTS "${JANSSON_INCLUDE_DIR}/jansson.h") set(regex_jansson_version "^#define[ \t]+JANSSON_VERSION[ \t]+\"([^\"]+)\".*") file(STRINGS "${JANSSON_INCLUDE_DIR}/jansson.h" jansson_version REGEX "${regex_jansson_version}") string(REGEX REPLACE "${regex_jansson_version}" "\\1" JANSSON_VERSION_STRING "${jansson_version}") unset(regex_jansson_version) unset(jansson_version) endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Jansson REQUIRED_VARS JANSSON_LIBRARY JANSSON_INCLUDE_DIR VERSION_VAR JANSSON_VERSION_STRING) if (JANSSON_FOUND) set(JANSSON_LIBRARIES ${JANSSON_LIBRARY}) set(JANSSON_INCLUDE_DIRS ${JANSSON_INCLUDE_DIR}) endif () mark_as_advanced(JANSSON_INCLUDE_DIR JANSSON_LIBRARY)ulfius-2.5.2/cmake-modules/FindMHD.cmake000066400000000000000000000054141341017022300177650ustar00rootroot00000000000000#.rst: # FindJansson # ----------- # # Find libmicrohttpd # # Find libmicrohttpd headers and libraries. # # :: # # MHD_FOUND - True if libmicrohttpd found. # MHD_INCLUDE_DIRS - Where to find microhttpd.h. # MHD_LIBRARIES - List of libraries when using libmicrohttpd. # MHD_VERSION_STRING - The version of libmicrohttpd found. #============================================================================= # Copyright 2018 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= find_package(PkgConfig QUIET) pkg_check_modules(PC_MHD QUIET libmicrohttpd) find_path(MHD_INCLUDE_DIR NAMES microhttpd.h HINTS ${PC_MHD_INCLUDEDIR} ${PC_MHD_INCLUDE_DIRS}) find_library(MHD_LIBRARY NAMES libmicrohttpd microhttpd HINTS ${PC_MHD_LIBDIR} ${PC_MHD_LIBRARY_DIRS}) if (PC_MHD_VERSION) set(MHD_VERSION_STRING ${PC_MHD_VERSION}) elseif (MHD_INCLUDE_DIR AND EXISTS "${MHD_INCLUDE_DIR}/microhttpd.h") set(regex_mhd_version "^#define[ \t]+MHD_VERSION[ \t]+([^\"]+).*") file(STRINGS "${MHD_INCLUDE_DIR}/microhttpd.h" mhd_version REGEX "${regex_mhd_version}") string(REGEX REPLACE "${regex_mhd_version}" "\\1" MHD_VERSION_NUM "${mhd_version}") unset(regex_mhd_version) unset(mhd_version) # parse MHD_VERSION from numerical format 0x12345678 to string format "12.34.56.78" so the version value can be compared to the one returned by pkg-config string(SUBSTRING ${MHD_VERSION_NUM} 2 2 MHD_VERSION_STRING_MAJOR) string(SUBSTRING ${MHD_VERSION_NUM} 4 2 MHD_VERSION_STRING_MINOR) string(SUBSTRING ${MHD_VERSION_NUM} 6 2 MHD_VERSION_STRING_REVISION) string(SUBSTRING ${MHD_VERSION_NUM} 8 2 MHD_VERSION_STRING_PATCH) set(MHD_VERSION_STRING "${MHD_VERSION_STRING_MAJOR}.${MHD_VERSION_STRING_MINOR}.${MHD_VERSION_STRING_REVISION}.${MHD_VERSION_STRING_PATCH}") endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(mhd REQUIRED_VARS MHD_LIBRARY MHD_INCLUDE_DIR VERSION_VAR MHD_VERSION_STRING) if (MHD_FOUND) set(MHD_LIBRARIES ${MHD_LIBRARY}) set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR}) endif () mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY) ulfius-2.5.2/cmake-modules/FindOrcania.cmake000066400000000000000000000054351341017022300207340ustar00rootroot00000000000000#.rst: # FindOrcania # ----------- # # Find Orcania # # Find Orcania headers and libraries. # # :: # # ORCANIA_FOUND - True if Orcania found. # ORCANIA_INCLUDE_DIRS - Where to find orcania.h. # ORCANIA_LIBRARIES - List of libraries when using Orcania. # ORCANIA_VERSION_STRING - The version of Orcania found. #============================================================================= # Copyright 2018 Silvio Clecio # Copyright 2018 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= find_package(PkgConfig QUIET) pkg_check_modules(PC_ORCANIA QUIET liborcania) find_path(ORCANIA_INCLUDE_DIR NAMES orcania.h HINTS ${PC_ORCANIA_INCLUDEDIR} ${PC_ORCANIA_INCLUDE_DIRS}) find_library(ORCANIA_LIBRARY NAMES orcania liborcania HINTS ${PC_ORCANIA_LIBDIR} ${PC_ORCANIA_LIBRARY_DIRS}) set(ORCANIA_VERSION_STRING 0.0.0) if (PC_ORCANIA_VERSION) set(ORCANIA_VERSION_STRING ${PC_ORCANIA_VERSION}) elseif (ORCANIA_INCLUDE_DIR AND EXISTS "${ORCANIA_INCLUDE_DIR}/orcania.h") set(regex_orcania_version "^#define[ \t]+ORCANIA_VERSION[ \t]+([^\"]+).*") file(STRINGS "${ORCANIA_INCLUDE_DIR}/orcania.h" orcania_version REGEX "${regex_orcania_version}") string(REGEX REPLACE "${regex_orcania_version}" "\\1" ORCANIA_VERSION_STRING "${orcania_version}") unset(regex_orcania_version) unset(orcania_version) if (NOT ORCANIA_VERSION_STRING) set(regex_orcania_version "^#define[ \t]+ORCANIA_VERSION[ \t]+([^\"]+).*") file(STRINGS "${ORCANIA_INCLUDE_DIR}/orcania-cfg.h" orcania_version REGEX "${regex_orcania_version}") string(REGEX REPLACE "${regex_orcania_version}" "\\1" ORCANIA_VERSION_STRING "${orcania_version}") unset(regex_orcania_version) unset(orcania_version) endif () endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Orcania REQUIRED_VARS ORCANIA_LIBRARY ORCANIA_INCLUDE_DIR VERSION_VAR ORCANIA_VERSION_STRING) if (ORCANIA_FOUND) set(ORCANIA_LIBRARIES ${ORCANIA_LIBRARY}) set(ORCANIA_INCLUDE_DIRS ${ORCANIA_INCLUDE_DIR}) endif () mark_as_advanced(ORCANIA_INCLUDE_DIR ORCANIA_LIBRARY) ulfius-2.5.2/cmake-modules/FindSubunit.cmake000066400000000000000000000036421341017022300210070ustar00rootroot00000000000000#.rst: # FindSubunit # ----------- # # Find Subunit # # Find Subunit headers and libraries. # # :: # # SUBUNIT_FOUND - True if Subunit found. # SUBUNIT_INCLUDE_DIRS - Where to find subunit/child.h. # SUBUNIT_LIBRARIES - List of libraries when using Subunit. # SUBUNIT_VERSION_STRING - The version of Subunit found. #============================================================================= # Copyright 2018 Silvio Clecio # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= # Fri Jan 19 22:27:51 -03 2018 find_package(PkgConfig QUIET) pkg_check_modules(PC_SUBUNIT QUIET libsubunit) set(SUBUNIT_VERSION_STRING "${PC_SUBUNIT_VERSION}") find_path(SUBUNIT_INCLUDE_DIR NAMES child.h HINTS ${PC_SUBUNIT_INCLUDEDIR} ${PC_SUBUNIT_INCLUDE_DIRS} PATH_SUFFIXES subunit) find_library(SUBUNIT_LIBRARY NAMES subunit libsubunit HINTS ${PC_SUBUNIT_LIBDIR} ${PC_SUBUNIT_LIBRARY_DIRS}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Subunit REQUIRED_VARS SUBUNIT_LIBRARY SUBUNIT_INCLUDE_DIR VERSION_VAR SUBUNIT_VERSION_STRING) if (SUBUNIT_FOUND) set(SUBUNIT_LIBRARIES ${SUBUNIT_LIBRARY}) set(SUBUNIT_INCLUDE_DIRS ${SUBUNIT_INCLUDE_DIR}) endif () mark_as_advanced(SUBUNIT_INCLUDE_DIR SUBUNIT_LIBRARY)ulfius-2.5.2/cmake-modules/FindYder.cmake000066400000000000000000000051411341017022300202550ustar00rootroot00000000000000#.rst: # FindYder # ----------- # # Find Yder # # Find Yder headers and libraries. # # :: # # YDER_FOUND - True if Yder found. # YDER_INCLUDE_DIRS - Where to find yder.h. # YDER_LIBRARIES - List of libraries when using Yder. # YDER_VERSION_STRING - The version of Yder found. #============================================================================= # Copyright 2018 Nicolas Mora # Copyright 2018 Silvio Clecio # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= find_package(PkgConfig QUIET) pkg_check_modules(PC_YDER QUIET libyder) find_path(YDER_INCLUDE_DIR NAMES yder.h HINTS ${PC_YDER_INCLUDEDIR} ${PC_YDER_INCLUDE_DIRS}) find_library(YDER_LIBRARY NAMES yder libyder HINTS ${PC_YDER_LIBDIR} ${PC_YDER_LIBRARY_DIRS}) set(YDER_VERSION_STRING 0.0.0) if (PC_YDER_VERSION) set(YDER_VERSION_STRING ${PC_YDER_VERSION}) elseif (YDER_INCLUDE_DIR AND EXISTS "${YDER_INCLUDE_DIR}/yder.h") set(regex_yder_version "^#define[ \t]+YDER_VERSION[ \t]+([^\"]+).*") file(STRINGS "${YDER_INCLUDE_DIR}/yder.h" yder_version REGEX "${regex_yder_version}") string(REGEX REPLACE "${regex_yder_version}" "\\1" YDER_VERSION_STRING "${yder_version}") unset(regex_yder_version) unset(yder_version) if (NOT YDER_VERSION_STRING) set(regex_yder_version "^#define[ \t]+YDER_VERSION[ \t]+([^\"]+).*") file(STRINGS "${YDER_INCLUDE_DIR}/yder-cfg.h" yder_version REGEX "${regex_yder_version}") string(REGEX REPLACE "${regex_yder_version}" "\\1" YDER_VERSION_STRING "${yder_version}") unset(regex_yder_version) unset(yder_version) endif () endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Yder REQUIRED_VARS YDER_LIBRARY YDER_INCLUDE_DIR VERSION_VAR YDER_VERSION_STRING) if (YDER_FOUND) set(YDER_LIBRARIES ${YDER_LIBRARY}) set(YDER_INCLUDE_DIRS ${YDER_INCLUDE_DIR}) endif () mark_as_advanced(YDER_INCLUDE_DIR YDER_LIBRARY) ulfius-2.5.2/example_callbacks/000077500000000000000000000000001341017022300164525ustar00rootroot00000000000000ulfius-2.5.2/example_callbacks/oauth2_bearer/000077500000000000000000000000001341017022300211745ustar00rootroot00000000000000ulfius-2.5.2/example_callbacks/oauth2_bearer/README.md000066400000000000000000000034611341017022300224570ustar00rootroot00000000000000# Token validation for resource service based on [Ulfius](https://github.com/babelouest/ulfius) framework These files contain an authentication callback for Ulfius framework to validate a Glewlwyd access token with the correct scope. To use this file, you must create a `struct _glewlwyd_resource_config` with your specific parameters: ```C struct _glewlwyd_resource_config { int method; // Values are G_METHOD_HEADER, G_METHOD_BODY or G_METHOD_URL for the access_token location, see https://tools.ietf.org/html/rfc6750 char * oauth_scope; // Scope values required by the resource, multiple values must be separated by a space character char * jwt_decode_key; // The key used to decode an access token jwt_alg_t jwt_alg; // The algorithm used to encode a token, see http://benmcollins.github.io/libjwt/ char * realm; // Optional, a realm value that will be sent back to the client unsigned short accept_access_token; // required, accept type acces_token unsigned short accept_client_token; // required, accept type client_token }; ``` Then, you use `callback_check_glewlwyd_access_token` as authentication callback for your ulfius endpoints that need to validate a glewlwyd access_token, example: ```C struct _glewlwyd_resource_config g_config; g_config.method = G_METHOD_HEADER; g_config.oauth_scope = "scope1"; g_config.jwt_decode_key = "secret"; g_config.jwt_alg = JWT_ALG_HS512; g_config.realm = "example"; g_config.accept_access_token = 1; g_config.accept_client_token = 0; // Example, add an authentication callback callback_check_glewlwyd_access_token for the endpoint GET "/api/resource/*" ulfius_add_endpoint_by_val(instance, "GET", "/api", "/resource/*", 0, &callback_check_glewlwyd_access_token, (void*)g_config); ``` ulfius-2.5.2/example_callbacks/oauth2_bearer/glewlwyd_resource.c000066400000000000000000000236271341017022300251170ustar00rootroot00000000000000/** * * Glewlwyd OAuth2 Authorization token check * * Copyright 2016-2018 Nicolas Mora * * Version 20180607 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * License as published by the Free Software Foundation; * version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include "glewlwyd_resource.h" /** * check if bearer token has some of the specified scope */ int callback_check_glewlwyd_access_token (const struct _u_request * request, struct _u_response * response, void * user_data) { struct _glewlwyd_resource_config * config = (struct _glewlwyd_resource_config *)user_data; json_t * j_access_token = NULL, * j_res_scope; int res = U_CALLBACK_UNAUTHORIZED, res_validity; const char * token_value = NULL; char * response_value = NULL; if (config != NULL) { switch (config->method) { case G_METHOD_HEADER: if (u_map_get(request->map_header, HEADER_AUTHORIZATION) != NULL) { if (o_strstr(u_map_get(request->map_header, HEADER_AUTHORIZATION), HEADER_PREFIX_BEARER) == u_map_get(request->map_header, HEADER_AUTHORIZATION)) { token_value = u_map_get(request->map_header, HEADER_AUTHORIZATION) + o_strlen(HEADER_PREFIX_BEARER); } } break; case G_METHOD_BODY: if (o_strstr(u_map_get(request->map_header, ULFIUS_HTTP_HEADER_CONTENT), MHD_HTTP_POST_ENCODING_FORM_URLENCODED) != NULL && u_map_get(request->map_post_body, BODY_URL_PARAMETER) != NULL) { token_value = u_map_get(request->map_post_body, BODY_URL_PARAMETER); } break; case G_METHOD_URL: token_value = u_map_get(request->map_url, BODY_URL_PARAMETER); break; } if (token_value != NULL) { j_access_token = access_token_check_signature(config, token_value); if (check_result_value(j_access_token, G_OK)) { res_validity = access_token_check_validity(config, json_object_get(j_access_token, "grants")); if (res_validity == G_OK) { j_res_scope = access_token_check_scope(config, json_object_get(j_access_token, "grants")); if (check_result_value(j_res_scope, G_ERROR_INSUFFICIENT_SCOPE)) { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"insufficient_scope\",error_description=\"The scope is invalid\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } else if (!check_result_value(j_res_scope, G_OK)) { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"Internal server error\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } else { res = U_CALLBACK_CONTINUE; response->shared_data = (void*)json_pack("{sssO}", "username", json_string_value(json_object_get(json_object_get(j_access_token, "grants"), "username")), "scope", json_object_get(j_res_scope, "scope")); if (response->shared_data == NULL) { res = U_CALLBACK_ERROR; } } json_decref(j_res_scope); } else if (res_validity == G_ERROR_INVALID_TOKEN) { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"The access token is invalid\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } else { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"Internal server error\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } } else { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"The access token is invalid\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } json_decref(j_access_token); } else { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_token\",error_description=\"The access token is missing\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } } return res; } /** * Validates if an access_token grants has a valid scope * return the final scope list on success */ json_t * access_token_check_scope(struct _glewlwyd_resource_config * config, json_t * j_access_token) { int i, scope_count_token, scope_count_expected; char ** scope_list_token, ** scope_list_expected; json_t * j_res = NULL, * j_scope_final_list = json_array(); if (j_scope_final_list != NULL) { if (j_access_token != NULL) { scope_count_token = split_string(json_string_value(json_object_get(j_access_token, "scope")), " ", &scope_list_token); scope_count_expected = split_string(config->oauth_scope, " ", &scope_list_expected); if (scope_count_token > 0 && scope_count_expected > 0) { for (i=0; scope_count_expected > 0 && scope_list_expected[i] != NULL; i++) { if (string_array_has_value((const char **)scope_list_token, scope_list_expected[i])) { json_array_append_new(j_scope_final_list, json_string(scope_list_expected[i])); } } if (json_array_size(j_scope_final_list) > 0) { j_res = json_pack("{sisO}", "result", G_OK, "scope", j_scope_final_list); } else { j_res = json_pack("{si}", "result", G_ERROR_INSUFFICIENT_SCOPE); } } else { j_res = json_pack("{si}", "result", G_ERROR_INTERNAL); } free_string_array(scope_list_token); free_string_array(scope_list_expected); } else { j_res = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } } else { j_res = json_pack("{si}", "result", G_ERROR_INTERNAL); } json_decref(j_scope_final_list); return j_res; } /** * Validates if an access_token grants has valid parameters: * - username: non empty string * - type: match "access_token" * - iat + expires_in < now */ int access_token_check_validity(struct _glewlwyd_resource_config * config, json_t * j_access_token) { time_t now; json_int_t expiration; int res; if (j_access_token != NULL) { // Token is valid, check type and expiration date time(&now); expiration = json_integer_value(json_object_get(j_access_token, "iat")) + json_integer_value(json_object_get(j_access_token, "expires_in")); if (now < expiration && json_object_get(j_access_token, "type") != NULL && json_is_string(json_object_get(j_access_token, "type")) && 0 == o_strcmp("access_token", json_string_value(json_object_get(j_access_token, "type"))) && json_object_get(j_access_token, "username") != NULL && json_is_string(json_object_get(j_access_token, "username")) && json_string_length(json_object_get(j_access_token, "username")) > 0) { res = G_OK; } else { res = G_ERROR_INVALID_REQUEST; } } else { res = G_ERROR_INVALID_TOKEN; } return res; } /** * validates if the token value is a valid jwt and has a valid signature */ json_t * access_token_check_signature(struct _glewlwyd_resource_config * config, const char * token_value) { json_t * j_return, * j_grants; jwt_t * jwt = NULL; char * grants; if (token_value != NULL) { if (!jwt_decode(&jwt, token_value, (const unsigned char *)config->jwt_decode_key, o_strlen(config->jwt_decode_key)) && jwt_get_alg(jwt) == config->jwt_alg) { grants = jwt_get_grants_json(jwt, NULL); j_grants = json_loads(grants, JSON_DECODE_ANY, NULL); if (j_grants != NULL) { j_return = json_pack("{siso}", "result", G_OK, "grants", j_grants); } else { j_return = json_pack("{si}", "result", G_ERROR); } o_free(grants); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } jwt_free(jwt); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } return j_return; } /** * Return the ayload of an access token */ json_t * access_token_get_payload(const char * token_value) { json_t * j_return, * j_grants; jwt_t * jwt = NULL; char * grants; if (token_value != NULL) { if (!jwt_decode(&jwt, token_value, NULL, 0)) { grants = jwt_get_grants_json(jwt, NULL); j_grants = json_loads(grants, JSON_DECODE_ANY, NULL); if (j_grants != NULL) { j_return = json_pack("{siso}", "result", G_OK, "grants", j_grants); } else { j_return = json_pack("{si}", "result", G_ERROR); } o_free(grants); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } jwt_free(jwt); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } return j_return; } ulfius-2.5.2/example_callbacks/oauth2_bearer/glewlwyd_resource.h000066400000000000000000000035751341017022300251240ustar00rootroot00000000000000/** * * Glewlwyd OAuth2 Authorization token check * * Copyright 2016-2018 Nicolas Mora * * Version 20180607 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * License as published by the Free Software Foundation; * version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #include #define G_OK 0 #define G_ERROR 1 #define G_ERROR_INTERNAL 2 #define G_ERROR_INVALID_REQUEST 3 #define G_ERROR_INVALID_TOKEN 4 #define G_ERROR_INSUFFICIENT_SCOPE 5 #define G_METHOD_HEADER 0 #define G_METHOD_BODY 1 #define G_METHOD_URL 2 #define HEADER_PREFIX_BEARER "Bearer " #define HEADER_RESPONSE "WWW-Authenticate" #define HEADER_AUTHORIZATION "Authorization" #define BODY_URL_PARAMETER "access_token" struct _glewlwyd_resource_config { int method; char * oauth_scope; char * jwt_decode_key; jwt_alg_t jwt_alg; char * realm; }; int callback_check_glewlwyd_access_token (const struct _u_request * request, struct _u_response * response, void * user_data); json_t * access_token_check_signature(struct _glewlwyd_resource_config * config, const char * token_value); json_t * access_token_get_payload(const char * token_value); int access_token_check_validity(struct _glewlwyd_resource_config * config, json_t * j_access_token); json_t * access_token_check_scope(struct _glewlwyd_resource_config * config, json_t * j_access_token); ulfius-2.5.2/example_callbacks/static_file/000077500000000000000000000000001341017022300207405ustar00rootroot00000000000000ulfius-2.5.2/example_callbacks/static_file/README.md000066400000000000000000000010221341017022300222120ustar00rootroot00000000000000# static file server callback function for Ulfius Framework Provides a simple static file server. `user_data` must be initialized with a `struct static_file_config` containing the following informations: - `files_path`: path to the DocumentRoot folder, can be relative or absolute - `mime_types`: a `struct _u_map` containing a set of mime-types with file extension as key and mime-type as value - `map_header`: a `struct _u_map` containing a set of headers that will be added to all responses within the `static_file_callback` ulfius-2.5.2/example_callbacks/static_file/static_file_callback.c000066400000000000000000000107121341017022300252070ustar00rootroot00000000000000/** * * Static file server Ulfius callback * * Copyright 2017-2018 Nicolas Mora * * Version 20181110 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * License as published by the Free Software Foundation; * version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #include #include #include "static_file_callback.h" /** * Return the filename extension */ const char * get_filename_ext(const char *path) { const char *dot = strrchr(path, '.'); if(!dot || dot == path) return "*"; if (strchr(dot, '?') != NULL) { *strchr(dot, '?') = '\0'; } return dot; } /** * Streaming callback function to ease sending large files */ static ssize_t callback_static_file_stream(void * cls, uint64_t pos, char * buf, size_t max) { if (cls != NULL) { return fread (buf, 1, max, (FILE *)cls); } else { return U_STREAM_END; } } /** * Cleanup FILE* structure when streaming is complete */ static void callback_static_file_stream_free(void * cls) { if (cls != NULL) { fclose((FILE *)cls); } } /** * static file callback endpoint */ int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data) { size_t length; FILE * f; char * file_requested, * file_path, * url_dup_save; const char * content_type; /* * Comment this if statement if you put static files url not in root, like /app */ if (response->shared_data != NULL) { return U_CALLBACK_CONTINUE; } if (user_data != NULL && ((struct _static_file_config *)user_data)->files_path != NULL) { file_requested = o_strdup(request->http_url); url_dup_save = file_requested; while (file_requested[0] == '/') { file_requested++; } file_requested += o_strlen(((struct _static_file_config *)user_data)->url_prefix); while (file_requested[0] == '/') { file_requested++; } if (strchr(file_requested, '#') != NULL) { *strchr(file_requested, '#') = '\0'; } if (strchr(file_requested, '?') != NULL) { *strchr(file_requested, '?') = '\0'; } if (file_requested == NULL || o_strlen(file_requested) == 0 || 0 == o_strcmp("/", file_requested)) { o_free(url_dup_save); url_dup_save = file_requested = o_strdup("index.html"); } file_path = msprintf("%s/%s", ((struct _static_file_config *)user_data)->files_path, file_requested); if (access(file_path, F_OK) != -1) { f = fopen (file_path, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); content_type = u_map_get_case(((struct _static_file_config *)user_data)->mime_types, get_filename_ext(file_requested)); if (content_type == NULL) { content_type = u_map_get(((struct _static_file_config *)user_data)->mime_types, "*"); y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested)); } u_map_put(response->map_header, "Content-Type", content_type); u_map_copy_into(response->map_header, ((struct _static_file_config *)user_data)->map_header); if (ulfius_set_stream_response(response, 200, callback_static_file_stream, callback_static_file_stream_free, length, STATIC_FILE_CHUNK, f) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_file - Error ulfius_set_stream_response"); } } } else { if (((struct _static_file_config *)user_data)->redirect_on_404 == NULL) { ulfius_set_string_body_response(response, 404, "File not found"); } else { ulfius_add_header_to_response(response, "Location", ((struct _static_file_config *)user_data)->redirect_on_404); response->status = 302; } } o_free(file_path); o_free(url_dup_save); return U_CALLBACK_CONTINUE; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error, user_data is NULL or inconsistent"); return U_CALLBACK_ERROR; } } ulfius-2.5.2/example_callbacks/static_file/static_file_callback.h000066400000000000000000000027421341017022300252200ustar00rootroot00000000000000/** * * Version 20181110 * * struct static_file_config must be initialized with proper values * files_path: path (relative or absolute) to the DocumentRoot folder * url_prefix: prefix used to access the callback function * mime_types: a struct _u_map filled with all the mime-types needed for a static file server * redirect_on_404: redirct uri on error 404, if NULL, send 404 * * example of mime-types used in Hutch: * { * key = ".html" * value = "text/html" * }, * { * key = ".css" * value = "text/css" * }, * { * key = ".js" * value = "application/javascript" * }, * { * key = ".png" * value = "image/png" * }, * { * key = ".jpg" * value = "image/jpeg" * }, * { * key = ".jpeg" * value = "image/jpeg" * }, * { * key = ".ttf" * value = "font/ttf" * }, * { * key = ".woff" * value = "font/woff" * }, * { * key = ".woff2" * value = "font/woff2" * }, * { * key = ".map" * value = "application/octet-stream" * }, * { * key = "*" * value = "application/octet-stream" * } * */ #ifndef _STATIC_FILE #define _STATIC_FILE #define STATIC_FILE_CHUNK 256 struct _static_file_config { char * files_path; char * url_prefix; struct _u_map * mime_types; struct _u_map * map_header; char * redirect_on_404; }; int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data); const char * get_filename_ext(const char *path); #endif ulfius-2.5.2/example_programs/000077500000000000000000000000001341017022300163655ustar00rootroot00000000000000ulfius-2.5.2/example_programs/.gitignore000066400000000000000000000000301341017022300203460ustar00rootroot00000000000000*.key *.pem *.crt *.csr ulfius-2.5.2/example_programs/CMakeLists.txt000066400000000000000000000104501341017022300211250ustar00rootroot00000000000000# # Ulfius library # # CMake file used to build example programs # # Copyright 2018 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # cmake_minimum_required(VERSION 3.5) project(ulfius_examples C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") # library info set(PROJECT_DESCRIPTION "HTTP Framework for REST API in C, using JSON or not, with websockets or not, with streaming data or not") set(PROJECT_BUGREPORT_PATH "https://github.com/babelouest/ulfius/issues") include(GNUInstallDirs) include(CheckSymbolExists) # cmake modules set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules) # check if _GNU_SOURCE is available if (NOT _GNU_SOURCE) check_symbol_exists(__GNU_LIBRARY__ "features.h" _GNU_SOURCE) if (NOT _GNU_SOURCE) unset(_GNU_SOURCE CACHE) check_symbol_exists(_GNU_SOURCE "features.h" _GNU_SOURCE) endif () endif () if (_GNU_SOURCE) add_definitions(-D_GNU_SOURCE) endif () include(FindUlfius) set(ULFIUS_MIN_VERSION "2.5") find_package(Ulfius ${ULFIUS_MIN_VERSION} REQUIRED) set(LIBS ${LIBS} ${ULFIUS_LIBRARIES} "-lorcania -ljansson") include_directories(${ULFIUS_INCLUDE_DIRS}) include(FindYder) find_package(Yder) if (YDER_FOUND) set(LIBS ${LIBS} "-lyder") include_directories(${YDER_INCLUDE_DIRS}) endif () # examples set(STATIC_CALLBACK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../example_callbacks/static_file/) include_directories(${STATIC_CALLBACK_DIR}) add_executable(simple_example ${CMAKE_CURRENT_SOURCE_DIR}/simple_example/simple_example.c) target_link_libraries(simple_example ${LIBS}) add_executable(multiple_callbacks_example ${CMAKE_CURRENT_SOURCE_DIR}/multiple_callbacks_example/multiple_callbacks_example.c) target_link_libraries(multiple_callbacks_example ${LIBS}) add_executable(stream_example ${CMAKE_CURRENT_SOURCE_DIR}/stream_example/stream_example.c) target_link_libraries(stream_example ${LIBS}) add_executable(test_u_map ${CMAKE_CURRENT_SOURCE_DIR}/test_u_map/test_u_map.c) target_link_libraries(test_u_map ${LIBS}) if (WITH_CURL) add_executable(stream_client ${CMAKE_CURRENT_SOURCE_DIR}/stream_example/stream_client.c) target_link_libraries(stream_client ${LIBS}) endif () if (WITH_JANSSON) add_executable(injection_example ${CMAKE_CURRENT_SOURCE_DIR}/injection_example/injection_example.c) target_link_libraries(injection_example ${LIBS}) add_executable(sheep_counter ${CMAKE_CURRENT_SOURCE_DIR}/sheep_counter/sheep_counter.c) target_link_libraries(sheep_counter ${LIBS}) endif () if (WITH_CURL AND WITH_JANSSON) add_executable(proxy_example ${CMAKE_CURRENT_SOURCE_DIR}/proxy_example/proxy.c) target_link_libraries(proxy_example ${LIBS}) add_executable(request_client ${CMAKE_CURRENT_SOURCE_DIR}/request_example/client.c) target_link_libraries(request_client ${LIBS}) add_executable(request_server ${CMAKE_CURRENT_SOURCE_DIR}/request_example/server.c) target_link_libraries(request_server ${LIBS}) add_executable(request_mail ${CMAKE_CURRENT_SOURCE_DIR}/request_example/mail.c) target_link_libraries(request_mail ${LIBS}) endif () if (WITH_WEBSOCKET) add_executable(websocket_server ${CMAKE_CURRENT_SOURCE_DIR}/websocket_example/websocket_server.c ${STATIC_CALLBACK_DIR}/static_file_callback.c) target_link_libraries(websocket_server ${LIBS}) add_executable(websocket_client ${CMAKE_CURRENT_SOURCE_DIR}/websocket_example/websocket_client.c ${STATIC_CALLBACK_DIR}/static_file_callback.c) target_link_libraries(websocket_client ${LIBS}) add_executable(auth_server ${CMAKE_CURRENT_SOURCE_DIR}/auth_example/auth_server.c) target_link_libraries(auth_server ${LIBS} gnutls) if (WITH_CURL) add_executable(auth_client ${CMAKE_CURRENT_SOURCE_DIR}/auth_example/auth_client.c) target_link_libraries(auth_client ${LIBS}) endif () endif () message(STATUS "Websocket support: ${WITH_WEBSOCKET}") message(STATUS "Outgoing requests support: ${WITH_CURL}") message(STATUS "Jansson library support: ${WITH_JANSSON}") message(STATUS "Yder library support: ${WITH_YDER}") ulfius-2.5.2/example_programs/Makefile000066400000000000000000000035601341017022300200310ustar00rootroot00000000000000# # Example program # # Makefile used to build all programs # # Copyright 2014-2015 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # ULFIUS_LOCATION=../src SIMPLE_EXAMPLE_LOCATION=./simple_example SHEEP_COUNTER_LOCATION=./sheep_counter PROXY_EXAMPLE_LOCATION=./proxy_example REQUEST_EXAMPLE_LOCATION=./request_example TEST_U_MAP_LOCATION=./test_u_map INJECTION_EXAMPLE_LOCATION=./injection_example AUTH_EXAMPLE_LOCATION=./auth_example STREAM_EXAMPLE_LOCATION=./stream_example MULTIPLE_CALLBACKS_LOCATION=./multiple_callbacks_example WEBSOCKET_EXAMPLE_LOCATION=./websocket_example all: debug debug: cd $(ULFIUS_LOCATION) && $(MAKE) debug cd $(SIMPLE_EXAMPLE_LOCATION) && $(MAKE) debug cd $(SHEEP_COUNTER_LOCATION) && $(MAKE) debug cd $(PROXY_EXAMPLE_LOCATION) && $(MAKE) debug cd $(REQUEST_EXAMPLE_LOCATION) && $(MAKE) debug cd $(INJECTION_EXAMPLE_LOCATION) && $(MAKE) debug cd $(AUTH_EXAMPLE_LOCATION) && $(MAKE) debug cd $(STREAM_EXAMPLE_LOCATION) && $(MAKE) debug cd $(TEST_U_MAP_LOCATION) && $(MAKE) debug cd $(MULTIPLE_CALLBACKS_LOCATION) && $(MAKE) debug cd $(WEBSOCKET_EXAMPLE_LOCATION) && $(MAKE) debug clean: cd $(SIMPLE_EXAMPLE_LOCATION) && $(MAKE) clean cd $(SHEEP_COUNTER_LOCATION) && $(MAKE) clean cd $(PROXY_EXAMPLE_LOCATION) && $(MAKE) clean cd $(REQUEST_EXAMPLE_LOCATION) && $(MAKE) clean cd $(INJECTION_EXAMPLE_LOCATION) && $(MAKE) clean cd $(AUTH_EXAMPLE_LOCATION) && $(MAKE) clean cd $(STREAM_EXAMPLE_LOCATION) && $(MAKE) clean cd $(TEST_U_MAP_LOCATION) && $(MAKE) clean cd $(MULTIPLE_CALLBACKS_LOCATION) && $(MAKE) clean cd $(WEBSOCKET_EXAMPLE_LOCATION) && $(MAKE) clean ulfius-2.5.2/example_programs/README.md000066400000000000000000000047621341017022300176550ustar00rootroot00000000000000# Ulfius Framework example programs Those programs require ulfius to be installed with its dependencies. To do so, in the root folder, run `make && sudo make install` to compile and install the library. Refer to the `README.md` for more information. Each program implement some functionalities provided by Ulfius, such as url parsing, request filling and response manipulation. For each example program, you can look at its `README.md`. Usually, a simple `make ` or `make test` is sufficient to run the example program. Or you can build all of them using the `CMake` script. ## Example programs available The example programs were developped to help implementing the functionnalities of the framework and test them. The tested functionnalities are: - `simple_example`: endpoint tests, url parameters, body parameters, json body parameters and cookies - `sheep_counter`: file server, upload file - `injection_example`: endpoints injection and remove during the program execution - `proxy_example`: proxyfies calls to another url - `auth_example`: HTTP Basic Auth - `stream_example`: data streaming (server and client side) - `request_example`: send http and smtp requests - `test_u_map`: struct _u_map tests - `multiple_callbacks_example`: Test new feature in Ulfius 2.0 where you can access multiple callback functions on a single endpoint, one after the other - `websocket_example`: Test websocket functionality, new feature in Ulfius 2.0 ## Build ### CMake - Multi architecture You can build Ulfius example programs using `cmake`, Ulfius library must be already installed, example: ```shell $ cd example_programs $ mkdir build $ cd build $ cmake .. $ make $ ./simple_example ``` The available options for `cmake` are: - `-DWITH_JANSSON=[on|off]` (default `on`): Build example programs with Jansson dependency - `-DWITH_CURL=[on|off]` (default `on`): Build example programs with libcurl dependency - `-DWITH_WEBSOCKET=[on|off]` (default `on`): Build example program with websocket functions, not available for Windows, requires libmicrohttpd 0.9.53 minimum and GnuTLS installed. ### Good ol' Makefile You can either use the `Makefile` available in the `example_programs` folder or each `Makefile` in each subfolders. If your ulfius library is built with options like `make CURLFLAG=1`, you must use the same option to build the examples. ```shell $ # Build all example programs $ cd example_programs $ make $ ./simple_example/simple_example $ # Build only simple_example $ cd example_programs/simple_example $ make && make test ``` ulfius-2.5.2/example_programs/auth_example/000077500000000000000000000000001341017022300210415ustar00rootroot00000000000000ulfius-2.5.2/example_programs/auth_example/Makefile000066400000000000000000000027231341017022300225050ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-lc -lgnutls -lulfius -lorcania -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: auth_client auth_server clean: rm -f *.o auth_client auth_server debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: auth_client auth_server ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON auth_client.o: auth_client.c ../../src/libulfius.so $(CC) $(CFLAGS) auth_client.c -DDEBUG -g -O0 auth_client: auth_client.o $(CC) -o auth_client auth_client.o $(LIBS) test_auth_client: auth_client LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./auth_client auth_server.o: auth_server.c ../../src/libulfius.so $(CC) $(CFLAGS) auth_server.c -DDEBUG -g -O0 auth_server: auth_server.o $(CC) -o auth_server auth_server.o $(LIBS) test_auth_server: auth_server LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./auth_server ulfius-2.5.2/example_programs/auth_example/README.md000066400000000000000000000007651341017022300223300ustar00rootroot00000000000000# Authentication example Provides an application that implement basic authentication ## Compile and run ```bash $ make $ ./auth_server ``` ## Usage Open in your browser the url `http://localhost:2884/auth/basic`, then on the authentication prompt, enter the `USER` and `PASSWORD` specified in `auth_server.c` (default test/testpassword) to be authenticated and allowed to access the endpoint. In another console, you can run the client program to test the use cases ```bash $ ./auth_client ``` ulfius-2.5.2/example_programs/auth_example/auth_client.c000066400000000000000000000143541341017022300235130ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * * How-to generate certificates using openssl for local tests only * * Server key and certificate * openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt * * Certificate authority * openssl genrsa -out ca.key 4096 * openssl req -new -x509 -days 365 -key ca.key -out ca.crt * * Run auth_server with the following command * $ ./auth_server server.key server.crt ca.crt * * Client Key and CSR * openssl genrsa -out client.key 4096 * openssl req -new -key client.key -out client.csr * openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt * * Run auth_client with the following command * ./auth_client client.crt client.key * */ #include #include #include #include #define SERVER_URL "http://localhost:2884/auth/basic" #define SERVER_URL_DEFAULT "http://localhost:2884/auth/default" #define SERVER_URL_CLIENT_CERT "https://localhost:2884/auth/client_cert" void print_response(struct _u_response * response) { if (response != NULL) { char response_body[response->binary_body_length + 1]; o_strncpy(response_body, response->binary_body, response->binary_body_length); response_body[response->binary_body_length] = '\0'; printf("status is\n%ld\n\nstring body is \n%s\n\n", response->status, response_body); } } int main (int argc, char **argv) { struct _u_response response; int res; struct _u_request req_list[6]; y_init_logs("auth_client", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "logs start"); #ifndef U_DISABLE_WEBSOCKET if (argc <= 2) { #endif ulfius_init_request(&req_list[0]); req_list[0].http_verb = o_strdup("GET"); req_list[0].http_url = o_strdup(SERVER_URL); ulfius_init_request(&req_list[1]); req_list[1].http_verb = o_strdup("GET"); req_list[1].http_url = o_strdup(SERVER_URL); req_list[1].auth_basic_user = o_strdup("test"); req_list[1].auth_basic_password = o_strdup("testpassword"); ulfius_init_request(&req_list[2]); req_list[2].http_verb = o_strdup("GET"); req_list[2].http_url = o_strdup(SERVER_URL); req_list[2].auth_basic_user = o_strdup("test"); req_list[2].auth_basic_password = o_strdup("wrongpassword"); ulfius_init_request(&req_list[3]); req_list[3].http_verb = o_strdup("GET"); req_list[3].http_url = o_strdup(SERVER_URL "/404"); ulfius_init_request(&req_list[4]); req_list[4].http_verb = o_strdup("GET"); req_list[4].http_url = o_strdup(SERVER_URL_DEFAULT); req_list[4].auth_basic_user = o_strdup("test"); req_list[4].auth_basic_password = o_strdup("testpassword"); ulfius_init_request(&req_list[5]); req_list[5].http_verb = o_strdup("GET"); req_list[5].http_url = o_strdup(SERVER_URL_DEFAULT); req_list[5].auth_basic_user = o_strdup("test"); req_list[5].auth_basic_password = o_strdup("wrongpassword"); printf("Press to run auth tests no authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[0], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run auth tests success authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[1], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run auth tests error authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[2], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run auth tests 404\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[3], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run default auth tests success authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[4], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run default auth tests error authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[5], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); ulfius_clean_request(&req_list[0]); ulfius_clean_request(&req_list[1]); ulfius_clean_request(&req_list[2]); ulfius_clean_request(&req_list[3]); ulfius_clean_request(&req_list[4]); ulfius_clean_request(&req_list[5]); #ifndef U_DISABLE_WEBSOCKET } else { ulfius_init_request(&req_list[0]); req_list[0].http_verb = o_strdup("GET"); req_list[0].http_url = o_strdup(SERVER_URL_CLIENT_CERT); req_list[0].check_server_certificate = 0; req_list[0].client_cert_file = o_strdup(argv[1]); req_list[0].client_key_file = o_strdup(argv[2]); req_list[0].client_key_password = argc>=4?o_strdup(argv[3]):NULL; printf("Press to run client certificate authentication test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[0], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); ulfius_clean_request(&req_list[0]); } #endif y_close_logs(); return 0; } ulfius-2.5.2/example_programs/auth_example/auth_server.c000066400000000000000000000135271341017022300235440ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This program implements basic auth example * * Copyright 2016-2017 Nicolas Mora * * License MIT * * How-to generate certificates using openssl for local tests only * * Server key and certificate * openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt * * Certificate authority * openssl genrsa -out ca.key 4096 * openssl req -new -x509 -days 365 -key ca.key -out ca.crt * * Run auth_server with the following command * $ ./auth_server server.key server.crt ca.crt * * Client Key and CSR * openssl genrsa -out client.key 4096 * openssl req -new -key client.key -out client.csr * openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt * * Run auth_client with the following command * ./auth_client client.crt client.key */ #include #include #include #include #include #define PORT 2884 #define PREFIX "/auth" #define USER "test" #define PASSWORD "testpassword" char * read_file(const char * filename) { char * buffer = NULL; long length; FILE * f = fopen (filename, "rb"); if (f != NULL) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc (length + 1); if (buffer != NULL) { fread (buffer, 1, length, f); buffer[length] = '\0'; } fclose (f); } return buffer; } /** * Auth function for basic authentication */ int callback_auth_basic_body (const struct _u_request * request, struct _u_response * response, void * user_data) { y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth user: %s", request->auth_basic_user); y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth password: %s", request->auth_basic_password); y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth param: %s", (char *)user_data); if (request->auth_basic_user != NULL && request->auth_basic_password != NULL && 0 == o_strcmp(request->auth_basic_user, USER) && 0 == o_strcmp(request->auth_basic_password, PASSWORD)) { return U_CALLBACK_CONTINUE; } else { ulfius_set_string_body_response(response, 401, "Error authentication"); return U_CALLBACK_UNAUTHORIZED; } } /** * Callback function for basic authentication */ int callback_auth_basic (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Basic auth callback"); return U_CALLBACK_CONTINUE; } #ifndef U_DISABLE_WEBSOCKET /** * Callback function on client certificate authentication */ int callback_auth_client_cert (const struct _u_request * request, struct _u_response * response, void * user_data) { char * dn = NULL, * issuer_dn = NULL, * response_message; size_t lbuf = 0, libuf = 0; if (request->client_cert != NULL) { gnutls_x509_crt_get_dn(request->client_cert, NULL, &lbuf); gnutls_x509_crt_get_issuer_dn(request->client_cert, NULL, &libuf); dn = o_malloc(lbuf + 1); issuer_dn = o_malloc(libuf + 1); if (dn != NULL && issuer_dn != NULL) { gnutls_x509_crt_get_dn(request->client_cert, dn, &lbuf); gnutls_x509_crt_get_issuer_dn(request->client_cert, issuer_dn, &libuf); dn[lbuf] = '\0'; issuer_dn[libuf] = '\0'; y_log_message(Y_LOG_LEVEL_DEBUG, "dn of the client: %s", dn); y_log_message(Y_LOG_LEVEL_DEBUG, "dn of the issuer: %s", issuer_dn); response_message = msprintf("client dn: '%s', ussued by: '%s'", dn, issuer_dn); ulfius_set_string_body_response(response, 200, response_message); o_free(response_message); } o_free(dn); o_free(issuer_dn); } else { ulfius_set_string_body_response(response, 400, "Invalid client certificate"); } return U_CALLBACK_CONTINUE; } #endif int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; y_init_logs("auth_server", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "logs start"); if (ulfius_init_instance(&instance, PORT, NULL, "auth_basic_default") != U_OK) { printf("Error ulfius_init_instance, abort\n"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/basic", 0, &callback_auth_basic_body, "auth param"); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/basic", 1, &callback_auth_basic, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/default", 1, &callback_auth_basic, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/default", 0, &callback_auth_basic_body, NULL); #ifndef U_DISABLE_WEBSOCKET if (argc > 3) { ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/client_cert", 0, &callback_auth_client_cert, NULL); } #endif #ifndef U_DISABLE_WEBSOCKET // Start the framework if (argc > 3) { char * server_key = read_file(argv[1]), * server_pem = read_file(argv[2]), * root_ca_pem = read_file(argv[3]); if (ulfius_start_secure_ca_trust_framework(&instance, server_key, server_pem, root_ca_pem) == U_OK) { printf("Start secure framework on port %u\n", instance.port); // Wait for the user to press on the console to quit the application printf("Press to quit server\n"); getchar(); } else { printf("Error starting secure framework\n"); } o_free(server_key); o_free(server_pem); o_free(root_ca_pem); } else if (ulfius_start_framework(&instance) == U_OK) { #endif printf("Start framework on port %u\n", instance.port); // Wait for the user to press on the console to quit the application printf("Press to quit server\n"); getchar(); #ifndef U_DISABLE_WEBSOCKET } else { printf("Error starting framework\n"); } #endif printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); y_close_logs(); return 0; } ulfius-2.5.2/example_programs/cmake-modules/000077500000000000000000000000001341017022300211135ustar00rootroot00000000000000ulfius-2.5.2/example_programs/cmake-modules/CMakeUninstall.cmake.in000066400000000000000000000020641341017022300253760ustar00rootroot00000000000000if (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") endif (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach (file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if (IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if (NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif (NOT "${rm_retval}" STREQUAL 0) else (IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif (IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach (file) ulfius-2.5.2/example_programs/cmake-modules/FindUlfius.cmake000066400000000000000000000066721341017022300242000ustar00rootroot00000000000000#.rst: # FindUlfius # ----------- # # Find Ulfius # # Find Ulfius headers and libraries. # # :: # # ULFIUS_FOUND - True if Ulfius found. # ULFIUS_INCLUDE_DIRS - Where to find ulfius.h. # ULFIUS_LIBRARIES - List of libraries when using Ulfius. # ULFIUS_VERSION_STRING - The version of Ulfius found. #============================================================================= # Copyright 2018 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= find_package(PkgConfig QUIET) pkg_check_modules(PC_ULFIUS QUIET ulfius) find_path(ULFIUS_INCLUDE_DIR NAMES ulfius.h HINTS ${PC_ULFIUS_INCLUDEDIR} ${PC_ULFIUS_INCLUDE_DIRS}) find_library(ULFIUS_LIBRARY NAMES ulfius libulfius HINTS ${PC_ULFIUS_LIBDIR} ${PC_ULFIUS_LIBRARY_DIRS}) set(ULFIUS_VERSION_STRING 0.0.0) if (PC_ULFIUS_VERSION) set(ULFIUS_VERSION_STRING ${PC_ULFIUS_VERSION}) elseif (ULFIUS_INCLUDE_DIR AND EXISTS "${ULFIUS_INCLUDE_DIR}/ulfius.h") set(regex_ulfius_version "^#define[ \t]+ULFIUS_VERSION[ \t]+([^\"]+).*") file(STRINGS "${ULFIUS_INCLUDE_DIR}/ulfius-cfg.h" ulfius_version REGEX "${regex_ulfius_version}") string(REGEX REPLACE "${regex_ulfius_version}" "\\1" ULFIUS_VERSION_STRING "${ulfius_version}") unset(regex_ulfius_version) unset(ulfius_version) set(regex_ulfius_disable_curl "^#define[ \t]+U_DISABLE_CURL.*") file(STRINGS "${ULFIUS_INCLUDE_DIR}/ulfius-cfg.h" ulfius_disable_curl REGEX "${regex_ulfius_disable_curl}") if (NOT "${ulfius_disable_curl}" STREQUAL "") set(WITH_CURL OFF) else () set(WITH_CURL ON) endif() unset(regex_ulfius_disable_curl) unset(ulfius_disable_curl) set(regex_ulfius_disable_jansson "^#define[ \t]+U_DISABLE_JANSSON.*") file(STRINGS "${ULFIUS_INCLUDE_DIR}/ulfius-cfg.h" ulfius_disable_jansson REGEX "${regex_ulfius_disable_jansson}") if (NOT "${ulfius_disable_jansson}" STREQUAL "") set(WITH_JANSSON OFF) else () set(WITH_JANSSON ON) endif() unset(regex_ulfius_disable_jansson) unset(ulfius_disable_jansson) set(regex_ulfius_disable_websocket "^#define[ \t]+U_DISABLE_WEBSOCKET.*") file(STRINGS "${ULFIUS_INCLUDE_DIR}/ulfius-cfg.h" ulfius_disable_websocket REGEX "${regex_ulfius_disable_websocket}") if (NOT "${ulfius_disable_websocket}" STREQUAL "") set(WITH_WEBSOCKET OFF) else () set(WITH_WEBSOCKET ON) endif() unset(regex_ulfius_disable_websocket) unset(ulfius_disable_websocket) endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Ulfius REQUIRED_VARS ULFIUS_LIBRARY ULFIUS_INCLUDE_DIR VERSION_VAR ULFIUS_VERSION_STRING) if (ULFIUS_FOUND) set(ULFIUS_LIBRARIES ${ULFIUS_LIBRARY}) set(ULFIUS_INCLUDE_DIRS ${ULFIUS_INCLUDE_DIR}) endif () mark_as_advanced(ULFIUS_INCLUDE_DIR ULFIUS_LIBRARY) ulfius-2.5.2/example_programs/cmake-modules/FindYder.cmake000066400000000000000000000051411341017022300236220ustar00rootroot00000000000000#.rst: # FindYder # ----------- # # Find Yder # # Find Yder headers and libraries. # # :: # # YDER_FOUND - True if Yder found. # YDER_INCLUDE_DIRS - Where to find yder.h. # YDER_LIBRARIES - List of libraries when using Yder. # YDER_VERSION_STRING - The version of Yder found. #============================================================================= # Copyright 2018 Nicolas Mora # Copyright 2018 Silvio Clecio # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . #============================================================================= find_package(PkgConfig QUIET) pkg_check_modules(PC_YDER QUIET libyder) find_path(YDER_INCLUDE_DIR NAMES yder.h HINTS ${PC_YDER_INCLUDEDIR} ${PC_YDER_INCLUDE_DIRS}) find_library(YDER_LIBRARY NAMES yder libyder HINTS ${PC_YDER_LIBDIR} ${PC_YDER_LIBRARY_DIRS}) set(YDER_VERSION_STRING 0.0.0) if (PC_YDER_VERSION) set(YDER_VERSION_STRING ${PC_YDER_VERSION}) elseif (YDER_INCLUDE_DIR AND EXISTS "${YDER_INCLUDE_DIR}/yder.h") set(regex_yder_version "^#define[ \t]+YDER_VERSION[ \t]+([^\"]+).*") file(STRINGS "${YDER_INCLUDE_DIR}/yder.h" yder_version REGEX "${regex_yder_version}") string(REGEX REPLACE "${regex_yder_version}" "\\1" YDER_VERSION_STRING "${yder_version}") unset(regex_yder_version) unset(yder_version) if (NOT YDER_VERSION_STRING) set(regex_yder_version "^#define[ \t]+YDER_VERSION[ \t]+([^\"]+).*") file(STRINGS "${YDER_INCLUDE_DIR}/yder-cfg.h" yder_version REGEX "${regex_yder_version}") string(REGEX REPLACE "${regex_yder_version}" "\\1" YDER_VERSION_STRING "${yder_version}") unset(regex_yder_version) unset(yder_version) endif () endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Yder REQUIRED_VARS YDER_LIBRARY YDER_INCLUDE_DIR VERSION_VAR YDER_VERSION_STRING) if (YDER_FOUND) set(YDER_LIBRARIES ${YDER_LIBRARY}) set(YDER_INCLUDE_DIRS ${YDER_INCLUDE_DIR}) endif () mark_as_advanced(YDER_INCLUDE_DIR YDER_LIBRARY) ulfius-2.5.2/example_programs/include/000077500000000000000000000000001341017022300200105ustar00rootroot00000000000000ulfius-2.5.2/example_programs/include/u_example.h000066400000000000000000000031171341017022300221420ustar00rootroot00000000000000/** * * Ulfius example programs * * Common header for all example programs * * Copyright 2018 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; * version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see . * */ #ifndef _U_EXAMPLE_ #define _U_EXAMPLE_ /** Define mock yder functions when yder is disabled **/ #ifdef U_DISABLE_YDER int y_init_logs(const char * app, const unsigned long init_mode, const unsigned long init_level, const char * init_log_file, const char * message) { (void)(app); (void)(init_mode); (void)(init_level); (void)(init_log_file); (void)(message); return 1; } int y_set_logs_callback(void (* y_callback_log_message) (void * cls, const char * app_name, const time_t date, const unsigned long level, const char * message), void * cls, const char * message) { (void)(y_callback_log_message); (void)(cls); (void)(message); return 1; } void y_log_message(const unsigned long type, const char * message, ...) { (void)(type); (void)(message); } int y_close_logs() { return 1; } #endif // U_DISABLE_YDER #endif // _U_EXAMPLE_ ulfius-2.5.2/example_programs/injection_example/000077500000000000000000000000001341017022300220625ustar00rootroot00000000000000ulfius-2.5.2/example_programs/injection_example/Makefile000066400000000000000000000024331341017022300235240ustar00rootroot00000000000000# # injection_example program # # Makefile used to build the software # # Copyright 2016-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-lc -lulfius -ljansson -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: injection_example clean: rm -f *.o injection_example debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: injection_example ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET injection_example.o: injection_example.c $(CC) $(CFLAGS) injection_example.c -DDEBUG -g -O0 injection_example: ../../src/libulfius.so injection_example.o $(CC) -o injection_example injection_example.o $(LIBS) test: injection_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./injection_example ulfius-2.5.2/example_programs/injection_example/README.md000066400000000000000000000014551341017022300233460ustar00rootroot00000000000000# Injection example Provides an example program that will add and remove endpoints during the execution ## Compile and run ```bash $ make test ``` ## Usage Right after starting the program, the endpoints available are the following: - `GET http://localhost:4528/inject/first` - `GET http://localhost:4528/inject/second` - `GET http://localhost:4528/inject/third` On the program console, press `` one time will add this new endpoint: - `GET http://localhost:4528/inject/fourth` Press `` again to add the following endpoint: - `GET http://localhost:4528/inject/fifth` Press `` to remove the following endpoint: - `GET http://localhost:4528/inject/fourth` Then press `` to quit the application You can test the presence or absence of the specified endpoints between each step. ulfius-2.5.2/example_programs/injection_example/injection_example.c000066400000000000000000000100761341017022300257270ustar00rootroot00000000000000/** * * Ulfius Framework injection_example program * * This example program describes the endpoints injections * * Copyright 2016-2017 Nicolas Mora * * License MIT * */ #include #include #include #include #include #include #include #define PORT 4528 #define PREFIX "/inject" /** * callback functions declaration */ int callback_first (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_second (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_third (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_fourth (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_fifth (const struct _u_request * request, struct _u_response * response, void * user_data); int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; y_init_logs("injection_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting injection_example"); if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/first", 1, &callback_first, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/second", 1, &callback_second, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/third", 1, &callback_third, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Start framework on port %d", instance.port); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to inject %s/fourth endpoint", PREFIX); getchar(); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/fourth", 1, &callback_fourth, NULL); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to inject %s/fifth endpoint", PREFIX); getchar(); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/fifth", 1, &callback_fifth, NULL); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to remove %s/fourth endpoint", PREFIX); getchar(); ulfius_remove_endpoint_by_val(&instance, "GET", PREFIX, "/fourth"); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to quit the application"); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); y_close_logs(); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback callback_first */ int callback_first (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_first!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_second */ int callback_second (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_second!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_third */ int callback_third (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_third!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_fourth */ int callback_fourth (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_fourth!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_fifth */ int callback_fifth (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_fifth!"); return U_CALLBACK_CONTINUE; } ulfius-2.5.2/example_programs/multiple_callbacks_example/000077500000000000000000000000001341017022300237325ustar00rootroot00000000000000ulfius-2.5.2/example_programs/multiple_callbacks_example/Makefile000066400000000000000000000026171341017022300254000ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) LIBS=-L$(LIBYDER_LOCATION) -lc -lulfius -lorcania -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: multiple_callbacks_example clean: rm -f *.o multiple_callbacks_example valgrind.txt debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: multiple_callbacks_example ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET multiple_callbacks_example.o: multiple_callbacks_example.c $(CC) $(CFLAGS) multiple_callbacks_example.c -DDEBUG -g -O0 multiple_callbacks_example: ../../src/libulfius.so multiple_callbacks_example.o $(CC) -o multiple_callbacks_example multiple_callbacks_example.o $(LIBS) test: multiple_callbacks_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./multiple_callbacks_example ulfius-2.5.2/example_programs/multiple_callbacks_example/README.md000066400000000000000000000024421341017022300252130ustar00rootroot00000000000000# multiple_callbacks_example Run a webservice with multiple endpoint callbacks possible and different levels of priority ## Compile and run ```bash $ make test ``` ## Endpoints available: ### Multiple callbacks in cascade - `GET http://localhost:6875/multiple/zero`: Send "Level zero" response (status 200) - `GET http://localhost:6875/multiple/zero/one`: Send "Level zero\nlevel one" response (status 200) - `GET http://localhost:6875/multiple/zero/one/two`: Send "Level zero\nlevel one\nlevel two" response (status 200) - `GET http://localhost:6875/multiple/zero/one/two/three`: Send "Level zero\nlevel one\nlevel two\nlevel three" response (status 200) ### Multiple callbacks but the cascade is stopped at `/zero/onec/` - `GET http://localhost:6875/multiple/zero/one`: Send "Level zero\nlevel one" response (status 200) - `GET http://localhost:6875/multiple/zero/one/two`: Send "Level zero\nlevel one" response (status 200) ### Hello World with authentication - `GET http://localhost:6875/multiple/auth/data`: Send "Hello World!" response if authentication is correct (status 200), otherwise status 401 with realm "default_realm" - `PUT http://localhost:6875/multiple/auth/data`: Send "Hello World!" response if authentication is correct (status 200), otherwise status 401 with realm "specific_realm" ulfius-2.5.2/example_programs/multiple_callbacks_example/multiple_callbacks_example.c000066400000000000000000000223431341017022300314470ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the multiple callback feature * that are available in ulfius 2.0 * * Copyright 2017 Nicolas Mora * * License MIT * */ #include #include #include #include #include #include #include #define PORT 6875 #define PREFIX "/multiple" #define USER "test" #define PASSWORD "testpassword" #define DEFAULT_REALM "default_realm" #define SPECIFIC_REALM "specific_realm" /** * callback functions declaration */ int callback_multiple_level_zero (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_one (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_two (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_three (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_one_complete (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_final (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_auth_check (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_auth_data (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data); /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, value is %s", keys[i], value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s", keys[i], value); if (to_return != NULL) { len = o_strlen(to_return) + o_strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (o_strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((o_strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } int main (int argc, char **argv) { int ret; // Set the framework port number struct _u_instance instance; y_init_logs("multiple_callbacks_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting multiple_callbacks_example"); if (ulfius_init_instance(&instance, PORT, NULL, DEFAULT_REALM) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Maximum body size sent by the client is 1 Kb instance.max_post_body_size = 1024; // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "*", PREFIX, "/zero/*", 99, &callback_multiple_level_final, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/*", 0, &callback_multiple_level_zero, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/one/*", 1, &callback_multiple_level_one, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/one/two/*", 2, &callback_multiple_level_two, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/one/two/three/", 3, &callback_multiple_level_three, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/onec/*", 1, &callback_multiple_level_one_complete, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/onec/two/*", 2, &callback_multiple_level_two, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/auth/*", 1, &callback_multiple_level_auth_check, NULL); ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, "/auth/*", 1, &callback_multiple_level_auth_check, NULL); ulfius_add_endpoint_by_val(&instance, "*", PREFIX, "/auth/data/", 2, &callback_multiple_level_auth_data, NULL); // default_endpoint declaration ulfius_set_default_endpoint(&instance, &callback_default, NULL); // Start the framework ret = ulfius_start_framework(&instance); if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Start framework on port %d", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); y_close_logs(); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function of level zero */ int callback_multiple_level_zero (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Level zero"); response->shared_data = o_strdup("shared from level zero"); return U_CALLBACK_CONTINUE; } /** * Callback function of level one */ int callback_multiple_level_one (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level one"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); return U_CALLBACK_CONTINUE; } /** * Callback function of level one with complete return */ int callback_multiple_level_one_complete (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level one"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); o_free(response->shared_data); return U_CALLBACK_COMPLETE; } /** * Callback function of level two */ int callback_multiple_level_two (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level two"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); return U_CALLBACK_CONTINUE; } /** * Callback function of level three */ int callback_multiple_level_three (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level three"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); return U_CALLBACK_CONTINUE; } /** * Final callback function called on multiple calls */ int callback_multiple_level_final (const struct _u_request * request, struct _u_response * response, void * user_data) { o_free(response->shared_data); return U_CALLBACK_COMPLETE; } /** * Check authentication */ int callback_multiple_level_auth_check (const struct _u_request * request, struct _u_response * response, void * user_data) { y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth user: %s", request->auth_basic_user); y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth password: %s", request->auth_basic_password); if (request->auth_basic_user != NULL && request->auth_basic_password != NULL && 0 == o_strcmp(request->auth_basic_user, USER) && 0 == o_strcmp(request->auth_basic_password, PASSWORD)) { return U_CALLBACK_CONTINUE; } else { if (0 == o_strcmp("PUT", request->http_verb)) { response->auth_realm = o_strdup(SPECIFIC_REALM); } ulfius_set_string_body_response(response, 401, "Error authentication"); return U_CALLBACK_UNAUTHORIZED; } } /** * Send Hello World! */ int callback_multiple_level_auth_data (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World!"); return U_CALLBACK_CONTINUE; } /** * Default callback function called if no endpoint has a match */ int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 404, "Page not found, do what you want"); return U_CALLBACK_CONTINUE; } ulfius-2.5.2/example_programs/proxy_example/000077500000000000000000000000001341017022300212615ustar00rootroot00000000000000ulfius-2.5.2/example_programs/proxy_example/Makefile000066400000000000000000000021611341017022300227210ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-lc -lorcania -lulfius -ljansson -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: proxy clean: rm -f *.o proxy debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: proxy ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET proxy.o: proxy.c $(CC) $(CFLAGS) proxy.c -DDEBUG -g -O0 proxy: ../../src/libulfius.so proxy.o $(CC) -o proxy proxy.o $(LIBS) test: proxy LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./proxy ulfius-2.5.2/example_programs/proxy_example/README.md000066400000000000000000000004451341017022300225430ustar00rootroot00000000000000# Proxy example Run a simple proxy application that will redirect all calls to `PROXY_DEST` address ## Compile and run ```bash $ make test ``` ## Usage: Open in your browser the url [http://localhost:7799/](http://localhost:7799/) to access the url specified by `PROXY_DEST` in `proxy.c` ulfius-2.5.2/example_programs/proxy_example/proxy.c000066400000000000000000000043161341017022300226120ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the main features * that are available in a callback function * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #include #include #define PORT 7799 #define PROXY_DEST "https://www.wikipedia.org" /** * callback function declaration */ int callback_get (const struct _u_request * request, struct _u_response * response, void * user_data); int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", NULL, "*", 0, &callback_get, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %u\n", instance.port); printf("Go to url / to proxify %s\n", PROXY_DEST); // Wait for the user to press on the console to quit the application getchar(); } else { printf("Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function that put a "Hello World!" string in the response */ int callback_get (const struct _u_request * request, struct _u_response * response, void * user_data) { struct _u_request * req = ulfius_duplicate_request(request); struct _u_response * res = o_malloc(sizeof(struct _u_response)); ulfius_init_response(res); int len; o_free(req->http_url); u_map_remove_from_key(req->map_header, "Host"); len = snprintf(NULL, 0, "%s%s", PROXY_DEST, request->http_url); req->http_url = o_malloc((len+1)*sizeof(char)); snprintf(req->http_url, (len+1), "%s%s", PROXY_DEST, request->http_url); printf("Accessing %s as proxy\n", req->http_url); req->timeout = 30; ulfius_send_http_request(req, res); ulfius_copy_response(response, res); ulfius_clean_response_full(res); ulfius_clean_request_full(req); return U_CALLBACK_CONTINUE; } ulfius-2.5.2/example_programs/request_example/000077500000000000000000000000001341017022300215705ustar00rootroot00000000000000ulfius-2.5.2/example_programs/request_example/Makefile000066400000000000000000000024651341017022300232370ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) $(CPPFLAGS) LIBS=-lc -ljansson -lorcania -lulfius -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: client server mail clean: rm -f *.o client server mail debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: client server mail ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET client.o: client.c ../../src/libulfius.so $(CC) $(CFLAGS) client.c -DDEBUG -g -O0 client: client.o $(CC) -o client client.o $(LIBS) server.o: server.c ../../src/libulfius.so $(CC) $(CFLAGS) server.c -DDEBUG -g -O0 server: server.o $(CC) -o server server.o $(LIBS) mail.o: mail.c ../../src/libulfius.so $(CC) $(CFLAGS) mail.c -DDEBUG -g -O0 mail: mail.o $(CC) -o mail mail.o $(LIBS) ulfius-2.5.2/example_programs/request_example/README.md000066400000000000000000000007411341017022300230510ustar00rootroot00000000000000# HTTP Request send examples Provides some basic http request send examples. ## Compile and run ```bash $ make $ ./server ``` On another console, run the following command ```bash $ ./client ``` Then follow the instructions and observe the results. ## Mail send example Modify the file `mail.c` values for `MAIL_SERVER`, `MAIL_SENDER` and `MAIL_RECIPIENT` with your own, then compile and run: ```bash $ ./mail ``` An email should be sent to the `MAIL_RECIPIENT` address. ulfius-2.5.2/example_programs/request_example/client.c000066400000000000000000000204351341017022300232160ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #include #include #define SERVER_URL_PREFIX "http://localhost:7778/curl" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { len = snprintf(NULL, 0, "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); if (to_return != NULL) { len = o_strlen(to_return) + o_strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); } else { to_return = o_malloc((o_strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } void print_response(struct _u_response * response) { if (response != NULL) { char * headers = print_map(response->map_header); char response_body[response->binary_body_length + 1]; o_strncpy(response_body, response->binary_body, response->binary_body_length); response_body[response->binary_body_length] = '\0'; printf("protocol is\n%s\n\n headers are \n%s\n\n body is \n%s\n\n", response->protocol, headers, response_body); o_free(headers); } } int main (int argc, char **argv) { struct _u_map headers, url_params, post_params, req_headers; char * string_body = "param1=one¶m2=two"; json_t * json_body = json_object(); struct _u_response response; int res; u_map_init(&headers); u_map_init(&url_params); u_map_put(&url_params, "test", "one"); u_map_put(&url_params, "other_test", "two"); u_map_init(&post_params); u_map_put(&post_params, "third_test", "three"); u_map_put(&post_params, "fourth_test", "four"); u_map_put(&post_params, "extreme_test", "Here ! are %9_ some $ ö\\)]= special châraçters"); u_map_init(&req_headers); u_map_put(&req_headers, "Content-Type", MHD_HTTP_POST_ENCODING_FORM_URLENCODED); json_object_set_new(json_body, "param1", json_string("one")); json_object_set_new(json_body, "param2", json_string("two")); struct _u_request req_list[8]; ulfius_init_request(&req_list[0]); ulfius_init_request(&req_list[1]); ulfius_init_request(&req_list[2]); ulfius_init_request(&req_list[3]); ulfius_init_request(&req_list[4]); ulfius_init_request(&req_list[5]); ulfius_init_request(&req_list[6]); ulfius_init_request(&req_list[7]); // Parameters in url req_list[0].http_verb = o_strdup("GET"); req_list[0].http_url = o_strdup(SERVER_URL_PREFIX "/get/"); req_list[0].timeout = 20; u_map_copy_into(req_list[0].map_url, &url_params); // No parameters req_list[1].http_verb = o_strdup("DELETE"); req_list[1].http_url = o_strdup(SERVER_URL_PREFIX "/delete/"); req_list[1].timeout = 20; // Parameters in post_map and string_body req_list[2].http_verb = o_strdup("POST"); req_list[2].http_url = o_strdup(SERVER_URL_PREFIX "/post/param/"); req_list[2].timeout = 20; u_map_copy_into(req_list[2].map_post_body, &post_params); req_list[2].binary_body = o_strdup(string_body); req_list[2].binary_body_length = o_strlen(string_body); // Paremeters in string body, header MHD_HTTP_POST_ENCODING_FORM_URLENCODED req_list[3].http_verb = o_strdup("POST"); req_list[3].http_url = o_strdup(SERVER_URL_PREFIX "/post/plain/"); req_list[3].timeout = 20; u_map_copy_into(req_list[3].map_header, &req_headers); req_list[3].binary_body = o_strdup(string_body); req_list[3].binary_body_length = o_strlen(string_body); // Parameters in json_body req_list[4].http_verb = o_strdup("POST"); req_list[4].http_url = o_strdup(SERVER_URL_PREFIX "/post/json/"); req_list[4].timeout = 20; u_map_copy_into(req_list[4].map_url, &url_params); ulfius_set_json_body_request(&req_list[4], json_body); // Paremeters in string body, header MHD_HTTP_POST_ENCODING_FORM_URLENCODED req_list[5].http_verb = o_strdup("PUT"); req_list[5].http_url = o_strdup(SERVER_URL_PREFIX "/put/plain/"); req_list[5].timeout = 20; u_map_copy_into(req_list[5].map_header, &req_headers); req_list[5].binary_body = o_strdup(string_body); req_list[5].binary_body_length = o_strlen(string_body); // Parameters in json_body req_list[6].http_verb = o_strdup("PUT"); req_list[6].http_url = o_strdup(SERVER_URL_PREFIX "/put/json/"); req_list[6].timeout = 20; u_map_copy_into(req_list[6].map_url, &url_params); ulfius_set_json_body_request(&req_list[6], json_body); // Parameters in post_map req_list[7].http_verb = o_strdup("POST"); req_list[7].http_url = o_strdup(SERVER_URL_PREFIX "/post/param/"); req_list[7].timeout = 20; u_map_copy_into(req_list[6].map_post_body, &post_params); printf("Press to run get test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[0], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run get test with no interest on the response\n"); getchar(); printf("Request sent, result is %d\n", ulfius_send_http_request(&req_list[0], NULL)); printf("Press to run delete test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[1], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post parameters test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[2], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post plain test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[3], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post json test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[4], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run put plain test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[5], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run put json test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[6], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post only test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[7], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); // Wait for the user to press on the console to quit the application printf("Press to quit test\n"); getchar(); json_decref(json_body); u_map_clean(&headers); u_map_clean(&url_params); u_map_clean(&post_params); u_map_clean(&req_headers); ulfius_clean_request(&req_list[0]); ulfius_clean_request(&req_list[1]); ulfius_clean_request(&req_list[2]); ulfius_clean_request(&req_list[3]); ulfius_clean_request(&req_list[4]); ulfius_clean_request(&req_list[5]); ulfius_clean_request(&req_list[6]); ulfius_clean_request(&req_list[7]); return 0; } ulfius-2.5.2/example_programs/request_example/mail.c000066400000000000000000000011451341017022300226570ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #define MAIL_SERVER "localhost" #define MAIL_SENDER "test@example.org" #define MAIL_RECIPIENT "test@example.com" int main(int argc, char ** argv) { printf("Send mail: %d\n", ulfius_send_smtp_email(MAIL_SERVER, 0, 0, 0, NULL, NULL, MAIL_SENDER, MAIL_RECIPIENT, NULL, NULL, "test", "This is a test\nHello!!!")); return 0; } ulfius-2.5.2/example_programs/request_example/server.c000066400000000000000000000072121341017022300232440ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #include #include #define PORT 7778 #define PREFIX "/curl" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { len = snprintf(NULL, 0, "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); if (to_return != NULL) { len = o_strlen(to_return) + o_strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); } else { to_return = o_malloc((o_strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } /** * Callback function that put "Hello World!" and all the data sent by the client in the response as string (http method, url, params, cookies, headers, post, json, and user specific data in the response */ int callback (const struct _u_request * request, struct _u_response * response, void * user_data) { char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie), * post_params = print_map(request->map_post_body); char request_body[request->binary_body_length + 1]; o_strncpy(request_body, request->binary_body, request->binary_body_length); request_body[request->binary_body_length] = '\0'; response->binary_body = o_strdup("ok"); response->binary_body_length = o_strlen("ok"); response->status = 200; printf("######################################################\n###################### Callback ######################\n######################################################\n\nMethod is %s\n url is %s\n\n parameters from the url are \n%s\n\n cookies are \n%s\n\n headers are \n%s\n\n post parameters are \n%s\n\n raw body is \n%s\n\n user data is %s\n\n", request->http_verb, request->http_url, url_params, cookies, headers, post_params, request_body, (char *)user_data); ulfius_add_cookie_to_response(response, "cook", "ie", NULL, 5000, NULL, NULL, 0, 0); u_map_put(response->map_header, "multiple-key", "value 1"); u_map_put(response->map_header, "Multiple-Key", "value 2"); o_free(url_params); o_free(headers); o_free(cookies); o_free(post_params); return U_CALLBACK_CONTINUE; } int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "*", PREFIX, "*", 0, &callback, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application printf("Press to quit server\n"); getchar(); } else { printf("Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } ulfius-2.5.2/example_programs/sheep_counter/000077500000000000000000000000001341017022300212305ustar00rootroot00000000000000ulfius-2.5.2/example_programs/sheep_counter/Makefile000066400000000000000000000023211341017022300226660ustar00rootroot00000000000000# # Sheep counter program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-lc -lulfius -lorcania -ljansson -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: sheep_counter clean: rm -f *.o sheep_counter debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: sheep_counter ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET sheep_counter.o: sheep_counter.c $(CC) $(CFLAGS) sheep_counter.c -DDEBUG -g -O0 sheep_counter: ../../src/libulfius.so sheep_counter.o $(CC) -o sheep_counter sheep_counter.o $(LIBS) test: sheep_counter LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./sheep_counter ulfius-2.5.2/example_programs/sheep_counter/README.md000066400000000000000000000024711341017022300225130ustar00rootroot00000000000000# sheep_example Run a simple file webserver and a small REST API that listen on port 7437, and implement a file upload service. ## Compile and run ```bash $ make test ``` ## Usage: ### Sheep counter Open in your browser the url [http://localhost:7437/index.html](http://localhost:7437/index.html), it's a jquery application that will count and display sheeps to help you fall asleep. There click on the buttons available to call the underline API. The API endpoints are the following: - `POST http://localhost:7437/sheep`: Initialise the counter with the specified `nbsheep` value provided in the json body (default 0) and return the new `nbsheep` value in the response as a json. The JSON body must be a single numeric value. - `PUT http://localhost:7437/sheep`: Add a sheep to the current counter and return the new `nbsheep` value in the response as a json - `DELETE http://localhost:7437/sheep`: Reset the sheep counter to 0 and return the new `nbsheep` value in the response as a json ### File upload Open in your browser the url [http://localhost:7437/upload.html](http://localhost:7437/upload.html), there upload a file, preferably not a big one, then click on the `Upload File` button. The API endpoint is the following: - `http://localhost:7437/upload`: upload a file and show informations about it in the response. ulfius-2.5.2/example_programs/sheep_counter/sheep_counter.c000066400000000000000000000242031341017022300242400ustar00rootroot00000000000000/** * * Ulfius Framework sheep_counter program * * This program implements a small set of webservices and a tiny static files server * * As a result, the index.html page will increment a sheep counter and * shows a new sheep on the screen every time an increment is done (every 5 seconds) * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #include #include #define PORT 7437 #define PREFIX "/sheep" #define FILE_PREFIX "/upload" #define STATIC_FOLDER "static" // Callback function used to serve static files that are present in the static folder int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to start a new count by setting the number of sheeps int callback_sheep_counter_start (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to reset the number of sheeps to 0 int callback_sheep_counter_reset (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to add one sheep to the counter int callback_sheep_counter_add (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to upload file int callback_upload_file (const struct _u_request * request, struct _u_response * response, void * user_data); // File upload callback function int file_upload_callback (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * user_data); /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); if (to_return != NULL) { len = o_strlen(to_return) + o_strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (o_strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((o_strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } /** * return the filename extension */ const char * get_filename_ext(const char *path) { const char *dot = o_strrchr(path, '.'); if(!dot || dot == path) return "*"; return dot + 1; } /** * Main function */ int main (int argc, char **argv) { // jansson integer type can vary #if JSON_INTEGER_IS_LONG_LONG long long nb_sheep = 0; #else long nb_sheep = 0; #endif // Initialize the instance struct _u_instance instance; y_init_logs("sheep_counter", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting sheep_counter"); if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Max post param size is 16 Kb, which means an uploaded file is no more than 16 Kb instance.max_post_param_size = 16*1024; if (ulfius_set_upload_file_callback_function(&instance, &file_upload_callback, "my cls") != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_set_upload_file_callback_function"); } // MIME types that will define the static files struct _u_map mime_types; u_map_init(&mime_types); u_map_put(&mime_types, ".html", "text/html"); u_map_put(&mime_types, ".css", "text/css"); u_map_put(&mime_types, ".js", "application/javascript"); u_map_put(&mime_types, ".png", "image/png"); u_map_put(&mime_types, ".jpeg", "image/jpeg"); u_map_put(&mime_types, ".jpg", "image/jpeg"); u_map_put(&mime_types, "*", "application/octet-stream"); // Endpoint list declaration // The first 3 are webservices with a specific url // The last endpoint will be called for every GET call and will serve the static files ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, NULL, 1, &callback_sheep_counter_start, &nb_sheep); ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, NULL, 1, &callback_sheep_counter_add, &nb_sheep); ulfius_add_endpoint_by_val(&instance, "DELETE", PREFIX, NULL, 1, &callback_sheep_counter_reset, &nb_sheep); ulfius_add_endpoint_by_val(&instance, "*", FILE_PREFIX, NULL, 1, &callback_upload_file, NULL); ulfius_add_endpoint_by_val(&instance, "GET", "*", NULL, 1, &callback_static_file, &mime_types); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start sheep counter on port %u\n", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { printf("Error starting framework\n"); } // Clean the mime map u_map_clean(&mime_types); printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); y_close_logs(); return 0; } /** * Start a new counter by setting the number of sheeps to a specific value * return the current number of sheeps in a json object */ int callback_sheep_counter_start (const struct _u_request * request, struct _u_response * response, void * user_data) { json_t * json_nb_sheep = ulfius_get_json_body_request(request, NULL), * json_body = NULL; #if JSON_INTEGER_IS_LONG_LONG long long * nb_sheep = user_data; #else long * nb_sheep = user_data; #endif if (json_nb_sheep != NULL) { * nb_sheep = json_integer_value(json_nb_sheep); } else { * nb_sheep = 0; } json_body = json_object(); json_object_set_new(json_body, "nbsheep", json_integer(* nb_sheep)); ulfius_set_json_body_response(response, 200, json_body); json_decref(json_nb_sheep); json_decref(json_body); return U_CALLBACK_CONTINUE; } /** * Reset the number of sheeps to 0 * return the current number of sheeps in a json object */ int callback_sheep_counter_reset (const struct _u_request * request, struct _u_response * response, void * user_data) { json_t * json_body = NULL; #if JSON_INTEGER_IS_LONG_LONG long long * nb_sheep = user_data; #else long * nb_sheep = user_data; #endif * nb_sheep = 0; json_body = json_object(); json_object_set_new(json_body, "nbsheep", json_integer(0)); ulfius_set_json_body_response(response, 200, json_body); json_decref(json_body); return U_CALLBACK_CONTINUE; } /** * Adds one sheep * return the current number of sheeps in a json object */ int callback_sheep_counter_add (const struct _u_request * request, struct _u_response * response, void * user_data) { json_t * json_body = NULL; #if JSON_INTEGER_IS_LONG_LONG long long * nb_sheep = user_data; #else long * nb_sheep = user_data; #endif (*nb_sheep)++; json_body = json_object(); json_object_set_new(json_body, "nbsheep", json_integer(*nb_sheep)); ulfius_set_json_body_response(response, 200, json_body); json_decref(json_body); return U_CALLBACK_CONTINUE; } /** * serve a static file to the client as a very simple http server */ int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data) { void * buffer = NULL; long length; FILE * f; char * file_path = msprintf("%s%s", STATIC_FOLDER, request->http_url); const char * content_type; if (access(file_path, F_OK) != -1) { f = fopen (file_path, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc(length*sizeof(void)); if (buffer) { fread (buffer, 1, length, f); } fclose (f); } if (buffer) { content_type = u_map_get((struct _u_map *)user_data, get_filename_ext(request->http_url)); response->binary_body = buffer; response->binary_body_length = length; u_map_put(response->map_header, "Content-Type", content_type); response->status = 200; } else { response->status = 404; } } else { response->status = 404; } o_free(file_path); return U_CALLBACK_CONTINUE; } /** * upload a file */ int callback_upload_file (const struct _u_request * request, struct _u_response * response, void * user_data) { char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie), * post_params = print_map(request->map_post_body); char * string_body = msprintf("Upload file\n\n method is %s\n url is %s\n\n parameters from the url are \n%s\n\n cookies are \n%s\n\n headers are \n%s\n\n post parameters are \n%s\n\n", request->http_verb, request->http_url, url_params, cookies, headers, post_params); ulfius_set_string_body_response(response, 200, string_body); o_free(url_params); o_free(headers); o_free(cookies); o_free(post_params); o_free(string_body); return U_CALLBACK_CONTINUE; } /** * File upload callback function */ int file_upload_callback (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls) { y_log_message(Y_LOG_LEVEL_DEBUG, "Got from file '%s' of the key '%s', offset %llu, size %zu, cls is '%s'", filename, key, off, size, cls); return U_OK; } ulfius-2.5.2/example_programs/sheep_counter/static/000077500000000000000000000000001341017022300225175ustar00rootroot00000000000000ulfius-2.5.2/example_programs/sheep_counter/static/README.md000066400000000000000000000000001341017022300237640ustar00rootroot00000000000000ulfius-2.5.2/example_programs/sheep_counter/static/index.html000066400000000000000000000035331341017022300245200ustar00rootroot00000000000000 Sheep counter



ulfius-2.5.2/example_programs/sheep_counter/static/sheep.png000066400000000000000000000433401341017022300243350ustar00rootroot00000000000000PNG  IHDR ,cAnsBIT|d pHYsYYe8tEXtSoftwarewww.inkscape.org<XtEXtCopyrightCC0 Public Domain Dedication http://creativecommons.org/publicdomain/zero/1.0/EIDATx%UMjrnb%'QE$1 (  6IVe "? A fڻv]<ߣܾ {{E*,mGkp7 66n4gÿzYg3k̫AEeteQm e!\gt{ " i"TT!sc  dN i4nnenЇGl| N4xPSQQiP6x?;s9g"yC^Ee"?e)\jH1 -TY&d0;+IM^^x _uEF_itc) 5"$pn% $GQ ]nnzͥGQ'hߚt]u{ dh[ְ:3"93kUe7E4=Mhmk hP]$1> QT\bR! QfPt ;=fװr0uUhPZ?t[ChyeRDy=-t[4x^']z${`oaQ)SR$c:Bv|F3%cLNX̲$t1+ $D9JQBQRaWE6g3 t*Fmn#{Vڑ YY1ʕNh\ҭP͊q( BBw4ьYhoE 㔴1D[.胶0bI`zҿGG1pLJdhP#P8IJ#dJ=]x ptZ0mEXu@a{]p E!"gN{7|> ;#m hB }/N9S|FNU} g%8 92߫t󯲛 RmR(ʗmXjhFl mTRzwEKl8X4χ=НYAWkdHۨ*!w=vl)GJ3Ss L5`ȸMsO>ϛ>}ST#RA^s\gM"at6k6,KG^zT #Zõ}bAi YAT\fKdh cpc$qֈWWMXG zѰФMǨh!SGCk6D ٨h4DƄ&߈SQ%"ar(wݐn;ynaEngQ7;uTuJzj9!$k0w_ '#U[eUV[-^c5Z+^{u]%:?x+W_%̘1cs w4el*Kוߺb aL~|aVr0t 6 dM:o|X}u89G)BJhnDДʘhWjy=dt RАr|"EeL MfYlu--𢾒ҋǯ1/ &p Ǟtl3"+{gE[y"k^7YhIמanH SAI8xf[I8Yѹ'z8q*i4&2hIqtVW- Ra^".d; GD yeGusL@K*89Pa-!I}F=lޗ(Oο/,s$(*L37{q*3x$ <ӌ051o`vO4|V@GA]s5upiCPaZDŽF>3 6\@Twu.YH{齎C)B"o9wOEywdz[^ '1v"rꪫ&]!e5{_?;bDTg4<\(G{-җtM>dG9I` p t6p2ډj!pNN,Hm tO/2FF5gqI:ęlSɦb4\yCpϢͻ ǡ|HJ8zt3&Z>Qz8ߣk[Ԫ/"Ѩ!vET|FBmύck 0A7BlԎƢ +A@rޏ- R!%/6Z>QwBFTQX[R!1$JUޣl[4(ʘcaF"먤Xn xl wP5dQhSꚸ G؞8t#)Shޱ Oٞx/ i7 Ũ5ACFmdFo[os5i" QK BrwFj|4BMut8,mpS絛FkE/&15Dl!Hle[&rګ)_y XKYӎ_|KI!((͍f֩ Ya!0 [gǣP#uLW6Ա/Eg&_djrz0B\"YjsAEXRWCIVsKhXQB*c!0cֆ(UZX.D)YBѮ6 ۗrO𷂲C_Y z(q0C+*-_z-  !eo”#em\~"i~{Qu,hS`?",kg+`/ 1A@ >tC0 N([OT W(ZkeZp]ylg= E~!|?eI(M@切t&}Hs%"b6zYDT&ġ}@2gu"*dS<Ϣ(01+߁_rY5'Nfc&z4!a1$"!͛$:.0ON;s|W_}u|]wųgώ/wqG|W{l{͒-֊f+">Nw}wgazú Y4ȃ_wE'J?Oz|+c=~8>s'6rymD<8O:{)}ni|vߐܧ>)nC\;hkfXF)I>Eq4M@m@1_BDЎjdq3ru$/l'?sC/֏|UɈ;3sʒ%# g,Ik񡾣by٘lP,X. U BzV<]e]&yU])妛n8m֐9J,8{{I{{ aMH8'}*M@-H&WR@Z<,q(_H:eF7m&q}|bhRNt( m= H>d W Y"JE˚@H {ġ `ynˤH'~&[6S(}$|@pme`GN&SlԮb6mzIwQGšʁ(E)7||>yr9ma>IiE3/0ڗOSB) :LgaX`٢Uq\Hz'6 *32Կj-ʩ%پEH UıdK!~![a-I4K6}@ҢQ/qQuICz BM SMhI'+7o/<`)+[jJ9 F:~_ơ {JZg}S!!hRԎ= C9V{[}b15=L9*ξEArV/5sz׻E$m'QOJ$ta]rꩧUU rld7(4H@!Z z5 I2X*~.O?-ϖ?H) :1aZnq}=أIsq]մ|s LC SPyd$âai|]"/#DI3ki/N-r)Lcf,CLL6"4>U EZB^RЅ;.3,k~Y{> m< k: X WA@A@߬2+HmY$>U9|jz{_җ& H 2j*A@@@WIv~#`!|&6Zl4%dllD'>BFIַ!(y$c`(C\T>KL#FL])rDy%k隀ԻmPG@B [ζ>Pzv %lPMےg}6袋&똰(N;4 ^3`ei%gT6퀾vm'D8\oMdH{6[ڶVz3uց,2]@mCO1K 'f;,YDAA$?oү(-;! ɔЖ֬A@wg >O/uon%{H}_wqlK̗"B-ܲАo|>ף>:HXR.~]yzM7q/4f:蠃$\rIu k/Ԁ7>kfþFF#25lJu|$B9 蜊C@Y IC^;6<`+o7FwtnV$WN?O6|8 1@UCГyi)߿1i~/8}DNPtj? sc(5ah IJu$ "A O&{5Fڄ~5{][cTG<" y5w;BUa-hmJNH"OV =5݌)/u\S}ݵt`0YS?+&U>t$@e]q &5f ! Q*DŧD/A@ǜdRQ:oReDl]_м0ɤ6 }Sc}sBK桌[nqײq?O:>SG8y/~1>=ZN/P֯e I. bZ M,zo$AZ:: o[-su~ϱH〇fo CB@R]B@h$t@?i"F7"%ƅ4`wo$5/?zLd3?^MdK.2ɉJ6z[~ [)FFBŜ^>Co>WfXH)W]u;\lI}Jmߕ:@K6ze<2}+F&II]Bm**TK>'_%fvҦ}ޖ{_* ;v׊~]&f ({0p'wtM(P{w #bFl`ץ^EMDȣ)3 S^auEIERO=Oڟ)[R9}{L[߆ob{_}LgY qO~ƞNm5 i$Uz7F)c~Y/PQ:ٜu]*W^y[xbR!!gO%9]NK0Ґ:7|2ڏ/P,Z 1=mBeIl򌶅(au1㖉~iAlӺ8CiZDDjTF0Ϥj,2ٸRkU|K4E0[ض6%PS2Cz+[.]5 S^Mu-D&5ȗ4{/.z 'G(旃v6 %CKD`y˱=W5zR.QbDƚHjW/lE2Rʚ_ҹs=hQ)"ͤiA!ׂK7&Ԍ'u92_w틑?.^>T* %bh(HEڬ-ڎ:!U"%G:6f^qtI7\KW6M8ꨣ&6/&rxM_gLWN%QJn:~6RڡK)L/aM7m4L5L8mID'9sfc?q*pd~IfmVi[q(zճ i-rO#E#Dy,HܣUE4 Ofwr4k@<)=qMٕ&ksM_6/Ni2]tTL*wMԢH-I?hu S^- ~wPr^*\` 69 /pBc!uTTT'u]ll|c9f"k_Dq?:&f'3C"aWwe7~]y|esrOЮ\д l2r PuL@+UR^WH=tY^UMIPǸ沕&iZGboS26TכS2IHQC xЎBrq>'!MB3xTl2:6@RC_\E-o PL̼{#$'s֫Y (UgK+"AՄ]!vQhǙDx>!!Gf`.ke/L2PrVchUՈ,+g9C*BiCK`c%J$ǡͿu943ij 'hVQ?`)}.J@E^*;}4gqDؚUKp$RltD@6GքfV)H :*u_EJ4s>έPT%vtQ+W讃|,5`Y׾fX )ESӇ|g.($讋D9x]jA|w6:/Zd@ס=#БxɈI N*]Znd!E}mzU|@uj?E@}lC"H="+"?N[$.Eռ.KxL#uq.+j`ef!yȅE@! r#ЌF5!O1>g[A)FIE?EFI =1{-E*_ayWʊFfb>^@J?W\QWsBۨ\ ˣjJ]8)4Wu>Ke˥[-P"Sь=ȗ%g41E_^ yjگ.:*޳mT'7%o!$OIhNNLq)40lϾ V:{ M4dzL]&(p7A^8$h|lO7QwlY)F HBUL[#[r9QU3vJ*}QmŢ>!EAiz%m8pjQrE]pODۢF [bz%BE-ȶu?&VPLaiOU`KLitOQKr}ZPS5m¥Q>,tێ,FlB\4;vQ.\CoRfCT&'Mi3n}Jj\'DPpVE+HpDK!{z(s JmOi>?!xS4MhAiZ،E}[yqґ 6ؠ5YϦCBUoOa!?,6EBu0Ri?FǦ} 8Y_rArT%XJ|'Kl'C `GUOWٟQ'r@EMo#&[~P^dO2ȸ$X“ ~m]t)hGF ZԂbI%Hm\dKR]i#4l2gꫳ9x|}t:; :}pU+o΃mӠJIۀ T%u>lų(]m$c;ڄO(o:=7D_VZ\9BgNՀwYF~:= l$_ao htU6cH-T]8C > 44 1O-J>}x9*dVMPQ[xrUKP}E=2^&W^a_9ɺGj1 ʫ+SZt,|5$Jֵmr傿k*!m.K>}% hk_= TNm_PѪa[os,Ox;H[(uT6p/}'/(`YMU9]tH*|ciJbтc4샣9-ω9atڴ&-ަ[D!+*:6|Bцq|!J]ZO\Rq8@)O/ A>ӡhC<"s-il:r޺gEaT4K.u!$z5!]CE4 0;7Rv=շ0vaO-@46JWrF|,kQ8)-5Ɋ|HTM 0]9;D#j ]VG!ڕ^ 訇2T-)̧tbL6 QCَcU@>'T|yӳ s+-aTQ yԨBmGŤb~7ym~R{ds@NO7d2W=dTgG~iJ;/ YmFҤS_3_/Y|o6fJż6hfY_cneEQ)_oMnU)|wu7lK7Uê3׼QQ[vs|z_^30\ {)2c@IM^XL?>p Jn(^}Qֻx918^*$]hYhX K]&Z22*,kpDBUjʐkD*{9~±~UOFIue}yZv(ʘD+?=5 x"fQ -s2J3nY{^)GkQΔ9ݗ! ݚ;;y.樆x|ɖg{k=,)Ry,jW *CB8h;8%gi}ls!ouG>iXa|&φ٨kDēwj|{A:on|IU !*zfϷt/m 5>*wxgNlPBzMJ6@R[ƽ@CP&jzI9y_j"396Hʄ `bcZdKLh.?"k3/r `H59 4%*!<,i{OvN['4(IahQZ&VXAQYCuJᶅuؚ!Web#sL,Hw`v1}( B0˃ =,oRi@ڵ m'jEN3H$+'i@y5rl"s2J86ma^[ ͡;ߓZ5 6l%5ɇqDBG礓19X,@6|(ɚ(^Mdns_ϊ7"K>=ɃU4H D|Z;E7 6(:[.p`2b/vd7.솵哐?B =Jٶ?uie3% bWS; ~IWqlǿ!k8),NU"_s\'m5PZhWv&XL%{ q砭F^^Jk]te^']||YKoaUZaȲzؒ6'Ј'`#ACGWGh&WX//< N2lw֘$!EhF+ aӰlP=mlHnO@ ϳfޡ q=<_=|S :lHܤQ ɦ %9,80/ R'f_c['L. TJbhcT\wÒI6 ' '"! WRkҋ9A8qLKM'2KB #Mz'E :Q|X2cK7yoWO1h(NMg;#pn:zW-L܆)&C*ȃ~$CK>_6m2%A"/²{M<)GbXkL&yQNͮZJVl-. ]wlQ9`aM=}9ϼؕMp fĴkZv[9'lhJK=xQ1&ؖ 1⋳$^[?i ҃68`{18`I1ey9Yz7B {^Zo^Tr<]m'gJfSG!;l0n0䣓_W!vg^EBH8H8>r aqKm/Ђ$rB2&xSYՖu!K< Kg,`ՄxݫHGPOPT6d hȎk(%KYT ,>,Y{x28mIGb^V5nHjy^Y'e: pFKP:\ZS7x__yBф֒MdLz_Oh{Z64nqr wn/nGGb*)Ǧ ;[-V! =)G!{#:tM8H3-䅲0Lsz?ySutQSJ}#/ 㾚~˚[Gxu(l7""%Ic7 R4ʸIe,y0Aa!I>&|xh{t{'tcQaaI4Ǐ5ۤ[C:o )J!?I 6-ۻ|-$:Tr RvNԳV4$RtTHìk//Y.65TH\W!߻T)IM$ܣۺ2`"Gzg3#%O:a{K2 IDdHϤ _%RXr䅢~$ $s6B# H؇) ӳaW]a7, }`Sl.GaH#Ryͷk 9\$'4I62DDҮu / Z"bsI&UCwI,:mVr]Rb!MtW.ȾPZZRiRP=zԂ#N)56+g1$pvRlrbJYIjQ@L/ާ{褔 Uz*)u1D5pNbJ ȻM v6x^Cv28+ L2'tP[Tqe"`[+ [dTdJaQ\Q.QN!a@>y#7)Ԣݩ[9A'q|MijIALgOb}/-d T))䟝[r#i5R!T&^yXRZ^RWU\enc+Tx4J62B$ Is+T"AdEt_0x0VHHrZKŋi:Ʊji*>{f\9Z[PF$;ީ(X[wtdblg(qXL. ¡Q.ۈhr%v fVpwٰ^"5R$sZYϣ'0]$l̦s$Di-X@*lrYl :, <z-nMȱY,!PHwQŗx\ ly\k=PK٫nb> N0zY"o SpŐkjIpnгy<a%χP>oW-kY6mYlh^<5pC0a԰iYh0zF2xn 6eLm'luW\6ot LJGcF/B2EC|hj>9C1N28!0a0MM0E.g  ?}JO8dEp gA/4?ѠѠ4bF Ѡq7z,~z>[b{}|Hh\mm558`{麥&8mIENDB`ulfius-2.5.2/example_programs/sheep_counter/static/styles.css000066400000000000000000000000701341017022300245510ustar00rootroot00000000000000/* Your Style */ body { background-color: #00AA00; } ulfius-2.5.2/example_programs/sheep_counter/static/upload.html000066400000000000000000000005341341017022300246730ustar00rootroot00000000000000
Select files to upload:

ulfius-2.5.2/example_programs/simple_example/000077500000000000000000000000001341017022300213715ustar00rootroot00000000000000ulfius-2.5.2/example_programs/simple_example/Makefile000066400000000000000000000026601341017022300230350ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # PREFIX=/usr/local ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CC=gcc CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-L$(LIBYDER_LOCATION) -lc -lulfius -lorcania -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: simple_example clean: rm -f *.o simple_example debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: simple_example ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET simple_example.o: simple_example.c $(CC) $(CFLAGS) simple_example.c -DDEBUG -g -O0 simple_example: ../../src/libulfius.so simple_example.o $(CC) -o simple_example simple_example.o $(LIBS) test: simple_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./simple_example static: simple_example.o $(CC) -o simple_example simple_example.o $(PREFIX)/lib/liborcania.a $(PREFIX)/lib/libyder.a $(PREFIX)/lib/libulfius.a -ljansson -lmicrohttpd -lpthread -lgnutls ulfius-2.5.2/example_programs/simple_example/README.md000066400000000000000000000032411341017022300226500ustar00rootroot00000000000000# simple_example Run a simple webservice that listen on port 8537. ## Compile and run ```bash $ make test ``` ## Compile with Ulfius built as a static archive Ulfius must be built as a static archive and installed in the $(PREFIX) directory. ```bash $ make static ``` ### https connection If you want this program to listen to a secure (https) connection, create a certificate with its certificate and private keys in to separate files, and run the program with the options `-secure `. ## Endpoints available: - `GET http://localhost:8537/test`: A "Hello World!" response (status 200) - `GET http://localhost:8537/test/empty`: An empty response (status 200) - `POST http://localhost:8537/test`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `GET http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `POST http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `PUT http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `DELETE http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `GET http://localhost:8537/test/multiple/:multiple/:multiple/:not_multiple`: Will display all requests parameters (body, headers, cookies, etc.) given by the client, and multiple values in `map_url:multiple` key - `GET http://localhost:8537/testcookie/:lang/:extra` Will send a cookie to the client with `lang` and `extra` values in it ulfius-2.5.2/example_programs/simple_example/simple_example.c000066400000000000000000000216161341017022300245470ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the main features * that are available in a callback function * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #ifndef _WIN32 #include #include #include #endif #include #include #define PORT 8537 #define PREFIX "/test" #define PREFIXJSON "/testjson" #define PREFIXCOOKIE "/testcookie" /** * callback functions declaration */ int callback_get_test (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_get_empty_response (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_post_test (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_all_test_foo (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_get_cookietest (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data); /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, value is %s", keys[i], value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s", keys[i], value); if (to_return != NULL) { len = o_strlen(to_return) + o_strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (o_strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((o_strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } char * read_file(const char * filename) { char * buffer = NULL; long length; FILE * f = fopen (filename, "rb"); if (f != NULL) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc (length + 1); if (buffer != NULL) { fread (buffer, 1, length, f); buffer[length] = '\0'; } fclose (f); } return buffer; } int main (int argc, char **argv) { int ret; // Set the framework port number struct _u_instance instance; y_init_logs("simple_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting simple_example"); if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Maximum body size sent by the client is 1 Kb instance.max_post_body_size = 1024; // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, NULL, 0, &callback_get_test, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/empty", 0, &callback_get_empty_response, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/multiple/:multiple/:multiple/:not_multiple", 0, &callback_all_test_foo, NULL); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, NULL, 0, &callback_post_test, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 1"); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 2"); ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 3"); ulfius_add_endpoint_by_val(&instance, "DELETE", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 4"); ulfius_add_endpoint_by_val(&instance, "GET", PREFIXCOOKIE, "/:lang/:extra", 0, &callback_get_cookietest, NULL); // default_endpoint declaration ulfius_set_default_endpoint(&instance, &callback_default, NULL); // Start the framework if (argc == 4 && o_strcmp("-secure", argv[1]) == 0) { // If command-line options are -secure , then open an https connection char * key_pem = read_file(argv[2]), * cert_pem = read_file(argv[3]); ret = ulfius_start_secure_framework(&instance, key_pem, cert_pem); o_free(key_pem); o_free(cert_pem); } else { // Open an http connection ret = ulfius_start_framework(&instance); } if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Start %sframework on port %d", ((argc == 4 && o_strcmp("-secure", argv[1]) == 0)?"secure ":""), instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); y_close_logs(); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function that put a "Hello World!" string in the response */ int callback_get_test (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World!"); return U_CALLBACK_CONTINUE; } /** * Callback function that put an empty response and a status 200 */ int callback_get_empty_response (const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_CONTINUE; } /** * Callback function that put a "Hello World!" and the post parameters send by the client in the response */ int callback_post_test (const struct _u_request * request, struct _u_response * response, void * user_data) { char * post_params = print_map(request->map_post_body); char * response_body = msprintf("Hello World!\n%s", post_params); ulfius_set_string_body_response(response, 200, response_body); o_free(response_body); o_free(post_params); return U_CALLBACK_CONTINUE; } /** * Callback function that put "Hello World!" and all the data sent by the client in the response as string (http method, url, params, cookies, headers, post, json, and user specific data in the response */ int callback_all_test_foo (const struct _u_request * request, struct _u_response * response, void * user_data) { char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie), * post_params = print_map(request->map_post_body); #ifndef _WIN32 char * response_body = msprintf("Hello World!\n\n method is %s\n url is %s\n\n parameters from the url are \n%s\n\n cookies are \n%s\n\n headers are \n%s\n\n post parameters are \n%s\n\n user data is %s\n\nclient address is %s\n\n", request->http_verb, request->http_url, url_params, cookies, headers, post_params, (char *)user_data, inet_ntoa(((struct sockaddr_in *)request->client_address)->sin_addr)); #else char * response_body = msprintf("Hello World!\n\n method is %s\n url is %s\n\n parameters from the url are \n%s\n\n cookies are \n%s\n\n headers are \n%s\n\n post parameters are \n%s\n\n user data is %s\n\n", request->http_verb, request->http_url, url_params, cookies, headers, post_params, (char *)user_data); #endif ulfius_set_string_body_response(response, 200, response_body); o_free(url_params); o_free(headers); o_free(cookies); o_free(post_params); o_free(response_body); return U_CALLBACK_CONTINUE; } /** * Callback function that sets cookies in the response * The counter cookie is incremented every time the client reloads this url */ int callback_get_cookietest (const struct _u_request * request, struct _u_response * response, void * user_data) { const char * lang = u_map_get(request->map_url, "lang"), * extra = u_map_get(request->map_url, "extra"), * counter = u_map_get(request->map_cookie, "counter"); char new_counter[8]; int i_counter; if (counter == NULL) { i_counter = 0; } else { i_counter = strtol(counter, NULL, 10); i_counter++; } snprintf(new_counter, 7, "%d", i_counter); ulfius_add_cookie_to_response(response, "lang", lang, NULL, 0, NULL, NULL, 0, 0); ulfius_add_cookie_to_response(response, "extra", extra, NULL, 0, NULL, NULL, 0, 0); ulfius_add_cookie_to_response(response, "counter", new_counter, NULL, 0, NULL, NULL, 0, 0); ulfius_set_string_body_response(response, 200, "Cookies set!"); return U_CALLBACK_CONTINUE; } /** * Default callback function called if no endpoint has a match */ int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 404, "Page not found, do what you want"); return U_CALLBACK_CONTINUE; } ulfius-2.5.2/example_programs/stream_example/000077500000000000000000000000001341017022300213735ustar00rootroot00000000000000ulfius-2.5.2/example_programs/stream_example/Makefile000066400000000000000000000030341341017022300230330ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2016 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-lc -lorcania -lulfius -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: stream_example stream_client clean: rm -f *.o stream_example stream_client debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: stream_example stream_client ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET stream_example.o: stream_example.c $(CC) $(CFLAGS) stream_example.c -DDEBUG -g -O0 stream_example: ../../src/libulfius.so stream_example.o $(CC) -o stream_example stream_example.o $(LIBS) stream_client.o: stream_client.c $(CC) $(CFLAGS) stream_client.c -DDEBUG -g -O0 stream_client: ../../src/libulfius.so stream_client.o $(CC) -o stream_client stream_client.o $(LIBS) test_example: stream_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./stream_example test_client: stream_client LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./stream_client ulfius-2.5.2/example_programs/stream_example/README.md000066400000000000000000000007041341017022300226530ustar00rootroot00000000000000# Streaming example Provides a streaming example with raw data or an mp3 file ## Compile and run ```bash $ make $ ./stream_example [path/to/an/mp3/file] ``` ## Usage run the `stream_client` program to view the streaming in real time. If you have specified an mp3 file to the stream_example program, open the following url in your browser: [http://localhost:7876/stream/audio](http://localhost:7876/stream/audio), this should play the audio file. ulfius-2.5.2/example_programs/stream_example/stream_client.c000066400000000000000000000046111341017022300243720ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program get a stream data from the server * * Copyright 2016 Nicolas Mora * * License MIT * */ #include #include #include #include #define URL "http://localhost:7876/stream" size_t my_write_body(void * contents, size_t size, size_t nmemb, void * user_data) { printf("got %s", (char *)contents); return size * nmemb; } size_t my_write_meta_body(void * contents, size_t size, size_t nmemb, void * user_data) { printf("got %zu %zu\n", size, nmemb); return size * nmemb; } int main(void) { struct _u_request request; struct _u_response response; int res; y_init_logs("stream_example client", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting stream_example"); ulfius_init_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("GET"); request.http_url = o_strdup(URL); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to run stream test"); getchar(); res = ulfius_send_http_streaming_request(&request, &response, my_write_body, NULL); if (res == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "ulfius_send_http_streaming_request OK"); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error in http request: %d", res); } ulfius_clean_response(&response); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to run stream audio test"); ulfius_init_response(&response); o_free(request.http_url); request.http_url = o_strdup(URL "/audio"); getchar(); res = ulfius_send_http_streaming_request(&request, &response, my_write_meta_body, NULL); if (res == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "ulfius_send_http_streaming_request OK"); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error in http request: %d", res); } ulfius_clean_response(&response); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to run no stream test"); ulfius_init_response(&response); o_free(request.http_url); request.http_url = o_strdup("http://www.w3.org/"); getchar(); res = ulfius_send_http_request(&request, &response); if (res == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "ulfius_send_http_request OK"); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error in http request: %d", res); } ulfius_clean_response(&response); ulfius_clean_request(&request); y_close_logs(); return 0; } ulfius-2.5.2/example_programs/stream_example/stream_example.c000066400000000000000000000071511341017022300245510ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program streams some data * * Copyright 2016 Nicolas Mora * * License MIT * */ #include #include #include #include #include #include #include #include #define PORT 7876 #define PREFIX "/stream" /** * callback functions declaration */ int callback_get_stream (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_get_audio_stream (const struct _u_request * request, struct _u_response * response, void * user_data); ssize_t stream_data (void * cls, uint64_t pos, char *buf, size_t max); ssize_t stream_audio_file (void * cls, uint64_t pos, char *buf, size_t max); void free_stream_data(void * cls); void free_stream_audio_file(void * cls); int main (int argc, char **argv) { int ret; // Set the framework port number struct _u_instance instance; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { printf("Error ulfius_init_instance, abort\n"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, NULL, 0, &callback_get_stream, NULL); if (argc > 1) { ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/audio", 0, &callback_get_audio_stream, argv[1]); } // Start the framework ret = ulfius_start_framework(&instance); if (ret == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { printf("Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function */ int callback_get_stream (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_stream_response(response, 200, stream_data, free_stream_data, U_STREAM_SIZE_UNKOWN, 32 * 1024, o_strdup("stream test")); return U_CALLBACK_CONTINUE; } int callback_get_audio_stream (const struct _u_request * request, struct _u_response * response, void * user_data) { FILE * file = fopen((char *)user_data, "rb"); int fd; struct stat buf; printf("stream audio file\n"); if (file != NULL) { fd = fileno (file); if (-1 == fd) { fclose (file); return U_ERROR; /* internal error */ } if ( (0 != fstat (fd, &buf)) || (! S_ISREG (buf.st_mode)) ) { /* not a regular file, refuse to serve */ fclose (file); file = NULL; return U_ERROR; } u_map_put(response->map_header, "Content-Type", "audio/mpeg"); ulfius_set_stream_response(response, 200, stream_audio_file, free_stream_audio_file, buf.st_size, 32 * 1024, file); return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_ERROR; } } ssize_t stream_data (void * cls, uint64_t pos, char * buf, size_t max) { printf("stream data %" PRIu64 " %zu\n", pos, max); sleep(1); if (pos <= 100) { snprintf(buf, max, "%s %" PRIu64 "\n", (char *)cls, pos); return o_strlen(buf); } else { return U_STREAM_END; } } ssize_t stream_audio_file (void * cls, uint64_t pos, char * buf, size_t max) { FILE *file = cls; (void) fseek (file, pos, SEEK_SET); return fread (buf, 1, max, file); } void free_stream_data(void * cls) { printf("clean data\n"); o_free(cls); } void free_stream_audio_file(void * cls) { printf("clean file\n"); FILE *file = cls; fclose (file); } ulfius-2.5.2/example_programs/test_u_map/000077500000000000000000000000001341017022300205255ustar00rootroot00000000000000ulfius-2.5.2/example_programs/test_u_map/Makefile000066400000000000000000000022751341017022300221730ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2014-2015 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-lc -lorcania -lulfius -L$(ULFIUS_LOCATION) ifndef YDERFLAG LIBS+= -lyder endif all: test_u_map clean: rm -f *.o test_u_map debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: test_u_map ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET test_u_map.o: test_u_map.c $(CC) $(CFLAGS) test_u_map.c -DDEBUG -g -O0 test_u_map: ../../src/libulfius.so test_u_map.o $(CC) -o test_u_map test_u_map.o $(LIBS) test: test_u_map LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./test_u_map ulfius-2.5.2/example_programs/test_u_map/README.md000066400000000000000000000002021341017022300217760ustar00rootroot00000000000000# _u_map test program Execute use cases for `struct _u_map` and display the results ## Compile and run ```bash $ make test ``` ulfius-2.5.2/example_programs/test_u_map/test_u_map.c000066400000000000000000000160251341017022300230350ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the main features * that are available in struct _u_map * * Copyright 2015 Nicolas Mora * * License MIT * */ #include #include #include #include /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); if (to_return != NULL) { len = o_strlen(to_return) + o_strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (o_strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((o_strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } /** * put the content of a file in the u_map with the file_path as the key using u_map_put_binary */ int put_file_content_in_map (struct _u_map * map, const char * file_path, uint64_t offset) { void * buffer = NULL; long length; FILE * f; int res = U_OK; if (access(file_path, F_OK) != -1 && map != NULL) { f = fopen (file_path, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc(length*sizeof(void)); if (buffer) { fread (buffer, 1, length, f); } fclose (f); } if (buffer) { res = u_map_put_binary(map,file_path, (char *)buffer, offset, length); o_free(buffer); } else { res = U_ERROR; } } else { res = U_ERROR_PARAMS; } return res; } int main (int argc, char **argv) { y_init_logs("test_u_map", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting test_u_map"); struct _u_map map, * map_copy; char * print, * print_copy; u_map_init(&map); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 1, map is\n%s\n", print); o_free(print); u_map_put(&map, "key1", "value1"); u_map_put(&map, "key2", "value2"); u_map_put(&map, "key3", "value3"); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 2, map is\n%s\n", print); o_free(print); u_map_put(&map, "key2", "value4"); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 3, map is\n%s\n", print); o_free(print); map_copy = u_map_copy(&map); u_map_put(map_copy, "key4", "value5"); print = print_map(&map); print_copy = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 4, map is\n%s\nmap_copy is\n%s\n", print, print_copy); o_free(print); o_free(print_copy); u_map_remove_at(&map, 2); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 5: remove item 2 from map, map is\n%s\n", print); o_free(print); u_map_remove_from_key(map_copy, "key1"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 6: remove key:key1 from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_key(map_copy, "key_nope"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 7: remove key:key_nope from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_key_case(map_copy, "Key2"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 8: remove case key:Key2 from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_value(map_copy, "value3"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 9: remove value:value3 from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_value_case(map_copy, "Value5"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 10: remove case value:Value5 from map_copy, map is\n%s\n", print); o_free(print); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key key1? %d", u_map_has_key(&map, "key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key key_nope? %d", u_map_has_key(&map, "key_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key Key1? %d", u_map_has_key(&map, "Key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key Key1 (no case) ? %d", u_map_has_key_case(&map, "Key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key Key_nope (no case) ? %d", u_map_has_key_case(&map, "Key_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value value1? %d", u_map_has_value(&map, "value1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value value_nope? %d", u_map_has_value(&map, "value_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value Value1? %d", u_map_has_value(&map, "Value1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value Value1 (no case) ? %d", u_map_has_value_case(&map, "Value1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value Value_nope (no case) ? %d", u_map_has_value_case(&map, "Value_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key key1: %s", u_map_get(&map, "key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key key_nope: %s", u_map_get(&map, "key_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key Key1 (no case): %s", u_map_get_case(&map, "Key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key Key_nope (no case): %s", u_map_get_case(&map, "Key_nope")); put_file_content_in_map(&map, "../sheep_counter/static/sheep.png", 0); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 11, map is\n%s\n", print); o_free(print); u_map_remove_from_key(&map, "../sheep_counter/static/sheep.png"); put_file_content_in_map(&map, "Makefile", 0); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 12, map is\n%s\n", print); o_free(print); u_map_put_binary(&map, "Makefile", "Replace the first characters", 0, o_strlen("Replace the first characters")); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 12, map is\n%s\n", print); o_free(print); u_map_put_binary(&map, "Makefile", "Append at the end of the value", u_map_get_length(&map, "Makefile"), o_strlen("Append at the end of the value")); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 12, map is\n%s\n", print); o_free(print); u_map_clean(&map); u_map_clean_full(map_copy); y_close_logs(); return 0; } ulfius-2.5.2/example_programs/websocket_example/000077500000000000000000000000001341017022300220665ustar00rootroot00000000000000ulfius-2.5.2/example_programs/websocket_example/Makefile000066400000000000000000000042411341017022300235270ustar00rootroot00000000000000# # Websocket example program # # Makefile used to build the software # # Copyright 2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_LOCATION=../../src ULFIUS_INCLUDE=../../include EXAMPLE_INCLUDE=../include CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -I$(STATIC_FILE_LOCATION) $(ADDITIONALFLAGS) $(CPPFLAGS) STATIC_FILE_LOCATION=../../example_callbacks/static_file LIBS=-lc -lulfius -lorcania -L$(ULFIUS_LOCATION) #SECUREFLAG=-https test.key test.pem ifndef YDERFLAG LIBS+= -lyder endif all: websocket_server websocket_client clean: rm -f *.o websocket_server websocket_client valgrind_server.txt valgrind_client.txt debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: websocket_server websocket_client ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON static_file_callback.o: $(STATIC_FILE_LOCATION)/static_file_callback.c $(CC) $(CFLAGS) $(STATIC_FILE_LOCATION)/static_file_callback.c websocket_server: ../../src/libulfius.so static_file_callback.o websocket_server.c $(CC) $(CFLAGS) websocket_server.c $(CC) -o websocket_server websocket_server.o static_file_callback.o $(LIBS) websocket_client: ../../src/libulfius.so websocket_client.c $(CC) $(CFLAGS) websocket_client.c $(CC) -o websocket_client websocket_client.o $(LIBS) test_server: websocket_server LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./websocket_server $(SECUREFLAG) test_client: websocket_client LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./websocket_client $(SECUREFLAG) memcheck_server: websocket_server LD_LIBRARY_PATH=../../src valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./websocket_server $(SECUREFLAG) 2>valgrind_server.txt memcheck_client: websocket_client LD_LIBRARY_PATH=../../src valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./websocket_client $(SECUREFLAG) 2>valgrind_client.txt ulfius-2.5.2/example_programs/websocket_example/README.md000066400000000000000000000031711341017022300233470ustar00rootroot00000000000000# websocket_server Run a webservice service or client. ## Compile and run ### HTTP mode Run a HTTP (non secure) webservice ```bash $ make debug $ LD_LIBRARY_PATH=../../src: ./websocket_server ``` ### HTTPS mode You must have a certificate key and pem files available. To generate one, you can for example use the following command: ```bash $ openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.key -sha256 ``` Run a HTTPS (secure) webservice ```bash $ make debug $ LD_LIBRARY_PATH=../../src: ./websocket_server -https cert.key cert.pem ``` ## Endpoints available: ### Websocket - `GET http://localhost:9275/websocket`: Websocket basic endpoint that will send binary or text messages every 2 seconds - `GET http://localhost:9275/websocket/file`: Websocket endpoint that will accept a binary message that can contain a file - `GET http://localhost:9275/websocket/echo`: Websocket echo endpoint, all received messages will be resend to the client ### Static file - `GET http://localhost:9275/static`: will serve the files located in the `static` folder ## Test the websocket ### Browser Open in your browser the url [http[s]://localhost:9275/static/index.html](http://localhost:9275/static/index.html), this will open an HTML page where you can test the websockets behaviour. ### Ulfius client Run the `websocket_client` program while the `websocket_server` is running. ### HTTP mode Run a HTTP (non secure) webservice ```bash $ make debug $ LD_LIBRARY_PATH=../../src: ./websocket_client ``` ### HTTPS mode Run a HTTPS (secure) webservice ```bash $ make debug $ LD_LIBRARY_PATH=../../src: ./websocket_client -https ``` ulfius-2.5.2/example_programs/websocket_example/static/000077500000000000000000000000001341017022300233555ustar00rootroot00000000000000ulfius-2.5.2/example_programs/websocket_example/static/index.html000066400000000000000000000145611341017022300253610ustar00rootroot00000000000000 Websocket example




ulfius-2.5.2/example_programs/websocket_example/static/styles.css000066400000000000000000000000701341017022300254070ustar00rootroot00000000000000/* Your Style */ body { background-color: #AAAAAA; } ulfius-2.5.2/example_programs/websocket_example/websocket_client.c000066400000000000000000000127711341017022300255660ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program implements a websocket * * Copyright 2017 Nicolas Mora * * License MIT * */ #include #include #include #include #include #include #include #define PORT "9275" #define PREFIX_WEBSOCKET "/websocket" #if defined(U_DISABLE_WEBSOCKET) int main() { fprintf(stderr, "Websocket not supported, please recompile ulfius with websocket support\n"); return 1; } #else void websocket_manager_callback(const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { if (websocket_manager_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_manager_user_data is %s", websocket_manager_user_data); } // Send text message without fragmentation if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen("Message without fragmentation from client"), "Message without fragmentation from client") != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send message without fragmentation"); } } // Send text message with fragmentation if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_fragmented_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen("Message with fragmentation from client"), "Message with fragmentation from client", 5) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send message with fragmentation"); } } // Send ping message if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_PING, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send ping message"); } } // Send binary message without fragmentation if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_BINARY, o_strlen("Message without fragmentation from client"), "Message without fragmentation from client") != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send binary message without fragmentation"); } } y_log_message(Y_LOG_LEVEL_DEBUG, "Closing websocket_manager_callback"); } /** * websocket_incoming_message_callback * Read incoming message and prints it on the console */ void websocket_incoming_message_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * last_message, void * websocket_incoming_message_user_data) { if (websocket_incoming_message_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_incoming_message_user_data is %s", websocket_incoming_message_user_data); } y_log_message(Y_LOG_LEVEL_DEBUG, "Incoming message, opcode: 0x%02x, mask: %d, len: %zu", last_message->opcode, last_message->has_mask, last_message->data_len); if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) { y_log_message(Y_LOG_LEVEL_DEBUG, "text payload '%.*s'", last_message->data_len, last_message->data); } else if (last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) { y_log_message(Y_LOG_LEVEL_DEBUG, "binary payload"); } } void websocket_onclose_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data) { if (websocket_onclose_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_onclose_user_data is %s", websocket_onclose_user_data); o_free(websocket_onclose_user_data); } } int main(int argc, char ** argv) { struct _u_request request; struct _u_response response; struct _websocket_client_handler websocket_client_handler; char * websocket_user_data = o_strdup("my user data"); char * url = (argc>1&&0==o_strcmp("-https", argv[1]))?"wss://localhost:" PORT PREFIX_WEBSOCKET:"ws://localhost:" PORT PREFIX_WEBSOCKET; y_init_logs("websocket_client", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_client"); ulfius_init_request(&request); ulfius_init_response(&response); if (ulfius_set_websocket_request(&request, url, "protocol", "extension") == U_OK) { request.check_server_certificate = 0; if (ulfius_open_websocket_client_connection(&request, &websocket_manager_callback, websocket_user_data, &websocket_incoming_message_callback, websocket_user_data, &websocket_onclose_callback, websocket_user_data, &websocket_client_handler, &response) == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Wait for user to press to close the program"); getchar(); ulfius_websocket_client_connection_close(&websocket_client_handler); y_log_message(Y_LOG_LEVEL_DEBUG, "Websocket closed"); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_open_websocket_client_connection"); o_free(websocket_user_data); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_set_websocket_request"); o_free(websocket_user_data); } ulfius_clean_request(&request); ulfius_clean_response(&response); y_close_logs(); return 0; } #endif ulfius-2.5.2/example_programs/websocket_example/websocket_server.c000066400000000000000000000275751341017022300256260ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program implements a websocket * * Copyright 2017 Nicolas Mora * * License MIT * */ #include #include #include #include #include #include #include #include "static_file_callback.h" #define PORT 9275 #define PREFIX_WEBSOCKET "/websocket" #define PREFIX_STATIC "/static" #if defined(U_DISABLE_WEBSOCKET) int main() { fprintf(stderr, "Websocket not supported, please recompile ulfius with websocket support\n"); return 1; } #else int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_websocket_echo (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_websocket_file (const struct _u_request * request, struct _u_response * response, void * user_data); static char * read_file(const char * filename) { char * buffer = NULL; long length; FILE * f; if (filename != NULL) { f = fopen (filename, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc (length + 1); if (buffer) { fread (buffer, 1, length, f); buffer[length] = '\0'; } fclose (f); } return buffer; } else { return NULL; } } /** * main function * open the wbservice on port 9275 */ int main(int argc, char ** argv) { int ret; struct _u_instance instance; struct _static_file_config file_config; char * cert_file = NULL, * key_file = NULL; y_init_logs("websocket_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_example"); file_config.mime_types = o_malloc(sizeof(struct _u_map)); u_map_init(file_config.mime_types); u_map_put(file_config.mime_types, ".html", "text/html"); u_map_put(file_config.mime_types, ".css", "text/css"); u_map_put(file_config.mime_types, ".js", "application/javascript"); u_map_put(file_config.mime_types, ".png", "image/png"); u_map_put(file_config.mime_types, ".jpg", "image/jpeg"); u_map_put(file_config.mime_types, ".jpeg", "image/jpeg"); u_map_put(file_config.mime_types, ".ttf", "font/ttf"); u_map_put(file_config.mime_types, ".woff", "font/woff"); u_map_put(file_config.mime_types, ".woff2", "font/woff2"); u_map_put(file_config.mime_types, ".map", "application/octet-stream"); u_map_put(file_config.mime_types, "*", "application/octet-stream"); file_config.files_path = "static"; file_config.url_prefix = PREFIX_STATIC; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/echo", 0, &callback_websocket_echo, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/file", 0, &callback_websocket_file, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_STATIC, "*", 0, &callback_static_file, &file_config); // Start the framework if (argc > 3 && 0 == o_strcmp(argv[1], "-https")) { key_file = read_file(argv[2]); cert_file = read_file(argv[3]); if (key_file == NULL || cert_file == NULL) { printf("Error reading https certificate files\n"); ret = U_ERROR_PARAMS; } else { ret = ulfius_start_secure_framework(&instance, key_file, cert_file); } o_free(key_file); o_free(cert_file); } else { ret = ulfius_start_framework(&instance); } if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_INFO, "Start framework on port %d %s", instance.port, (argc > 1 && 0 == o_strcmp(argv[1], "-https"))?"https mode":"http mode"); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_INFO, "End framework"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); u_map_clean_full(file_config.mime_types); y_close_logs(); return 0; } /** * websocket_onclose_callback * onclose callback function * Used to clear data after the websocket connection is closed */ void websocket_onclose_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data) { if (websocket_onclose_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_onclose_user_data is %s", websocket_onclose_user_data); o_free(websocket_onclose_user_data); } } void websocket_onclose_file_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_onclose_file_callback"); } /** * websocket_manager_callback * send 5 text messages and 1 ping for 11 seconds, then closes the websocket */ void websocket_manager_callback(const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { if (websocket_manager_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_manager_user_data is %s", websocket_manager_user_data); } // Send text message without fragmentation if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen("Message without fragmentation from server"), "Message without fragmentation from server") != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send message without fragmentation"); } } // Send text message with fragmentation for ulfius clients only, browsers seem to dislike fragmented messages if (o_strncmp(u_map_get(request->map_header, "User-Agent"), U_WEBSOCKET_USER_AGENT, o_strlen(U_WEBSOCKET_USER_AGENT)) == 0 && ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_fragmented_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen("Message with fragmentation from server"), "Message with fragmentation from server", 5) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send message with fragmentation"); } } // Send ping message if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_PING, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send ping message"); } } // Send binary message without fragmentation if (ulfius_websocket_wait_close(websocket_manager, 2000) == U_WEBSOCKET_STATUS_OPEN) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_BINARY, o_strlen("Message without fragmentation from server"), "Message without fragmentation from server") != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error send binary message without fragmentation"); } } y_log_message(Y_LOG_LEVEL_DEBUG, "Closing websocket_manager_callback"); } void websocket_manager_file_callback(const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { y_log_message(Y_LOG_LEVEL_DEBUG, "Opening websocket_manager_file_callback"); for (;;) { sleep(1); if (websocket_manager == NULL || !websocket_manager->connected) { break; } } y_log_message(Y_LOG_LEVEL_DEBUG, "Closing websocket_manager_file_callback"); } /** * websocket_incoming_message_callback * Read incoming message and prints it on the console */ void websocket_incoming_message_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * last_message, void * websocket_incoming_message_user_data) { if (websocket_incoming_message_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_incoming_message_user_data is %s", websocket_incoming_message_user_data); } y_log_message(Y_LOG_LEVEL_DEBUG, "Incoming message, opcode: 0x%02x, mask: %d, len: %zu", last_message->opcode, last_message->has_mask, last_message->data_len); if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) { y_log_message(Y_LOG_LEVEL_DEBUG, "text payload '%.*s'", last_message->data_len, last_message->data); } else if (last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) { y_log_message(Y_LOG_LEVEL_DEBUG, "binary payload"); } } void websocket_echo_message_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * last_message, void * websocket_incoming_message_user_data) { y_log_message(Y_LOG_LEVEL_DEBUG, "Incoming message, opcode: 0x%02x, mask: %d, len: %zu, text payload '%.*s'", last_message->opcode, last_message->has_mask, last_message->data_len, last_message->data_len, last_message->data); if (ulfius_websocket_send_message(websocket_manager, last_message->opcode, last_message->data_len, last_message->data) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_websocket_send_message"); } } void websocket_incoming_file_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * last_message, void * websocket_incoming_message_user_data) { char * my_message = msprintf("Incoming file %p, opcode: 0x%02x, mask: %d, len: %zu", last_message, last_message->opcode, last_message->has_mask, last_message->data_len); y_log_message(Y_LOG_LEVEL_DEBUG, my_message); ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(my_message), my_message); o_free(my_message); } /** * Ulfius main callback function that simply calls the websocket manager and closes */ int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data) { char * websocket_user_data = o_strdup("my_user_data"); int ret; if ((ret = ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_callback, websocket_user_data, &websocket_incoming_message_callback, websocket_user_data, &websocket_onclose_callback, websocket_user_data)) == U_OK) { return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_ERROR; } } int callback_websocket_echo (const struct _u_request * request, struct _u_response * response, void * user_data) { char * websocket_user_data = o_strdup("my_user_data"); int ret; y_log_message(Y_LOG_LEVEL_DEBUG, "Client connected to echo websocket"); if ((ret = ulfius_set_websocket_response(response, NULL, NULL, NULL, NULL, &websocket_echo_message_callback, websocket_user_data, &websocket_onclose_callback, websocket_user_data)) == U_OK) { return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_ERROR; } } int callback_websocket_file (const struct _u_request * request, struct _u_response * response, void * user_data) { int ret; if ((ret = ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_file_callback, NULL, &websocket_incoming_file_callback, NULL, &websocket_onclose_file_callback, NULL)) == U_OK) { return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_ERROR; } } #endif ulfius-2.5.2/include/000077500000000000000000000000001341017022300144435ustar00rootroot00000000000000ulfius-2.5.2/include/u_private.h000066400000000000000000000146451341017022300166240ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_private.h: private structures and functions declarations * * Copyright 2015-2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #ifndef __U_PRIVATE_H__ #define __U_PRIVATE_H__ #include "ulfius.h" /** Macro to avoid compiler warning when some parameters are unused and that's ok **/ #define UNUSED(x) (void)(x) /** * For using Ulfius in embedded systems * Thanks to Dirk Uhlemann */ #ifdef U_WITH_FREERTOS #include #define sockaddr freertos_sockaddr typedef unsigned long int socklen_t; #else #ifdef U_WITH_LWIP #include #endif // U_WITH_LWIP #endif // U_WITH_FREERTOS /********************************** * Internal functions declarations **********************************/ /** * ulfius_endpoint_match * return the endpoint array matching the url called with the proper http method * the returned array always has its last value to NULL * return NULL on memory error */ struct _u_endpoint ** ulfius_endpoint_match(const char * method, const char * url, struct _u_endpoint * endpoint_list); /** * ulfius_parse_url * fills map with the keys/values defined in the url that are described in the endpoint format url * return U_OK on success */ int ulfius_parse_url(const char * url, const struct _u_endpoint * endpoint, struct _u_map * map, int check_utf8); /** * ulfius_set_response_header * adds headers defined in the response_map_header to the response * return the number of added headers, -1 on error */ int ulfius_set_response_header(struct MHD_Response * response, const struct _u_map * response_map_header); /** * ulfius_set_response_cookie * adds cookies defined in the response_map_cookie * return the number of added headers, -1 on error */ int ulfius_set_response_cookie(struct MHD_Response * mhd_response, const struct _u_response * response); /** * The utf8_check() function scans the '\0'-terminated string starting * at s. It returns a pointer to the first byte of the first malformed * or overlong UTF-8 sequence found, or NULL if the string contains * only correct UTF-8. It also spots UTF-8 sequences that could cause * trouble if converted to UTF-16, namely surrogate characters * (U+D800..U+DFFF) and non-Unicode positions (U+FFFE..U+FFFF). This * routine is very likely to find a malformed sequence if the input * uses any other encoding than UTF-8. It therefore can be used as a * very effective heuristic for distinguishing between UTF-8 and other * encodings. * * I wrote this code mainly as a specification of functionality; there * are no doubt performance optimizations possible for certain CPUs. * * Markus Kuhn -- 2005-03-30 * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html */ const unsigned char * utf8_check(const char * s_orig); #ifndef U_DISABLE_WEBSOCKET /** * Websocket callback function for MHD * Starts the websocket manager if set, * then sets a listening message loop * Complete the callback when the websocket is closed * The websocket can be closed by the client, the manager, the program, or on network disconnect */ void ulfius_start_websocket_cb (void *cls, struct MHD_Connection *connection, void *con_cls, const char *extra_in, size_t extra_in_size, MHD_socket sock, struct MHD_UpgradeResponseHandle *urh); /** * Generates a handhshake answer from the key given in parameter */ int ulfius_generate_handshake_answer(const char * key, char * out_digest); /** * Return a match list between two list of items * If match is NULL, then return source duplicate * result must be u_free'd after use */ int ulfius_check_list_match(const char * source, const char * match, const char * separator, char ** result); /** * Return the first match between two list of items * If match is NULL, then return the first element of source * result must be u_free'd after use */ int ulfius_check_first_match(const char * source, const char * match, const char * separator, char ** result); /** * Initialize a websocket message list * Return U_OK on success */ int ulfius_init_websocket_message_list(struct _websocket_message_list * message_list); /** * Clear data of a websocket message list */ void ulfius_clear_websocket_message_list(struct _websocket_message_list * message_list); /** * Append a message in a message list * Return U_OK on success */ int ulfius_push_websocket_message(struct _websocket_message_list * message_list, struct _websocket_message * message); /** * Clear data of a websocket */ int ulfius_clear_websocket(struct _websocket * websocket); /** * Clear data of a websocket_manager */ void ulfius_clear_websocket_manager(struct _websocket_manager * websocket_manager); /** * Close the websocket */ int ulfius_close_websocket(struct _websocket * websocket); /** * Run the websocket manager in a separated detached thread */ void * ulfius_thread_websocket_manager_run(void * args); /** * Add a websocket in the list of active websockets of the instance */ int ulfius_instance_add_websocket_active(struct _u_instance * instance, struct _websocket * websocket); /** * Remove a websocket from the list of active websockets of the instance */ int ulfius_instance_remove_websocket_active(struct _u_instance * instance, struct _websocket * websocket); /** * Initialize a struct _websocket * return U_OK on success */ int ulfius_init_websocket(struct _websocket * websocket); /** * Initialize a struct _websocket_manager * return U_OK on success */ int ulfius_init_websocket_manager(struct _websocket_manager * websocket_manager); /** * Check if the response corresponds to the transformation of the key with the magic string */ int ulfius_check_handshake_response(const char * key, const char * response); #endif // U_DISABLE_WEBSOCKET #endif // __U_PRIVATE_H__ ulfius-2.5.2/include/ulfius-cfg.h.in000066400000000000000000000020571341017022300172710ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * ulfius-cfg.h.in: configuration file * * Copyright 2018 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #ifndef _ULFIUS_CFG_H_ #define _ULFIUS_CFG_H_ #define ULFIUS_VERSION ${PROJECT_VERSION} #cmakedefine U_DISABLE_JANSSON #cmakedefine U_DISABLE_CURL #cmakedefine U_DISABLE_WEBSOCKET #cmakedefine U_DISABLE_YDER #cmakedefine U_WITH_FREERTOS #cmakedefine U_WITH_LWIP #endif /* _ULFIUS_CFG_H_ */ ulfius-2.5.2/include/ulfius.h000066400000000000000000001546751341017022300161450ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * ulfius.h: public structures and functions declarations * * Copyright 2015-2018 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #ifndef __ULFIUS_H__ #define __ULFIUS_H__ #include "ulfius-cfg.h" /** External dependencies **/ #ifndef U_DISABLE_WEBSOCKET #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #ifndef POLLRDHUP #define POLLRDHUP 0x2000 #endif #endif #include #include #if defined(_WIN32) && !defined(U_DISABLE_WEBSOCKET) #define U_DISABLE_WEBSOCKET #endif #if (MHD_VERSION < 0x00095300) && !defined(U_DISABLE_WEBSOCKET) #define U_DISABLE_WEBSOCKET #endif /** Angharad libraries **/ #include /** To disable all yder log messages, this flag must be enabled **/ #ifndef U_DISABLE_YDER #include #else #define Y_LOG_MODE_NONE 0 #define Y_LOG_MODE_CONSOLE 0 #define Y_LOG_MODE_SYSLOG 0 #define Y_LOG_MODE_FILE 0 #define Y_LOG_MODE_JOURNALD 0 #define Y_LOG_MODE_CALLBACK 0 #define Y_LOG_MODE_CURRENT 0 #define Y_LOG_LEVEL_NONE 0 #define Y_LOG_LEVEL_DEBUG 0 #define Y_LOG_LEVEL_INFO 0 #define Y_LOG_LEVEL_WARNING 0 #define Y_LOG_LEVEL_ERROR 0 #define Y_LOG_LEVEL_CURRENT 0 int y_init_logs(const char * app, const unsigned long init_mode, const unsigned long init_level, const char * init_log_file, const char * message); int y_set_logs_callback(void (* y_callback_log_message) (void * cls, const char * app_name, const time_t date, const unsigned long level, const char * message), void * cls, const char * message); void y_log_message(const unsigned long type, const char * message, ...); int y_close_logs(); #endif #ifndef U_DISABLE_JANSSON #include #endif #define ULFIUS_STREAM_BLOCK_SIZE_DEFAULT 1024 #define U_STREAM_END MHD_CONTENT_READER_END_OF_STREAM #define U_STREAM_ERROR MHD_CONTENT_READER_END_WITH_ERROR #define U_STREAM_SIZE_UNKOWN MHD_SIZE_UNKNOWN #define U_OK 0 // No error #define U_ERROR 1 // Error #define U_ERROR_MEMORY 2 // Error in memory allocation #define U_ERROR_PARAMS 3 // Error in input parameters #define U_ERROR_LIBMHD 4 // Error in libmicrohttpd execution #define U_ERROR_LIBCURL 5 // Error in libcurl execution #define U_ERROR_NOT_FOUND 6 // Something was not found #define U_ERROR_DISCONNECTED 7 // Connection closed #define U_CALLBACK_CONTINUE 0 #define U_CALLBACK_COMPLETE 1 #define U_CALLBACK_UNAUTHORIZED 2 #define U_CALLBACK_ERROR 3 #define U_COOKIE_SAME_SITE_NONE 0 #define U_COOKIE_SAME_SITE_STRICT 1 #define U_COOKIE_SAME_SITE_LAX 2 /************* * Structures *************/ /** * struct _u_map */ struct _u_map { int nb_values; char ** keys; char ** values; size_t * lengths; }; /** * struct _u_cookie * the structure containing the response cookie parameters */ struct _u_cookie { char * key; char * value; char * expires; unsigned int max_age; char * domain; char * path; int secure; int http_only; int same_site; }; /** * * Structure of request parameters * * Contains request data * http_protocol: http protocol used (1.0 or 1.1) * http_verb: http method (GET, POST, PUT, DELETE, etc.), use '*' to match all http methods * http_url: url used to call this callback function or full url to call when used in a ulfius_send_http_request * proxy: proxy address to use for outgoing connections, used by ulfius_send_http_request * check_server_certificate: do not check server certificate and hostname if false (default true), used by ulfius_send_http_request * timeout connection timeout used by ulfius_send_http_request, default is 0 * client_address: IP address of the client * auth_basic_user: basic authtication username * auth_basic_password: basic authtication password * map_url: map containing the url variables, both from the route and the ?key=value variables * map_header: map containing the header variables * map_cookie: map containing the cookie variables * map_post_body: map containing the post body variables (if available) * binary_body: pointer to raw body * binary_body_length: length of raw body * client_cert: x509 certificate of the client if the instance uses client certificate authentication and the client is authenticated * available only if websocket support is enabled * client_cert_file: path to client certificate file for sending http requests with certificate authentication * available only if websocket support is enabled * client_key_file: path to client key file for sending http requests with certificate authentication * available only if websocket support is enabled * client_key_password: password to unlock client key file * available only if websocket support is enabled */ struct _u_request { char * http_protocol; char * http_verb; char * http_url; char * proxy; int check_server_certificate; long timeout; struct sockaddr * client_address; char * auth_basic_user; char * auth_basic_password; struct _u_map * map_url; struct _u_map * map_header; struct _u_map * map_cookie; struct _u_map * map_post_body; void * binary_body; size_t binary_body_length; #ifndef U_DISABLE_WEBSOCKET gnutls_x509_crt_t client_cert; char * client_cert_file; char * client_key_file; char * client_key_password; #endif }; /** * * Structure of response parameters * * Contains response data that must be set by the user * status: HTTP status code (200, 404, 500, etc) * protocol: HTTP Protocol sent * map_header: map containing the header variables * nb_cookies: number of cookies sent * map_cookie: array of cookies sent * auth_realm: realm to send to the client on authenticationb failed * binary_body: a void * containing a raw binary content * binary_body_length: the length of the binary_body * stream_callback: callback function to stream data in response body * stream_callback_free: callback function to free data allocated for streaming * stream_size: size of the streamed data (U_STREAM_SIZE_UNKOWN if unknown) * stream_block_size: size of each block to be streamed, set according to your system * stream_user_data: user defined data that will be available in your callback stream functions * websocket_handle: handle for websocket extension * shared_data: any data shared between callback functions, must be allocated and freed by the callback functions * timeout: Timeout in seconds to close the connection because of inactivity between the client and the server * */ struct _u_response { long status; char * protocol; struct _u_map * map_header; unsigned int nb_cookies; struct _u_cookie * map_cookie; char * auth_realm; void * binary_body; size_t binary_body_length; ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max); void (* stream_callback_free) (void * stream_user_data); uint64_t stream_size; size_t stream_block_size; void * stream_user_data; void * websocket_handle; void * shared_data; unsigned int timeout; }; /** * * Structure of an endpoint * * Contains all informations needed for an endpoint * http_method: http verb (GET, POST, PUT, etc.) in upper case * url_prefix: prefix for the url (optional) * url_format: string used to define the endpoint format * separate words with / * to define a variable in the url, prefix it with @ or : * example: /test/resource/:name/elements * on an url_format that ends with '*', the rest of the url will not be tested * priority: endpoint priority in descending order (0 is the higher priority) * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in callback_function * */ struct _u_endpoint { char * http_method; char * url_prefix; char * url_format; unsigned int priority; int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) struct _u_response * response, // Output parameters (set by the user) void * user_data); void * user_data; }; /** * * Structure of an instance * * Contains the needed data for an ulfius instance to work * * mhd_daemon: pointer to the libmicrohttpd daemon * status: status of the current instance, status are U_STATUS_STOP, U_STATUS_RUNNING or U_STATUS_ERROR * port: port number to listen to * bind_address: ip address to listen to (optional) * timeout: Timeout to close the connection because of inactivity between the client and the server * nb_endpoints: Number of available endpoints * default_auth_realm: Default realm on authentication error * endpoint_list: List of available endpoints * default_endpoint: Default endpoint if no other endpoint match the current url * default_headers: Default headers that will be added to all response->map_header * max_post_param_size: maximum size for a post parameter, 0 means no limit, default 0 * max_post_body_size: maximum size for the entire post body, 0 means no limit, default 0 * websocket_handler: handler for the websocket structure * file_upload_callback: callback function to manage file upload by blocks * file_upload_cls: any pointer to pass to the file_upload_callback function * mhd_response_copy_data: to choose between MHD_RESPMEM_MUST_COPY and MHD_RESPMEM_MUST_FREE * check_utf8: check that all parameters values in the request (url, header and post_body) * are valid utf8 strings, if a parameter value has non utf8 character, the value * will be ignored, default 1 * use_client_cert_auth: Internal variable use to indicate if the instance uses client certificate authentication * Do not change this value, available only if websocket support is enabled * */ struct _u_instance { struct MHD_Daemon * mhd_daemon; int status; unsigned int port; struct sockaddr_in * bind_address; unsigned int timeout; int nb_endpoints; char * default_auth_realm; struct _u_endpoint * endpoint_list; struct _u_endpoint * default_endpoint; struct _u_map * default_headers; size_t max_post_param_size; size_t max_post_body_size; void * websocket_handler; int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls); void * file_upload_cls; int mhd_response_copy_data; int check_utf8; #ifndef U_DISABLE_WEBSOCKET int use_client_cert_auth; #endif }; /** * Structures used to facilitate data manipulations (internal) */ struct connection_info_struct { struct _u_instance * u_instance; struct MHD_PostProcessor * post_processor; int has_post_processor; int callback_first_iteration; struct _u_request * request; size_t max_post_param_size; struct _u_map map_url_initial; }; /********************************** * Instance functions declarations **********************************/ /** * free data allocated by ulfius functions */ void u_free(void * data); /** * ulfius_init_instance * * Initialize a struct _u_instance * with default values * return U_OK on success */ int ulfius_init_instance(struct _u_instance * u_instance, unsigned int port, struct sockaddr_in * bind_address, const char * default_auth_realm); /** * ulfius_clean_instance * * Clean memory allocated by a struct _u_instance * */ void ulfius_clean_instance(struct _u_instance * u_instance); /** * ulfius_start_framework * Initializes the framework and run the webservice based on the parameters given * * u_instance: pointer to a struct _u_instance that describe its port and bind address * return U_OK on success */ int ulfius_start_framework(struct _u_instance * u_instance); /** * ulfius_start_secure_framework * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection * * u_instance: pointer to a struct _u_instance that describe its port and bind address * key_pem: private key for the server * cert_pem: server certificate * return U_OK on success */ int ulfius_start_secure_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem); #ifndef U_DISABLE_WEBSOCKET /** * ulfius_start_secure_ca_trust_framework * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection * And using a root server to authenticate client connections * * u_instance: pointer to a struct _u_instance that describe its port and bind address * key_pem: private key for the server * cert_pem: server certificate * root_ca_pem: client root CA you're willing to trust for this instance * return U_OK on success */ int ulfius_start_secure_ca_trust_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem, const char * root_ca_pem); #endif /** * ulfius_stop_framework * * Stop the webservice * u_instance: pointer to a struct _u_instance that describe its port and bind address * return U_OK on success */ int ulfius_stop_framework(struct _u_instance * u_instance); /** * ulfius_set_upload_file_callback_function * * Set the callback function to handle file upload * Used to facilitate large files upload management * The callback function file_upload_callback will be called * multiple times, with the uploaded file in striped in parts * * Warning: If this function is used, all the uploaded files * for the instance will be managed via this function, and they * will no longer be available in the struct _u_request in the * ulfius callback function afterwards. * * Thanks to Thad Phetteplace for the help on this feature * * u_instance: pointer to a struct _u_instance that describe its port and bind address * file_upload_callback: Pointer to a callback function that will handle all file uploads * cls: a pointer that will be passed to file_upload_callback each tim it's called */ int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance, int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls), void * cls); /*********************************** * Endpoints functions declarations ***********************************/ /** * Add a struct _u_endpoint * to the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint: pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list * return U_OK on success */ int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint); /** * Add a struct _u_endpoint * to the specified u_instance with its values specified * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * http_method: http verb (GET, POST, PUT, etc.) in upper case * url_prefix: prefix for the url (optional) * url_format: string used to define the endpoint format * separate words with / * to define a variable in the url, prefix it with @ or : * example: /test/resource/:name/elements * on an url_format that ends with '*', the rest of the url will not be tested * priority: endpoint priority in descending order (0 is the higher priority) * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in callback_function * return U_OK on success */ int ulfius_add_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format, unsigned int priority, int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) struct _u_response * response, // Output parameters (set by the user) void * user_data), void * user_data); /** * Add a struct _u_endpoint * list to the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint_list: pointer to an array of struct _u_endpoint ending with a ulfius_empty_endpoint() that will be copied in the u_instance endpoint_list * return U_OK on success */ int ulfius_add_endpoint_list(struct _u_instance * u_instance, const struct _u_endpoint ** u_endpoint_list); /** * Remove a struct _u_endpoint * from the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint: pointer to a struct _u_endpoint that will be removed in the u_instance endpoint_list * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match * If no endpoint is found, return U_ERROR_NOT_FOUND * return U_OK on success */ int ulfius_remove_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint); /** * ulfius_set_default_endpoint * Set the default endpoint * This endpoint will be called if no endpoint match the url called * u_instance: pointer to a struct _u_instance that describe its port and bind address * auth_function: a pointer to a function that will be executed prior to the callback for authentication * you must declare the function as described. * auth_data: a pointer to a data or a structure that will be available in auth_function * auth_realm: realm value for authentication * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in callback_function * to remove a default endpoint, call ulfius_set_default_endpoint with NULL parameter for callback_function * return U_OK on success */ int ulfius_set_default_endpoint(struct _u_instance * u_instance, int (* callback_function)(const struct _u_request * request, struct _u_response * response, void * user_data), void * user_data); /** * Remove a struct _u_endpoint * from the specified u_instance * using the specified values used to identify an endpoint * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * http_method: http_method used by the endpoint * url_prefix: url_prefix used by the endpoint * url_format: url_format used by the endpoint * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match * If no endpoint is found, return U_ERROR_NOT_FOUND * return U_OK on success */ int ulfius_remove_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format); /** * ulfius_empty_endpoint * return an empty endpoint that goes at the end of an endpoint list */ const struct _u_endpoint * ulfius_empty_endpoint(); /** * ulfius_copy_endpoint * return a copy of an endpoint with duplicate values */ int ulfius_copy_endpoint(struct _u_endpoint * dest, const struct _u_endpoint * source); /** * u_copy_endpoint_list * return a copy of an endpoint list with duplicate values * returned value must be free'd after use */ struct _u_endpoint * ulfius_duplicate_endpoint_list(const struct _u_endpoint * endpoint_list); /** * ulfius_clean_endpoint * free allocated memory by an endpoint */ void ulfius_clean_endpoint(struct _u_endpoint * endpoint); /** * ulfius_clean_endpoint_list * free allocated memory by an endpoint list */ void ulfius_clean_endpoint_list(struct _u_endpoint * endpoint_list); /** * ulfius_equals_endpoints * Compare 2 endpoints and return true if their method, prefix and format are the same or if both are NULL */ int ulfius_equals_endpoints(const struct _u_endpoint * endpoint1, const struct _u_endpoint * endpoint2); #ifndef U_DISABLE_CURL /******************************************** * Requests/Responses functions declarations ********************************************/ /** * ulfius_send_http_request * Send a HTTP request and store the result into a _u_response * return U_OK on success */ int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response); /** * ulfius_send_http_streaming_request * Send a HTTP request and store the result into a _u_response * Except for the body which will be available using write_body_function in the write_body_data * return U_OK on success */ int ulfius_send_http_streaming_request(const struct _u_request * request, struct _u_response * response, size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data), void * write_body_data); /** * ulfius_send_smtp_email * Send an email using libcurl * email is plain/text and UTF8 charset * host: smtp server host name * port: tcp port number (optional, 0 for default) * use_tls: true if the connection is tls secured * verify_certificate: true if you want to disable the certificate verification on a tls server * user: connection user name (optional, NULL: no user name) * password: connection password (optional, NULL: no password) * from: from address (mandatory) * to: to recipient address (mandatory) * cc: cc recipient address (optional, NULL: no cc) * bcc: bcc recipient address (optional, NULL: no bcc) * subject: email subject (mandatory) * mail_body: email body (mandatory) * return U_OK on success */ int ulfius_send_smtp_email(const char * host, const int port, const int use_tls, const int verify_certificate, const char * user, const char * password, const char * from, const char * to, const char * cc, const char * bcc, const char * subject, const char * mail_body); #endif /** * ulfius_add_cookie_to_response * add a cookie to the cookie map * return U_OK on success */ int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age, const char * domain, const char * path, const int secure, const int http_only); /** * ulfius_add_same_site_cookie_to_response * add a cookie to the cookie map with a SameSite attribute * the same_site parameter must have one of the following values: * - U_COOKIE_SAME_SITE_NONE - No SameSite attribute * - U_COOKIE_SAME_SITE_STRICT - SameSite attribute set to 'Strict' * - U_COOKIE_SAME_SITE_LAX - SameSite attribute set to 'Lax' * return U_OK on success */ int ulfius_add_same_site_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age, const char * domain, const char * path, const int secure, const int http_only, const int same_site); /** * ulfius_add_header_to_response * add a header to the response * return U_OK on success */ int ulfius_add_header_to_response(struct _u_response * response, const char * key, const char * value); /** * ulfius_set_string_body_response * Add a string body to a response * body must end with a '\0' character * return U_OK on success */ int ulfius_set_string_body_response(struct _u_response * response, const unsigned int status, const char * body); /** * ulfius_set_binary_body_response * Add a binary body to a response * return U_OK on success */ int ulfius_set_binary_body_response(struct _u_response * response, const unsigned int status, const char * body, const size_t length); /** * ulfius_set_empty_body_response * Set an empty response with only a status * return U_OK on success */ int ulfius_set_empty_body_response(struct _u_response * response, const unsigned int status); /** * ulfius_set_stream_response * Set an stream response with a status * return U_OK on success */ int ulfius_set_stream_response(struct _u_response * response, const unsigned int status, ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max), void (* stream_callback_free) (void * stream_user_data), uint64_t stream_size, size_t stream_block_size, void * stream_user_data); /** * ulfius_init_request * Initialize a request structure by allocating inner elements * return U_OK on success */ int ulfius_init_request(struct _u_request * request); /** * ulfius_clean_request * clean the specified request's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_request_full * return U_OK on success */ int ulfius_clean_request(struct _u_request * request); /** * ulfius_clean_request_full * clean the specified request and all its elements * return U_OK on success */ int ulfius_clean_request_full(struct _u_request * request); /** * ulfius_copy_request * Copy the source request elements into the dest request * return U_OK on success */ int ulfius_copy_request(struct _u_request * dest, const struct _u_request * source); /** * ulfius_init_response * Initialize a response structure by allocating inner elements * return U_OK on success */ int ulfius_init_response(struct _u_response * response); /** * ulfius_clean_response * clean the specified response's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_response_full * return U_OK on success */ int ulfius_clean_response(struct _u_response * response); /** * ulfius_clean_response_full * clean the specified response and all its elements * return U_OK on success */ int ulfius_clean_response_full(struct _u_response * response); /** * ulfius_copy_response * Copy the source response elements into the dest response * return U_OK on success */ int ulfius_copy_response(struct _u_response * dest, const struct _u_response * source); /** * ulfius_clean_cookie * clean the cookie's elements * return U_OK on success */ int ulfius_clean_cookie(struct _u_cookie * cookie); /** * Copy the cookie source elements into dest elements * return U_OK on success */ int ulfius_copy_cookie(struct _u_cookie * dest, const struct _u_cookie * source); /** * create a new request based on the source elements * returned value must be cleaned after use */ struct _u_request * ulfius_duplicate_request(const struct _u_request * request); /** * create a new response based on the source elements * return value must be cleaned after use */ struct _u_response * ulfius_duplicate_response(const struct _u_response * response); #ifndef U_DISABLE_JANSSON /** * ulfius_get_json_body_request * Get JSON structure from the request body if the request is valid * In case of an error in getting or parsing JSON data in the request, * the structure json_error_t * json_error will be filled with an error * message if json_error is not NULL */ json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error); /** * ulfius_set_json_body_request * Add a json_t j_body to a request * return U_OK on success */ int ulfius_set_json_body_request(struct _u_request * request, json_t * j_body); /** * ulfius_get_json_body_response * Get JSON structure from the response body if the request is valid * In case of an error in getting or parsing JSON data in the request, * the structure json_error_t * json_error will be filled with an error * message if json_error is not NULL */ json_t * ulfius_get_json_body_response(struct _u_response * response, json_error_t * json_error); /** * ulfius_set_json_body_response * Add a json_t j_body to a response * return U_OK on success */ int ulfius_set_json_body_response(struct _u_response * response, const unsigned int status, const json_t * j_body); #endif /************************************************************************ * _u_map declarations * * _u_map is a simple map structure that handles sets of key/value maps * ************************************************************************/ /** * initialize a struct _u_map * this function MUST be called after a declaration or allocation * return U_OK on success */ int u_map_init(struct _u_map * map); /** * free the struct _u_map's inner components * return U_OK on success */ int u_map_clean(struct _u_map * u_map); /** * free the struct _u_map and its components * return U_OK on success */ int u_map_clean_full(struct _u_map * u_map); /** * free an enum return by functions u_map_enum_keys or u_map_enum_values * return U_OK on success */ int u_map_clean_enum(char ** array); /** * returns an array containing all the keys in the struct _u_map * return an array of char * ending with a NULL element */ const char ** u_map_enum_keys(const struct _u_map * u_map); /** * returns an array containing all the values in the struct _u_map * return an array of char * ending with a NULL element */ const char ** u_map_enum_values(const struct _u_map * u_map); /** * return true if the sprcified u_map contains the specified key * false otherwise * search is case sensitive */ int u_map_has_key(const struct _u_map * u_map, const char * key); /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case sensitive */ int u_map_has_value(const struct _u_map * u_map, const char * value); /** * return true if the sprcified u_map contains the specified value up until the specified length * false otherwise * search is case sensitive */ int u_map_has_value_binary(const struct _u_map * u_map, const char * value, size_t length); /** * return true if the sprcified u_map contains the specified key * false otherwise * search is case insensitive */ int u_map_has_key_case(const struct _u_map * u_map, const char * key); /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case insensitive */ int u_map_has_value_case(const struct _u_map * u_map, const char * value); /** * add the specified key/value pair into the specified u_map * if the u_map already contains a pair with the same key, replace the value * return U_OK on success */ int u_map_put(struct _u_map * u_map, const char * key, const char * value); /** * add the specified key/binary value pair into the specified u_map * if the u_map already contains a pair with the same key, * replace the value at the specified offset with the specified length * return U_OK on success */ int u_map_put_binary(struct _u_map * u_map, const char * key, const char * value, uint64_t offset, size_t length); /** * get the value length corresponding to the specified key in the u_map * return -1 if no match found * search is case sensitive */ ssize_t u_map_get_length(const struct _u_map * u_map, const char * key); /** * get the value length corresponding to the specified key in the u_map * return -1 if no match found * search is case insensitive */ ssize_t u_map_get_case_length(const struct _u_map * u_map, const char * key); /** * get the value corresponding to the specified key in the u_map * return NULL if no match found * search is case sensitive */ const char * u_map_get(const struct _u_map * u_map, const char * key); /** * get the value corresponding to the specified key in the u_map * return NULL if no match found * search is case insensitive */ const char * u_map_get_case(const struct _u_map * u_map, const char * key); /** * remove an pair key/value that has the specified key * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_key(struct _u_map * u_map, const char * key); /** * remove all pairs key/value that has the specified key (case insensitive search) * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_key_case(struct _u_map * u_map, const char * key); /** * remove all pairs key/value that has the specified value * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value(struct _u_map * u_map, const char * value); /** * remove all pairs key/value that has the specified value (case insensitive search) * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value_case(struct _u_map * u_map, const char * value); /** * remove all pairs key/value that has the specified value up until the specified length * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value_binary(struct _u_map * u_map, const char * key, size_t length); /** * remove the pair key/value at the specified index * return U_OK on success, U_NOT_FOUND if index is out of bound, error otherwise */ int u_map_remove_at(struct _u_map * u_map, const int index); /** * Create an exact copy of the specified struct _u_map * return a reference to the copy, NULL otherwise * returned value must be free'd after use */ struct _u_map * u_map_copy(const struct _u_map * source); /** * Copy all key/values pairs of source into dest * If key is already present in dest, it's overwritten * return U_OK on success, error otherwise */ int u_map_copy_into(struct _u_map * dest, const struct _u_map * source); /** * Return the number of key/values pair in the specified struct _u_map * Return -1 on error */ int u_map_count(const struct _u_map * source); /** * Empty a struct u_map of all its elements * return U_OK on success, error otherwise */ int u_map_empty(struct _u_map * u_map); #ifndef U_DISABLE_WEBSOCKET /********************************** * Websocket functions declarations **********************************/ #define U_WEBSOCKET_USER_AGENT "Ulfius Websocket Client Framework" #define U_WEBSOCKET_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define U_WEBSOCKET_UPGRADE_VALUE "websocket" #define U_WEBSOCKET_BAD_REQUEST_BODY "Error in websocket handshake, wrong parameters" #define U_WEBSOCKET_USEC_WAIT 50 #define WEBSOCKET_MAX_CLOSE_TRY 10 #define U_WEBSOCKET_BIT_FIN 0x80 #define U_WEBSOCKET_MASK 0x80 #define U_WEBSOCKET_LEN_MASK 0x7F #define U_WEBSOCKET_OPCODE_CONTINUE 0x00 #define U_WEBSOCKET_OPCODE_TEXT 0x01 #define U_WEBSOCKET_OPCODE_BINARY 0x02 #define U_WEBSOCKET_OPCODE_CLOSE 0x08 #define U_WEBSOCKET_OPCODE_PING 0x09 #define U_WEBSOCKET_OPCODE_PONG 0x0A #define U_WEBSOCKET_OPCODE_CLOSED 0xFD #define U_WEBSOCKET_OPCODE_ERROR 0xFE #define U_WEBSOCKET_OPCODE_NONE 0xFF #define U_WEBSOCKET_NONE 0 #define U_WEBSOCKET_SERVER 1 #define U_WEBSOCKET_CLIENT 2 #define U_WEBSOCKET_STATUS_OPEN 0 #define U_WEBSOCKET_STATUS_CLOSE 1 #define U_WEBSOCKET_STATUS_ERROR 2 #define WEBSOCKET_RESPONSE_HTTP 0x0001 #define WEBSOCKET_RESPONSE_UPGRADE 0x0002 #define WEBSOCKET_RESPONSE_CONNECTION 0x0004 #define WEBSOCKET_RESPONSE_ACCEPT 0x0008 #define WEBSOCKET_RESPONSE_PROTCOL 0x0010 #define WEBSOCKET_RESPONSE_EXTENSION 0x0020 /** * Websocket manager structure * contains among other things the socket * the status (open, closed), and the list of incoming and outcoming messages * Used on public callback functions */ struct _websocket_manager { struct _websocket_message_list * message_list_incoming; struct _websocket_message_list * message_list_outcoming; int connected; int close_flag; MHD_socket mhd_sock; int tcp_sock; int tls; gnutls_session_t gnutls_session; gnutls_certificate_credentials_t xcred; char * protocol; char * extensions; pthread_mutex_t read_lock; pthread_mutex_t write_lock; pthread_mutex_t status_lock; pthread_cond_t status_cond; struct pollfd fds; int type; }; /** * websocket message structure * contains all the data of a websocket message * and the timestamp of when it was sent of received */ struct _websocket_message { time_t datestamp; uint8_t opcode; uint8_t has_mask; uint8_t mask[4]; size_t data_len; char * data; }; /** * List of websocket messages */ struct _websocket_message_list { struct _websocket_message ** list; size_t len; }; /** * websocket structure * contains all the data of the websocket */ struct _websocket { struct _u_instance * instance; struct _u_request * request; void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data); void * websocket_manager_user_data; void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data); void * websocket_incoming_user_data; void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data); void * websocket_onclose_user_data; struct _websocket_manager * websocket_manager; struct MHD_UpgradeResponseHandle * urh; }; /** * Handler for the websocket client, to allow the program to know the status of a websocket client */ struct _websocket_client_handler { struct _websocket * websocket; struct _u_response * response; }; /********************************/ /** Common websocket functions **/ /********************************/ /** * Send a message in the websocket * Return U_OK on success */ int ulfius_websocket_send_message(struct _websocket_manager * websocket_manager, const uint8_t opcode, const uint64_t data_len, const char * data); /** * Send a fragmented message in the websocket * each fragment size will be at most fragment_len * Return U_OK on success */ int ulfius_websocket_send_fragmented_message(struct _websocket_manager * websocket_manager, const uint8_t opcode, const uint64_t data_len, const char * data, const uint64_t fragment_len); /** * Return the first message of the message list * Return NULL if message_list has no message * Returned value must be cleared after use * Use it with struct _websocket_manager->message_list_incoming * or struct _websocket_manager->message_list_outcoming */ struct _websocket_message * ulfius_websocket_pop_first_message(struct _websocket_message_list * message_list); /** * Clear data of a websocket message */ void ulfius_clear_websocket_message(struct _websocket_message * message); /********************************/ /** Server websocket functions **/ /********************************/ /** * Set a websocket in the response * You must set at least websocket_manager_callback or websocket_incoming_message_callback * @Parameters * response: struct _u_response to send back the websocket initialization, mandatory * websocket_protocol: list of protocols, separated by a comma, or NULL if all protocols are accepted * websocket_extensions: list of extensions, separated by a comma, or NULL if all extensions are accepted * websocket_manager_callback: callback function called right after the handshake acceptance, optional * websocket_manager_user_data: any data that will be given to the websocket_manager_callback, optional * websocket_incoming_message_callback: callback function called on each incoming complete message, optional * websocket_incoming_user_data: any data that will be given to the websocket_incoming_message_callback, optional * websocket_onclose_callback: callback function called right before closing the websocket, must be complete for the websocket to close * websocket_onclose_user_data: any data that will be given to the websocket_onclose_callback, optional * @Return value: U_OK on success */ int ulfius_set_websocket_response(struct _u_response * response, const char * websocket_protocol, const char * websocket_extensions, void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data), void * websocket_manager_user_data, void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data), void * websocket_incoming_user_data, void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data), void * websocket_onclose_user_data); /** * Sets the websocket in closing mode * The websocket will not necessarily be closed at the return of this function, * it will process through the end of the `websocket_manager_callback` * and the `websocket_onclose_callback` calls first. * return U_OK on success * or U_ERROR on error */ int ulfius_websocket_send_close_signal(struct _websocket_manager * websocket_manager); /** * Returns the status of the websocket connection * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_status(struct _websocket_manager * websocket_manager); /** * Wait until the websocket connection is closed or the timeout in milliseconds is reached * if timeout is 0, no timeout is set * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_wait_close(struct _websocket_manager * websocket_manager, unsigned int timeout); /********************************/ /** Client websocket functions **/ /********************************/ /** * Open a websocket client connection * Return U_OK on success */ int ulfius_open_websocket_client_connection(struct _u_request * request, void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data), void * websocket_manager_user_data, void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data), void * websocket_incoming_user_data, void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data), void * websocket_onclose_user_data, struct _websocket_client_handler * websocket_client_handler, struct _u_response * response); /** * Send a close signal to the websocket * return U_OK when the signal is sent * or U_ERROR on error */ int ulfius_websocket_client_connection_send_close_signal(struct _websocket_client_handler * websocket_client_handler); /** * Closes a websocket client connection * return U_OK when the websocket is closed * or U_ERROR on error */ int ulfius_websocket_client_connection_close(struct _websocket_client_handler * websocket_client_handler); /** * Returns the status of the websocket client connection * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_client_connection_status(struct _websocket_client_handler * websocket_client_handler); /** * Wait until the websocket client connection is closed or the timeout in milliseconds is reached * if timeout is 0, no timeout is set * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_client_connection_wait_close(struct _websocket_client_handler * websocket_client_handler, unsigned int timeout); /** * Set values for a struct _u_request to open a websocket * request must be previously initialized * Return U_OK on success */ int ulfius_set_websocket_request(struct _u_request * request, const char * url, const char * websocket_protocol, const char * websocket_extensions); #endif /** Macro values **/ #define ULFIUS_URL_SEPARATOR "/" #define ULFIUS_HTTP_ENCODING_JSON "application/json" #define ULFIUS_HTTP_HEADER_CONTENT "Content-Type" #define ULFIUS_HTTP_NOT_FOUND_BODY "Resource not found" #define ULFIUS_HTTP_ERROR_BODY "Server Error" #define ULFIUS_COOKIE_ATTRIBUTE_EXPIRES "Expires" #define ULFIUS_COOKIE_ATTRIBUTE_MAX_AGE "Max-Age" #define ULFIUS_COOKIE_ATTRIBUTE_DOMAIN "Domain" #define ULFIUS_COOKIE_ATTRIBUTE_PATH "Path" #define ULFIUS_COOKIE_ATTRIBUTE_SECURE "Secure" #define ULFIUS_COOKIE_ATTRIBUTE_HTTPONLY "HttpOnly" #define ULFIUS_POSTBUFFERSIZE 1024 #define U_STATUS_STOP 0 #define U_STATUS_RUNNING 1 #define U_STATUS_ERROR 2 #ifndef U_DISABLE_WEBSOCKET /** * websocket_manager_callback: callback function for working with the websocket * websocket_manager_user_data: user-defined data that will be handled to websocket_manager_callback * websocket_incoming_message_callback: callback function that will be called every time a message arrives from the client in the websocket * websocket_incoming_user_data: user-defined data that will be handled to websocket_incoming_message_callback * websocket_onclose_callback: callback function that will be called if the websocket is open while the program calls ulfius_stop_framework * websocket_onclose_user_data: user-defined data that will be handled to websocket_onclose_callback */ struct _websocket_handle { char * websocket_protocol; char * websocket_extensions; void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data); void * websocket_manager_user_data; void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data); void * websocket_incoming_user_data; void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data); void * websocket_onclose_user_data; }; struct _websocket_handler { size_t nb_websocket_active; struct _websocket ** websocket_active; pthread_mutex_t websocket_close_lock; pthread_cond_t websocket_close_cond; int pthread_init; }; /* * ulfius_export_client_certificate_pem * Exports the client certificate using PEM format * request: struct _u_request used * returned value must be u_free'd after use */ char * ulfius_export_client_certificate_pem(const struct _u_request * request); /* * ulfius_import_client_certificate_pem * Imports the client certificate using PEM format * request: struct _u_request used * str_cert: client certificate in PEM format * return U_OK on success; */ int ulfius_import_client_certificate_pem(struct _u_request * request, const char * str_cert); #endif // U_DISABLE_WEBSOCKET #endif // __ULFIUS_H__ ulfius-2.5.2/include/yuarel.h000066400000000000000000000101011341017022300161060ustar00rootroot00000000000000/** * Copyright (C) 2016 Jack Engqvist Johansson * * 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. */ #ifndef INC_YUAREL_H #define INC_YUAREL_H #ifdef __cplusplus extern "C" { #endif /** * The struct where the parsed values will be stored: * * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] * * Note: to make sure that no strings are copied, the first slash "/" in the * path will be used to null terminate the hostname if no port is supplied. */ struct yuarel { char *scheme; /* scheme, without ":" and "//" */ char *username; /* username, default: NULL */ char *password; /* password, default: NULL */ char *host; /* hostname or IP address */ int port; /* port, default: 0 */ char *path; /* path, without leading "/", default: NULL */ char *query; /* query, default: NULL */ char *fragment; /* fragment, default: NULL */ }; /* A struct to hold the query string parameter values. */ struct yuarel_param { char *key; char *val; }; /** * Parse a URL to a struct. * * The URL string should be in one of the following formats: * * Absolute URL: * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] [ "#" fragment ] * * Relative URL: * path [ "?" query ] [ "#" fragment ] * * The following parts will be parsed to the corresponding struct member. * * *url: a pointer to the struct where to store the parsed values. * *url_str: a pointer to the url to be parsed (null terminated). * * Returns 0 on success, otherwise -1. */ extern int yuarel_parse(struct yuarel *url, char *url_str); /** * Split a path into several strings. * * No data is copied, the slashed are used as null terminators and then * pointers to each path part will be stored in **parts. Double slashes will be * treated as one. * * *path: the path to split. * **parts: a pointer to an array of (char *) where to store the result. * max_parts: max number of parts to parse. * * Returns the number of parsed items. -1 on error. */ extern int yuarel_split_path(char *path, char **parts, int max_parts); /** * Parse a query string into a key/value struct. * * The query string should be a null terminated string of parameters separated by * a delimiter. Each parameter are checked for the equal sign character. If it * appears in the parameter, it will be used as a null terminator and the part * that comes after it will be the value of the parameter. * * No data are copied, the equal sign and delimiters are used as null * terminators and then pointers to each parameter key and value will be stored * in the yuarel_param struct. * * *query: the query string to parse. * delimiter: the character that separates the key/value pairs from eachother. * *params: an array of (struct yuarel_param) where to store the result. * max_values: max number of parameters to parse. * * Returns the number of parsed items. -1 on error. */ extern int yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params); #ifdef __cplusplus } #endif #endif /* INC_YUAREL_H */ ulfius-2.5.2/libulfius.pc.in000066400000000000000000000006031341017022300157460ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: @PROJECT_NAME@ Description: @PROJECT_DESCRIPTION@ URL: @PROJECT_BUGREPORT_PATH@ Version: @LIBRARY_VERSION@ Requires: @PKGCONF_REQ@ Requires.private: @PKGCONF_REQ_PRIVATE@ Libs: -L${libdir} -lulfius Cflags: -I${includedir} ulfius-2.5.2/src/000077500000000000000000000000001341017022300136075ustar00rootroot00000000000000ulfius-2.5.2/src/Makefile000066400000000000000000000121521341017022300152500ustar00rootroot00000000000000# # Ulfius Framework # # Makefile used to build the software # # Copyright 2015-2018 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this library. If not, see . # ULFIUS_INCLUDE=../include CONFIG_TEMPLATE=$(ULFIUS_INCLUDE)/ulfius-cfg.h.in CONFIG_FILE=$(ULFIUS_INCLUDE)/ulfius-cfg.h DESTDIR=/usr/local CC=gcc CFLAGS+=-c -pedantic -std=gnu99 -fPIC -Wall -D_REENTRANT -I$(ULFIUS_INCLUDE) $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-L$(DESTDIR)/lib -lc -lmicrohttpd -lorcania -lpthread $(LDFLAGS) OBJECTS=ulfius.o u_map.o u_request.o u_response.o u_send_request.o u_websocket.o yuarel.o OUTPUT=libulfius.so VERSION=2.5.2 ifndef JANSSONFLAG DISABLE_JANSSON=0 LJANSSON=-ljansson else DISABLE_JANSSON=1 endif ifndef CURLFLAG DISABLE_CURL=0 LCURL=-lcurl else DISABLE_CURL=1 endif ifndef WEBSOCKETFLAG DISABLE_WEBSOCKET=0 LWEBSOCKET=-lgnutls else DISABLE_WEBSOCKET=1 endif ifndef YDERFLAG DISABLE_YDER=0 LYDER=-lyder else DISABLE_YDER=1 endif ifndef FREERTOSFLAG WITH_FREERTOS=0 else WITH_FREERTOS=1 endif ifndef LWIPFLAG WITH_LWIP=0 else CFLAGS+= $(shell pkg-config --cflags lwip) LIBS+= $(shell pkg-config --libs lwip) WITH_LWIP=1 endif ifeq ($(WITH_FREERTOS)$(WITH_LWIP),11) $(error FREERTOSFLAG and LWIPFLAG can't be enabled together) endif .PHONY: all clean all: release $(CONFIG_FILE): @cp $(CONFIG_TEMPLATE) $(CONFIG_FILE) @echo Config file $(CONFIG_FILE) generated @sed -i -e 's/$${PROJECT_VERSION}/$(VERSION)/g' $(CONFIG_FILE) @if [ "$(DISABLE_JANSSON)" = "1" ]; then \ sed -i -e 's/\#cmakedefine U_DISABLE_JANSSON/\#define U_DISABLE_JANSSON/g' $(CONFIG_FILE); \ echo "JANSSON SUPPORT DISABLED"; \ else \ sed -i -e 's/\#cmakedefine U_DISABLE_JANSSON/\/* #undef U_DISABLE_JANSSON *\//g' $(CONFIG_FILE); \ echo "JANSSON SUPPORT ENABLED"; \ fi @if [ "$(DISABLE_CURL)" = "1" ]; then \ sed -i -e 's/\#cmakedefine U_DISABLE_CURL/\#define U_DISABLE_CURL/g' $(CONFIG_FILE); \ echo "CURL SUPPORT DISABLED"; \ else \ sed -i -e 's/\#cmakedefine U_DISABLE_CURL/\/* #undef U_DISABLE_CURL *\//g' $(CONFIG_FILE); \ echo "CURL SUPPORT ENABLED"; \ fi @pkg-config --atleast-version=0.9.53 libmicrohttpd; \ if [ $$? -ne 0 ] || [ "$(DISABLE_WEBSOCKET)" = "1" ]; then \ sed -i -e 's/\#cmakedefine U_DISABLE_WEBSOCKET/\#define U_DISABLE_WEBSOCKET/g' $(CONFIG_FILE); \ echo "WEBSOCKET SUPPORT DISABLED"; \ else \ sed -i -e 's/\#cmakedefine U_DISABLE_WEBSOCKET/\/* #undef U_DISABLE_WEBSOCKET *\//g' $(CONFIG_FILE); \ echo "WEBSOCKET SUPPORT ENABLED"; \ fi @if [ "$(DISABLE_YDER)" = "1" ]; then \ sed -i -e 's/\#cmakedefine U_DISABLE_YDER/\#define U_DISABLE_YDER/g' $(CONFIG_FILE); \ echo "YDER SUPPORT DISABLED"; \ else \ sed -i -e 's/\#cmakedefine U_DISABLE_YDER/\/* #undef U_DISABLE_YDER *\//g' $(CONFIG_FILE); \ echo "YDER SUPPORT ENABLED"; \ fi @if [ "$(WITH_FREERTOS)" = "1" ]; then \ sed -i -e 's/\#cmakedefine U_WITH_FREERTOS/\#define U_WITH_FREERTOS/g' $(CONFIG_FILE); \ echo "FREERTOS SUPPORT ENABLED"; \ else \ sed -i -e 's/\#cmakedefine U_WITH_FREERTOS/\/* #undef U_WITH_FREERTOS *\//g' $(CONFIG_FILE); \ echo "FREERTOS SUPPORT DISABLED"; \ fi @if [ "$(WITH_LWIP)" = "1" ]; then \ sed -i -e 's/\#cmakedefine U_WITH_LWIP/\#define U_WITH_LWIP/g' $(CONFIG_FILE); \ echo "LWIP SUPPORT ENABLED"; \ else \ sed -i -e 's/\#cmakedefine U_WITH_LWIP/\/* #undef U_WITH_LWIP *\//g' $(CONFIG_FILE); \ echo "LWIP SUPPORT DISABLED"; \ fi target: $(OBJECTS) %.o: %.c $(CONFIG_FILE) $(ULFIUS_INCLUDE)/ulfius.h $(ULFIUS_INCLUDE)/u_private.h $(ULFIUS_INCLUDE)/yuarel.h $(CC) $(CFLAGS) $< libulfius.so: $(OBJECTS) $(CC) -shared -fPIC -Wl,-soname,$(OUTPUT) -o $(OUTPUT).$(VERSION) $(OBJECTS) $(LIBS) $(LYDER) $(LJANSSON) $(LCURL) $(LWEBSOCKET) ln -sf $(OUTPUT).$(VERSION) $(OUTPUT) libulfius.a: $(OBJECTS) ar rcs libulfius.a $(OBJECTS) clean: rm -f *.o *.so *.a $(OUTPUT) $(OUTPUT).* $(CONFIG_FILE) install: all install $(OUTPUT).$(VERSION) $(DESTDIR)/lib install -m644 $(ULFIUS_INCLUDE)/ulfius.h $(DESTDIR)/include install -m644 $(CONFIG_FILE) $(DESTDIR)/include -ldconfig static-install: static install libulfius.a $(DESTDIR)/lib install -m644 $(ULFIUS_INCLUDE)/ulfius.h $(DESTDIR)/include install -m644 $(CONFIG_FILE) $(DESTDIR)/include uninstall: rm -f $(DESTDIR)/lib/$(OUTPUT) $(DESTDIR)/lib/libulfius.a rm -f $(DESTDIR)/lib/$(OUTPUT).* rm -f $(DESTDIR)/include/ulfius.h rm -f $(DESTDIR)/include/ulfius-cfg.h debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: libulfius.so release: ADDITIONALFLAGS=-O3 release: libulfius.so static: ADDITIONALFLAGS=-O3 static: libulfius.a static-debug: ADDITIONALFLAGS=-DDEBUG -g -O0 static-debug: libulfius.a ulfius-2.5.2/src/u_map.c000066400000000000000000000412261341017022300150610ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_umap.c: Simple map structure functions definitions * not memory friendly, all pointer returned must be freed after use * * Copyright 2015-2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #include #include #include "u_private.h" #include "ulfius.h" /** * initialize a struct _u_map * this function MUST be called after a declaration or allocation * return U_OK on success */ int u_map_init(struct _u_map * map) { if (map != NULL) { map->nb_values = 0; map->keys = o_malloc(sizeof(char *)); if (map->keys == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for map->keys"); return U_ERROR_MEMORY; } map->keys[0] = NULL; map->values = o_malloc(sizeof(char *)); if (map->values == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for map->values"); o_free(map->keys); return U_ERROR_MEMORY; } map->values[0] = NULL; map->lengths = o_malloc(sizeof(size_t)); if (map->lengths == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for map->lengths"); o_free(map->keys); o_free(map->values); return U_ERROR_MEMORY; } map->lengths[0] = 0; return U_OK; } else { return U_ERROR_PARAMS; } } /** * free the struct _u_map's inner components * return U_OK on success */ int u_map_clean(struct _u_map * u_map) { if (u_map != NULL) { u_map_clean_enum(u_map->keys); u_map_clean_enum(u_map->values); o_free(u_map->lengths); return U_OK; } else { return U_ERROR_PARAMS; } } /** * free the struct _u_map and its components * return U_OK on success */ int u_map_clean_full(struct _u_map * u_map) { if (u_map_clean(u_map) == U_OK) { o_free(u_map); return U_OK; } else { return U_ERROR_PARAMS; } } /** * free an enum return by functions u_map_enum_keys or u_map_enum_values * return U_OK on success */ int u_map_clean_enum(char ** array) { int i; if (array != NULL) { for (i=0; array[i] != NULL; i++) { o_free(array[i]); array[i] = NULL; } o_free(array); return U_OK; } else { return U_ERROR_PARAMS; } } /** * returns an array containing all the keys in the struct _u_map * return an array of char * ending with a NULL element */ const char ** u_map_enum_keys(const struct _u_map * u_map) { return (const char **)u_map->keys; } /** * returns an array containing all the values in the struct _u_map * return an array of char * ending with a NULL element */ const char ** u_map_enum_values(const struct _u_map * u_map) { return (const char **)u_map->values; } /** * return true if the sprcified u_map contains the specified key * false otherwise * search is case sensitive */ int u_map_has_key(const struct _u_map * u_map, const char * key) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { return 1; } } } return 0; } /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case sensitive */ int u_map_has_value(const struct _u_map * u_map, const char * value) { return u_map_has_value_binary(u_map, value, o_strlen(value)); } /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case sensitive */ int u_map_has_value_binary(const struct _u_map * u_map, const char * value, size_t length) { int i; if (u_map != NULL && value != NULL) { for (i=0; u_map->values[i] != NULL; i++) { if (0 == memcmp(u_map->values[i], value, length)) { return 1; } } } return 0; } /** * add the specified key/value pair into the specified u_map * if the u_map already contains a pair with the same key, replace the value * return U_OK on success */ int u_map_put(struct _u_map * u_map, const char * key, const char * value) { if (value != NULL) { return u_map_put_binary(u_map, key, value, 0, o_strlen(value)+1); } else { return u_map_put_binary(u_map, key, NULL, 0, 0); } } /** * add the specified key/binary value pair into the specified u_map * if the u_map already contains a pair with the same key, * replace the value at the specified offset with the specified length * return U_OK on success */ int u_map_put_binary(struct _u_map * u_map, const char * key, const char * value, uint64_t offset, size_t length) { int i; char * dup_key, * dup_value; if (u_map != NULL && key != NULL && o_strlen(key) > 0) { for (i=0; i < u_map->nb_values; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { // Key already exist, extend and/or replace value if (u_map->lengths[i] < (offset + length)) { u_map->values[i] = o_realloc(u_map->values[i], (offset + length)*sizeof(char)); if (u_map->values[i] == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->values"); return U_ERROR_MEMORY; } } if (value != NULL) { memcpy(u_map->values[i]+offset, value, length); } if (u_map->lengths[i] < (offset + length)) { u_map->lengths[i] = (offset + length); } return U_OK; } } if (u_map->values[i] == NULL) { // Not found, add key/value dup_key = o_strdup(key); if (dup_key == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dup_key"); return U_ERROR_MEMORY; } if (value != NULL) { dup_value = o_malloc((offset + length)*sizeof(char)); if (dup_value == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dup_value"); o_free(dup_key); return U_ERROR_MEMORY; } memcpy((dup_value + offset), value, length); } else { dup_value = NULL; } // Append key for (i = 0; u_map->keys[i] != NULL; i++); u_map->keys = o_realloc(u_map->keys, (i + 2)*sizeof(char *)); if (u_map->keys == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->keys"); o_free(dup_key); o_free(dup_value); return U_ERROR_MEMORY; } u_map->keys[i] = (char *)dup_key; u_map->keys[i+1] = NULL; // Append value u_map->values = o_realloc(u_map->values, (i + 2)*sizeof(char *)); if (u_map->values == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->values"); o_free(dup_key); o_free(dup_value); return U_ERROR_MEMORY; } u_map->values[i] = (char *)dup_value; u_map->values[i+1] = NULL; // Append length u_map->lengths = o_realloc(u_map->lengths, (i + 2)*sizeof(size_t)); if (u_map->lengths == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths"); o_free(dup_key); o_free(dup_value); return U_ERROR_MEMORY; } u_map->lengths[i] = (offset + length); u_map->lengths[i+1] = 0; u_map->nb_values++; } return U_OK; } else { return U_ERROR_PARAMS; } } /** * remove an pair key/value that has the specified key * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_key(struct _u_map * u_map, const char * key) { int i, res, found = 0; if (u_map == NULL || key == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == o_strcmp(u_map->keys[i], key)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * remove all pairs key/value that has the specified key (case insensitive search) * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_key_case(struct _u_map * u_map, const char * key) { int i, res, found = 0; if (u_map == NULL || key == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == o_strcasecmp(u_map->keys[i], key)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * remove all pairs key/value that has the specified value * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value(struct _u_map * u_map, const char * value) { return u_map_remove_from_value_binary(u_map, value, o_strlen(value)); } /** * remove all pairs key/value that has the specified value up until the specified length * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value_binary(struct _u_map * u_map, const char * value, size_t length) { int i, res, found = 0; if (u_map == NULL || value == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == memcmp(u_map->values[i], value, length)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * remove all pairs key/value that has the specified value (case insensitive search) * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise */ int u_map_remove_from_value_case(struct _u_map * u_map, const char * value) { int i, res, found = 0; if (u_map == NULL || value == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == o_strcasecmp(u_map->values[i], value)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * remove the pair key/value at the specified index * return U_OK on success, U_NOT_FOUND if index is out of bound, error otherwise */ int u_map_remove_at(struct _u_map * u_map, const int index) { int i; if (u_map == NULL || index < 0) { return U_ERROR_PARAMS; } else if (index >= u_map->nb_values) { return U_ERROR_NOT_FOUND; } else { o_free(u_map->keys[index]); o_free(u_map->values[index]); for (i = index; i < u_map->nb_values; i++) { u_map->keys[i] = u_map->keys[i + 1]; u_map->values[i] = u_map->values[i + 1]; u_map->lengths[i] = u_map->lengths[i + 1]; } u_map->keys = o_realloc(u_map->keys, (u_map->nb_values)*sizeof(char *)); if (u_map->keys == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->keys"); return U_ERROR_MEMORY; } u_map->values = o_realloc(u_map->values, (u_map->nb_values)*sizeof(char *)); if (u_map->values == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->values"); return U_ERROR_MEMORY; } u_map->lengths = o_realloc(u_map->lengths, (u_map->nb_values)*sizeof(char *)); if (u_map->lengths == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths"); return U_ERROR_MEMORY; } u_map->nb_values--; return U_OK; } } /** * get the value corresponding to the specified key in the u_map * return NULL if no match found * search is case sensitive */ const char * u_map_get(const struct _u_map * u_map, const char * key) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { if (u_map->lengths[i] > 0) { return u_map->values[i]; } else { return NULL; } } } return NULL; } else { return NULL; } } /** * return true if the sprcified u_map contains the specified key * false otherwise * search is case insensitive */ int u_map_has_key_case(const struct _u_map * u_map, const char * key) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->keys[i], key)) { return 1; } } } return 0; } /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case insensitive */ int u_map_has_value_case(const struct _u_map * u_map, const char * value) { int i; if (u_map != NULL && value != NULL) { for (i=0; u_map->values[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->values[i], value)) { return 1; } } } return 0; } /** * get the value corresponding to the specified key in the u_map * return NULL if no match found * search is case insensitive */ const char * u_map_get_case(const struct _u_map * u_map, const char * key) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->keys[i], key)) { return u_map->values[i]; } } return NULL; } else { return NULL; } } /** * get the value length corresponding to the specified key in the u_map * return -1 if no match found * search is case sensitive */ ssize_t u_map_get_length(const struct _u_map * u_map, const char * key) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { return u_map->lengths[i]; } } return -1; } else { return -1; } } /** * get the value length corresponding to the specified key in the u_map * return -1 if no match found * search is case insensitive */ ssize_t u_map_get_case_length(const struct _u_map * u_map, const char * key) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->keys[i], key)) { return u_map->lengths[i]; } } return -1; } else { return -1; } } /** * Create an exact copy of the specified struct _u_map * return a reference to the copy, NULL otherwise * returned value must be cleaned after use */ struct _u_map * u_map_copy(const struct _u_map * source) { struct _u_map * copy = NULL; const char ** keys, * value; int i; if (source != NULL) { copy = o_malloc(sizeof(struct _u_map)); if (copy == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map_copy.copy"); return NULL; } if (u_map_init(copy) != U_OK) { o_free(copy); return NULL; } keys = u_map_enum_keys(source); for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(source, keys[i]); if (value == NULL || u_map_put_binary(copy, keys[i], value, 0, source->lengths[i]) != U_OK) { return NULL; } } } return copy; } /** * Copy all key/values pairs of source into dest * If key is already present in dest, it's overwritten * return U_OK on success, error otherwise */ int u_map_copy_into(struct _u_map * dest, const struct _u_map * source) { const char ** keys; int i, res; if (source != NULL && dest != NULL) { keys = u_map_enum_keys(source); for (i=0; keys != NULL && keys[i] != NULL; i++) { res = u_map_put(dest, keys[i], u_map_get(source, keys[i])); if (res != U_OK) { return res; } } return U_OK; } else { return U_ERROR_PARAMS; } } /** * Return the number of key/values pair in the specified struct _u_map * Return -1 on error */ int u_map_count(const struct _u_map * source) { if (source != NULL) { if (source->nb_values >= 0) { return source->nb_values; } } return -1; } /** * Empty a struct u_map of all its elements * return U_OK on success, error otherwise */ int u_map_empty(struct _u_map * u_map) { int ret = u_map_clean(u_map); if (ret == U_OK) { return u_map_init(u_map); } else { return ret; } } ulfius-2.5.2/src/u_request.c000066400000000000000000000607371341017022300160040ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_request.c: request related functions defintions * * Copyright 2015-2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #include #include #include "u_private.h" #include "ulfius.h" /** * Splits the url to an array of char * */ static char ** ulfius_split_url(const char * prefix, const char * url) { char * saveptr = NULL, * cur_word = NULL, ** to_return = o_malloc(sizeof(char*)), * url_cpy = NULL, * url_cpy_addr = NULL; int counter = 1; if (to_return != NULL) { to_return[0] = NULL; if (prefix != NULL) { url_cpy = url_cpy_addr = o_strdup(prefix); cur_word = strtok_r( url_cpy, ULFIUS_URL_SEPARATOR, &saveptr ); while (cur_word != NULL) { to_return = o_realloc(to_return, (counter+1)*sizeof(char*)); if (to_return == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return"); break; } to_return[counter-1] = o_strdup(cur_word); if (to_return[counter-1] == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return[counter-1]"); break; } to_return[counter] = NULL; counter++; cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); } o_free(url_cpy_addr); url_cpy_addr = NULL; } if (url != NULL) { url_cpy = url_cpy_addr = o_strdup(url); cur_word = strtok_r( url_cpy, ULFIUS_URL_SEPARATOR, &saveptr ); while (cur_word != NULL) { if (0 != o_strcmp("", cur_word) && cur_word[0] != '?') { to_return = o_realloc(to_return, (counter+1)*sizeof(char*)); if (to_return == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return"); break; } to_return[counter-1] = o_strdup(cur_word); if (to_return[counter-1] == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return[counter-1]"); break; } to_return[counter] = NULL; counter++; } cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); } o_free(url_cpy_addr); url_cpy_addr = NULL; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return"); } return to_return; } /** * Compare two endoints by their priorities * Used by qsort to compare endpoints */ static int compare_endpoint_priorities(const void * a, const void * b) { struct _u_endpoint * e1 = *(struct _u_endpoint **)a, * e2 = *(struct _u_endpoint **)b; if (e1->priority < e2->priority) { return -1; } else if (e1->priority > e2->priority) { return 1; } else { return 0; } } /** * ulfius_url_format_match * return true if splitted_url matches splitted_url_format * false otherwise */ static int ulfius_url_format_match(const char ** splitted_url, const char ** splitted_url_format) { int i; for (i=0; splitted_url_format[i] != NULL; i++) { if (splitted_url_format[i][0] == '*' && splitted_url_format[i+1] == NULL) { return 1; } if (splitted_url[i] == NULL || (splitted_url_format[i][0] != '@' && splitted_url_format[i][0] != ':' && 0 != o_strcmp(splitted_url_format[i], splitted_url[i]))) { return 0; } } return (splitted_url[i] == NULL && splitted_url_format[i] == NULL); } /** * ulfius_endpoint_match * return the endpoint array matching the url called with the proper http method * the returned array always has its last value to NULL * return NULL on memory error * returned value must be free'd after use */ struct _u_endpoint ** ulfius_endpoint_match(const char * method, const char * url, struct _u_endpoint * endpoint_list) { char ** splitted_url, ** splitted_url_format; struct _u_endpoint ** endpoint_returned = o_malloc(sizeof(struct _u_endpoint *)); int i; size_t count = 0; if (endpoint_returned == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for endpoint_returned"); } else { endpoint_returned[0] = NULL; if (method != NULL && url != NULL && endpoint_list != NULL) { splitted_url = ulfius_split_url(url, NULL); for (i=0; (splitted_url != NULL && !ulfius_equals_endpoints(&(endpoint_list[i]), ulfius_empty_endpoint())); i++) { if (0 == o_strcasecmp(endpoint_list[i].http_method, method) || endpoint_list[i].http_method[0] == '*') { splitted_url_format = ulfius_split_url(endpoint_list[i].url_prefix, endpoint_list[i].url_format); if (splitted_url_format != NULL && ulfius_url_format_match((const char **)splitted_url, (const char **)splitted_url_format)) { endpoint_returned = o_realloc(endpoint_returned, (count+2)*sizeof(struct _u_endpoint *)); if (endpoint_returned != NULL) { endpoint_returned[count] = (endpoint_list + i); endpoint_returned[count + 1] = NULL; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reallocating memory for endpoint_returned"); } count++; u_map_clean_enum(splitted_url_format); splitted_url_format = NULL; } u_map_clean_enum(splitted_url_format); splitted_url_format = NULL; } } u_map_clean_enum(splitted_url); splitted_url = NULL; } } /* * only sort if endpoint_returned is != NULL * can be NULL either after initial o_malloc() or after o_realloc() */ if (endpoint_returned != NULL) { qsort(endpoint_returned, count, sizeof(struct endpoint_list *), &compare_endpoint_priorities); } return endpoint_returned; } /** * ulfius_parse_url * fills map with the keys/values defined in the url that are described in the endpoint format url * return U_OK on success */ int ulfius_parse_url(const char * url, const struct _u_endpoint * endpoint, struct _u_map * map, int check_utf8) { char * saveptr = NULL, * cur_word = NULL, * url_cpy = NULL, * url_cpy_addr = NULL; char * saveptr_format = NULL, * saveptr_prefix = NULL, * cur_word_format = NULL, * url_format_cpy = NULL, * url_format_cpy_addr = NULL; if (map != NULL && endpoint != NULL) { url_cpy = url_cpy_addr = o_strdup(url); url_format_cpy = url_format_cpy_addr = o_strdup(endpoint->url_prefix); cur_word = strtok_r( url_cpy, ULFIUS_URL_SEPARATOR, &saveptr ); if (endpoint->url_prefix != NULL && url_format_cpy == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for url_format_cpy"); } else if (url_format_cpy != NULL) { cur_word_format = strtok_r( url_format_cpy, ULFIUS_URL_SEPARATOR, &saveptr_prefix ); } while (cur_word_format != NULL && cur_word != NULL) { // Ignoring url_prefix words cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); cur_word_format = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr_prefix ); } o_free(url_format_cpy_addr); url_format_cpy = url_format_cpy_addr = o_strdup(endpoint->url_format); if (endpoint->url_format != NULL && url_format_cpy == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for url_format_cpy"); return U_ERROR_MEMORY; } else if (url_format_cpy != NULL) { cur_word_format = strtok_r( url_format_cpy, ULFIUS_URL_SEPARATOR, &saveptr_format ); } while (cur_word_format != NULL && cur_word != NULL) { if ((cur_word_format[0] == ':' || cur_word_format[0] == '@') && (!check_utf8 || utf8_check(cur_word) == NULL)) { if (u_map_has_key(map, cur_word_format+1)) { char * concat_url_param = msprintf("%s,%s", u_map_get(map, cur_word_format+1), cur_word); if (concat_url_param == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for concat_url_param"); o_free(url_cpy_addr); o_free(url_format_cpy_addr); return U_ERROR_MEMORY; } else if (u_map_put(map, cur_word_format+1, concat_url_param) != U_OK) { url_cpy_addr = NULL; url_format_cpy_addr = NULL; return U_ERROR_MEMORY; } o_free(concat_url_param); } else { if (u_map_put(map, cur_word_format+1, cur_word) != U_OK) { url_cpy_addr = NULL; url_format_cpy_addr = NULL; return U_ERROR_MEMORY; } } } cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); cur_word_format = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr_format ); } o_free(url_cpy_addr); o_free(url_format_cpy_addr); url_cpy_addr = NULL; url_format_cpy_addr = NULL; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_init_request * Initialize a request structure by allocating inner elements * return U_OK on success */ int ulfius_init_request(struct _u_request * request) { if (request != NULL) { request->map_url = o_malloc(sizeof(struct _u_map)); request->auth_basic_user = NULL; request->auth_basic_password = NULL; request->map_header = o_malloc(sizeof(struct _u_map)); request->map_cookie = o_malloc(sizeof(struct _u_map)); request->map_post_body = o_malloc(sizeof(struct _u_map)); if (request->map_post_body == NULL || request->map_cookie == NULL || request->map_url == NULL || request->map_header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for request->map*"); ulfius_clean_request(request); return U_ERROR_MEMORY; } u_map_init(request->map_url); u_map_init(request->map_header); u_map_init(request->map_cookie); u_map_init(request->map_post_body); request->http_protocol = NULL; request->http_verb = NULL; request->http_url = NULL; request->proxy = NULL; request->timeout = 0L; request->check_server_certificate = 1; request->client_address = NULL; request->binary_body = NULL; request->binary_body_length = 0; #ifndef U_DISABLE_WEBSOCKET request->client_cert = NULL; request->client_cert_file = NULL; request->client_key_file = NULL; request->client_key_password = NULL; #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_clean_request * clean the specified request's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_request_full * return U_OK on success */ int ulfius_clean_request(struct _u_request * request) { if (request != NULL) { o_free(request->http_protocol); o_free(request->http_verb); o_free(request->http_url); o_free(request->proxy); o_free(request->auth_basic_user); o_free(request->auth_basic_password); o_free(request->client_address); u_map_clean_full(request->map_url); u_map_clean_full(request->map_header); u_map_clean_full(request->map_cookie); u_map_clean_full(request->map_post_body); o_free(request->binary_body); request->http_protocol = NULL; request->http_verb = NULL; request->http_url = NULL; request->proxy = NULL; request->client_address = NULL; request->map_url = NULL; request->map_header = NULL; request->map_cookie = NULL; request->map_post_body = NULL; request->binary_body = NULL; #ifndef U_DISABLE_WEBSOCKET gnutls_x509_crt_deinit(request->client_cert); o_free(request->client_cert_file); o_free(request->client_key_file); o_free(request->client_key_password); #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_clean_request_full * clean the specified request and all its elements * return U_OK on success */ int ulfius_clean_request_full(struct _u_request * request) { if (ulfius_clean_request(request) == U_OK) { o_free(request); return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_copy_request * Copy the source request elements into the dest request * return U_OK on success */ int ulfius_copy_request(struct _u_request * dest, const struct _u_request * source) { int ret = U_OK; if (dest != NULL && source != NULL) { dest->http_protocol = o_strdup(source->http_protocol); dest->http_verb = o_strdup(source->http_verb); dest->http_url = o_strdup(source->http_url); dest->proxy = o_strdup(source->proxy); dest->check_server_certificate = source->check_server_certificate; dest->timeout = source->timeout; dest->auth_basic_user = o_strdup(source->auth_basic_user); dest->auth_basic_password = o_strdup(source->auth_basic_password); dest->client_address = o_malloc(sizeof(struct sockaddr)); if (dest->client_address != NULL) { memcpy(dest->client_address, source->client_address, sizeof(struct sockaddr)); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for dest->client_address"); ret = U_ERROR_MEMORY; } if (ret == U_OK && u_map_clean(dest->map_url) == U_OK && u_map_init(dest->map_url) == U_OK) { if (u_map_copy_into(dest->map_url, source->map_url) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_url"); ret = U_ERROR; } } else if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_url"); ret = U_ERROR_MEMORY; } if (ret == U_OK && u_map_clean(dest->map_header) == U_OK && u_map_init(dest->map_header) == U_OK) { if (u_map_copy_into(dest->map_header, source->map_header) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_header"); ret = U_ERROR; } } else if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_header"); ret = U_ERROR_MEMORY; } if (ret == U_OK && u_map_clean(dest->map_cookie) == U_OK && u_map_init(dest->map_cookie) == U_OK) { if (u_map_copy_into(dest->map_cookie, source->map_cookie) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_cookie"); ret = U_ERROR; } } else if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_cookie"); ret = U_ERROR_MEMORY; } if (ret == U_OK && u_map_clean(dest->map_post_body) == U_OK && u_map_init(dest->map_post_body) == U_OK) { if (u_map_copy_into(dest->map_post_body, source->map_post_body) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_post_body"); ret = U_ERROR; } } else if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_post_body"); ret = U_ERROR_MEMORY; } if (ret == U_OK) { dest->binary_body_length = source->binary_body_length; dest->binary_body = o_malloc(source->binary_body_length); if (dest->binary_body != NULL) { memcpy(dest->binary_body, source->binary_body, source->binary_body_length); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for dest->binary_body"); ret = U_ERROR_MEMORY; } } #ifndef U_DISABLE_WEBSOCKET if (ret == U_OK && source->client_cert != NULL) { if (gnutls_x509_crt_init(&dest->client_cert) == 0) { char * str_cert = ulfius_export_client_certificate_pem(source); if (ulfius_import_client_certificate_pem(dest, str_cert) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_import_client_certificate_pem"); ret = U_ERROR; } o_free(str_cert); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_x509_crt_init"); ret = U_ERROR; } } #endif return ret; } else { return U_ERROR_PARAMS; } } /** * create a new request based on the source elements * returned value must be free'd after use */ struct _u_request * ulfius_duplicate_request(const struct _u_request * request) { struct _u_request * new_request = NULL; if (request != NULL) { new_request = o_malloc(sizeof(struct _u_request)); if (new_request == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_request"); return NULL; } if (ulfius_init_request(new_request) == U_OK) { new_request->http_protocol = o_strdup(request->http_protocol); new_request->http_verb = o_strdup(request->http_verb); new_request->http_url = o_strdup(request->http_url); new_request->proxy = o_strdup(request->proxy); if ((new_request->http_verb == NULL && request->http_verb != NULL) || (new_request->http_url == NULL && request->http_url != NULL) || (new_request->proxy == NULL && request->proxy != NULL) || (new_request->http_protocol == NULL && request->http_protocol != NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_duplicate_request"); ulfius_clean_request_full(new_request); return NULL; } if (request->client_address != NULL) { new_request->client_address = o_malloc(sizeof(struct sockaddr)); if (new_request->client_address == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_request->client_address"); ulfius_clean_request_full(new_request); return NULL; } memcpy(new_request->client_address, request->client_address, sizeof(struct sockaddr)); } new_request->check_server_certificate = request->check_server_certificate; new_request->timeout = request->timeout; new_request->auth_basic_user = o_strdup(request->auth_basic_user); new_request->auth_basic_password = o_strdup(request->auth_basic_password); u_map_clean_full(new_request->map_url); u_map_clean_full(new_request->map_header); u_map_clean_full(new_request->map_cookie); u_map_clean_full(new_request->map_post_body); new_request->map_url = u_map_copy(request->map_url); new_request->map_header = u_map_copy(request->map_header); new_request->map_cookie = u_map_copy(request->map_cookie); new_request->map_post_body = u_map_copy(request->map_post_body); if ((new_request->map_url == NULL && request->map_url != NULL) || (new_request->map_header == NULL && request->map_header != NULL) || (new_request->map_cookie == NULL && request->map_cookie != NULL) || (new_request->map_post_body == NULL && request->map_post_body != NULL)) { ulfius_clean_request_full(new_request); return NULL; } if (request->binary_body != NULL && request->binary_body_length > 0) { new_request->binary_body = o_malloc(request->binary_body_length); if (new_request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_request->binary_body"); ulfius_clean_request_full(new_request); return NULL; } memcpy(new_request->binary_body, request->binary_body, request->binary_body_length); } else { new_request->binary_body_length = 0; new_request->binary_body = NULL; } new_request->binary_body_length = request->binary_body_length; #ifndef U_DISABLE_WEBSOCKET if (request->client_cert != NULL) { if (gnutls_x509_crt_init(&new_request->client_cert) == 0) { char * str_cert = ulfius_export_client_certificate_pem(request); if (ulfius_import_client_certificate_pem(new_request, str_cert) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_import_client_certificate_pem"); ulfius_clean_request_full(new_request); return NULL; } o_free(str_cert); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_x509_crt_init"); ulfius_clean_request_full(new_request); return NULL; } } #endif } else { o_free(new_request); new_request = NULL; } } return new_request; } #ifndef U_DISABLE_JANSSON /** * ulfius_set_json_body_request * Add a json_t j_body to a response * return U_OK on success */ int ulfius_set_json_body_request(struct _u_request * request, json_t * j_body) { if (request != NULL && j_body != NULL && (json_is_array(j_body) || json_is_object(j_body))) { // Free all the bodies available o_free(request->binary_body); request->binary_body = NULL; request->binary_body_length = 0; request->binary_body = (void*) json_dumps(j_body, JSON_COMPACT); if (request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for request->binary_body"); return U_ERROR_MEMORY; } request->binary_body_length = o_strlen((char*)request->binary_body); u_map_put(request->map_header, ULFIUS_HTTP_HEADER_CONTENT, ULFIUS_HTTP_ENCODING_JSON); return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_get_json_body_request * Get JSON structure from the request body if the request is valid * request: struct _u_request used * json_error: structure to store json_error_t if specified */ json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error) { if (request != NULL && request->map_header != NULL && NULL != o_strstr(u_map_get_case(request->map_header, ULFIUS_HTTP_HEADER_CONTENT), ULFIUS_HTTP_ENCODING_JSON)) { return json_loadb(request->binary_body, request->binary_body_length, JSON_DECODE_ANY, json_error); } else if (json_error != NULL) { json_error->line = 1; json_error->position = 1; snprintf(json_error->source, (JSON_ERROR_SOURCE_LENGTH - 1), "ulfius_get_json_body_request"); if (NULL == request) { json_error->column = 7; snprintf(json_error->text, (JSON_ERROR_TEXT_LENGTH - 1), "Request not set."); } else if (NULL == request->map_header) { json_error->column = 26; snprintf(json_error->text, (JSON_ERROR_TEXT_LENGTH - 1), "Request header not set."); } else if (NULL == o_strstr(u_map_get_case(request->map_header, ULFIUS_HTTP_HEADER_CONTENT), ULFIUS_HTTP_ENCODING_JSON)) { json_error->column = 57; snprintf(json_error->text, (JSON_ERROR_TEXT_LENGTH - 1), "HEADER content not valid. Expected containging '%s' in header - received '%s'.", ULFIUS_HTTP_ENCODING_JSON, u_map_get_case(request->map_header, ULFIUS_HTTP_HEADER_CONTENT)); } } return NULL; } #endif #ifndef U_DISABLE_WEBSOCKET /* * ulfius_export_client_certificate_pem * Exports the client certificate using PEM format * request: struct _u_request used * returned value must be u_free'd after use */ char * ulfius_export_client_certificate_pem(const struct _u_request * request) { char * str_cert = NULL; gnutls_datum_t g_cert; if (request != NULL && request->client_cert != NULL) { if (gnutls_x509_crt_export2(request->client_cert, GNUTLS_X509_FMT_PEM, &g_cert) == 0) { str_cert = o_strndup((const char *)g_cert.data, g_cert.size); gnutls_free(g_cert.data); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_x509_crt_export2"); } } return str_cert; } /* * ulfius_import_client_certificate_pem * Imports the client certificate using PEM format * request: struct _u_request used * str_cert: client certificate in PEM format * return U_OK on success; */ int ulfius_import_client_certificate_pem(struct _u_request * request, const char * str_cert) { int ret; gnutls_datum_t g_cert; if (request != NULL && str_cert != NULL) { g_cert.data = (unsigned char *)str_cert; g_cert.size = o_strlen(str_cert); if (gnutls_x509_crt_init(&request->client_cert)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_x509_crt_init"); ret = U_ERROR; } else if (gnutls_x509_crt_import(request->client_cert, &g_cert, GNUTLS_X509_FMT_PEM) == 0) { ret = U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_x509_crt_import"); ret = U_ERROR; } } else { ret = U_ERROR_PARAMS; } return ret; } #endif ulfius-2.5.2/src/u_response.c000066400000000000000000000726101341017022300161430ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_response.c: response related functions defintions * * Copyright 2015-2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #include #include "u_private.h" #include "ulfius.h" /** * Add a cookie in the cookie map as defined in the RFC 6265 * Returned value must be free'd after use */ static char * ulfius_generate_cookie_header(const struct _u_cookie * cookie) { char * attr_expires = NULL, * attr_max_age = NULL, * attr_domain = NULL, * attr_path = NULL; char * attr_secure = NULL, * attr_http_only = NULL, * cookie_header_value = NULL, * same_site = NULL; if (cookie != NULL) { if (cookie->expires != NULL) { attr_expires = msprintf("; %s=%s", ULFIUS_COOKIE_ATTRIBUTE_EXPIRES, cookie->expires); if (attr_expires == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_expires"); return NULL; } } else { attr_expires = o_strdup(""); } if (cookie->max_age > 0) { attr_max_age = msprintf("; %s=%d", ULFIUS_COOKIE_ATTRIBUTE_MAX_AGE, cookie->max_age); if (attr_max_age == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_max_age"); o_free(attr_expires); attr_expires = NULL; return NULL; } } else { attr_max_age = o_strdup(""); } if (cookie->domain != NULL) { attr_domain = msprintf("; %s=%s", ULFIUS_COOKIE_ATTRIBUTE_DOMAIN, cookie->domain); if (attr_domain == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_domain"); o_free(attr_expires); attr_expires = NULL; o_free(attr_max_age); attr_max_age = NULL; return NULL; } } else { attr_domain = o_strdup(""); } if (cookie->path != NULL) { attr_path = msprintf("; %s=%s", ULFIUS_COOKIE_ATTRIBUTE_PATH, cookie->path); if (attr_path == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_path"); o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; return NULL; } } else { attr_path = o_strdup(""); } if (cookie->secure) { attr_secure = msprintf("; %s", ULFIUS_COOKIE_ATTRIBUTE_SECURE); if (attr_secure == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_secure"); o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); o_free(attr_path); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; attr_path = NULL; return NULL; } } else { attr_secure = o_strdup(""); } if (cookie->http_only) { attr_http_only = msprintf("; %s", ULFIUS_COOKIE_ATTRIBUTE_HTTPONLY); if (attr_http_only == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_http_only"); o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); o_free(attr_path); o_free(attr_secure); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; attr_path = NULL; attr_secure = NULL; return NULL; } } else { attr_http_only = o_strdup(""); } if (cookie->same_site == U_COOKIE_SAME_SITE_STRICT) { same_site = o_strdup("; SameSite=Strict"); } else if (cookie->same_site == U_COOKIE_SAME_SITE_LAX) { same_site = o_strdup("; SameSite=Lax"); } else { same_site = o_strdup(""); } if (attr_expires == NULL || attr_max_age == NULL || attr_domain == NULL || attr_path == NULL || attr_secure == NULL || attr_http_only == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_generate_cookie_header"); } else { cookie_header_value = msprintf("%s=%s%s%s%s%s%s%s%s", cookie->key, cookie->value, attr_expires, attr_max_age, attr_domain, attr_path, attr_secure, attr_http_only, same_site); if (cookie_header_value == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for cookie_header_value"); } } o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); o_free(attr_path); o_free(attr_secure); o_free(attr_http_only); o_free(same_site); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; attr_path = NULL; attr_secure = NULL; attr_http_only = NULL; same_site = NULL; return cookie_header_value; } else { return NULL; } } /** * ulfius_set_response_header * adds headers defined in the response_map_header to the response * return the number of added headers, -1 on error */ int ulfius_set_response_header(struct MHD_Response * response, const struct _u_map * response_map_header) { const char ** header_keys = u_map_enum_keys(response_map_header); const char * header_value; int i = -1, ret; if (header_keys != NULL && response != NULL && response_map_header != NULL) { for (i=0; header_keys != NULL && header_keys[i] != NULL; i++) { header_value = u_map_get(response_map_header, header_keys[i]); if (header_value != NULL) { ret = MHD_add_response_header (response, header_keys[i], header_value); if (ret == MHD_NO) { i = -1; break; } } } } return i; } /** * ulfius_set_response_cookie * adds cookies defined in the response_map_cookie * return the number of added headers, -1 on error */ int ulfius_set_response_cookie(struct MHD_Response * mhd_response, const struct _u_response * response) { int ret; unsigned int i; char * header; if (mhd_response != NULL && response != NULL) { for (i=0; inb_cookies; i++) { header = ulfius_generate_cookie_header(&response->map_cookie[i]); if (header != NULL) { ret = MHD_add_response_header (mhd_response, MHD_HTTP_HEADER_SET_COOKIE, header); o_free(header); if (ret == MHD_NO) { i = -1; break; } } else { i = -1; break; } } return i; } else { return -1; } } /** * ulfius_add_cookie_to_response * add a cookie to the cookie map * return U_OK on success */ int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age, const char * domain, const char * path, const int secure, const int http_only) { return ulfius_add_same_site_cookie_to_response(response, key, value, expires, max_age, domain, path, secure, http_only, U_COOKIE_SAME_SITE_NONE); } /** * ulfius_add_same_site_cookie_to_response * add a cookie to the cookie map with a SameSite attribute * the same_site parameter must have one of the following values: * - U_COOKIE_SAME_SITE_NONE - No SameSite attribute * - U_COOKIE_SAME_SITE_STRICT - SameSite attribute set to 'Strict' * - U_COOKIE_SAME_SITE_LAX - SameSite attribute set to 'Lax' * return U_OK on success */ int ulfius_add_same_site_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age, const char * domain, const char * path, const int secure, const int http_only, const int same_site) { unsigned int i; if (response != NULL && key != NULL && (same_site == U_COOKIE_SAME_SITE_NONE || same_site == U_COOKIE_SAME_SITE_STRICT || same_site == U_COOKIE_SAME_SITE_LAX)) { // Look for cookies with the same key for (i=0; inb_cookies; i++) { if (0 == o_strcmp(response->map_cookie[i].key, key)) { // Key found, replace cookie o_free(response->map_cookie[i].value); o_free(response->map_cookie[i].expires); o_free(response->map_cookie[i].domain); o_free(response->map_cookie[i].path); response->map_cookie[i].value = o_strdup(value!=NULL?value:""); response->map_cookie[i].expires = o_strdup(expires); response->map_cookie[i].domain = o_strdup(domain); response->map_cookie[i].path = o_strdup(path); response->map_cookie[i].max_age = max_age; response->map_cookie[i].secure = secure; response->map_cookie[i].http_only = http_only; response->map_cookie[i].same_site = same_site; if ((value != NULL && response->map_cookie[i].value == NULL) || (expires != NULL && response->map_cookie[i].expires == NULL) || (domain != NULL && response->map_cookie[i].domain == NULL) || (path != NULL && response->map_cookie[i].path == NULL)) { ulfius_clean_cookie(&response->map_cookie[i]); o_free(response->map_cookie[i].value); o_free(response->map_cookie[i].expires); o_free(response->map_cookie[i].domain); o_free(response->map_cookie[i].path); return U_ERROR_MEMORY; } else { return U_OK; } } } // Key not found, inserting a new cookie if (response->nb_cookies == 0) { response->map_cookie = o_malloc(sizeof(struct _u_cookie)); if (response->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->map_cookie"); return U_ERROR_MEMORY; } } else { response->map_cookie = o_realloc(response->map_cookie, (response->nb_cookies + 1) * sizeof(struct _u_cookie)); if (response->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->map_cookie"); return U_ERROR_MEMORY; } } response->map_cookie[response->nb_cookies].key = o_strdup(key); response->map_cookie[response->nb_cookies].value = o_strdup(value!=NULL?value:""); response->map_cookie[response->nb_cookies].expires = o_strdup(expires); response->map_cookie[response->nb_cookies].max_age = max_age; response->map_cookie[response->nb_cookies].domain = o_strdup(domain); response->map_cookie[response->nb_cookies].path = o_strdup(path); response->map_cookie[response->nb_cookies].secure = secure; response->map_cookie[response->nb_cookies].http_only = http_only; response->map_cookie[response->nb_cookies].same_site = same_site; if ((key != NULL && response->map_cookie[response->nb_cookies].key == NULL) || (value != NULL && response->map_cookie[response->nb_cookies].value == NULL) || (expires != NULL && response->map_cookie[response->nb_cookies].expires == NULL) || (domain != NULL && response->map_cookie[response->nb_cookies].domain == NULL) || (path != NULL && response->map_cookie[response->nb_cookies].path == NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_add_cookie_to_response"); ulfius_clean_cookie(&response->map_cookie[response->nb_cookies]); o_free(response->map_cookie[response->nb_cookies].key); o_free(response->map_cookie[response->nb_cookies].value); o_free(response->map_cookie[response->nb_cookies].expires); o_free(response->map_cookie[response->nb_cookies].domain); o_free(response->map_cookie[response->nb_cookies].path); return U_ERROR_MEMORY; } response->nb_cookies++; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_clean_cookie * clean the cookie's elements * return U_OK on success */ int ulfius_clean_cookie(struct _u_cookie * cookie) { if (cookie != NULL) { o_free(cookie->key); o_free(cookie->value); o_free(cookie->expires); o_free(cookie->domain); o_free(cookie->path); cookie->key = NULL; cookie->value = NULL; cookie->expires = NULL; cookie->domain = NULL; cookie->path = NULL; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Copy the cookie source elements into dest elements * return U_OK on success */ int ulfius_copy_cookie(struct _u_cookie * dest, const struct _u_cookie * source) { if (source != NULL && dest != NULL) { dest->key = o_strdup(source->key); dest->value = o_strdup(source->value); dest->expires = o_strdup(source->expires); dest->max_age = source->max_age; dest->domain = o_strdup(source->domain); dest->path = o_strdup(source->path); dest->secure = source->secure; dest->http_only = source->http_only; dest->same_site = source->same_site; if (dest->path == NULL || dest->domain == NULL || dest->expires == NULL || dest->value == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_copy_cookie"); o_free(dest->path); o_free(dest->domain); o_free(dest->expires); o_free(dest->value); return U_ERROR_MEMORY; } else { return U_OK; } } return U_ERROR_PARAMS; } /** * ulfius_clean_response * clean the specified response's inner elements * user must free the parent pointer if needed after clean * or use ulfius_clean_response_full * return U_OK on success */ int ulfius_clean_response(struct _u_response * response) { unsigned int i; if (response != NULL) { o_free(response->protocol); response->protocol = NULL; u_map_clean_full(response->map_header); response->map_header = NULL; for (i=0; inb_cookies; i++) { ulfius_clean_cookie(&response->map_cookie[i]); } o_free(response->auth_realm); o_free(response->map_cookie); o_free(response->binary_body); response->auth_realm = NULL; response->map_cookie = NULL; response->binary_body = NULL; #ifndef U_DISABLE_WEBSOCKET /* ulfius_clean_response might be called without websocket_handle being initialized */ if ((struct _websocket_handle *)response->websocket_handle) { o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_extensions); o_free(response->websocket_handle); } #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_clean_response_full * clean the specified response and all its elements * return U_OK on success */ int ulfius_clean_response_full(struct _u_response * response) { if (ulfius_clean_response(response) == U_OK) { o_free(response); return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_init_response * Initialize a response structure by allocating inner elements * return U_OK on success */ int ulfius_init_response(struct _u_response * response) { if (response != NULL) { response->status = 200; response->map_header = o_malloc(sizeof(struct _u_map)); if (response->map_header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->map_header"); return U_ERROR_MEMORY; } if (u_map_init(response->map_header) != U_OK) { return U_ERROR_PARAMS; } response->auth_realm = NULL; response->map_cookie = NULL; response->nb_cookies = 0; response->protocol = NULL; response->binary_body = NULL; response->binary_body_length = 0; response->stream_callback = NULL; response->stream_size = -1; response->stream_block_size = ULFIUS_STREAM_BLOCK_SIZE_DEFAULT; response->stream_callback_free = NULL; response->timeout = 0; response->shared_data = NULL; #ifndef U_DISABLE_WEBSOCKET response->websocket_handle = o_malloc(sizeof(struct _websocket_handle)); if (response->websocket_handle == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->websocket_handle"); return U_ERROR_MEMORY; } ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data = NULL; #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * create a new response based on the source elements * return value must be free'd after use */ struct _u_response * ulfius_duplicate_response(const struct _u_response * response) { struct _u_response * new_response = NULL; unsigned int i; if (response != NULL) { new_response = o_malloc(sizeof(struct _u_response)); if (new_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response"); return NULL; } ulfius_init_response(new_response); new_response->status = response->status; new_response->protocol = o_strdup(response->protocol); if (new_response->protocol == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response->protocol"); ulfius_clean_response_full(new_response); return NULL; } u_map_clean_full(new_response->map_header); new_response->map_header = u_map_copy(response->map_header); new_response->auth_realm = o_strdup(response->auth_realm); new_response->nb_cookies = response->nb_cookies; if (response->nb_cookies > 0) { new_response->map_cookie = o_malloc(response->nb_cookies*sizeof(struct _u_cookie)); if (new_response->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response->map_cookie"); o_free(new_response); return NULL; } for (i=0; inb_cookies; i++) { ulfius_copy_cookie(&new_response->map_cookie[i], &response->map_cookie[i]); } } else { new_response->map_cookie = NULL; } if (response->binary_body != NULL && response->binary_body_length > 0) { new_response->binary_body = o_malloc(response->binary_body_length); if (new_response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response->binary_body"); o_free(new_response->map_cookie); o_free(new_response); return NULL; } new_response->binary_body_length = response->binary_body_length; memcpy(new_response->binary_body, response->binary_body, response->binary_body_length); } #ifndef U_DISABLE_WEBSOCKET if ((new_response->websocket_handle = o_malloc(sizeof(struct _websocket_handle))) != NULL) { if (response->websocket_handle != NULL) { ((struct _websocket_handle *)new_response->websocket_handle)->websocket_protocol = o_strdup(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); ((struct _websocket_handle *)new_response->websocket_handle)->websocket_extensions = o_strdup(((struct _websocket_handle *)response->websocket_handle)->websocket_extensions); ((struct _websocket_handle *)new_response->websocket_handle)->websocket_manager_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_manager_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_incoming_message_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_incoming_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_onclose_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_onclose_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data; } } else { ulfius_clean_response_full(new_response); return NULL; } #endif } return new_response; } /** * ulfius_copy_response * Copy the source response elements into the des response * return U_OK on success */ int ulfius_copy_response(struct _u_response * dest, const struct _u_response * source) { unsigned int i; if (dest != NULL && source != NULL) { dest->status = source->status; dest->protocol = o_strdup(source->protocol); if (dest->protocol == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dest->protocol"); return U_ERROR_MEMORY; } u_map_clean_full(dest->map_header); dest->map_header = u_map_copy(source->map_header); if (dest->map_header == NULL) { return U_ERROR_MEMORY; } dest->nb_cookies = source->nb_cookies; if (source->nb_cookies > 0) { dest->map_cookie = o_malloc(source->nb_cookies*sizeof(struct _u_cookie)); if (dest->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dest->map_cookie"); return U_ERROR_MEMORY; } for (i=0; inb_cookies; i++) { ulfius_copy_cookie(&dest->map_cookie[i], &source->map_cookie[i]); } } else { dest->map_cookie = NULL; } if (source->binary_body != NULL && source->binary_body_length > 0) { dest->binary_body = o_malloc(source->binary_body_length); if (dest->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dest->binary_body"); return U_ERROR_MEMORY; } dest->binary_body_length = source->binary_body_length; memcpy(dest->binary_body, source->binary_body, source->binary_body_length); } #ifndef U_DISABLE_WEBSOCKET if (source->websocket_handle != NULL) { ((struct _websocket_handle *)dest->websocket_handle)->websocket_protocol = o_strdup(((struct _websocket_handle *)source->websocket_handle)->websocket_protocol); ((struct _websocket_handle *)dest->websocket_handle)->websocket_extensions = o_strdup(((struct _websocket_handle *)source->websocket_handle)->websocket_extensions); ((struct _websocket_handle *)dest->websocket_handle)->websocket_manager_callback = ((struct _websocket_handle *)source->websocket_handle)->websocket_manager_callback; ((struct _websocket_handle *)dest->websocket_handle)->websocket_manager_user_data = ((struct _websocket_handle *)source->websocket_handle)->websocket_manager_user_data; ((struct _websocket_handle *)dest->websocket_handle)->websocket_incoming_message_callback = ((struct _websocket_handle *)source->websocket_handle)->websocket_incoming_message_callback; ((struct _websocket_handle *)dest->websocket_handle)->websocket_incoming_user_data = ((struct _websocket_handle *)source->websocket_handle)->websocket_incoming_user_data; ((struct _websocket_handle *)dest->websocket_handle)->websocket_onclose_callback = ((struct _websocket_handle *)source->websocket_handle)->websocket_onclose_callback; ((struct _websocket_handle *)dest->websocket_handle)->websocket_onclose_user_data = ((struct _websocket_handle *)source->websocket_handle)->websocket_onclose_user_data; } #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_set_string_body_response * Set a string binary_body to a response * binary_body must end with a '\0' character * return U_OK on success */ int ulfius_set_string_body_response(struct _u_response * response, const unsigned int status, const char * binary_body) { if (response != NULL && binary_body != NULL) { size_t binary_body_length = o_strlen(binary_body); // Free the binary_body available o_free(response->binary_body); response->binary_body = o_malloc(binary_body_length); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } else { response->status = status; response->binary_body_length = binary_body_length; memcpy(response->binary_body, binary_body, binary_body_length); return U_OK; } } else { return U_ERROR_PARAMS; } } /** * ulfius_set_binary_body_response * Add a binary binary_body to a response * return U_OK on success */ int ulfius_set_binary_body_response(struct _u_response * response, const unsigned int status, const char * binary_body, const size_t length) { if (response != NULL && binary_body != NULL && length > 0) { // Free all the bodies available o_free(response->binary_body); response->binary_body = NULL; response->binary_body_length = 0; response->binary_body = o_malloc(length); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } memcpy(response->binary_body, binary_body, length); response->binary_body_length = length; response->status = status; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_set_empty_body_response * Set an empty response with only a status * return U_OK on success */ int ulfius_set_empty_body_response(struct _u_response * response, const unsigned int status) { if (response != NULL) { // Free all the bodies available response->binary_body = NULL; response->binary_body_length = 0; response->status = status; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_set_stream_response * Set an stream response with a status * set stream_size to -1 if unknown * set stream_block_size to a proper value based on the system * return U_OK on success */ int ulfius_set_stream_response(struct _u_response * response, const unsigned int status, ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max), void (* stream_callback_free) (void * stream_user_data), uint64_t stream_size, size_t stream_block_size, void * stream_user_data) { if (response != NULL && stream_callback != NULL) { // Free all the bodies available o_free(response->binary_body); response->binary_body = NULL; response->binary_body_length = 0; response->status = status; response->stream_callback = stream_callback; response->stream_callback_free = stream_callback_free; response->stream_size = stream_size; response->stream_block_size = stream_block_size; response->stream_user_data = stream_user_data; return U_OK; } else { return U_ERROR_PARAMS; } } #ifndef U_DISABLE_JANSSON /** * ulfius_set_json_body_response * Add a json_t j_body to a response * return U_OK on success */ int ulfius_set_json_body_response(struct _u_response * response, const unsigned int status, const json_t * j_body) { if (response != NULL && j_body != NULL && (json_is_array(j_body) || json_is_object(j_body))) { // Free all the bodies available o_free(response->binary_body); response->binary_body = NULL; response->binary_body_length = 0; response->binary_body = (void*) json_dumps(j_body, JSON_COMPACT); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } response->binary_body_length = o_strlen((char*)response->binary_body); response->status = status; u_map_put(response->map_header, ULFIUS_HTTP_HEADER_CONTENT, ULFIUS_HTTP_ENCODING_JSON); return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_get_json_body_response * Get JSON structure from the request body if the request is valid * request: struct _u_request used * json_error: structure to store json_error_t if specified */ json_t * ulfius_get_json_body_response(struct _u_response * response, json_error_t * json_error) { if (response != NULL && response->map_header != NULL && NULL != o_strstr(u_map_get_case(response->map_header, ULFIUS_HTTP_HEADER_CONTENT), ULFIUS_HTTP_ENCODING_JSON)) { return json_loadb(response->binary_body, response->binary_body_length, JSON_DECODE_ANY, json_error); } return NULL; } #endif /** * ulfius_add_header_to_response * add a header to the response * return U_OK on success */ int ulfius_add_header_to_response(struct _u_response * response, const char * key, const char * value) { if (response != NULL && key != NULL && value != NULL) { return u_map_put(response->map_header, key, value); return U_OK; } else { return U_ERROR_PARAMS; } } ulfius-2.5.2/src/u_send_request.c000066400000000000000000001013721341017022300170040ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_send_request.c: send request related functions defintions * * Copyright 2015-2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #include "u_private.h" #include "ulfius.h" #ifndef U_DISABLE_CURL #include #include #include #include /** * Internal structure used to store temporarly the response body */ typedef struct _body { char * data; size_t size; } body; /** * ulfius_write_body * Internal function used to write the body response into a _body structure */ static size_t ulfius_write_body(void * contents, size_t size, size_t nmemb, void * user_data) { size_t realsize = size * nmemb; body * body_data = (body *) user_data; body_data->data = o_realloc(body_data->data, body_data->size + realsize + 1); if(body_data->data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for body_data->data"); return 0; } memcpy(&(body_data->data[body_data->size]), contents, realsize); body_data->size += realsize; body_data->data[body_data->size] = 0; return realsize; } /** * write_header * Write the header value into the response map_header structure * return the size_t of the header written */ static size_t write_header(void * buffer, size_t size, size_t nitems, void * user_data) { struct _u_response * response = (struct _u_response *) user_data; char * header = (char *)buffer, * key, * value, * saveptr, * tmp; if (o_strchr(header, ':') != NULL) { if (response->map_header != NULL) { // Expecting a header (key: value) key = trimwhitespace(strtok_r(header, ":", &saveptr)); value = trimwhitespace(strtok_r(NULL, "", &saveptr)); if (!u_map_has_key_case(response->map_header, key)) { u_map_put(response->map_header, key, value); } else { tmp = msprintf("%s, %s", u_map_get_case(response->map_header, key), value); if (u_map_remove_from_key_case(response->map_header, key) != U_OK || u_map_put(response->map_header, key, tmp)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting header value for name %s", key); } o_free(tmp); } } } else if (o_strlen(trimwhitespace(header)) > 0) { // Expecting the HTTP/x.x header if (response->protocol != NULL) { o_free(response->protocol); } response->protocol = o_strdup(header); if (response->protocol == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->protocol"); return 0; } } return nitems * size; } /** * ulfius_send_http_request * Send a HTTP request and store the result into a _u_response * return U_OK on success */ int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response) { body body_data; body_data.size = 0; body_data.data = NULL; int res; res = ulfius_send_http_streaming_request(request, response, ulfius_write_body, (void *)&body_data); if (res == U_OK && response != NULL) { if (body_data.data != NULL && body_data.size > 0) { response->binary_body = o_malloc(body_data.size); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); o_free(body_data.data); return U_ERROR_MEMORY; } memcpy(response->binary_body, body_data.data, body_data.size); response->binary_body_length = body_data.size; } o_free(body_data.data); return U_OK; } else { o_free(body_data.data); return res; } } /** * ulfius_send_http_streaming_request * Send a HTTP request and store the result into a _u_response * Except for the body which will be available using write_body_function in the write_body_data * return U_OK on success */ int ulfius_send_http_streaming_request(const struct _u_request * request, struct _u_response * response, size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data), void * write_body_data) { CURLcode res; CURL * curl_handle = NULL; struct curl_slist * header_list = NULL, * cookies_list = NULL; char * key_esc, * value_esc, * cookie, * header, * param, * fp = "?", * np = "&"; const char * value, ** keys; int i, has_params = 0, len; struct _u_request * copy_request = NULL; o_malloc_t malloc_fn; o_realloc_t realloc_fn; o_free_t free_fn; if (request != NULL) { // Duplicate the request and work on it copy_request = ulfius_duplicate_request(request); if (copy_request == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_duplicate_request"); return U_ERROR_MEMORY; } else { o_get_alloc_funcs(&malloc_fn, &realloc_fn, &free_fn); if (curl_global_init_mem(CURL_GLOBAL_DEFAULT, malloc_fn, free_fn, realloc_fn, *o_strdup, *calloc) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_global_init_mem"); return U_ERROR_MEMORY; } curl_handle = curl_easy_init(); // Append header values if (copy_request->map_header == NULL) { copy_request->map_header = o_malloc(sizeof(struct _u_map)); if (copy_request->map_header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->map_header"); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } if (u_map_init(copy_request->map_header) != U_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } } // Set basic auth if defined if (copy_request->auth_basic_user != NULL && copy_request->auth_basic_password != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC) == CURLE_OK) { if (curl_easy_setopt(curl_handle, CURLOPT_USERNAME, copy_request->auth_basic_user) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, copy_request->auth_basic_password) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting HTTP Basic user name or password"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting HTTP Basic Auth option"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } } #ifndef U_DISABLE_WEBSOCKET // Set client certificate authentication if defined if (request->client_cert_file != NULL && request->client_key_file != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_SSLCERT, request->client_cert_file) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting client certificate file"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } else if (curl_easy_setopt(curl_handle, CURLOPT_SSLKEY, request->client_key_file) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting client key file"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } else if (request->client_key_password != NULL && curl_easy_setopt(curl_handle, CURLOPT_KEYPASSWD, request->client_key_password) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting client key password"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } } #endif // Set proxy if defined if (copy_request->proxy != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_PROXY, copy_request->proxy) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting proxy option"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } } has_params = (o_strchr(copy_request->http_url, '?') != NULL); if (copy_request->map_url != NULL && u_map_count(copy_request->map_url) > 0) { // Append url parameters keys = u_map_enum_keys(copy_request->map_url); // Append parameters from map_url for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(copy_request->map_url, keys[i]); key_esc = curl_easy_escape(curl_handle, keys[i], 0); value_esc = curl_easy_escape(curl_handle, value, 0); if (key_esc == NULL || value_esc == NULL) { curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } param = msprintf("%s=%s", key_esc, value_esc); if (param == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for param"); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } if (has_params == 0) { copy_request->http_url = o_realloc(copy_request->http_url, o_strlen(copy_request->http_url) + o_strlen(param) + 2); if (copy_request->http_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->http_url"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } strcat(copy_request->http_url, fp); strcat(copy_request->http_url, param); has_params = 1; } else { copy_request->http_url = o_realloc(copy_request->http_url, o_strlen(copy_request->http_url) + o_strlen(param) + 2); if (copy_request->http_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->http_url"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } strcat(copy_request->http_url, np); strcat(copy_request->http_url, param); } o_free(param); curl_free(key_esc); curl_free(value_esc); } } if (u_map_count(copy_request->map_post_body) > 0) { // Append MHD_HTTP_POST_ENCODING_FORM_URLENCODED post parameters keys = u_map_enum_keys(copy_request->map_post_body); for (i=0; keys != NULL && keys[i] != NULL; i++) { // Build parameter value = u_map_get(copy_request->map_post_body, keys[i]); if (value == NULL) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } key_esc = curl_easy_escape(curl_handle, keys[i], 0); value_esc = curl_easy_escape(curl_handle, value, 0); if (key_esc == NULL || value_esc == NULL) { curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } param = msprintf("%s=%s", key_esc, value_esc); if (param == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for param"); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } // Append parameter to body if (copy_request->binary_body_length == 0) { len = o_strlen(param) + sizeof(char); copy_request->binary_body = o_malloc(len); if (copy_request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->binary_body"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } o_strcpy(copy_request->binary_body, ""); strcat(copy_request->binary_body, param); copy_request->binary_body_length = len; } else { len = (copy_request->binary_body_length + o_strlen(param) + sizeof(char)); copy_request->binary_body = o_realloc(copy_request->binary_body, len); if (copy_request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->binary_body"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } memcpy((char*)copy_request->binary_body + copy_request->binary_body_length, np, 1); memcpy((char*)copy_request->binary_body + copy_request->binary_body_length + 1, param, o_strlen(param)); copy_request->binary_body_length = len; } o_free(param); curl_free(key_esc); curl_free(value_esc); } if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, copy_request->binary_body) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields"); return U_ERROR_LIBCURL; } if (u_map_put(copy_request->map_header, ULFIUS_HTTP_HEADER_CONTENT, MHD_HTTP_POST_ENCODING_FORM_URLENCODED) != U_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } } if (u_map_count(copy_request->map_header) > 0) { // Append map headers keys = u_map_enum_keys(copy_request->map_header); for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(copy_request->map_header, keys[i]); if (value == NULL) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } header = msprintf("%s:%s", keys[i], value); if (header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for header"); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } header_list = curl_slist_append(header_list, header); if (header_list == NULL) { o_free(header); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } o_free(header); } } if (copy_request->map_cookie != NULL && u_map_count(copy_request->map_cookie) > 0) { // Append cookies keys = u_map_enum_keys(copy_request->map_cookie); for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(copy_request->map_cookie, keys[i]); if (value == NULL) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } cookie = msprintf("%s=%s", keys[i], value); if (cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for cookie"); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } if (curl_easy_setopt(curl_handle, CURLOPT_COOKIE, cookie) != CURLE_OK) { o_free(cookie); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } o_free(cookie); } } // Request parameters if (curl_easy_setopt(curl_handle, CURLOPT_URL, copy_request->http_url) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, copy_request->http_verb) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, header_list) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (1)"); return U_ERROR_LIBCURL; } // Set CURLOPT_WRITEFUNCTION if specified if (write_body_function != NULL && curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_body_function) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (2)"); return U_ERROR_LIBCURL; } // Set CURLOPT_WRITEDATA if specified if (write_body_data != NULL && curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, write_body_data) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (3)"); return U_ERROR_LIBCURL; } // Disable certificate validation if needed if (!copy_request->check_server_certificate) { if (curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (4)"); return U_ERROR_LIBCURL; } } // Set request timeout value if (copy_request->timeout) { if (curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, copy_request->timeout) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (5)"); return U_ERROR_LIBCURL; } } // Response parameters if (response != NULL) { if (response->map_header != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, write_header) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, response) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers"); return U_ERROR_LIBCURL; } } } if (copy_request->binary_body != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, copy_request->binary_body) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields"); return U_ERROR_LIBCURL; } if (copy_request->binary_body_length > 0) { if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, copy_request->binary_body_length) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields"); return U_ERROR_LIBCURL; } } } if (curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1) != CURLE_OK) { curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl CURLOPT_NOSIGNAL"); return U_ERROR_LIBCURL; } if (curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, "") != CURLE_OK) { // Apparently you have to do that to tell libcurl you'll need cookies afterwards curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl CURLOPT_COOKIEFILE"); return U_ERROR_LIBCURL; } res = curl_easy_perform(curl_handle); if(res == CURLE_OK && response != NULL) { if (curl_easy_getinfo (curl_handle, CURLINFO_RESPONSE_CODE, &response->status) != CURLE_OK) { curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing http request, libcurl error: %d, error message %s", res, curl_easy_strerror(res)); return U_ERROR_LIBCURL; } if (curl_easy_getinfo(curl_handle, CURLINFO_COOKIELIST, &cookies_list) == CURLE_OK) { struct curl_slist * nc = cookies_list; char * key = NULL, * value = NULL, * expires = NULL, * domain = NULL, * path = NULL; int secure = 0, http_only = 0; while (nc != NULL) { char * nc_dup = o_strdup(nc->data), * saveptr, * elt; int counter = 0; if (nc_dup != NULL) { elt = strtok_r(nc_dup, "\t", &saveptr); while (elt != NULL) { // libcurl cookie format is domain\tsecure\tpath\thttp_only\texpires\tkey\tvalue switch (counter) { case 0: domain = o_strdup(elt); break; case 1: secure = (0==o_strcmp(elt, "TRUE")); break; case 2: path = o_strdup(elt); break; case 3: http_only = (0==o_strcmp(elt, "TRUE")); break; case 4: expires = o_strdup(elt); break; case 5: key = o_strdup(elt); break; case 6: value = o_strdup(elt); break; } elt = strtok_r(NULL, "\t", &saveptr); counter++; } if (ulfius_add_cookie_to_response(response, key, value, expires, 0, domain, path, secure, http_only) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error adding cookie %s/%s to response", key, value); } o_free(key); o_free(value); o_free(domain); o_free(path); o_free(expires); } o_free(nc_dup); nc = nc->next; } } else { curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing http request, libcurl error: %d, error message %s", res, curl_easy_strerror(res)); return U_ERROR_LIBCURL; } curl_slist_free_all(cookies_list); } curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); if (res == CURLE_OK) { return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing curl command: %s", curl_easy_strerror(res)); return U_ERROR_LIBCURL; } } } return U_ERROR_PARAMS; } /** * ulfius_send_smtp_email body fill function and structures */ #define MAIL_DATE 0 #define MAIL_TO 1 #define MAIL_FROM 2 #define MAIL_CC 3 #define MAIL_SUBJECT 4 #define MAIL_DATA 5 #define MAIL_END 6 struct upload_status { int lines_read; char * to; char * from; char * cc; char * subject; char * data; }; static size_t smtp_payload_source(void * ptr, size_t size, size_t nmemb, void * userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; char * data = NULL; size_t len = 0; if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { return 0; } if (upload_ctx->lines_read == MAIL_DATE) { time_t now_sec; struct tm now; time(&now_sec); data = o_malloc(128*sizeof(char)); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_DATE\n"); return 0; } else { gmtime_r(&now_sec, &now); #ifdef _WIN32 strftime(data, 128, "Date: %a, %d %b %Y %H:%M:%S %z\r\n", &now); #else strftime(data, 128, "Date: %a, %d %b %Y %T %z\r\n", &now); #endif len = o_strlen(data); } } else if (upload_ctx->lines_read == MAIL_TO) { data = msprintf("To: %s\r\n", upload_ctx->to); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_TO\n"); return 0; } len = o_strlen(data); } else if (upload_ctx->lines_read == MAIL_FROM) { data = msprintf("From: %s\r\n", upload_ctx->from); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_FROM\n"); return 0; } len = o_strlen(data); } else if (upload_ctx->lines_read == MAIL_CC && upload_ctx->cc) { data = msprintf("Cc: %s\r\n", upload_ctx->cc); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_CC\n"); return 0; } len = o_strlen(data); } else if (upload_ctx->lines_read == MAIL_SUBJECT) { data = msprintf("Subject: %s\r\n", upload_ctx->subject); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_SUBJECT\n"); return 0; } len = o_strlen(data); } else if (upload_ctx->lines_read == MAIL_DATA) { data = msprintf("Content-Type: text/plain; charset=utf-8\r\n\r\n%s\r\n", upload_ctx->data); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_DATA\n"); return 0; } len = o_strlen(data); } if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error while processing upload_ctx->lines_read\n"); return 0; } if (upload_ctx->lines_read != MAIL_END) { memcpy(ptr, data, (len+1)); upload_ctx->lines_read++; // Skip next if it's cc and there is no cc if (upload_ctx->lines_read == MAIL_CC && !upload_ctx->cc) { upload_ctx->lines_read++; } o_free(data); return len; } else if (upload_ctx->lines_read == MAIL_END) { return 0; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting mail payload, len is %d, lines_read is %d", len, upload_ctx->lines_read); return 0; } } /** * Send an email using libcurl * email is plain/text and UTF8 charset * host: smtp server host name * port: tcp port number (optional, 0 for default) * use_tls: true if the connection is tls secured * verify_certificate: true if you want to disable the certificate verification on a tls server * user: connection user name (optional, NULL: no user name) * password: connection password (optional, NULL: no password) * from: from address (mandatory) * to: to recipient address (mandatory) * cc: cc recipient address (optional, NULL: no cc) * bcc: bcc recipient address (optional, NULL: no bcc) * subject: email subject (mandatory) * mail_body: email body (mandatory) * return U_OK on success */ int ulfius_send_smtp_email(const char * host, const int port, const int use_tls, const int verify_certificate, const char * user, const char * password, const char * from, const char * to, const char * cc, const char * bcc, const char * subject, const char * mail_body) { CURL * curl_handle; CURLcode res = CURLE_OK; char * smtp_url; int cur_port; struct curl_slist * recipients = NULL; struct upload_status upload_ctx; if (host != NULL && from != NULL && to != NULL && mail_body != NULL) { curl_handle = curl_easy_init(); if (curl_handle != NULL) { if (port == 0 && !use_tls) { cur_port = 25; } else if (port == 0 && use_tls) { cur_port = 587; } else { cur_port = port; } smtp_url = msprintf("smtp%s://%s:%d", use_tls?"s":"", host, cur_port); if (smtp_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for smtp_url"); return U_ERROR_MEMORY; } curl_easy_setopt(curl_handle, CURLOPT_URL, smtp_url); if (use_tls) { curl_easy_setopt(curl_handle, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); } if (use_tls && !verify_certificate) { curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); } if (user != NULL && password != NULL) { curl_easy_setopt(curl_handle, CURLOPT_USERNAME, user); curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password); } curl_easy_setopt(curl_handle, CURLOPT_MAIL_FROM, from); recipients = curl_slist_append(recipients, to); if (cc != NULL) { recipients = curl_slist_append(recipients, cc); } if (bcc != NULL) { recipients = curl_slist_append(recipients, bcc); } curl_easy_setopt(curl_handle, CURLOPT_MAIL_RCPT, recipients); upload_ctx.lines_read = 0; upload_ctx.to = (char*)to; upload_ctx.from = (char*)from; upload_ctx.cc = (char*)cc; upload_ctx.subject = (char*)subject; upload_ctx.data = (char*)mail_body; curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, smtp_payload_source); curl_easy_setopt(curl_handle, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L); res = curl_easy_perform(curl_handle); curl_slist_free_all(recipients); curl_easy_cleanup(curl_handle); o_free(smtp_url); if (res != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending smtp message, libcurl error: %d, error message %s", res, curl_easy_strerror(res)); return U_ERROR_LIBCURL; } else { return U_OK; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing curl_easy_init"); return U_ERROR_LIBCURL; } } else { return U_ERROR_PARAMS; } } #endif ulfius-2.5.2/src/u_websocket.c000066400000000000000000002147011341017022300162720ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_websocket.c: websocket implementation * * Copyright 2017-2018 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #include "u_private.h" #include "ulfius.h" #ifndef U_DISABLE_WEBSOCKET #include "yuarel.h" #include #include #include #include #include #include #include #include #include #include #include #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) /**********************************/ /** Internal websocket functions **/ /**********************************/ static int is_websocket_data_available(struct _websocket_manager * websocket_manager) { int ret = 0, poll_ret = 0; poll_ret = poll(&websocket_manager->fds, 1, U_WEBSOCKET_USEC_WAIT); if (poll_ret == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error poll websocket read"); websocket_manager->connected = 0; } else if (websocket_manager->fds.revents & (POLLRDHUP|POLLERR|POLLHUP|POLLNVAL)) { websocket_manager->connected = 0; } else if (poll_ret > 0) { ret = 1; } return ret; } static ssize_t read_data_from_socket(struct _websocket_manager * websocket_manager, uint8_t * data, size_t len) { ssize_t ret = 0, data_len; if (len > 0) { do { if (websocket_manager->tls) { data_len = gnutls_record_recv(websocket_manager->gnutls_session, data, (len - ret)); } else if (websocket_manager->type == U_WEBSOCKET_SERVER) { data_len = read(websocket_manager->mhd_sock, data, (len - ret)); } else { data_len = read(websocket_manager->tcp_sock, data, (len - ret)); } if (data_len > 0) { ret += data_len; } else if (data_len < 0) { ret = -1; break; } } while (ret < (ssize_t)len); } return ret; } /** * Workaround to make sure a message, as long as it can be is complete sent */ static void ulfius_websocket_send_frame(struct _websocket_manager * websocket_manager, const uint8_t * data, size_t len) { ssize_t ret = 0, off; if (data != NULL && len > 0) { for (off = 0; (size_t)off < len; off += ret) { if (websocket_manager->type == U_WEBSOCKET_SERVER) { ret = send(websocket_manager->mhd_sock, &data[off], len - off, MSG_NOSIGNAL); if (ret < 0) { break; } } else { if (websocket_manager->tls) { ret = gnutls_record_send(websocket_manager->gnutls_session, &data[off], len - off); } else { ret = send(websocket_manager->tcp_sock, &data[off], len - off, MSG_NOSIGNAL); } if (ret < 0) { break; } } } } } /** * Builds a websocket frame from the given struct _websocket_message * returns U_OK on success * frame must be free'd after use */ static int ulfius_build_frame (const struct _websocket_message * message, const uint64_t data_offset, const uint64_t data_len, uint8_t ** frame, size_t * frame_len) { int ret, has_fin = 0; unsigned int i; uint64_t off, frame_data_len; if (message != NULL && frame != NULL && frame_len != NULL) { *frame_len = 2; if (message->data_len > 65536) { *frame_len += 8; } else if (message->data_len > 128) { *frame_len += 2; } if (message->has_mask) { *frame_len += 4; } if (data_offset + data_len >= message->data_len) { frame_data_len = message->data_len - data_offset; has_fin = 1; } else { frame_data_len = data_len; } *frame_len += frame_data_len; *frame = o_malloc(*frame_len); if (*frame != NULL) { if (has_fin) { (*frame)[0] = (message->opcode | U_WEBSOCKET_BIT_FIN); } else { (*frame)[0] = 0; } if (message->data_len > 65536) { (*frame)[1] = 127; (*frame)[2] = (uint8_t)(frame_data_len >> 54); (*frame)[3] = (uint8_t)(frame_data_len >> 48); (*frame)[4] = (uint8_t)(frame_data_len >> 40); (*frame)[5] = (uint8_t)(frame_data_len >> 32); (*frame)[6] = (uint8_t)(frame_data_len >> 24); (*frame)[7] = (uint8_t)(frame_data_len >> 16); (*frame)[8] = (uint8_t)(frame_data_len >> 8); (*frame)[9] = (uint8_t)(frame_data_len); off = 10; } else if (data_len > 125) { (*frame)[1] = 126; (*frame)[2] = (uint8_t)(frame_data_len >> 8); (*frame)[3] = (uint8_t)(frame_data_len); off = 4; } else { (*frame)[1] = (uint8_t)frame_data_len; off = 2; } if (message->has_mask) { (*frame)[1] |= U_WEBSOCKET_MASK; // Append mask memcpy(*frame + off, message->mask, 4); off += 4; for (i=0; i < frame_data_len; i++) { (*frame)[off + i] = message->data[data_offset + i] ^ message->mask[i%4]; } } else { memcpy((*frame) + off, message->data + data_offset, frame_data_len); } ret = U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for *frame"); ret = U_ERROR_MEMORY; } } else { ret = U_ERROR_PARAMS; } return ret; } /** * Builds a struct _websocket_message using the given parameters * returns a newly allocated struct _websocket_message * returned value must be free'd after use */ static struct _websocket_message * ulfius_build_message (const uint8_t opcode, const short int has_mask, const char * data, const uint64_t data_len) { struct _websocket_message * new_message = NULL; if (( opcode == U_WEBSOCKET_OPCODE_TEXT || opcode == U_WEBSOCKET_OPCODE_BINARY || opcode == U_WEBSOCKET_OPCODE_CLOSE || opcode == U_WEBSOCKET_OPCODE_PING || opcode == U_WEBSOCKET_OPCODE_PONG ) && (data_len == 0 || data != NULL)) { new_message = o_malloc(sizeof(struct _websocket_message)); if (new_message != NULL) { if (data_len) { new_message->data = o_malloc(data_len*sizeof(char)); } else { new_message->data = NULL; } if (!data_len || new_message->data != NULL) { new_message->opcode = opcode; new_message->data_len = data_len; if (!has_mask) { new_message->has_mask = 0; memset(new_message->mask, 0, 4); } else { new_message->mask[0] = rand(); new_message->mask[1] = rand(); new_message->mask[2] = rand(); new_message->mask[3] = rand(); new_message->has_mask = 1; } if (data_len > 0) { memcpy(new_message->data, data, data_len); } time(&new_message->datestamp); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for new_message->data"); o_free(new_message); new_message = NULL; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for new_message"); } } return new_message; } /** * Builds a struct _websocket_message using the given parameters * Sends message to the websocket recipient in fragment if required * Then pushes the message in the outcoming message list * returns U_OK on success */ static int ulfius_send_websocket_message_managed(struct _websocket_manager * websocket_manager, const uint8_t opcode, const uint64_t data_len, const char * data, const uint64_t fragment_len) { size_t offset = 0, cur_len; struct _websocket_message * message; uint8_t * frame = NULL; size_t frame_len = 0; int ret = U_OK; if (data != NULL || data_len == 0) { if (pthread_mutex_lock(&websocket_manager->write_lock)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking write lock"); } else { message = ulfius_build_message(opcode, (websocket_manager->type == U_WEBSOCKET_CLIENT), data, data_len); if (message != NULL) { if (ulfius_push_websocket_message(websocket_manager->message_list_outcoming, message) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pushing new websocket message in list"); } while (offset < data_len) { cur_len = fragment_len<(data_len - offset)?fragment_len:(data_len - offset); if ((ret = ulfius_build_frame(message, offset, cur_len, &frame, &frame_len)) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_build_frame"); break; } else { ulfius_websocket_send_frame(websocket_manager, frame, frame_len); offset += cur_len; o_free(frame); frame = NULL; frame_len = 0; } } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_build_message"); ret = U_ERROR; } pthread_mutex_unlock(&websocket_manager->write_lock); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_send_websocket_message_managed params"); ret = U_ERROR_PARAMS; } return ret; } /** * Read and parse a new message from the websocket * Return the opcode of the new websocket, U_WEBSOCKET_OPCODE_NONE if no message arrived, or U_WEBSOCKET_OPCODE_ERROR on error * Sets the new message in the message variable */ static int ulfius_read_incoming_message(struct _websocket_manager * websocket_manager, struct _websocket_message ** message) { int ret = U_OK, fin = 0, i; uint8_t header[2] = {0}, payload_len[8] = {0}, masking_key[4] = {0}; uint8_t * payload_data = NULL; size_t msg_len = 0; ssize_t len = 0; *message = o_malloc(sizeof(struct _websocket_message)); if (*message != NULL) { (*message)->data_len = 0; (*message)->has_mask = 0; (*message)->data = NULL; time(&(*message)->datestamp); do { if (ret == U_OK) { // Read header if ((len = read_data_from_socket(websocket_manager, header, 2)) == 2) { (*message)->opcode = header[0] & 0x0F; fin = (header[0] & U_WEBSOCKET_BIT_FIN); if ((header[1] & U_WEBSOCKET_LEN_MASK) <= 125) { msg_len = (header[1] & U_WEBSOCKET_LEN_MASK); } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 126) { len = read_data_from_socket(websocket_manager, payload_len, 2); if (len == 2) { msg_len = payload_len[1] | ((uint64_t)payload_len[0] << 8); } else if (len >= 0) { ret = U_ERROR; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket message length"); } else { ret = U_ERROR_DISCONNECTED; } } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 127) { len = read_data_from_socket(websocket_manager, payload_len, 8); if (len == 8) { msg_len = payload_len[7] | ((uint64_t)payload_len[6] << 8) | ((uint64_t)payload_len[5] << 16) | ((uint64_t)payload_len[4] << 24) | ((uint64_t)payload_len[3] << 32) | ((uint64_t)payload_len[2] << 40) | ((uint64_t)payload_len[1] << 48) | ((uint64_t)payload_len[0] << 54); } else if (len >= 0) { ret = U_ERROR; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket message length"); } else { ret = U_ERROR_DISCONNECTED; } } } else if (len == 0) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket"); ret = U_ERROR; } else { ret = U_ERROR_DISCONNECTED; } if (ret == U_OK) { if (websocket_manager->type == U_WEBSOCKET_SERVER) { // Read mask if (header[1] & U_WEBSOCKET_MASK) { (*message)->has_mask = 1; len = read_data_from_socket(websocket_manager, masking_key, 4); if (len != 4 && len >= 0) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket for mask"); ret = U_ERROR; } else if (len < 0) { ret = U_ERROR_DISCONNECTED; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Incoming message has no MASK flag, exiting"); ret = U_ERROR; } } else { if ((header[1] & U_WEBSOCKET_MASK)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Incoming message has MASK flag while it should not, exiting"); ret = U_ERROR; } } } if (ret == U_OK) { payload_data = o_malloc(msg_len*sizeof(uint8_t)); len = read_data_from_socket(websocket_manager, payload_data, msg_len); if (len < 0) { ret = U_ERROR_DISCONNECTED; } else if ((unsigned int)len != msg_len) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket for payload_data"); ret = U_ERROR; } else { // If mask, decode message (*message)->data = o_realloc((*message)->data, (msg_len+(*message)->data_len)*sizeof(uint8_t)); if ((*message)->has_mask) { for (i = (*message)->data_len; (unsigned int)i < (*message)->data_len + msg_len; i++) { (*message)->data[i] = payload_data[i-(*message)->data_len] ^ masking_key[(i-(*message)->data_len)%4]; } } else { memcpy((*message)->data+(*message)->data_len, payload_data, msg_len); } (*message)->data_len += msg_len; } o_free(payload_data); } if (!fin) { while (!is_websocket_data_available(websocket_manager)); } } } while (ret == U_OK && !fin); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for *message"); } if (ret != U_OK) { ulfius_clear_websocket_message(*message); } return ret; } /** * Run the websocket manager in a separated detached thread */ void * ulfius_thread_websocket_manager_run(void * args) { struct _websocket * websocket = (struct _websocket *)args; if (websocket != NULL) { websocket->websocket_manager_callback(websocket->request, websocket->websocket_manager, websocket->websocket_manager_user_data); // Send close message if the websocket is still open if (websocket->websocket_manager->connected) { websocket->websocket_manager->close_flag = 1; } } return NULL; } /** * Run websocket in a separate thread * then sets a listening message loop * Complete the callback when the websocket is closed * The websocket can be closed by the client, the manager, the program, or on network disconnect */ void * ulfius_thread_websocket(void * data) { struct _websocket * websocket = (struct _websocket*)data; struct _websocket_message * message = NULL; pthread_t thread_websocket_manager; int thread_ret_websocket_manager = 1; if (websocket != NULL && websocket->websocket_manager != NULL) { if (websocket->websocket_manager_callback != NULL) { thread_ret_websocket_manager = pthread_create(&thread_websocket_manager, NULL, ulfius_thread_websocket_manager_run, (void *)websocket); if (thread_ret_websocket_manager) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error creating websocket manager thread, return code: %d", thread_ret_websocket_manager); websocket->websocket_manager->connected = 0; } } while (websocket->websocket_manager->connected) { message = NULL; if (websocket->websocket_manager->close_flag) { if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending close message on close_flag"); } websocket->websocket_manager->connected = 0; } else { if (is_websocket_data_available(websocket->websocket_manager)) { if (pthread_mutex_lock(&websocket->websocket_manager->read_lock)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking websocket read lock messages"); websocket->websocket_manager->connected = 0; } else { if (ulfius_read_incoming_message(websocket->websocket_manager, &message) == U_OK) { if (message->opcode == U_WEBSOCKET_OPCODE_CLOSE) { // Send close command back, then close the socket if (ulfius_send_websocket_message_managed(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL, 0) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending close command"); } websocket->websocket_manager->connected = 0; } else if (message->opcode == U_WEBSOCKET_OPCODE_PING) { // Send pong command if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_PONG, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending pong command"); websocket->websocket_manager->connected = 0; } } if (websocket->websocket_incoming_message_callback != NULL) { websocket->websocket_incoming_message_callback(websocket->request, websocket->websocket_manager, message, websocket->websocket_incoming_user_data); } if (ulfius_push_websocket_message(websocket->websocket_manager->message_list_incoming, message) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pushing new websocket message in list"); websocket->websocket_manager->connected = 0; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_read_incoming_message"); websocket->websocket_manager->connected = 0; } pthread_mutex_unlock(&websocket->websocket_manager->read_lock); } } } } // Wait for thread manager to close if exists if (!thread_ret_websocket_manager) { pthread_join(thread_websocket_manager, NULL); } // Call websocket_onclose_callback if set if (websocket->websocket_onclose_callback != NULL) { websocket->websocket_onclose_callback(websocket->request, websocket->websocket_manager, websocket->websocket_onclose_user_data); } if (ulfius_close_websocket(websocket) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error closing websocket"); } // Broadcast end signal if (websocket->websocket_manager->type == U_WEBSOCKET_CLIENT) { pthread_mutex_lock(&websocket->websocket_manager->status_lock); pthread_cond_broadcast(&websocket->websocket_manager->status_cond); pthread_mutex_unlock(&websocket->websocket_manager->status_lock); } else if (websocket->websocket_manager->type == U_WEBSOCKET_SERVER) { ulfius_clear_websocket(websocket); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error websocket parameters"); } pthread_exit(NULL); } /** * Read the next line in http response * Fill buffer until \r\n is read or buffer_len is reached * return U_OK on success */ static int ulfius_get_next_line_from_http_response(struct _websocket * websocket, char * buffer, size_t buffer_len, size_t * line_len) { size_t offset = 0; int eol = 0, ret = U_ERROR; uint8_t car; *line_len = 0; do { if (read_data_from_socket(websocket->websocket_manager, &car, 1) == 1) { buffer[offset] = car; } if (offset > 0 && buffer[offset-1] == '\r' && buffer[offset] == '\n') { eol = 1; buffer[offset-1] = '\0'; *line_len = offset - 1; ret = U_OK; } offset++; } while (!eol && offset < buffer_len); return ret; } /** * Sends the HTTP request and read the HTTP response * Verify if the response has the expexted paramters to open the * websocket * Return U_OK on success */ static int ulfius_websocket_connection_handshake(struct _u_request * request, struct yuarel * y_url, struct _websocket * websocket, struct _u_response * response) { int websocket_response_http = 0, i, ret, check_websocket = WEBSOCKET_RESPONSE_UPGRADE | WEBSOCKET_RESPONSE_CONNECTION | WEBSOCKET_RESPONSE_ACCEPT; unsigned int websocket_response = 0; char * http_line, ** split_line = NULL, * key, * value, * separator; char buffer[4096] = {0}; const char ** keys; size_t buffer_len = 4096, line_len; // Send HTTP Request http_line = msprintf("%s %s%s%s HTTP/%s\r\n", request->http_verb, o_strlen(y_url->path)?y_url->path:"/", y_url->query!=NULL?"?":"", y_url->query!=NULL?y_url->query:"", request->http_protocol); ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); o_free(http_line); http_line = msprintf("Host: %s\r\n", y_url->host); ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); o_free(http_line); http_line = msprintf("Upgrade: websocket\r\n"); ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); o_free(http_line); http_line = msprintf("Connection: Upgrade\r\n"); ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); o_free(http_line); http_line = msprintf("Origin: %s://%s\r\n", y_url->scheme, y_url->host); ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); o_free(http_line); keys = u_map_enum_keys(request->map_header); for (i=0; keys[i] != NULL; i++) { http_line = msprintf("%s: %s\r\n", keys[i], u_map_get(request->map_header, keys[i])); ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); o_free(http_line); if (0 == o_strcmp("Sec-WebSocket-Protocol", keys[i])) { check_websocket |= WEBSOCKET_RESPONSE_PROTCOL; } else if (0 == o_strcmp("Sec-WebSocket-Extension", keys[i])) { check_websocket |= WEBSOCKET_RESPONSE_EXTENSION; } } if (websocket->websocket_manager->tcp_sock >= 0) { // Send empty line const char * empty = "\r\n"; ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)empty, o_strlen(empty)); } // Read and parse response if (ulfius_get_next_line_from_http_response(websocket, buffer, buffer_len, &line_len) == U_OK) { if (split_string(buffer, " ", &split_line) >= 2 && 0 == o_strcmp(split_line[0], "HTTP/1.1") && 0 == o_strcmp(split_line[1], "101")) { websocket_response_http = 1; response->status = strtol(split_line[1], NULL, 10); response->protocol = o_strdup("1.1"); } free_string_array(split_line); } if (websocket_response_http) { do { if (ulfius_get_next_line_from_http_response(websocket, buffer, buffer_len, &line_len) == U_OK) { if (o_strlen(buffer) && (separator = o_strchr(buffer, ':')) != NULL) { key = o_strndup(buffer, (separator - buffer)); value = o_strdup(separator + 1); if (u_map_put(response->map_header, key, value) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error adding header %s:%s to the response structure", key, value); } if (0 == o_strcmp(buffer, "Upgrade: websocket")) { websocket_response |= WEBSOCKET_RESPONSE_UPGRADE; } else if (0 == o_strcmp(buffer, "Connection: Upgrade")) { websocket_response |= WEBSOCKET_RESPONSE_CONNECTION; } else if (0 == o_strcmp(key, "Sec-WebSocket-Protocol")) { websocket->websocket_manager->protocol = o_strdup(value); websocket_response |= WEBSOCKET_RESPONSE_PROTCOL; } else if (0 == o_strcmp(key, "Sec-WebSocket-Extension")) { websocket->websocket_manager->extensions = o_strdup(value); websocket_response |= WEBSOCKET_RESPONSE_EXTENSION; } else if (0 == o_strcmp(buffer, "Sec-WebSocket-Accept") && ulfius_check_handshake_response(u_map_get(request->map_header, "Sec-WebSocket-Key"), value) == U_OK) { websocket_response |= WEBSOCKET_RESPONSE_ACCEPT; } o_free(key); o_free(value); } else if (0 == o_strcmp(buffer, "")) { // Websocket HTTP response header complete break; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_get_next_line_from_http_response, abort parsing response"); close(websocket->websocket_manager->tcp_sock); websocket->websocket_manager->tcp_sock = -1; break; } } while (1); } if (!websocket_response_http || !(websocket_response & check_websocket) || response->status != 101) { if (u_map_has_key(response->map_header, "Content-Length")) { response->binary_body_length = strtol(u_map_get(response->map_header, "Content-Length"), NULL, 10); response->binary_body = o_malloc(response->binary_body_length); if (response->binary_body != NULL) { if (read_data_from_socket(websocket->websocket_manager, response->binary_body, response->binary_body_length) != (ssize_t)response->binary_body_length) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error read_data_from_socket for response->binary_body"); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for response->binary_body"); } } close(websocket->websocket_manager->tcp_sock); websocket->websocket_manager->tcp_sock = -1; ret = U_ERROR; } else { ret = U_OK; } return ret; } /** * Opens a websocket connection to the specified server * Returns U_OK on success */ static int ulfius_open_websocket(struct _u_request * request, struct yuarel * y_url, struct _websocket * websocket, struct _u_response * response) { int ret; struct sockaddr_in server; struct hostent * he; websocket->websocket_manager->tcp_sock = socket(AF_INET, SOCK_STREAM, 0); if (websocket->websocket_manager->tcp_sock != -1) { if ((he = gethostbyname(y_url->host)) != NULL) { memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); server.sin_family = AF_INET; server.sin_port = htons(y_url->port); if (connect(websocket->websocket_manager->tcp_sock, (struct sockaddr *)&server , sizeof(server)) >= 0) { websocket->websocket_manager->fds.fd = websocket->websocket_manager->tcp_sock; websocket->websocket_manager->connected = 1; websocket->websocket_manager->close_flag = 0; websocket->urh = NULL; websocket->instance = NULL; ret = ulfius_websocket_connection_handshake(request, y_url, websocket, response); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error connecting socket"); close(websocket->websocket_manager->tcp_sock); websocket->websocket_manager->tcp_sock = -1; ret = U_ERROR; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gethostbyname"); ret = U_ERROR; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error opening socket"); ret = U_ERROR; } return ret; } /** * Opens a websocket connection to the specified server using a tls socket * Returns U_OK on success */ static int ulfius_open_websocket_tls(struct _u_request * request, struct yuarel * y_url, struct _websocket * websocket, struct _u_response * response) { int ret; struct sockaddr_in server; struct hostent * he; gnutls_datum_t out; int type; unsigned status; if (gnutls_global_init() >= 0) { if (gnutls_certificate_allocate_credentials(&websocket->websocket_manager->xcred) >= 0 && gnutls_certificate_set_x509_system_trust(websocket->websocket_manager->xcred) >= 0 && gnutls_init(&websocket->websocket_manager->gnutls_session, GNUTLS_CLIENT) >= 0 && gnutls_server_name_set(websocket->websocket_manager->gnutls_session, GNUTLS_NAME_DNS, y_url->host, o_strlen(y_url->host)) >= 0 && gnutls_set_default_priority(websocket->websocket_manager->gnutls_session) >= 0 && gnutls_credentials_set(websocket->websocket_manager->gnutls_session, GNUTLS_CRD_CERTIFICATE, websocket->websocket_manager->xcred) >= 0) { if (request->check_server_certificate) { gnutls_session_set_verify_cert(websocket->websocket_manager->gnutls_session, y_url->host, 0); } websocket->websocket_manager->tcp_sock = socket(AF_INET, SOCK_STREAM, 0); if (websocket->websocket_manager->tcp_sock != -1) { if ((he = gethostbyname(y_url->host)) != NULL) { memset(&server, '\0', sizeof(server)); memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); server.sin_family = AF_INET; server.sin_port = htons(y_url->port); if (connect(websocket->websocket_manager->tcp_sock, (struct sockaddr *)&server , sizeof(server)) >= 0) { websocket->websocket_manager->fds.fd = websocket->websocket_manager->tcp_sock; websocket->websocket_manager->connected = 1; websocket->websocket_manager->close_flag = 0; websocket->urh = NULL; websocket->instance = NULL; gnutls_transport_set_int(websocket->websocket_manager->gnutls_session, websocket->websocket_manager->tcp_sock); gnutls_handshake_set_timeout(websocket->websocket_manager->gnutls_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); do { ret = gnutls_handshake(websocket->websocket_manager->gnutls_session); } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); if (ret < 0) { if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { /* check certificate verification status */ type = gnutls_certificate_type_get(websocket->websocket_manager->gnutls_session); status = gnutls_session_get_verify_cert_status(websocket->websocket_manager->gnutls_session); if (gnutls_certificate_verification_status_print(status, type, &out, 0) >= 0) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Certificate verify output: %s\n", out.data); gnutls_free(out.data); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_certificate_verification_status_print"); } } y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Handshake failed: %s\n", gnutls_strerror(ret)); ret = U_ERROR; } else { char * desc = gnutls_session_get_desc(websocket->websocket_manager->gnutls_session); gnutls_free(desc); ret = ulfius_websocket_connection_handshake(request, y_url, websocket, response); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error connecting socket"); close(websocket->websocket_manager->tcp_sock); websocket->websocket_manager->tcp_sock = -1; ret = U_ERROR; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gethostbyname"); ret = U_ERROR; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error opening socket"); ret = U_ERROR; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initialize gnutls routines"); ret = U_ERROR; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_global_init"); ret = U_ERROR; } return ret; } /** * Websocket callback function for MHD * Starts the websocket manager if set, */ void ulfius_start_websocket_cb (void * cls, struct MHD_Connection * connection, void * con_cls, const char * extra_in, size_t extra_in_size, MHD_socket sock, struct MHD_UpgradeResponseHandle * urh) { struct _websocket * websocket = (struct _websocket *)cls; pthread_t thread_websocket; int thread_ret_websocket = 0, thread_detach_websocket = 0; UNUSED(connection); UNUSED(con_cls); UNUSED(extra_in); UNUSED(extra_in_size); if (websocket != NULL) { websocket->urh = urh; // Run websocket manager in a thread websocket->websocket_manager->type = U_WEBSOCKET_SERVER; websocket->websocket_manager->mhd_sock = sock; websocket->websocket_manager->fds.fd = sock; websocket->websocket_manager->fds.events = POLLIN | POLLRDHUP; websocket->websocket_manager->connected = 1; websocket->websocket_manager->close_flag = 0; thread_ret_websocket = pthread_create(&thread_websocket, NULL, ulfius_thread_websocket, (void *)websocket); thread_detach_websocket = pthread_detach(thread_websocket); if (thread_ret_websocket || thread_detach_websocket) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error creating or detaching websocket manager thread, return code: %d, detach code: %d", thread_ret_websocket, thread_detach_websocket); ulfius_clear_websocket(websocket); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error websocket is NULL"); ulfius_clear_websocket(websocket); } return; } /** * Check if the response corresponds to the transformation of the key with the magic string */ int ulfius_check_handshake_response(const char * key, const char * response) { char websocket_accept[32] = {0}; if (key != NULL && response != NULL) { if (ulfius_generate_handshake_answer(key, websocket_accept) && 0 == o_strcmp(websocket_accept, response)) { return U_OK; } else { return U_ERROR; } } else { return U_ERROR_PARAMS; } } /** * Generates a handhshake answer from the key given in parameter */ int ulfius_generate_handshake_answer(const char * key, char * out_digest) { gnutls_datum_t key_data; unsigned char encoded_key[32] = {0}; size_t encoded_key_size = 32, encoded_key_size_base64; int res, to_return = 0; key_data.data = (unsigned char*)msprintf("%s%s", key, U_WEBSOCKET_MAGIC_STRING); key_data.size = o_strlen((const char *)key_data.data); if (key_data.data != NULL && out_digest != NULL && (res = gnutls_fingerprint(GNUTLS_DIG_SHA1, &key_data, encoded_key, &encoded_key_size)) == GNUTLS_E_SUCCESS) { if (o_base64_encode(encoded_key, encoded_key_size, (unsigned char *)out_digest, &encoded_key_size_base64)) { to_return = 1; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error base64 encoding hashed key"); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error getting sha1 signature for key"); } o_free(key_data.data); return to_return; } /** * Initialize a websocket message list * Return U_OK on success */ int ulfius_init_websocket_message_list(struct _websocket_message_list * message_list) { if (message_list != NULL) { message_list->len = 0; message_list->list = NULL; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Append a message in a message list * Return U_OK on success */ int ulfius_push_websocket_message(struct _websocket_message_list * message_list, struct _websocket_message * message) { if (message_list != NULL && message != NULL) { message_list->list = o_realloc(message_list->list, (message_list->len+1)*sizeof(struct _websocket_message *)); if (message_list->list != NULL) { message_list->list[message_list->len] = message; message_list->len++; return U_OK; } else { return U_ERROR_MEMORY; } } else { return U_ERROR_PARAMS; } } /** * Return a match list between two list of items * If match is NULL, then return source duplicate * Returned value must be u_free'd after use */ int ulfius_check_list_match(const char * source, const char * match, const char * separator, char ** result) { char ** source_list = NULL, ** match_list = NULL; int i, ret = U_OK; if (result != NULL) { *result = NULL; if (match == NULL) { *result = o_strdup(source); } else { if (source != NULL) { if (split_string(source, separator, &source_list) > 0 && split_string(match, separator, &match_list) > 0) { for (i=0; source_list[i] != NULL; i++) { if (string_array_has_trimmed_value((const char **)match_list, source_list[i])) { if (*result == NULL) { *result = o_strdup(trimwhitespace(source_list[i])); } else { char * tmp = msprintf("%s%s%s", *result, separator, trimwhitespace(source_list[i])); o_free(*result); *result = tmp; } } } free_string_array(source_list); free_string_array(match_list); } if (*result == NULL) { ret = U_ERROR; } } } } else { ret = U_ERROR_PARAMS; } return ret; } /** * Return the first match between two list of items * If match is NULL, then return the first element of source * Returned value must be u_free'd after use */ int ulfius_check_first_match(const char * source, const char * match, const char * separator, char ** result) { char ** source_list = NULL, ** match_list = NULL; int i, ret = U_OK; if (result != NULL) { *result = NULL; if (match == NULL) { if (source != NULL) { if (split_string(source, separator, &source_list) > 0) { *result = o_strdup(trimwhitespace(source_list[0])); } free_string_array(source_list); } } else { if (source != NULL) { if (split_string(source, separator, &source_list) > 0 && split_string(match, separator, &match_list) > 0) { for (i=0; source_list[i] != NULL && *result == NULL; i++) { if (string_array_has_trimmed_value((const char **)match_list, source_list[i])) { if (*result == NULL) { *result = o_strdup(trimwhitespace(source_list[i])); } } } free_string_array(source_list); free_string_array(match_list); } } if (*result == NULL) { ret = U_ERROR; } } } else { ret = U_ERROR_PARAMS; } return ret; } /** * Close the websocket */ int ulfius_close_websocket(struct _websocket * websocket) { if (websocket != NULL && websocket->websocket_manager != NULL) { if (websocket->websocket_manager->type == U_WEBSOCKET_CLIENT && websocket->websocket_manager->tls) { gnutls_bye(websocket->websocket_manager->gnutls_session, GNUTLS_SHUT_RDWR); gnutls_deinit(websocket->websocket_manager->gnutls_session); gnutls_certificate_free_credentials(websocket->websocket_manager->xcred); gnutls_global_deinit(); } if (websocket->websocket_manager->type == U_WEBSOCKET_CLIENT) { shutdown(websocket->websocket_manager->tcp_sock, SHUT_RDWR); close(websocket->websocket_manager->tcp_sock); } websocket->websocket_manager->connected = 0; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Add a websocket in the list of active websockets of the instance */ int ulfius_instance_add_websocket_active(struct _u_instance * instance, struct _websocket * websocket) { if (instance != NULL && websocket != NULL) { ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = o_realloc(((struct _websocket_handler *)instance->websocket_handler)->websocket_active, (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active+1)*sizeof(struct _websocket *)); if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active != NULL) { ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active] = websocket; ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active++; return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for instance->websocket_handler->websocket_active"); return U_ERROR_MEMORY; } } else { return U_ERROR_PARAMS; } } /** * Remove a websocket from the list of active websockets of the instance */ int ulfius_instance_remove_websocket_active(struct _u_instance * instance, struct _websocket * websocket) { size_t i, j; if (instance != NULL && ((struct _websocket_handler *)instance->websocket_handler)->websocket_active != NULL && websocket != NULL) { for (i=0; i<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active; i++) { if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active[i] == websocket) { if (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active > 1) { for (j=i; j<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active-1; j++) { ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j] = ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j+1]; } ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = o_realloc(((struct _websocket_handler *)instance->websocket_handler)->websocket_active, (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active-1)*sizeof(struct _websocket *)); if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for instance->websocket_active"); return U_ERROR_MEMORY; } } else { o_free(((struct _websocket_handler *)instance->websocket_handler)->websocket_active); ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = NULL; } ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active--; pthread_mutex_lock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock); pthread_cond_broadcast(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_cond); pthread_mutex_unlock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock); return U_OK; } } return U_ERROR_NOT_FOUND; } else { return U_ERROR_PARAMS; } } /********************************/ /** Common websocket functions **/ /********************************/ /** * Send a fragmented message in the websocket * each fragment size will be at most fragment_len * Return U_OK on success */ int ulfius_websocket_send_fragmented_message(struct _websocket_manager * websocket_manager, const uint8_t opcode, const uint64_t data_len, const char * data, const uint64_t fragment_len) { int ret = U_OK, ret_message, count = WEBSOCKET_MAX_CLOSE_TRY; struct _websocket_message * message; if (websocket_manager != NULL && websocket_manager->connected) { if (opcode == U_WEBSOCKET_OPCODE_CLOSE) { if (ulfius_send_websocket_message_managed(websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL, 0) == U_OK) { // If message sent is U_WEBSOCKET_OPCODE_CLOSE, wait for the close response for WEBSOCKET_MAX_CLOSE_TRY messages max, then close the connection do { if (is_websocket_data_available(websocket_manager)) { message = NULL; ret_message = ulfius_read_incoming_message(websocket_manager, &message); if (ret_message == U_OK && message != NULL) { if (message->opcode == U_WEBSOCKET_OPCODE_CLOSE) { websocket_manager->connected = 0; } if (ulfius_push_websocket_message(websocket_manager->message_list_incoming, message) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pushing new websocket message in list"); } } else { websocket_manager->connected = 0; } } } while (websocket_manager->connected && (count-- > 0)); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending U_WEBSOCKET_OPCODE_CLOSE message"); } websocket_manager->connected = 0; } else { ret = ulfius_send_websocket_message_managed(websocket_manager, opcode, data_len, data, fragment_len); } } else { ret = U_ERROR_PARAMS; } return ret; } /** * Send a message in the websocket * Return U_OK on success */ int ulfius_websocket_send_message(struct _websocket_manager * websocket_manager, const uint8_t opcode, const uint64_t data_len, const char * data) { return ulfius_websocket_send_fragmented_message(websocket_manager, opcode, data_len, data, data_len); } /** * Return the first message of the message list * Return NULL if message_list has no message * Returned value must be cleared after use */ struct _websocket_message * ulfius_websocket_pop_first_message(struct _websocket_message_list * message_list) { size_t len; struct _websocket_message * message = NULL; if (message_list != NULL && message_list->len > 0) { message = message_list->list[0]; for (len=0; len < message_list->len-1; len++) { message_list->list[len] = message_list->list[len+1]; } if (message_list->len > 1) { message_list->list = o_realloc(message_list->list, (message_list->len-1)); } else { o_free(message_list->list); message_list->list = NULL; } message_list->len--; } return message; } /** * Clear data of a websocket message */ void ulfius_clear_websocket_message(struct _websocket_message * message) { if (message != NULL) { o_free(message->data); o_free(message); } } /************************************/ /** Init/clear websocket functions **/ /************************************/ /** * Clear all data related to the websocket */ int ulfius_clear_websocket(struct _websocket * websocket) { if (websocket != NULL) { if (websocket->websocket_manager->type == U_WEBSOCKET_SERVER && MHD_upgrade_action (websocket->urh, MHD_UPGRADE_ACTION_CLOSE) != MHD_YES) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending MHD_UPGRADE_ACTION_CLOSE frame to urh"); } ulfius_instance_remove_websocket_active(websocket->instance, websocket); ulfius_clean_request_full(websocket->request); websocket->request = NULL; ulfius_clear_websocket_manager(websocket->websocket_manager); o_free(websocket->websocket_manager); websocket->websocket_manager = NULL; o_free(websocket); return U_OK; } else { return U_ERROR_PARAMS; } } /** * Clear data of a websocket message list */ void ulfius_clear_websocket_message_list(struct _websocket_message_list * message_list) { size_t i; if (message_list != NULL) { for (i=0; i < message_list->len; i++) { ulfius_clear_websocket_message(message_list->list[i]); } o_free(message_list->list); } } /** * Initialize a struct _websocket * return U_OK on success */ int ulfius_init_websocket(struct _websocket * websocket) { if (websocket != NULL) { websocket->instance = NULL; websocket->request = NULL; websocket->websocket_manager_callback = NULL; websocket->websocket_manager_user_data = NULL; websocket->websocket_incoming_message_callback = NULL; websocket->websocket_incoming_user_data = NULL; websocket->websocket_onclose_callback = NULL; websocket->websocket_onclose_user_data = NULL; websocket->websocket_manager = o_malloc(sizeof(struct _websocket_manager)); websocket->urh = NULL; if (websocket->websocket_manager == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for websocket_manager"); return U_ERROR_MEMORY; } else { websocket->websocket_manager->tls = 0; if (ulfius_init_websocket_manager(websocket->websocket_manager) != U_OK) { o_free(websocket->websocket_manager); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_init_websocket_manager"); return U_ERROR; } else { return U_OK; } } } else { return U_ERROR_PARAMS; } } /** * Initialize a struct _websocket_manager * return U_OK on success */ int ulfius_init_websocket_manager(struct _websocket_manager * websocket_manager) { pthread_mutexattr_t mutexattr; int ret = U_OK; if (websocket_manager != NULL) { websocket_manager->connected = 0; websocket_manager->close_flag = 0; websocket_manager->mhd_sock = 0; websocket_manager->tcp_sock = 0; websocket_manager->protocol = NULL; websocket_manager->extensions = NULL; pthread_mutexattr_init ( &mutexattr ); pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE ); if (pthread_mutex_init(&(websocket_manager->read_lock), &mutexattr) != 0 || pthread_mutex_init(&(websocket_manager->write_lock), &mutexattr) != 0) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Impossible to initialize Mutex Lock for websocket"); ret = U_ERROR; } else if (pthread_mutex_init(&websocket_manager->status_lock, NULL) || pthread_cond_init(&websocket_manager->status_cond, NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing status_lock or status_cond"); ret = U_ERROR; } else if ((websocket_manager->message_list_incoming = o_malloc(sizeof(struct _websocket_message_list))) == NULL || ulfius_init_websocket_message_list(websocket_manager->message_list_incoming) != U_OK || (websocket_manager->message_list_outcoming = o_malloc(sizeof(struct _websocket_message_list))) == NULL || ulfius_init_websocket_message_list(websocket_manager->message_list_outcoming) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing message_list_incoming or message_list_outcoming"); ret = U_ERROR_MEMORY; } websocket_manager->fds.events = POLLIN | POLLRDHUP; websocket_manager->type = U_WEBSOCKET_NONE; if (ret != U_OK) { o_free(websocket_manager->message_list_incoming); o_free(websocket_manager->message_list_outcoming); } pthread_mutexattr_destroy(&mutexattr); } else { ret = U_ERROR_PARAMS; } return ret; } /** * Clear data of a websocket_manager */ void ulfius_clear_websocket_manager(struct _websocket_manager * websocket_manager) { if (websocket_manager != NULL) { pthread_mutex_destroy(&websocket_manager->read_lock); pthread_mutex_destroy(&websocket_manager->write_lock); ulfius_clear_websocket_message_list(websocket_manager->message_list_incoming); o_free(websocket_manager->message_list_incoming); websocket_manager->message_list_incoming = NULL; ulfius_clear_websocket_message_list(websocket_manager->message_list_outcoming); o_free(websocket_manager->message_list_outcoming); websocket_manager->message_list_outcoming = NULL; o_free(websocket_manager->protocol); o_free(websocket_manager->extensions); } } /********************************/ /** Server websocket functions **/ /********************************/ /** * Set a websocket in the response * You must set at least websocket_manager_callback or websocket_incoming_message_callback * @Parameters * response: struct _u_response to send back the websocket initialization, mandatory * websocket_protocol: list of protocols, separated by a comma, or NULL if all protocols are accepted * websocket_extensions: list of extensions, separated by a comma, or NULL if all extensions are accepted * websocket_manager_callback: callback function called right after the handshake acceptance, optional * websocket_manager_user_data: any data that will be given to the websocket_manager_callback, optional * websocket_incoming_message_callback: callback function called on each incoming complete message, optional * websocket_incoming_user_data: any data that will be given to the websocket_incoming_message_callback, optional * websocket_onclose_callback: callback function called right before closing the websocket, must be complete for the websocket to close * websocket_onclose_user_data: any data that will be given to the websocket_onclose_callback, optional * @Return value: U_OK on success */ int ulfius_set_websocket_response(struct _u_response * response, const char * websocket_protocol, const char * websocket_extensions, void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data), void * websocket_manager_user_data, void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data), void * websocket_incoming_user_data, void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data), void * websocket_onclose_user_data) { if (response != NULL && (websocket_manager_callback != NULL || websocket_incoming_message_callback)) { if (((struct _websocket_handle *)response->websocket_handle)->websocket_protocol != NULL) { o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); } ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol = o_strdup(websocket_protocol); if (((struct _websocket_handle *)response->websocket_handle)->websocket_protocol == NULL && websocket_protocol != NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for response->websocket_protocol"); return U_ERROR_MEMORY; } if (((struct _websocket_handle *)response->websocket_handle)->websocket_extensions != NULL) { o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_extensions); } ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions = o_strdup(websocket_extensions); if (((struct _websocket_handle *)response->websocket_handle)->websocket_extensions == NULL && websocket_extensions != NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for response->websocket_extensions"); o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); return U_ERROR_MEMORY; } ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback = websocket_manager_callback; ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data = websocket_manager_user_data; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback = websocket_incoming_message_callback; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = websocket_incoming_user_data; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = websocket_onclose_callback; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data = websocket_onclose_user_data; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Sets the websocket in closing mode * The websocket will not necessarily be closed at the return of this function, * it will process through the end of the `websocket_manager_callback` * and the `websocket_onclose_callback` calls first. * return U_OK on success * or U_ERROR on error */ int ulfius_websocket_send_close_signal(struct _websocket_manager * websocket_manager) { if (websocket_manager != NULL) { websocket_manager->close_flag = 1; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Returns the status of the websocket connection * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_status(struct _websocket_manager * websocket_manager) { if (websocket_manager != NULL) { return websocket_manager->connected?U_WEBSOCKET_STATUS_OPEN:U_WEBSOCKET_STATUS_CLOSE; } else { return U_WEBSOCKET_STATUS_ERROR; } } /** * Wait until the websocket connection is closed or the timeout in milliseconds is reached * if timeout is 0, no timeout is set * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_wait_close(struct _websocket_manager * websocket_manager, unsigned int timeout) { struct timespec abstime; int ret; if (websocket_manager != NULL) { if (websocket_manager->connected) { if (timeout) { clock_gettime(CLOCK_REALTIME, &abstime); abstime.tv_nsec += ((timeout%1000) * 1000000); abstime.tv_sec += (timeout / 1000); pthread_mutex_lock(&websocket_manager->status_lock); ret = pthread_cond_timedwait(&websocket_manager->status_cond, &websocket_manager->status_lock, &abstime); pthread_mutex_unlock(&websocket_manager->status_lock); return ((ret == ETIMEDOUT && websocket_manager->connected)?U_WEBSOCKET_STATUS_OPEN:U_WEBSOCKET_STATUS_CLOSE); } else { pthread_mutex_lock(&websocket_manager->status_lock); pthread_cond_wait(&websocket_manager->status_cond, &websocket_manager->status_lock); pthread_mutex_unlock(&websocket_manager->status_lock); return U_WEBSOCKET_STATUS_CLOSE; } } else { return U_WEBSOCKET_STATUS_CLOSE; } } else { return U_WEBSOCKET_STATUS_ERROR; } } /********************************/ /** Client websocket functions **/ /********************************/ static long random_at_most(long max) { unsigned long // max <= RAND_MAX < ULONG_MAX, so this is okay. num_bins = (unsigned long) max + 1, num_rand = (unsigned long) RAND_MAX + 1, bin_size = num_rand / num_bins, defect = num_rand % num_bins; long x; do { x = random(); } // This is carefully written not to overflow while (num_rand - defect <= (unsigned long)x); // Truncated division is intentional return x/bin_size; } /** * Generates a random string and store it in str */ static char * rand_string(char * str, size_t str_size) { const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; size_t n; if (str_size > 0 && str != NULL) { for (n = 0; n < str_size; n++) { long key = random_at_most((sizeof(charset)) - 2); str[n] = charset[key]; } str[str_size] = '\0'; return str; } else { return NULL; } } /** * Set values for a struct _u_request to open a websocket * request must be previously initialized * Return U_OK on success */ int ulfius_set_websocket_request(struct _u_request * request, const char * url, const char * websocket_protocol, const char * websocket_extensions) { int ret; char rand_str[17] = {0}, rand_str_base64[25] = {0}; size_t out_len; if (request != NULL && url != NULL) { o_free(request->http_protocol); o_free(request->http_verb); o_free(request->http_url); request->http_protocol = o_strdup("1.1"); request->http_verb = o_strdup("GET"); request->http_url = o_strdup(url); if (websocket_protocol != NULL) { u_map_put(request->map_header, "Sec-WebSocket-Protocol", websocket_protocol); } if (websocket_extensions != NULL) { u_map_put(request->map_header, "Sec-WebSocket-Extensions", websocket_extensions); } u_map_put(request->map_header, "Sec-WebSocket-Version", "13"); u_map_put(request->map_header, "Upgrade", "websocket"); u_map_put(request->map_header, "Connection", "Upgrade"); u_map_put(request->map_header, "Content-Length", "0"); u_map_put(request->map_header, "User-Agent", U_WEBSOCKET_USER_AGENT "/" STR(ULFIUS_VERSION)); srand(time(NULL)); rand_string(rand_str, 16); if (!o_base64_encode((unsigned char *)rand_str, 16, (unsigned char *)rand_str_base64, &out_len)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error o_base64_encode with the input string %s", rand_str); ret = U_ERROR; } else { u_map_put(request->map_header, "Sec-WebSocket-Key", rand_str_base64); ret = U_OK; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_set_websocket_request input parameters"); ret = U_ERROR; } return ret; } /** * Open a websocket client connection * Return U_OK on success */ int ulfius_open_websocket_client_connection(struct _u_request * request, void (* websocket_manager_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data), void * websocket_manager_user_data, void (* websocket_incoming_message_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data), void * websocket_incoming_user_data, void (* websocket_onclose_callback) (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data), void * websocket_onclose_user_data, struct _websocket_client_handler * websocket_client_handler, struct _u_response * response) { int ret; struct yuarel y_url; char * url, * basic_auth_encoded_header, * basic_auth, * basic_auth_encoded; size_t basic_auth_encoded_len; struct _websocket * websocket; pthread_t thread_websocket; int thread_ret_websocket = 0, thread_detach_websocket = 0; if (request != NULL && response != NULL && (websocket_manager_callback != NULL || websocket_incoming_message_callback != NULL)) { url = o_strdup(request->http_url); if (!yuarel_parse(&y_url, url)) { if (0 == o_strcasecmp("http", y_url.scheme) || 0 == o_strcasecmp("https", y_url.scheme) || 0 == o_strcasecmp("ws", y_url.scheme) || 0 == o_strcasecmp("wss", y_url.scheme)) { if (!y_url.port) { if (0 == o_strcasecmp("http", y_url.scheme) || 0 == o_strcasecmp("ws", y_url.scheme)) { y_url.port = 80; } else { y_url.port = 443; } } if (y_url.username != NULL && y_url.password != NULL) { basic_auth = msprintf("%s:%s", y_url.username, y_url.password); basic_auth_encoded = o_malloc((o_strlen(basic_auth)*4/3)+1); memset(basic_auth_encoded, 0, (o_strlen(basic_auth)*4/3)+1); if (o_base64_encode((const unsigned char *)basic_auth, o_strlen(basic_auth), (unsigned char *)basic_auth_encoded, &basic_auth_encoded_len)) { basic_auth_encoded_header = msprintf("Basic: %s", basic_auth_encoded); u_map_remove_from_key(request->map_header, "Authorization"); u_map_put(request->map_header, "Authorization", basic_auth_encoded_header); o_free(basic_auth_encoded_header); o_free(basic_auth_encoded); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error o_base64_encode"); } o_free(basic_auth); } websocket = o_malloc(sizeof(struct _websocket)); if (websocket != NULL && ulfius_init_websocket(websocket) == U_OK) { websocket->request = ulfius_duplicate_request(request); websocket->websocket_manager->type = U_WEBSOCKET_CLIENT; websocket->websocket_manager_callback = websocket_manager_callback; websocket->websocket_manager_user_data = websocket_manager_user_data; websocket->websocket_incoming_message_callback = websocket_incoming_message_callback; websocket->websocket_incoming_user_data = websocket_incoming_user_data; websocket->websocket_onclose_callback = websocket_onclose_callback; websocket->websocket_onclose_user_data = websocket_onclose_user_data; // Open connection if (0 == o_strcasecmp("http", y_url.scheme) || 0 == o_strcasecmp("ws", y_url.scheme)) { websocket->websocket_manager->tls = 0; if (ulfius_open_websocket(request, &y_url, websocket, response) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_open_websocket"); ulfius_clear_websocket(websocket); ret = U_ERROR; } else { ret = U_OK; } } else { websocket->websocket_manager->tls = 1; if (ulfius_open_websocket_tls(request, &y_url, websocket, response) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_open_websocket_tls"); ulfius_clear_websocket(websocket); ret = U_ERROR; } else { ret = U_OK; } } if (ret == U_OK) { thread_ret_websocket = pthread_create(&thread_websocket, NULL, ulfius_thread_websocket, (void *)websocket); thread_detach_websocket = pthread_detach(thread_websocket); if (thread_ret_websocket || thread_detach_websocket) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error creating or detaching websocket manager thread, return code: %d, detach code: %d", thread_ret_websocket, thread_detach_websocket); ulfius_clear_websocket(websocket); ret = U_ERROR; } websocket_client_handler->websocket = websocket; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for websocket"); ret = U_ERROR_MEMORY; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Unknown scheme, please use one of the following: 'http', 'https', 'ws', 'wss'"); ret = U_ERROR_PARAMS; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error parsing url"); ret = U_ERROR_PARAMS; } o_free(url); } else { ret = U_ERROR_PARAMS; } return ret; } /** * Send a close signal to the websocket * return U_OK when the signal is sent * or U_ERROR on error */ int ulfius_websocket_client_connection_send_close_signal(struct _websocket_client_handler * websocket_client_handler) { if (websocket_client_handler != NULL) { return ulfius_websocket_send_close_signal(websocket_client_handler->websocket->websocket_manager); } else { return U_ERROR_PARAMS; } } /** * Closes a websocket client connection * return U_OK when the websocket is closed * or U_ERROR on error */ int ulfius_websocket_client_connection_close(struct _websocket_client_handler * websocket_client_handler) { if (websocket_client_handler != NULL) { if (ulfius_websocket_send_close_signal(websocket_client_handler->websocket->websocket_manager) == U_OK) { if (ulfius_websocket_wait_close(websocket_client_handler->websocket->websocket_manager, 0) != U_WEBSOCKET_STATUS_CLOSE) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_send_close_signal"); return U_ERROR; } ulfius_clear_websocket(websocket_client_handler->websocket); return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_send_close_signal"); return U_ERROR; } } else { return U_ERROR_PARAMS; } } /** * Returns the status of the websocket client connection * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_client_connection_status(struct _websocket_client_handler * websocket_client_handler) { if (websocket_client_handler != NULL) { return ulfius_websocket_status(websocket_client_handler->websocket->websocket_manager); } else { return U_WEBSOCKET_STATUS_ERROR; } } /** * Wait until the websocket client connection is closed or the timeout in milliseconds is reached * if timeout is 0, no timeout is set * Returned values can be U_WEBSOCKET_STATUS_OPEN or U_WEBSOCKET_STATUS_CLOSE * wether the websocket is open or closed, or U_WEBSOCKET_STATUS_ERROR on error */ int ulfius_websocket_client_connection_wait_close(struct _websocket_client_handler * websocket_client_handler, unsigned int timeout) { if (websocket_client_handler != NULL) { return ulfius_websocket_wait_close(websocket_client_handler->websocket->websocket_manager, timeout); } else { return U_WEBSOCKET_STATUS_ERROR; } } #endif ulfius-2.5.2/src/ulfius.c000066400000000000000000002063421341017022300152710ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * ulfius.c: framework functions definitions * * Copyright 2015-2018 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this library. If not, see . * */ #include #include #include #include "u_private.h" #include "ulfius.h" /** Define mock yder functions when yder is disabled **/ #ifdef U_DISABLE_YDER int y_init_logs(const char * app, const unsigned long init_mode, const unsigned long init_level, const char * init_log_file, const char * message) { (void)(app); (void)(init_mode); (void)(init_level); (void)(init_log_file); (void)(message); return 1; } int y_set_logs_callback(void (* y_callback_log_message) (void * cls, const char * app_name, const time_t date, const unsigned long level, const char * message), void * cls, const char * message) { (void)(y_callback_log_message); (void)(cls); (void)(message); return 1; } void y_log_message(const unsigned long type, const char * message, ...) { (void)(type); (void)(message); } int y_close_logs() { return 1; } #endif /** * Fill a map with the key/values specified */ static int ulfius_fill_map_check_utf8(void * cls, enum MHD_ValueKind kind, const char * key, const char * value) { char * tmp; int res; UNUSED(kind); if (cls == NULL || key == NULL) { // Invalid parameters y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid parameters for ulfius_fill_map_check_utf8"); return MHD_NO; } else if (utf8_check(key) == NULL && (value == NULL || utf8_check(value) == NULL)) { if (u_map_get(((struct _u_map *)cls), key) != NULL) { // u_map already has a value with this this key, appending value separated with a comma ',') tmp = msprintf("%s,%s", u_map_get(((struct _u_map *)cls), key), (value==NULL?"":value)); res = u_map_put(((struct _u_map *)cls), key, tmp); o_free(tmp); if (res == U_OK) { return MHD_YES; } else { return MHD_NO; } } else if (u_map_put(((struct _u_map *)cls), key, (value==NULL?"":value)) == U_OK) { return MHD_YES; } else { return MHD_NO; } } else { return MHD_YES; } } /** * Fill a map with the key/values specified */ static int ulfius_fill_map(void * cls, enum MHD_ValueKind kind, const char * key, const char * value) { char * tmp; int res; UNUSED(kind); if (cls == NULL || key == NULL) { // Invalid parameters y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid parameters for ulfius_fill_map"); return MHD_NO; } else if (u_map_get(((struct _u_map *)cls), key) != NULL) { // u_map already has a value with this this key, appending value separated with a comma ',') tmp = msprintf("%s,%s", u_map_get(((struct _u_map *)cls), key), (value==NULL?"":value)); res = u_map_put(((struct _u_map *)cls), key, tmp); o_free(tmp); if (res == U_OK) { return MHD_YES; } else { return MHD_NO; } } else if (u_map_put(((struct _u_map *)cls), key, (value==NULL?"":value)) == U_OK) { return MHD_YES; } else { return MHD_NO; } } /** * ulfius_is_valid_endpoint * return true if the endpoind has valid parameters */ static int ulfius_is_valid_endpoint(const struct _u_endpoint * endpoint, int to_delete) { if (endpoint != NULL) { if (ulfius_equals_endpoints(endpoint, ulfius_empty_endpoint())) { // Should be the last endpoint of the list to close it return 1; } else if (endpoint->http_method == NULL) { return 0; } else if (!to_delete && endpoint->callback_function == NULL) { return 0; } else if (endpoint->url_prefix == NULL && endpoint->url_format == NULL) { return 0; } else { return 1; } } else { return 0; } } /** * ulfius_validate_endpoint_list * return true if endpoint_list has valid parameters */ static int ulfius_validate_endpoint_list(const struct _u_endpoint * endpoint_list, int nb_endpoints) { int i; if (endpoint_list != NULL) { for (i=0; i < nb_endpoints; i++) { if (i == 0 && ulfius_equals_endpoints(ulfius_empty_endpoint(), &endpoint_list[i])) { // One can not have an empty endpoint in the beginning of the list y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, no empty endpoint allowed in the beginning of the endpoint list"); return U_ERROR_PARAMS; } else if (!ulfius_is_valid_endpoint(&endpoint_list[i], 0)) { // One must set at least the parameters http_method, url_format and callback_function y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, endpoint at index %d has invalid parameters", i); return U_ERROR_PARAMS; } } return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, no endpoint list"); return U_ERROR_PARAMS; } } /** * ulfius_validate_instance * return true if u_instance has valid parameters */ static int ulfius_validate_instance(const struct _u_instance * u_instance) { if (u_instance == NULL || u_instance->port <= 0 || u_instance->port >= 65536 || ulfius_validate_endpoint_list(u_instance->endpoint_list, u_instance->nb_endpoints) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, instance or has invalid parameters"); return U_ERROR_PARAMS; } return U_OK; } /** * Internal method used to duplicate the full url before it's manipulated and modified by MHD */ static void * ulfius_uri_logger (void * cls, const char * uri) { struct connection_info_struct * con_info = o_malloc (sizeof (struct connection_info_struct)); UNUSED(cls); if (con_info != NULL) { con_info->callback_first_iteration = 1; con_info->u_instance = NULL; u_map_init(&con_info->map_url_initial); con_info->request = o_malloc(sizeof(struct _u_request)); if (con_info->request == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info->request"); o_free(con_info); return NULL; } if (NULL == con_info->request || ulfius_init_request(con_info->request) != U_OK) { ulfius_clean_request_full(con_info->request); o_free(con_info); return NULL; } con_info->request->http_url = o_strdup(uri); if (con_info->request->http_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info->request->http_url"); ulfius_clean_request_full(con_info->request); o_free(con_info); return NULL; } con_info->max_post_param_size = 0; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info"); } return con_info; } /** * ulfius_get_body_from_response * Extract the body data from the response if any * Copy it in newly allocated response_buffer and set the size in response_buffer_len * return U_OK on success */ static int ulfius_get_body_from_response(struct _u_response * response, void ** response_buffer, size_t * response_buffer_len) { if (response == NULL || response_buffer == NULL || response_buffer_len == NULL) { return U_ERROR_PARAMS; } else { if (response->binary_body != NULL && response->binary_body_length > 0) { // The user sent a binary response *response_buffer = o_malloc(response->binary_body_length); if (*response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response->binary_body = o_strdup(ULFIUS_HTTP_ERROR_BODY); response->binary_body_length = o_strlen(ULFIUS_HTTP_ERROR_BODY); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } } else { memcpy(*response_buffer, response->binary_body, response->binary_body_length); *response_buffer_len = response->binary_body_length; } } else { *response_buffer = NULL; *response_buffer_len = 0; } return U_OK; } } /** * mhd_request_completed * function used to clean data allocated after a web call is complete */ static void mhd_request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { struct connection_info_struct *con_info = *con_cls; UNUSED(toe); UNUSED(connection); UNUSED(cls); if (NULL == con_info) { return; } if (con_info->has_post_processor && con_info->post_processor != NULL) { MHD_destroy_post_processor (con_info->post_processor); } ulfius_clean_request_full(con_info->request); u_map_clean(&con_info->map_url_initial); con_info->request = NULL; o_free(con_info); con_info = NULL; *con_cls = NULL; } /** * mhd_iterate_post_data * function used to iterate post parameters * if a parameter is larger than max_post_param_size, truncate it * return MHD_NO on error */ static int mhd_iterate_post_data (void * coninfo_cls, enum MHD_ValueKind kind, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size) { struct connection_info_struct * con_info = coninfo_cls; size_t cur_size = size; char * data_dup, * filename_param; UNUSED(kind); if (filename != NULL && con_info->u_instance != NULL && con_info->u_instance->file_upload_callback != NULL) { if (con_info->u_instance->file_upload_callback(con_info->request, key, filename, content_type, transfer_encoding, data, off, size, con_info->u_instance->file_upload_cls) == U_OK) { return MHD_YES; } else { return MHD_NO; } } else { if (con_info->u_instance) { if (con_info->u_instance->check_utf8 && (utf8_check(key) != NULL || data == NULL || utf8_check(data) != NULL || (filename != NULL && utf8_check(filename) != NULL))) { return MHD_YES; } else { data_dup = o_strndup(data, size); // Force value to end with a NULL character if (con_info->max_post_param_size > 0) { if (off > con_info->max_post_param_size) { return MHD_YES; } else if (off + size > con_info->max_post_param_size) { cur_size = con_info->max_post_param_size - off; } } } } else { return MHD_NO; } if (filename != NULL) { filename_param = msprintf("%s_filename", key); if (u_map_put((struct _u_map *)con_info->request->map_post_body, filename_param, filename) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_put filename value"); } o_free(filename_param); } if (cur_size > 0 && data_dup != NULL && u_map_put_binary((struct _u_map *)con_info->request->map_post_body, key, data_dup, off, cur_size + 1) == U_OK) { o_free(data_dup); return MHD_YES; } else { o_free(data_dup); return MHD_NO; } } } /** * ulfius_webservice_dispatcher * function executed by libmicrohttpd every time an HTTP call is made * return MHD_NO on error */ static int ulfius_webservice_dispatcher (void * cls, struct MHD_Connection * connection, const char * url, const char * method, const char * version, const char * upload_data, size_t * upload_data_size, void ** con_cls) { struct _u_endpoint * endpoint_list = ((struct _u_instance *)cls)->endpoint_list, ** current_endpoint_list = NULL, * current_endpoint = NULL; struct connection_info_struct * con_info = * con_cls; int mhd_ret = MHD_NO, callback_ret = U_OK, i, close_loop = 0, inner_error = U_OK, mhd_response_flag; #ifndef U_DISABLE_WEBSOCKET // Websocket variables int upgrade_protocol = 0; char * protocol = NULL, * extension = NULL; // Client certificate authentication variables const union MHD_ConnectionInfo * ci; unsigned int listsize; const gnutls_datum_t * pcert; gnutls_certificate_status_t client_cert_status; int ret_cert; #endif char * content_type, * auth_realm = NULL; struct _u_response * response = NULL; struct sockaddr * so_client; void * response_buffer = NULL; size_t response_buffer_len = 0; // Response variables struct MHD_Response * mhd_response = NULL; // Prepare for POST or PUT input data // Initialize the input maps if (con_info == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error con_info is NULL"); return MHD_NO; } if (con_info->u_instance == NULL) { con_info->u_instance = (struct _u_instance *)cls; } if (con_info->callback_first_iteration) { #ifndef U_DISABLE_WEBSOCKET ci = MHD_get_connection_info (connection, MHD_CONNECTION_INFO_GNUTLS_SESSION); if (((struct _u_instance *)cls)->use_client_cert_auth && ci != NULL && ci->tls_session != NULL) { if ((ret_cert = gnutls_certificate_verify_peers2(ci->tls_session, &client_cert_status)) != 0 && ret_cert != GNUTLS_E_NO_CERTIFICATE_FOUND) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error gnutls_certificate_verify_peers2"); } else if (!ret_cert) { pcert = gnutls_certificate_get_peers(ci->tls_session, &listsize); if ((pcert == NULL) || (listsize == 0)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Failed to retrieve client certificate chain"); } else if (gnutls_x509_crt_init(&(con_info->request->client_cert))) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Failed to initialize client certificate"); } else if (gnutls_x509_crt_import(con_info->request->client_cert, &pcert[0], GNUTLS_X509_FMT_DER)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Failed to import client certificate"); gnutls_x509_crt_deinit(con_info->request->client_cert); } } } #endif con_info->callback_first_iteration = 0; so_client = MHD_get_connection_info (connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS)->client_addr; con_info->has_post_processor = 0; con_info->max_post_param_size = ((struct _u_instance *)cls)->max_post_param_size; con_info->request->http_protocol = o_strdup(version); con_info->request->http_verb = o_strdup(method); con_info->request->client_address = o_malloc(sizeof(struct sockaddr)); if (con_info->request->client_address == NULL || con_info->request->http_verb == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating client_address or http_verb"); return MHD_NO; } memcpy(con_info->request->client_address, so_client, sizeof(struct sockaddr)); if (con_info->u_instance->check_utf8) { MHD_get_connection_values (connection, MHD_HEADER_KIND, ulfius_fill_map_check_utf8, con_info->request->map_header); MHD_get_connection_values (connection, MHD_GET_ARGUMENT_KIND, ulfius_fill_map_check_utf8, &con_info->map_url_initial); MHD_get_connection_values (connection, MHD_COOKIE_KIND, ulfius_fill_map_check_utf8, con_info->request->map_cookie); } else { MHD_get_connection_values (connection, MHD_HEADER_KIND, ulfius_fill_map, con_info->request->map_header); MHD_get_connection_values (connection, MHD_GET_ARGUMENT_KIND, ulfius_fill_map, &con_info->map_url_initial); MHD_get_connection_values (connection, MHD_COOKIE_KIND, ulfius_fill_map, con_info->request->map_cookie); } content_type = (char*)u_map_get_case(con_info->request->map_header, ULFIUS_HTTP_HEADER_CONTENT); // Set POST Processor if content-type is properly set if (content_type != NULL && (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, o_strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) || 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, o_strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))) { con_info->has_post_processor = 1; con_info->post_processor = MHD_create_post_processor (connection, ULFIUS_POSTBUFFERSIZE, mhd_iterate_post_data, (void *) con_info); if (NULL == con_info->post_processor) { ulfius_clean_request_full(con_info->request); con_info->request = NULL; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating post_processor"); return MHD_NO; } } return MHD_YES; } else if (*upload_data_size != 0) { size_t body_len = con_info->request->binary_body_length + *upload_data_size, upload_data_size_current = *upload_data_size; if (((struct _u_instance *)cls)->max_post_body_size > 0 && con_info->request->binary_body_length + *upload_data_size > ((struct _u_instance *)cls)->max_post_body_size) { body_len = ((struct _u_instance *)cls)->max_post_body_size; upload_data_size_current = ((struct _u_instance *)cls)->max_post_body_size - con_info->request->binary_body_length; } if (body_len >= con_info->request->binary_body_length) { con_info->request->binary_body = o_realloc(con_info->request->binary_body, body_len); if (con_info->request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info->request->binary_body"); return MHD_NO; } else { memcpy((char*)con_info->request->binary_body + con_info->request->binary_body_length, upload_data, upload_data_size_current); con_info->request->binary_body_length += upload_data_size_current; // Handles request binary_body const char * content_type = u_map_get_case(con_info->request->map_header, ULFIUS_HTTP_HEADER_CONTENT); if (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, o_strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) || 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, o_strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA))) { MHD_post_process (con_info->post_processor, upload_data, *upload_data_size); } *upload_data_size = 0; return MHD_YES; } } else { return MHD_YES; } } else { // Check if the endpoint has one or more matches current_endpoint_list = ulfius_endpoint_match(method, url, endpoint_list); // Set to default_endpoint if no match if ((current_endpoint_list == NULL || current_endpoint_list[0] == NULL) && ((struct _u_instance *)cls)->default_endpoint != NULL && ((struct _u_instance *)cls)->default_endpoint->callback_function != NULL) { current_endpoint_list = o_realloc(current_endpoint_list, 2*sizeof(struct _u_endpoint *)); if (current_endpoint_list != NULL) { current_endpoint_list[0] = ((struct _u_instance *)cls)->default_endpoint; current_endpoint_list[1] = NULL; } } mhd_response_flag = ((struct _u_instance *)cls)->mhd_response_copy_data?MHD_RESPMEM_MUST_COPY:MHD_RESPMEM_MUST_FREE; if (current_endpoint_list != NULL && current_endpoint_list[0] != NULL) { response = o_malloc(sizeof(struct _u_response)); if (response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating response"); mhd_ret = MHD_NO; } else if (ulfius_init_response(response) != U_OK) { o_free(response); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_init_response"); mhd_ret = MHD_NO; } else { // Add default headers (if any) to the response header maps if (((struct _u_instance *)cls)->default_headers != NULL && u_map_count(((struct _u_instance *)cls)->default_headers) > 0) { u_map_clean_full(response->map_header); response->map_header = u_map_copy(((struct _u_instance *)cls)->default_headers); } // Initialize auth variables con_info->request->auth_basic_user = MHD_basic_auth_get_username_password(connection, &con_info->request->auth_basic_password); for (i=0; current_endpoint_list[i] != NULL && !close_loop; i++) { current_endpoint = current_endpoint_list[i]; u_map_empty(con_info->request->map_url); u_map_copy_into(con_info->request->map_url, &con_info->map_url_initial); if (ulfius_parse_url(url, current_endpoint, con_info->request->map_url, con_info->u_instance->check_utf8) != U_OK) { o_free(response); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error parsing url: ", url); mhd_ret = MHD_NO; } // Run callback function with the input parameters filled for the current callback callback_ret = current_endpoint->callback_function(con_info->request, response, current_endpoint->user_data); if (response->timeout > 0 && MHD_set_connection_option(connection, MHD_CONNECTION_OPTION_TIMEOUT, response->timeout) != MHD_YES) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting connection response timeout value"); } if (response->stream_callback != NULL) { // Call the stream_callback function to build the response binary_body // A stram_callback is always the last one mhd_response = MHD_create_response_from_callback(response->stream_size, response->stream_block_size, response->stream_callback, response->stream_user_data, response->stream_callback_free); if (mhd_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_from_callback"); mhd_ret = MHD_NO; } else if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); mhd_ret = MHD_NO; } close_loop = 1; #ifndef U_DISABLE_WEBSOCKET } else if (((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback != NULL || ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback != NULL) { // if the session is a valid websocket request, // Initiate an UPGRADE session, // then run the websocket callback functions with initialized data if (NULL != o_strcasestr(u_map_get_case(con_info->request->map_header, "upgrade"), U_WEBSOCKET_UPGRADE_VALUE) && NULL != u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Key") && NULL != u_map_get_case(con_info->request->map_header, "Origin") && NULL != o_strcasestr(u_map_get_case(con_info->request->map_header, "Connection"), "Upgrade") && 0 == o_strcmp(con_info->request->http_protocol, "HTTP/1.1") && 0 == o_strcmp(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Version"), "13") && 0 == o_strcmp(con_info->request->http_verb, "GET")) { int ret_protocol = 0, ret_extensions = 0; // Check websocket_protocol and websocket_extensions to match ours if ((ret_extensions = ulfius_check_list_match(u_map_get(con_info->request->map_header, "Sec-WebSocket-Extensions"), ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions, ";", &extension)) == U_OK && (ret_protocol = ulfius_check_first_match(u_map_get(con_info->request->map_header, "Sec-WebSocket-Protocol"), ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol, ",", &protocol)) == U_OK) { char websocket_accept[32] = {0}; if (ulfius_generate_handshake_answer(u_map_get(con_info->request->map_header, "Sec-WebSocket-Key"), websocket_accept)) { struct _websocket * websocket = o_malloc(sizeof(struct _websocket)); if (websocket != NULL && ulfius_init_websocket(websocket) == U_OK) { websocket->request = ulfius_duplicate_request(con_info->request); if (websocket->request != NULL) { websocket->instance = (struct _u_instance *)cls; websocket->websocket_manager_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback; websocket->websocket_manager_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data; websocket->websocket_incoming_message_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback; websocket->websocket_incoming_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data; websocket->websocket_onclose_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback; websocket->websocket_onclose_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data; mhd_response = MHD_create_response_for_upgrade(ulfius_start_websocket_cb, websocket); if (mhd_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_for_upgrade"); mhd_ret = MHD_NO; } else { MHD_add_response_header (mhd_response, MHD_HTTP_HEADER_UPGRADE, U_WEBSOCKET_UPGRADE_VALUE); MHD_add_response_header (mhd_response, "Sec-WebSocket-Accept", websocket_accept); MHD_add_response_header (mhd_response, "Sec-WebSocket-Protocol", protocol); if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); mhd_ret = MHD_NO; } else { ulfius_instance_add_websocket_active((struct _u_instance *)cls, websocket); upgrade_protocol = 1; } } } else { o_free(websocket); // Error building struct _websocket, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for websocket->request"); mhd_ret = MHD_NO; } else { response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); } } } else { // Error building struct _websocket, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); } } } else { // Error building ulfius_generate_handshake_answer, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); } } } else { response->status = MHD_HTTP_BAD_REQUEST; response_buffer = msprintf("%s%s", (ret_protocol!=U_OK?"Error validating protocol\n":""), (ret_extensions!=U_OK?"Error validating extensions":"")); y_log_message(Y_LOG_LEVEL_DEBUG, "Ulfius - Error websocket connection: %s", response_buffer); mhd_response = MHD_create_response_from_buffer (o_strlen(response_buffer), response_buffer, mhd_response_flag ); } o_free(protocol); o_free(extension); } else { response_buffer = msprintf("%s%s%s%s%s%s%s", o_strcasestr(u_map_get_case(con_info->request->map_header, "upgrade"), U_WEBSOCKET_UPGRADE_VALUE)==NULL?"No Upgrade websocket header\n":"", o_strcasestr(u_map_get_case(con_info->request->map_header, "Connection"), "Upgrade")==NULL?"No Connection Upgrade header\n":"", u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Key")==NULL?"No Sec-WebSocket-Key header\n":"", u_map_get_case(con_info->request->map_header, "Origin")?"No Origin header\n":"", o_strcmp(con_info->request->http_protocol, "HTTP/1.1")!=0?"Wrong HTTP Protocolv":"", o_strcmp(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Version"), "13")!=0?"Wrong websocket version\n":"", o_strcmp(con_info->request->http_verb, "GET")!=0?"Method is not GET":""); response->status = MHD_HTTP_BAD_REQUEST; y_log_message(Y_LOG_LEVEL_DEBUG, "Ulfius - Error websocket connection: %s", response_buffer); mhd_response = MHD_create_response_from_buffer (o_strlen(response_buffer), response_buffer, mhd_response_flag ); } close_loop = 1; #endif } else { if (callback_ret == U_CALLBACK_CONTINUE && current_endpoint_list[i+1] == NULL) { // If callback_ret is U_CALLBACK_CONTINUE but callback function is the last one on the list callback_ret = U_CALLBACK_COMPLETE; } // Test callback_ret to know what to do switch (callback_ret) { case U_CALLBACK_CONTINUE: break; case U_CALLBACK_COMPLETE: close_loop = 1; if (ulfius_get_body_from_response(response, &response_buffer, &response_buffer_len) == U_OK) { // Build the response binary_body mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); if (mhd_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_from_buffer"); mhd_ret = MHD_NO; } else if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); mhd_ret = MHD_NO; } } else { // Error building response, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); } } break; case U_CALLBACK_UNAUTHORIZED: close_loop = 1; // Wrong credentials, send status 401 and realm value if set if (ulfius_get_body_from_response(response, &response_buffer, &response_buffer_len) == U_OK) { mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { inner_error = U_ERROR_PARAMS; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response->binary_body = o_strdup(ULFIUS_HTTP_ERROR_BODY); response->binary_body_length = o_strlen(ULFIUS_HTTP_ERROR_BODY); if (response->binary_body == NULL) { inner_error = U_ERROR_MEMORY; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); mhd_ret = MHD_NO; } } else { inner_error = U_CALLBACK_UNAUTHORIZED; } } else { // Error building response, sending error 500 response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { inner_error = U_ERROR_MEMORY; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); inner_error = U_CALLBACK_UNAUTHORIZED; } } if (response->auth_realm != NULL) { auth_realm = response->auth_realm; } else if (((struct _u_instance *)cls)->default_auth_realm != NULL) { auth_realm = ((struct _u_instance *)cls)->default_auth_realm; } break; case U_CALLBACK_ERROR: default: close_loop = 1; response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); } break; } } } if (mhd_response != NULL) { if (auth_realm != NULL && inner_error == U_CALLBACK_UNAUTHORIZED) { mhd_ret = MHD_queue_basic_auth_fail_response (connection, auth_realm, mhd_response); } else if (inner_error == U_CALLBACK_UNAUTHORIZED) { mhd_ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, mhd_response); #ifndef U_DISABLE_WEBSOCKET } else if (upgrade_protocol) { mhd_ret = MHD_queue_response (connection, MHD_HTTP_SWITCHING_PROTOCOLS, mhd_response); #endif } else { mhd_ret = MHD_queue_response (connection, response->status, mhd_response); } MHD_destroy_response (mhd_response); // Free Response parameters ulfius_clean_response_full(response); response = NULL; } } } else { response_buffer = o_strdup(ULFIUS_HTTP_NOT_FOUND_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = o_strlen(ULFIUS_HTTP_NOT_FOUND_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, mhd_response_flag ); mhd_ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, mhd_response); MHD_destroy_response (mhd_response); } } if (mhd_response_flag == MHD_RESPMEM_MUST_COPY) { o_free(response_buffer); } o_free(current_endpoint_list); return mhd_ret; } } /** * ulfius_run_mhd_daemon * Starts a mhd daemon for the specified instance * return a pointer to the mhd_daemon on success, NULL on error * */ static struct MHD_Daemon * ulfius_run_mhd_daemon(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem, const char * root_ca_perm) { unsigned int mhd_flags = MHD_USE_THREAD_PER_CONNECTION; int index; #ifdef DEBUG mhd_flags |= MHD_USE_DEBUG; #endif #if MHD_VERSION >= 0x00095300 mhd_flags |= MHD_USE_INTERNAL_POLLING_THREAD; #endif #ifndef U_DISABLE_WEBSOCKET mhd_flags |= MHD_ALLOW_UPGRADE; #endif if (u_instance->mhd_daemon == NULL) { struct MHD_OptionItem mhd_ops[8]; // Default options mhd_ops[0].option = MHD_OPTION_NOTIFY_COMPLETED; mhd_ops[0].value = (intptr_t)mhd_request_completed; mhd_ops[0].ptr_value = NULL; mhd_ops[1].option = MHD_OPTION_SOCK_ADDR; mhd_ops[1].value = 0; mhd_ops[1].ptr_value = (void *)u_instance->bind_address; mhd_ops[2].option = MHD_OPTION_URI_LOG_CALLBACK; mhd_ops[2].value = (intptr_t)ulfius_uri_logger; mhd_ops[2].ptr_value = NULL; index = 3; if (key_pem != NULL && cert_pem != NULL) { // HTTPS parameters mhd_flags |= MHD_USE_SSL; mhd_ops[index].option = MHD_OPTION_HTTPS_MEM_KEY; mhd_ops[index].value = 0; mhd_ops[index].ptr_value = (void*)key_pem; mhd_ops[index + 1].option = MHD_OPTION_HTTPS_MEM_CERT; mhd_ops[index + 1].value = 0; mhd_ops[index + 1].ptr_value = (void*)cert_pem; index += 2; if (root_ca_perm != NULL) { mhd_ops[index].option = MHD_OPTION_HTTPS_MEM_TRUST; mhd_ops[index].value = 0; mhd_ops[index].ptr_value = (void *)root_ca_perm; index++; } } if (u_instance->timeout > 0) { mhd_ops[index].option = MHD_OPTION_CONNECTION_TIMEOUT; mhd_ops[index].value = u_instance->timeout; mhd_ops[index].ptr_value = NULL; index++; } mhd_ops[index].option = MHD_OPTION_END; mhd_ops[index].value = 0; mhd_ops[index].ptr_value = NULL; return MHD_start_daemon ( mhd_flags, u_instance->port, NULL, NULL, &ulfius_webservice_dispatcher, (void *)u_instance, MHD_OPTION_ARRAY, mhd_ops, MHD_OPTION_END ); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, instance already started"); return NULL; } } /** * ulfius_start_framework * Initializes the framework and run the webservice based on the parameters given * return true if no error * * u_instance: pointer to a struct _u_instance that describe its port and bind address * return U_OK on success */ int ulfius_start_framework(struct _u_instance * u_instance) { #ifndef U_DISABLE_WEBSOCKET return ulfius_start_secure_ca_trust_framework(u_instance, NULL, NULL, NULL); #else return ulfius_start_secure_framework(u_instance, NULL, NULL); #endif } /** * ulfius_start_secure_framework * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection * * u_instance: pointer to a struct _u_instance that describe its port and bind address * key_pem: private key for the server * cert_pem: server certificate * return U_OK on success */ int ulfius_start_secure_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem) { #ifndef U_DISABLE_WEBSOCKET return ulfius_start_secure_ca_trust_framework(u_instance, key_pem, cert_pem, NULL); #else // Check parameters and validate u_instance and endpoint_list that there is no mistake if (u_instance == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_start_secure_framework - Error, u_instance is NULL"); return U_ERROR_PARAMS; } else if ((key_pem == NULL && cert_pem != NULL) || (key_pem != NULL && cert_pem == NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_start_secure_framework - Error, you must specify key_pem and cert_pem"); return U_ERROR_PARAMS; } if (ulfius_validate_instance(u_instance) == U_OK) { u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, key_pem, cert_pem, NULL); if (u_instance->mhd_daemon == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting"); u_instance->status = U_STATUS_ERROR; return U_ERROR_LIBMHD; } else { u_instance->status = U_STATUS_RUNNING; return U_OK; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_start_secure_framework - error input parameters"); return U_ERROR_PARAMS; } #endif } #ifndef U_DISABLE_WEBSOCKET /** * ulfius_start_secure_ca_trust_framework * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection * And using a root server to authenticate client connections * * u_instance: pointer to a struct _u_instance that describe its port and bind address * key_pem: private key for the server * cert_pem: server certificate * root_ca_pem: client root CA you're willing to trust for this instance * return U_OK on success */ int ulfius_start_secure_ca_trust_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem, const char * root_ca_pem) { // Check parameters and validate u_instance and endpoint_list that there is no mistake if (u_instance == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_start_secure_ca_trust_framework - Error, u_instance is NULL"); return U_ERROR_PARAMS; } else if ((key_pem == NULL && cert_pem != NULL) || (key_pem != NULL && cert_pem == NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_start_secure_ca_trust_framework - Error, you must specify key_pem and cert_pem"); return U_ERROR_PARAMS; } else if (root_ca_pem != NULL && (key_pem == NULL || cert_pem == NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_start_secure_ca_trust_framework - Error, you must specify key_pem and cert_pem in addition to root_ca_pem"); return U_ERROR_PARAMS; } if (root_ca_pem != NULL) { u_instance->use_client_cert_auth = 1; } else { u_instance->use_client_cert_auth = 0; } if (ulfius_validate_instance(u_instance) == U_OK) { u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, key_pem, cert_pem, root_ca_pem); if (u_instance->mhd_daemon == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting"); u_instance->status = U_STATUS_ERROR; return U_ERROR_LIBMHD; } else { u_instance->status = U_STATUS_RUNNING; return U_OK; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_start_secure_ca_trust_framework - error input parameters"); return U_ERROR_PARAMS; } } #endif /** * ulfius_stop_framework * * Stop the webservice * u_instance: pointer to a struct _u_instance that describe its port and bind address * return U_OK on success */ int ulfius_stop_framework(struct _u_instance * u_instance) { if (u_instance != NULL && u_instance->mhd_daemon != NULL) { #ifndef U_DISABLE_WEBSOCKET int i; // Loop in all active websockets and send close signal for (i=((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active-1; i>=0; i--) { ((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active[i]->websocket_manager->close_flag = 1; } pthread_mutex_lock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); while (((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active > 0) { pthread_cond_wait(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, &((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); } pthread_mutex_unlock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); #endif MHD_stop_daemon (u_instance->mhd_daemon); u_instance->mhd_daemon = NULL; u_instance->status = U_STATUS_STOP; return U_OK; } else if (u_instance != NULL) { u_instance->status = U_STATUS_ERROR; return U_ERROR_PARAMS; } else { return U_ERROR; } } /** * ulfius_copy_endpoint * return a copy of an endpoint with duplicate values */ int ulfius_copy_endpoint(struct _u_endpoint * dest, const struct _u_endpoint * source) { if (source != NULL && dest != NULL) { dest->http_method = o_strdup(source->http_method); dest->url_prefix = o_strdup(source->url_prefix); dest->url_format = o_strdup(source->url_format); dest->callback_function = source->callback_function; dest->user_data = source->user_data; dest->priority = source->priority; if (ulfius_is_valid_endpoint(dest, 0)) { return U_OK; } else { return U_ERROR_MEMORY; } } return U_ERROR_PARAMS; } /** * duplicate_endpoint_list * return a copy of an endpoint list with duplicate values * returned value must be free'd after use */ struct _u_endpoint * ulfius_duplicate_endpoint_list(const struct _u_endpoint * endpoint_list) { struct _u_endpoint * to_return = NULL; int i; if (endpoint_list != NULL) { for (i=0; endpoint_list[i].http_method != NULL; i++) { if ((to_return = o_realloc(to_return, (i+1)*sizeof(struct _u_endpoint *))) == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for duplicate_endpoint_list.to_return"); return NULL; } else { ulfius_copy_endpoint(&to_return[i], &endpoint_list[i]); } } } return to_return; } /** * clean_endpoint * free allocated memory by an endpoint */ void ulfius_clean_endpoint(struct _u_endpoint * endpoint) { if (endpoint != NULL) { o_free(endpoint->http_method); o_free(endpoint->url_prefix); o_free(endpoint->url_format); endpoint->http_method = NULL; endpoint->url_prefix = NULL; endpoint->url_format = NULL; } } /** * ulfius_clean_endpoint_list * free allocated memory by an endpoint list */ void ulfius_clean_endpoint_list(struct _u_endpoint * endpoint_list) { int i; if (endpoint_list != NULL) { for (i=0; endpoint_list[i].http_method != NULL; i++) { ulfius_clean_endpoint(&endpoint_list[i]); } o_free(endpoint_list); } } /** * Add a struct _u_endpoint * to the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint: pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list * return U_OK on success */ int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint) { int res; if (u_instance != NULL && u_endpoint != NULL) { if (ulfius_is_valid_endpoint(u_endpoint, 0)) { if (u_instance->endpoint_list == NULL) { // No endpoint, create a list with 2 endpoints so the last one is an empty one u_instance->endpoint_list = o_malloc(2 * sizeof(struct _u_endpoint)); if (u_instance->endpoint_list == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, Error allocating memory for u_instance->endpoint_list"); return U_ERROR_MEMORY; } u_instance->nb_endpoints = 1; } else { u_instance->nb_endpoints++; u_instance->endpoint_list = o_realloc(u_instance->endpoint_list, (u_instance->nb_endpoints + 1) * sizeof(struct _u_endpoint)); if (u_instance->endpoint_list == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, Error reallocating memory for u_instance->endpoint_list"); return U_ERROR_MEMORY; } } res = ulfius_copy_endpoint(&u_instance->endpoint_list[u_instance->nb_endpoints - 1], u_endpoint); if (res != U_OK) { return res; } else { // Add empty endpoint at the end of the endpoint list ulfius_copy_endpoint(&u_instance->endpoint_list[u_instance->nb_endpoints], ulfius_empty_endpoint()); } return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, invalid struct _u_endpoint"); return U_ERROR_PARAMS; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, invalid parameters"); return U_ERROR_PARAMS; } return U_ERROR; } /** * Add a struct _u_endpoint * list to the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint_list: pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list * return U_OK on success */ int ulfius_add_endpoint_list(struct _u_instance * u_instance, const struct _u_endpoint ** u_endpoint_list) { int i, res; if (u_instance != NULL && u_endpoint_list != NULL) { for (i=0; !ulfius_equals_endpoints(u_endpoint_list[i], ulfius_empty_endpoint()); i++) { res = ulfius_add_endpoint(u_instance, u_endpoint_list[i]); if (res != U_OK) { return res; } } return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint_list, invalid parameters"); return U_ERROR_PARAMS; } return U_ERROR; } /** * Remove a struct _u_endpoint * from the specified u_instance * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * u_endpoint: pointer to a struct _u_endpoint that will be removed in the u_instance endpoint_list * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match * If no endpoint is found, return U_ERROR_NOT_FOUND * return U_OK on success */ int ulfius_remove_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint) { int i, j, found = 0; if (u_instance != NULL && u_endpoint != NULL && !ulfius_equals_endpoints(u_endpoint, ulfius_empty_endpoint()) && ulfius_is_valid_endpoint(u_endpoint, 1)) { for (i=0; inb_endpoints; i++) { // Compare u_endpoint with u_instance->endpoint_list[i] if ((u_endpoint->http_method != NULL && 0 == o_strcmp(u_instance->endpoint_list[i].http_method, u_endpoint->http_method)) && ((u_instance->endpoint_list[i].url_prefix == NULL && u_endpoint->url_prefix == NULL) || (u_instance->endpoint_list[i].url_prefix != NULL && u_endpoint->url_prefix != NULL && 0 == o_strcmp(u_instance->endpoint_list[i].url_prefix, u_endpoint->url_prefix))) && ((u_instance->endpoint_list[i].url_format == NULL && u_endpoint->url_format == NULL) || (u_instance->endpoint_list[i].url_format != NULL && u_endpoint->url_format != NULL && 0 == o_strcmp(u_instance->endpoint_list[i].url_format, u_endpoint->url_format)))) { // It's a match! // Remove current endpoint and move the next ones to their previous index, then reduce the endpoint_list by 1 found = 1; o_free(u_instance->endpoint_list[i].http_method); o_free(u_instance->endpoint_list[i].url_prefix); o_free(u_instance->endpoint_list[i].url_format); for (j=i; jnb_endpoints; j++) { u_instance->endpoint_list[j] = u_instance->endpoint_list[j+1]; } u_instance->nb_endpoints--; u_instance->endpoint_list = o_realloc(u_instance->endpoint_list, (u_instance->nb_endpoints + 1)*sizeof(struct _u_endpoint)); if (u_instance->endpoint_list == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, Error reallocating memory for u_instance->endpoint_list"); return U_ERROR_MEMORY; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_remove_endpoint, invalid parameters"); return U_ERROR_PARAMS; } return U_ERROR; } /** * ulfius_empty_endpoint * return an empty endpoint that goes at the end of an endpoint list */ const struct _u_endpoint * ulfius_empty_endpoint() { static struct _u_endpoint empty_endpoint; empty_endpoint.http_method = NULL; empty_endpoint.url_prefix = NULL; empty_endpoint.url_format = NULL; empty_endpoint.callback_function = NULL; empty_endpoint.user_data = NULL; return &empty_endpoint; } /** * ulfius_equals_endpoints * Compare 2 endpoints and return true if their method, prefix and format are the same or if both are NULL */ int ulfius_equals_endpoints(const struct _u_endpoint * endpoint1, const struct _u_endpoint * endpoint2) { if (endpoint1 != NULL && endpoint2 != NULL) { if (endpoint1 == endpoint2) { return 1; } else if (o_strcmp(endpoint2->http_method, endpoint1->http_method) != 0) { return 0; } else if (o_strcmp(endpoint2->url_prefix, endpoint1->url_prefix) != 0) { return 0; } else if (o_strcmp(endpoint2->url_format, endpoint1->url_format) != 0) { return 0; } else { return 1; } } else { return 1; } } /** * Add a struct _u_endpoint * to the specified u_instance with its values specified * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * http_method: http verb (GET, POST, PUT, etc.) in upper case * url_prefix: prefix for the url (optional) * url_format: string used to define the endpoint format * separate words with / * to define a variable in the url, prefix it with @ or : * example: /test/resource/:name/elements * on an url_format that ends with '*', the rest of the url will not be tested * priority: endpoint priority in descending order (0 is the higher priority) * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in callback_function * return U_OK on success */ int ulfius_add_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format, unsigned int priority, int (* callback_function)(const struct _u_request * request, // Input parameters (set by the framework) struct _u_response * response, // Output parameters (set by the user) void * user_data), void * user_data) { struct _u_endpoint endpoint; if (u_instance != NULL) { endpoint.http_method = (char *)http_method; endpoint.url_prefix = (char *)url_prefix; endpoint.url_format = (char *)url_format; endpoint.priority = priority; endpoint.callback_function = callback_function; endpoint.user_data = user_data; return ulfius_add_endpoint(u_instance, &endpoint); } else { return U_ERROR_PARAMS; } } /** * Remove a struct _u_endpoint * from the specified u_instance * using the specified values used to identify an endpoint * Can be done during the execution of the webservice for injection * u_instance: pointer to a struct _u_instance that describe its port and bind address * http_method: http_method used by the endpoint * url_prefix: url_prefix used by the endpoint * url_format: url_format used by the endpoint * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match * If no endpoint is found, return U_ERROR_NOT_FOUND * return U_OK on success */ int ulfius_remove_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format) { struct _u_endpoint endpoint; if (u_instance != NULL) { endpoint.http_method = (char *)http_method; endpoint.url_prefix = (char *)url_prefix; endpoint.url_format = (char *)url_format; endpoint.callback_function = NULL; return ulfius_remove_endpoint(u_instance, &endpoint); } else { return U_ERROR_PARAMS; } } /** * ulfius_set_default_endpoint * Set the default endpoint * This endpoint will be called if no endpoint match the url called * callback_function: a pointer to a function that will be executed each time the endpoint is called * you must declare the function as described. * user_data: a pointer to a data or a structure that will be available in callback_function * to remove a default endpoint function, call ulfius_set_default_endpoint with NULL parameter for callback_function * return U_OK on success */ int ulfius_set_default_endpoint(struct _u_instance * u_instance, int (* callback_function)(const struct _u_request * request, struct _u_response * response, void * user_data), void * user_data) { if (u_instance != NULL && callback_function != NULL) { if (u_instance->default_endpoint == NULL) { u_instance->default_endpoint = o_malloc(sizeof(struct _u_endpoint)); if (u_instance->default_endpoint == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_instance->default_endpoint"); return U_ERROR_MEMORY; } } u_instance->default_endpoint->http_method = NULL; u_instance->default_endpoint->url_prefix = NULL; u_instance->default_endpoint->url_format = NULL; u_instance->default_endpoint->callback_function = callback_function; u_instance->default_endpoint->user_data = user_data; u_instance->default_endpoint->priority = 0; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_set_upload_file_callback_function * * Set the callback function to handle file upload * Used to facilitate large files upload management * The callback function file_upload_callback will be called * multiple times, with the uploaded file in striped in parts * * Warning: If this function is used, all the uploaded files * for the instance will be managed via this function, and they * will no longer be available in the struct _u_request in the * ulfius callback function afterwards. * * Thanks to Thad Phetteplace for the help on this feature * * u_instance: pointer to a struct _u_instance that describe its port and bind address * file_upload_callback: Pointer to a callback function that will handle all file uploads * cls: a pointer that will be passed to file_upload_callback each tim it's called */ int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance, int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls), void * cls) { if (u_instance != NULL && file_upload_callback != NULL) { u_instance->file_upload_callback = file_upload_callback; u_instance->file_upload_cls = cls; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_clean_instance * * Clean memory allocated by a struct _u_instance * */ void ulfius_clean_instance(struct _u_instance * u_instance) { if (u_instance != NULL) { ulfius_clean_endpoint_list(u_instance->endpoint_list); u_map_clean_full(u_instance->default_headers); o_free(u_instance->default_auth_realm); o_free(u_instance->default_endpoint); u_instance->endpoint_list = NULL; u_instance->default_headers = NULL; u_instance->default_auth_realm = NULL; u_instance->bind_address = NULL; u_instance->default_endpoint = NULL; #ifndef U_DISABLE_WEBSOCKET /* ulfius_clean_instance might be called without websocket_handler being initialized */ if ((struct _websocket_handler *)u_instance->websocket_handler) { if (((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init && (pthread_mutex_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock) || pthread_cond_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond))) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error destroying websocket_close_lock or websocket_close_cond"); } o_free(u_instance->websocket_handler); u_instance->websocket_handler = NULL; } #endif } } /** * ulfius_init_instance * * Initialize a struct _u_instance * with default values * port: tcp port to bind to, must be between 1 and 65535 * bind_address: IP address to listen to, optional, the reference is borrowed, the structure isn't copied * default_auth_realm: default realm to send to the client on authentication error * return U_OK on success */ int ulfius_init_instance(struct _u_instance * u_instance, unsigned int port, struct sockaddr_in * bind_address, const char * default_auth_realm) { if (u_instance != NULL && port > 0 && port < 65536) { u_instance->mhd_daemon = NULL; u_instance->status = U_STATUS_STOP; u_instance->port = port; u_instance->bind_address = bind_address; u_instance->timeout = 0; u_instance->default_auth_realm = o_strdup(default_auth_realm); u_instance->nb_endpoints = 0; u_instance->endpoint_list = NULL; u_instance->default_headers = o_malloc(sizeof(struct _u_map)); u_instance->mhd_response_copy_data = 0; u_instance->check_utf8 = 1; if (u_instance->default_headers == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_instance->default_headers"); ulfius_clean_instance(u_instance); return U_ERROR_MEMORY; } u_map_init(u_instance->default_headers); u_instance->default_endpoint = NULL; u_instance->max_post_param_size = 0; u_instance->max_post_body_size = 0; u_instance->file_upload_callback = NULL; u_instance->file_upload_cls = NULL; #ifndef U_DISABLE_WEBSOCKET u_instance->use_client_cert_auth = 0; u_instance->websocket_handler = o_malloc(sizeof(struct _websocket_handler)); if (u_instance->websocket_handler == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_instance->websocket_handler"); ulfius_clean_instance(u_instance); return U_ERROR_MEMORY; } ((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init = 0; ((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active = 0; ((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active = NULL; if (pthread_mutex_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock, NULL) || pthread_cond_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing websocket_close_lock or websocket_close_cond"); ulfius_clean_instance(u_instance); return U_ERROR_MEMORY; } ((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init = 1; #else u_instance->websocket_handler = NULL; #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * free data allocated by ulfius functions */ void u_free(void * data) { o_free(data); } /** * The utf8_check() function scans the '\0'-terminated string starting * at s. It returns a pointer to the first byte of the first malformed * or overlong UTF-8 sequence found, or NULL if the string contains * only correct UTF-8. It also spots UTF-8 sequences that could cause * trouble if converted to UTF-16, namely surrogate characters * (U+D800..U+DFFF) and non-Unicode positions (U+FFFE..U+FFFF). This * routine is very likely to find a malformed sequence if the input * uses any other encoding than UTF-8. It therefore can be used as a * very effective heuristic for distinguishing between UTF-8 and other * encodings. * * I wrote this code mainly as a specification of functionality; there * are no doubt performance optimizations possible for certain CPUs. * * Markus Kuhn -- 2005-03-30 * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html */ const unsigned char * utf8_check(const char * s_orig) { const unsigned char * s = (unsigned char *)s_orig; while (*s) { if (*s < 0x80) { /* 0xxxxxxx */ s++; } else if ((s[0] & 0xe0) == 0xc0) { /* 110XXXXx 10xxxxxx */ if ((s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) { /* overlong? */ return s; } else { s += 2; } } else if ((s[0] & 0xf0) == 0xe0) { /* 1110XXXX 10Xxxxxx 10xxxxxx */ if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ (s[0] == 0xef && s[1] == 0xbf && (s[2] & 0xfe) == 0xbe)) { /* U+FFFE or U+FFFF? */ return s; } else { s += 3; } } else if ((s[0] & 0xf8) == 0xf0) { /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 || (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) { /* > U+10FFFF? */ return s; } else { s += 4; } } else { return s; } } return NULL; } ulfius-2.5.2/src/yuarel.c000066400000000000000000000142731341017022300152630ustar00rootroot00000000000000/** * Copyright (C) 2016,2017 Jack Engqvist Johansson * * 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. */ typedef int make_iso_compilers_happy; #ifndef U_DISABLE_WEBSOCKET #include #include #include #include "yuarel.h" /** * Parse a non null terminated string into an integer. * * str: the string containing the number. * len: Number of characters to parse. */ static inline int natoi(const char *str, size_t len) { unsigned int i, r = 0; for (i = 0; i < len; i++) { r *= 10; r += str[i] - '0'; } return r; } /** * Check if a URL is relative (no scheme and hostname). * * url: the string containing the URL to check. * * Returns 1 if relative, otherwise 0. */ static inline int is_relative(const char *url) { return (*url == '/') ? 1 : 0; } /** * Parse the scheme of a URL by inserting a null terminator after the scheme. * * str: the string containing the URL to parse. Will be modified. * * Returns a pointer to the hostname on success, otherwise NULL. */ static inline char * parse_scheme(char *str) { char *s; /* If not found or first in string, return error */ s = strchr(str, ':'); if (s == NULL || s == str) { return NULL; } /* If not followed by two slashes, return error */ if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') { return NULL; } *s = '\0'; // Replace ':' with NULL return s + 3; } /** * Find a character in a string, replace it with '\0' and return the next * character in the string. * * str: the string to search in. * find: the character to search for. * * Returns a pointer to the character after the one to search for. If not * found, NULL is returned. */ static inline char * find_and_terminate(char *str, char find) { str = strchr(str, find); if (NULL == str) { return NULL; } *str = '\0'; return str + 1; } /* Yes, the following functions could be implemented as preprocessor macros instead of inline functions, but I think that this approach will be more clean in this case. */ static inline char * find_fragment(char *str) { return find_and_terminate(str, '#'); } static inline char * find_query(char *str) { return find_and_terminate(str, '?'); } static inline char * find_path(char *str) { return find_and_terminate(str, '/'); } /** * Parse a URL string to a struct. * * url: pointer to the struct where to store the parsed URL parts. * u: the string containing the URL to be parsed. * * Returns 0 on success, otherwise -1. */ int yuarel_parse(struct yuarel *url, char *u) { if (NULL == url || NULL == u) { return -1; } memset(url, 0, sizeof (struct yuarel)); /* (Fragment) */ url->fragment = find_fragment(u); /* (Query) */ url->query = find_query(u); /* Relative URL? Parse scheme and hostname */ if (!is_relative(u)) { /* Scheme */ url->scheme = u; u = parse_scheme(u); if (u == NULL) { return -1; } /* Host */ if ('\0' == *u) { return -1; } url->host = u; /* (Path) */ url->path = find_path(u); /* (Credentials) */ u = strchr(url->host, '@'); if (NULL != u) { /* Missing credentials? */ if (u == url->host) { return -1; } url->username = url->host; url->host = u + 1; *u = '\0'; u = strchr(url->username, ':'); if (NULL == u) { return -1; } url->password = u + 1; *u = '\0'; } /* Missing hostname? */ if ('\0' == *url->host) { return -1; } /* (Port) */ u = strchr(url->host, ':'); if (NULL != u && (NULL == url->path || u < url->path)) { *(u++) = '\0'; if ('\0' == *u) { return -1; } if (url->path) { url->port = natoi(u, url->path - u - 1); } else { url->port = atoi(u); } } /* Missing hostname? */ if ('\0' == *url->host) { return -1; } } else { /* (Path) */ url->path = find_path(u); } return 0; } /** * Split a path into several strings. * * No data is copied, the slashed are used as null terminators and then * pointers to each path part will be stored in **parts. Double slashes will be * treated as one. * * path: the path to split. * parts: a pointer to an array of (char *) where to store the result. * max_parts: max number of parts to parse. */ int yuarel_split_path(char *path, char **parts, int max_parts) { int i = 0; if (NULL == path || '\0' == *path) { return -1; } do { /* Forward to after slashes */ while (*path == '/') path++; if ('\0' == *path) { break; } parts[i++] = path; path = strchr(path, '/'); if (NULL == path) { break; } *(path++) = '\0'; } while (i < max_parts); return i; } int yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params) { int i = 0; if (NULL == query || '\0' == *query) { return -1; } params[i++].key = query; while (i < max_params && NULL != (query = strchr(query, delimiter))) { *query = '\0'; params[i].key = ++query; params[i].val = NULL; /* Go back and split previous param */ if (i > 0) { if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) { *(params[i - 1].val)++ = '\0'; } } i++; } /* Go back and split last param */ if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) { *(params[i - 1].val)++ = '\0'; } return i; } #endif // U_DISABLE_WEBSOCKET ulfius-2.5.2/test/000077500000000000000000000000001341017022300137775ustar00rootroot00000000000000ulfius-2.5.2/test/Makefile000066400000000000000000000040551341017022300154430ustar00rootroot00000000000000# # Ulfius library # # Makefile used to build the tests # # Copyright 2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU GENERAL PUBLIC LICENSE # License as published by the Free Software Foundation; # version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU GENERAL PUBLIC LICENSE for more details. # # You should have received a copy of the GNU General Public # License along with this program. If not, see . # ULFIUS_INCLUDE=../include ULFIUS_LOCATION=../src CC=gcc CFLAGS+=-Wall -D_REENTRANT -I$(ULFIUS_INCLUDE) -DDEBUG -g -O0 $(CPPFLAGS) LIBS=-lc -lorcania -lulfius -lgnutls -lcheck -lpthread -lm -lrt -lsubunit -L$(ULFIUS_LOCATION) # Use this LIBS below if you don't have/need gnutls #LIBS=-lc -lorcania -lyder -lulfius -lcheck -lpthread -lm -lrt -lsubunit -L$(ULFIUS_LOCATION) # Use this LIBS below if you use yder logs #LIBS=-lc -lorcania -lyder -lulfius -lgnutls -lcheck -lpthread -lm -lrt -lsubunit -L$(ULFIUS_LOCATION) all: test clean: rm -f *.o u_map core framework websocket valgrind.txt libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug u_map: u_map.c $(CC) $(CFLAGS) u_map.c -o u_map $(LIBS) test_u_map: libulfius.so u_map -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./u_map core: core.c $(CC) $(CFLAGS) core.c -o core $(LIBS) test_core: libulfius.so core -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./core framework: framework.c $(CC) $(CFLAGS) framework.c -o framework $(LIBS) test_framework: libulfius.so framework -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./framework websocket: websocket.c $(CC) $(CFLAGS) websocket.c -o websocket $(LIBS) test_websocket: libulfius.so websocket -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./websocket test: test_u_map test_core test_framework test_websocket check: test ulfius-2.5.2/test/client.crt000066400000000000000000000037041341017022300157730ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFjjCCA3YCAQEwDQYJKoZIhvcNAQELBQAwgYoxCzAJBgNVBAYTAkNBMQ8wDQYD VQQIDAZRdWViZWMxDzANBgNVBAcMBlF1ZWJlYzEPMA0GA1UECgwGVWxmaXVzMRAw DgYDVQQLDAd0ZXN0LWNhMRIwEAYDVQQDDAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0B CQEWE3dlYm1hc3RlckBsb2NhbGhvc3QwHhcNMTgxMjAyMTkzNzI0WhcNMTkxMjAy MTkzNzI0WjCBjjELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzEPMA0GA1UE BwwGUXVlYmVjMQ8wDQYDVQQKDAZVbGZpdXMxFDASBgNVBAsMC3Rlc3QtY2xpZW50 MRIwEAYDVQQDDAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0BCQEWE3dlYm1hc3RlckBs b2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC7sYBRUgCA bD9jueGjJKh3CNbtdKsppsi7N4h02bh3cbY0wrrPaVzM4+23rSbGUuvZBJPIRFZn XvIanQalcJQqXSmWIqzmz+ovwlWrNzpkEN1AwpQ9h61rk9V6wUKusBJSpqtIS/YH KXNt8hjzbKIwoAKRefuM1lxL91N3ENPRgh2skbRfA2yOog7uhQRbFIZYC1vJ9tzw luLwNOz5juGEl60IDvpV4yuq3oK5eE+YvIalE/uQh5mvih6Gz9ti0ihTsIYf370c h1IStd5+mD36uX0WnEijkKDGPZvbnMQLhq71M5VC0MMGd7Zbj7d2NMxBNOL6H3rm BxaRi0jMMIMDKMhoDR76OKbnDr/90XmgzTebNcq6JA2XL7QBiVriUykej7brDln3 G/afGC8ShCj6yohCmPyvIRMrmFWzCzbr4ZQvdprNXPqlHOzYuRFINl/Fnv3Sx7V/ 8hJMaG9c+BoJ29cTAnvX5zp7lszb7JoTB0IXdutT6eDm8A+zSQbdFWfNzr7pgQOw fX1bZZeJHEO0YTNYHcFSz0YDOkWEFgy4KEPzDvjEh/fh9F56/MoaEF4Uj8vL/Cer oj98eYCTODuR1jreetiLiFafwZNI6OGe7L1YuR0TIWJRUTetNsAPSoIt+DQVGMoi TdTK8nb/0xGt3Z0CEg7b7Om83CFsedvluwIDAQABMA0GCSqGSIb3DQEBCwUAA4IC AQB5/RdG4e+NvRy9RG2ls+yEJdljPWV9KrdEoD9WX9MauHfvxpW3j9hi7lCohBR5 6oOIsWl+YrkHkgz6RRNlArJBtGhR4tQg3kB5ZTp5PcUUvI9VFVmgGoU3O9rnOtTi +AaEn3Sbx/KkgLnDEgPRMDj1BEVx4O6CQcnRdx9qZCbKXnGAAe5uHSAOOj7ujz9/ TW5W+KlN1RYMlWAH7SWuW2Y+kPKLOK/HpXKuxHZfFfiddya072Ufm0pnapBmvi50 rUmrTnk0DQN/o1vQgrGyg2C48QlUX3IEvl5z6fD8FKC1q2v0L2e0A5jmPWqsQe2B jnp8VYEgD1jIX8Pchy8GE8/NsHNkYEOqtkc/k3oT8I/J4mcHewFlKXu83X+lUhmh vGwTGJEAHFJX7pBdqqoK9EeHrGj0MiwkfqZnBcjspXTYPPLqwWR+iyU20XCwaOcV iYox45FK4XqYiQqciarkQP18vgwtDcMfRfwtRFUTtTRgSULsdrWsMzwX+0lxEab0 Sb4BkU0+YTknpyI8KtyRkQzezsaRVekfHow1pdNWYYrW5P0jmi/V6tezt/609ohL 3+AThUfrgdPSQlV9OrINbMA2cPruHcyqgj+Uh8JBvsIShPxKa17ZLkTWVUgJ+ZJb T6q4HuclYAw3AERGHSM1KPi9OuVw51TzTrxReqrvlL5UxQ== -----END CERTIFICATE----- ulfius-2.5.2/test/client.key000066400000000000000000000062531341017022300157750ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEAu7GAUVIAgGw/Y7nhoySodwjW7XSrKabIuzeIdNm4d3G2NMK6 z2lczOPtt60mxlLr2QSTyERWZ17yGp0GpXCUKl0pliKs5s/qL8JVqzc6ZBDdQMKU PYeta5PVesFCrrASUqarSEv2BylzbfIY82yiMKACkXn7jNZcS/dTdxDT0YIdrJG0 XwNsjqIO7oUEWxSGWAtbyfbc8Jbi8DTs+Y7hhJetCA76VeMrqt6CuXhPmLyGpRP7 kIeZr4oehs/bYtIoU7CGH9+9HIdSErXefpg9+rl9FpxIo5Cgxj2b25zEC4au9TOV QtDDBne2W4+3djTMQTTi+h965gcWkYtIzDCDAyjIaA0e+jim5w6//dF5oM03mzXK uiQNly+0AYla4lMpHo+26w5Z9xv2nxgvEoQo+sqIQpj8ryETK5hVsws26+GUL3aa zVz6pRzs2LkRSDZfxZ790se1f/ISTGhvXPgaCdvXEwJ71+c6e5bM2+yaEwdCF3br U+ng5vAPs0kG3RVnzc6+6YEDsH19W2WXiRxDtGEzWB3BUs9GAzpFhBYMuChD8w74 xIf34fReevzKGhBeFI/Ly/wnq6I/fHmAkzg7kdY63nrYi4hWn8GTSOjhnuy9WLkd EyFiUVE3rTbAD0qCLfg0FRjKIk3UyvJ2/9MRrd2dAhIO2+zpvNwhbHnb5bsCAwEA AQKCAgEArd0K5El1tYCQZ6y0Ww+GDl3d2jCMrRSWNPaILNXRjrOC+PS5X5BWKmWo utBmbqg5WNpomsbS0wacdhKnKXFTjrql3zFXwKcwY/U8rlGGgVMt4ONdtnGML27N /dwwDOmEz5McbugOiyvWhS4R9svWofxW5LN+VUPsU2JTqqOn0xcYrdZ6otjlImr0 MfZ9xnEI0V4UNM28dBERPzmHmtTECrMF0cUuF4lL1ggwR+YutWlNwHT2PzedIYJs LK02qB/Mw1ltpPD4mQiw+iwGYnZcU3inat5yWzGJ/JjtW02oPJUSVIjn5vDC96bF AaPralAWNQDskI3ApnxCZ6gCU0NM13LZmah1TEtm065rlkA10zTMquS+2SYZHzpX IYz05yAiRQ6ljkz/BaPGBNXhDeiTB09voaOYJB0aWr/61pjH8yzElX3599LOGse8 Si+3rIbblDfJSUoGsapJ2Ri21HBA1frKJEi1a67l9AhZIiGPScOAErZHcnVHDEnm Q247yugBIalIpeUA/8ThIaFs5Ofn35RHpPzCoLwRcpJZbwwzjL3uE2Iuv/hjfMTR NTQXhJbkbzFYRM1OhJqkuyiljAEbgDGeOn6LKcRm7ZaiWJTHz5WAfCp8S3BrF4Le gfzruEWulFQkXBAgCV/A0lCHlnbg5Wrh//VZR1jhOVYglGSJ9yECggEBAPl4IzY/ qFRCS+mE3gagrtCVHZd0ZmK+ZN1xzJNVJSx1pVmlP86O/Ds4Gz6h8gGQDTm4vOEM NSI5b85IfLNG9ihp1rDVN/sofObP9xOmU0V2l8D+5qLiZGUNfrGant0fCA5TuLLE Z1PQvV4j3JB5ymvGYlER3vVLUceUX2vA2FfNV/XBV+YS7L0qJJPKJ+ZgfQM+QdBi uD5udcsOR6hwVIxFYRQJrf2vpFJd8c0yzJbQRtqoe15Hj5IIID3qJRX+zmSxVVuW LG6Mmvqx9cLQznC7Tm7ip3KmLg9TNtBgM+pwobdYSdq5Se2XW9/jF1sQpX/yALoV ritzaQ81nok0zG0CggEBAMCbXIeZUOkv5ghOWbd3WgPsDT2LHWdT7keSS3qbxq1Q 05nYzd4frZhzc5cAIaInGxBrMGwUAEGvwCo9siL6p3RYtNNEqBoXdaBDxSPMkZjP j6bIHW8Xxuv8TgyWG7Ut0d3XJ7JM+DlVQN5HSixeLH5/NO1VcpuVl8kVpNf/Samh xhxIWK/YGdXyEBj5Fz4ZS8sG1+g7nS+Oo7NYFpqi8lZExIxbjgAWhueOTL5jqv7j YY/LmBynx9JOccimDADotOHMR8YOZqjF6VOfutrEVcRRt0FFAWE3rTFNk/RTQHUB kNRyif8U52w5PfLGNirjT4gGanLWmN93ojiHS+cc0ccCggEANjtWtElcZ4zOAeLf fWNa/X8dIrqsM1UeikFd7r14ylR88KMK3vWYgastyQzdlldheKXfcbD8sAb6dkat zS9k/d4cTO7dL51aobaeCMiEm7ovPUZsjwZWOt2XKDbryDghwTOUGFC4AbGEKT30 ifff+FP1NZiD9qE8Ev3/TGZs4ZGDSKqSWDLPAkRyFSrCR8O8CYN0PT/ou7G5q73b +BMLXj8K3pdIGrkfKMTBdxFPBo4aiRhNUW6PGHjpwWvwSfrNlXgzjSh1Wkb4vf0H 5yULa9hcMaDWNAdKRgyaAuyW/KUlkrz/uSvElYDs+RveDO7ue4T5gXqLFWkOcr7+ SueOdQKCAQA+2fPl4+f9UD4Wc8rfDbl4Ei136c+cikz1Wg37Yp4ArKGAULlNRLUX TIVpvwc6rNwuDWxtuFElHj7tCdj5hSXj15aaYosgiIs/0x2fwv+4B/Nuj1rbh7zI ATtO7CT6iIs1gyJXErrlqqZSYp0XwVtvo/8Xe4y8rKItYjy6p3nYOww06n7WUwSL RkRugcpN319WT1NhWyK+BCPzQEmQCKBf+mzRCIdcC/3Vzh/I+Skxp+2MSPnIUA/q 1uJWgajlGwr7q/e377ccWAj92t/Ux8DdIXVVfTkoCk0gC5q+XfRouiwSx5W0gZPL QxquAHRopmirkLxn4RNdyOJM5Ammz0a9AoIBAQC2zxu/rdE6T/049GWx8Pr539pe Js/Vs4GxadjdwcYxHQsN+uigfzUqTi4Rdsen+os/3FAbNZOjbWDFQKeGS+ifI8j1 PQzkCL6/ax7aevDmgvnyt7LvVLcuWbJcWVFEk2VytlKK+sOJHI0iuyPqrD4FAEOV gR3IpTQ9PfiUS429LrR1zB2/vSMebQz2kqSZT8oKZosB4MiqZiCPssAEAAEvOdro kFaKqoc8q2rj1qKHshBND764tUHNz0OSeXPM4Dal0RJobCfUPO2bIamm34vsKH2u wmDwRFprLP39jqrO8/CGwoU2NFSqD4Fpzv28sriibucXqP5K3dk/XeSbEXd+ -----END RSA PRIVATE KEY----- ulfius-2.5.2/test/core.c000066400000000000000000000240121341017022300150720ustar00rootroot00000000000000/* Public domain, no copyright. Use at your own risk. */ #include #include #include #include #include #ifndef _WIN32 #include #endif #include #include #include int callback_function_empty(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_CONTINUE; } #ifndef _WIN32 START_TEST(test_ulfius_init_instance) { struct _u_instance u_instance; struct sockaddr_in listen; listen.sin_family = AF_INET; listen.sin_addr.s_addr = htonl (INADDR_ANY); ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, NULL), U_OK); ulfius_clean_instance(&u_instance); ck_assert_int_eq(ulfius_init_instance(&u_instance, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 99999, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, &listen, NULL), U_OK); ulfius_clean_instance(&u_instance); ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, "test_ulfius"), U_OK); ulfius_clean_instance(&u_instance); } END_TEST #else START_TEST(test_ulfius_init_instance) { struct _u_instance u_instance; ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, NULL), U_OK); ulfius_clean_instance(&u_instance); ck_assert_int_eq(ulfius_init_instance(&u_instance, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 99999, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, "test_ulfius"), U_OK); ulfius_clean_instance(&u_instance); } END_TEST #endif START_TEST(test_endpoint) { struct _u_instance u_instance; struct _u_endpoint endpoint; endpoint.http_method = "nope"; endpoint.url_prefix = NULL; endpoint.url_format = NULL; endpoint.priority = 0; endpoint.callback_function = NULL; ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.http_method = NULL; endpoint.url_prefix = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.url_prefix = NULL; endpoint.url_format = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.callback_function = &callback_function_empty; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.url_format = NULL; endpoint.url_prefix = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.url_prefix = NULL; endpoint.http_method = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.http_method = o_strdup("test0"); endpoint.url_prefix = o_strdup("test0"); endpoint.url_format = o_strdup("test0"); ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", NULL, NULL, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", NULL, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, NULL, "nope", 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", "nope", 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", NULL, "nope", 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", "nope", NULL, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", NULL, NULL, 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", NULL, 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, NULL, "nope", 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", "nope", 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "test1", "test1", "test1", 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "test2", NULL, "test2", 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "test3", "test3", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_remove_endpoint(&u_instance, &endpoint), U_OK); ck_assert_int_eq(ulfius_remove_endpoint(&u_instance, &endpoint), U_ERROR_NOT_FOUND); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "nope", "nope", NULL), U_ERROR_NOT_FOUND); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "nope", NULL, "nope"), U_ERROR_NOT_FOUND); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "test1", "test1", "test1"), U_OK); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "test2", NULL, "test2"), U_OK); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "test3", "test3", NULL), U_OK); ck_assert_int_eq(ulfius_set_default_endpoint(&u_instance, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_set_default_endpoint(&u_instance, &callback_function_empty, NULL), U_OK); o_free(endpoint.http_method); o_free(endpoint.url_prefix); o_free(endpoint.url_format); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_start_instance) { struct _u_instance u_instance; ck_assert_int_eq(ulfius_start_framework(NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_start_secure_framework(NULL, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", NULL, "test", 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ck_assert_int_eq(ulfius_stop_framework(&u_instance), U_OK); ck_assert_int_eq(ulfius_start_secure_framework(&u_instance, "error", "error"), U_ERROR_LIBMHD); ck_assert_int_eq(ulfius_start_secure_framework(&u_instance, "-----BEGIN PRIVATE KEY-----\ MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDr90HrswgEmln/\ rXeNqYq0boIvas5wu27hmeHDdGGKtkCWIWGAo9GUy45xqsI4mDl3bOWS+pmb/3yi\ +nhe+BmYHvEqUFo1JfUcVMxaNEbdd9REytMjKdOS+kkLf++BBRoZI/g8DggIu+Ri\ dOSypk+pUECyQxROsyCrB/FgXuKbyC4QNl7fqZxMSpzw7jsWCZiwFv4pu8kMqzDG\ 2wTl/r/4STyK4Pj2TVa/JVzbZbH7VfcjT8MdMsXvKhlmPywjbqo70Hnmt3cnakYF\ X+07ncx/5mjYYd3eSFgiNXr7WNw2rhFKtfTUcjrqSw9FDxmHFWUU76mwJyUo02N9\ ViakSoQpAgMBAAECggEBAJp/VBwdJpzM6yxqyaJpZbXpvTeKuQw6zMjN1nIBG3SV\ DAjAZnSxziGcffGSmoQvt0CoflAT4MuxJkwXrwSPcUKWz9Sis82kwq4AH6TYIaYU\ NVmtazzUwAC1+2maJJjXXFUlpfy8Oypsy4ZjfvIxzmrPbuzI2t0Ej9kr5DDzL3BL\ CWQ/U7w7y4KC0Pnq1ueIzM+UJIfvI0ldUcXHWsAnjyQzwgFBC35qDOfDTw0YUJv+\ ElfFFcGYCA+9wlQyhM/zhAWqKgZ2mwAS6WykgbSc7j4NDjlmZwf4ZuTxbDUV1kBX\ pPH21snqO42CFpw9hRUAA0W0XydCIfUhH8/6tH9enQECgYEA+rM9f6cUk3c7aLWs\ hnauVqJuyGhgCkMyF9sSxgfcs87OVLNuGgaTIfwcT/7oxAY8G7sY44cbk1ZRhh7y\ 6kf01xqiJeXxBQei1qiJxMb2gukvpeY81s2Mg9og5d9qbEhLzp8TdiRJHxLIiGwF\ xOM69CpugKN4T0Zum7EBGeSvmBECgYEA8PRG5SRTE4JwzGtLuTbMbjYTqyEjXAkB\ zo33a92znA0EXEeLCl845EUgzUkSkeN/T/uyWRjj0hrPU99UaaXHt3bc+lrDHrc7\ WFAR3QoAfFFJPIqqwiHcBDdTeAozQ8IOqFIxspl72RukuRdeQR3EdfcF9TUZyUbU\ k8SuRioggpkCgYA2scgnA3KvwYGKlKgxJc9fQ0zcGDlrw8E4BymPXsO9zs6hGAxb\ TTfoYDJlGX361kli22zQpvdTK6/ZjQL+LfiyvTLHBeWRbVsPbfGwpp+9a9ZjYVnA\ m1OeqIYo4Jc9TICNcZMzYTM6vkRVzwtrKw//mQpGsmNbGEilWvaciZHtoQKBgQDo\ FDBQtir6SJIConm9/ETtBlLtai6Xj+lYnK6qC1DaxkLj6tjF9a9jVh3g/DfRopBW\ ZnSCkpGkJcR54Up5s35ofCkdTdxPsmaLihuaje6nztc+Y8VS1LAIs41GunRkF/5s\ KzbI8kIyfAitag+Toms+v93SLwIWNo27gh3lYOANSQKBgQDIidSO3fzB+jzJh7R0\ Yy9ADWbBsLxc8u+sBdxmZBGl+l4YZWNPlQsnsafwcpJWT3le6N7Ri3iuOZw9KiGe\ QDkc7olxUZZ3pshg+cOORK6jVE8v6FeUlLnxpeAWa4C4JDawGPTOBct6bVBl5sxi\ 7GaqDcEK1TSxc4cUaiiPDNNXQA==\ -----END PRIVATE KEY-----", "-----BEGIN CERTIFICATE-----\ MIIDhTCCAm2gAwIBAgIJANrO2RnCbURLMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV\ BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\ aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzA0MjgxNTA0\ NDVaFw0xODA0MjgxNTA0NDVaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l\ LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV\ BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOv3\ QeuzCASaWf+td42pirRugi9qznC7buGZ4cN0YYq2QJYhYYCj0ZTLjnGqwjiYOXds\ 5ZL6mZv/fKL6eF74GZge8SpQWjUl9RxUzFo0Rt131ETK0yMp05L6SQt/74EFGhkj\ +DwOCAi75GJ05LKmT6lQQLJDFE6zIKsH8WBe4pvILhA2Xt+pnExKnPDuOxYJmLAW\ /im7yQyrMMbbBOX+v/hJPIrg+PZNVr8lXNtlsftV9yNPwx0yxe8qGWY/LCNuqjvQ\ eea3dydqRgVf7TudzH/maNhh3d5IWCI1evtY3DauEUq19NRyOupLD0UPGYcVZRTv\ qbAnJSjTY31WJqRKhCkCAwEAAaNQME4wHQYDVR0OBBYEFPFfmGA3jO9koBZNGNZC\ T/dZHZyHMB8GA1UdIwQYMBaAFPFfmGA3jO9koBZNGNZCT/dZHZyHMAwGA1UdEwQF\ MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIc8Yuom4vz82izNEV+9bcCvuabcVwLH\ Qgpv5Nzy/W+1hDoqfMfKNwOSdUB7jZoDaNDG1WhjKGGCLTAx4Hx+q1LwUXvu4Bs1\ woocge65bl85h10l2TxxnlT5BIJezm5r3NiZSwOK2zxxIEyL4vh+b/xqQblBEkR3\ e4/A4Ugn9Egh8GdpF4klGp4MjjpRyAVI7BDaleAhvDSfPmm7ylHJ2y7CLI9ApOQY\ glwRuTmowAZQtaSiE1Ox7QtWj858HDzzTZyFWRG/MNqQptn7AMTPJv3DivNfDNPj\ fYxFAheH3CjryHqqR9DD+d9396W8mqEaUp+plMwSjpcTDSR4rEQkUJg=\ -----END CERTIFICATE-----"), U_OK); ck_assert_int_eq(ulfius_stop_framework(&u_instance), U_OK); ulfius_clean_instance(&u_instance); } END_TEST static Suite *ulfius_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Ulfius core function tests"); tc_core = tcase_create("test_ulfius_core"); tcase_add_test(tc_core, test_ulfius_init_instance); tcase_add_test(tc_core, test_endpoint); tcase_add_test(tc_core, test_ulfius_start_instance); tcase_set_timeout(tc_core, 30); suite_add_tcase(s, tc_core); return s; } int main(int argc, char *argv[]) { int number_failed; Suite *s; SRunner *sr; //y_init_logs("Ulfius", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting Ulfius core tests"); s = ulfius_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); //y_close_logs(); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } ulfius-2.5.2/test/framework.c000066400000000000000000001150641341017022300161470ustar00rootroot00000000000000/* Public domain, no copyright. Use at your own risk. */ #include #include #include #include #include #ifndef _WIN32 #include #else #include #endif #include #include #include #define CERT_KEY "-----BEGIN PRIVATE KEY-----\ MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDr90HrswgEmln/\ rXeNqYq0boIvas5wu27hmeHDdGGKtkCWIWGAo9GUy45xqsI4mDl3bOWS+pmb/3yi\ +nhe+BmYHvEqUFo1JfUcVMxaNEbdd9REytMjKdOS+kkLf++BBRoZI/g8DggIu+Ri\ dOSypk+pUECyQxROsyCrB/FgXuKbyC4QNl7fqZxMSpzw7jsWCZiwFv4pu8kMqzDG\ 2wTl/r/4STyK4Pj2TVa/JVzbZbH7VfcjT8MdMsXvKhlmPywjbqo70Hnmt3cnakYF\ X+07ncx/5mjYYd3eSFgiNXr7WNw2rhFKtfTUcjrqSw9FDxmHFWUU76mwJyUo02N9\ ViakSoQpAgMBAAECggEBAJp/VBwdJpzM6yxqyaJpZbXpvTeKuQw6zMjN1nIBG3SV\ DAjAZnSxziGcffGSmoQvt0CoflAT4MuxJkwXrwSPcUKWz9Sis82kwq4AH6TYIaYU\ NVmtazzUwAC1+2maJJjXXFUlpfy8Oypsy4ZjfvIxzmrPbuzI2t0Ej9kr5DDzL3BL\ CWQ/U7w7y4KC0Pnq1ueIzM+UJIfvI0ldUcXHWsAnjyQzwgFBC35qDOfDTw0YUJv+\ ElfFFcGYCA+9wlQyhM/zhAWqKgZ2mwAS6WykgbSc7j4NDjlmZwf4ZuTxbDUV1kBX\ pPH21snqO42CFpw9hRUAA0W0XydCIfUhH8/6tH9enQECgYEA+rM9f6cUk3c7aLWs\ hnauVqJuyGhgCkMyF9sSxgfcs87OVLNuGgaTIfwcT/7oxAY8G7sY44cbk1ZRhh7y\ 6kf01xqiJeXxBQei1qiJxMb2gukvpeY81s2Mg9og5d9qbEhLzp8TdiRJHxLIiGwF\ xOM69CpugKN4T0Zum7EBGeSvmBECgYEA8PRG5SRTE4JwzGtLuTbMbjYTqyEjXAkB\ zo33a92znA0EXEeLCl845EUgzUkSkeN/T/uyWRjj0hrPU99UaaXHt3bc+lrDHrc7\ WFAR3QoAfFFJPIqqwiHcBDdTeAozQ8IOqFIxspl72RukuRdeQR3EdfcF9TUZyUbU\ k8SuRioggpkCgYA2scgnA3KvwYGKlKgxJc9fQ0zcGDlrw8E4BymPXsO9zs6hGAxb\ TTfoYDJlGX361kli22zQpvdTK6/ZjQL+LfiyvTLHBeWRbVsPbfGwpp+9a9ZjYVnA\ m1OeqIYo4Jc9TICNcZMzYTM6vkRVzwtrKw//mQpGsmNbGEilWvaciZHtoQKBgQDo\ FDBQtir6SJIConm9/ETtBlLtai6Xj+lYnK6qC1DaxkLj6tjF9a9jVh3g/DfRopBW\ ZnSCkpGkJcR54Up5s35ofCkdTdxPsmaLihuaje6nztc+Y8VS1LAIs41GunRkF/5s\ KzbI8kIyfAitag+Toms+v93SLwIWNo27gh3lYOANSQKBgQDIidSO3fzB+jzJh7R0\ Yy9ADWbBsLxc8u+sBdxmZBGl+l4YZWNPlQsnsafwcpJWT3le6N7Ri3iuOZw9KiGe\ QDkc7olxUZZ3pshg+cOORK6jVE8v6FeUlLnxpeAWa4C4JDawGPTOBct6bVBl5sxi\ 7GaqDcEK1TSxc4cUaiiPDNNXQA==\ -----END PRIVATE KEY-----" #define CERT_PEM "-----BEGIN CERTIFICATE-----\ MIIDhTCCAm2gAwIBAgIJANrO2RnCbURLMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV\ BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\ aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzA0MjgxNTA0\ NDVaFw0xODA0MjgxNTA0NDVaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l\ LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV\ BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOv3\ QeuzCASaWf+td42pirRugi9qznC7buGZ4cN0YYq2QJYhYYCj0ZTLjnGqwjiYOXds\ 5ZL6mZv/fKL6eF74GZge8SpQWjUl9RxUzFo0Rt131ETK0yMp05L6SQt/74EFGhkj\ +DwOCAi75GJ05LKmT6lQQLJDFE6zIKsH8WBe4pvILhA2Xt+pnExKnPDuOxYJmLAW\ /im7yQyrMMbbBOX+v/hJPIrg+PZNVr8lXNtlsftV9yNPwx0yxe8qGWY/LCNuqjvQ\ eea3dydqRgVf7TudzH/maNhh3d5IWCI1evtY3DauEUq19NRyOupLD0UPGYcVZRTv\ qbAnJSjTY31WJqRKhCkCAwEAAaNQME4wHQYDVR0OBBYEFPFfmGA3jO9koBZNGNZC\ T/dZHZyHMB8GA1UdIwQYMBaAFPFfmGA3jO9koBZNGNZCT/dZHZyHMAwGA1UdEwQF\ MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIc8Yuom4vz82izNEV+9bcCvuabcVwLH\ Qgpv5Nzy/W+1hDoqfMfKNwOSdUB7jZoDaNDG1WhjKGGCLTAx4Hx+q1LwUXvu4Bs1\ woocge65bl85h10l2TxxnlT5BIJezm5r3NiZSwOK2zxxIEyL4vh+b/xqQblBEkR3\ e4/A4Ugn9Egh8GdpF4klGp4MjjpRyAVI7BDaleAhvDSfPmm7ylHJ2y7CLI9ApOQY\ glwRuTmowAZQtaSiE1Ox7QtWj858HDzzTZyFWRG/MNqQptn7AMTPJv3DivNfDNPj\ fYxFAheH3CjryHqqR9DD+d9396W8mqEaUp+plMwSjpcTDSR4rEQkUJg=\ -----END CERTIFICATE-----" #define CERT_CA "-----BEGIN CERTIFICATE-----\ MIIF9zCCA9+gAwIBAgIUQhoxaAC0kHeq1DNRL8fA0/WZCsYwDQYJKoZIhvcNAQEL\ BQAwgYoxCzAJBgNVBAYTAkNBMQ8wDQYDVQQIDAZRdWViZWMxDzANBgNVBAcMBlF1\ ZWJlYzEPMA0GA1UECgwGVWxmaXVzMRAwDgYDVQQLDAd0ZXN0LWNhMRIwEAYDVQQD\ DAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0BCQEWE3dlYm1hc3RlckBsb2NhbGhvc3Qw\ HhcNMTgxMjAyMTkzNTA1WhcNMTkxMjAyMTkzNTA1WjCBijELMAkGA1UEBhMCQ0Ex\ DzANBgNVBAgMBlF1ZWJlYzEPMA0GA1UEBwwGUXVlYmVjMQ8wDQYDVQQKDAZVbGZp\ dXMxEDAOBgNVBAsMB3Rlc3QtY2ExEjAQBgNVBAMMCWxvY2FsaG9zdDEiMCAGCSqG\ SIb3DQEJARYTd2VibWFzdGVyQGxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQAD\ ggIPADCCAgoCggIBALG4KdvHcAsKqaNe4RPVvGUA7P+N9+8vs8dTBQ16VEUGHCp5\ bWdqpvWdiHfxKmCZVtVUkYGwOsFmsbOsAfQgF1gcqTFyIRCwdt2PbvWso2n3S+aJ\ gCdqzYMpCoW09/1izAGqXVWrWhtBEFJiFSx0wbkzEF87ATLv1eox0g1C3y9LuIv0\ T1nK36jPzC/S7hazCd4IXlzPMy858VJn8qv09TeexYLEEcvn70vTQzoOKQKAWTDO\ BgwSDcfmez2uXktIuJR7EW3weaj6xJQWpnB8eb+A3H82TtP/EhB20ipc/X/lNBaj\ 58yKj5XJwVAoteIGYDV7PBzwFhyqhbErDNsMDcR2CrNLIzpdG/BpIwVbAyjoYCUm\ OGxw8k+WXfyvTyS9TTi//sZbNRaVGAir63LFwCh0YbJ36DfKpjPrUoqDkO93iX3C\ x12Nq5Mry8v6PgQsPt+sqZsDYW2BU0ib/pb4cksgBZakJcMFLcYi5tlCqFis1ehn\ QjAZWn+kGCc4Tmy/l9mBBdRiYpP77QH6QVh4bPHzxaYiOoLfBajE6dWCXdLYOu+m\ oZQmlcRWuP5AhKi0QAaI1/RAKGpO0dEUH7Dnl5TqR8twC88ZetkZPEJYHb3Vsy7U\ aA0EvoHi1xTdh0tPN7AL0RRD8ekyh273CUMHg3vI0qjv29MWSBzCDa9X2SL5AgMB\ AAGjUzBRMB0GA1UdDgQWBBQkE2nQu0XaDkJdLSQsgjTIXULLGDAfBgNVHSMEGDAW\ gBQkE2nQu0XaDkJdLSQsgjTIXULLGDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3\ DQEBCwUAA4ICAQASrWMYrsMtIfUsN3srEAZqutcVAN7PWbzVNAjWXEqbw/HV7m8J\ w09MTfmwmQEhw4d7fwzfax5vqKeqfRb7i8f+HzR8b9egCn8n4hWUjiAhS1Naj8OP\ RCrvPyVI1/h3iMZZXlITljtgk7rZn5Yv/WjKz/l32KIHwMzmk5aTGkmr0oyWxj1Y\ eJymZ0RYih+X4hUvAlW3a3AlKbvq7KW9bYU4LHJWixgP16tXOR8k0aPKpyVuNCzN\ 7/23vfPeawQuW7C23+IuDuPyrpivw4dbcKNcy1KfGXmc8kQDIv6uPqdWa5ngvVua\ SHb23nA4HG3k5ed8iqarDmu4nU9TTu8qTaMKSuEAjRsPnehO/Fi/m3mMSnGEXxa1\ WWl1wZxvFoD4IsexNZaZcii9f0Ctn3KbL7HwlJliogc2jDVHheS5aVsVnl2UsNJf\ x4nYzPZ6bdLawcviwo9eQWXlEjoKYyBTL1LFNt9S/iEW3WUOaaLJVH7+uHOn4/6N\ wiMh3OqcyePyB+MKr3tvsmYZyYVwCW/pRmATM+F4ol23OWhXyYN+AFdXiMBjwdUX\ jAwgDammq/ymxfgKE7IX8z9+f+8UVcBaiXVUTWzgSHnjjPW8+9WExUws8BFD7+81\ G66c7c4qxP2fq5vQiYJUVEpNd4Z4+EbvMDrg4CsTVaoI1OWlHWcBfNHupw==\ -----END CERTIFICATE-----" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, value is %s", keys[i], value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s", keys[i], value); if (to_return != NULL) { len = o_strlen(to_return) + o_strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (o_strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((o_strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } int callback_function_empty(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_CONTINUE; } int callback_function_return_url(const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, request->http_url); return U_CALLBACK_CONTINUE; } int callback_function_error(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_ERROR; } int callback_function_unauthorized(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_UNAUTHORIZED; } int callback_function_check_auth(const struct _u_request * request, struct _u_response * response, void * user_data) { if (o_strcmp("user", request->auth_basic_user) == 0 && o_strcmp("password", request->auth_basic_password) == 0) { return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_UNAUTHORIZED; } } int callback_function_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * param3, * body; if (u_map_has_key(request->map_url, "param3")) { param3 = msprintf(", param3 is %s", u_map_get(request->map_url, "param3")); } else { param3 = o_strdup(""); } body = msprintf("param1 is %s, param2 is %s%s", u_map_get(request->map_url, "param1"), u_map_get(request->map_url, "param2"), param3); ulfius_set_string_body_response(response, 200, body); o_free(body); o_free(param3); return U_CALLBACK_CONTINUE; } int callback_function_body_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * body; body = msprintf("param1 is %s, param2 is %s", u_map_get(request->map_post_body, "param1"), u_map_get(request->map_post_body, "param2")); ulfius_set_string_body_response(response, 200, body); o_free(body); return U_CALLBACK_CONTINUE; } int callback_function_header_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * body; body = msprintf("param1 is %s, param2 is %s", u_map_get(request->map_header, "param1"), u_map_get(request->map_header, "param2")); ulfius_set_string_body_response(response, 200, body); o_free(body); return U_CALLBACK_CONTINUE; } int callback_function_cookie_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * body; body = msprintf("param1 is %s", u_map_get(request->map_cookie, "param1")); ck_assert_int_eq(ulfius_set_string_body_response(response, 200, body), U_OK); ck_assert_int_eq(ulfius_add_cookie_to_response(response, "param2", "value_cookie", NULL, 100, "localhost", "/cookie", 0, 1), U_OK); ck_assert_int_eq(ulfius_add_same_site_cookie_to_response(response, "cookieSameSiteStrict", "value_cookie", NULL, 100, "localhost", "/cookie", 0, 1, U_COOKIE_SAME_SITE_STRICT), U_OK); ck_assert_int_eq(ulfius_add_same_site_cookie_to_response(response, "cookieSameSiteLax", "value_cookie", NULL, 100, "localhost", "/cookie", 0, 1, U_COOKIE_SAME_SITE_LAX), U_OK); ck_assert_int_eq(ulfius_add_same_site_cookie_to_response(response, "cookieSameSiteNone", "value_cookie", NULL, 100, "localhost", "/cookie", 0, 1, U_COOKIE_SAME_SITE_NONE), U_OK); ck_assert_int_ne(ulfius_add_same_site_cookie_to_response(response, "cookieSameSiteError", "value_cookie", NULL, 100, "localhost", "/cookie", 0, 1, 42), U_OK); o_free(body); return U_CALLBACK_CONTINUE; } int callback_function_multiple_continue(const struct _u_request * request, struct _u_response * response, void * user_data) { if (response->binary_body != NULL) { char * body = msprintf("%.*s\n%s", response->binary_body_length, (char*)response->binary_body, request->http_url); ulfius_set_string_body_response(response, 200, body); o_free(body); } else { ulfius_set_string_body_response(response, 200, request->http_url); } return U_CALLBACK_CONTINUE; } int callback_function_multiple_complete(const struct _u_request * request, struct _u_response * response, void * user_data) { if (response->binary_body != NULL) { char * body = msprintf("%.*s\n%s", response->binary_body_length, (char*)response->binary_body, request->http_url); ulfius_set_string_body_response(response, 200, body); o_free(body); } else { ulfius_set_string_body_response(response, 200, request->http_url); } return U_CALLBACK_COMPLETE; } ssize_t stream_data (void * cls, uint64_t pos, char * buf, size_t max) { usleep(100); if (pos <= 100) { snprintf(buf, max, "%s %" PRIu64 "\n", (char *)cls, pos + 1); return o_strlen(buf); } else { return MHD_CONTENT_READER_END_OF_STREAM; } } void free_stream_data(void * cls) { o_free(cls); } int callback_function_stream (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_stream_response(response, 200, stream_data, free_stream_data, U_STREAM_SIZE_UNKOWN, 32 * 1024, o_strdup("stream test")); return U_OK; } size_t my_write_body(void * contents, size_t size, size_t nmemb, void * user_data) { ck_assert_int_eq(o_strncmp((char *)contents, "stream test ", o_strlen("stream test ")), 0); ck_assert_int_ne(strtol((char *)contents + o_strlen("stream test "), NULL, 10), 0); return size * nmemb; } int callback_check_utf8_ignored(const struct _u_request * request, struct _u_response * response, void * user_data) { ck_assert_int_eq(u_map_has_key(request->map_header, "utf8_param"), 0); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param1"), 0); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param2"), 0); ck_assert_int_eq(u_map_has_key(request->map_post_body, "utf8_param"), 0); ck_assert_int_eq(u_map_has_key(request->map_header, "utf8_param_valid"), 1); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param_valid1"), 1); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param_valid2"), 1); ck_assert_int_eq(u_map_has_key(request->map_post_body, "utf8_param_valid"), 1); return U_OK; } int callback_check_utf8_not_ignored(const struct _u_request * request, struct _u_response * response, void * user_data) { ck_assert_int_eq(u_map_has_key(request->map_header, "utf8_param"), 1); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param1"), 1); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param2"), 1); ck_assert_int_eq(u_map_has_key(request->map_post_body, "utf8_param"), 1); ck_assert_int_eq(u_map_has_key(request->map_header, "utf8_param_valid"), 1); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param_valid1"), 1); ck_assert_int_eq(u_map_has_key(request->map_url, "utf8_param_valid2"), 1); ck_assert_int_eq(u_map_has_key(request->map_post_body, "utf8_param_valid"), 1); return U_OK; } #ifndef U_DISABLE_WEBSOCKET int callback_auth_client_cert (const struct _u_request * request, struct _u_response * response, void * user_data) { char * dn; size_t lbuf = 0; ck_assert_ptr_ne(request->client_cert, NULL); gnutls_x509_crt_get_dn(request->client_cert, NULL, &lbuf); dn = o_malloc(lbuf + 1); gnutls_x509_crt_get_dn(request->client_cert, dn, &lbuf); dn[lbuf] = '\0'; ck_assert_str_eq(dn, "C=CA,ST=Quebec,L=Quebec,O=Ulfius,OU=test-client,CN=localhost,EMAIL=webmaster@localhost"); ck_assert_int_eq(ulfius_set_string_body_response(response, 200, dn), U_OK); o_free(dn); return U_CALLBACK_CONTINUE; } int callback_no_auth_client_cert (const struct _u_request * request, struct _u_response * response, void * user_data) { ck_assert_ptr_eq(request->client_cert, NULL); return U_CALLBACK_CONTINUE; } #endif START_TEST(test_ulfius_simple_endpoint) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "POST", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "PUT", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "DELETE", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "url", NULL, 0, &callback_function_return_url, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "*", "url", NULL, 0, &callback_function_return_url, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "error", NULL, 0, &callback_function_error, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "unauthorized", NULL, 0, &callback_function_unauthorized, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "check_auth", NULL, 0, &callback_function_check_auth, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/nope"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("POST"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("PUT"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("DELETE"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("OPTION"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/url"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/url", o_strlen("/url")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("OPTION"); request.http_url = o_strdup("http://localhost:8080/url"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/url", o_strlen("/url")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("test"); request.http_url = o_strdup("http://localhost:8080/url"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/url", o_strlen("/url")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/error"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 500); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/unauthorized"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 401); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/check_auth"); request.auth_basic_user = o_strdup("nope"); request.auth_basic_password = o_strdup("nope"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 401); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/check_auth"); request.auth_basic_user = o_strdup("user"); request.auth_basic_password = o_strdup("password"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_parameters) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; const char * set_cookie; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "param", "/:param1/@param2/", 0, &callback_function_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "param", "/:param1/@param2/:param3/:param3", 0, &callback_function_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "POST", "param", NULL, 0, &callback_function_body_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "header", NULL, 0, &callback_function_header_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "cookie", NULL, 0, &callback_function_cookie_param, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/param/value1/value2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value1, param2 is value2", o_strlen("param1 is value1, param2 is value2")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/param/value1/value2/value3.1/value3.2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value1, param2 is value2, param3 is value3.1,value3.2", o_strlen("param1 is value1, param2 is value2, param3 is value3.1,value3.2")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("POST"); request.http_url = o_strdup("http://localhost:8080/param/"); u_map_put(request.map_post_body, "param1", "value3"); u_map_put(request.map_post_body, "param2", "value4"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value3, param2 is value4", o_strlen("param1 is value3, param2 is value4")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/header"); u_map_put(request.map_header, "param1", "value5"); u_map_put(request.map_header, "param2", "value6"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value5, param2 is value6", o_strlen("param1 is value5, param2 is value6")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/cookie"); u_map_put(request.map_cookie, "param1", "value7"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value7", o_strlen("param1 is value7")), 0); set_cookie = u_map_get(response.map_header, "Set-Cookie"); ck_assert_ptr_ne(o_strstr(set_cookie, "param2=value_cookie; Max-Age=100; Domain=localhost; Path=/cookie; HttpOnly"), NULL); ck_assert_ptr_ne(o_strstr(set_cookie, "cookieSameSiteStrict=value_cookie; Max-Age=100; Domain=localhost; Path=/cookie; HttpOnly; SameSite=Strict"), NULL); ck_assert_ptr_ne(o_strstr(set_cookie, "cookieSameSiteLax=value_cookie; Max-Age=100; Domain=localhost; Path=/cookie; HttpOnly; SameSite=Lax"), NULL); ck_assert_ptr_ne(o_strstr(set_cookie, "cookieSameSiteNone=value_cookie; Max-Age=100; Domain=localhost; Path=/cookie; HttpOnly"), NULL); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_injection) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "inject1", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject1"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "inject2", NULL, 0, &callback_function_empty, NULL), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "GET", "inject2", NULL), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_multiple) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "*", 0, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "/:param1/*", 1, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "/:param1/:param2/*", 2, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "/:param1/:param2/:param3", 3, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "*", 0, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "/:param1/*", 1, &callback_function_multiple_complete, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "/:param1/:param2/*", 2, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "/:param1/:param2/:param3", 3, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple", o_strlen("/multiple")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple/value1"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple/value1\n/multiple/value1", o_strlen("/multiple/value1\n/multiple/value1")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple/value1/value2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple/value1/value2\n/multiple/value1/value2\n/multiple/value1/value2", o_strlen("/multiple/value1/value2\n/multiple/value1/value2\n/multiple/value1/value2")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple/value1/value2/value3"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3", o_strlen("/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/nope/value1/value2/value3/value4"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple_complete/value1/value2/value3"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple_complete/value1/value2/value3\n/multiple_complete/value1/value2/value3", o_strlen("/multiple_complete/value1/value2/value3\n/multiple_complete/value1/value2/value3")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_stream) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "stream", NULL, 0, &callback_function_stream, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/stream"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_streaming_request(&request, &response, my_write_body, NULL), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_utf8_not_ignored) { char * invalid_utf8_seq2 = msprintf("value %c%c", 0xC3, 0x28); char * invalid_utf8_seq3 = msprintf("value %c%c%c", 0xE2, 0x28, 0xA1); char * invalid_utf8_seq4 = msprintf("value %c%c%c%c", 0xF0, 0x90, 0x28, 0xBC); char * valid_utf8 = "valid value ȸ Ɇ ɤ ¯\\_(ツ)_/¯"; struct _u_instance u_instance; struct _u_request request; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); u_instance.check_utf8 = 0; ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "POST", "utf8", ":utf8_param1/:utf8_param_valid", 0, &callback_check_utf8_not_ignored, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = msprintf("http://localhost:8080/utf8/%s/%s/?utf8_param3=%s&utf8_param_valid2=%s&utf8_param_empty=&utf8_param_null", invalid_utf8_seq3, valid_utf8, invalid_utf8_seq2, valid_utf8); request.http_verb = o_strdup("POST"); u_map_put(request.map_header, "utf8_param", invalid_utf8_seq3); u_map_put(request.map_header, "utf8_param_valid", valid_utf8); u_map_put(request.map_post_body, "utf8_param", invalid_utf8_seq4); u_map_put(request.map_post_body, "utf8_param_valid", valid_utf8); u_map_put(request.map_post_body, "utf8_param_empty", ""); ck_assert_int_eq(ulfius_send_http_request(&request, NULL), U_OK); ulfius_clean_request(&request); o_free(invalid_utf8_seq2); o_free(invalid_utf8_seq3); o_free(invalid_utf8_seq4); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_utf8_ignored) { char * invalid_utf8_seq2 = msprintf("invalid value %c%c", 0xC3, 0x28); char * invalid_utf8_seq3 = msprintf("invalid value %c%c%c", 0xE2, 0x28, 0xA1); char * invalid_utf8_seq4 = msprintf("invalid value %c%c%c%c", 0xF0, 0x90, 0x28, 0xBC); char * valid_utf8 = "valid value ȸ Ɇ ɤ ¯\\_(ツ)_/¯"; struct _u_instance u_instance; struct _u_request request; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(u_instance.check_utf8, 1); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "POST", "utf8", ":utf8_param1/:utf8_param_valid", 0, &callback_check_utf8_ignored, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = msprintf("http://localhost:8080/utf8/%s/%s/?utf8_param3=%s&utf8_param_valid2=%s", invalid_utf8_seq3, valid_utf8, invalid_utf8_seq2, valid_utf8); request.http_verb = o_strdup("POST"); u_map_put(request.map_header, "utf8_param", invalid_utf8_seq3); u_map_put(request.map_header, "utf8_param_valid", valid_utf8); u_map_put(request.map_post_body, "utf8_param", invalid_utf8_seq4); u_map_put(request.map_post_body, "utf8_param_valid", valid_utf8); ck_assert_int_eq(ulfius_send_http_request(&request, NULL), U_OK); ulfius_clean_request(&request); o_free(invalid_utf8_seq2); o_free(invalid_utf8_seq3); o_free(invalid_utf8_seq4); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST #ifndef U_DISABLE_WEBSOCKET START_TEST(test_ulfius_server_ca_trust) { struct _u_instance u_instance; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "stream", NULL, 0, &callback_function_stream, NULL), U_OK); ck_assert_int_ne(ulfius_start_secure_ca_trust_framework(&u_instance, NULL, CERT_PEM, CERT_CA), U_OK); ck_assert_int_ne(ulfius_start_secure_ca_trust_framework(&u_instance, CERT_KEY, NULL, CERT_CA), U_OK); ck_assert_int_eq(ulfius_start_secure_ca_trust_framework(&u_instance, CERT_KEY, CERT_PEM, CERT_CA), U_OK); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_client_certificate) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "cert_auth", NULL, 0, &callback_auth_client_cert, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "nocert_auth", NULL, 0, &callback_no_auth_client_cert, NULL), U_OK); ck_assert_int_eq(ulfius_start_secure_ca_trust_framework(&u_instance, CERT_KEY, CERT_PEM, CERT_CA), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("https://localhost:8080/nocert_auth"); request.check_server_certificate = 0; request.client_key_file = o_strdup("client.key"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("https://localhost:8080/nocert_auth"); request.check_server_certificate = 0; request.client_cert_file = o_strdup("client.crt"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("https://localhost:8080/cert_auth"); request.check_server_certificate = 0; request.client_cert_file = o_strdup("client.crt"); request.client_key_file = o_strdup("client.key"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_stop_framework(&u_instance); ulfius_clean_instance(&u_instance); } END_TEST #endif static Suite *ulfius_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Ulfius framework function tests"); tc_core = tcase_create("test_ulfius_framework"); tcase_add_test(tc_core, test_ulfius_simple_endpoint); tcase_add_test(tc_core, test_ulfius_endpoint_parameters); tcase_add_test(tc_core, test_ulfius_endpoint_injection); tcase_add_test(tc_core, test_ulfius_endpoint_multiple); tcase_add_test(tc_core, test_ulfius_endpoint_stream); tcase_add_test(tc_core, test_ulfius_utf8_not_ignored); tcase_add_test(tc_core, test_ulfius_utf8_ignored); #ifndef U_DISABLE_WEBSOCKET tcase_add_test(tc_core, test_ulfius_server_ca_trust); tcase_add_test(tc_core, test_ulfius_client_certificate); #endif tcase_set_timeout(tc_core, 30); suite_add_tcase(s, tc_core); return s; } int main(int argc, char *argv[]) { int number_failed; Suite *s; SRunner *sr; //y_init_logs("Ulfius", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting Ulfius core tests"); s = ulfius_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); //y_close_logs(); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } ulfius-2.5.2/test/u_map.c000066400000000000000000000167621341017022300152600ustar00rootroot00000000000000/* Public domain, no copyright. Use at your own risk. */ #include #include #include #include #include #include #include START_TEST(test_u_map_init) { struct _u_map map; ck_assert_int_eq(u_map_init(&map), U_OK); ck_assert_int_eq(u_map_init(NULL), U_ERROR_PARAMS); u_map_clean(&map); } END_TEST START_TEST(test_u_map_put) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key", "value"), U_OK); ck_assert_int_eq(u_map_put(&map, "key", NULL), U_OK); ck_assert_int_eq(u_map_put(&map, NULL, "value"), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put(&map, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put(NULL, "key", "value"), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put(&map, "key", "new_value"), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "bkey", "value", 0, o_strlen("value")), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "bkey", NULL, 0, o_strlen("value")), U_OK); ck_assert_int_eq(u_map_put_binary(&map, NULL, "value", 0, o_strlen("value")), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put_binary(&map, NULL, NULL, 0, o_strlen("value")), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put_binary(NULL, "bkey", "value", 0, o_strlen("value")), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put_binary(&map, "bkey", "new_value", 0, o_strlen("new_value")), U_OK); u_map_clean(&map); } END_TEST START_TEST(test_u_map_get) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key", "value"), U_OK); ck_assert_str_eq(u_map_get(&map, "key"), "value"); ck_assert_int_eq(u_map_get_length(&map, "key"), 6); ck_assert_ptr_eq((void *)u_map_get(&map, "nope"), NULL); ck_assert_ptr_eq((void *)u_map_get(NULL, "key"), NULL); ck_assert_int_eq(u_map_get_length(&map, "nope"), -1); ck_assert_int_eq(u_map_get_length(NULL, "key"), -1); ck_assert_str_eq(u_map_get_case(&map, "Key"), "value"); ck_assert_int_eq(u_map_get_case_length(&map, "kEy"), 6); ck_assert_ptr_eq((void *)u_map_get_case(&map, "noPE"), NULL); ck_assert_ptr_eq((void *)u_map_get_case(NULL, "keY"), NULL); ck_assert_int_eq(u_map_get_case_length(&map, "noPe"), -1); ck_assert_int_eq(u_map_get_case_length(NULL, "KEy"), -1); u_map_clean(&map); } END_TEST START_TEST(test_u_map_get_case) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key", "value"), U_OK); ck_assert_str_eq(u_map_get(&map, "key"), "value"); ck_assert_int_eq(u_map_get_case_length(&map, "key"), 6); ck_assert_ptr_eq((void *)u_map_get_case(&map, "nope"), NULL); ck_assert_ptr_eq((void *)u_map_get_case(NULL, "key"), NULL); ck_assert_int_eq(u_map_get_case_length(&map, "nope"), -1); ck_assert_int_eq(u_map_get_case_length(NULL, "key"), -1); u_map_clean(&map); } END_TEST START_TEST(test_u_map_enum) { struct _u_map map; const char ** enum_keys, ** enum_values; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_put(&map, "key4", "value4"), U_OK); enum_keys = u_map_enum_keys(&map); enum_values = u_map_enum_values(&map); ck_assert_ptr_ne(enum_keys, NULL); ck_assert_ptr_ne(enum_values, NULL); ck_assert_int_eq(u_map_count(&map), 4); u_map_clean(&map); } END_TEST START_TEST(test_u_map_has) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "key4", "value4", 0, o_strlen("value4")), U_OK); ck_assert_int_eq(u_map_has_key(&map, "key3"), 1); ck_assert_int_eq(u_map_has_key(&map, "nope"), 0); ck_assert_int_eq(u_map_has_value(&map, "value1"), 1); ck_assert_int_eq(u_map_has_value(&map, "nope"), 0); ck_assert_int_eq(u_map_has_value_binary(&map, "value4", o_strlen("value4")), 1); ck_assert_int_eq(u_map_has_value_binary(&map, "nope", o_strlen("nope")), 0); ck_assert_int_eq(u_map_has_key_case(&map, "Key3"), 1); ck_assert_int_eq(u_map_has_key_case(&map, "Nope"), 0); ck_assert_int_eq(u_map_has_value_case(&map, "ValuE1"), 1); ck_assert_int_eq(u_map_has_value_case(&map, "nOpe"), 0); u_map_clean(&map); } END_TEST START_TEST(test_u_map_remove) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_put(&map, "key4", "value4"), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "key5", "value5", 0, o_strlen("value5")), U_OK); ck_assert_int_eq(u_map_put(&map, "key6", "value6"), U_OK); ck_assert_int_eq(u_map_put(&map, "key7", "value7"), U_OK); ck_assert_int_eq(u_map_put(&map, "key8", "value8"), U_OK); ck_assert_int_eq(u_map_remove_from_key(&map, "key1"), U_OK); ck_assert_int_eq(u_map_remove_from_key(&map, "nope"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_key_case(&map, "Key2"), U_OK); ck_assert_int_eq(u_map_remove_from_key_case(&map, "nOPe"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_value(&map, "value3"), U_OK); ck_assert_int_eq(u_map_remove_from_value(&map, "nope"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_value_case(&map, "value4"), U_OK); ck_assert_int_eq(u_map_remove_from_value_case(&map, "nOPe"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_value_binary(&map, "value5", o_strlen("value5")), U_OK); ck_assert_int_eq(u_map_remove_from_value_binary(&map, "nope", o_strlen("nope")), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_at(&map, 1), U_OK); ck_assert_int_eq(u_map_remove_at(&map, 10), U_ERROR_NOT_FOUND); u_map_clean(&map); } END_TEST START_TEST(test_u_map_copy_empty) { struct _u_map map, copy_map; u_map_init(&map); u_map_init(©_map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_copy_into(©_map, &map), U_OK); ck_assert_int_eq(u_map_copy_into(NULL, &map), U_ERROR_PARAMS); ck_assert_int_eq(u_map_copy_into(©_map, NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_copy_into(NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_count(&map), 3); ck_assert_int_eq(u_map_count(©_map), 3); ck_assert_int_eq(u_map_empty(©_map), U_OK); ck_assert_int_eq(u_map_empty(NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_count(©_map), 0); u_map_clean(&map); u_map_clean(©_map); } END_TEST static Suite *ulfius_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Ulfius struct _u_map function tests"); tc_core = tcase_create("test_ulfius_u_map"); tcase_add_test(tc_core, test_u_map_init); tcase_add_test(tc_core, test_u_map_put); tcase_add_test(tc_core, test_u_map_get); tcase_add_test(tc_core, test_u_map_get_case); tcase_add_test(tc_core, test_u_map_enum); tcase_add_test(tc_core, test_u_map_has); tcase_add_test(tc_core, test_u_map_remove); tcase_add_test(tc_core, test_u_map_copy_empty); tcase_set_timeout(tc_core, 30); suite_add_tcase(s, tc_core); return s; } int main(int argc, char *argv[]) { int number_failed; Suite *s; SRunner *sr; s = ulfius_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } ulfius-2.5.2/test/websocket.c000066400000000000000000000175601341017022300161420ustar00rootroot00000000000000/* Public domain, no copyright. Use at your own risk. */ #include #include #include #include #include #include #include #define WEBSOCKET_URL "http://localhost:8378/websocket" #define DEFAULT_PROTOCOL "proto" #define DEFAULT_EXTENSION "ext" #define DEFAULT_MESSAGE "message content with a few characters" #define PORT 9275 #define PREFIX_WEBSOCKET "/websocket" #ifndef U_DISABLE_WEBSOCKET void websocket_manager_callback_empty (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { } void websocket_incoming_message_callback_empty (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data) { } void websocket_onclose_callback_empty (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data) { } void websocket_echo_message_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * last_message, void * websocket_incoming_message_user_data) { if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) { ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, last_message->data_len, last_message->data), U_OK); } } void websocket_manager_callback_client (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { int i; for (i=0; i<4; i++) { if (ulfius_websocket_wait_close(websocket_manager, 50) == U_WEBSOCKET_STATUS_OPEN) { if (i%2) { ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(DEFAULT_MESSAGE), DEFAULT_MESSAGE), U_OK); } else { ck_assert_int_eq(ulfius_websocket_send_fragmented_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(DEFAULT_MESSAGE), DEFAULT_MESSAGE, (o_strlen(DEFAULT_MESSAGE)/4)), U_OK); } } } } void websocket_incoming_message_callback_client (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data) { ck_assert_int_eq(0, o_strncmp(message->data, DEFAULT_MESSAGE, message->data_len)); } int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data) { int ret; ret = ulfius_set_websocket_response(response, NULL, NULL, NULL, NULL, &websocket_echo_message_callback, NULL, NULL, NULL); ck_assert_int_eq(ret, U_OK); return (ret == U_OK)?U_CALLBACK_CONTINUE:U_CALLBACK_ERROR; } START_TEST(test_websocket_ulfius_set_websocket_response) { struct _u_response response; ulfius_init_response(&response); ck_assert_int_eq(ulfius_set_websocket_response(NULL, DEFAULT_PROTOCOL, DEFAULT_EXTENSION, &websocket_manager_callback_empty, NULL, NULL, NULL, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_set_websocket_response(&response, DEFAULT_PROTOCOL, DEFAULT_EXTENSION, NULL, NULL, NULL, NULL, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_set_websocket_response(&response, DEFAULT_PROTOCOL, DEFAULT_EXTENSION, NULL, NULL, NULL, NULL, &websocket_onclose_callback_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_set_websocket_response(&response, DEFAULT_PROTOCOL, DEFAULT_EXTENSION, &websocket_manager_callback_empty, NULL, NULL, NULL, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_set_websocket_response(&response, DEFAULT_PROTOCOL, DEFAULT_EXTENSION, NULL, NULL, &websocket_incoming_message_callback_empty, NULL, NULL, NULL), U_OK); ulfius_clean_response(&response); } END_TEST START_TEST(test_websocket_ulfius_set_websocket_request) { struct _u_request request; ulfius_init_request(&request); ck_assert_int_eq(ulfius_set_websocket_request(NULL, WEBSOCKET_URL, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_ERROR); ck_assert_int_eq(ulfius_set_websocket_request(&request, NULL, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_ERROR); ck_assert_int_eq(ulfius_set_websocket_request(&request, WEBSOCKET_URL, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_OK); ck_assert_int_eq(ulfius_set_websocket_request(&request, WEBSOCKET_URL, NULL, DEFAULT_EXTENSION), U_OK); ck_assert_int_eq(ulfius_set_websocket_request(&request, WEBSOCKET_URL, DEFAULT_PROTOCOL, NULL), U_OK); ck_assert_int_eq(ulfius_set_websocket_request(&request, WEBSOCKET_URL, NULL, NULL), U_OK); ulfius_clean_request(&request); } END_TEST START_TEST(test_websocket_ulfius_open_websocket_client_connection_error) { struct _u_request request; struct _u_response response; struct _websocket_client_handler websocket_client_handler; ulfius_init_request(&request); ulfius_init_response(&response); ck_assert_int_eq(ulfius_set_websocket_request(&request, WEBSOCKET_URL, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_OK); ck_assert_int_eq(ulfius_open_websocket_client_connection(NULL, &websocket_manager_callback_empty, NULL, NULL, NULL, NULL, NULL, &websocket_client_handler, &response), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, NULL, NULL, NULL, NULL, NULL, NULL, &websocket_client_handler, &response), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, NULL, NULL, NULL, NULL, &websocket_onclose_callback_empty, NULL, &websocket_client_handler, &response), U_ERROR_PARAMS); ulfius_clean_request(&request); ulfius_clean_response(&response); } END_TEST START_TEST(test_websocket_ulfius_websocket_client) { struct _u_instance instance; struct _u_request request; struct _u_response response; struct _websocket_client_handler websocket_client_handler; char url[64]; ck_assert_int_eq(ulfius_init_instance(&instance, PORT, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&instance), U_OK); ulfius_init_request(&request); ulfius_init_response(&response); sprintf(url, "ws://localhost:%d/%s", PORT, PREFIX_WEBSOCKET); ck_assert_int_eq(ulfius_set_websocket_request(&request, url, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_OK); ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, &websocket_manager_callback_client, NULL, &websocket_incoming_message_callback_client, NULL, NULL, NULL, &websocket_client_handler, &response), U_OK); ck_assert_int_eq(ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0), U_WEBSOCKET_STATUS_CLOSE); ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK); ulfius_clean_instance(&instance); ulfius_clean_request(&request); ulfius_clean_response(&response); } END_TEST #endif static Suite *ulfius_suite(void) { Suite *s; TCase *tc_websocket; s = suite_create("Ulfius websocket functions tests"); tc_websocket = tcase_create("test_ulfius_websocket"); #ifndef U_DISABLE_WEBSOCKET tcase_add_test(tc_websocket, test_websocket_ulfius_set_websocket_response); tcase_add_test(tc_websocket, test_websocket_ulfius_set_websocket_request); tcase_add_test(tc_websocket, test_websocket_ulfius_open_websocket_client_connection_error); tcase_add_test(tc_websocket, test_websocket_ulfius_websocket_client); #endif tcase_set_timeout(tc_websocket, 30); suite_add_tcase(s, tc_websocket); return s; } int main(int argc, char *argv[]) { int number_failed; Suite *s; SRunner *sr; //y_init_logs("Ulfius", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting Ulfius websocket tests"); s = ulfius_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); //y_close_logs(); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } ulfius-2.5.2/tools/000077500000000000000000000000001341017022300141605ustar00rootroot00000000000000ulfius-2.5.2/tools/uwsc/000077500000000000000000000000001341017022300151415ustar00rootroot00000000000000ulfius-2.5.2/tools/uwsc/Makefile000066400000000000000000000026141341017022300166040ustar00rootroot00000000000000# # uwsc - Ulfius Websocket client # # Makefile used to build the software # # Copyright 2018 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT License # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # CC=gcc ULFIUS_INCLUDE=../../include ULFIUS_LOCATION=../../src DESTDIR=/usr/local ifndef YDERFLAG LYDER=-lyder endif CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) $(ADDITIONALFLAGS) $(CPPFLAGS) LIBS=-lc -lulfius -lorcania $(LYDER) -L$(ULFIUS_LOCATION) REMOTE_URL=http://localhost:9275/websocket/echo COMMAND=./uwsc --output-log-file=out.log $(REMOTE_URL) all: uwsc clean: rm -f *.o uwsc valgrind.txt out.log debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: uwsc ../../src/libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL uwsc: ../../src/libulfius.so uwsc.c $(CC) $(CFLAGS) uwsc.c $(CC) -o uwsc uwsc.o $(LIBS) test: uwsc LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} $(COMMAND) memcheck: uwsc LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all $(COMMAND) 2>valgrind.txt install: uwsc cp uwsc $(DESTDIR)/bin uninstall: rm -f $(DESTDIR)/bin/uwsc ulfius-2.5.2/tools/uwsc/README.md000066400000000000000000000045551341017022300164310ustar00rootroot00000000000000# uwsc - Ulfius WebSocket Client Simple command-line application to connect to websocket services Copyright 2018 Nicolas Mora This program is free software; you can redistribute it and/or modify it under the terms of the MIT License. ## Overview Can connect to websocket services, both ws:// (http) or wss:// (https). When the websocket is connected, messages from the server are displayed in the terminal and messages can be sent to the service via a prompt `> `. To quit uwsc during connection, enter the message `!q`. ## Options uwsc has some options available: ```shell -o --output-log-file=PATH Print yder log messages in a file -x --add-header=HEADER Add the specified header of the form 'key:value' -b --send-binary-file=PATH Sends the content of a file at the beginning of the connection in a binary frame -t --send-text-file=PATH Sends the content of a file at the beginning of the connection in a text frame -i --non-interactive-send Do not prompt for message sending -l --non-listening Do not listen for incoming messages -f --fragmentation= Specify the max length of a frame and fragment the message to send if required, 0 means no fragmentation, default 0 -p --protocol=PROTOCOL Specify the Websocket Protocol values, default none -e --extensions=EXTENSION Specify the Websocket extensions values, default none -s --non-secure Do not check server certificate -v --version Print Glewlwyd's current version -h --help Print this message ``` ### Examples Here are some examples on how to use uwsc options ```shell $ # run uwsc to a websocket service $ uwsc http://localhost:9275/websocket $ # run uwsc to a secured websocket service $ uwsc https://localhost:9275/websocket $ # run uwsc with a specified protocol and extension list $ uwsc --protocol=myProtocol --extensions=ext1;ext2 $ # run uwsc to a websocket service with additional header values $ uwsc --add-header="Authorization: Bearer abcd.efgh.xyz" --add-header="pragma: no-cache" http://localhost:9275/websocket $ # run uwsc to send a text file content to the service as first message, then the prompt will be available $ uwsc --send-text-file=/path/to/file http://localhost:9275/websocket $ # run uwsc without prompt $ uwsc -i http://localhost:9275/websocket $ # all messages will be fragmented with the maximum payload size specified $ uwsc --fragmentation=42 http://localhost:9275/websocket ``` ulfius-2.5.2/tools/uwsc/uwsc.c000066400000000000000000000356741341017022300163050ustar00rootroot00000000000000/** * * uwsc - Ulfius Websocket client * * Command-line application to connect to a websocket service * * Copyright 2018 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; * version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see . * */ #include #include #include #include /** Define mock yder functions when yder is disabled **/ #ifdef U_DISABLE_YDER int y_init_logs(const char * app, const unsigned long init_mode, const unsigned long init_level, const char * init_log_file, const char * message) { (void)(app); (void)(init_mode); (void)(init_level); (void)(init_log_file); (void)(message); return 1; } int y_set_logs_callback(void (* y_callback_log_message) (void * cls, const char * app_name, const time_t date, const unsigned long level, const char * message), void * cls, const char * message) { (void)(y_callback_log_message); (void)(cls); (void)(message); return 1; } void y_log_message(const unsigned long type, const char * message, ...) { (void)(type); (void)(message); } int y_close_logs() { return 1; } #endif #define _UWSC_VERSION_ "0.9" #ifndef U_DISABLE_WEBSOCKET struct _config { char * log_path; char * binary_file_send; char * text_file_send; int non_interactive; int non_listening; unsigned int fragmentation; char * protocol; char * extensions; struct _u_request * request; struct _u_response * response; }; static char * read_file(const char * filename, size_t * filesize) { char * buffer = NULL; long length; FILE * f; if (filename != NULL) { f = fopen (filename, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); if (filesize != NULL) { *filesize = length; } fseek (f, 0, SEEK_SET); buffer = o_malloc (length + 1); if (buffer != NULL) { if (fread (buffer, 1, length, f)) { buffer[length] = '\0'; } } fclose (f); } return buffer; } else { return NULL; } } static void uwsc_manager_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { char message[257] = {0}; struct _websocket_message * last_message; struct _config * config = (struct _config *)websocket_manager_user_data; char * file_content; size_t file_len; if (config->text_file_send != NULL) { file_content = read_file(config->text_file_send, &file_len); if (file_content != NULL && file_len > 0) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, file_len, file_content) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending text file"); } else { last_message = ulfius_websocket_pop_first_message(websocket_manager->message_list_outcoming); if (last_message != NULL) { ulfius_clear_websocket_message(last_message); } } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error reading file %s", config->text_file_send); } o_free(file_content); } else if (config->binary_file_send != NULL) { file_content = read_file(config->binary_file_send, &file_len); if (file_content != NULL && file_len > 0) { if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_BINARY, file_len, file_content) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending binary file"); } else { last_message = ulfius_websocket_pop_first_message(websocket_manager->message_list_outcoming); if (last_message != NULL) { ulfius_clear_websocket_message(last_message); } } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error reading file %s", config->binary_file_send); } o_free(file_content); } do { if (!config->non_interactive) { if (fgets(message, 256, stdin) != NULL) { if (o_strlen(message)) { if (0 == o_strncmp(message, "!q", o_strlen("!q"))) { fprintf(stdout, "\b\bQuit uwsc\n"); if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending close message"); } } else { fprintf(stdout, "\b\bSend '%.*s'\n> ", (int)(o_strlen(message)-1), message); fflush(stdout); if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(message)-1, message) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending message '%.*s'", (int)(o_strlen(message)-1), message); } else { last_message = ulfius_websocket_pop_first_message(websocket_manager->message_list_outcoming); if (last_message != NULL) { ulfius_clear_websocket_message(last_message); } } } } } } } while (o_strncmp(message, "!q", o_strlen("!q")) != 0 && ulfius_websocket_status(websocket_manager) == U_WEBSOCKET_STATUS_OPEN); } static void uwsc_manager_incoming (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * message, void * websocket_incoming_user_data) { struct _websocket_message * last_message; struct _config * config = (struct _config *)websocket_incoming_user_data; if (message->opcode == U_WEBSOCKET_OPCODE_CLOSE) { fprintf(stdout, "\b\bConnection closed by the server, press to exit\n> "); fflush(stdout); } if (!config->non_listening) { if (message->opcode == U_WEBSOCKET_OPCODE_TEXT) { fprintf(stdout, "\b\bServer message: '%.*s'\n> ", (int)message->data_len, message->data); fflush(stdout); } else if (message->opcode == U_WEBSOCKET_OPCODE_BINARY) { fprintf(stdout, "\b\bServer sent binary message, length %zu\n> ", message->data_len); fflush(stdout); } } last_message = ulfius_websocket_pop_first_message(websocket_manager->message_list_incoming); if (last_message != NULL) { ulfius_clear_websocket_message(last_message); } } static void print_help(FILE * output) { fprintf(output, "\nuwsc - Ulfius Websocket Client\n"); fprintf(output, "\n"); fprintf(output, "Version %s\n", _UWSC_VERSION_); fprintf(output, "\n"); fprintf(output, "Copyright 2018 Nicolas Mora \n"); fprintf(output, "\n"); fprintf(output, "This program is free software; you can redistribute it and/or\n"); fprintf(output, "modify it under the terms of the MIT LICENSE\n"); fprintf(output, "\n"); fprintf(output, "Command-line options:\n"); fprintf(output, "\n"); fprintf(output, "-o --output-log-file=PATH\n"); fprintf(output, "\tPrint yder log messages in a file\n"); fprintf(output, "-x --add-header=HEADER\n"); fprintf(output, "\tAdd the specified header of the form 'key:value'\n"); fprintf(output, "-b --send-binary-file=PATH\n"); fprintf(output, "\tSends the content of a file at the beginning of the connection in a binary frame\n"); fprintf(output, "-t --send-text-file=PATH\n"); fprintf(output, "\tSends the content of a file at the beginning of the connection in a text frame\n"); fprintf(output, "-i --non-interactive-send\n"); fprintf(output, "\tDo not prompt for message sending\n"); fprintf(output, "-l --non-listening\n"); fprintf(output, "\tDo not listen for incoming messages\n"); fprintf(output, "-f --fragmentation=\n"); fprintf(output, "\tSpecify the max length of a frame and fragment the message to send if required, 0 means no fragmentation, default 0\n"); fprintf(output, "-p --protocol=PROTOCOL\n"); fprintf(output, "\tSpecify the Websocket Protocol values, default none\n"); fprintf(output, "-e --extensions=EXTENSION\n"); fprintf(output, "\tSpecify the Websocket extensions values, default none\n"); fprintf(output, "-s --non-secure\n"); fprintf(output, "\tDo not check server certificate\n"); fprintf(output, "-v --version\n"); fprintf(output, "\tPrint uwsc's current version\n\n"); fprintf(output, "-h --help\n"); fprintf(output, "\tPrint this message\n\n"); } static int init_config(struct _config * config) { if (config != NULL) { config->log_path = NULL; config->binary_file_send = NULL; config->text_file_send = NULL; config->non_interactive = 0; config->non_listening = 0; config->fragmentation = 0; config->protocol = NULL; config->extensions = NULL; config->request = NULL; config->response = NULL; if ((config->request = o_malloc(sizeof(struct _u_request))) == NULL) { return 0; } if ((config->response = o_malloc(sizeof(struct _u_response))) == NULL) { return 0; } if (ulfius_init_request(config->request) != U_OK) { return 0; } if (ulfius_init_response(config->response) != U_OK) { return 0; } return 1; } else { return 0; } } static void exit_program(struct _config ** config, int exit_value) { if (config != NULL) { o_free((*config)->log_path); o_free((*config)->binary_file_send); o_free((*config)->text_file_send); o_free((*config)->protocol); o_free((*config)->extensions); ulfius_clean_request_full((*config)->request); ulfius_clean_response_full((*config)->response); o_free(*config); } exit(exit_value); } int main (int argc, char ** argv) { struct _config * config; int next_option; const char * short_options = "o::x::b::t::i::l::f::p::e::s::v::h::"; static const struct option long_options[]= { {"output-log-file", required_argument, NULL, 'o'}, // Sets an output file for logging messages {"add-header", required_argument, NULL, 'x'}, // Add the specified header of the form 'key:value' {"send-binary-file", required_argument, NULL, 'b'}, // Sends the content of a file at the beginning of the connection in a binary frame {"send-text-file", required_argument, NULL, 't'}, // Sends the content of a file at the beginning of the connection in a text frame {"non-interactive-send", no_argument, NULL, 'i'}, // Do not prompt for message sending {"non-listening", no_argument, NULL, 'l'}, // Do not listen for incoming messages {"fragmentation", required_argument, NULL, 'f'}, // Specify the max length of a frame and fragment the message to send if required {"protocol", required_argument, NULL, 'p'}, // Websocket protocol {"extensions", required_argument, NULL, 'e'}, // Websocket extensions {"non-secure", no_argument, NULL, 's'}, // Do not check server certificate {"version", no_argument, NULL, 'v'}, // Show version {"help", no_argument, NULL, 'h'}, // print help {NULL, 0, NULL, 0} }; char * url = NULL; struct _websocket_client_handler websocket_client_handler; config = o_malloc(sizeof(struct _config)); if (config == NULL || !init_config(config)) { fprintf(stderr, "Error initialize configuration\n"); exit_program(NULL, 1); } // TODO: Complete option manager do { char * key, * value; next_option = getopt_long(argc, argv, short_options, long_options, NULL); switch (next_option) { case 'o': config->log_path = o_strdup(optarg); break; case 'x': if (NULL != (value = o_strchr(optarg, ':'))) { key = o_strndup(optarg, (value-optarg)); u_map_put(config->request->map_header, key, value+1); o_free(key); } else { fprintf(stderr, "Error, a header value must have the following format: :\n\nuse uwsc -h for help\n"); exit_program(&config, 1); // Print help and exit } break; case 'b': if (config->text_file_send != NULL) { fprintf(stderr, "Error, you can't send a binary file and a text file\n\nuse uwsc -h for help\n"); exit_program(&config, 1); } config->binary_file_send = o_strdup(optarg); break; case 't': if (config->binary_file_send != NULL) { fprintf(stderr, "Error, you can't send a binary file and a text file\n\nuse uwsc -h for help\n"); exit_program(&config, 1); } config->text_file_send = o_strdup(optarg); break; case 'i': config->non_interactive = 1; break; case 'l': config->non_listening = 1; break; case 'f': config->fragmentation = strtol(optarg, NULL, 10); break; case 'p': config->protocol = o_strdup(optarg); break; case 'e': config->extensions = o_strdup(optarg); break; case 's': config->request->check_server_certificate = 0; break; case 'v': // Print version and exit fprintf(stdout, "%s\n", _UWSC_VERSION_); exit_program(&config, 0); break; case 'h': // Print help and exit print_help(stdout); exit_program(&config, 0); break; default: break; } } while (next_option != -1); if (config->log_path != NULL) { y_init_logs("Ulfius Websocket Client", Y_LOG_MODE_FILE, Y_LOG_LEVEL_DEBUG, config->log_path, "Start uwsc"); } if (optind < argc) { url = argv[optind]; } else { fprintf(stderr, "No url specified\n\n"); print_help(stderr); exit_program(&config, 1); } if (ulfius_set_websocket_request(config->request, url, config->protocol, config->extensions) == U_OK) { if (ulfius_open_websocket_client_connection(config->request, &uwsc_manager_callback, config, &uwsc_manager_incoming, config, NULL, NULL, &websocket_client_handler, config->response) == U_OK) { fprintf(stdout, "Websocket connected, you can send text messages of maximum 256 characters.\nTo exit uwsc, type !q\n> "); fflush(stdout); ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0); fprintf(stdout, "Websocket closed\n"); ulfius_websocket_client_connection_close(&websocket_client_handler); } else { fprintf(stderr, "Error connecting to websocket\n"); y_log_message(Y_LOG_LEVEL_ERROR, "Error connecting to websocket"); } } else { fprintf(stderr, "Error initializing websocket request\n"); y_log_message(Y_LOG_LEVEL_ERROR, "Error initializing websocket request"); } if (config->log_path != NULL) { y_close_logs(); } exit_program(&config, 0); return 0; } #else #include int main() { fprintf(stderr, "Websocket not supported in your Ulfius library, you must rebuild ulfius and uwsc with Websocket support\n"); return 1; } #endif // U_DISABLE_WEBSOCKET