Codebase list ulfius / 1682bb3
New upstream version 2.7.0 Nicolas Mora 3 years ago
61 changed file(s) with 5247 addition(s) and 1556 deletion(s). Raw diff Collapse all Expand all
99 - name: install dependencies
1010 run: |
1111 sudo apt-get update
12 sudo apt-get install -y cmake pkg-config check libsubunit-dev cppcheck libsystemd-dev libmicrohttpd-dev libgnutls28-dev libjansson-dev libcurl4-gnutls-dev gnutls-bin
12 sudo apt-get install -y cmake pkg-config check libsubunit-dev cppcheck libsystemd-dev libmicrohttpd-dev libgnutls28-dev libjansson-dev libcurl4-gnutls-dev zlib1g-dev gnutls-bin
1313 sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
1414 sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
1515 - name: cppcheck
393393
394394 # run tests with Makefile
395395 sudo ldconfig
396 make clean debug check
396 make clean debug
4747
4848 - run: |
4949 sudo apt-get update
50 sudo apt-get install -y cmake pkg-config libsystemd-dev libmicrohttpd-dev libgnutls28-dev libjansson-dev libcurl4-gnutls-dev gnutls-bin doxygen
50 sudo apt-get install -y cmake pkg-config libsystemd-dev libmicrohttpd-dev libgnutls28-dev libjansson-dev libcurl4-gnutls-dev zlib1g-dev gnutls-bin doxygen
5151
5252 # prepare build folders
5353 mkdir build
55 dist: bionic
66 addons:
77 apt:
8 packages: [ libgnutls28-dev, libjansson-dev, libcurl4-gnutls-dev, libmicrohttpd-dev, check, libsubunit-dev, libsystemd-dev, cppcheck, gnutls-bin ]
8 packages: [ libgnutls28-dev, libjansson-dev, libcurl4-gnutls-dev, libmicrohttpd-dev, check, libsubunit-dev, libsystemd-dev, zlib1g-dev, cppcheck, gnutls-bin ]
99
1010 jobs:
1111 include:
1212 - env: LABEL=gcc
1313 compiler: gcc
14 - env: LABEL=cland
14 - env: LABEL=clang
1515 compiler: clang
1616 - env: LABEL=cppcheck
17 script:
18 - cppcheck --force --enable=warning,missingInclude --error-exitcode=1 . >build.log 2>&1 || (cat build.log && exit 1)
19 # ppc64le support code
20 - env: LABEL=gcc
21 arch: ppc64le
22 compiler: gcc
23 - env: LABEL=clang
24 arch: ppc64le
25 compiler: clang
26 - env: LABEL=cppcheck
27 arch: ppc64le
1728 script:
1829 - cppcheck --force --enable=warning,missingInclude --error-exitcode=1 . >build.log 2>&1 || (cat build.log && exit 1)
1930
716727 - make clean debug GNUTLSFLAG=1 CURLFLAG=1 JANSSONFLAG=1
717728 - make clean debug GNUTLSFLAG=1 CURLFLAG=1 WEBSOCKETFLAG=1
718729 - make clean debug GNUTLSFLAG=1 CURLFLAG=1
719
720 # run tests with Makefile
721 - sudo ldconfig
722 - make clean debug check
723
2929 - [Messages manipulation](#messages-manipulation)
3030 - [Server-side websocket](#server-side-websocket)
3131 - [Open a websocket communication](#open-a-websocket-communication)
32 - [Advanced websocket extension](#advanced-websocket-extension)
33 - [Built-in server extension permessage-deflate](#built-in-server-extension-permessage-deflate)
3234 - [Close a websocket communication](#close-a-websocket-communication)
3335 - [Websocket status](#websocket-status)
3436 - [Client-side websocket](#client-side-websocket)
3537 - [Prepare the request](#prepare-the-request)
38 - [Built-in client extension permessage-deflate](#built-in-client-extension-permessage-deflate)
3639 - [Open the websocket](#open-the-websocket)
3740 - [Websocket status](#websocket-status-1)
3841 - [Outgoing request functions](#outgoing-request-functions)
3942 - [Send HTTP request API](#send-http-request-api)
4043 - [Send SMTP request API](#send-http-request-api)
4144 - [struct _u_map API](#struct-_u_map-api)
45 - [What's new in Ulfius 2.7?](#whats-new-in-ulfius-27)
4246 - [What's new in Ulfius 2.6?](#whats-new-in-ulfius-26)
4347 - [What's new in Ulfius 2.5?](#whats-new-in-ulfius-25)
4448 - [What's new in Ulfius 2.4?](#whats-new-in-ulfius-24)
859863 The callback returned value can have the following values:
860864
861865 - `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.
866 - `U_CALLBACK_IGNORE`: 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, the counter `request->callback_position` will not be incremented. If at the end of the callback list `request->callback_position` is 0, the default callback (if set) will be called.
862867 - `U_CALLBACK_COMPLETE`: The framework must complete the transaction and send the response to the client without calling any further callback function.
863868 - `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.
864869 - `U_CALLBACK_ERROR`: An error occurred during execution, the framework will complete the transaction without calling any further callback function and send an error 500 to the client.
12171222 */
12181223 struct _websocket_message {
12191224 time_t datestamp; // datestamp when the message was transmitted
1225 uint8_t rsv; // RSV (extension) flags, must be binary tested over U_WEBSOCKET_RSV1, U_WEBSOCKET_RSV2, U_WEBSOCKET_RSV3
12201226 uint8_t opcode; // opcode of the message: U_WEBSOCKET_OPCODE_TEXT, U_WEBSOCKET_OPCODE_BINARY, U_WEBSOCKET_OPCODE_PING, U_WEBSOCKET_OPCODE_PONG
12211227 uint8_t has_mask; // Flag to specify if the message has a mask
12221228 uint8_t mask[4]; // mask
13441350
13451351 For each of these callback function, you can specify a `*_user_data` pointer containing any data you need.
13461352
1353 #### Advanced websocket extension <a name="advanced-websocket-extension"></a>
1354
1355 Since Ulfius 2.7.0, you have advanced functions to handle websocket extensions based on the functions `ulfius_add_websocket_extension_message_perform` for the server websockets and `ulfius_add_websocket_client_extension_message_perform` for the clients websockets.
1356
1357 ```C
1358 /**
1359 * Adds a set of callback functions to perform a message transformation via an extension
1360 * @param response struct _u_response to send back the websocket initialization, mandatory
1361 * @param extension the expected extension value
1362 * @param websocket_extension_message_out_perform a callback function called before a message is sent to the server
1363 * @param websocket_extension_message_out_perform_user_data a user-defined pointer passed to websocket_extension_message_out_perform
1364 * @param websocket_extension_message_in_perform a callback function called after a message is received from the server
1365 * @param websocket_extension_message_in_perform_user_data a user-defined pointer passed to websocket_extension_message_in_perform
1366 * @param websocket_extension_server_match a callback function called on handshake response to match an extensions value with the given callback message perform extensions
1367 * if NULL, then extension_client and the extension sent by the server will be compared for an exact match to enable this extension
1368 * @param websocket_extension_server_match_user_data a user-defined pointer passed to websocket_extension_server_match
1369 * @param websocket_extension_free_context a callback function called during the websocket close to free the context created in websocket_extension_server_match
1370 * @param websocket_extension_free_context_user_data a user-defined pointer passed to websocket_extension_free_context
1371 * @return U_OK on success
1372 */
1373 int ulfius_add_websocket_extension_message_perform(struct _u_response * response,
1374 const char * extension_server,
1375 int (* websocket_extension_message_out_perform)(const uint8_t opcode,
1376 uint8_t * rsv,
1377 const uint64_t data_len_in,
1378 const char * data_in,
1379 uint64_t * data_len_out,
1380 char ** data_out,
1381 const uint64_t fragment_len,
1382 void * user_data,
1383 void * context),
1384 void * websocket_extension_message_out_perform_user_data,
1385 int (* websocket_extension_message_in_perform)(const uint8_t opcode,
1386 uint8_t rsv,
1387 const uint64_t data_len_in,
1388 const char * data_in,
1389 uint64_t * data_len_out,
1390 char ** data_out,
1391 const uint64_t fragment_len,
1392 void * user_data,
1393 void * context),
1394 void * websocket_extension_message_in_perform_user_data,
1395 int (* websocket_extension_server_match)(const char * extension_client,
1396 const char ** extension_client_list,
1397 char ** extension_server,
1398 void * user_data,
1399 void ** context),
1400 void * websocket_extension_server_match_user_data,
1401 void (* websocket_extension_free_context)(void * user_data,
1402 void * context),
1403 void * websocket_extension_free_context_user_data);
1404
1405 /**
1406 * Adds a set of callback functions to perform a message transformation via an extension
1407 * @param websocket_client_handler the handler of the websocket
1408 * @param extension the expected extension value
1409 * @param websocket_extension_message_out_perform a callback function called before a message is sent to the client
1410 * @param websocket_extension_message_out_perform_user_data a user-defined pointer passed to websocket_extension_message_out_perform
1411 * @param websocket_extension_message_in_perform a callback function called after a message is received from the client
1412 * @param websocket_extension_message_in_perform_user_data a user-defined pointer passed to websocket_extension_message_in_perform
1413 * @param websocket_extension_client_match a callback function called on handshake response to match an extensions value with the given callback message perform extensions
1414 * if NULL, then extension and the extension sent by the client will be compared for an exact match to enable this extension
1415 * @param websocket_extension_client_match_user_data a user-defined pointer passed to websocket_extension_client_match
1416 * @param websocket_extension_free_context a callback function called during the websocket close to free the context created in websocket_extension_server_match
1417 * @param websocket_extension_free_context_user_data a user-defined pointer passed to websocket_extension_free_context
1418 * @return U_OK on success
1419 */
1420 int ulfius_add_websocket_client_extension_message_perform(struct _websocket_client_handler * websocket_client_handler,
1421 const char * extension,
1422 int (* websocket_extension_message_out_perform)(const uint8_t opcode,
1423 uint8_t * rsv,
1424 const uint64_t data_len_in,
1425 const char * data_in,
1426 uint64_t * data_len_out,
1427 char ** data_out,
1428 const uint64_t fragment_len,
1429 void * user_data,
1430 void * context),
1431 void * websocket_extension_message_out_perform_user_data,
1432 int (* websocket_extension_message_in_perform)(const uint8_t opcode,
1433 uint8_t rsv,
1434 const uint64_t data_len_in,
1435 const char * data_in,
1436 uint64_t * data_len_out,
1437 char ** data_out,
1438 const uint64_t fragment_len,
1439 void * user_data,
1440 void * context),
1441 void * websocket_extension_message_in_perform_user_data,
1442 int (* websocket_extension_client_match)(const char * extension_server,
1443 void * user_data,
1444 void ** context),
1445 void * websocket_extension_client_match_user_data,
1446 void (* websocket_extension_free_context)(void * user_data,
1447 void * context),
1448 void * websocket_extension_free_context_user_data);
1449 ```
1450
1451 These functions add the possibility to run a callback function before a message is sent and/or after a message is received.
1452
1453 The callback functions `websocket_extension_server_match` and `websocket_extension_client_match` can be use if you expect to match an extension with parameters. If `NULL`, then instead an exact match between `const char * extension` and the extension received will be checked to enable or not this extension callback functions.
1454
1455 ```C
1456 /**
1457 * Checks an extension sent by the client if it matches the expected extension
1458 * if the function return U_OK, the extension will be enabled and the functions
1459 * websocket_extension_message_out_perform and websocket_extension_message_in_perform available
1460 * @param extension_client the extension value to check
1461 * @param extension_client_list the list of all extension_client to match
1462 * @param extension_server the extension value to return to the client
1463 * @param user_data user-defined data
1464 * @param context context to allocate
1465 * @return U_OK on success
1466 */
1467 int websocket_extension_server_match(const char * extension_client,
1468 const char ** extension_client_list,
1469 char ** extension_server,
1470 void * user_data,
1471 void ** context);
1472
1473 /**
1474 * Checks an extension sent by the server if it matches the expected extension
1475 * if the function return U_OK, the extension will be enabled and the functions
1476 * websocket_extension_message_out_perform and websocket_extension_message_in_perform available
1477 * @param extension_server the extension value to check
1478 * @param user_data user-defined data
1479 * @param context context to allocate
1480 * @return U_OK on success
1481 */
1482 int websocket_extension_client_match(const char * extension_server, void * user_data, void ** context);
1483 ```
1484
1485 The callback function `websocket_extension_message_out_perform` can modify the message data and data lenght and the RSV flags. The callback function `websocket_extension_message_in_perform` can modify the message data only. Inside these functions, `data_in` and `data_len_in` are the current data, your extension callback function must update `data_out` with a `o_malloc`'ed data and set the new data length using `data_len_out` and return `U_OK` on success.
1486 If your function doesn't return `U_OK`, the message data won't be updated and `data_out` won't be free'd if set.
1487
1488 You can call `ulfius_add_websocket_extension_message_perform` or `ulfius_add_websocket_client_extension_message_perform` multiple times for a websocket definition. In that case the extension callbacks function will be called in the same order for the `websocket_extension_message_out_perform` callbacks, and in reverse order for the `websocket_extension_message_in_perform` callbacks.
1489
1490 #### Built-in server extension permessage-deflate <a name="built-in-server-extension-permessage-deflate"></a>
1491
1492 The Websocket extension [permessage-deflate](https://tools.ietf.org/html/rfc7692) used to compress the message data is available in Ulfius. To use this extension in your websocket server, you must call `ulfius_add_websocket_deflate_extension` after calling `ulfius_set_websocket_response` and before finishing the callback endpoint.
1493
1494 ```C
1495 /**
1496 * Adds the required extension message perform to implement message compression according to
1497 * RFC 7692: Compression Extensions for WebSocket
1498 * https://tools.ietf.org/html/rfc7692
1499 * Due to limited implementation, will force response parameters to server_no_context_takeover; client_no_context_takeover
1500 * @param response struct _u_response to send back the websocket initialization, mandatory
1501 * @return U_OK on success
1502 */
1503 int ulfius_add_websocket_deflate_extension(struct _u_response * response);
1504 ```
1505
1506 See the sample code in [websocket_example/websocket_server.c](example_programs/websocket_example/websocket_server.c)
1507
13471508 #### Close a websocket communication <a name="close-a-websocket-communication"></a>
13481509
13491510 To close a websocket communication from the server, you can do one of the following:
14231584 Any body parameter or body raw value will be ignored, the header `Content-Length` will be set to 0.
14241585
14251586 The header `User-Agent` value will be `Ulfius Websocket Client Framework`, feel free to modify it afterwards if you need.
1587
1588 #### Built-in client extension permessage-deflate <a name="built-in-client-extension-permessage-deflate"></a>
1589
1590 The Websocket extension [permessage-deflate](https://tools.ietf.org/html/rfc7692) used to compress the message data is available in Ulfius. To use this extension in your websocket client, you must call `ulfius_add_websocket_client_deflate_extension` after calling `ulfius_set_websocket_request` and before calling `ulfius_open_websocket_client_connection`. Also, the parameter `struct _websocket_client_handler` must be initialized to `{NULL, NULL}` before calling `ulfius_set_websocket_request`.
1591
1592 ```
1593 /**
1594 * Adds the required extension message perform to implement message compression according to
1595 * RFC 7692: Compression Extensions for WebSocket
1596 * https://tools.ietf.org/html/rfc7692
1597 * @param websocket_client_handler the handler of the websocket
1598 * @return U_OK on success
1599 */
1600 int ulfius_add_websocket_client_deflate_extension(struct _websocket_client_handler * websocket_client_handler);
1601 ```
1602
1603 See the sample code in [websocket_example/websocket_client.c](example_programs/websocket_example/websocket_client.c)
14261604
14271605 #### Opening the websocket connection <a name="opening-the-websocket-connection"></a>
14281606
18041982 int u_map_count(const struct _u_map * source);
18051983 ```
18061984
1985 ## What's new in Ulfius 2.7? <a name="whats-new-in-ulfius-27"></a>
1986
1987 Allow `Content-Enconding` header with `ulfius_send_http_request` to compress the response body
1988 Add http_compression callback example
1989 Add static_compressed_inmemory_website callback example
1990 Add callback return value `U_CALLBACK_IGNORE` to igore incrementation of `request->callback_position`
1991 Add `ulfius_add_websocket_extension_message_perform` and `ulfius_add_websocket_client_extension_message_perform` for advanced websocket extensions management
1992 Add [Compression Extensions for WebSocket](https://tools.ietf.org/html/rfc7692)
1993
1994 ### Breaking change with struct _websocket_client_handler
1995
1996 When declaring a `struct _websocket_client_handler` for websocket client API, you must initialize its members content to `NULL` before using it:
1997
1998 ```C
1999 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
2000 ```
2001
18072002 ## What's new in Ulfius 2.6? <a name="whats-new-in-ulfius-26"></a>
18082003
18092004 Add IPv6 support.
20872282
20882283 ```C
20892284 #define U_CALLBACK_CONTINUE 0 // Will replace U_OK
2090 #define U_CALLBACK_COMPLETE 1
2091 #define U_CALLBACK_UNAUTHORIZED 2 // Will replace U_ERROR_UNAUTHORIZED
2092 #define U_CALLBACK_ERROR 3 // Will replace U_ERROR
2285 #define U_CALLBACK_IGNORE 1
2286 #define U_CALLBACK_COMPLETE 2
2287 #define U_CALLBACK_UNAUTHORIZED 3 // Will replace U_ERROR_UNAUTHORIZED
2288 #define U_CALLBACK_ERROR 4 // Will replace U_ERROR
20932289 ```
20942290
20952291 If you want more details on the multiple callback functions, check the [documentation](#callback-functions-return-value).
00 # Ulfius Changelog
1
2 ## 2.7.0
3
4 - Allow `Content-Enconding` header with `ulfius_send_http_request` to compress the response body
5 - Add http_compression callback example
6 - Add static_compressed_inmemory_website callback example
7 - Add callback return value `U_CALLBACK_IGNORE` to igore incrementation of `request->callback_position`
8 - Add `ulfius_add_websocket_extension_message_perform` and `ulfius_add_websocket_client_extension_message_perform` for advanced websocket extensions management
9 - Add [Compression Extensions for WebSocket](https://tools.ietf.org/html/rfc7692)
10 - Fix lots of websocket bugs thanks to [Autobahn|Testsuite](https://github.com/crossbario/autobahn-testsuite).
111
212 ## 2.6.9
313
2828 set(PROJECT_HOMEPAGE_URL "https://github.com/babelouest/ulfius/")
2929 set(PROJECT_BUGREPORT_PATH "https://github.com/babelouest/ulfius/issues")
3030 set(LIBRARY_VERSION_MAJOR "2")
31 set(LIBRARY_VERSION_MINOR "6")
32 set(LIBRARY_VERSION_PATCH "9")
31 set(LIBRARY_VERSION_MINOR "7")
32 set(LIBRARY_VERSION_PATCH "0")
3333
3434 set(PROJECT_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}")
3535 set(PROJECT_VERSION_MAJOR ${LIBRARY_VERSION_MAJOR})
153153
154154 if (WITH_WEBSOCKET)
155155 set(U_DISABLE_WEBSOCKET OFF)
156 include(FindZLIB)
157 find_package(ZLIB REQUIRED)
158 if (ZLIB_FOUND)
159 set(LIBS ${LIBS} ${ZLIB_LIBRARIES})
160 include_directories(${ZLIB_INCLUDE_DIRS})
161 endif ()
156162 else ()
157163 set(U_DISABLE_WEBSOCKET ON)
158164 endif ()
0 libelous# Install Ulfius
0 # Install Ulfius
11
22 - [Distribution packages](#distribution-packages)
33 - [Pre-compiled packages](#pre-compiled-packages)
2121
2222 ## Pre-compiled packages
2323
24 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`.
24 You can install Ulfius with a pre-compiled package available in the [release pages](https://github.com/babelouest/ulfius/releases/latest/). `jansson`, `libmicrohttpd`, `gnutls`, `libcurl-gnutls` and `zlib` development files packages are required to install Ulfius. The packages files `ulfius-dev-full_*` contain the libraries `orcania`, `yder` and `ulfius`.
2525
26 For example, to install Ulfius with the `ulfius-dev-full_2.6.8_Debian_stretch_x86_64.tar.gz` package downloaded on the `releases` page, you must execute the following commands:
26 For example, to install Ulfius with the `ulfius-dev-full_2.7.0_Debian_stretch_x86_64.tar.gz` package downloaded on the `releases` page, you must execute the following commands:
2727
2828 ```shell
2929 $ sudo apt install -y libmicrohttpd-dev libjansson-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev libsystemd-dev
30 $ wget https://github.com/babelouest/ulfius/releases/download/v2.6.8/ulfius-dev-full_2.6.8_debian_buster_x86_64.tar.gz
31 $ tar xf ulfius-dev-full_2.6.8_debian_buster_x86_64.tar.gz
30 $ wget https://github.com/babelouest/ulfius/releases/download/v2.7.0/ulfius-dev-full_2.7.0_debian_buster_x86_64.tar.gz
31 $ tar xf ulfius-dev-full_2.7.0_debian_buster_x86_64.tar.gz
3232 $ sudo dpkg -i liborcania-dev_2.1.1_Debian_stretch_x86_64.deb
33 $ sudo dpkg -i libyder-dev_1.4.11_Debian_stretch_x86_64.deb
34 $ sudo dpkg -i libulfius-dev_2.6.8_Debian_stretch_x86_64.deb
33 $ sudo dpkg -i libyder-dev_1.4.12_Debian_stretch_x86_64.deb
34 $ sudo dpkg -i libulfius-dev_2.7.0_Debian_stretch_x86_64.deb
3535 ```
3636
3737 If there's no package available for your distribution, you can recompile it manually using `CMake` or `Makefile`.
4747 - libgnutls, libgcrypt (optional), required for Websockets and https support
4848 - libcurl (optional), required to send http/smtp requests
4949 - libsystemd (optional), required for [Yder](https://github.com/babelouest/yder) to log messages in journald
50 - zlib (optional), required for Websockets support
5051
5152 Note: the build stacks require a compiler (`gcc` or `clang`), `make`, `cmake` (if using CMake build), and `pkg-config`.
5253
5354 For example, to install all the external dependencies on Debian Stretch, run as root:
5455
5556 ```shell
56 # apt-get install libmicrohttpd-dev libjansson-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev
57 # apt install -y libmicrohttpd-dev libjansson-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev zlib1g-dev
5758 ```
5859
5960 ### Good ol' Makefile
3636 cd $(EXAMPLES_LOCATION) && $(MAKE) clean
3737 cd $(UWSC_LOCATION) && $(MAKE) clean
3838 cd $(TESTS_LOCATION) && $(MAKE) clean
39 rm -rf doc/html
39 cd $(TESTS_LOCATION)/autobahn && $(MAKE) clean
40 rm -rf doc/html $(TESTS_LOCATION)/cert/server.* $(TESTS_LOCATION)/cert/root* $(TESTS_LOCATION)/cert/client*
4041
4142 examples:
4243 cd $(EXAMPLES_LOCATION) && $(MAKE) $*
0 # Response body compression callback function for Ulfius Framework
1
2 Compress the response body using `deflate` or `gzip` depending on the request header `Accept-Encoding` and the callback configuration. The rest of the response, status, headers, cookies won't change. After compressing response body, the response header Content-Encoding will be set accordingly.
3
4 By default, this callback will use `gzip` algorithm if possible, `deflate` otherwise.
5
6 To use this callback function, declare it in your endpoint list with the lowest priority to make sure it's called at the end of the callback functions list. This callback function won't be called if a websocket or a streaming response is declared in a previous callback.
7
8 Simple usage example:
9
10 ```C
11 // Will call callback_http_compression for every endpoint
12 ulfius_add_endpoint_by_val(&instance, "*", NULL, "*", 1000, &callback_http_compression, NULL);
13 ```
14
15 Use `callback_http_compression` for `deflate` compression only:
16
17 ```C
18 struct _http_compression_config config;
19 config.allow_gzip = 0;
20 config.allow_deflate = 1;
21
22 ulfius_add_endpoint_by_val(&instance, "*", NULL, "*", 1000, &callback_http_compression, &config);
23 ```
0 /**
1 *
2 * Response body compression callback function for Ulfius Framework
3 *
4 * Copyright 2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20201101
7 *
8 * Compress the response body using `deflate` or `gzip` depending on the request header `Accept-Encoding` and the callback configuration.
9 * The rest of the response, status, headers, cookies won't change.
10 * After compressing response body, the response header Content-Encoding will be set accordingly.
11 *
12 * The MIT License (MIT)
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this software and associated documentation files (the "Software"), to deal
16 * in the Software without restriction, including without limitation the rights
17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 * copies of the Software, and to permit persons to whom the Software is
19 * furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in all
22 * copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 */
33
34 #include <zlib.h>
35 #include <string.h>
36 #include <ulfius.h>
37
38 #include "http_compression_callback.h"
39
40 #define U_COMPRESS_NONE 0
41 #define U_COMPRESS_GZIP 1
42 #define U_COMPRESS_DEFL 2
43
44 #define U_ACCEPT_HEADER "Accept-Encoding"
45 #define U_CONTENT_HEADER "Content-Encoding"
46
47 #define U_ACCEPT_GZIP "gzip"
48 #define U_ACCEPT_DEFLATE "deflate"
49
50 #define U_GZIP_WINDOW_BITS 15
51 #define U_GZIP_ENCODING 16
52
53 #define CHUNK 0x4000
54
55 static void * u_zalloc(void * q, unsigned n, unsigned m) {
56 (void)q;
57 return o_malloc((size_t) n * m);
58 }
59
60 static void u_zfree(void *q, void *p) {
61 (void)q;
62 o_free(p);
63 }
64
65 int callback_http_compression (const struct _u_request * request, struct _u_response * response, void * user_data) {
66 struct _http_compression_config * config = (struct _http_compression_config *)user_data;
67 char ** accept_list = NULL;
68 int ret = U_CALLBACK_IGNORE, compress_mode = U_COMPRESS_NONE, res;
69 z_stream defstream;
70 char * data_zip = NULL;
71 size_t data_zip_len;
72
73 if (response->binary_body_length && u_map_has_key_case(request->map_header, U_ACCEPT_HEADER)) {
74 if (split_string(u_map_get_case(request->map_header, U_ACCEPT_HEADER), ",", &accept_list)) {
75 if ((config == NULL || config->allow_gzip) && string_array_has_trimmed_value((const char **)accept_list, U_ACCEPT_GZIP)) {
76 compress_mode = U_COMPRESS_GZIP;
77 } else if ((config == NULL || config->allow_deflate) && string_array_has_trimmed_value((const char **)accept_list, U_ACCEPT_DEFLATE)) {
78 compress_mode = U_COMPRESS_DEFL;
79 }
80
81 if (compress_mode != U_COMPRESS_NONE) {
82 data_zip_len = (2*response->binary_body_length)+20;
83 if ((data_zip = o_malloc(data_zip_len)) != NULL) {
84 defstream.zalloc = u_zalloc;
85 defstream.zfree = u_zfree;
86 defstream.opaque = Z_NULL;
87 defstream.avail_in = (uInt)response->binary_body_length;
88 defstream.next_in = (Bytef *)response->binary_body;
89 defstream.next_out = (Bytef *)data_zip;
90 defstream.avail_out = (uInt)data_zip_len;
91
92 if (compress_mode == U_COMPRESS_GZIP) {
93 if (deflateInit2(&defstream,
94 Z_BEST_COMPRESSION,
95 Z_DEFLATED,
96 U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
97 8,
98 Z_DEFAULT_STRATEGY) != Z_OK) {
99 y_log_message(Y_LOG_LEVEL_ERROR, "callback_http_compression - Error deflateInit (gzip)");
100 ret = U_CALLBACK_ERROR;
101 }
102 } else {
103 if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
104 y_log_message(Y_LOG_LEVEL_ERROR, "callback_http_compression - Error deflateInit (deflate)");
105 ret = U_CALLBACK_ERROR;
106 }
107 }
108 if (ret == U_CALLBACK_IGNORE) {
109 res = deflate(&defstream, Z_FINISH);
110 if (res == Z_STREAM_END) {
111 ulfius_set_binary_body_response(response, response->status, (const char *)data_zip, defstream.total_out);
112 u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
113 } else {
114 y_log_message(Y_LOG_LEVEL_ERROR, "callback_http_compression - Error deflate API url %s: %d", request->http_url, res);
115 }
116 deflateEnd(&defstream);
117 }
118 } else {
119 y_log_message(Y_LOG_LEVEL_ERROR, "callback_http_compression - Error allocating resources for data_zip");
120 ret = U_CALLBACK_ERROR;
121 }
122 o_free(data_zip);
123 }
124 }
125 free_string_array(accept_list);
126 }
127
128 return ret;
129 }
0 /**
1 *
2 * Response body compression callback function for Ulfius Framework
3 *
4 * Copyright 2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20201101
7 *
8 * Compress the response body using `deflate` or `gzip` depending on the request header `Accept-Encoding` and the callback configuration.
9 * The rest of the response, status, headers, cookies won't change.
10 * After compressing response body, the response header Content-Encoding will be set accordingly.
11 *
12 * The MIT License (MIT)
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this software and associated documentation files (the "Software"), to deal
16 * in the Software without restriction, including without limitation the rights
17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 * copies of the Software, and to permit persons to whom the Software is
19 * furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in all
22 * copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 */
33
34 #ifndef _U_HTTP_COMPRESSION
35 #define _U_HTTP_COMPRESSION
36
37 /**
38 * If both values are set to true, the first compression algorithm used will be gzip
39 */
40 struct _http_compression_config {
41 int allow_gzip;
42 int allow_deflate;
43 };
44
45 /**
46 * Compress response->binary_body using gzip or deflate algorithm
47 * depending on the request header Accept-Encoding and the
48 * struct _http_compression_config configuration value
49 * If user_data is NULL, it will considered as allow_gzip and allow_deflate to true
50 * After compressing response body, will set response header Content-Encoding accordingly
51 */
52 int callback_http_compression (const struct _u_request * request, struct _u_response * response, void * user_data);
53
54 #endif
0 # Token validation for resource service based on [Ulfius](https://github.com/babelouest/ulfius) framework
1
2 These files contain an authentication callback for Ulfius framework to validate a [JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens Draft 10](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-10) with the correct scope.
3
4 [rhonabwy](https://github.com/babelouest/rhonabwy) is required.
5
6 To use this file, you must create a `struct _glewlwyd_resource_config` with your specific parameters:
7
8 ```C
9 struct _glewlwyd_resource_config {
10 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
11 char * oauth_scope; // Scope values required by the resource, multiple values must be separated by a space character
12 jwt_t * jwt; // The jwt used to decode an access token, the jwt must be initialized with the public key or jwks used to verify the signature
13 jwa_alg alg; // The algorithm used to encode a token, see https://babelouest.github.io/rhonabwy/
14 char * realm; // Optional, a realm value that will be sent back to the client
15 unsigned short accept_access_token; // required, accept type access_token
16 unsigned short accept_client_token; // required, accept type client_token
17 };
18 ```
19
20 Then, you use `callback_check_glewlwyd_access_token` as authentication callback for your ulfius endpoints that need to validate a glewlwyd access_token, example:
21
22 ```C
23 struct _glewlwyd_resource_config g_config;
24 jwt_t * jwt;
25 r_jwt_init(&jwt);
26 r_jwt_add_sign_keys_json_str(jwt, NULL, "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","\
27 "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\",\"use\":\"enc\",\"kid\":\"1\"}");
28 g_config.method = G_METHOD_HEADER;
29 g_config.oauth_scope = "scope1";
30 g_config.jwt = jwt;
31 g_config.alg = R_JWA_ALG_ES256;
32 g_config.realm = "example";
33 g_config.accept_access_token = 1;
34 g_config.accept_client_token = 0;
35
36 // Example, add an authentication callback callback_check_glewlwyd_access_token for the endpoint GET "/api/resource/*"
37 ulfius_add_endpoint_by_val(instance, "GET", "/api", "/resource/*", &callback_check_glewlwyd_access_token, (void*)g_config);
38 ```
39
40 If a DPoP token is included in the request, it can be verified using `verify_dpop_proof`:
41
42 ```C
43 /**
44 * Verifies if a DPoP header exists and if it does, verifies that it's a valid DPoP header
45 */
46 json_t * verify_dpop_proof(const struct _u_request * request, const char * htm, const char * htu, time_t max_iat, const char * jkt);
47 ```
0 /**
1 *
2 * Glewlwyd SSO Access Token token check
3 *
4 * Copyright 2016-2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20201013
7 *
8 * The MIT License (MIT)
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 *
28 */
29
30 #include <string.h>
31 #include <time.h>
32 #include <orcania.h>
33 #include <ulfius.h>
34 #include <jansson.h>
35
36 #include "oidc_resource.h"
37
38 /**
39 * Check if the result json object has a "result" element that is equal to value
40 */
41 static int check_result_value(json_t * result, const int value) {
42 return (result != NULL &&
43 json_is_object(result) &&
44 json_object_get(result, "result") != NULL &&
45 json_is_integer(json_object_get(result, "result")) &&
46 json_integer_value(json_object_get(result, "result")) == value);
47 }
48
49 /**
50 * Validates if an access_token grants has a valid scope
51 * return the final scope list on success
52 */
53 static json_t * access_token_check_scope(struct _oidc_resource_config * config, json_t * j_access_token) {
54 int i, scope_count_token, scope_count_expected;
55 char ** scope_list_token, ** scope_list_expected;
56 json_t * j_res = NULL, * j_scope_final_list = json_array();
57
58 if (j_scope_final_list != NULL) {
59 if (j_access_token != NULL) {
60 scope_count_token = split_string(json_string_value(json_object_get(j_access_token, "scope")), " ", &scope_list_token);
61 if (o_strlen(config->oauth_scope)) {
62 scope_count_expected = split_string(config->oauth_scope, " ", &scope_list_expected);
63 if (scope_count_token > 0 && scope_count_expected > 0) {
64 for (i=0; scope_count_expected > 0 && scope_list_expected[i] != NULL; i++) {
65 if (string_array_has_value((const char **)scope_list_token, scope_list_expected[i])) {
66 json_array_append_new(j_scope_final_list, json_string(scope_list_expected[i]));
67 }
68 }
69 if (json_array_size(j_scope_final_list) > 0) {
70 j_res = json_pack("{sisO}", "result", G_TOKEN_OK, "scope", j_scope_final_list);
71 } else {
72 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INSUFFICIENT_SCOPE);
73 }
74 } else {
75 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INTERNAL);
76 }
77 free_string_array(scope_list_expected);
78 } else {
79 j_res = json_pack("{sisO}", "result", G_TOKEN_OK, "scope", json_object_get(j_access_token, "scope"));
80 }
81 free_string_array(scope_list_token);
82 } else {
83 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
84 }
85 } else {
86 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INTERNAL);
87 }
88 json_decref(j_scope_final_list);
89 return j_res;
90 }
91
92 /**
93 * Validates if an access_token grants has valid parameters:
94 * - sub: non empty string
95 * - aud: non empty string
96 * - type: match "access_token" or "client_token"
97 * - exp < now
98 */
99 static int access_token_check_validity(struct _oidc_resource_config * config, json_t * j_access_token) {
100 time_t now;
101 json_int_t expiration;
102 int res;
103
104 if (j_access_token != NULL) {
105 // Token is valid, check type and expiration date
106 time(&now);
107 expiration = json_integer_value(json_object_get(j_access_token, "exp"));
108 if (now < expiration &&
109 json_object_get(j_access_token, "type") != NULL &&
110 json_is_string(json_object_get(j_access_token, "type"))) {
111 if (config->accept_access_token &&
112 0 == o_strcmp("access_token", json_string_value(json_object_get(j_access_token, "type"))) &&
113 json_string_length(json_object_get(j_access_token, "sub")) > 0) {
114 res = G_TOKEN_OK;
115 } else if (config->accept_client_token &&
116 0 == o_strcmp("client_token", json_string_value(json_object_get(j_access_token, "type"))) &&
117 json_string_length(json_object_get(j_access_token, "aud")) > 0) {
118 res = G_TOKEN_OK;
119 } else {
120 res = G_TOKEN_ERROR_INVALID_REQUEST;
121 }
122 } else {
123 res = G_TOKEN_ERROR_INVALID_REQUEST;
124 }
125 } else {
126 res = G_TOKEN_ERROR_INVALID_TOKEN;
127 }
128 return res;
129 }
130
131 /**
132 * validates if the token value is a valid jwt and has a valid signature
133 */
134 static json_t * access_token_check_signature(struct _oidc_resource_config * config, const char * token_value) {
135 json_t * j_return = NULL, * j_grants;
136 jwt_t * jwt = r_jwt_copy(config->jwt);
137 jwk_t * jwk = NULL;
138 const char * kid;
139
140 if (token_value != NULL) {
141 if (r_jwt_parse(jwt, token_value, 0) == RHN_OK) {
142 if ((kid = r_jwt_get_header_str_value(jwt, "kid")) != NULL) {
143 if ((jwk = r_jwks_get_by_kid(jwt->jwks_pubkey_sign, kid)) == NULL) {
144 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
145 }
146 } else {
147 jwk = r_jwk_copy(config->jwk_verify_default);
148 }
149 if (j_return == NULL) {
150 if (r_jwt_verify_signature(jwt, jwk, 0) == RHN_OK && r_jwt_get_sign_alg(jwt) == config->alg) {
151 j_grants = r_jwt_get_full_claims_json_t(jwt);
152 if (j_grants != NULL) {
153 j_return = json_pack("{siso}", "result", G_TOKEN_OK, "grants", j_grants);
154 } else {
155 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
156 }
157 } else {
158 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
159 }
160 }
161 r_jwk_free(jwk);
162 } else {
163 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
164 }
165 } else {
166 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
167 }
168 r_jwt_free(jwt);
169 return j_return;
170 }
171
172 /**
173 * check if bearer token has some of the specified scope
174 */
175 int callback_check_glewlwyd_oidc_access_token (const struct _u_request * request, struct _u_response * response, void * user_data) {
176 struct _oidc_resource_config * config = (struct _oidc_resource_config *)user_data;
177 json_t * j_access_token = NULL, * j_res_scope;
178 int res = U_CALLBACK_UNAUTHORIZED, res_validity;
179 const char * token_value = NULL;
180 char * response_value = NULL;
181
182 if (config != NULL) {
183 switch (config->method) {
184 case G_METHOD_HEADER:
185 if (u_map_get_case(request->map_header, HEADER_AUTHORIZATION) != NULL) {
186 if (o_strstr(u_map_get_case(request->map_header, HEADER_AUTHORIZATION), HEADER_PREFIX_BEARER) == u_map_get_case(request->map_header, HEADER_AUTHORIZATION)) {
187 token_value = u_map_get_case(request->map_header, HEADER_AUTHORIZATION) + o_strlen(HEADER_PREFIX_BEARER);
188 }
189 }
190 break;
191 case G_METHOD_BODY:
192 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) {
193 token_value = u_map_get(request->map_post_body, BODY_URL_PARAMETER);
194 }
195 break;
196 case G_METHOD_URL:
197 token_value = u_map_get(request->map_url, BODY_URL_PARAMETER);
198 break;
199 }
200 if (token_value != NULL) {
201 j_access_token = access_token_check_signature(config, token_value);
202 if (check_result_value(j_access_token, G_TOKEN_OK)) {
203 res_validity = access_token_check_validity(config, json_object_get(j_access_token, "grants"));
204 if (res_validity == G_TOKEN_OK) {
205 j_res_scope = access_token_check_scope(config, json_object_get(j_access_token, "grants"));
206 if (check_result_value(j_res_scope, G_TOKEN_ERROR_INSUFFICIENT_SCOPE)) {
207 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?",":""));
208 u_map_put(response->map_header, HEADER_RESPONSE, response_value);
209 o_free(response_value);
210 } else if (!check_result_value(j_res_scope, G_TOKEN_OK)) {
211 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?",":""));
212 u_map_put(response->map_header, HEADER_RESPONSE, response_value);
213 o_free(response_value);
214 } else {
215 res = U_CALLBACK_CONTINUE;
216 response->shared_data = (void*)json_pack("{sssOsO*}", "sub", json_string_value(json_object_get(json_object_get(j_access_token, "grants"), "sub")), "scope", json_object_get(j_res_scope, "scope"), "jkt", json_object_get(json_object_get(json_object_get(j_access_token, "grants"), "cnf"), "jkt"));
217 if (json_object_get(json_object_get(j_access_token, "grants"), "aud") != NULL) {
218 json_object_set((void*)response->shared_data, "aud", json_object_get(json_object_get(j_access_token, "grants"), "aud"));
219 }
220 if (json_object_get(json_object_get(j_access_token, "grants"), "client_id") != NULL) {
221 json_object_set((void*)response->shared_data, "client_id", json_object_get(json_object_get(j_access_token, "grants"), "client_id"));
222 }
223 if (json_object_get(json_object_get(j_access_token, "grants"), "claims") != NULL) {
224 json_object_set((void*)response->shared_data, "claims", json_object_get(json_object_get(j_access_token, "grants"), "claims"));
225 }
226 if (response->shared_data == NULL) {
227 res = U_CALLBACK_ERROR;
228 }
229 }
230 json_decref(j_res_scope);
231 } else if (res_validity == G_TOKEN_ERROR_INVALID_TOKEN) {
232 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?",":""));
233 u_map_put(response->map_header, HEADER_RESPONSE, response_value);
234 o_free(response_value);
235 } else {
236 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?",":""));
237 u_map_put(response->map_header, HEADER_RESPONSE, response_value);
238 o_free(response_value);
239 }
240 } else {
241 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?",":""));
242 u_map_put(response->map_header, HEADER_RESPONSE, response_value);
243 o_free(response_value);
244 }
245 json_decref(j_access_token);
246 } else {
247 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?",":""));
248 u_map_put(response->map_header, HEADER_RESPONSE, response_value);
249 o_free(response_value);
250 }
251 }
252 return res;
253 }
254
255 /**
256 * Parse the DPoP header and extract its jkt value if the DPoP is valid
257 */
258 json_t * verify_dpop_proof(const struct _u_request * request, const char * htm, const char * htu, time_t max_iat, const char * jkt) {
259 json_t * j_return = NULL, * j_header = NULL, * j_claims = NULL;
260 const char * dpop_header;
261 jwt_t * dpop_jwt = NULL;
262 jwa_alg alg;
263 jwk_t * jwk_header = NULL;
264 char * jkt_from_token = NULL;
265 time_t now;
266
267 if ((dpop_header = u_map_get_case(request->map_header, HEADER_DPOP)) != NULL) {
268 if (r_jwt_init(&dpop_jwt) == RHN_OK) {
269 if (r_jwt_parse(dpop_jwt, dpop_header, R_FLAG_IGNORE_REMOTE) == RHN_OK) {
270 if (r_jwt_verify_signature(dpop_jwt, NULL, R_FLAG_IGNORE_REMOTE) == RHN_OK) {
271 do {
272 if (0 != o_strcmp("dpop+jwt", r_jwt_get_header_str_value(dpop_jwt, "typ"))) {
273 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid typ");
274 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
275 break;
276 }
277 if ((alg = r_jwt_get_sign_alg(dpop_jwt)) != R_JWA_ALG_RS256 && alg != R_JWA_ALG_RS384 && alg != R_JWA_ALG_RS512 &&
278 alg != R_JWA_ALG_ES256 && alg != R_JWA_ALG_ES384 && alg != R_JWA_ALG_ES512 && alg != R_JWA_ALG_EDDSA && alg != R_JWA_ALG_ES256K) {
279 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid sign_alg");
280 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
281 break;
282 }
283 if ((j_header = r_jwt_get_full_header_json_t(dpop_jwt)) == NULL) {
284 y_log_message(Y_LOG_LEVEL_ERROR, "verify_dpop_proof - Error r_jwt_get_full_header_json_t");
285 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
286 break;
287 }
288 if ((j_claims = r_jwt_get_full_claims_json_t(dpop_jwt)) == NULL) {
289 y_log_message(Y_LOG_LEVEL_ERROR, "verify_dpop_proof - Error r_jwt_get_full_claims_json_t");
290 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
291 break;
292 }
293 if (json_object_get(j_header, "x5c") != NULL || json_object_get(j_header, "x5u") != NULL) {
294 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid header, x5c or x5u present");
295 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
296 break;
297 }
298 if (r_jwk_init(&jwk_header) != RHN_OK) {
299 y_log_message(Y_LOG_LEVEL_ERROR, "verify_dpop_proof - Error r_jwk_init");
300 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
301 break;
302 }
303 if (r_jwk_import_from_json_t(jwk_header, json_object_get(j_header, "jwk")) != RHN_OK) {
304 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid jwk property in header");
305 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
306 break;
307 }
308 if (!o_strlen(r_jwt_get_claim_str_value(dpop_jwt, "jti"))) {
309 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid jti");
310 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
311 break;
312 }
313 if (0 != o_strcmp(htm, r_jwt_get_claim_str_value(dpop_jwt, "htm"))) {
314 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid htm");
315 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
316 break;
317 }
318 if (0 != o_strcmp(htu, r_jwt_get_claim_str_value(dpop_jwt, "htu"))) {
319 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid htu");
320 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
321 break;
322 }
323 time(&now);
324 if ((time_t)r_jwt_get_claim_int_value(dpop_jwt, "iat") > now || ((time_t)r_jwt_get_claim_int_value(dpop_jwt, "iat"))+max_iat < now) {
325 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid iat");
326 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
327 break;
328 }
329 if ((jkt_from_token = r_jwk_thumbprint(jwk_header, R_JWK_THUMB_SHA256, R_FLAG_IGNORE_REMOTE)) == NULL) {
330 y_log_message(Y_LOG_LEVEL_ERROR, "verify_dpop_proof - Error r_jwk_thumbprint");
331 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
332 break;
333 }
334 if (0 != o_strcmp(jkt, jkt_from_token)) {
335 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - jkt value doesn't match");
336 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
337 break;
338 }
339 } while (0);
340 if (j_return == NULL) {
341 j_return = json_pack("{sisO*sO*}", "result", G_TOKEN_OK, "header", j_header, "claims", j_claims);
342 }
343 json_decref(j_header);
344 json_decref(j_claims);
345 r_jwk_free(jwk_header);
346 o_free(jkt_from_token);
347 } else {
348 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid signature");
349 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_REQUEST);
350 }
351 } else {
352 y_log_message(Y_LOG_LEVEL_DEBUG, "verify_dpop_proof - Invalid DPoP token");
353 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
354 }
355 } else {
356 y_log_message(Y_LOG_LEVEL_ERROR, "verify_dpop_proof - Error r_jwt_init");
357 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INTERNAL);
358 }
359 r_jwt_free(dpop_jwt);
360 } else {
361 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
362 }
363 return j_return;
364 }
0 /**
1 *
2 * Glewlwyd SSO Access Token token check
3 *
4 * Copyright 2016-2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20201013
7 *
8 * The MIT License (MIT)
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 *
28 */
29 #include <jansson.h>
30 #include <rhonabwy.h>
31
32 #define G_TOKEN_OK 0
33 #define G_TOKEN_ERROR 1
34 #define G_TOKEN_ERROR_INTERNAL 2
35 #define G_TOKEN_ERROR_INVALID_REQUEST 3
36 #define G_TOKEN_ERROR_INVALID_TOKEN 4
37 #define G_TOKEN_ERROR_INSUFFICIENT_SCOPE 5
38
39 #define G_METHOD_HEADER 0
40 #define G_METHOD_BODY 1
41 #define G_METHOD_URL 2
42
43 #define HEADER_PREFIX_BEARER "Bearer "
44 #define HEADER_RESPONSE "WWW-Authenticate"
45 #define HEADER_AUTHORIZATION "Authorization"
46 #define BODY_URL_PARAMETER "access_token"
47 #define HEADER_DPOP "DPoP"
48
49 struct _oidc_resource_config {
50 int method;
51 char * oauth_scope;
52 jwt_t * jwt;
53 jwk_t * jwk_verify_default;
54 jwa_alg alg;
55 char * realm;
56 unsigned short accept_access_token;
57 unsigned short accept_client_token;
58 };
59
60 /**
61 *
62 * check if bearer token has some of the specified scope
63 * Return G_TOKEN_OK on success
64 * or G_TOKEN_ERROR* on any other case
65 *
66 */
67 int callback_check_glewlwyd_oidc_access_token (const struct _u_request * request, struct _u_response * response, void * user_data);
68
69 /**
70 * Verifies if a DPoP header exists and if it does, verifies that it's a valid DPoP header
71 */
72 json_t * verify_dpop_proof(const struct _u_request * request, const char * htm, const char * htu, time_t max_iat, const char * jkt);
00 # Token validation for resource service based on [Ulfius](https://github.com/babelouest/ulfius) framework
11
2 These files contain an authentication callback for Ulfius framework to validate a Glewlwyd access token with the correct scope.
2 These files contain an authentication callback for Ulfius framework to validate a Glewlwyd OAuth2 access token with the correct scope.
33
4 [libjwt](https://github.com/benmcollins/libjwt) is required.
4 [rhonabwy](https://github.com/babelouest/rhonabwy) is required.
55
66 To use this file, you must create a `struct _glewlwyd_resource_config` with your specific parameters:
77
99 struct _glewlwyd_resource_config {
1010 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
1111 char * oauth_scope; // Scope values required by the resource, multiple values must be separated by a space character
12 char * jwt_decode_key; // The key used to decode an access token
13 jwt_alg_t jwt_alg; // The algorithm used to encode a token, see http://benmcollins.github.io/libjwt/
12 jwt_t * jwt; // The jwt used to decode an access token, the jwt must be initialized with the public key or jwks used to verify the signature
13 jwa_alg alg; // The algorithm used to encode a token, see https://babelouest.github.io/rhonabwy/
1414 char * realm; // Optional, a realm value that will be sent back to the client
1515 unsigned short accept_access_token; // required, accept type access_token
1616 unsigned short accept_client_token; // required, accept type client_token
2121
2222 ```C
2323 struct _glewlwyd_resource_config g_config;
24 jwt_t * jwt;
25 r_jwt_init(&jwt);
26 r_jwt_add_sign_keys_json_str(jwt, NULL, "{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","\
27 "\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\",\"use\":\"enc\",\"kid\":\"1\"}");
2428 g_config.method = G_METHOD_HEADER;
2529 g_config.oauth_scope = "scope1";
26 g_config.jwt_decode_key = "secret";
27 g_config.jwt_alg = JWT_ALG_HS512;
30 g_config.jwt = jwt;
31 g_config.alg = R_JWA_ALG_ES256;
2832 g_config.realm = "example";
2933 g_config.accept_access_token = 1;
3034 g_config.accept_client_token = 0;
11 *
22 * Glewlwyd SSO Access Token token check
33 *
4 * Copyright 2016-2019 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20190810
4 * Copyright 2016-2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20200508
77 *
88 * The MIT License (MIT)
99 *
3131 #include <time.h>
3232 #include <orcania.h>
3333 #include <ulfius.h>
34 #include <jansson.h>
3534
3635 #include "glewlwyd_resource.h"
3736
4746 }
4847
4948 /**
49 * Validates if an access_token grants has a valid scope
50 * return the final scope list on success
51 */
52 static json_t * access_token_check_scope(struct _glewlwyd_resource_config * config, json_t * j_access_token) {
53 int i, scope_count_token, scope_count_expected;
54 char ** scope_list_token, ** scope_list_expected;
55 json_t * j_res = NULL, * j_scope_final_list = json_array();
56
57 if (j_scope_final_list != NULL) {
58 if (j_access_token != NULL) {
59 scope_count_token = split_string(json_string_value(json_object_get(j_access_token, "scope")), " ", &scope_list_token);
60 if (o_strlen(config->oauth_scope)) {
61 scope_count_expected = split_string(config->oauth_scope, " ", &scope_list_expected);
62 if (scope_count_token > 0 && scope_count_expected > 0) {
63 for (i=0; scope_count_expected > 0 && scope_list_expected[i] != NULL; i++) {
64 if (string_array_has_value((const char **)scope_list_token, scope_list_expected[i])) {
65 json_array_append_new(j_scope_final_list, json_string(scope_list_expected[i]));
66 }
67 }
68 if (json_array_size(j_scope_final_list) > 0) {
69 j_res = json_pack("{sisO}", "result", G_TOKEN_OK, "scope", j_scope_final_list);
70 } else {
71 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INSUFFICIENT_SCOPE);
72 }
73 } else {
74 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INTERNAL);
75 }
76 free_string_array(scope_list_expected);
77 } else {
78 j_res = json_pack("{sis[]}", "result", G_TOKEN_OK, "scope");
79 }
80 free_string_array(scope_list_token);
81 } else {
82 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
83 }
84 } else {
85 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INTERNAL);
86 }
87 json_decref(j_scope_final_list);
88 return j_res;
89 }
90
91 /**
92 * Validates if an access_token grants has valid parameters:
93 * - username: non empty string
94 * - type: match "access_token"
95 * - iat + expires_in < now
96 */
97 static int access_token_check_validity(struct _glewlwyd_resource_config * config, json_t * j_access_token) {
98 time_t now;
99 json_int_t expiration;
100 int res;
101
102 if (j_access_token != NULL) {
103 // Token is valid, check type and expiration date
104 time(&now);
105 expiration = json_integer_value(json_object_get(j_access_token, "iat")) + json_integer_value(json_object_get(j_access_token, "expires_in"));
106 if (now < expiration &&
107 json_object_get(j_access_token, "type") != NULL &&
108 json_is_string(json_object_get(j_access_token, "type"))) {
109 if (config->accept_access_token &&
110 0 == o_strcmp("access_token", json_string_value(json_object_get(j_access_token, "type"))) &&
111 json_object_get(j_access_token, "username") != NULL &&
112 json_is_string(json_object_get(j_access_token, "username")) &&
113 json_string_length(json_object_get(j_access_token, "username")) > 0) {
114 res = G_TOKEN_OK;
115 } else if (config->accept_client_token &&
116 0 == o_strcmp("client_token", json_string_value(json_object_get(j_access_token, "type"))) &&
117 json_object_get(j_access_token, "client_id") != NULL &&
118 json_is_string(json_object_get(j_access_token, "client_id")) &&
119 json_string_length(json_object_get(j_access_token, "client_id")) > 0) {
120 res = G_TOKEN_OK;
121 } else {
122 res = G_TOKEN_ERROR_INVALID_REQUEST;
123 }
124 } else {
125 res = G_TOKEN_ERROR_INVALID_REQUEST;
126 }
127 } else {
128 res = G_TOKEN_ERROR_INVALID_TOKEN;
129 }
130 return res;
131 }
132
133 /**
134 * validates if the token value is a valid jwt and has a valid signature
135 */
136 static json_t * access_token_check_signature(struct _glewlwyd_resource_config * config, const char * token_value) {
137 json_t * j_return, * j_grants;
138 jwt_t * jwt = r_jwt_copy(config->jwt);
139
140 if (token_value != NULL) {
141 if (r_jwt_parse(jwt, token_value, 0) == RHN_OK && r_jwt_verify_signature(jwt, NULL, 0) == RHN_OK && r_jwt_get_sign_alg(jwt) == config->alg) {
142 j_grants = r_jwt_get_full_claims_json_t(jwt);
143 if (j_grants != NULL) {
144 j_return = json_pack("{siso}", "result", G_TOKEN_OK, "grants", j_grants);
145 } else {
146 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
147 }
148 } else {
149 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
150 }
151 } else {
152 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
153 }
154 r_jwt_free(jwt);
155 return j_return;
156 }
157
158 /**
50159 * check if bearer token has some of the specified scope
51160 */
52161 int callback_check_glewlwyd_access_token (const struct _u_request * request, struct _u_response * response, void * user_data) {
66175 }
67176 break;
68177 case G_METHOD_BODY:
69 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) {
178 if (o_strstr(u_map_get_case(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) {
70179 token_value = u_map_get(request->map_post_body, BODY_URL_PARAMETER);
71180 }
72181 break;
119228 }
120229 return res;
121230 }
122
123 /**
124 * Validates if an access_token grants has a valid scope
125 * return the final scope list on success
126 */
127 json_t * access_token_check_scope(struct _glewlwyd_resource_config * config, json_t * j_access_token) {
128 int i, scope_count_token, scope_count_expected;
129 char ** scope_list_token, ** scope_list_expected;
130 json_t * j_res = NULL, * j_scope_final_list = json_array();
131
132 if (j_scope_final_list != NULL) {
133 if (j_access_token != NULL) {
134 scope_count_token = split_string(json_string_value(json_object_get(j_access_token, "scope")), " ", &scope_list_token);
135 if (o_strlen(config->oauth_scope)) {
136 scope_count_expected = split_string(config->oauth_scope, " ", &scope_list_expected);
137 if (scope_count_token > 0 && scope_count_expected > 0) {
138 for (i=0; scope_count_expected > 0 && scope_list_expected[i] != NULL; i++) {
139 if (string_array_has_value((const char **)scope_list_token, scope_list_expected[i])) {
140 json_array_append_new(j_scope_final_list, json_string(scope_list_expected[i]));
141 }
142 }
143 if (json_array_size(j_scope_final_list) > 0) {
144 j_res = json_pack("{sisO}", "result", G_TOKEN_OK, "scope", j_scope_final_list);
145 } else {
146 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INSUFFICIENT_SCOPE);
147 }
148 } else {
149 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INTERNAL);
150 }
151 free_string_array(scope_list_expected);
152 } else {
153 j_res = json_pack("{sis[]}", "result", G_TOKEN_OK, "scope");
154 }
155 free_string_array(scope_list_token);
156 } else {
157 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
158 }
159 } else {
160 j_res = json_pack("{si}", "result", G_TOKEN_ERROR_INTERNAL);
161 }
162 json_decref(j_scope_final_list);
163 return j_res;
164 }
165
166 /**
167 * Validates if an access_token grants has valid parameters:
168 * - username: non empty string
169 * - type: match "access_token"
170 * - iat + expires_in < now
171 */
172 int access_token_check_validity(struct _glewlwyd_resource_config * config, json_t * j_access_token) {
173 time_t now;
174 json_int_t expiration;
175 int res;
176
177 if (j_access_token != NULL) {
178 // Token is valid, check type and expiration date
179 time(&now);
180 expiration = json_integer_value(json_object_get(j_access_token, "iat")) + json_integer_value(json_object_get(j_access_token, "expires_in"));
181 if (now < expiration &&
182 json_object_get(j_access_token, "type") != NULL &&
183 json_is_string(json_object_get(j_access_token, "type"))) {
184 if (config->accept_access_token &&
185 0 == o_strcmp("access_token", json_string_value(json_object_get(j_access_token, "type"))) &&
186 json_object_get(j_access_token, "username") != NULL &&
187 json_is_string(json_object_get(j_access_token, "username")) &&
188 json_string_length(json_object_get(j_access_token, "username")) > 0) {
189 res = G_TOKEN_OK;
190 } else if (config->accept_client_token &&
191 0 == o_strcmp("client_token", json_string_value(json_object_get(j_access_token, "type"))) &&
192 json_object_get(j_access_token, "client_id") != NULL &&
193 json_is_string(json_object_get(j_access_token, "client_id")) &&
194 json_string_length(json_object_get(j_access_token, "client_id")) > 0) {
195 res = G_TOKEN_OK;
196 } else {
197 res = G_TOKEN_ERROR_INVALID_REQUEST;
198 }
199 } else {
200 res = G_TOKEN_ERROR_INVALID_REQUEST;
201 }
202 } else {
203 res = G_TOKEN_ERROR_INVALID_TOKEN;
204 }
205 return res;
206 }
207
208 /**
209 * validates if the token value is a valid jwt and has a valid signature
210 */
211 json_t * access_token_check_signature(struct _glewlwyd_resource_config * config, const char * token_value) {
212 json_t * j_return, * j_grants;
213 jwt_t * jwt = NULL;
214 char * grants;
215
216 if (token_value != NULL) {
217 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) {
218 grants = jwt_get_grants_json(jwt, NULL);
219 j_grants = json_loads(grants, JSON_DECODE_ANY, NULL);
220 if (j_grants != NULL) {
221 j_return = json_pack("{siso}", "result", G_TOKEN_OK, "grants", j_grants);
222 } else {
223 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
224 }
225 o_free(grants);
226 } else {
227 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
228 }
229 jwt_free(jwt);
230 } else {
231 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
232 }
233 return j_return;
234 }
235
236 /**
237 * Return the payload of an access token
238 */
239 json_t * access_token_get_payload(const char * token_value) {
240 json_t * j_return, * j_grants;
241 jwt_t * jwt = NULL;
242 char * grants;
243
244 if (token_value != NULL) {
245 if (!jwt_decode(&jwt, token_value, NULL, 0)) {
246 grants = jwt_get_grants_json(jwt, NULL);
247 j_grants = json_loads(grants, JSON_DECODE_ANY, NULL);
248 if (j_grants != NULL) {
249 j_return = json_pack("{siso}", "result", G_TOKEN_OK, "grants", j_grants);
250 } else {
251 j_return = json_pack("{si}", "result", G_TOKEN_ERROR);
252 }
253 o_free(grants);
254 } else {
255 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
256 }
257 jwt_free(jwt);
258 } else {
259 j_return = json_pack("{si}", "result", G_TOKEN_ERROR_INVALID_TOKEN);
260 }
261 return j_return;
262 }
11 *
22 * Glewlwyd SSO Access Token token check
33 *
4 * Copyright 2016-2019 Nicolas Mora <mail@babelouest.org>
4 * Copyright 2016-2020 Nicolas Mora <mail@babelouest.org>
55 *
6 * Version 20190810
6 * Version 20200508
77 *
88 * The MIT License (MIT)
99 *
2626 * SOFTWARE.
2727 *
2828 */
29 #include <jwt.h>
29 #include <jansson.h>
30 #include <rhonabwy.h>
3031
3132 #define G_TOKEN_OK 0
3233 #define G_TOKEN_ERROR 1
4748 struct _glewlwyd_resource_config {
4849 int method;
4950 char * oauth_scope;
50 char * jwt_decode_key;
51 jwt_alg_t jwt_alg;
51 jwt_t * jwt;
52 jwa_alg alg;
5253 char * realm;
5354 unsigned short accept_access_token;
5455 unsigned short accept_client_token;
5758 /**
5859 *
5960 * check if bearer token has some of the specified scope
60 * Return G_TOKEN_OK on success
61 * or G_TOKEN_ERROR* on any other case
6261 *
6362 */
6463 int callback_check_glewlwyd_access_token (const struct _u_request * request, struct _u_response * response, void * user_data);
65
66 /**
67 *
68 * Validates if an access_token grants has a valid scope
69 * return the final scope list on success
70 *
71 */
72 json_t * access_token_check_scope(struct _glewlwyd_resource_config * config, json_t * j_access_token);
73
74 /**
75 *
76 * Validates if an access_token grants has valid parameters:
77 * - username: non empty string
78 * - type: match "access_token"
79 * - iat + expires_in < now
80 *
81 * Return G_TOKEN_OK on success
82 * or G_TOKEN_ERROR* on any other case
83 *
84 */
85 int access_token_check_validity(struct _glewlwyd_resource_config * config, json_t * j_access_token);
86
87 /**
88 *
89 * validates if the token value is a valid jwt and has a valid signature
90 *
91 */
92 json_t * access_token_check_signature(struct _glewlwyd_resource_config * config, const char * token_value);
93
94 /**
95 *
96 * Return the payload of an access token
97 *
98 */
99 json_t * access_token_get_payload(const char * token_value);
0 # Static file server with compression callback function for Ulfius Framework
1
2 Provides a static file server where HTTP compress is allowed if specified in the mime-types. A memory cache system can be used to retrieve compressed files more easily. This cache system can be disabled if you don't want to overload the memory with a huge static website.
3
4 `user_data` must be initialized with a `struct _u_compressed_inmemory_website_config` containing the following informations:
5
6 - `files_path`: path to the DocumentRoot folder, can be relative or absolute
7 - `url_prefix`: prefix used to access the callback function
8 - `mime_types`: a `struct _u_map` containing a set of mime-types with file extension as key and mime-type as value
9 - `mime_types_compressed`: A `string_array` structure containing the list of mime-types allowed for compression
10 - `mime_types_compressed_size`: The number of elements in `mime_types_compressed`
11 - `map_header`: a `struct _u_map` containing a set of headers that will be added to all responses within the `static_file_callback`
12 - `redirect_on_404`: redirct uri on error 404, if NULL, send 404
13 - `allow_gzip`: Set to true if you want to allow gzip compression (default true)
14 - `allow_deflate`: Set to true if you want to allow deflate compression (default true)
15 - `allow_cache_compressed`: set to true if you want to allow memory cache for compressed files (default true)
16 - `lock`: mutex lock (do not touch this variable)
17 - `gzip_files`: a `struct _u_map` containing cached gzip files
18 - `deflate_files`: a `struct _u_map` containing cached deflate files
19
20 To use the callback function callback_static_compressed_inmemory_website, you must pass an initialized `struct _u_compressed_inmemory_website_config` as user_data with your configuration.
21
22 The functions `u_init_compressed_inmemory_website_config`, `u_clean_compressed_inmemory_website_config` and `u_add_mime_types_compressed` are dedicated to manipulate the `struct _u_compressed_inmemory_website_config`.
23
24 Here is a sample code on how to use the callback function:
25
26 ```C
27 struct _u_compressed_inmemory_website_config config;
28
29 if (u_init_compressed_inmemory_website_config(&config) == U_OK) {
30 // Add mime types
31 u_map_put(&config->mime_types, "*", "application/octet-stream");
32 u_map_put(&config->mime_types, ".html", "text/html");
33 u_map_put(&config->mime_types, ".css", "text/css");
34 u_map_put(&config->mime_types, ".js", "application/javascript");
35 u_map_put(&config->mime_types, ".json", "application/json");
36 u_map_put(&config->mime_types, ".png", "image/png");
37 u_map_put(&config->mime_types, ".gif", "image/gif");
38 u_map_put(&config->mime_types, ".jpeg", "image/jpeg");
39 u_map_put(&config->mime_types, ".jpg", "image/jpeg");
40 u_map_put(&config->mime_types, ".ttf", "font/ttf");
41 u_map_put(&config->mime_types, ".woff", "font/woff");
42 u_map_put(&config->mime_types, ".ico", "image/x-icon");
43
44 // specify compressed mime types
45 u_add_mime_types_compressed(&config, "text/html");
46 u_add_mime_types_compressed(&config, "text/css");
47 u_add_mime_types_compressed(&config, "application/javascript");
48 u_add_mime_types_compressed(&config, "application/json");
49
50 // Add callback function to all endpoints
51 ulfius_add_endpoint_by_val(instance, "GET", NULL, "*", 0, &callback_static_compressed_inmemory_website, &config);
52 } else {
53 // Error
54 }
55 ```
0 /**
1 *
2 * Static file server with compression Ulfius callback
3 *
4 * Copyright 2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20201101
7 *
8 * The MIT License (MIT)
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 *
28 * `files_path`: path to the DocumentRoot folder, can be relative or absolute
29 * `url_prefix`: prefix used to access the callback function
30 * `mime_types`: a `struct _u_map` containing a set of mime-types with file extension as key and mime-type as value
31 * `mime_types_compressed`: A `string_array` structure containing the list of mime-types allowed for compression
32 * `mime_types_compressed_size`: The number of elements in `mime_types_compressed`
33 * `map_header`: a `struct _u_map` containing a set of headers that will be added to all responses within the `static_file_callback`
34 * `redirect_on_404`: redirct uri on error 404, if NULL, send 404
35 * `allow_gzip`: Set to true if you want to allow gzip compression (default true)
36 * `allow_deflate`: Set to true if you want to allow deflate compression (default true)
37 * `allow_cache_compressed`: set to true if you want to allow memory cache for compressed files (default true)
38 * `lock`: mutex lock (do not touch this variable)
39 * `gzip_files`: a `struct _u_map` containing cached gzip files
40 * `deflate_files`: a `struct _u_map` containing cached deflate files
41 *
42 * example of mime-types used in Hutch:
43 * {
44 * key = ".html"
45 * value = "text/html"
46 * },
47 * {
48 * key = ".css"
49 * value = "text/css"
50 * },
51 * {
52 * key = ".js"
53 * value = "application/javascript"
54 * },
55 * {
56 * key = ".png"
57 * value = "image/png"
58 * },
59 * {
60 * key = ".jpg"
61 * value = "image/jpeg"
62 * },
63 * {
64 * key = ".jpeg"
65 * value = "image/jpeg"
66 * },
67 * {
68 * key = ".ttf"
69 * value = "font/ttf"
70 * },
71 * {
72 * key = ".woff"
73 * value = "font/woff"
74 * },
75 * {
76 * key = ".woff2"
77 * value = "font/woff2"
78 * },
79 * {
80 * key = ".map"
81 * value = "application/octet-stream"
82 * },
83 * {
84 * key = "*"
85 * value = "application/octet-stream"
86 * }
87 *
88 */
89 #include <pthread.h>
90 #include <zlib.h>
91 #include <string.h>
92 #include <ulfius.h>
93
94 #include "static_compressed_inmemory_website_callback.h"
95
96 #define U_COMPRESS_NONE 0
97 #define U_COMPRESS_GZIP 1
98 #define U_COMPRESS_DEFL 2
99
100 #define U_ACCEPT_HEADER "Accept-Encoding"
101 #define U_CONTENT_HEADER "Content-Encoding"
102
103 #define U_ACCEPT_GZIP "gzip"
104 #define U_ACCEPT_DEFLATE "deflate"
105
106 #define U_GZIP_WINDOW_BITS 15
107 #define U_GZIP_ENCODING 16
108
109 #define CHUNK 0x4000
110
111 static void * u_zalloc(void * q, unsigned n, unsigned m) {
112 (void)q;
113 return o_malloc((size_t) n * m);
114 }
115
116 static void u_zfree(void *q, void *p) {
117 (void)q;
118 o_free(p);
119 }
120
121 /**
122 * Return the filename extension
123 */
124 static const char * get_filename_ext(const char *path) {
125 const char *dot = strrchr(path, '.');
126 if(!dot || dot == path) return "*";
127 if (strchr(dot, '?') != NULL) {
128 *strchr(dot, '?') = '\0';
129 }
130 return dot;
131 }
132
133 /**
134 * Streaming callback function to ease sending large files
135 */
136 static ssize_t callback_static_file_uncompressed_stream(void * cls, uint64_t pos, char * buf, size_t max) {
137 (void)(pos);
138 if (cls != NULL) {
139 return fread (buf, sizeof(char), max, (FILE *)cls);
140 } else {
141 return U_STREAM_END;
142 }
143 }
144
145 /**
146 * Cleanup FILE* structure when streaming is complete
147 */
148 static void callback_static_file_uncompressed_stream_free(void * cls) {
149 if (cls != NULL) {
150 fclose((FILE *)cls);
151 }
152 }
153
154 /**
155 * static file callback endpoint
156 */
157 static int callback_static_file_uncompressed (const struct _u_request * request, struct _u_response * response, void * user_data) {
158 size_t length;
159 FILE * f;
160 char * file_requested, * file_path, * url_dup_save;
161 const char * content_type;
162 int ret = U_CALLBACK_CONTINUE;
163
164 if (user_data != NULL && ((struct _u_compressed_inmemory_website_config *)user_data)->files_path != NULL) {
165 file_requested = o_strdup(request->http_url);
166 url_dup_save = file_requested;
167
168 while (file_requested[0] == '/') {
169 file_requested++;
170 }
171 file_requested += o_strlen(((struct _u_compressed_inmemory_website_config *)user_data)->url_prefix);
172 while (file_requested[0] == '/') {
173 file_requested++;
174 }
175
176 if (strchr(file_requested, '#') != NULL) {
177 *strchr(file_requested, '#') = '\0';
178 }
179
180 if (strchr(file_requested, '?') != NULL) {
181 *strchr(file_requested, '?') = '\0';
182 }
183
184 if (file_requested == NULL || o_strlen(file_requested) == 0 || 0 == o_strcmp("/", file_requested)) {
185 o_free(url_dup_save);
186 url_dup_save = file_requested = o_strdup("index.html");
187 }
188
189 file_path = msprintf("%s/%s", ((struct _u_compressed_inmemory_website_config *)user_data)->files_path, file_requested);
190
191 if (access(file_path, F_OK) != -1) {
192 f = fopen (file_path, "rb");
193 if (f) {
194 fseek (f, 0, SEEK_END);
195 length = ftell (f);
196 fseek (f, 0, SEEK_SET);
197
198 content_type = u_map_get_case(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, get_filename_ext(file_requested));
199 if (content_type == NULL) {
200 content_type = u_map_get(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, "*");
201 y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
202 }
203 u_map_put(response->map_header, "Content-Type", content_type);
204 u_map_copy_into(response->map_header, &((struct _u_compressed_inmemory_website_config *)user_data)->map_header);
205
206 if (ulfius_set_stream_response(response, 200, callback_static_file_uncompressed_stream, callback_static_file_uncompressed_stream_free, length, CHUNK, f) != U_OK) {
207 y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error ulfius_set_stream_response");
208 }
209 }
210 } else {
211 if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
212 ret = U_CALLBACK_IGNORE;
213 } else {
214 ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
215 response->status = 302;
216 }
217 }
218 o_free(file_path);
219 o_free(url_dup_save);
220 } else {
221 y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error, user_data is NULL or inconsistent");
222 ret = U_CALLBACK_ERROR;
223 }
224 return ret;
225 }
226
227 int u_init_compressed_inmemory_website_config(struct _u_compressed_inmemory_website_config * config) {
228 int ret = U_OK;
229 pthread_mutexattr_t mutexattr;
230
231 if (config != NULL) {
232 config->files_path = NULL;
233 config->url_prefix = NULL;
234 config->redirect_on_404 = NULL;
235 config->allow_gzip = 1;
236 config->allow_deflate = 1;
237 config->mime_types_compressed = NULL;
238 config->mime_types_compressed_size = 0;
239 config->allow_cache_compressed = 1;
240 if ((ret = u_map_init(&(config->mime_types))) != U_OK) {
241 y_log_message(Y_LOG_LEVEL_ERROR, "u_init_compressed_inmemory_website_config - Error u_map_init mime_types");
242 } else if ((ret = u_map_init(&(config->map_header))) != U_OK) {
243 y_log_message(Y_LOG_LEVEL_ERROR, "u_init_compressed_inmemory_website_config - Error u_map_init map_header");
244 } else if ((ret = u_map_init(&(config->gzip_files))) != U_OK) {
245 y_log_message(Y_LOG_LEVEL_ERROR, "u_init_compressed_inmemory_website_config - Error u_map_init gzip_files");
246 } else if ((ret = u_map_init(&(config->deflate_files))) != U_OK) {
247 y_log_message(Y_LOG_LEVEL_ERROR, "u_init_compressed_inmemory_website_config - Error u_map_init deflate_files");
248 } else {
249 pthread_mutexattr_init (&mutexattr);
250 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
251 if (pthread_mutex_init(&(config->lock), &mutexattr) != 0) {
252 y_log_message(Y_LOG_LEVEL_ERROR, "u_init_compressed_inmemory_website_config - Error pthread_mutex_init");
253 ret = U_ERROR;
254 }
255 }
256 }
257 return ret;
258 }
259
260 void u_clean_compressed_inmemory_website_config(struct _u_compressed_inmemory_website_config * config) {
261 if (config != NULL) {
262 u_map_clean(&(config->mime_types));
263 u_map_clean(&(config->map_header));
264 u_map_clean(&(config->gzip_files));
265 u_map_clean(&(config->deflate_files));
266 free_string_array(config->mime_types_compressed);
267 pthread_mutex_destroy(&(config->lock));
268 }
269 }
270
271 int u_add_mime_types_compressed(struct _u_compressed_inmemory_website_config * config, const char * mime_type) {
272 int ret;
273 if (config != NULL && o_strlen(mime_type)) {
274 if ((config->mime_types_compressed = o_realloc(config->mime_types_compressed, (config->mime_types_compressed_size+2)*sizeof(char*))) != NULL) {
275 config->mime_types_compressed[config->mime_types_compressed_size] = o_strdup(mime_type);
276 config->mime_types_compressed[config->mime_types_compressed_size+1] = NULL;
277 config->mime_types_compressed_size++;
278 ret = U_OK;
279 } else {
280 y_log_message(Y_LOG_LEVEL_ERROR, "u_add_mime_types_compressed - Error allocating resources for mime_types_compressed");
281 ret = U_ERROR;
282 }
283 } else {
284 ret = U_ERROR_PARAMS;
285 }
286 return ret;
287 }
288
289 int callback_static_compressed_inmemory_website (const struct _u_request * request, struct _u_response * response, void * user_data) {
290 struct _u_compressed_inmemory_website_config * config = (struct _u_compressed_inmemory_website_config *)user_data;
291 char ** accept_list = NULL;
292 int ret = U_CALLBACK_CONTINUE, compress_mode = U_COMPRESS_NONE, res;
293 z_stream defstream;
294 unsigned char * file_content, * file_content_orig = NULL;
295 size_t length, read_length, offset;
296 FILE * f;
297 char * file_requested, * file_path, * url_dup_save, * data_zip = NULL;
298 const char * content_type;
299
300 /*
301 * Comment this if statement if you don't access static files url from root dir, like /app
302 */
303 if (request->callback_position > 0) {
304 return U_CALLBACK_IGNORE;
305 } else {
306 file_requested = o_strdup(request->http_url);
307 url_dup_save = file_requested;
308
309 while (file_requested[0] == '/') {
310 file_requested++;
311 }
312 file_requested += o_strlen((config->url_prefix));
313 while (file_requested[0] == '/') {
314 file_requested++;
315 }
316
317 if (strchr(file_requested, '#') != NULL) {
318 *strchr(file_requested, '#') = '\0';
319 }
320
321 if (strchr(file_requested, '?') != NULL) {
322 *strchr(file_requested, '?') = '\0';
323 }
324
325 if (file_requested == NULL || o_strlen(file_requested) == 0 || 0 == o_strcmp("/", file_requested)) {
326 o_free(url_dup_save);
327 url_dup_save = file_requested = o_strdup("index.html");
328 }
329
330 if (!u_map_has_key_case(response->map_header, U_CONTENT_HEADER)) {
331 if (split_string(u_map_get_case(request->map_header, U_ACCEPT_HEADER), ",", &accept_list)) {
332 if (config->allow_gzip && string_array_has_trimmed_value((const char **)accept_list, U_ACCEPT_GZIP)) {
333 compress_mode = U_COMPRESS_GZIP;
334 } else if (config->allow_deflate && string_array_has_trimmed_value((const char **)accept_list, U_ACCEPT_DEFLATE)) {
335 compress_mode = U_COMPRESS_DEFL;
336 }
337
338 content_type = u_map_get_case(&config->mime_types, get_filename_ext(file_requested));
339 if (content_type == NULL) {
340 content_type = u_map_get(&config->mime_types, "*");
341 y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
342 }
343 if (!string_array_has_value((const char **)config->mime_types_compressed, content_type)) {
344 compress_mode = U_COMPRESS_NONE;
345 }
346
347 u_map_put(response->map_header, "Content-Type", content_type);
348 u_map_copy_into(response->map_header, &config->map_header);
349
350 if (compress_mode != U_COMPRESS_NONE) {
351 if (compress_mode == U_COMPRESS_GZIP && config->allow_cache_compressed && u_map_has_key(&config->gzip_files, file_requested)) {
352 ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
353 u_map_put(response->map_header, U_CONTENT_HEADER, U_ACCEPT_GZIP);
354 } else if (compress_mode == U_COMPRESS_DEFL && config->allow_cache_compressed && u_map_has_key(&config->deflate_files, file_requested)) {
355 ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
356 u_map_put(response->map_header, U_CONTENT_HEADER, U_ACCEPT_DEFLATE);
357 } else {
358 file_path = msprintf("%s/%s", ((struct _u_compressed_inmemory_website_config *)user_data)->files_path, file_requested);
359
360 if (access(file_path, F_OK) != -1) {
361 if (!pthread_mutex_lock(&config->lock)) {
362 f = fopen (file_path, "rb");
363 if (f) {
364 fseek (f, 0, SEEK_END);
365 offset = length = ftell (f);
366 fseek (f, 0, SEEK_SET);
367
368 if ((file_content_orig = file_content = o_malloc(length)) != NULL && (data_zip = o_malloc((2*length)+20)) != NULL) {
369 defstream.zalloc = u_zalloc;
370 defstream.zfree = u_zfree;
371 defstream.opaque = Z_NULL;
372 defstream.avail_in = (uInt)length;
373 defstream.next_in = (Bytef *)file_content;
374 defstream.avail_out = length;
375 defstream.next_out = (Bytef *)data_zip;
376 while ((read_length = fread(file_content, sizeof(char), offset, f))) {
377 file_content += read_length;
378 offset -= read_length;
379 }
380
381 if (compress_mode == U_COMPRESS_GZIP) {
382 if (deflateInit2(&defstream,
383 Z_DEFAULT_COMPRESSION,
384 Z_DEFLATED,
385 U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
386 8,
387 Z_DEFAULT_STRATEGY) != Z_OK) {
388 y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (gzip)");
389 ret = U_CALLBACK_ERROR;
390 }
391 } else {
392 if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
393 y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (deflate)");
394 ret = U_CALLBACK_ERROR;
395 }
396 }
397 if (ret == U_CALLBACK_CONTINUE) {
398 res = deflate(&defstream, Z_FINISH);
399 if (res == Z_STREAM_END) {
400 if (compress_mode == U_COMPRESS_GZIP) {
401 if (config->allow_cache_compressed) {
402 u_map_put_binary(&config->gzip_files, file_requested, data_zip, 0, defstream.total_out);
403 }
404 ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
405 } else {
406 if (config->allow_cache_compressed) {
407 u_map_put_binary(&config->deflate_files, file_requested, data_zip, 0, defstream.total_out);
408 }
409 ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
410 }
411 u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
412 } else {
413 y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflate file url %s: %d", file_requested, res);
414 ret = callback_static_file_uncompressed(request, response, user_data);
415 }
416 }
417 deflateEnd(&defstream);
418 } else {
419 y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resource for file_content or data_zip");
420 ret = U_CALLBACK_ERROR;
421 }
422 o_free(file_content_orig);
423 o_free(data_zip);
424 fclose(f);
425 }
426 pthread_mutex_unlock(&config->lock);
427 } else {
428 y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error pthread_lock_mutex");
429 ret = U_CALLBACK_ERROR;
430 }
431 } else {
432 if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
433 ret = U_CALLBACK_IGNORE;
434 } else {
435 ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
436 response->status = 302;
437 }
438 }
439 o_free(file_path);
440 }
441 } else {
442 ret = callback_static_file_uncompressed(request, response, user_data);
443 }
444 free_string_array(accept_list);
445 }
446 }
447 o_free(url_dup_save);
448 }
449
450 return ret;
451 }
0 /**
1 *
2 * Static file server with compression Ulfius callback
3 *
4 * Copyright 2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20201101
7 *
8 * The MIT License (MIT)
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 *
28 * `files_path`: path to the DocumentRoot folder, can be relative or absolute
29 * `url_prefix`: prefix used to access the callback function
30 * `mime_types`: a `struct _u_map` containing a set of mime-types with file extension as key and mime-type as value
31 * `mime_types_compressed`: A `string_array` structure containing the list of mime-types allowed for compression
32 * `mime_types_compressed_size`: The number of elements in `mime_types_compressed`
33 * `map_header`: a `struct _u_map` containing a set of headers that will be added to all responses within the `static_file_callback`
34 * `redirect_on_404`: redirct uri on error 404, if NULL, send 404
35 * `allow_gzip`: Set to true if you want to allow gzip compression (default true)
36 * `allow_deflate`: Set to true if you want to allow deflate compression (default true)
37 * `allow_cache_compressed`: set to true if you want to allow memory cache for compressed files (default true)
38 * `lock`: mutex lock (do not touch this variable)
39 * `gzip_files`: a `struct _u_map` containing cached gzip files
40 * `deflate_files`: a `struct _u_map` containing cached deflate files
41 *
42 * example of mime-types used in Hutch:
43 * {
44 * key = ".html"
45 * value = "text/html"
46 * },
47 * {
48 * key = ".css"
49 * value = "text/css"
50 * },
51 * {
52 * key = ".js"
53 * value = "application/javascript"
54 * },
55 * {
56 * key = ".png"
57 * value = "image/png"
58 * },
59 * {
60 * key = ".jpg"
61 * value = "image/jpeg"
62 * },
63 * {
64 * key = ".jpeg"
65 * value = "image/jpeg"
66 * },
67 * {
68 * key = ".ttf"
69 * value = "font/ttf"
70 * },
71 * {
72 * key = ".woff"
73 * value = "font/woff"
74 * },
75 * {
76 * key = ".woff2"
77 * value = "font/woff2"
78 * },
79 * {
80 * key = ".map"
81 * value = "application/octet-stream"
82 * },
83 * {
84 * key = "*"
85 * value = "application/octet-stream"
86 * }
87 *
88 */
89
90 #ifndef _U_STATIC_COMPRESSED_INMEMORY_WEBSITE
91 #define _U_STATIC_COMPRESSED_INMEMORY_WEBSITE
92
93 struct _u_compressed_inmemory_website_config {
94 char * files_path;
95 char * url_prefix;
96 struct _u_map mime_types;
97 char ** mime_types_compressed;
98 size_t mime_types_compressed_size;
99 struct _u_map map_header;
100 char * redirect_on_404;
101 int allow_gzip;
102 int allow_deflate;
103 int allow_cache_compressed;
104 pthread_mutex_t lock;
105 struct _u_map gzip_files;
106 struct _u_map deflate_files;
107 };
108
109 int u_init_compressed_inmemory_website_config(struct _u_compressed_inmemory_website_config * config);
110
111 void u_clean_compressed_inmemory_website_config(struct _u_compressed_inmemory_website_config * config);
112
113 int u_add_mime_types_compressed(struct _u_compressed_inmemory_website_config * config, const char * mime_type);
114
115 int callback_static_compressed_inmemory_website (const struct _u_request * request, struct _u_response * response, void * user_data);
116
117 #endif
0 # static file server callback function for Ulfius Framework
0 # Static file server callback function for Ulfius Framework
11
22 Provides a simple static file server. `user_data` must be initialized with a `struct static_file_config` containing the following informations:
33
44 - `files_path`: path to the DocumentRoot folder, can be relative or absolute
5 - `url_prefix`: prefix used to access the callback function
56 - `mime_types`: a `struct _u_map` containing a set of mime-types with file extension as key and mime-type as value
67 - `map_header`: a `struct _u_map` containing a set of headers that will be added to all responses within the `static_file_callback`
8 - `redirect_on_404`: redirct uri on error 404, if NULL, send 404
9
10 Here is a sample code on how to use the callback function:
11
12 ```C
13 struct static_file_config config;
14
15 // Add mime types
16 u_map_put(&config->mime_types, "*", "application/octet-stream");
17 u_map_put(&config->mime_types, ".html", "text/html");
18 u_map_put(&config->mime_types, ".css", "text/css");
19 u_map_put(&config->mime_types, ".js", "application/javascript");
20 u_map_put(&config->mime_types, ".json", "application/json");
21 u_map_put(&config->mime_types, ".png", "image/png");
22 u_map_put(&config->mime_types, ".gif", "image/gif");
23 u_map_put(&config->mime_types, ".jpeg", "image/jpeg");
24 u_map_put(&config->mime_types, ".jpg", "image/jpeg");
25 u_map_put(&config->mime_types, ".ttf", "font/ttf");
26 u_map_put(&config->mime_types, ".woff", "font/woff");
27 u_map_put(&config->mime_types, ".ico", "image/x-icon");
28
29 // Add callback function to all endpoints
30 ulfius_add_endpoint_by_val(instance, "GET", NULL, "*", 0, &callback_static_file, &config);
31 ```
11 *
22 * Static file server Ulfius callback
33 *
4 * Copyright 2017-2018 Nicolas Mora <mail@babelouest.org>
4 * Copyright 2017-2020 Nicolas Mora <mail@babelouest.org>
55 *
6 * Version 20181110
7 *
6 * Version 20201028
7 *
88 * The MIT License (MIT)
99 *
1010 * Permission is hereby granted, free of charge, to any person obtaining a copy
2525 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2626 * SOFTWARE.
2727 *
28 * struct static_file_config must be initialized with proper values
29 * files_path: path (relative or absolute) to the DocumentRoot folder
30 * url_prefix: prefix used to access the callback function
31 * mime_types: a struct _u_map filled with all the mime-types needed for a static file server
32 * redirect_on_404: redirct uri on error 404, if NULL, send 404
33 *
34 * example of mime-types used in Hutch:
35 * {
36 * key = ".html"
37 * value = "text/html"
38 * },
39 * {
40 * key = ".css"
41 * value = "text/css"
42 * },
43 * {
44 * key = ".js"
45 * value = "application/javascript"
46 * },
47 * {
48 * key = ".png"
49 * value = "image/png"
50 * },
51 * {
52 * key = ".jpg"
53 * value = "image/jpeg"
54 * },
55 * {
56 * key = ".jpeg"
57 * value = "image/jpeg"
58 * },
59 * {
60 * key = ".ttf"
61 * value = "font/ttf"
62 * },
63 * {
64 * key = ".woff"
65 * value = "font/woff"
66 * },
67 * {
68 * key = ".woff2"
69 * value = "font/woff2"
70 * },
71 * {
72 * key = ".map"
73 * value = "application/octet-stream"
74 * },
75 * {
76 * key = "*"
77 * value = "application/octet-stream"
78 * }
79 *
2880 */
2981
3082 #include <string.h>
83 #include <orcania.h>
84 #include <yder.h>
3185 #include <ulfius.h>
3286
3387 #include "static_file_callback.h"
48102 * Streaming callback function to ease sending large files
49103 */
50104 static ssize_t callback_static_file_stream(void * cls, uint64_t pos, char * buf, size_t max) {
105 (void)(pos);
51106 if (cls != NULL) {
52107 return fread (buf, 1, max, (FILE *)cls);
53108 } else {
74129 const char * content_type;
75130
76131 /*
77 * Comment this if statement if you put static files url not in root, like /app
132 * Comment this if statement if you don't access static files url from root dir, like /app
78133 */
79 if (response->shared_data != NULL) {
134 if (request->callback_position > 0) {
80135 return U_CALLBACK_CONTINUE;
81 }
82
83 if (user_data != NULL && ((struct _static_file_config *)user_data)->files_path != NULL) {
136 } else if (user_data != NULL && ((struct _static_file_config *)user_data)->files_path != NULL) {
84137 file_requested = o_strdup(request->http_url);
85138 url_dup_save = file_requested;
86139
00 /**
11 *
2 * Version 20181110
2 * Static file server Ulfius callback
33 *
4 * Copyright 2017-2020 Nicolas Mora <mail@babelouest.org>
5 *
6 * Version 20201028
7 *
48 * The MIT License (MIT)
59 *
610 * Permission is hereby granted, free of charge, to any person obtaining a copy
2024 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2125 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2226 * SOFTWARE.
23 *
27 *
2428 * struct static_file_config must be initialized with proper values
2529 * files_path: path (relative or absolute) to the DocumentRoot folder
2630 * url_prefix: prefix used to access the callback function
7579 *
7680 */
7781
78 #ifndef _STATIC_FILE
79 #define _STATIC_FILE
82 #ifndef _U_STATIC_FILE
83 #define _U_STATIC_FILE
8084
8185 #define STATIC_FILE_CHUNK 256
8286
4747 add_definitions(-D_GNU_SOURCE)
4848 endif ()
4949
50 include(FindZLIB)
51 find_package(ZLIB REQUIRED)
52 if (ZLIB_FOUND)
53 set(LIBS ${LIBS} ${ZLIB_LIBRARIES})
54 include_directories(${ZLIB_INCLUDE_DIRS})
55 endif ()
56
57 find_package(Threads REQUIRED)
58 set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT})
59
5060 include(FindUlfius)
5161 set(ULFIUS_MIN_VERSION "2.6")
5262 find_package(Ulfius ${ULFIUS_MIN_VERSION} REQUIRED)
6272
6373 # examples
6474
65 set(STATIC_CALLBACK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../example_callbacks/static_file/)
75 set(STATIC_CALLBACK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../example_callbacks/static_compressed_inmemory_website/)
6676 include_directories(${STATIC_CALLBACK_DIR})
77
78 set(HTTP_COMRESSION_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../example_callbacks/http_compression/)
79 include_directories(${HTTP_COMRESSION_DIR})
6780
6881 add_executable(simple_example ${CMAKE_CURRENT_SOURCE_DIR}/simple_example/simple_example.c)
6982 target_link_libraries(simple_example ${LIBS})
8699 add_executable(injection_example ${CMAKE_CURRENT_SOURCE_DIR}/injection_example/injection_example.c)
87100 target_link_libraries(injection_example ${LIBS})
88101
89 add_executable(sheep_counter ${CMAKE_CURRENT_SOURCE_DIR}/sheep_counter/sheep_counter.c)
102 add_executable(sheep_counter ${CMAKE_CURRENT_SOURCE_DIR}/sheep_counter/sheep_counter.c ${STATIC_CALLBACK_DIR}/static_compressed_inmemory_website_callback.c ${HTTP_COMRESSION_DIR}/http_compression_callback.c)
90103 target_link_libraries(sheep_counter ${LIBS})
91104 endif ()
92105
105118 endif ()
106119
107120 if (WITH_WEBSOCKET)
108 add_executable(websocket_server ${CMAKE_CURRENT_SOURCE_DIR}/websocket_example/websocket_server.c ${STATIC_CALLBACK_DIR}/static_file_callback.c)
121 add_executable(websocket_server ${CMAKE_CURRENT_SOURCE_DIR}/websocket_example/websocket_server.c ${STATIC_CALLBACK_DIR}/static_compressed_inmemory_website_callback.c)
122 set_target_properties(websocket_server PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/websocket_example/")
109123 target_link_libraries(websocket_server ${LIBS})
110 add_executable(websocket_client ${CMAKE_CURRENT_SOURCE_DIR}/websocket_example/websocket_client.c ${STATIC_CALLBACK_DIR}/static_file_callback.c)
124 add_executable(websocket_client ${CMAKE_CURRENT_SOURCE_DIR}/websocket_example/websocket_client.c)
125 set_target_properties(websocket_client PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/websocket_example/")
111126 target_link_libraries(websocket_client ${LIBS})
112127
113128 add_executable(auth_server ${CMAKE_CURRENT_SOURCE_DIR}/auth_example/auth_server.c)
123138 message(STATUS "Outgoing requests support: ${WITH_CURL}")
124139 message(STATUS "Jansson library support: ${WITH_JANSSON}")
125140 message(STATUS "Yder library support: ${WITH_YDER}")
126
1515 ULFIUS_LOCATION=../../src
1616 ULFIUS_INCLUDE=../../include
1717 EXAMPLE_INCLUDE=../include
18 CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS)
19 LIBS=-lc -lulfius -lorcania -ljansson -L$(ULFIUS_LOCATION)
18 STATIC_COMPRESSED_INMEMORY_WEBSITE=../../example_callbacks/static_compressed_inmemory_website
19 HTTP_COMRESSION=../../example_callbacks/http_compression
20 CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -I$(STATIC_COMPRESSED_INMEMORY_WEBSITE) -I$(HTTP_COMRESSION) -D_REENTRANT $(ADDITIONALFLAGS) $(CPPFLAGS)
21 LIBS=-lc -lz -lpthread -lulfius -lorcania -ljansson -L$(ULFIUS_LOCATION)
2022
2123 ifndef YDERFLAG
2224 LIBS+= -lyder
3436 ../../src/libulfius.so:
3537 cd $(ULFIUS_LOCATION) && $(MAKE) debug CURLFLAG=1 GNUTLSFLAG=1
3638
39 static_compressed_inmemory_website_callback.o: $(STATIC_COMPRESSED_INMEMORY_WEBSITE)/static_compressed_inmemory_website_callback.c
40 $(CC) $(CFLAGS) $(STATIC_COMPRESSED_INMEMORY_WEBSITE)/static_compressed_inmemory_website_callback.c
41
42 http_compression_callback.o: $(HTTP_COMRESSION)/http_compression_callback.c
43 $(CC) $(CFLAGS) $(HTTP_COMRESSION)/http_compression_callback.c
44
3745 sheep_counter.o: sheep_counter.c
3846 $(CC) $(CFLAGS) sheep_counter.c -DDEBUG -g -O0
3947
40 sheep_counter: ../../src/libulfius.so sheep_counter.o
41 $(CC) -o sheep_counter sheep_counter.o $(LIBS)
48 sheep_counter: ../../src/libulfius.so sheep_counter.o static_compressed_inmemory_website_callback.o http_compression_callback.o
49 $(CC) -o sheep_counter sheep_counter.o static_compressed_inmemory_website_callback.o http_compression_callback.o $(LIBS)
4250
4351 test: sheep_counter
4452 LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./sheep_counter
1717
1818 #include <ulfius.h>
1919 #include <u_example.h>
20
21 #include "static_compressed_inmemory_website_callback.h"
22 #include "http_compression_callback.h"
2023
2124 #define PORT 7437
2225 #define PREFIX "/sheep"
2326 #define FILE_PREFIX "/upload"
2427 #define STATIC_FOLDER "static"
2528
26 // Callback function used to serve static files that are present in the static folder
27 int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data);
28
29 // Callback function used to start a new count by setting the number of sheeps
30 int callback_sheep_counter_start (const struct _u_request * request, struct _u_response * response, void * user_data);
31
32 // Callback function used to reset the number of sheeps to 0
33 int callback_sheep_counter_reset (const struct _u_request * request, struct _u_response * response, void * user_data);
34
35 // Callback function used to add one sheep to the counter
36 int callback_sheep_counter_add (const struct _u_request * request, struct _u_response * response, void * user_data);
37
38 // Callback function used to upload file
39 int callback_upload_file (const struct _u_request * request, struct _u_response * response, void * user_data);
40
41 // File upload callback function
42 int file_upload_callback (const struct _u_request * request,
43 const char * key,
44 const char * filename,
45 const char * content_type,
46 const char * transfer_encoding,
47 const char * data,
48 uint64_t off,
49 size_t size,
50 void * user_data);
5129 /**
5230 * decode a u_map into a string
5331 */
8260 }
8361
8462 /**
85 * return the filename extension
86 */
87 const char * get_filename_ext(const char *path) {
88 const char *dot = o_strrchr(path, '.');
89 if(!dot || dot == path) return "*";
90 return dot + 1;
91 }
92
93 /**
94 * Main function
95 */
96 int main (int argc, char **argv) {
97
98 // jansson integer type can vary
99 #if JSON_INTEGER_IS_LONG_LONG
100 long long nb_sheep = 0;
101 #else
102 long nb_sheep = 0;
103 #endif
104
105 // Initialize the instance
106 struct _u_instance instance;
107
108 y_init_logs("sheep_counter", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting sheep_counter");
109
110 if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) {
111 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort");
112 return(1);
113 }
114
115 // Max post param size is 16 Kb, which means an uploaded file is no more than 16 Kb
116 instance.max_post_param_size = 16*1024;
117
118 if (ulfius_set_upload_file_callback_function(&instance, &file_upload_callback, "my cls") != U_OK) {
119 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_set_upload_file_callback_function");
120 }
121
122 // MIME types that will define the static files
123 struct _u_map mime_types;
124 u_map_init(&mime_types);
125 u_map_put(&mime_types, ".html", "text/html");
126 u_map_put(&mime_types, ".css", "text/css");
127 u_map_put(&mime_types, ".js", "application/javascript");
128 u_map_put(&mime_types, ".png", "image/png");
129 u_map_put(&mime_types, ".jpeg", "image/jpeg");
130 u_map_put(&mime_types, ".jpg", "image/jpeg");
131 u_map_put(&mime_types, "*", "application/octet-stream");
132
133 // Endpoint list declaration
134 // The first 3 are webservices with a specific url
135 // The last endpoint will be called for every GET call and will serve the static files
136 ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, NULL, 1, &callback_sheep_counter_start, &nb_sheep);
137 ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, NULL, 1, &callback_sheep_counter_add, &nb_sheep);
138 ulfius_add_endpoint_by_val(&instance, "DELETE", PREFIX, NULL, 1, &callback_sheep_counter_reset, &nb_sheep);
139 ulfius_add_endpoint_by_val(&instance, "*", FILE_PREFIX, NULL, 1, &callback_upload_file, NULL);
140 ulfius_add_endpoint_by_val(&instance, "GET", "*", NULL, 1, &callback_static_file, &mime_types);
141
142 // Start the framework
143 if (ulfius_start_framework(&instance) == U_OK) {
144 printf("Start sheep counter on port %u\n", instance.port);
145
146 // Wait for the user to press <enter> on the console to quit the application
147 getchar();
148 } else {
149 printf("Error starting framework\n");
150 }
151
152 // Clean the mime map
153 u_map_clean(&mime_types);
154
155 printf("End framework\n");
156 ulfius_stop_framework(&instance);
157 ulfius_clean_instance(&instance);
158
159 y_close_logs();
160
161 return 0;
162 }
163
164 /**
16563 * Start a new counter by setting the number of sheeps to a specific value
16664 * return the current number of sheeps in a json object
16765 */
168 int callback_sheep_counter_start (const struct _u_request * request, struct _u_response * response, void * user_data) {
66 static int callback_sheep_counter_start (const struct _u_request * request, struct _u_response * response, void * user_data) {
16967 json_t * json_nb_sheep = ulfius_get_json_body_request(request, NULL), * json_body = NULL;
17068
171 #if JSON_INTEGER_IS_LONG_LONG
172 long long * nb_sheep = user_data;
173 #else
174 long * nb_sheep = user_data;
175 #endif
69 json_int_t * nb_sheep = user_data;
17670
17771 if (json_nb_sheep != NULL) {
17872 * nb_sheep = json_integer_value(json_object_get(json_nb_sheep,"nbsheep"));
19286 * Reset the number of sheeps to 0
19387 * return the current number of sheeps in a json object
19488 */
195 int callback_sheep_counter_reset (const struct _u_request * request, struct _u_response * response, void * user_data) {
89 static int callback_sheep_counter_reset (const struct _u_request * request, struct _u_response * response, void * user_data) {
19690 json_t * json_body = NULL;
197 #if JSON_INTEGER_IS_LONG_LONG
198 long long * nb_sheep = user_data;
199 #else
200 long * nb_sheep = user_data;
201 #endif
91 json_int_t * nb_sheep = user_data;
20292 * nb_sheep = 0;
20393
20494 json_body = json_object();
213103 * Adds one sheep
214104 * return the current number of sheeps in a json object
215105 */
216 int callback_sheep_counter_add (const struct _u_request * request, struct _u_response * response, void * user_data) {
106 static int callback_sheep_counter_add (const struct _u_request * request, struct _u_response * response, void * user_data) {
217107 json_t * json_body = NULL;
218 #if JSON_INTEGER_IS_LONG_LONG
219 long long * nb_sheep = user_data;
220 #else
221 long * nb_sheep = user_data;
222 #endif
108 json_int_t * nb_sheep = user_data;
223109
224110 (*nb_sheep)++;
225111
232118 }
233119
234120 /**
235 * serve a static file to the client as a very simple http server
236 */
237 int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data) {
238 void * buffer = NULL;
239 long length;
240 FILE * f;
241 char * file_path = msprintf("%s%s", STATIC_FOLDER, request->http_url);
242 const char * content_type;
243
244 if (access(file_path, F_OK) != -1) {
245 f = fopen (file_path, "rb");
246 if (f) {
247 fseek (f, 0, SEEK_END);
248 length = ftell (f);
249 fseek (f, 0, SEEK_SET);
250 buffer = o_malloc(length*sizeof(void));
251 if (buffer) {
252 fread (buffer, 1, length, f);
253 }
254 fclose (f);
255 }
256
257 if (buffer) {
258 content_type = u_map_get((struct _u_map *)user_data, get_filename_ext(request->http_url));
259 response->binary_body = buffer;
260 response->binary_body_length = length;
261 u_map_put(response->map_header, "Content-Type", content_type);
262 response->status = 200;
263 } else {
264 response->status = 404;
265 }
266 } else {
267 response->status = 404;
268 }
269 o_free(file_path);
270 return U_CALLBACK_CONTINUE;
271 }
272
273 /**
274121 * upload a file
275122 */
276 int callback_upload_file (const struct _u_request * request, struct _u_response * response, void * user_data) {
123 static int callback_upload_file (const struct _u_request * request, struct _u_response * response, void * user_data) {
277124 char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie),
278125 * post_params = print_map(request->map_post_body);
279126
291138 /**
292139 * File upload callback function
293140 */
294 int file_upload_callback (const struct _u_request * request,
141 static int file_upload_callback (const struct _u_request * request,
295142 const char * key,
296143 const char * filename,
297144 const char * content_type,
303150 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);
304151 return U_OK;
305152 }
153
154 /**
155 * Main function
156 */
157 int main (int argc, char **argv) {
158
159 struct _u_compressed_inmemory_website_config file_config;
160 json_int_t nb_sheep = 0;
161
162 // Initialize the instance
163 struct _u_instance instance;
164
165 y_init_logs("sheep_counter", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting sheep_counter");
166
167 if (u_init_compressed_inmemory_website_config(&file_config) == U_OK) {
168 u_map_put(&file_config.mime_types, ".html", "text/html");
169 u_map_put(&file_config.mime_types, ".css", "text/css");
170 u_map_put(&file_config.mime_types, ".js", "application/javascript");
171 u_map_put(&file_config.mime_types, ".png", "image/png");
172 u_map_put(&file_config.mime_types, ".jpg", "image/jpeg");
173 u_map_put(&file_config.mime_types, ".jpeg", "image/jpeg");
174 u_map_put(&file_config.mime_types, ".ttf", "font/ttf");
175 u_map_put(&file_config.mime_types, ".woff", "font/woff");
176 u_map_put(&file_config.mime_types, ".woff2", "font/woff2");
177 u_map_put(&file_config.mime_types, ".map", "application/octet-stream");
178 u_map_put(&file_config.mime_types, ".json", "application/json");
179 u_map_put(&file_config.mime_types, "*", "application/octet-stream");
180 file_config.files_path = "static";
181 file_config.url_prefix = FILE_PREFIX;
182
183 if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) {
184 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort");
185 return(1);
186 }
187
188 // Max post param size is 16 Kb, which means an uploaded file is no more than 16 Kb
189 instance.max_post_param_size = 16*1024;
190
191 if (ulfius_set_upload_file_callback_function(&instance, &file_upload_callback, "my cls") != U_OK) {
192 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_set_upload_file_callback_function");
193 }
194
195 // Endpoint list declaration
196 // The first 3 are webservices with a specific url
197 // The last endpoint will be called for every GET call and will serve the static files
198 ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, NULL, 1, &callback_sheep_counter_start, &nb_sheep);
199 ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, NULL, 1, &callback_sheep_counter_add, &nb_sheep);
200 ulfius_add_endpoint_by_val(&instance, "DELETE", PREFIX, NULL, 1, &callback_sheep_counter_reset, &nb_sheep);
201 ulfius_add_endpoint_by_val(&instance, "*", PREFIX, NULL, 2, &callback_http_compression, NULL);
202 ulfius_add_endpoint_by_val(&instance, "*", FILE_PREFIX, NULL, 1, &callback_upload_file, NULL);
203 ulfius_add_endpoint_by_val(&instance, "GET", "*", NULL, 1, &callback_static_compressed_inmemory_website, &file_config);
204
205 // Start the framework
206 if (ulfius_start_framework(&instance) == U_OK) {
207 printf("Start sheep counter on port %u\n", instance.port);
208
209 // Wait for the user to press <enter> on the console to quit the application
210 getchar();
211 } else {
212 printf("Error starting framework\n");
213 }
214
215 printf("End framework\n");
216 ulfius_stop_framework(&instance);
217 ulfius_clean_instance(&instance);
218 u_clean_compressed_inmemory_website_config(&file_config);
219 }
220
221 y_close_logs();
222
223 return 0;
224 }
3636 $("#counter").val(counter);
3737 var left = (Math.random() * ($(document).width())).toFixed()-300;
3838 var top = (Math.random() * ($(document).height())).toFixed();
39 $("#layout").append("<img id='sheep-"+counter+"' src='/sheep.png'>");
39 $("#layout").append("<img id='sheep-"+counter+"' src='/static/sheep.png'>");
4040 $("#sheep-"+counter).css({"position":"absolute","top": top + "px", "left": left + "px"});
4141 }
4242 });
22 #
33 # Makefile used to build the software
44 #
5 # Copyright 2017 Nicolas Mora <mail@babelouest.org>
5 # Copyright 2017-2020 Nicolas Mora <mail@babelouest.org>
66 #
77 # This program is free software; you can redistribute it and/or
88 # modify it under the terms of the MIT License
1515 ULFIUS_LOCATION=../../src
1616 ULFIUS_INCLUDE=../../include
1717 EXAMPLE_INCLUDE=../include
18 CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -I$(STATIC_FILE_LOCATION) $(ADDITIONALFLAGS) $(CPPFLAGS)
18 CFLAGS+=-c -Wall -I$(ULFIUS_INCLUDE) -I$(EXAMPLE_INCLUDE) -I$(STATIC_FILE_LOCATION) -I$(STATIC_COMPRESSED_INMEMORY_WEBSITE) $(ADDITIONALFLAGS) $(CPPFLAGS)
1919 STATIC_FILE_LOCATION=../../example_callbacks/static_file
20 LIBS=-lc -lulfius -lorcania -L$(ULFIUS_LOCATION)
20 STATIC_COMPRESSED_INMEMORY_WEBSITE=../../example_callbacks/static_compressed_inmemory_website
21 LIBS=-lc -lz -lpthread -ljansson -lulfius -lorcania -L$(ULFIUS_LOCATION)
22 LIBULFIUS=../../src/libulfius.so
2123 #SECUREFLAG=-https test.key test.pem
2224
2325 ifndef YDERFLAG
3335
3436 debug: websocket_server websocket_client
3537
36 ../../src/libulfius.so:
37 cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=1
38 $(LIBULFIUS): $(ULFIUS_LOCATION)/ulfius.c $(ULFIUS_LOCATION)/u_map.c $(ULFIUS_LOCATION)/u_request.c $(ULFIUS_LOCATION)/u_response.c $(ULFIUS_LOCATION)/u_send_request.c $(ULFIUS_LOCATION)/u_websocket.c $(ULFIUS_LOCATION)/yuarel.c $(ULFIUS_INCLUDE)/ulfius.h $(ULFIUS_INCLUDE)/u_private.h
39 cd $(ULFIUS_LOCATION) && $(MAKE) debug
3840
3941 static_file_callback.o: $(STATIC_FILE_LOCATION)/static_file_callback.c
4042 $(CC) $(CFLAGS) $(STATIC_FILE_LOCATION)/static_file_callback.c
4143
42 websocket_server: ../../src/libulfius.so static_file_callback.o websocket_server.c
44 static_compressed_inmemory_website_callback.o: $(STATIC_COMPRESSED_INMEMORY_WEBSITE)/static_compressed_inmemory_website_callback.c
45 $(CC) $(CFLAGS) $(STATIC_COMPRESSED_INMEMORY_WEBSITE)/static_compressed_inmemory_website_callback.c
46
47 websocket_server: $(LIBULFIUS) static_compressed_inmemory_website_callback.o websocket_server.c
4348 $(CC) $(CFLAGS) websocket_server.c
44 $(CC) -o websocket_server websocket_server.o static_file_callback.o $(LIBS)
49 $(CC) -o websocket_server websocket_server.o static_compressed_inmemory_website_callback.o $(LIBS)
4550
46 websocket_client: ../../src/libulfius.so websocket_client.c
51 websocket_client: $(LIBULFIUS) websocket_client.c
4752 $(CC) $(CFLAGS) websocket_client.c
4853 $(CC) -o websocket_client websocket_client.o $(LIBS)
4954
2323
2424 function connectSocket(echo) {
2525 if (location.protocol === "https:") {
26 mySocket = new WebSocket("wss://" + location.hostname + ":" + location.port + "/websocket" + (echo?"/echo":""), ["plop"]);
26 mySocket = new WebSocket("wss://" + location.hostname + ":" + location.port + "/websocket" + (echo?"/echo":""));
2727 } else {
28 mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket" + (echo?"/echo":""), ["plop"]);
28 mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket" + (echo?"/echo":""));
2929 }
3030 mySocket.onmessage = function (event) {
3131 if (event.data instanceof Blob) {
9797 var curFileData = ev2.target.result;
9898 if (curFileData) {
9999 if (location.protocol === "https:") {
100 mySocket = new WebSocket("wss://" + location.hostname + ":" + location.port + "/websocket/file", ["file"]);
100 mySocket = new WebSocket("wss://" + location.hostname + ":" + location.port + "/websocket/file");
101101 } else {
102 mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file", ["file"]);
102 mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file");
103103 }
104104 mySocket.binaryType = "arraybuffer";
105105 mySocket.onopen = function () {
125125 var curFileText = ev2.target.result;
126126 if (curFileText) {
127127 if (location.protocol === "https:") {
128 mySocket = new WebSocket("wss://" + location.hostname + ":" + location.port + "/websocket/file", ["file"]);
128 mySocket = new WebSocket("wss://" + location.hostname + ":" + location.port + "/websocket/file");
129129 } else {
130 mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file", ["file"]);
130 mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file");
131131 }
132132 mySocket.binaryType = "arraybuffer";
133133 mySocket.onopen = function () {
111111 int main(int argc, char ** argv) {
112112 struct _u_request request;
113113 struct _u_response response;
114 struct _websocket_client_handler websocket_client_handler;
114 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
115115 char * websocket_user_data = o_strdup("my user data");
116116 char * url = (argc>1&&0==o_strcmp("-https", argv[1]))?"wss://localhost:" PORT PREFIX_WEBSOCKET:"ws://localhost:" PORT PREFIX_WEBSOCKET;
117117
118118 y_init_logs("websocket_client", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_client");
119119 ulfius_init_request(&request);
120120 ulfius_init_response(&response);
121 if (ulfius_set_websocket_request(&request, url, "protocol", "extension") == U_OK) {
121 if (ulfius_set_websocket_request(&request, url, "protocol", "permessage-deflate") == U_OK) {
122 ulfius_add_websocket_client_deflate_extension(&websocket_client_handler);
122123 request.check_server_certificate = 0;
123124 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) {
124125 y_log_message(Y_LOG_LEVEL_DEBUG, "Wait for user to press <enter> to close the program");
1818 #include <ulfius.h>
1919 #include <u_example.h>
2020
21 #include "static_file_callback.h"
21 #include "static_compressed_inmemory_website_callback.h"
2222
2323 #define PORT 9275
2424 #define PREFIX_WEBSOCKET "/websocket"
6767 int main(int argc, char ** argv) {
6868 int ret;
6969 struct _u_instance instance;
70 struct _static_file_config file_config;
70 struct _u_compressed_inmemory_website_config file_config;
7171 char * cert_file = NULL, * key_file = NULL;
7272
7373 y_init_logs("websocket_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_example");
7474
75 file_config.mime_types = o_malloc(sizeof(struct _u_map));
76 u_map_init(file_config.mime_types);
77 u_map_put(file_config.mime_types, ".html", "text/html");
78 u_map_put(file_config.mime_types, ".css", "text/css");
79 u_map_put(file_config.mime_types, ".js", "application/javascript");
80 u_map_put(file_config.mime_types, ".png", "image/png");
81 u_map_put(file_config.mime_types, ".jpg", "image/jpeg");
82 u_map_put(file_config.mime_types, ".jpeg", "image/jpeg");
83 u_map_put(file_config.mime_types, ".ttf", "font/ttf");
84 u_map_put(file_config.mime_types, ".woff", "font/woff");
85 u_map_put(file_config.mime_types, ".woff2", "font/woff2");
86 u_map_put(file_config.mime_types, ".map", "application/octet-stream");
87 u_map_put(file_config.mime_types, "*", "application/octet-stream");
88 file_config.files_path = "static";
89 file_config.url_prefix = PREFIX_STATIC;
90 file_config.map_header = o_malloc(sizeof(struct _u_map));
91 u_map_init(file_config.map_header);
92
93 if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) {
94 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort");
95 return(1);
96 }
97
98 u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*");
99
100 // Endpoint list declaration
101 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, NULL);
102 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/echo", 0, &callback_websocket_echo, NULL);
103 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/file", 0, &callback_websocket_file, NULL);
104 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_STATIC, "*", 0, &callback_static_file, &file_config);
105
106 // Start the framework
107 if (argc > 3 && 0 == o_strcmp(argv[1], "-https")) {
108 key_file = read_file(argv[2]);
109 cert_file = read_file(argv[3]);
110 if (key_file == NULL || cert_file == NULL) {
111 printf("Error reading https certificate files\n");
112 ret = U_ERROR_PARAMS;
75 if (u_init_compressed_inmemory_website_config(&file_config) == U_OK) {
76 u_map_put(&file_config.mime_types, ".html", "text/html");
77 u_map_put(&file_config.mime_types, ".css", "text/css");
78 u_map_put(&file_config.mime_types, ".js", "application/javascript");
79 u_map_put(&file_config.mime_types, ".png", "image/png");
80 u_map_put(&file_config.mime_types, ".jpg", "image/jpeg");
81 u_map_put(&file_config.mime_types, ".jpeg", "image/jpeg");
82 u_map_put(&file_config.mime_types, ".ttf", "font/ttf");
83 u_map_put(&file_config.mime_types, ".woff", "font/woff");
84 u_map_put(&file_config.mime_types, ".woff2", "font/woff2");
85 u_map_put(&file_config.mime_types, ".map", "application/octet-stream");
86 u_map_put(&file_config.mime_types, ".json", "application/json");
87 u_map_put(&file_config.mime_types, "*", "application/octet-stream");
88 file_config.files_path = "static";
89 file_config.url_prefix = PREFIX_STATIC;
90
91 if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) {
92 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort");
93 return(1);
94 }
95
96 u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*");
97
98 // Endpoint list declaration
99 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, NULL);
100 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/echo", 0, &callback_websocket_echo, NULL);
101 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, "/file", 0, &callback_websocket_file, NULL);
102 ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_STATIC, "*", 0, &callback_static_compressed_inmemory_website, &file_config);
103
104 // Start the framework
105 if (argc > 3 && 0 == o_strcmp(argv[1], "-https")) {
106 key_file = read_file(argv[2]);
107 cert_file = read_file(argv[3]);
108 if (key_file == NULL || cert_file == NULL) {
109 printf("Error reading https certificate files\n");
110 ret = U_ERROR_PARAMS;
111 } else {
112 ret = ulfius_start_secure_framework(&instance, key_file, cert_file);
113 }
114 o_free(key_file);
115 o_free(cert_file);
113116 } else {
114 ret = ulfius_start_secure_framework(&instance, key_file, cert_file);
115 }
116 o_free(key_file);
117 o_free(cert_file);
118 } else {
119 ret = ulfius_start_framework(&instance);
120 }
121
122 if (ret == U_OK) {
123 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");
124
125 // Wait for the user to press <enter> on the console to quit the application
126 getchar();
127 } else {
128 y_log_message(Y_LOG_LEVEL_ERROR, "Error starting framework");
129 }
130 y_log_message(Y_LOG_LEVEL_INFO, "End framework");
131
132 ulfius_stop_framework(&instance);
133 ulfius_clean_instance(&instance);
134 u_map_clean_full(file_config.mime_types);
135 u_map_clean_full(file_config.map_header);
117 ret = ulfius_start_framework(&instance);
118 }
119
120 if (ret == U_OK) {
121 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");
122
123 // Wait for the user to press <enter> on the console to quit the application
124 getchar();
125 } else {
126 y_log_message(Y_LOG_LEVEL_ERROR, "Error starting framework");
127 }
128 y_log_message(Y_LOG_LEVEL_INFO, "End framework");
129
130 ulfius_stop_framework(&instance);
131 ulfius_clean_instance(&instance);
132 u_clean_compressed_inmemory_website_config(&file_config);
133 }
134
136135 y_close_logs();
137136
138137 return 0;
272271 int ret;
273272
274273 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) {
274 ulfius_add_websocket_deflate_extension(response);
275275 return U_CALLBACK_CONTINUE;
276276 } else {
277277 return U_CALLBACK_ERROR;
284284
285285 y_log_message(Y_LOG_LEVEL_DEBUG, "Client connected to echo websocket");
286286 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) {
287 ulfius_add_websocket_deflate_extension(response);
287288 return U_CALLBACK_CONTINUE;
288289 } else {
289290 return U_CALLBACK_ERROR;
294295 int ret;
295296
296297 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) {
298 ulfius_add_websocket_deflate_extension(response);
297299 return U_CALLBACK_CONTINUE;
298300 } else {
299301 return U_CALLBACK_ERROR;
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * u_private.h: private structures and functions declarations
7 *
7 *
88 * Copyright 2015-2017 Nicolas Mora <mail@babelouest.org>
99 *
1010 * This program is free software; you can redistribute it and/or
1919 *
2020 * You should have received a copy of the GNU General Public
2121 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 *
22 *
2323 */
2424
2525 #ifndef __U_PRIVATE_H__
7979 int ulfius_set_response_cookie(struct MHD_Response * mhd_response, const struct _u_response * response);
8080
8181 /**
82 * The utf8_check() function scans the '\0'-terminated string starting
82 * The utf8_check() function scans the string starting
8383 * at s. It returns a pointer to the first byte of the first malformed
8484 * or overlong UTF-8 sequence found, or NULL if the string contains
8585 * only correct UTF-8. It also spots UTF-8 sequences that could cause
9494 * are no doubt performance optimizations possible for certain CPUs.
9595 *
9696 * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30
97 * Nicolas Mora <mail@babelouest.org>
9798 * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html
9899 */
99 const unsigned char * utf8_check(const char * s_orig);
100 const unsigned char * utf8_check(const char * s_orig, size_t len);
100101
101102 #ifndef U_DISABLE_WEBSOCKET
103
104 #define _U_W_BUFF_LEN 256
105 #define _U_W_EXT_DEFLATE "permessage-deflate"
106 #define _U_W_EXT_DEFLATE_S_CTX "server_no_context_takeover"
107 #define _U_W_EXT_DEFLATE_C_CTX "client_no_context_takeover"
108
109 void ulfius_free_websocket_extension_pointer_list(void * extension);
110
111 void ulfius_free_websocket_extension(struct _websocket_extension * websocket_extension);
112
113 int ulfius_init_websocket_extension(struct _websocket_extension * websocket_extension);
102114
103115 /**
104116 * Websocket callback function for MHD
167179 int ulfius_close_websocket(struct _websocket * websocket);
168180
169181 /**
170 * Run the websocket manager in a separated detached thread
171 */
172 void * ulfius_thread_websocket_manager_run(void * args);
173
174 /**
175182 * Add a websocket in the list of active websockets of the instance
176183 */
177184 int ulfius_instance_add_websocket_active(struct _u_instance * instance, struct _websocket * websocket);
179186 /**
180187 * Remove a websocket from the list of active websockets of the instance
181188 */
182 int ulfius_instance_remove_websocket_active(struct _u_instance * instance, struct _websocket * websocket);
189 int ulfius_instance_remove_websocket_active(struct _u_instance * instance, struct _websocket * websocket);
183190
184191 /**
185192 * Initialize a struct _websocket
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * ulfius-cfg.h.in: configuration file
7 *
7 *
88 * Copyright 2018-2020 Nicolas Mora <mail@babelouest.org>
99 *
1010 * This program is free software; you can redistribute it and/or
1919 *
2020 * You should have received a copy of the GNU General Public
2121 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 *
22 *
2323 */
2424 #ifndef _ULFIUS_CFG_H_
2525 #define _ULFIUS_CFG_H_
00 /**
1 *
1 *
22 * @file ulfius.h
33 * @brief Ulfius framework
4 *
4 *
55 * REST framework library
6 *
6 *
77 * ulfius.h: public structures and functions declarations
8 *
8 *
99 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
1010 *
1111 * This program is free software; you can redistribute it and/or
2020 *
2121 * You should have received a copy of the GNU General Public
2222 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 *
23 *
2424 */
2525
2626 #ifndef __ULFIUS_H__
4545
4646 #ifndef U_DISABLE_WEBSOCKET
4747 #include <poll.h>
48 #include <zlib.h>
4849 #ifndef POLLRDHUP
4950 #define POLLRDHUP 0x2000
5051 #endif
115116 #define U_ERROR_DISCONNECTED 7 ///< Connection closed
116117
117118 #define U_CALLBACK_CONTINUE 0 ///< Callback exited with success, continue to next callback
118 #define U_CALLBACK_COMPLETE 1 ///< Callback exited with success, exit callback list
119 #define U_CALLBACK_UNAUTHORIZED 2 ///< Request is unauthorized, exit callback list and return status 401
120 #define U_CALLBACK_ERROR 3 ///< Error during request process, exit callback list and return status 500
119 #define U_CALLBACK_IGNORE 1 ///< Callback decided to be ignored, request.callback_position will not be incremented, continue to next callback
120 #define U_CALLBACK_COMPLETE 2 ///< Callback exited with success, exit callback list
121 #define U_CALLBACK_UNAUTHORIZED 3 ///< Request is unauthorized, exit callback list and return status 401
122 #define U_CALLBACK_ERROR 4 ///< Error during request process, exit callback list and return status 500
121123
122124 #define U_COOKIE_SAME_SITE_NONE 0 ///< Set same_site cookie property to 0
123125 #define U_COOKIE_SAME_SITE_STRICT 1 ///< Set same_site cookie property to strict
215217 };
216218
217219 /**
218 *
219 * @struct _u_request request parameters
220 *
221 * @struct _u_request
220222 * @brief definition of the parameters available in a struct _u_request
221 *
223 *
222224 */
223225 struct _u_request {
224226 char * http_protocol; /* !< http protocol used (1.0 or 1.1) */
255257 };
256258
257259 /**
258 *
259 * @struct _u_response response parameters
260 *
261 * @struct _u_response
260262 * @brief definition of the parameters available in a struct _u_response
261 *
263 *
262264 */
263265 struct _u_response {
264266 long status; /* !< HTTP status code (200, 404, 500, etc) */
280282 };
281283
282284 /**
283 *
284 * @struct _u_endpoint endpoint definition
285 *
286 * @struct _u_endpoint
285287 * @brief Contains all informations needed for an endpoint
286 *
288 *
287289 */
288290 struct _u_endpoint {
289291 char * http_method; /* !< http verb (GET, POST, PUT, etc.) in upper case */
297299 };
298300
299301 /**
300 *
301 * @struct _u_instance Ulfius instance definition
302 *
303 * @struct _u_instance
302304 * @brief Contains the needed data for an ulfius instance to work
303 *
305 *
304306 */
305307 struct _u_instance {
306308 struct MHD_Daemon * mhd_daemon; /* !< pointer to the libmicrohttpd daemon */
321323 size_t max_post_body_size; /* !< maximum size for the entire post body, 0 means no limit, default 0 */
322324 void * websocket_handler; /* !< handler for the websocket structure */
323325 int (* file_upload_callback) (const struct _u_request * request, /* !< callback function to manage file upload by blocks */
324 const char * key,
325 const char * filename,
326 const char * content_type,
327 const char * transfer_encoding,
328 const char * data,
329 uint64_t off,
330 size_t size,
326 const char * key,
327 const char * filename,
328 const char * content_type,
329 const char * transfer_encoding,
330 const char * data,
331 uint64_t off,
332 size_t size,
331333 void * cls);
332334 void * file_upload_cls; /* !< any pointer to pass to the file_upload_callback function */
333335 int mhd_response_copy_data; /* !< to choose between MHD_RESPMEM_MUST_COPY and MHD_RESPMEM_MUST_FREE, only if you use MHD < 0.9.61, otherwise this option is skipped because it's useless */
359361 **********************************/
360362
361363 /**
362 * @defgroup memory
364 * @defgroup mem memory
363365 * memory management function
364366 * @{
365367 */
396398
397399 /**
398400 * ulfius_init_instance
399 *
401 *
400402 * Initialize a struct _u_instance * with default values
401403 * Binds to IPV4 addresses only
404 * @param u_instance the ulfius instance to initialize
402405 * @param port tcp port to bind to, must be between 1 and 65535
403406 * @param bind_address IPv4 address to listen to, optional, the reference is borrowed, the structure isn't copied
404407 * @param default_auth_realm default realm to send to the client on authentication error
409412 #if MHD_VERSION >= 0x00095208
410413 /**
411414 * ulfius_init_instance_ipv6
412 *
415 *
413416 * Initialize a struct _u_instance * with default values
414417 * Binds to IPV6 and IPV4 addresses or IPV6 addresses only
415418 * @param port tcp port to bind to, must be between 1 and 65535
423426
424427 /**
425428 * ulfius_clean_instance
426 *
429 *
427430 * Clean memory allocated by a struct _u_instance *
428431 * @param u_instance an Ulfius instance
429432 */
432435 /**
433436 * ulfius_start_framework
434437 * Initializes the framework and run the webservice based on the parameters given
435 *
438 *
436439 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
437440 * @return U_OK on success
438441 */
441444 /**
442445 * ulfius_start_secure_framework
443446 * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection
444 *
447 *
445448 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
446449 * @param key_pem private key for the server
447450 * @param cert_pem server certificate
454457 * ulfius_start_secure_ca_trust_framework
455458 * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection
456459 * And using a root server to authenticate client connections
457 *
460 *
458461 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
459462 * @param key_pem private key for the server
460463 * @param cert_pem server certificate
476479 * i.e.: you're on your own
477480 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
478481 * @param mhd_flags OR-ed combination of MHD_FLAG values
479 * @param mhd_ops struct MHD_OptionItem * options table,
482 * @param mhd_ops struct MHD_OptionItem * options table,
480483 * - MUST contain an option with the fllowing value: {.option = MHD_OPTION_NOTIFY_COMPLETED; .value = (intptr_t)mhd_request_completed; .ptr_value = NULL;}
481484 * - MUST contain an option with the fllowing value: {.option = MHD_OPTION_URI_LOG_CALLBACK; .value = (intptr_t)ulfius_uri_logger; .ptr_value = NULL;}
482485 * - MUST end with a terminal struct MHD_OptionItem: {.option = MHD_OPTION_END; .value = 0; .ptr_value = NULL;}
492495
493496 /**
494497 * ulfius_stop_framework
495 *
498 *
496499 * Stop the webservice
497500 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
498501 * @return U_OK on success
501504
502505 /**
503506 * ulfius_set_upload_file_callback_function
504 *
507 *
505508 * Set the callback function to handle file upload
506509 * Used to facilitate large files upload management
507510 * The callback function file_upload_callback will be called
508511 * multiple times, with the uploaded file in striped in parts
509 *
512 *
510513 * Warning: If this function is used, all the uploaded files
511514 * for the instance will be managed via this function, and they
512515 * will no longer be available in the struct _u_request in the
513516 * ulfius callback function afterwards.
514 *
517 *
515518 * Thanks to Thad Phetteplace for the help on this feature
516 *
519 *
517520 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
518521 * @param file_upload_callback Pointer to a callback function that will handle all file uploads
519522 * @param cls a pointer that will be passed to file_upload_callback each tim it's called
520523 * @return U_OK on success
521524 */
522525 int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance,
523 int (* file_upload_callback) (const struct _u_request * request,
524 const char * key,
525 const char * filename,
526 const char * content_type,
527 const char * transfer_encoding,
528 const char * data,
529 uint64_t off,
530 size_t size,
526 int (* file_upload_callback) (const struct _u_request * request,
527 const char * key,
528 const char * filename,
529 const char * content_type,
530 const char * transfer_encoding,
531 const char * data,
532 uint64_t off,
533 size_t size,
531534 void * cls),
532535 void * cls);
533536
550553 * Can be done during the execution of the webservice for injection
551554 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
552555 * @param u_endpoint pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list
553 * @param return U_OK on success
556 * @return U_OK on success
554557 */
555558 int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint);
556559
606609 * Set the default endpoint
607610 * This endpoint will be called if no endpoint match the url called
608611 * @param u_instance pointer to a struct _u_instance that describe its port and bind address
609 * @param auth_function a pointer to a function that will be executed prior to the callback for authentication
610 * you must declare the function as described.
611 * @param auth_data a pointer to a data or a structure that will be available in auth_function
612 * @param auth_realm realm value for authentication
613 * callback_function a pointer to a function that will be executed each time the endpoint is called
614 * you must declare the function as described.
612 * @param callback_function a pointer to a function that will be executed each time the endpoint is called
613 * you must declare the function as described.
615614 * @param user_data a pointer to a data or a structure that will be available in callback_function
616615 * to remove a default endpoint, call ulfius_set_default_endpoint with NULL parameter for callback_function
617616 * @return U_OK on success
735734 * @param mail_body email body (mandatory)
736735 * @return U_OK on success
737736 */
738 int ulfius_send_smtp_email(const char * host,
739 const int port,
740 const int use_tls,
741 const int verify_certificate,
742 const char * user,
743 const char * password,
744 const char * from,
745 const char * to,
746 const char * cc,
747 const char * bcc,
748 const char * subject,
737 int ulfius_send_smtp_email(const char * host,
738 const int port,
739 const int use_tls,
740 const int verify_certificate,
741 const char * user,
742 const char * password,
743 const char * from,
744 const char * to,
745 const char * cc,
746 const char * bcc,
747 const char * subject,
749748 const char * mail_body);
750749
751750 /**
767766 * @return U_OK on success
768767 */
769768
770 int ulfius_send_smtp_rich_email(const char * host,
771 const int port,
772 const int use_tls,
773 const int verify_certificate,
774 const char * user,
775 const char * password,
776 const char * from,
777 const char * to,
778 const char * cc,
779 const char * bcc,
769 int ulfius_send_smtp_rich_email(const char * host,
770 const int port,
771 const int use_tls,
772 const int verify_certificate,
773 const char * user,
774 const char * password,
775 const char * from,
776 const char * to,
777 const char * cc,
778 const char * bcc,
780779 const char * content_type,
781 const char * subject,
780 const char * subject,
782781 const char * mail_body);
783782 #endif
784783
801800 * @param expires the expiration date of the ccokie in ISO format (optional)
802801 * @param max_age the maximum age of the cookie in seconds (optional)
803802 * @param domain the domain of the cookie (optional)
804 * @param pat the path of the cookie (optional)
803 * @param path the path of the cookie (optional)
805804 * @param secure wether the cookie must be secure or not (optional)
806805 * @param http_only wether the cookie must be used only for http requests or not (optional)
807806 * @return U_OK on success
808807 */
809 int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age,
808 int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age,
810809 const char * domain, const char * path, const int secure, const int http_only);
811810
812811 /**
818817 * @param expires the expiration date of the ccokie in ISO format (optional)
819818 * @param max_age the maximum age of the cookie in seconds (optional)
820819 * @param domain the domain of the cookie (optional)
821 * @param pat the path of the cookie (optional)
820 * @param path the path of the cookie (optional)
822821 * @param secure wether the cookie must be secure or not (optional)
823822 * @param http_only wether the cookie must be used only for http requests or not (optional)
824823 * @param same_site parameter must have one of the following values:
827826 * - U_COOKIE_SAME_SITE_LAX - SameSite attribute set to 'Lax'
828827 * @return U_OK on success
829828 */
830 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,
829 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,
831830 const char * domain, const char * path, const int secure, const int http_only, const int same_site);
832831
833832 /**
929928 * @param stream_user_data a user-defined pointer that will be available in stream_callback and stream_callback_free
930929 * @return U_OK on success
931930 */
932 int ulfius_set_stream_response(struct _u_response * response,
931 int ulfius_set_stream_response(struct _u_response * response,
933932 const unsigned int status,
934933 ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max),
935934 void (* stream_callback_free) (void * stream_user_data),
11411140 * ulfius_set_json_body_response
11421141 * Add a json_t j_body to a response
11431142 * @param response the response to retrieve the JSON data
1143 * @param status the HTTP status for the response
11441144 * @param j_body a json_t to stringify in the body
11451145 * @return U_OK on success
11461146 */
11581158 */
11591159
11601160 /************************************************************************
1161 * _u_map declarations *
1161 * _u_map declarations *
11621162 * _u_map is a simple map structure that handles sets of key/value maps *
11631163 ************************************************************************/
11641164
14051405 */
14061406
14071407 /**
1408 * @defgroup websocket
1408 * @defgroup websocket Websockets
14091409 * Websocket management functions
14101410 * @{
14111411 */
14451445 #define U_WEBSOCKET_STATUS_CLOSE 1
14461446 #define U_WEBSOCKET_STATUS_ERROR 2
14471447
1448 #define U_WEBSOCKET_RSV1 0x40
1449 #define U_WEBSOCKET_RSV2 0x20
1450 #define U_WEBSOCKET_RSV3 0x10
1451
14481452 #define WEBSOCKET_RESPONSE_HTTP 0x0001
14491453 #define WEBSOCKET_RESPONSE_UPGRADE 0x0002
14501454 #define WEBSOCKET_RESPONSE_CONNECTION 0x0004
14521456 #define WEBSOCKET_RESPONSE_PROTCOL 0x0010
14531457 #define WEBSOCKET_RESPONSE_EXTENSION 0x0020
14541458
1459 #define WEBSOCKET_DEFLATE_CHUNK_SIZE 32768
1460 #define WEBSOCKET_DEFLATE_WINDOWS_BITS 15
1461
1462 /**
1463 * @struct _websocket_deflate_context websocket extension permessage-deflate context
1464 */
1465 struct _websocket_deflate_context {
1466 z_stream infstream;
1467 z_stream defstream;
1468 int deflate_mask;
1469 int inflate_mask;
1470 uint server_no_context_takeover;
1471 uint client_no_context_takeover;
1472 uint server_max_window_bits;
1473 uint client_max_window_bits;
1474 };
1475
1476 /**
1477 * @struct _websocket_extension Websocket extension structure
1478 * contains callback functions and context to handle websocket extensions
1479 */
1480 struct _websocket_extension {
1481 char * extension_server;
1482 char * extension_client;
1483 uint8_t rsv;
1484 int (* websocket_extension_message_out_perform)(const uint8_t opcode,
1485 const uint64_t data_len_in,
1486 const char * data_in,
1487 uint64_t * data_len_out,
1488 char ** data_out,
1489 const uint64_t fragment_len,
1490 void * user_data,
1491 void * context);
1492 void * websocket_extension_message_out_perform_user_data;
1493 int (* websocket_extension_message_in_perform)(const uint8_t opcode,
1494 const uint64_t data_len_in,
1495 const char * data_in,
1496 uint64_t * data_len_out,
1497 char ** data_out,
1498 const uint64_t fragment_len,
1499 void * user_data,
1500 void * context);
1501 void * websocket_extension_message_in_perform_user_data;
1502 int (* websocket_extension_server_match)(const char * extension_client,
1503 const char ** extension_client_list,
1504 char ** extension_server,
1505 void * user_data,
1506 void ** context);
1507 void * websocket_extension_server_match_user_data;
1508 int (* websocket_extension_client_match)(const char * extension_server,
1509 void * user_data,
1510 void ** context);
1511 void * websocket_extension_client_match_user_data;
1512 void (* websocket_extension_free_context)(void * user_data,
1513 void * context);
1514 void * websocket_extension_free_context_user_data;
1515 int enabled;
1516 void * context;
1517 };
1518
14551519 /**
14561520 * @struct _websocket_manager Websocket manager structure
14571521 * contains among other things the socket
14621526 struct _websocket_message_list * message_list_incoming; /* !< list of incoming messages */
14631527 struct _websocket_message_list * message_list_outcoming; /* !< list of outcoming messages */
14641528 int connected; /* !< flag to know if the websocket is connected or not */
1529 int ping_sent; /* !< flag to know if the websocket has sent a ping frame or not, before receiving a pong */
14651530 int close_flag; /* !< flag to set before closing a websocket */
14661531 MHD_socket mhd_sock; /* !< reference to libmicrohttpd's socket for websocket server */
14671532 int tcp_sock; /* !< tcp socket for websocket client */
14741539 pthread_mutex_t write_lock; /* !< mutex to write data in the socket */
14751540 pthread_mutex_t status_lock; /* !< mutex to broadcast new status */
14761541 pthread_cond_t status_cond; /* !< condition to broadcast new status */
1477 struct pollfd fds;
1542 struct pollfd fds_in;
1543 struct pollfd fds_out;
14781544 int type;
1545 int rsv_expected;
1546 struct _pointer_list * websocket_extension_list;
14791547 };
14801548
14811549 /**
14851553 */
14861554 struct _websocket_message {
14871555 time_t datestamp; /* !< date stamp of the message */
1556 uint8_t rsv; /* !< flags RSV1-3 of the message */
14881557 uint8_t opcode; /* !< opcode for the message (string or binary) */
14891558 uint8_t has_mask; /* !< does the message contain a mask? */
14901559 uint8_t mask[4]; /* !< mask used if any */
14911560 size_t data_len; /* !< length of the data */
14921561 char * data; /* !< message data */
1562 size_t fragment_len; /* !< length of the fragment, 0 if not fragmented */
1563 uint8_t fin; /* !< flag fin (end of fragmented message) */
14931564 };
14941565
14951566 /**
16151686 */
16161687 int ulfius_set_websocket_response(struct _u_response * response,
16171688 const char * websocket_protocol,
1618 const char * websocket_extensions,
1689 const char * websocket_extensions,
16191690 void (* websocket_manager_callback) (const struct _u_request * request,
16201691 struct _websocket_manager * websocket_manager,
16211692 void * websocket_manager_user_data),
16291700 struct _websocket_manager * websocket_manager,
16301701 void * websocket_onclose_user_data),
16311702 void * websocket_onclose_user_data);
1703
1704 /**
1705 * Adds a set of callback functions to perform a message transformation via an extension
1706 * @param response struct _u_response to send back the websocket initialization, mandatory
1707 * @param extension_server the expected extension value
1708 * @param rsv the expected rsv flag attached to this extension
1709 * @param websocket_extension_message_out_perform a callback function called before a message is sent to the server
1710 * @param websocket_extension_message_out_perform_user_data a user-defined pointer passed to websocket_extension_message_out_perform
1711 * @param websocket_extension_message_in_perform a callback function called after a message is received from the server
1712 * @param websocket_extension_message_in_perform_user_data a user-defined pointer passed to websocket_extension_message_in_perform
1713 * @param websocket_extension_server_match a callback function called on handshake response to match an extensions value with the given callback message perform extensions
1714 * if NULL, then extension_client and the extension sent by the server will be compared for an exact match to enable this extension
1715 * @param websocket_extension_server_match_user_data a user-defined pointer passed to websocket_extension_server_match
1716 * @param websocket_extension_free_context a callback function called during the websocket close to free the context created in websocket_extension_server_match
1717 * @param websocket_extension_free_context_user_data a user-defined pointer passed to websocket_extension_free_context
1718 * @return U_OK on success
1719 */
1720 int ulfius_add_websocket_extension_message_perform(struct _u_response * response,
1721 const char * extension_server,
1722 uint8_t rsv,
1723 int (* websocket_extension_message_out_perform)(const uint8_t opcode,
1724 const uint64_t data_len_in,
1725 const char * data_in,
1726 uint64_t * data_len_out,
1727 char ** data_out,
1728 const uint64_t fragment_len,
1729 void * user_data,
1730 void * context),
1731 void * websocket_extension_message_out_perform_user_data,
1732 int (* websocket_extension_message_in_perform)(const uint8_t opcode,
1733 const uint64_t data_len_in,
1734 const char * data_in,
1735 uint64_t * data_len_out,
1736 char ** data_out,
1737 const uint64_t fragment_len,
1738 void * user_data,
1739 void * context),
1740 void * websocket_extension_message_in_perform_user_data,
1741 int (* websocket_extension_server_match)(const char * extension_client,
1742 const char ** extension_client_list,
1743 char ** extension_server,
1744 void * user_data,
1745 void ** context),
1746 void * websocket_extension_server_match_user_data,
1747 void (* websocket_extension_free_context)(void * user_data,
1748 void * context),
1749 void * websocket_extension_free_context_user_data);
1750
1751 /**
1752 * websocket_extension_message_out_perform used in
1753 * ulfius_add_websocket_deflate_extension and ulfius_add_websocket_client_deflate_extension
1754 * will compress the message in data_in and set the result in *data_out
1755 * @param opcode the opcode of the message to send
1756 * @param data_len_in length of data_in
1757 * @param data_in payload data to transform
1758 * @param data_len_out length of *data_out to be set by the function
1759 * @param data_out result of the transformed data_in
1760 * @param fragment_len fragmentation length of the message
1761 * @param user_data user-defined data
1762 * @param context context of the extension
1763 * @return U_OK on success
1764 */
1765 int websocket_extension_message_out_deflate(const uint8_t opcode,
1766 const uint64_t data_len_in,
1767 const char * data_in,
1768 uint64_t * data_len_out,
1769 char ** data_out,
1770 const uint64_t fragment_len,
1771 void * user_data,
1772 void * context);
1773
1774 /**
1775 * websocket_extension_message_in_perform used in
1776 * ulfius_add_websocket_deflate_extension and ulfius_add_websocket_client_deflate_extension
1777 * @param opcode the opcode of the message to send
1778 * @param data_len_in length of data_in
1779 * @param data_in payload data to transform
1780 * @param data_len_out length of *data_out to be set by the function
1781 * @param data_out result of the transformed data_in
1782 * @param fragment_len fragmentation length of the message
1783 * @param user_data user-defined data
1784 * @param context context of the extension
1785 * @return U_OK on success
1786 */
1787 int websocket_extension_message_in_inflate(const uint8_t opcode,
1788 const uint64_t data_len_in,
1789 const char * data_in,
1790 uint64_t * data_len_out,
1791 char ** data_out,
1792 const uint64_t fragment_len,
1793 void * user_data,
1794 void * context);
1795
1796 /**
1797 * Free deflate extension context
1798 * @param user_data user-defined data
1799 * @param context context of the extension
1800 * @return U_OK on success
1801 */
1802 void websocket_extension_deflate_free_context(void * user_data, void * context);
1803
1804 /**
1805 * websocket_extension_server_match used in ulfius_add_websocket_deflate_extension
1806 * @param extension_client the extension value to check
1807 * @param extension_client_list the list of all extension_client to match
1808 * @param extension_server the extension value to return to the client
1809 * @param user_data user-defined data
1810 * @param context context to allocate
1811 * @return U_OK on success
1812 */
1813 int websocket_extension_server_match_deflate(const char * extension_client, const char ** extension_client_list, char ** extension_server, void * user_data, void ** context);
1814
1815 /**
1816 * Adds the required extension message perform to implement message compression according to
1817 * RFC 7692: Compression Extensions for WebSocket
1818 * https://tools.ietf.org/html/rfc7692
1819 * Due to limited implementation, will force response parameters to server_no_context_takeover; client_no_context_takeover
1820 * @param response struct _u_response to send back the websocket initialization, mandatory
1821 * @return U_OK on success
1822 */
1823 int ulfius_add_websocket_deflate_extension(struct _u_response * response);
16321824
16331825 /**
16341826 * Sets the websocket in closing mode
16931885 void * websocket_onclose_user_data,
16941886 struct _websocket_client_handler * websocket_client_handler,
16951887 struct _u_response * response);
1888
1889 /**
1890 * Adds a set of callback functions to perform a message transformation via an extension
1891 * @param websocket_client_handler the handler of the websocket
1892 * @param extension the expected extension value
1893 * @param websocket_extension_message_out_perform a callback function called before a message is sent to the client
1894 * @param websocket_extension_message_out_perform_user_data a user-defined pointer passed to websocket_extension_message_out_perform
1895 * @param websocket_extension_message_in_perform a callback function called after a message is received from the client
1896 * @param websocket_extension_message_in_perform_user_data a user-defined pointer passed to websocket_extension_message_in_perform
1897 * @param websocket_extension_client_match a callback function called on handshake response to match an extensions value with the given callback message perform extensions
1898 * if NULL, then extension and the extension sent by the client will be compared for an exact match to enable this extension
1899 * @param websocket_extension_client_match_user_data a user-defined pointer passed to websocket_extension_client_match
1900 * @param websocket_extension_free_context a callback function called during the websocket close to free the context created in websocket_extension_server_match
1901 * @param websocket_extension_free_context_user_data a user-defined pointer passed to websocket_extension_free_context
1902 * @return U_OK on success
1903 */
1904 int ulfius_add_websocket_client_extension_message_perform(struct _websocket_client_handler * websocket_client_handler,
1905 const char * extension,
1906 uint8_t rsv,
1907 int (* websocket_extension_message_out_perform)(const uint8_t opcode,
1908 const uint64_t data_len_in,
1909 const char * data_in,
1910 uint64_t * data_len_out,
1911 char ** data_out,
1912 const uint64_t fragment_len,
1913 void * user_data,
1914 void * context),
1915 void * websocket_extension_message_out_perform_user_data,
1916 int (* websocket_extension_message_in_perform)(const uint8_t opcode,
1917 const uint64_t data_len_in,
1918 const char * data_in,
1919 uint64_t * data_len_out,
1920 char ** data_out,
1921 const uint64_t fragment_len,
1922 void * user_data,
1923 void * context),
1924 void * websocket_extension_message_in_perform_user_data,
1925 int (* websocket_extension_client_match)(const char * extension_server,
1926 void * user_data,
1927 void ** context),
1928 void * websocket_extension_client_match_user_data,
1929 void (* websocket_extension_free_context)(void * user_data,
1930 void * context),
1931 void * websocket_extension_free_context_user_data);
1932
1933 /**
1934 * websocket_extension_client_match used in ulfius_add_websocket_deflate_extension
1935 * @param extension_server the extension value to check
1936 * @param user_data user-defined data
1937 * @param context context to allocate
1938 * @return U_OK on success
1939 */
1940 int websocket_extension_client_match_deflate(const char * extension_server, void * user_data, void ** context);
1941
1942 /**
1943 * Adds the required extension message perform to implement message compression according to
1944 * RFC 7692: Compression Extensions for WebSocket
1945 * https://tools.ietf.org/html/rfc7692
1946 * @param websocket_client_handler the handler of the websocket
1947 * @return U_OK on success
1948 */
1949 int ulfius_add_websocket_client_deflate_extension(struct _websocket_client_handler * websocket_client_handler);
1950
16961951 /**
16971952 * Send a close signal to the websocket
16981953 * @param websocket_client_handler the handler to the websocket connection
17672022 * @struct _websocket_handle handle for a websocket
17682023 */
17692024 struct _websocket_handle {
1770 char * websocket_protocol; /* !< protocol for the websocket */
1771 char * websocket_extensions; /* !< extensions for the websocket */
1772 void (* websocket_manager_callback) (const struct _u_request * request, /* !< callback function for working with the websocket */
1773 struct _websocket_manager * websocket_manager,
1774 void * websocket_manager_user_data);
1775 void * websocket_manager_user_data; /* !< user-defined data that will be handled to websocket_manager_callback */
1776 void (* websocket_incoming_message_callback) (const struct _u_request * request, /* !< callback function that will be called every time a message arrives from the client in the websocket */
1777 struct _websocket_manager * websocket_manager,
1778 const struct _websocket_message * message,
1779 void * websocket_incoming_user_data);
1780 void * websocket_incoming_user_data; /* !< user-defined data that will be handled to websocket_incoming_message_callback */
1781 void (* websocket_onclose_callback) (const struct _u_request * request, /* !< callback function that will be called if the websocket is open while the program calls ulfius_stop_framework */
1782 struct _websocket_manager * websocket_manager,
1783 void * websocket_onclose_user_data);
1784 void * websocket_onclose_user_data; /* !< user-defined data that will be handled to websocket_onclose_callback */
2025 char * websocket_protocol; /* !< protocol for the websocket */
2026 char * websocket_extensions; /* !< extensions for the websocket */
2027 void (* websocket_manager_callback) (const struct _u_request * request, /* !< callback function for working with the websocket */
2028 struct _websocket_manager * websocket_manager,
2029 void * websocket_manager_user_data);
2030 void * websocket_manager_user_data; /* !< user-defined data that will be handled to websocket_manager_callback */
2031 void (* websocket_incoming_message_callback) (const struct _u_request * request, /* !< callback function that will be called every time a message arrives from the client in the websocket */
2032 struct _websocket_manager * websocket_manager,
2033 const struct _websocket_message * message,
2034 void * websocket_incoming_user_data);
2035 void * websocket_incoming_user_data; /* !< user-defined data that will be handled to websocket_incoming_message_callback */
2036 void (* websocket_onclose_callback) (const struct _u_request * request, /* !< callback function that will be called if the websocket is open while the program calls ulfius_stop_framework */
2037 struct _websocket_manager * websocket_manager,
2038 void * websocket_onclose_user_data);
2039 void * websocket_onclose_user_data; /* !< user-defined data that will be handled to websocket_onclose_callback */
2040 int rsv_expected;
2041 struct _pointer_list * websocket_extension_list;
17852042 };
17862043
17872044 /**
17882045 * @struct _websocket_handler handler for the websockets list
17892046 */
17902047 struct _websocket_handler {
2048 pthread_mutex_t websocket_active_lock; /* !< mutex to change nb_websocket_active value */
17912049 size_t nb_websocket_active; /* !< number of active websocket */
17922050 struct _websocket ** websocket_active; /* !< array of active websocket */
17932051 pthread_mutex_t websocket_close_lock; /* !< mutex to broadcast close signal */
3030 DESTDIR=/usr/local
3131 CC=gcc
3232 CFLAGS+=-c -pedantic -std=gnu99 -fPIC -Wall -Werror -Wextra -D_REENTRANT -I$(ULFIUS_INCLUDE) $(ADDITIONALFLAGS) $(CPPFLAGS)
33 LIBS=-L$(DESTDIR)/lib -lc -lmicrohttpd -lorcania -lpthread $(LDFLAGS)
33 LIBS=-L$(DESTDIR)/lib -lc -lmicrohttpd -lorcania -lpthread -lz $(LDFLAGS)
3434 SONAME = -soname
3535 ifeq ($(shell uname -s),Darwin)
3636 SONAME = -install_name
3838 OBJECTS=ulfius.o u_map.o u_request.o u_response.o u_send_request.o u_websocket.o yuarel.o
3939 OUTPUT=libulfius.so
4040 VERSION_MAJOR=2
41 VERSION_MINOR=6
42 VERSION_PATCH=9
41 VERSION_MINOR=7
42 VERSION_PATCH=0
4343
4444 ifndef JANSSONFLAG
4545 DISABLE_JANSSON=0
101101 $(CONFIG_FILE):
102102 @cp $(CONFIG_TEMPLATE) $(CONFIG_FILE)
103103 @echo Config file $(CONFIG_FILE) generated
104 @sed -i -e 's/$${PROJECT_VERSION}/$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)/g' $(CONFIG_FILE)
105 @sed -i -e 's/$${PROJECT_VERSION_MAJOR}/$(VERSION_MAJOR)/g' $(CONFIG_FILE)
106 @sed -i -e 's/$${PROJECT_VERSION_MINOR}/$(VERSION_MINOR)/g' $(CONFIG_FILE)
107 @sed -i -e 's/$${PROJECT_VERSION_PATCH}/$(VERSION_PATCH)/g' $(CONFIG_FILE)
108 @sed -i -e 's/$${PROJECT_VERSION_NUMBER}/$(shell printf '%02d' $(VERSION_MAJOR))$(shell printf '%02d' $(VERSION_MINOR))$(shell printf '%02d' $(VERSION_PATCH))/g' $(CONFIG_FILE)
104 @sed -i -e 's/$${PROJECT_VERSION}/$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)/g' $(CONFIG_FILE)
105 @sed -i -e 's/$${PROJECT_VERSION_MAJOR}/$(VERSION_MAJOR)/g' $(CONFIG_FILE)
106 @sed -i -e 's/$${PROJECT_VERSION_MINOR}/$(VERSION_MINOR)/g' $(CONFIG_FILE)
107 @sed -i -e 's/$${PROJECT_VERSION_PATCH}/$(VERSION_PATCH)/g' $(CONFIG_FILE)
108 @sed -i -e 's/$${PROJECT_VERSION_NUMBER}/$(shell printf '%02d' $(VERSION_MAJOR))$(shell printf '%02d' $(VERSION_MINOR))$(shell printf '%02d' $(VERSION_PATCH))/g' $(CONFIG_FILE)
109109 @if [ "$(DISABLE_JANSSON)" = "1" ]; then \
110110 sed -i -e 's/\#cmakedefine U_DISABLE_JANSSON/\#define U_DISABLE_JANSSON/g' $(CONFIG_FILE); \
111111 echo "JANSSON SUPPORT DISABLED"; \
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * u_umap.c: Simple map structure functions definitions
77 * not memory friendly, all pointer returned must be freed after use
8 *
8 *
99 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
1010 *
1111 * This program is free software; you can redistribute it and/or
2020 *
2121 * You should have received a copy of the GNU General Public
2222 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 *
23 *
2424 */
2525
2626 #include <stdio.h>
2929 #include "u_private.h"
3030 #include "ulfius.h"
3131
32 /**
33 * initialize a struct _u_map
34 * this function MUST be called after a declaration or allocation
35 * return U_OK on success
36 */
3732 int u_map_init(struct _u_map * u_map) {
3833 if (u_map != NULL) {
3934 u_map->nb_values = 0;
5146 return U_ERROR_MEMORY;
5247 }
5348 u_map->values[0] = NULL;
54
49
5550 u_map->lengths = o_malloc(sizeof(size_t));
5651 if (u_map->lengths == NULL) {
5752 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths");
6762 }
6863 }
6964
70 /**
71 * free the struct _u_map's inner components
72 * return U_OK on success
73 */
7465 int u_map_clean(struct _u_map * u_map) {
7566 int i;
7667 if (u_map != NULL) {
8778 }
8879 }
8980
90 /**
91 * free the struct _u_map and its components
92 * return U_OK on success
93 */
9481 int u_map_clean_full(struct _u_map * u_map) {
9582 if (u_map_clean(u_map) == U_OK) {
9683 o_free(u_map);
10087 }
10188 }
10289
103 /**
104 * free an enum return by functions u_map_enum_keys or u_map_enum_values
105 * return U_OK on success
106 */
10790 int u_map_clean_enum(char ** array) {
10891 int i;
10992 if (array != NULL) {
118101 }
119102 }
120103
121 /**
122 * returns an array containing all the keys in the struct _u_map
123 * return an array of char * ending with a NULL element
124 */
125104 const char ** u_map_enum_keys(const struct _u_map * u_map) {
126105 return (const char **)u_map->keys;
127106 }
128107
129 /**
130 * returns an array containing all the values in the struct _u_map
131 * return an array of char * ending with a NULL element
132 */
133108 const char ** u_map_enum_values(const struct _u_map * u_map) {
134109 return (const char **)u_map->values;
135110 }
136111
137 /**
138 * return true if the sprcified u_map contains the specified key
139 * false otherwise
140 * search is case sensitive
141 */
142112 int u_map_has_key(const struct _u_map * u_map, const char * key) {
143113 int i;
144114 if (u_map != NULL && key != NULL) {
151121 return 0;
152122 }
153123
154 /**
155 * return true if the sprcified u_map contains the specified value
156 * false otherwise
157 * search is case sensitive
158 */
159124 int u_map_has_value(const struct _u_map * u_map, const char * value) {
160125 return u_map_has_value_binary(u_map, value, o_strlen(value));
161126 }
162127
163 /**
164 * return true if the sprcified u_map contains the specified value
165 * false otherwise
166 * search is case sensitive
167 */
168128 int u_map_has_value_binary(const struct _u_map * u_map, const char * value, size_t length) {
169129 int i;
170130 if (u_map != NULL && value != NULL) {
177137 return 0;
178138 }
179139
180 /**
181 * add the specified key/value pair into the specified u_map
182 * if the u_map already contains a pair with the same key, replace the value
183 * return U_OK on success
184 */
185140 int u_map_put(struct _u_map * u_map, const char * key, const char * value) {
186141 if (value != NULL) {
187142 return u_map_put_binary(u_map, key, value, 0, o_strlen(value)+1);
190145 }
191146 }
192147
193 /**
194 * add the specified key/binary value pair into the specified u_map
195 * if the u_map already contains a pair with the same key,
196 * replace the value at the specified offset with the specified length
197 * return U_OK on success
198 */
199148 int u_map_put_binary(struct _u_map * u_map, const char * key, const char * value, uint64_t offset, size_t length) {
200149 int i;
201150 char * dup_key, * dup_value;
241190 } else {
242191 dup_value = o_strdup("");
243192 }
244
193
245194 // Append key
246195 for (i = 0; u_map->keys[i] != NULL; i++);
247196 u_map->keys = o_realloc(u_map->keys, (i + 2)*sizeof(char *));
253202 }
254203 u_map->keys[i] = (char *)dup_key;
255204 u_map->keys[i+1] = NULL;
256
205
257206 // Append value
258207 u_map->values = o_realloc(u_map->values, (i + 2)*sizeof(char *));
259208 if (u_map->values == NULL) {
264213 }
265214 u_map->values[i] = (char *)dup_value;
266215 u_map->values[i+1] = NULL;
267
216
268217 // Append length
269218 u_map->lengths = o_realloc(u_map->lengths, (i + 2)*sizeof(size_t));
270219 if (u_map->lengths == NULL) {
275224 }
276225 u_map->lengths[i] = (offset + length);
277226 u_map->lengths[i+1] = 0;
278
227
279228 u_map->nb_values++;
280229 }
281230 return U_OK;
284233 }
285234 }
286235
287 /**
288 * remove an pair key/value that has the specified key
289 * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise
290 */
291236 int u_map_remove_from_key(struct _u_map * u_map, const char * key) {
292237 int i, res, found = 0;
293
238
294239 if (u_map == NULL || key == NULL) {
295240 return U_ERROR_PARAMS;
296241 } else {
311256 }
312257 }
313258
314 /**
315 * remove all pairs key/value that has the specified key (case insensitive search)
316 * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise
317 */
318259 int u_map_remove_from_key_case(struct _u_map * u_map, const char * key) {
319260 int i, res, found = 0;
320
261
321262 if (u_map == NULL || key == NULL) {
322263 return U_ERROR_PARAMS;
323264 } else {
338279 }
339280 }
340281
341 /**
342 * remove all pairs key/value that has the specified value
343 * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise
344 */
345282 int u_map_remove_from_value(struct _u_map * u_map, const char * value) {
346283 return u_map_remove_from_value_binary(u_map, value, o_strlen(value));
347284 }
348285
349 /**
350 * remove all pairs key/value that has the specified value up until the specified length
351 * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise
352 */
353286 int u_map_remove_from_value_binary(struct _u_map * u_map, const char * value, size_t length) {
354287 int i, res, found = 0;
355
288
356289 if (u_map == NULL || value == NULL) {
357290 return U_ERROR_PARAMS;
358291 } else {
373306 }
374307 }
375308
376 /**
377 * remove all pairs key/value that has the specified value (case insensitive search)
378 * return U_OK on success, U_NOT_FOUND if key was not found, error otherwise
379 */
380309 int u_map_remove_from_value_case(struct _u_map * u_map, const char * value) {
381310 int i, res, found = 0;
382
311
383312 if (u_map == NULL || value == NULL) {
384313 return U_ERROR_PARAMS;
385314 } else {
400329 }
401330 }
402331
403 /**
404 * remove the pair key/value at the specified index
405 * return U_OK on success, U_NOT_FOUND if index is out of bound, error otherwise
406 */
407332 int u_map_remove_at(struct _u_map * u_map, const int index) {
408333 int i;
409334 if (u_map == NULL || index < 0) {
433358 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths");
434359 return U_ERROR_MEMORY;
435360 }
436
361
437362 u_map->nb_values--;
438363 return U_OK;
439364 }
440365 }
441366
442 /**
443 * get the value corresponding to the specified key in the u_map
444 * return NULL if no match found
445 * search is case sensitive
446 */
447367 const char * u_map_get(const struct _u_map * u_map, const char * key) {
448368 int i;
449369 if (u_map != NULL && key != NULL) {
462382 }
463383 }
464384
465 /**
466 * return true if the sprcified u_map contains the specified key
467 * false otherwise
468 * search is case insensitive
469 */
470385 int u_map_has_key_case(const struct _u_map * u_map, const char * key) {
471386 int i;
472387 if (u_map != NULL && key != NULL) {
479394 return 0;
480395 }
481396
482 /**
483 * return true if the sprcified u_map contains the specified value
484 * false otherwise
485 * search is case insensitive
486 */
487397 int u_map_has_value_case(const struct _u_map * u_map, const char * value) {
488398 int i;
489399 if (u_map != NULL && value != NULL) {
496406 return 0;
497407 }
498408
499 /**
500 * get the value corresponding to the specified key in the u_map
501 * return NULL if no match found
502 * search is case insensitive
503 */
504409 const char * u_map_get_case(const struct _u_map * u_map, const char * key) {
505410 int i;
506411 if (u_map != NULL && key != NULL) {
515420 }
516421 }
517422
518 /**
519 * get the value length corresponding to the specified key in the u_map
520 * return -1 if no match found
521 * search is case sensitive
522 */
523423 ssize_t u_map_get_length(const struct _u_map * u_map, const char * key) {
524424 int i;
525425 if (u_map != NULL && key != NULL) {
534434 }
535435 }
536436
537 /**
538 * get the value length corresponding to the specified key in the u_map
539 * return -1 if no match found
540 * search is case insensitive
541 */
542437 ssize_t u_map_get_case_length(const struct _u_map * u_map, const char * key) {
543438 int i;
544439 if (u_map != NULL && key != NULL) {
553448 }
554449 }
555450
556 /**
557 * Create an exact copy of the specified struct _u_map
558 * return a reference to the copy, NULL otherwise
559 * returned value must be cleaned after use
560 */
561451 struct _u_map * u_map_copy(const struct _u_map * source) {
562452 struct _u_map * copy = NULL;
563453 const char ** keys, * value;
583473 return copy;
584474 }
585475
586 /**
587 * Copy all key/values pairs of source into dest
588 * If key is already present in dest, it's overwritten
589 * return U_OK on success, error otherwise
590 */
591476 int u_map_copy_into(struct _u_map * dest, const struct _u_map * source) {
592477 const char ** keys;
593478 int i, res;
594
479
595480 if (source != NULL && dest != NULL) {
596481 keys = u_map_enum_keys(source);
597482 for (i=0; keys != NULL && keys[i] != NULL; i++) {
606491 }
607492 }
608493
609 /**
610 * Return the number of key/values pair in the specified struct _u_map
611 * Return -1 on error
612 */
613494 int u_map_count(const struct _u_map * source) {
614495 if (source != NULL) {
615496 if (source->nb_values >= 0) {
619500 return -1;
620501 }
621502
622 /**
623 * Empty a struct u_map of all its elements
624 * return U_OK on success, error otherwise
625 */
626503 int u_map_empty(struct _u_map * u_map) {
627504 int ret = u_map_clean(u_map);
628505 if (ret == U_OK) {
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * u_request.c: request related functions defintions
7 *
7 *
88 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
99 *
1010 * This program is free software; you can redistribute it and/or
1919 *
2020 * You should have received a copy of the GNU General Public
2121 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 *
22 *
2323 */
2424 #include <stdlib.h>
2525 #include <stdarg.h>
3939 static char ** ulfius_split_url(const char * prefix, const char * url) {
4040 char * saveptr = NULL, * cur_word = NULL, ** to_return = o_malloc(sizeof(char*)), * url_cpy = NULL, * url_cpy_addr = NULL;
4141 int counter = 1;
42
42
4343 if (to_return != NULL) {
4444 to_return[0] = NULL;
4545 if (prefix != NULL) {
9898 */
9999 static int compare_endpoint_priorities(const void * a, const void * b) {
100100 struct _u_endpoint * e1 = *(struct _u_endpoint **)a, * e2 = *(struct _u_endpoint **)b;
101
101
102102 if (e1->priority < e2->priority) {
103103 return -1;
104104 } else if (e1->priority > e2->priority) {
115115 */
116116 static int ulfius_url_format_match(const char ** splitted_url, const char ** splitted_url_format) {
117117 int i;
118
118
119119 for (i=0; splitted_url_format[i] != NULL; i++) {
120120 if (splitted_url_format[i][0] == '*' && splitted_url_format[i+1] == NULL) {
121121 return 1;
149149 * pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
150150 pstr += 2;
151151 }
152 } else if (* pstr == '+') {
152 } else if (* pstr == '+') {
153153 * pbuf++ = ' ';
154154 } else {
155155 * pbuf++ = * pstr;
175175 struct _u_endpoint ** endpoint_returned = o_malloc(sizeof(struct _u_endpoint *));
176176 int i;
177177 size_t count = 0;
178
178
179179 if (endpoint_returned == NULL) {
180180 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for endpoint_returned");
181181 } else {
212212 splitted_url = NULL;
213213 }
214214 }
215 /*
215 /*
216216 * only sort if endpoint_returned is != NULL
217217 * can be NULL either after initial o_malloc() or after o_realloc()
218218 */
247247 cur_word_format = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr_prefix );
248248 }
249249 o_free(url_format_cpy_addr);
250
250
251251 url_format_cpy = url_format_cpy_addr = o_strdup(endpoint->url_format);
252252 if (endpoint->url_format != NULL && url_format_cpy == NULL) {
253253 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for url_format_cpy");
256256 cur_word_format = strtok_r( url_format_cpy, ULFIUS_URL_SEPARATOR, &saveptr_format );
257257 }
258258 while (cur_word_format != NULL && cur_word != NULL) {
259 if ((cur_word_format[0] == ':' || cur_word_format[0] == '@') && (!check_utf8 || utf8_check(cur_word) == NULL)) {
259 if ((cur_word_format[0] == ':' || cur_word_format[0] == '@') && (!check_utf8 || utf8_check(cur_word, o_strlen(cur_word)) == NULL)) {
260260 if (u_map_has_key(map, cur_word_format+1)) {
261261 concat_url_param = msprintf("%s,%s", u_map_get(map, cur_word_format+1), cur_word);
262262 if (concat_url_param == NULL) {
306306 request->map_header = o_malloc(sizeof(struct _u_map));
307307 request->map_cookie = o_malloc(sizeof(struct _u_map));
308308 request->map_post_body = o_malloc(sizeof(struct _u_map));
309 if (request->map_post_body == NULL || request->map_cookie == NULL ||
309 if (request->map_post_body == NULL || request->map_cookie == NULL ||
310310 request->map_url == NULL || request->map_header == NULL) {
311311 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for request->map*");
312312 ulfius_clean_request(request);
432432 dest->auth_basic_user = o_strdup(source->auth_basic_user);
433433 dest->auth_basic_password = o_strdup(source->auth_basic_password);
434434 dest->callback_position = source->callback_position;
435
435
436436 if (source->client_address != NULL) {
437437 dest->client_address = o_malloc(sizeof(struct sockaddr));
438438 if (dest->client_address != NULL) {
442442 ret = U_ERROR_MEMORY;
443443 }
444444 }
445
445
446446 if (ret == U_OK && u_map_clean(dest->map_url) == U_OK && u_map_init(dest->map_url) == U_OK) {
447447 if (u_map_copy_into(dest->map_url, source->map_url) != U_OK) {
448448 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_url");
452452 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_url");
453453 ret = U_ERROR_MEMORY;
454454 }
455
455
456456 if (ret == U_OK && u_map_clean(dest->map_header) == U_OK && u_map_init(dest->map_header) == U_OK) {
457457 if (u_map_copy_into(dest->map_header, source->map_header) != U_OK) {
458458 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_header");
462462 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_header");
463463 ret = U_ERROR_MEMORY;
464464 }
465
465
466466 if (ret == U_OK && u_map_clean(dest->map_cookie) == U_OK && u_map_init(dest->map_cookie) == U_OK) {
467467 if (u_map_copy_into(dest->map_cookie, source->map_cookie) != U_OK) {
468468 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_cookie");
472472 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_cookie");
473473 ret = U_ERROR_MEMORY;
474474 }
475
475
476476 if (ret == U_OK && u_map_clean(dest->map_post_body) == U_OK && u_map_init(dest->map_post_body) == U_OK) {
477477 if (u_map_copy_into(dest->map_post_body, source->map_post_body) != U_OK) {
478478 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_post_body");
482482 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_post_body");
483483 ret = U_ERROR_MEMORY;
484484 }
485
485
486486 if (ret == U_OK) {
487487 if (source->binary_body_length) {
488488 dest->binary_body_length = source->binary_body_length;
834834 * Get JSON structure from the request body if the request is valid
835835 * request: struct _u_request used
836836 * json_error: structure to store json_error_t if specified
837 */
837 */
838838 json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error) {
839839 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)) {
840840 return json_loadb(request->binary_body, request->binary_body_length, JSON_DECODE_ANY, json_error);
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * u_response.c: response related functions defintions
7 *
7 *
88 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
99 *
1010 * This program is free software; you can redistribute it and/or
1919 *
2020 * You should have received a copy of the GNU General Public
2121 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 *
22 *
2323 */
2424 #include <string.h>
2525
2626 #include "u_private.h"
2727 #include "ulfius.h"
2828
29 /**
30 * Add a cookie in the cookie map as defined in the RFC 6265
31 * Returned value must be free'd after use
32 */
3329 static char * ulfius_generate_cookie_header(const struct _u_cookie * cookie) {
3430 char * attr_expires = NULL, * attr_max_age = NULL, * attr_domain = NULL, * attr_path = NULL;
3531 char * attr_secure = NULL, * attr_http_only = NULL, * cookie_header_value = NULL, * same_site = NULL;
127123 } else {
128124 same_site = o_strdup("");
129125 }
130
126
131127 if (attr_expires == NULL || attr_max_age == NULL || attr_domain == NULL || attr_path == NULL || attr_secure == NULL || attr_http_only == NULL) {
132128 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_generate_cookie_header");
133129 } else {
156152 }
157153 }
158154
159 /**
160 * ulfius_set_response_header
161 * adds headers defined in the response_map_header to the response
162 * return the number of added headers, -1 on error
163 */
164155 int ulfius_set_response_header(struct MHD_Response * response, const struct _u_map * response_map_header) {
165156 const char ** header_keys = u_map_enum_keys(response_map_header);
166157 const char * header_value;
180171 return i;
181172 }
182173
183 /**
184 * ulfius_set_response_cookie
185 * adds cookies defined in the response_map_cookie
186 * return the number of added headers, -1 on error
187 */
188174 int ulfius_set_response_cookie(struct MHD_Response * mhd_response, const struct _u_response * response) {
189175 int ret;
190176 unsigned int i;
210196 }
211197 }
212198
213 /**
214 * ulfius_add_cookie_to_response
215 * add a cookie to the cookie map
216 * return U_OK on success
217 */
218 int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age,
199 int ulfius_add_cookie_to_response(struct _u_response * response, const char * key, const char * value, const char * expires, const unsigned int max_age,
219200 const char * domain, const char * path, const int secure, const int http_only) {
220201 return ulfius_add_same_site_cookie_to_response(response, key, value, expires, max_age, domain, path, secure, http_only, U_COOKIE_SAME_SITE_NONE);
221202 }
222203
223 /**
224 * ulfius_add_same_site_cookie_to_response
225 * add a cookie to the cookie map with a SameSite attribute
226 * the same_site parameter must have one of the following values:
227 * - U_COOKIE_SAME_SITE_NONE - No SameSite attribute
228 * - U_COOKIE_SAME_SITE_STRICT - SameSite attribute set to 'Strict'
229 * - U_COOKIE_SAME_SITE_LAX - SameSite attribute set to 'Lax'
230 * return U_OK on success
231 */
232 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,
204 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,
233205 const char * domain, const char * path, const int secure, const int http_only, const int same_site) {
234206 unsigned int i;
235207 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)) {
264236 }
265237 }
266238 }
267
239
268240 // Key not found, inserting a new cookie
269241 if (response->nb_cookies == 0) {
270242 response->map_cookie = o_malloc(sizeof(struct _u_cookie));
288260 response->map_cookie[response->nb_cookies].secure = secure;
289261 response->map_cookie[response->nb_cookies].http_only = http_only;
290262 response->map_cookie[response->nb_cookies].same_site = same_site;
291 if ((key != NULL && response->map_cookie[response->nb_cookies].key == NULL) || (value != NULL && response->map_cookie[response->nb_cookies].value == NULL) ||
263 if ((key != NULL && response->map_cookie[response->nb_cookies].key == NULL) || (value != NULL && response->map_cookie[response->nb_cookies].value == NULL) ||
292264 (expires != NULL && response->map_cookie[response->nb_cookies].expires == NULL) || (domain != NULL && response->map_cookie[response->nb_cookies].domain == NULL) ||
293265 (path != NULL && response->map_cookie[response->nb_cookies].path == NULL)) {
294266 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_add_cookie_to_response");
307279 }
308280 }
309281
310 /**
311 * ulfius_clean_cookie
312 * clean the cookie's elements
313 * return U_OK on success
314 */
315282 int ulfius_clean_cookie(struct _u_cookie * cookie) {
316283 if (cookie != NULL) {
317284 o_free(cookie->key);
330297 }
331298 }
332299
333 /**
334 * Copy the cookie source elements into dest elements
335 * return U_OK on success
336 */
337300 int ulfius_copy_cookie(struct _u_cookie * dest, const struct _u_cookie * source) {
338301 if (source != NULL && dest != NULL) {
339302 dest->key = o_strdup(source->key);
359322 return U_ERROR_PARAMS;
360323 }
361324
362 /**
363 * ulfius_clean_response
364 * clean the specified response's inner elements
365 * user must free the parent pointer if needed after clean
366 * or use ulfius_clean_response_full
367 * return U_OK on success
368 */
369325 int ulfius_clean_response(struct _u_response * response) {
370326 unsigned int i;
371327 if (response != NULL) {
387343 if ((struct _websocket_handle *)response->websocket_handle) {
388344 o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol);
389345 o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_extensions);
346 pointer_list_clean_free(((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list, &ulfius_free_websocket_extension_pointer_list);
347 o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list);
390348 o_free(response->websocket_handle);
391349 }
392350 #endif
396354 }
397355 }
398356
399 /**
400 * ulfius_clean_response_full
401 * clean the specified response and all its elements
402 * return U_OK on success
403 */
404357 int ulfius_clean_response_full(struct _u_response * response) {
405358 if (ulfius_clean_response(response) == U_OK) {
406359 o_free(response);
410363 }
411364 }
412365
413 /**
414 * ulfius_init_response
415 * Initialize a response structure by allocating inner elements
416 * return U_OK on success
417 */
418366 int ulfius_init_response(struct _u_response * response) {
419367 if (response != NULL) {
420368 response->status = 200;
453401 ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = NULL;
454402 ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = NULL;
455403 ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data = NULL;
404 ((struct _websocket_handle *)response->websocket_handle)->rsv_expected = 0;
405 if ((((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list = o_malloc(sizeof(struct _pointer_list))) == NULL) {
406 o_free(response->websocket_handle);
407 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->websocket_handle->websocket_extension_list");
408 return U_ERROR_MEMORY;
409 }
410 pointer_list_init(((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list);
456411 #endif
457412 return U_OK;
458413 } else {
460415 }
461416 }
462417
463 /**
464 * ulfius_copy_response
465 * Copy the source response elements into the des response
466 * return U_OK on success
467 */
468418 int ulfius_copy_response(struct _u_response * dest, const struct _u_response * source) {
469419 unsigned int i;
470420 if (dest != NULL && source != NULL) {
493443 } else {
494444 dest->map_cookie = NULL;
495445 }
496
446
497447 if (source->binary_body != NULL && source->binary_body_length > 0) {
498448 dest->binary_body = o_malloc(source->binary_body_length);
499449 if (dest->binary_body == NULL) {
503453 dest->binary_body_length = source->binary_body_length;
504454 memcpy(dest->binary_body, source->binary_body, source->binary_body_length);
505455 }
506
456
507457 if (source->stream_callback != NULL) {
508458 dest->stream_callback = source->stream_callback;
509459 dest->stream_callback_free = source->stream_callback_free;
511461 dest->stream_block_size = source->stream_block_size;
512462 dest->stream_user_data = source->stream_user_data;
513463 }
514
464
515465 dest->shared_data = source->shared_data;
516466 dest->timeout = source->timeout;
517467 #ifndef U_DISABLE_WEBSOCKET
532482 }
533483 }
534484
535 /**
536 * create a new response based on the source elements
537 * return value must be free'd after use
538 */
539485 struct _u_response * ulfius_duplicate_response(const struct _u_response * response) {
540486 struct _u_response * new_response = NULL;
541487 if (response != NULL) {
559505 return new_response;
560506 }
561507
562 /**
563 * ulfius_set_string_body_response
564 * Set a string string_body to a response
565 * string_body must end with a '\0' character
566 * return U_OK on success
567 */
568508 int ulfius_set_string_body_response(struct _u_response * response, const unsigned int status, const char * string_body) {
569509 if (response != NULL && string_body != NULL) {
570510 // Free all the bodies available
583523 }
584524 }
585525
586 /**
587 * ulfius_set_binary_body_response
588 * Add a binary binary_body to a response
589 * return U_OK on success
590 */
591526 int ulfius_set_binary_body_response(struct _u_response * response, const unsigned int status, const char * binary_body, const size_t length) {
592527 if (response != NULL && binary_body != NULL && length > 0) {
593528 // Free all the bodies available
609544 }
610545 }
611546
612 /**
613 * ulfius_set_empty_body_response
614 * Set an empty response with only a status
615 * return U_OK on success
616 */
617547 int ulfius_set_empty_body_response(struct _u_response * response, const unsigned int status) {
618548 if (response != NULL) {
619549 // Free all the bodies available
620550 o_free(response->binary_body);
621551 response->binary_body = NULL;
622552 response->binary_body_length = 0;
623
553
624554 response->status = status;
625555 return U_OK;
626556 } else {
628558 }
629559 }
630560
631 /**
632 * ulfius_set_stream_response
633 * Set an stream response with a status
634 * set stream_size to -1 if unknown
635 * set stream_block_size to a proper value based on the system
636 * return U_OK on success
637 */
638 int ulfius_set_stream_response(struct _u_response * response,
561 int ulfius_set_stream_response(struct _u_response * response,
639562 const unsigned int status,
640563 ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max),
641564 void (* stream_callback_free) (void * stream_user_data),
647570 o_free(response->binary_body);
648571 response->binary_body = NULL;
649572 response->binary_body_length = 0;
650
573
651574 response->status = status;
652575 response->stream_callback = stream_callback;
653576 response->stream_callback_free = stream_callback_free;
660583 }
661584 }
662585
663 /**
664 * ulfius_set_response_properties
665 * Set a list of properties to a response
666 * return U_OK on success
667 */
668586 int ulfius_set_response_properties(struct _u_response * response, ...) {
669587 u_option option;
670588 int ret = U_OK;
775693 }
776694 #endif
777695
778 /**
779 * ulfius_add_header_to_response
780 * add a header to the response
781 * return U_OK on success
782 */
783696 int ulfius_add_header_to_response(struct _u_response * response, const char * key, const char * value) {
784697 if (response != NULL && key != NULL && value != NULL) {
785698 return u_map_put(response->map_header, key, value);
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * u_send_request.c: send request related functions defintions
7 *
7 *
88 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
99 *
1010 * This program is free software; you can redistribute it and/or
4040 #include <curl/curl.h>
4141 #include <string.h>
4242
43 #define U_ACCEPT_HEADER "Accept-Encoding"
44
4345 #ifdef _MSC_VER
4446 #define strtok_r strtok_s
4547
7981 static size_t ulfius_write_body(void * contents, size_t size, size_t nmemb, void * user_data) {
8082 size_t realsize = size * nmemb;
8183 struct _u_body * body_data = (struct _u_body *) user_data;
82
84
8385 body_data->data = o_realloc(body_data->data, body_data->size + realsize + 1);
8486 if(body_data->data == NULL) {
8587 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for body_data->data");
8688 return 0;
8789 }
88
90
8991 memcpy(&(body_data->data[body_data->size]), contents, realsize);
9092 body_data->size += realsize;
9193 body_data->data[body_data->size] = 0;
92
94
9395 return realsize;
9496 }
9597
99101 * return the size_t of the header written
100102 */
101103 static size_t write_header(void * buffer, size_t size, size_t nitems, void * user_data) {
102
104
103105 struct _u_response * response = (struct _u_response *) user_data;
104106 char * header = (char *)buffer, * key, * value, * saveptr, * tmp;
105
107
106108 if (o_strchr(header, ':') != NULL) {
107109 if (response->map_header != NULL) {
108110 // Expecting a header (key: value)
109111 key = trimwhitespace(strtok_r(header, ":", &saveptr));
110112 value = trimwhitespace(strtok_r(NULL, "", &saveptr));
111
113
112114 if (!u_map_has_key_case(response->map_header, key)) {
113115 u_map_put(response->map_header, key, value);
114116 } else {
130132 return 0;
131133 }
132134 }
133
135
134136 return nitems * size;
135137 }
136138
137139 static size_t smtp_payload_source(void * ptr, size_t size, size_t nmemb, void * userp) {
138140 struct _u_smtp_payload *upload_ctx = (struct _u_smtp_payload *)userp;
139141 size_t len;
140
142
141143 if ((size * nmemb) < (upload_ctx->len - upload_ctx->offset)) {
142144 len = size*nmemb;
143145 } else {
158160 body_data.size = 0;
159161 body_data.data = NULL;
160162 int res;
161
163
162164 res = ulfius_send_http_streaming_request(request, response, ulfius_write_body, (void *)&body_data);
163165 if (res == U_OK && response != NULL) {
164166 if (body_data.data != NULL && body_data.size > 0) {
186188 * return U_OK on success
187189 */
188190 int ulfius_send_http_streaming_request(const struct _u_request * request,
189 struct _u_response * response,
190 size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data),
191 struct _u_response * response,
192 size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data),
191193 void * write_body_data) {
192194 CURLcode res;
193195 CURL * curl_handle = NULL;
200202 if (request != NULL) {
201203 // Duplicate the request and work on it
202204 if ((copy_request = ulfius_duplicate_request(request)) != NULL) {
203
205
204206 if ((curl_handle = curl_easy_init()) != NULL) {
205207 ret = U_OK;
206208
207209 // Here comes the fake loop with breaks to exit smoothly
208210 do {
211 // Set proxy if defined
212 if (u_map_has_key_case(copy_request->map_header, U_ACCEPT_HEADER)) {
213 if (curl_easy_setopt(curl_handle, CURLOPT_ACCEPT_ENCODING, u_map_get_case(copy_request->map_header, U_ACCEPT_HEADER)) != CURLE_OK) {
214 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting accept encoding option");
215 ret = U_ERROR_LIBCURL;
216 break;
217 }
218 }
219
209220 // Set basic auth if defined
210221 if (copy_request->auth_basic_user != NULL && copy_request->auth_basic_password != NULL) {
211222 if (curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC) == CURLE_OK) {
246257 ret = U_ERROR_LIBCURL;
247258 break;
248259 }
249
260
250261 // Set proxy if defined
251262 if (copy_request->proxy != NULL) {
252263 if (curl_easy_setopt(curl_handle, CURLOPT_PROXY, copy_request->proxy) != CURLE_OK) {
688699 * mail_body: email body (mandatory)
689700 * return U_OK on success
690701 */
691 int ulfius_send_smtp_rich_email(const char * host,
692 const int port,
693 const int use_tls,
694 const int verify_certificate,
695 const char * user,
696 const char * password,
697 const char * from,
698 const char * to,
699 const char * cc,
700 const char * bcc,
702 int ulfius_send_smtp_rich_email(const char * host,
703 const int port,
704 const int use_tls,
705 const int verify_certificate,
706 const char * user,
707 const char * password,
708 const char * from,
709 const char * to,
710 const char * cc,
711 const char * bcc,
701712 const char * content_type,
702 const char * subject,
713 const char * subject,
703714 const char * mail_body) {
704715 CURL * curl_handle;
705716 CURLcode res = CURLE_OK;
891902 * mail_body: email body (mandatory)
892903 * return U_OK on success
893904 */
894 int ulfius_send_smtp_email(const char * host,
895 const int port,
896 const int use_tls,
897 const int verify_certificate,
898 const char * user,
899 const char * password,
900 const char * from,
901 const char * to,
902 const char * cc,
903 const char * bcc,
904 const char * subject,
905 int ulfius_send_smtp_email(const char * host,
906 const int port,
907 const int use_tls,
908 const int verify_certificate,
909 const char * user,
910 const char * password,
911 const char * from,
912 const char * to,
913 const char * cc,
914 const char * bcc,
915 const char * subject,
905916 const char * mail_body) {
906917 return ulfius_send_smtp_rich_email(host, port, use_tls, verify_certificate, user, password, from, to, cc, bcc, "text/plain; charset=utf-8", subject, mail_body);
907918 }
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * u_websocket.c: websocket implementation
7 *
7 *
88 * Copyright 2017-2020 Nicolas Mora <mail@babelouest.org>
99 *
1010 * This program is free software; you can redistribute it and/or
1919 *
2020 * You should have received a copy of the GNU General Public
2121 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 *
22 *
2323 */
2424
2525 #include "u_private.h"
4343 #include <netdb.h>
4444 #include <stdlib.h>
4545 #include <gnutls/crypto.h>
46 #include <zlib.h>
47 #include <errno.h>
4648
4749 #define STR_HELPER(x) #x
4850 #define STR(x) STR_HELPER(x)
51
52 #define U_WEBSOCKET_DEFAULT_MEMORY_LEVEL 4
53 #define U_WEBSOCKET_DEFAULT_WINDOWS_BITS 15
4954
5055 /**********************************/
5156 /** Internal websocket functions **/
5257 /**********************************/
5358
59 void ulfius_free_websocket_extension_pointer_list(void * extension) {
60 ulfius_free_websocket_extension((struct _websocket_extension *)extension);
61 }
62
63 void ulfius_free_websocket_extension(struct _websocket_extension * websocket_extension) {
64 if (websocket_extension != NULL) {
65 o_free(websocket_extension->extension_server);
66 o_free(websocket_extension->extension_client);
67 o_free(websocket_extension);
68 }
69 }
70
71 int ulfius_init_websocket_extension(struct _websocket_extension * websocket_extension) {
72 if (websocket_extension != NULL) {
73 websocket_extension->extension_server = NULL;
74 websocket_extension->extension_client = NULL;
75 websocket_extension->websocket_extension_message_out_perform = NULL;
76 websocket_extension->websocket_extension_message_out_perform_user_data = NULL;
77 websocket_extension->websocket_extension_message_in_perform = NULL;
78 websocket_extension->websocket_extension_message_in_perform_user_data = NULL;
79 websocket_extension->websocket_extension_server_match = NULL;
80 websocket_extension->websocket_extension_server_match_user_data = NULL;
81 websocket_extension->websocket_extension_client_match = NULL;
82 websocket_extension->websocket_extension_client_match_user_data = NULL;
83 websocket_extension->enabled = 0;
84 return U_OK;
85 } else {
86 return U_ERROR_PARAMS;
87 }
88 }
89
5490 static int is_websocket_data_available(struct _websocket_manager * websocket_manager) {
5591 int ret = 0, poll_ret = 0;
56
92
5793 if (websocket_manager->tls) {
5894 ret = gnutls_record_check_pending(websocket_manager->gnutls_session);
5995 if (ret)
6096 return ret;
6197 }
62 poll_ret = poll(&websocket_manager->fds, 1, U_WEBSOCKET_USEC_WAIT);
98 poll_ret = poll(&websocket_manager->fds_in, 1, U_WEBSOCKET_USEC_WAIT);
6399 if (poll_ret == -1) {
64100 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error poll websocket read");
65101 websocket_manager->connected = 0;
66 } else if (websocket_manager->fds.revents & (POLLRDHUP|POLLERR|POLLHUP|POLLNVAL)) {
102 } else if (websocket_manager->fds_in.revents & (POLLRDHUP|POLLERR|POLLHUP|POLLNVAL)) {
67103 websocket_manager->connected = 0;
68104 } else if (poll_ret > 0) {
69105 ret = 1;
71107 return ret;
72108 }
73109
110 static int is_websocket_write_available(struct _websocket_manager * websocket_manager) {
111 int ret = 0, poll_ret = 0;
112
113 if (websocket_manager->tls) {
114 ret = gnutls_record_check_pending(websocket_manager->gnutls_session);
115 if (ret)
116 return ret;
117 }
118 poll_ret = poll(&websocket_manager->fds_out, 1, U_WEBSOCKET_USEC_WAIT);
119 if (poll_ret == -1) {
120 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error poll websocket write");
121 websocket_manager->connected = 0;
122 } else if (websocket_manager->fds_out.revents & (POLLRDHUP|POLLERR|POLLHUP|POLLNVAL)) {
123 websocket_manager->connected = 0;
124 } else if (poll_ret > 0) {
125 ret = 1;
126 }
127 return ret;
128 }
129
74130 static ssize_t read_data_from_socket(struct _websocket_manager * websocket_manager, uint8_t * data, size_t len) {
75131 ssize_t ret = 0, data_len;
76
132
77133 if (len > 0) {
78134 do {
79135 if (is_websocket_data_available(websocket_manager)) {
91147 break;
92148 }
93149 }
94 } while (ret < (ssize_t)len);
150 } while (websocket_manager->connected && ret < (ssize_t)len);
95151 }
96152 return ret;
97153 }
104160 if (data != NULL && len > 0) {
105161 for (off = 0; (size_t)off < len; off += ret) {
106162 if (websocket_manager->type == U_WEBSOCKET_SERVER) {
107 ret = send(websocket_manager->mhd_sock, &data[off], len - off, MSG_NOSIGNAL);
108 if (ret < 0) {
163 if (is_websocket_write_available(websocket_manager)) {
164 ret = send(websocket_manager->mhd_sock, &data[off], len - off, MSG_NOSIGNAL);
165 if (ret < 0) {
166 break;
167 }
168 } else {
109169 break;
110170 }
111171 } else {
155215 *frame = o_malloc(*frame_len);
156216 if (*frame != NULL) {
157217 if (has_fin) {
158 (*frame)[0] = (message->opcode | U_WEBSOCKET_BIT_FIN);
218 (*frame)[0] = (message->opcode | message->rsv | U_WEBSOCKET_BIT_FIN);
159219 } else {
160 (*frame)[0] = 0;
220 (*frame)[0] = message->rsv;
161221 }
162222 if (message->data_len >= 65536) {
163223 (*frame)[1] = 127;
207267 * returned value must be free'd after use
208268 */
209269 static struct _websocket_message * ulfius_build_message (const uint8_t opcode,
270 const uint8_t rsv,
210271 const short int has_mask,
211272 const char * data,
212273 const uint64_t data_len) {
213274 struct _websocket_message * new_message = NULL;
214 if ((
275 if (
215276 opcode == U_WEBSOCKET_OPCODE_TEXT ||
216277 opcode == U_WEBSOCKET_OPCODE_BINARY ||
217278 opcode == U_WEBSOCKET_OPCODE_CLOSE ||
218279 opcode == U_WEBSOCKET_OPCODE_PING ||
219280 opcode == U_WEBSOCKET_OPCODE_PONG
220 ) &&
221 (data_len == 0 || data != NULL)) {
281 ) {
222282 new_message = o_malloc(sizeof(struct _websocket_message));
223283 if (new_message != NULL) {
224284 if (data_len) {
228288 }
229289 if (!data_len || new_message->data != NULL) {
230290 new_message->opcode = opcode;
291 new_message->rsv = rsv;
231292 new_message->data_len = data_len;
232293 if (!has_mask) {
233294 new_message->has_mask = 0;
248309 } else {
249310 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for new_message");
250311 }
312 } else {
313 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Send invalid opcode error");
251314 }
252315 return new_message;
253316 }
260323 */
261324 static int ulfius_send_websocket_message_managed(struct _websocket_manager * websocket_manager,
262325 const uint8_t opcode,
326 const uint8_t rsv,
263327 const uint64_t data_len,
264328 const char * data,
265329 const uint64_t fragment_len) {
268332 uint8_t * frame = NULL;
269333 size_t frame_len = 0;
270334 int ret = U_OK;
271
272 if (data != NULL || data_len == 0) {
273 if (pthread_mutex_lock(&websocket_manager->write_lock)) {
274 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking write lock");
275 } else {
276 message = ulfius_build_message(opcode, (websocket_manager->type == U_WEBSOCKET_CLIENT), data, data_len);
277 if (message != NULL) {
278 if (ulfius_push_websocket_message(websocket_manager->message_list_outcoming, message) != U_OK) {
279 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pushing new websocket message in list");
280 }
335
336 if (pthread_mutex_lock(&websocket_manager->write_lock)) {
337 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking write lock");
338 } else {
339 message = ulfius_build_message(opcode, rsv, (websocket_manager->type == U_WEBSOCKET_CLIENT), data, data_len);
340 if (message != NULL) {
341 if (ulfius_push_websocket_message(websocket_manager->message_list_outcoming, message) != U_OK) {
342 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pushing new websocket message in list");
343 }
344 if (data_len) {
281345 while (offset < data_len) {
282 cur_len = fragment_len<(data_len - offset)?fragment_len:(data_len - offset);
346 if (!fragment_len) {
347 cur_len = data_len<(data_len - offset)?data_len:(data_len - offset);
348 } else {
349 cur_len = fragment_len<(data_len - offset)?fragment_len:(data_len - offset);
350 }
283351 if ((ret = ulfius_build_frame(message, offset, cur_len, &frame, &frame_len)) != U_OK) {
284352 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_build_frame");
285353 break;
290358 frame = NULL;
291359 frame_len = 0;
292360 }
361 message->opcode = U_WEBSOCKET_OPCODE_CONTINUE;
293362 }
294363 } else {
295 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_build_message");
296 ret = U_ERROR;
297 }
298 pthread_mutex_unlock(&websocket_manager->write_lock);
299 }
300 } else {
301 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_send_websocket_message_managed params");
364 if (ulfius_build_frame(message, 0, 0, &frame, &frame_len) == U_OK) {
365 ulfius_websocket_send_frame(websocket_manager, frame, frame_len);
366 }
367 o_free(frame);
368 }
369 } else {
370 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_build_message");
371 ret = U_ERROR;
372 }
373 pthread_mutex_unlock(&websocket_manager->write_lock);
374 }
375 return ret;
376 }
377
378 static int ulfius_merge_fragmented_message(struct _websocket_message * message_orig, struct _websocket_message * message_next_fragment) {
379 int ret;
380 if (message_orig != NULL && message_next_fragment != NULL && !message_orig->fin) {
381 if ((message_orig->opcode == U_WEBSOCKET_OPCODE_TEXT || message_orig->opcode == U_WEBSOCKET_OPCODE_BINARY) && message_next_fragment->opcode == U_WEBSOCKET_OPCODE_CONTINUE) {
382 if (!message_orig->fragment_len) {
383 message_orig->fragment_len = message_orig->data_len;
384 }
385 if (message_next_fragment->fin) {
386 message_orig->fin = message_next_fragment->fin;
387 }
388 if (message_next_fragment->data_len) {
389 if ((message_orig->data = o_realloc(message_orig->data, message_orig->data_len+message_next_fragment->data_len)) != NULL) {
390 memcpy(message_orig->data+message_orig->data_len, message_next_fragment->data, message_next_fragment->data_len);
391 message_orig->data_len += message_next_fragment->data_len;
392 ret = U_OK;
393 } else {
394 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reallocating resources for data");
395 ret = U_ERROR_PARAMS;
396 }
397 } else {
398 ret = U_OK;
399 }
400 } else {
401 ret = U_ERROR_PARAMS;
402 }
403 } else {
302404 ret = U_ERROR_PARAMS;
303405 }
304406 return ret;
310412 * Sets the new message in the message variable
311413 */
312414 static int ulfius_read_incoming_message(struct _websocket_manager * websocket_manager, struct _websocket_message ** message) {
313 int ret = U_OK, fin = 0, i;
415 int ret = U_OK, i;
314416 uint8_t header[2] = {0}, payload_len[8] = {0}, masking_key[4] = {0};
315417 uint8_t * payload_data = NULL;
316418 size_t msg_len = 0;
317419 ssize_t len = 0;
318
319 *message = o_malloc(sizeof(struct _websocket_message));
320 if (*message != NULL) {
321 (*message)->data_len = 0;
322 (*message)->has_mask = 0;
323 (*message)->data = NULL;
324 time(&(*message)->datestamp);
325
326 do {
420
421 if (pthread_mutex_lock(&websocket_manager->read_lock)) {
422 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking websocket read lock messages");
423 websocket_manager->connected = 0;
424 } else {
425 *message = o_malloc(sizeof(struct _websocket_message));
426 if (*message != NULL) {
427 (*message)->data_len = 0;
428 (*message)->has_mask = 0;
429 (*message)->data = NULL;
430 (*message)->fragment_len = 0;
431 (*message)->opcode = 0;
432 (*message)->rsv = 0;
433 (*message)->fin = 0;
434 time(&(*message)->datestamp);
435
436 // Read header
437 if ((len = read_data_from_socket(websocket_manager, header, 2)) == 2) {
438 (*message)->opcode = header[0] & 0x0F;
439 (*message)->rsv = header[0] & 0x70;
440 (*message)->fin = (header[0] & U_WEBSOCKET_BIT_FIN);
441 if ((header[1] & U_WEBSOCKET_LEN_MASK) <= 125) {
442 msg_len = (header[1] & U_WEBSOCKET_LEN_MASK);
443 } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 126) {
444 len = read_data_from_socket(websocket_manager, payload_len, 2);
445 if (len == 2) {
446 msg_len = payload_len[1] | ((uint64_t)payload_len[0] << 8);
447 } else if (len >= 0) {
448 ret = U_ERROR;
449 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket message length");
450 } else {
451 ret = U_ERROR_DISCONNECTED;
452 }
453 } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 127) {
454 len = read_data_from_socket(websocket_manager, payload_len, 8);
455 if (len == 8) {
456 msg_len = payload_len[7] |
457 ((uint64_t)payload_len[6] << 8) |
458 ((uint64_t)payload_len[5] << 16) |
459 ((uint64_t)payload_len[4] << 24) |
460 ((uint64_t)payload_len[3] << 32) |
461 ((uint64_t)payload_len[2] << 40) |
462 ((uint64_t)payload_len[1] << 48) |
463 ((uint64_t)payload_len[0] << 54);
464 } else if (len >= 0) {
465 ret = U_ERROR;
466 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket message length");
467 } else {
468 ret = U_ERROR_DISCONNECTED;
469 }
470 }
471 } else if (len == 0) {
472 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket");
473 ret = U_ERROR;
474 } else {
475 ret = U_ERROR_DISCONNECTED;
476 }
477 if (!(*message)->fin) {
478 if (!(*message)->fragment_len) {
479 (*message)->fragment_len = msg_len;
480 }
481 }
482
327483 if (ret == U_OK) {
328 // Read header
329 if ((len = read_data_from_socket(websocket_manager, header, 2)) == 2) {
330 (*message)->opcode = header[0] & 0x0F;
331 fin = (header[0] & U_WEBSOCKET_BIT_FIN);
332 if ((header[1] & U_WEBSOCKET_LEN_MASK) <= 125) {
333 msg_len = (header[1] & U_WEBSOCKET_LEN_MASK);
334 } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 126) {
335 len = read_data_from_socket(websocket_manager, payload_len, 2);
336 if (len == 2) {
337 msg_len = payload_len[1] | ((uint64_t)payload_len[0] << 8);
338 } else if (len >= 0) {
484 if (websocket_manager->type == U_WEBSOCKET_SERVER) {
485 // Read mask
486 if (header[1] & U_WEBSOCKET_MASK) {
487 (*message)->has_mask = 1;
488 len = read_data_from_socket(websocket_manager, masking_key, 4);
489 if (len != 4 && len >= 0) {
490 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket for mask");
339491 ret = U_ERROR;
340 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket message length");
341 } else {
492 } else if (len < 0) {
342493 ret = U_ERROR_DISCONNECTED;
343494 }
344 } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 127) {
345 len = read_data_from_socket(websocket_manager, payload_len, 8);
346 if (len == 8) {
347 msg_len = payload_len[7] |
348 ((uint64_t)payload_len[6] << 8) |
349 ((uint64_t)payload_len[5] << 16) |
350 ((uint64_t)payload_len[4] << 24) |
351 ((uint64_t)payload_len[3] << 32) |
352 ((uint64_t)payload_len[2] << 40) |
353 ((uint64_t)payload_len[1] << 48) |
354 ((uint64_t)payload_len[0] << 54);
355 } else if (len >= 0) {
356 ret = U_ERROR;
357 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket message length");
358 } else {
359 ret = U_ERROR_DISCONNECTED;
360 }
361 }
362 } else if (len == 0) {
363 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket");
364 ret = U_ERROR;
495 } else {
496 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Incoming message has no MASK flag, exiting");
497 ret = U_ERROR;
498 }
365499 } else {
366 ret = U_ERROR_DISCONNECTED;
367 }
368
369 if (ret == U_OK) {
370 if (websocket_manager->type == U_WEBSOCKET_SERVER) {
371 // Read mask
372 if (header[1] & U_WEBSOCKET_MASK) {
373 (*message)->has_mask = 1;
374 len = read_data_from_socket(websocket_manager, masking_key, 4);
375 if (len != 4 && len >= 0) {
376 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket for mask");
377 ret = U_ERROR;
378 } else if (len < 0) {
379 ret = U_ERROR_DISCONNECTED;
380 }
381 } else {
382 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Incoming message has no MASK flag, exiting");
383 ret = U_ERROR;
384 }
385 } else {
386 if ((header[1] & U_WEBSOCKET_MASK)) {
387 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Incoming message has MASK flag while it should not, exiting");
388 ret = U_ERROR;
389 }
390 }
391 }
392 if (ret == U_OK) {
393 payload_data = o_malloc(msg_len*sizeof(uint8_t));
500 if ((header[1] & U_WEBSOCKET_MASK)) {
501 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Incoming message has MASK flag while it should not, exiting");
502 ret = U_ERROR;
503 }
504 }
505 }
506 if (ret == U_OK && msg_len) {
507 payload_data = o_malloc(msg_len*sizeof(uint8_t));
508 if (payload_data != NULL) {
394509 len = read_data_from_socket(websocket_manager, payload_data, msg_len);
395510 if (len < 0) {
396511 ret = U_ERROR_DISCONNECTED;
399514 ret = U_ERROR;
400515 } else {
401516 // If mask, decode message
402 (*message)->data = o_realloc((*message)->data, (msg_len+(*message)->data_len)*sizeof(uint8_t));
517 (*message)->data = o_malloc((msg_len+(*message)->data_len)*sizeof(uint8_t));
403518 if ((*message)->has_mask) {
404519 for (i = (*message)->data_len; (unsigned int)i < (*message)->data_len + msg_len; i++) {
405520 (*message)->data[i] = payload_data[i-(*message)->data_len] ^ masking_key[(i-(*message)->data_len)%4];
410525 (*message)->data_len += msg_len;
411526 }
412527 o_free(payload_data);
413 }
414 if (!fin) {
415 while (!is_websocket_data_available(websocket_manager));
416 }
417 }
418 } while (ret == U_OK && !fin);
419 } else {
420 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for *message");
528 } else {
529 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for payload_data, exiting");
530 ret = U_ERROR;
531 }
532 }
533 } else {
534 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for *message");
535 }
536 pthread_mutex_unlock(&websocket_manager->read_lock);
421537 }
422538 if (ret != U_OK) {
423539 ulfius_clear_websocket_message(*message);
428544 /**
429545 * Run the websocket manager in a separated detached thread
430546 */
431 void * ulfius_thread_websocket_manager_run(void * args) {
547 static void * ulfius_thread_websocket_manager_run(void * args) {
432548 struct _websocket * websocket = (struct _websocket *)args;
433549 if (websocket != NULL) {
434550 websocket->websocket_manager_callback(websocket->request, websocket->websocket_manager, websocket->websocket_manager_user_data);
435
551
436552 // Send close message if the websocket is still open
437553 if (websocket->websocket_manager->connected) {
438 websocket->websocket_manager->close_flag = 1;
554 if (pthread_mutex_lock(&websocket->websocket_manager->read_lock)) {
555 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking websocket read lock messages");
556 } else {
557 websocket->websocket_manager->close_flag = 1;
558 pthread_mutex_unlock(&websocket->websocket_manager->read_lock);
559 }
439560 }
440561 }
441562 return NULL;
563 }
564
565 static int ulfius_websocket_extension_message_in_perform_apply(struct _websocket * websocket, struct _websocket_message * message) {
566 uint64_t data_out_len = 0, data_in_len = 0;
567 char * data_out = NULL, * data_in = NULL;
568 int ret = U_OK;
569 size_t len, i;
570 struct _websocket_extension * extension;
571
572 if ((len = pointer_list_size(websocket->websocket_manager->websocket_extension_list))) {
573 data_in = NULL;
574 if (!message->data_len || (message->data_len && (data_in = o_malloc(message->data_len*sizeof(char))) != NULL)) {
575 if (message->data_len) {
576 memcpy(data_in, message->data, message->data_len);
577 }
578 data_in_len = message->data_len;
579 for (i=0; i<len && ret == U_OK; i++) {
580 extension = pointer_list_get_at(websocket->websocket_manager->websocket_extension_list, (len-i-1));
581 if (extension != NULL &&
582 extension->enabled &&
583 extension->websocket_extension_message_in_perform != NULL &&
584 (message->rsv & extension->rsv)) {
585 if (extension->websocket_extension_message_in_perform(message->opcode,
586 data_in_len,
587 data_in,
588 &data_out_len,
589 &data_out,
590 0,
591 extension->websocket_extension_message_out_perform_user_data,
592 extension->context) != U_OK) {
593 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error performing websocket_extension_message_in_perform at index %zu", i);
594 ret = U_ERROR;
595 } else {
596 o_free(data_in);
597 data_in = NULL;
598 data_in_len = 0;
599 if (data_out_len) {
600 if ((data_in = o_malloc(data_out_len*sizeof(char))) != NULL) {
601 memcpy(data_in, data_out, data_out_len);
602 data_in_len = data_out_len;
603 } else {
604 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for data_in (%zu) (incoming)", i);
605 ret = U_ERROR_MEMORY;
606 }
607 }
608 o_free(data_out);
609 data_out = NULL;
610 data_out_len = 0;
611 }
612 }
613 }
614 o_free(message->data);
615 message->data = data_in;
616 message->data_len = data_in_len;
617 } else {
618 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for data_in (incoming) %zu", message->data_len);
619 ret = U_ERROR_MEMORY;
620 }
621 }
622 return ret;
442623 }
443624
444625 /**
447628 * Complete the callback when the websocket is closed
448629 * The websocket can be closed by the client, the manager, the program, or on network disconnect
449630 */
450 void * ulfius_thread_websocket(void * data) {
631 static void * ulfius_thread_websocket(void * data) {
451632 struct _websocket * websocket = (struct _websocket*)data;
452 struct _websocket_message * message = NULL;
633 struct _websocket_message * message = NULL, * message_previous = NULL;
453634 pthread_t thread_websocket_manager;
454 int thread_ret_websocket_manager = 1;
455
635 int thread_ret_websocket_manager = 1, ret = U_OK;
636
456637 if (websocket != NULL && websocket->websocket_manager != NULL) {
457638 if (websocket->websocket_manager_callback != NULL) {
458639 thread_ret_websocket_manager = pthread_create(&thread_websocket_manager, NULL, ulfius_thread_websocket_manager_run, (void *)websocket);
461642 websocket->websocket_manager->connected = 0;
462643 }
463644 }
464 while (websocket->websocket_manager->connected) {
465 message = NULL;
645 while (websocket->websocket_manager->connected && ret == U_OK) {
466646 if (websocket->websocket_manager->close_flag) {
467647 if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) {
468648 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending close message on close_flag");
470650 websocket->websocket_manager->connected = 0;
471651 } else {
472652 if (is_websocket_data_available(websocket->websocket_manager)) {
473 if (pthread_mutex_lock(&websocket->websocket_manager->read_lock)) {
474 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking websocket read lock messages");
475 websocket->websocket_manager->connected = 0;
476 } else {
477 if (ulfius_read_incoming_message(websocket->websocket_manager, &message) == U_OK) {
478 if (message->opcode == U_WEBSOCKET_OPCODE_CLOSE) {
479 // Send close command back, then close the socket
480 if (ulfius_send_websocket_message_managed(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL, 0) != U_OK) {
653 message = NULL;
654 if (ulfius_read_incoming_message(websocket->websocket_manager, &message) == U_OK) {
655 if (message->opcode == U_WEBSOCKET_OPCODE_CLOSE && message->fin) {
656 // Send close command back, then close the socket
657 if (message->data_len <= 125) {
658 if (ulfius_send_websocket_message_managed(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, 0, NULL, 0) != U_OK) {
481659 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending close command");
482660 }
483 websocket->websocket_manager->connected = 0;
484 } else if (message->opcode == U_WEBSOCKET_OPCODE_PING) {
661 }
662 websocket->websocket_manager->connected = 0;
663 } else if (message->opcode == U_WEBSOCKET_OPCODE_PING && message->fin) {
664 if (pthread_mutex_lock(&websocket->websocket_manager->write_lock)) {
665 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking write lock");
666 } else {
485667 // Send pong command
486 if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_PONG, 0, NULL) != U_OK) {
487 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending pong command");
668 if (message->data_len <= 125) {
669 if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_PONG, message->data_len, message->data) != U_OK) {
670 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending pong command");
671 websocket->websocket_manager->connected = 0;
672 }
673 } else {
488674 websocket->websocket_manager->connected = 0;
489675 }
676 pthread_mutex_unlock(&websocket->websocket_manager->write_lock);
490677 }
491 if (websocket->websocket_incoming_message_callback != NULL) {
492 websocket->websocket_incoming_message_callback(websocket->request, websocket->websocket_manager, message, websocket->websocket_incoming_user_data);
678 } else if (message->opcode == U_WEBSOCKET_OPCODE_PONG && message->fin) {
679 if (websocket->websocket_manager->ping_sent) {
680 websocket->websocket_manager->ping_sent = 0;
493681 }
494 if (ulfius_push_websocket_message(websocket->websocket_manager->message_list_incoming, message) != U_OK) {
495 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pushing new websocket message in list");
682 } else if (message->opcode == U_WEBSOCKET_OPCODE_TEXT || message->opcode == U_WEBSOCKET_OPCODE_BINARY || message->opcode == U_WEBSOCKET_OPCODE_CONTINUE) {
683 if (message->fin && message->opcode == U_WEBSOCKET_OPCODE_CONTINUE && message_previous == NULL) {
684 y_log_message(Y_LOG_LEVEL_DEBUG, "Ulfius - Invalid fragmented message");
685 websocket->websocket_manager->connected = 0;
686 } else if (message->fin) {
687 if (message_previous != NULL) {
688 if (ulfius_merge_fragmented_message(message_previous, message) != U_OK) {
689 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error merging final fragmented messages");
690 ret = U_ERROR;
691 } else {
692 ulfius_clear_websocket_message(message);
693 message = message_previous;
694 message_previous = NULL;
695 }
696 }
697 if (ret == U_OK) {
698 if (ulfius_websocket_extension_message_in_perform_apply(websocket, message) == U_OK) {
699 if (!message->rsv || (websocket->websocket_manager->rsv_expected & message->rsv)) {
700 if (message->opcode != U_WEBSOCKET_OPCODE_TEXT || !message->data_len || utf8_check(message->data, message->data_len) == NULL) {
701 if (websocket->websocket_incoming_message_callback != NULL) {
702 websocket->websocket_incoming_message_callback(websocket->request, websocket->websocket_manager, message, websocket->websocket_incoming_user_data);
703 }
704 if (ulfius_push_websocket_message(websocket->websocket_manager->message_list_incoming, message) != U_OK) {
705 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pushing new websocket message in list");
706 websocket->websocket_manager->connected = 0;
707 } else {
708 message = NULL;
709 }
710 } else {
711 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Invalid UTF8 text message");
712 websocket->websocket_manager->connected = 0;
713 }
714 } else {
715 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Unexpected rsv message");
716 websocket->websocket_manager->connected = 0;
717 }
718 } else {
719 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_extension_message_in_perform_apply");
720 websocket->websocket_manager->connected = 0;
721 }
722 }
723 } else if (message->opcode == U_WEBSOCKET_OPCODE_TEXT || message->opcode == U_WEBSOCKET_OPCODE_BINARY || message->opcode == U_WEBSOCKET_OPCODE_CONTINUE) {
724 if (message->opcode != U_WEBSOCKET_OPCODE_CONTINUE || message_previous != NULL) {
725 if (message_previous == NULL) {
726 message_previous = message;
727 message = NULL;
728 } else {
729 if (ulfius_merge_fragmented_message(message_previous, message) != U_OK) {
730 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error merging fragmented messages");
731 websocket->websocket_manager->connected = 0;
732 }
733 }
734 } else {
735 y_log_message(Y_LOG_LEVEL_DEBUG, "Ulfius - Invalid continue message");
736 websocket->websocket_manager->connected = 0;
737 }
738 } else {
739 y_log_message(Y_LOG_LEVEL_DEBUG, "Ulfius - Invalid fragmented message");
496740 websocket->websocket_manager->connected = 0;
497741 }
498742 } else {
499 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_read_incoming_message");
743 // Invalid opcode
744 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid opcode");
500745 websocket->websocket_manager->connected = 0;
501746 }
502 pthread_mutex_unlock(&websocket->websocket_manager->read_lock);
503 }
504 }
505 }
506 }
747 } else {
748 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_read_incoming_message");
749 websocket->websocket_manager->connected = 0;
750 }
751 ulfius_clear_websocket_message(message);
752 }
753 }
754 }
755 ulfius_clear_websocket_message(message_previous);
507756 // Wait for thread manager to close if exists
508757 if (!thread_ret_websocket_manager) {
509758 pthread_join(thread_websocket_manager, NULL);
538787 size_t offset = 0;
539788 int eol = 0, ret = U_ERROR;
540789 uint8_t car;
541
790
542791 *line_len = 0;
543792 do {
544793 if (read_data_from_socket(websocket->websocket_manager, &car, 1) == 1) {
545794 buffer[offset] = car;
546795 }
547
796
548797 if (offset > 0 && buffer[offset-1] == '\r' && buffer[offset] == '\n') {
549798 eol = 1;
550799 buffer[offset-1] = '\0';
551800 *line_len = offset - 1;
552801 ret = U_OK;
553802 }
554
803
555804 offset++;
556 } while (!eol && offset < buffer_len);
805 } while (websocket->websocket_manager->connected && !eol && offset < buffer_len);
806
807 if (!websocket->websocket_manager->connected && !eol && offset < buffer_len) {
808 read_data_from_socket(websocket->websocket_manager, (uint8_t *)buffer+offset, (buffer_len-offset-1));
809 buffer[buffer_len-1] = '\0';
810 *line_len = buffer_len;
811 }
557812 return ret;
558813 }
559814
564819 * Return U_OK on success
565820 */
566821 static int ulfius_websocket_connection_handshake(struct _u_request * request, struct yuarel * y_url, struct _websocket * websocket, struct _u_response * response) {
567 int websocket_response_http = 0, i, ret, check_websocket = WEBSOCKET_RESPONSE_UPGRADE | WEBSOCKET_RESPONSE_CONNECTION | WEBSOCKET_RESPONSE_ACCEPT;
822 int websocket_response_http = 0, ret, check_websocket = WEBSOCKET_RESPONSE_UPGRADE | WEBSOCKET_RESPONSE_CONNECTION | WEBSOCKET_RESPONSE_ACCEPT, extension_enabled;
568823 unsigned int websocket_response = 0;
569 char * http_line, ** split_line = NULL, * key, * value, * separator;
824 char * http_line, ** split_line = NULL, * key, * value, * separator, ** extension_list = NULL;
570825 char buffer[4096] = {0};
571826 const char ** keys;
572 size_t buffer_len = 4096, line_len;
573
827 size_t buffer_len = 4096, line_len, extension_len, i, j;
828 struct _websocket_extension * w_extension;
829
574830 // Send HTTP Request
575831 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);
576832 ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line));
577833 o_free(http_line);
578
579 http_line = msprintf("Host: %s\r\n", y_url->host);
834
835 if (y_url->port) {
836 http_line = msprintf("Host: %s:%d\r\n", y_url->host, y_url->port);
837 } else {
838 http_line = msprintf("Host: %s\r\n", y_url->host);
839 }
580840 ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line));
581841 o_free(http_line);
582
842
583843 http_line = msprintf("Upgrade: websocket\r\n");
584844 ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line));
585845 o_free(http_line);
586
846
587847 http_line = msprintf("Connection: Upgrade\r\n");
588848 ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line));
589849 o_free(http_line);
590
850
591851 http_line = msprintf("Origin: %s://%s\r\n", y_url->scheme, y_url->host);
592852 ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line));
593853 o_free(http_line);
594
854
595855 keys = u_map_enum_keys(request->map_header);
596856 for (i=0; keys[i] != NULL; i++) {
597857 http_line = msprintf("%s: %s\r\n", keys[i], u_map_get_case(request->map_header, keys[i]));
603863 check_websocket |= WEBSOCKET_RESPONSE_EXTENSION;
604864 }
605865 }
606
866
607867 if (websocket->websocket_manager->tcp_sock >= 0) {
608868 // Send empty line
609869 const char * empty = "\r\n";
610870 ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)empty, o_strlen(empty));
611871 }
612
872
613873 // Read and parse response
614874 if (ulfius_get_next_line_from_http_response(websocket, buffer, buffer_len, &line_len) == U_OK) {
615 if (split_string(buffer, " ", &split_line) >= 2 && 0 == o_strcmp(split_line[0], "HTTP/1.1") && 0 == o_strcmp(split_line[1], "101")) {
875 if (split_string(buffer, " ", &split_line) >= 2 && 0 == o_strcmp(split_line[0], "HTTP/1.1")) {
616876 websocket_response_http = 1;
617877 response->status = strtol(split_line[1], NULL, 10);
618878 response->protocol = o_strdup("1.1");
619879 }
620880 free_string_array(split_line);
621881 }
622 if (websocket_response_http) {
882 if (websocket_response_http && response->status == 101) {
623883 do {
624884 if (ulfius_get_next_line_from_http_response(websocket, buffer, buffer_len, &line_len) == U_OK) {
625885 if (o_strlen(buffer) && (separator = o_strchr(buffer, ':')) != NULL) {
628888 if (u_map_put(response->map_header, key, value) != U_OK) {
629889 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error adding header %s:%s to the response structure", key, value);
630890 }
631 if (0 == o_strcmp(buffer, "Upgrade: websocket")) {
891 if (0 == o_strcasecmp(buffer, "Upgrade: websocket")) {
632892 websocket_response |= WEBSOCKET_RESPONSE_UPGRADE;
633 } else if (0 == o_strcmp(buffer, "Connection: Upgrade")) {
893 } else if (0 == o_strcasecmp(buffer, "Connection: Upgrade")) {
634894 websocket_response |= WEBSOCKET_RESPONSE_CONNECTION;
635 } else if (0 == o_strcmp(key, "Sec-WebSocket-Protocol")) {
895 } else if (0 == o_strcasecmp(key, "Sec-WebSocket-Protocol")) {
636896 websocket->websocket_manager->protocol = o_strdup(value);
637897 websocket_response |= WEBSOCKET_RESPONSE_PROTCOL;
638 } else if (0 == o_strcmp(key, "Sec-WebSocket-Extension")) {
898 } else if (0 == o_strcasecmp(key, "Sec-WebSocket-Extensions")) {
639899 websocket->websocket_manager->extensions = o_strdup(value);
640900 websocket_response |= WEBSOCKET_RESPONSE_EXTENSION;
641 } else if (0 == o_strcmp(buffer, "Sec-WebSocket-Accept") && ulfius_check_handshake_response(u_map_get_case(request->map_header, "Sec-WebSocket-Key"), value) == U_OK) {
901 } else if (0 == o_strcasecmp(buffer, "Sec-WebSocket-Accept") && ulfius_check_handshake_response(u_map_get_case(request->map_header, "Sec-WebSocket-Key"), value) == U_OK) {
642902 websocket_response |= WEBSOCKET_RESPONSE_ACCEPT;
643903 }
644904 o_free(key);
655915 }
656916 } while (1);
657917 }
658
918
659919 if (!websocket_response_http || !(websocket_response & check_websocket) || response->status != 101) {
660920 if (u_map_has_key(response->map_header, "Content-Length")) {
661921 response->binary_body_length = strtol(u_map_get(response->map_header, "Content-Length"), NULL, 10);
673933 ret = U_ERROR;
674934 } else {
675935 ret = U_OK;
676 }
677
936 if (o_strlen(websocket->websocket_manager->extensions)) {
937 extension_len = pointer_list_size(websocket->websocket_manager->websocket_extension_list);
938 if (extension_len) {
939 if (split_string(websocket->websocket_manager->extensions, ",", &extension_list)) {
940 for (i=0; extension_list[i]!=NULL; i++) {
941 extension_enabled = 0;
942 for (j=0; j<extension_len; j++) {
943 w_extension = pointer_list_get_at(websocket->websocket_manager->websocket_extension_list, j);
944 if (w_extension != NULL && !w_extension->enabled) {
945 if (w_extension->websocket_extension_client_match != NULL) {
946 if (w_extension->websocket_extension_client_match(trimwhitespace(extension_list[i]), w_extension->websocket_extension_client_match_user_data, &w_extension->context) == U_OK) {
947 if (!(w_extension->rsv & websocket->websocket_manager->rsv_expected)) {
948 websocket->websocket_manager->rsv_expected |= w_extension->rsv;
949 w_extension->extension_server = o_strdup(extension_list[i]);
950 w_extension->enabled = 1;
951 extension_enabled = 1;
952 }
953 break;
954 }
955 } else {
956 if (0 == o_strcmp(trimwhitespace(extension_list[i]), w_extension->extension_client)) {
957 if (!(w_extension->rsv & websocket->websocket_manager->rsv_expected)) {
958 websocket->websocket_manager->rsv_expected |= w_extension->rsv;
959 w_extension->extension_server = o_strdup(extension_list[i]);
960 w_extension->enabled = 1;
961 extension_enabled = 1;
962 }
963 break;
964 }
965 }
966 } else if (w_extension == NULL) {
967 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error pointer_list_get_at %zu", j);
968 ret = U_ERROR;
969 }
970 }
971 if (!extension_enabled) {
972 y_log_message(Y_LOG_LEVEL_DEBUG, "Extension '%s' not enabled, aborting", extension_list[i]);
973 ret = U_ERROR;
974 break;
975 }
976 }
977 } else {
978 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error split_string extensions");
979 ret = U_ERROR;
980 }
981 free_string_array(extension_list);
982 }
983 }
984 }
985
678986 return ret;
679987 }
680988
686994 int ret;
687995 struct sockaddr_in server;
688996 struct hostent * he;
689
997
690998 websocket->websocket_manager->tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
691999 if (websocket->websocket_manager->tcp_sock != -1) {
6921000 if ((he = gethostbyname(y_url->host)) != NULL) {
6931001 memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
6941002 server.sin_family = AF_INET;
6951003 server.sin_port = htons(y_url->port);
696
1004
6971005 if (connect(websocket->websocket_manager->tcp_sock, (struct sockaddr *)&server , sizeof(server)) >= 0) {
698 websocket->websocket_manager->fds.fd = websocket->websocket_manager->tcp_sock;
1006 websocket->websocket_manager->fds_in.fd = websocket->websocket_manager->tcp_sock;
1007 websocket->websocket_manager->fds_out.fd = websocket->websocket_manager->tcp_sock;
6991008 websocket->websocket_manager->connected = 1;
7001009 websocket->websocket_manager->close_flag = 0;
7011010 websocket->urh = NULL;
7021011 websocket->instance = NULL;
703
1012
7041013 ret = ulfius_websocket_connection_handshake(request, y_url, websocket, response);
7051014 } else {
7061015 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error connecting socket");
7301039 gnutls_datum_t out;
7311040 int type;
7321041 unsigned status;
733
1042
7341043 if (gnutls_global_init() >= 0) {
7351044 if (gnutls_certificate_allocate_credentials(&websocket->websocket_manager->xcred) >= 0 &&
7361045 gnutls_certificate_set_x509_system_trust(websocket->websocket_manager->xcred) >= 0 &&
7381047 gnutls_server_name_set(websocket->websocket_manager->gnutls_session, GNUTLS_NAME_DNS, y_url->host, o_strlen(y_url->host)) >= 0 &&
7391048 gnutls_set_default_priority(websocket->websocket_manager->gnutls_session) >= 0 &&
7401049 gnutls_credentials_set(websocket->websocket_manager->gnutls_session, GNUTLS_CRD_CERTIFICATE, websocket->websocket_manager->xcred) >= 0) {
741
1050
7421051 if (request->check_server_certificate) {
7431052 gnutls_session_set_verify_cert(websocket->websocket_manager->gnutls_session, y_url->host, 0);
7441053 }
7501059 server.sin_family = AF_INET;
7511060 server.sin_port = htons(y_url->port);
7521061 if (connect(websocket->websocket_manager->tcp_sock, (struct sockaddr *)&server , sizeof(server)) >= 0) {
753 websocket->websocket_manager->fds.fd = websocket->websocket_manager->tcp_sock;
1062 websocket->websocket_manager->fds_in.fd = websocket->websocket_manager->tcp_sock;
1063 websocket->websocket_manager->fds_out.fd = websocket->websocket_manager->tcp_sock;
7541064 websocket->websocket_manager->connected = 1;
7551065 websocket->websocket_manager->close_flag = 0;
7561066 websocket->urh = NULL;
7571067 websocket->instance = NULL;
758
1068
7591069 gnutls_transport_set_int(websocket->websocket_manager->gnutls_session, websocket->websocket_manager->tcp_sock);
7601070 gnutls_handshake_set_timeout(websocket->websocket_manager->gnutls_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
7611071
7631073 ret = gnutls_handshake(websocket->websocket_manager->gnutls_session);
7641074 }
7651075 while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
766
1076
7671077 if (ret < 0) {
7681078 if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) {
7691079 /* check certificate verification status */
7811091 } else {
7821092 char * desc = gnutls_session_get_desc(websocket->websocket_manager->gnutls_session);
7831093 gnutls_free(desc);
784
1094
7851095 ret = ulfius_websocket_connection_handshake(request, y_url, websocket, response);
7861096 }
7871097 } else {
8081118 ret = U_ERROR;
8091119 }
8101120 return ret;
1121 }
1122
1123 static void * u_zalloc(void * q, unsigned n, unsigned m) {
1124 (void)q;
1125 return o_malloc((size_t) n * m);
1126 }
1127
1128 static void u_zfree(void *q, void *p) {
1129 (void)q;
1130 o_free(p);
8111131 }
8121132
8131133 /**
8281148 UNUSED(con_cls);
8291149 UNUSED(extra_in);
8301150 UNUSED(extra_in_size);
831
1151
8321152 if (websocket != NULL) {
8331153 websocket->urh = urh;
8341154 // Run websocket manager in a thread
8351155 websocket->websocket_manager->type = U_WEBSOCKET_SERVER;
8361156 websocket->websocket_manager->mhd_sock = sock;
837 websocket->websocket_manager->fds.fd = sock;
838 websocket->websocket_manager->fds.events = POLLIN | POLLRDHUP;
1157 websocket->websocket_manager->fds_in.fd = sock;
1158 websocket->websocket_manager->fds_in.events = POLLIN | POLLRDHUP;
1159 websocket->websocket_manager->fds_out.fd = sock;
1160 websocket->websocket_manager->fds_out.events = POLLOUT | POLLRDHUP;
8391161 websocket->websocket_manager->connected = 1;
8401162 websocket->websocket_manager->close_flag = 0;
8411163 thread_ret_websocket = pthread_create(&thread_websocket, NULL, ulfius_thread_websocket, (void *)websocket);
8601182 */
8611183 int ulfius_check_handshake_response(const char * key, const char * response) {
8621184 char websocket_accept[32] = {0};
863
1185
8641186 if (key != NULL && response != NULL) {
8651187 if (ulfius_generate_handshake_answer(key, websocket_accept) && 0 == o_strcmp(websocket_accept, response)) {
8661188 return U_OK;
8801202 unsigned char encoded_key[32] = {0};
8811203 size_t encoded_key_size = 32, encoded_key_size_base64;
8821204 int res, to_return = 0;
883
1205
8841206 key_data.data = (unsigned char*)msprintf("%s%s", key, U_WEBSOCKET_MAGIC_STRING);
8851207 key_data.size = o_strlen((const char *)key_data.data);
886
1208
8871209 if (key_data.data != NULL && out_digest != NULL && (res = gnutls_fingerprint(GNUTLS_DIG_SHA1, &key_data, encoded_key, &encoded_key_size)) == GNUTLS_E_SUCCESS) {
8881210 if (o_base64_encode(encoded_key, encoded_key_size, (unsigned char *)out_digest, &encoded_key_size_base64)) {
8891211 to_return = 1;
9381260 int ulfius_check_list_match(const char * source, const char * match, const char * separator, char ** result) {
9391261 char ** source_list = NULL, ** match_list = NULL;
9401262 int i, ret = U_OK;
941
1263
9421264 if (result != NULL) {
9431265 *result = NULL;
9441266 if (match == NULL) {
9791301 int ulfius_check_first_match(const char * source, const char * match, const char * separator, char ** result) {
9801302 char ** source_list = NULL, ** match_list = NULL;
9811303 int i, ret = U_OK;
982
1304
9831305 if (result != NULL) {
9841306 *result = NULL;
9851307 if (match == NULL) {
10421364 if (instance != NULL && websocket != NULL) {
10431365 ((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 *));
10441366 if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active != NULL) {
1045 ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active] = websocket;
1046 ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active++;
1047 return U_OK;
1367 if (pthread_mutex_lock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_active_lock)) {
1368 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking websocket_active_lock");
1369 return U_ERROR;
1370 } else {
1371 ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active] = websocket;
1372 ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active++;
1373 pthread_mutex_unlock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_active_lock);
1374 return U_OK;
1375 }
10481376 } else {
10491377 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for instance->websocket_handler->websocket_active");
10501378 return U_ERROR_MEMORY;
10591387 */
10601388 int ulfius_instance_remove_websocket_active(struct _u_instance * instance, struct _websocket * websocket) {
10611389 size_t i, j;
1062 if (instance != NULL && ((struct _websocket_handler *)instance->websocket_handler)->websocket_active != NULL && websocket != NULL) {
1063 for (i=0; i<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active; i++) {
1064 if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active[i] == websocket) {
1065 if (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active > 1) {
1066 for (j=i; j<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active-1; j++) {
1067 ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j] = ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j+1];
1068 }
1069 ((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 *));
1070 if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active == NULL) {
1071 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for instance->websocket_active");
1072 return U_ERROR_MEMORY;
1073 }
1074 } else {
1075 o_free(((struct _websocket_handler *)instance->websocket_handler)->websocket_active);
1076 ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = NULL;
1077 }
1078 ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active--;
1079 pthread_mutex_lock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock);
1080 pthread_cond_broadcast(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_cond);
1081 pthread_mutex_unlock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock);
1082 return U_OK;
1083 }
1084 }
1085 return U_ERROR_NOT_FOUND;
1086 } else {
1087 return U_ERROR_PARAMS;
1088 }
1390 int ret;
1391 if (instance != NULL && instance->websocket_handler != NULL && ((struct _websocket_handler *)instance->websocket_handler)->websocket_active != NULL && websocket != NULL) {
1392 if (pthread_mutex_lock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_active_lock)) {
1393 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking websocket_active_lock");
1394 ret = U_ERROR;
1395 } else {
1396 ret = U_ERROR_NOT_FOUND;
1397 for (i=0; i<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active && ret == U_ERROR_NOT_FOUND; i++) {
1398 if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active[i] == websocket) {
1399 if (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active > 1) {
1400 for (j=i; j<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active-1; j++) {
1401 ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j] = ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j+1];
1402 }
1403 ((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 *));
1404 if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active == NULL) {
1405 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for instance->websocket_active");
1406 ret = U_ERROR_MEMORY;
1407 }
1408 } else {
1409 o_free(((struct _websocket_handler *)instance->websocket_handler)->websocket_active);
1410 ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = NULL;
1411 }
1412 ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active--;
1413 pthread_mutex_lock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock);
1414 pthread_cond_broadcast(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_cond);
1415 pthread_mutex_unlock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock);
1416 ret = U_OK;
1417 }
1418 }
1419 pthread_mutex_unlock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_active_lock);
1420 }
1421 } else {
1422 ret = U_ERROR_PARAMS;
1423 }
1424 return ret;
10891425 }
10901426
10911427 /********************************/
11041440 const uint64_t fragment_len) {
11051441 int ret = U_OK, ret_message, count = WEBSOCKET_MAX_CLOSE_TRY;
11061442 struct _websocket_message * message;
1107
1443 uint8_t rsv = 0;
1444 size_t i, len;
1445 uint64_t data_out_len = 0, data_in_len = 0;
1446 char * data_out = NULL, * data_in = NULL;
1447 struct _websocket_extension * extension;
1448
11081449 if (websocket_manager != NULL && websocket_manager->connected) {
11091450 if (opcode == U_WEBSOCKET_OPCODE_CLOSE) {
1110 if (ulfius_send_websocket_message_managed(websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL, 0) == U_OK) {
1451 if (ulfius_send_websocket_message_managed(websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, 0, NULL, 0) == U_OK) {
11111452 // If message sent is U_WEBSOCKET_OPCODE_CLOSE, wait for the close response for WEBSOCKET_MAX_CLOSE_TRY messages max, then close the connection
11121453 do {
11131454 if (is_websocket_data_available(websocket_manager)) {
11291470 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending U_WEBSOCKET_OPCODE_CLOSE message");
11301471 }
11311472 websocket_manager->connected = 0;
1132 } else {
1133 ret = ulfius_send_websocket_message_managed(websocket_manager, opcode, data_len, data, fragment_len);
1473 } else if (opcode == U_WEBSOCKET_OPCODE_PING && websocket_manager->ping_sent) {
1474 // Ignore sending a second ping before the pong has arrived
1475 ret = U_OK;
1476 } else {
1477 ret = U_OK;
1478 if ((data_len && (data_in = o_malloc(data_len*sizeof(char))) != NULL) || !data_len) {
1479 if (data != NULL) {
1480 memcpy(data_in, data, data_len);
1481 } else {
1482 memset(data_in, 0, data_len);
1483 }
1484 data_in_len = data_len;
1485 if ((len = pointer_list_size(websocket_manager->websocket_extension_list)) && (opcode == U_WEBSOCKET_OPCODE_BINARY || opcode == U_WEBSOCKET_OPCODE_TEXT)) {
1486 for (i=0; ret == U_OK && i<len && (extension = (struct _websocket_extension *)pointer_list_get_at(websocket_manager->websocket_extension_list, i)) != NULL; i++) {
1487 if (extension->enabled && extension->websocket_extension_message_out_perform != NULL) {
1488 if ((ret = extension->websocket_extension_message_out_perform(opcode, data_in_len, data_in, &data_out_len, &data_out, fragment_len, extension->websocket_extension_message_out_perform_user_data, extension->context)) != U_OK) {
1489 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error performing websocket_extension_message_out_perform at index %zu", i);
1490 } else {
1491 rsv |= extension->rsv;
1492 o_free(data_in);
1493 if ((data_in = o_malloc(data_out_len*sizeof(char))) != NULL) {
1494 memcpy(data_in, data_out, data_out_len);
1495 data_in_len = data_out_len;
1496 } else {
1497 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for data_in (%zu) (outcoming)", i);
1498 ret = U_ERROR_MEMORY;
1499 }
1500 o_free(data_out);
1501 data_out = NULL;
1502 data_out_len = 0;
1503 }
1504 }
1505 }
1506 }
1507 if (ret == U_OK) {
1508 ret = ulfius_send_websocket_message_managed(websocket_manager, opcode, rsv, data_in_len, data_in, fragment_len);
1509 }
1510 } else {
1511 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for data_in (outcoming)");
1512 ret = U_ERROR_MEMORY;
1513 }
1514 o_free(data_in);
11341515 }
11351516 } else {
11361517 ret = U_ERROR_PARAMS;
11461527 const uint8_t opcode,
11471528 const uint64_t data_len,
11481529 const char * data) {
1149 return ulfius_websocket_send_fragmented_message(websocket_manager, opcode, data_len, data, data_len);
1530 return ulfius_websocket_send_fragmented_message(websocket_manager, opcode, data_len, data, 0);
11501531 }
11511532
11521533 /**
12811662 int ulfius_init_websocket_manager(struct _websocket_manager * websocket_manager) {
12821663 pthread_mutexattr_t mutexattr;
12831664 int ret = U_OK;
1284
1665
12851666 if (websocket_manager != NULL) {
12861667 websocket_manager->connected = 0;
12871668 websocket_manager->close_flag = 0;
1669 websocket_manager->ping_sent = 0;
12881670 websocket_manager->mhd_sock = 0;
12891671 websocket_manager->tcp_sock = 0;
12901672 websocket_manager->protocol = NULL;
12911673 websocket_manager->extensions = NULL;
1674 websocket_manager->rsv_expected = 0;
12921675 pthread_mutexattr_init ( &mutexattr );
12931676 pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE );
12941677 if (pthread_mutex_init(&(websocket_manager->read_lock), &mutexattr) != 0 || pthread_mutex_init(&(websocket_manager->write_lock), &mutexattr) != 0) {
13041687 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing message_list_incoming or message_list_outcoming");
13051688 ret = U_ERROR_MEMORY;
13061689 }
1307 websocket_manager->fds.events = POLLIN | POLLRDHUP;
1690 websocket_manager->fds_in.events = POLLIN | POLLRDHUP;
1691 websocket_manager->fds_out.events = POLLOUT | POLLRDHUP;
13081692 websocket_manager->type = U_WEBSOCKET_NONE;
13091693
13101694 if (ret != U_OK) {
13111695 o_free(websocket_manager->message_list_incoming);
13121696 o_free(websocket_manager->message_list_outcoming);
13131697 }
1698 websocket_manager->websocket_extension_list = NULL;
13141699 pthread_mutexattr_destroy(&mutexattr);
13151700 } else {
13161701 ret = U_ERROR_PARAMS;
13221707 * Clear data of a websocket_manager
13231708 */
13241709 void ulfius_clear_websocket_manager(struct _websocket_manager * websocket_manager) {
1710 size_t len, i;
1711 struct _websocket_extension * extension;
1712
13251713 if (websocket_manager != NULL) {
13261714 pthread_mutex_destroy(&websocket_manager->read_lock);
13271715 pthread_mutex_destroy(&websocket_manager->write_lock);
13331721 websocket_manager->message_list_outcoming = NULL;
13341722 o_free(websocket_manager->protocol);
13351723 o_free(websocket_manager->extensions);
1724 if ((len = pointer_list_size(websocket_manager->websocket_extension_list))) {
1725 for (i=0; i<len; i++) {
1726 extension = pointer_list_get_at(websocket_manager->websocket_extension_list, i);
1727 if (extension != NULL && extension->enabled && extension->websocket_extension_free_context != NULL) {
1728 extension->websocket_extension_free_context(extension->websocket_extension_free_context_user_data, extension->context);
1729 }
1730 }
1731 }
1732 pointer_list_clean_free(websocket_manager->websocket_extension_list, &ulfius_free_websocket_extension_pointer_list);
1733 o_free(websocket_manager->websocket_extension_list);
13361734 }
13371735 }
13381736
13401738 /** Server websocket functions **/
13411739 /********************************/
13421740
1343 /**
1344 * Set a websocket in the response
1345 * You must set at least websocket_manager_callback or websocket_incoming_message_callback
1346 * @Parameters
1347 * response: struct _u_response to send back the websocket initialization, mandatory
1348 * websocket_protocol: list of protocols, separated by a comma, or NULL if all protocols are accepted
1349 * websocket_extensions: list of extensions, separated by a comma, or NULL if all extensions are accepted
1350 * websocket_manager_callback: callback function called right after the handshake acceptance, optional
1351 * websocket_manager_user_data: any data that will be given to the websocket_manager_callback, optional
1352 * websocket_incoming_message_callback: callback function called on each incoming complete message, optional
1353 * websocket_incoming_user_data: any data that will be given to the websocket_incoming_message_callback, optional
1354 * websocket_onclose_callback: callback function called right before closing the websocket, must be complete for the websocket to close
1355 * websocket_onclose_user_data: any data that will be given to the websocket_onclose_callback, optional
1356 * @Return value: U_OK on success
1357 */
13581741 int ulfius_set_websocket_response(struct _u_response * response,
13591742 const char * websocket_protocol,
1360 const char * websocket_extensions,
1743 const char * websocket_extensions,
13611744 void (* websocket_manager_callback) (const struct _u_request * request,
13621745 struct _websocket_manager * websocket_manager,
13631746 void * websocket_manager_user_data),
13951778 ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = websocket_incoming_user_data;
13961779 ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = websocket_onclose_callback;
13971780 ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data = websocket_onclose_user_data;
1781 ((struct _websocket_handle *)response->websocket_handle)->rsv_expected = 0;
13981782 return U_OK;
13991783 } else {
14001784 return U_ERROR_PARAMS;
14011785 }
1786 }
1787
1788 int ulfius_add_websocket_extension_message_perform(struct _u_response * response,
1789 const char * extension_server,
1790 uint8_t rsv,
1791 int (* websocket_extension_message_out_perform)(const uint8_t opcode,
1792 const uint64_t data_len_in,
1793 const char * data_in,
1794 uint64_t * data_len_out,
1795 char ** data_out,
1796 const uint64_t fragment_len,
1797 void * user_data,
1798 void * context),
1799 void * websocket_extension_message_out_perform_user_data,
1800 int (* websocket_extension_message_in_perform)(const uint8_t opcode,
1801 const uint64_t data_len_in,
1802 const char * data_in,
1803 uint64_t * data_len_out,
1804 char ** data_out,
1805 const uint64_t fragment_len,
1806 void * user_data,
1807 void * context),
1808 void * websocket_extension_message_in_perform_user_data,
1809 int (* websocket_extension_server_match)(const char * extension_client,
1810 const char ** extension_client_list,
1811 char ** extension_server,
1812 void * user_data,
1813 void ** context),
1814 void * websocket_extension_server_match_user_data,
1815 void (* websocket_extension_free_context)(void * user_data,
1816 void * context),
1817 void * websocket_extension_free_context_user_data) {
1818 int ret;
1819 struct _websocket_extension * extension;
1820
1821 if (response != NULL && o_strlen(extension_server) &&
1822 (websocket_extension_message_out_perform != NULL || websocket_extension_message_in_perform != NULL) &&
1823 (rsv == U_WEBSOCKET_RSV1 || rsv == U_WEBSOCKET_RSV2 || rsv == U_WEBSOCKET_RSV3)) {
1824 if ((extension = o_malloc(sizeof(struct _websocket_extension))) != NULL) {
1825 if (ulfius_init_websocket_extension(extension) == U_OK) {
1826 extension->extension_server = o_strdup(extension_server);
1827 extension->rsv = rsv;
1828 extension->websocket_extension_message_out_perform = websocket_extension_message_out_perform;
1829 extension->websocket_extension_message_out_perform_user_data = websocket_extension_message_out_perform_user_data;
1830 extension->websocket_extension_message_in_perform = websocket_extension_message_in_perform;
1831 extension->websocket_extension_message_in_perform_user_data = websocket_extension_message_in_perform_user_data;
1832 extension->websocket_extension_server_match = websocket_extension_server_match;
1833 extension->websocket_extension_server_match_user_data = websocket_extension_server_match_user_data;
1834 extension->websocket_extension_free_context = websocket_extension_free_context;
1835 extension->websocket_extension_free_context_user_data = websocket_extension_free_context_user_data;
1836 extension->context = NULL;
1837 if (pointer_list_append(((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list, extension)) {
1838 ret = U_OK;
1839 } else {
1840 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_extension_message_perform - Error pointer_list_append");
1841 ret = U_ERROR;
1842 }
1843 } else {
1844 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_extension_message_perform - Error ulfius_init_websocket_extension");
1845 ret = U_ERROR;
1846 }
1847 } else {
1848 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_extension_message_perform - Error allocating resources for struct _websocket_extension");
1849 ret = U_ERROR_MEMORY;
1850 }
1851 } else {
1852 ret = U_ERROR_PARAMS;
1853 }
1854 return ret;
1855 }
1856
1857 int websocket_extension_message_out_deflate(const uint8_t opcode,
1858 const uint64_t data_len_in,
1859 const char * data_in,
1860 uint64_t * data_len_out,
1861 char ** data_out,
1862 const uint64_t fragment_len,
1863 void * user_data,
1864 void * context) {
1865 struct _websocket_deflate_context * deflate_context = (struct _websocket_deflate_context *)context;
1866 int ret;
1867 (void)opcode;
1868 (void)fragment_len;
1869 (void)user_data;
1870
1871 if (data_len_in) {
1872 if (deflate_context != NULL) {
1873 *data_out = NULL;
1874 *data_len_out = 0;
1875
1876 deflate_context->defstream.avail_in = (uInt)data_len_in;
1877 deflate_context->defstream.next_in = (Bytef *)data_in;
1878
1879 ret = U_OK;
1880 do {
1881 if ((*data_out = o_realloc(*data_out, (*data_len_out)+_U_W_BUFF_LEN)) != NULL) {
1882 deflate_context->defstream.avail_out = _U_W_BUFF_LEN;
1883 deflate_context->defstream.next_out = ((Bytef *)*data_out)+(*data_len_out);
1884 int res;
1885 switch ((res = deflate(&deflate_context->defstream, deflate_context->deflate_mask))) {
1886 case Z_OK:
1887 case Z_STREAM_END:
1888 case Z_BUF_ERROR:
1889 break;
1890 default:
1891 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error deflate");
1892 ret = U_ERROR;
1893 break;
1894 }
1895 (*data_len_out) += _U_W_BUFF_LEN - deflate_context->defstream.avail_out;
1896 } else {
1897 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error allocating resources for data_in_suffix");
1898 ret = U_ERROR;
1899 }
1900 } while (U_OK == ret && deflate_context->defstream.avail_out == 0);
1901
1902 // https://github.com/madler/zlib/issues/149
1903 if (U_OK == ret && Z_BLOCK == deflate_context->deflate_mask) {
1904 if ((*data_out = o_realloc(*data_out, (*data_len_out)+_U_W_BUFF_LEN)) != NULL) {
1905 deflate_context->defstream.avail_out = _U_W_BUFF_LEN;
1906 deflate_context->defstream.next_out = ((Bytef *)*data_out)+(*data_len_out);
1907 switch (deflate(&deflate_context->defstream, Z_FULL_FLUSH)) {
1908 case Z_OK:
1909 case Z_STREAM_END:
1910 case Z_BUF_ERROR:
1911 break;
1912 default:
1913 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error inflate (2)");
1914 ret = U_ERROR;
1915 break;
1916 }
1917 (*data_len_out) += _U_W_BUFF_LEN - deflate_context->defstream.avail_out;
1918 } else {
1919 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error allocating resources for data_in_suffix (2)");
1920 ret = U_ERROR;
1921 }
1922 }
1923
1924 if (U_OK != ret) {
1925 o_free(*data_out);
1926 *data_out = NULL;
1927 *data_len_out = 0;
1928 } else {
1929 if ((*(unsigned char **)data_out)[*data_len_out-1] == 0xff && (*(unsigned char **)data_out)[*data_len_out-2] == 0xff && (*(unsigned char **)data_out)[*data_len_out-3] == 0x00 && (*(unsigned char **)data_out)[*data_len_out-4] == 0x00) {
1930 *data_len_out -= 4;
1931 } else {
1932 (*(unsigned char **)data_out)[*data_len_out] = '\0';
1933 (*data_len_out)++;
1934 }
1935 }
1936 } else {
1937 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error context is NULL");
1938 ret = U_ERROR;
1939 }
1940 } else {
1941 *data_len_out = 0;
1942 ret = U_OK;
1943 }
1944 return ret;
1945 }
1946
1947 int websocket_extension_message_in_inflate(const uint8_t opcode,
1948 const uint64_t data_len_in,
1949 const char * data_in,
1950 uint64_t * data_len_out,
1951 char ** data_out,
1952 const uint64_t fragment_len,
1953 void * user_data,
1954 void * context) {
1955 struct _websocket_deflate_context * deflate_context = (struct _websocket_deflate_context *)context;
1956 unsigned char * data_in_suffix;
1957 unsigned char suffix[4] = {0x00, 0x00, 0xff, 0xff};
1958 int ret;
1959 (void)opcode;
1960 (void)fragment_len;
1961 (void)user_data;
1962
1963 if (data_len_in) {
1964 if (deflate_context != NULL) {
1965 *data_out = NULL;
1966 *data_len_out = 0;
1967 if ((data_in_suffix = o_malloc(data_len_in+4)) != NULL) {
1968 memcpy(data_in_suffix, data_in, data_len_in);
1969 memcpy(data_in_suffix+data_len_in, suffix, 4);
1970
1971 deflate_context->infstream.avail_in = (uInt)data_len_in+4;
1972 deflate_context->infstream.next_in = (Bytef *)data_in_suffix;
1973
1974 ret = U_OK;
1975 do {
1976 if ((*data_out = o_realloc(*data_out, (*data_len_out)+_U_W_BUFF_LEN)) != NULL) {
1977 deflate_context->infstream.avail_out = _U_W_BUFF_LEN;
1978 deflate_context->infstream.next_out = ((Bytef *)*data_out)+(*data_len_out);
1979 switch (inflate(&deflate_context->infstream, deflate_context->inflate_mask)) {
1980 case Z_OK:
1981 case Z_STREAM_END:
1982 case Z_BUF_ERROR:
1983 break;
1984 default:
1985 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error inflate");
1986 ret = U_ERROR;
1987 break;
1988 }
1989 (*data_len_out) += _U_W_BUFF_LEN - deflate_context->infstream.avail_out;
1990 } else {
1991 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error allocating resources for data_in_suffix");
1992 ret = U_ERROR;
1993 }
1994 } while (U_OK == ret && deflate_context->infstream.avail_out == 0);
1995
1996 o_free(data_in_suffix);
1997 if (U_OK != ret) {
1998 o_free(*data_out);
1999 *data_out = NULL;
2000 *data_len_out = 0;
2001 }
2002 } else {
2003 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error allocating resources for data_in_suffix");
2004 ret = U_ERROR;
2005 }
2006 } else {
2007 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error context is NULL");
2008 ret = U_ERROR;
2009 }
2010 } else {
2011 // If RSV1 flag isn't set, copy data as is (as seen in firefox)
2012 *data_len_out = data_len_in;
2013 *data_out = o_malloc(data_len_in);
2014 if (*data_out != NULL) {
2015 memcpy(*data_out, data_in, data_len_in);
2016 ret = U_OK;
2017 } else {
2018 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error allocating resources for data_out");
2019 ret = U_ERROR;
2020 }
2021 }
2022 return ret;
2023 }
2024
2025 int websocket_extension_server_match_deflate(const char * extension_client, const char ** extension_client_list, char ** extension_server, void * user_data, void ** context) {
2026 (void)user_data;
2027 size_t i;
2028 int first_param = 0, ret, server_bits_changed = 0, client_bits_changed = 0;
2029 char ** parameters = NULL, ** param_value = NULL;
2030 long int window_bits;
2031
2032 if (0 == o_strncmp(extension_client, _U_W_EXT_DEFLATE, o_strlen(_U_W_EXT_DEFLATE))) {
2033 // Check if the current extension is the first extension permessage-deflate
2034 for (i=0; extension_client_list[i] != NULL; i++) {
2035 if (0 == o_strncmp(extension_client_list[i], _U_W_EXT_DEFLATE, o_strlen(_U_W_EXT_DEFLATE))) {
2036 if (0 == o_strcmp(extension_client, extension_client_list[i])) {
2037 first_param = 1;
2038 }
2039 break;
2040 }
2041 }
2042 if (first_param) {
2043 if ((*context = o_malloc(sizeof(struct _websocket_deflate_context))) != NULL) {
2044 ((struct _websocket_deflate_context *)*context)->server_no_context_takeover = 0;
2045 ((struct _websocket_deflate_context *)*context)->client_no_context_takeover = 0;
2046 ((struct _websocket_deflate_context *)*context)->server_max_window_bits = WEBSOCKET_DEFLATE_WINDOWS_BITS;
2047 ((struct _websocket_deflate_context *)*context)->client_max_window_bits = WEBSOCKET_DEFLATE_WINDOWS_BITS;
2048 ((struct _websocket_deflate_context *)*context)->deflate_mask = Z_SYNC_FLUSH;
2049 ((struct _websocket_deflate_context *)*context)->inflate_mask = Z_SYNC_FLUSH;
2050 // Parse extension parameters
2051 ret = U_OK;
2052 if (o_strlen(extension_client) > o_strlen(_U_W_EXT_DEFLATE)) {
2053 if (split_string(extension_client+o_strlen(_U_W_EXT_DEFLATE), ";", &parameters)) {
2054 for (i=0; parameters[i] != NULL; i++) {
2055 if (0 == o_strcmp("server_no_context_takeover", trimwhitespace(parameters[i]))) {
2056 ((struct _websocket_deflate_context *)*context)->server_no_context_takeover = 1;
2057 ((struct _websocket_deflate_context *)*context)->deflate_mask = Z_FULL_FLUSH;
2058 } else if (0 == o_strcmp("client_no_context_takeover", trimwhitespace(parameters[i]))) {
2059 ((struct _websocket_deflate_context *)*context)->client_no_context_takeover = 1;
2060 ((struct _websocket_deflate_context *)*context)->inflate_mask = Z_FULL_FLUSH;
2061 } else if (0 == o_strncmp("server_max_window_bits", trimwhitespace(parameters[i]), o_strlen("server_max_window_bits"))) {
2062 if (split_string(trimwhitespace(parameters[i]), "=", &param_value) == 2) {
2063 window_bits = strtol(param_value[1], NULL, 10);
2064 if (window_bits >= 8 && window_bits <= 15) {
2065 if (window_bits == 8) {
2066 window_bits = 9; // Is really 8, but zlib does not support value 8 so increase to 9 - Morten Houmøller Nygaard
2067 }
2068 ((struct _websocket_deflate_context *)*context)->server_max_window_bits = (uint)window_bits;
2069 server_bits_changed = 1;
2070 } else {
2071 y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_extension_server_match_deflate - Error server_max_window_bits value");
2072 ret = U_ERROR;
2073 }
2074 free_string_array(param_value);
2075 } else {
2076 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_server_match_deflate - Error split_string param_value server_max_window_bits");
2077 ret = U_ERROR;
2078 }
2079 } else if (0 == o_strncmp("client_max_window_bits", trimwhitespace(parameters[i]), o_strlen("client_max_window_bits"))) {
2080 if (split_string(trimwhitespace(parameters[i]), "=", &param_value)) {
2081 if (o_strlen(trimwhitespace(param_value[1]))) {
2082 window_bits = strtol(trimwhitespace(param_value[1]), NULL, 10);
2083 } else {
2084 window_bits = WEBSOCKET_DEFLATE_WINDOWS_BITS;
2085 }
2086 if (window_bits >= 8 && window_bits <= 15) {
2087 if (window_bits == 8) {
2088 window_bits = 9; // Is really 8, but zlib does not support value 8 so increase to 9 - Morten Houmøller Nygaard
2089 }
2090 ((struct _websocket_deflate_context *)*context)->client_max_window_bits = (uint)window_bits;
2091 client_bits_changed = 1;
2092 } else {
2093 y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_extension_server_match_deflate - Error client_max_window_bits value");
2094 ret = U_ERROR;
2095 }
2096 free_string_array(param_value);
2097 } else {
2098 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_server_match_deflate - Error split_string param_value client_max_window_bits");
2099 ret = U_ERROR;
2100 }
2101 } else if (o_strlen(trimwhitespace(parameters[i]))) {
2102 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_server_match_deflate - Invalid parameter");
2103 ret = U_ERROR;
2104 }
2105 }
2106 } else {
2107 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_server_match_deflate - Error split_string parameters");
2108 ret = U_ERROR;
2109 }
2110 free_string_array(parameters);
2111 }
2112 if (ret == U_OK) {
2113 ((struct _websocket_deflate_context *)*context)->defstream.zalloc = u_zalloc;
2114 ((struct _websocket_deflate_context *)*context)->defstream.zfree = u_zfree;
2115 ((struct _websocket_deflate_context *)*context)->defstream.opaque = Z_NULL;
2116 ((struct _websocket_deflate_context *)*context)->infstream.zalloc = u_zalloc;
2117 ((struct _websocket_deflate_context *)*context)->infstream.zfree = u_zfree;
2118 ((struct _websocket_deflate_context *)*context)->infstream.opaque = Z_NULL;
2119
2120 if (deflateInit2(&((struct _websocket_deflate_context *)*context)->defstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -((struct _websocket_deflate_context *)*context)->server_max_window_bits, U_WEBSOCKET_DEFAULT_MEMORY_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
2121 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_server_match_deflate - Error deflateInit2");
2122 deflateEnd(&((struct _websocket_deflate_context *)*context)->defstream);
2123 o_free(*context);
2124 *context = NULL;
2125 ret = U_ERROR;
2126 } else if (inflateInit2(&((struct _websocket_deflate_context *)*context)->infstream, -((struct _websocket_deflate_context *)*context)->client_max_window_bits) != Z_OK) {
2127 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_server_match_deflate - Error inflateInit2");
2128 inflateEnd(&((struct _websocket_deflate_context *)*context)->infstream);
2129 deflateEnd(&((struct _websocket_deflate_context *)*context)->defstream);
2130 o_free(*context);
2131 *context = NULL;
2132 ret = U_ERROR;
2133 }
2134 if (ret == U_OK) {
2135 *extension_server = o_strdup(_U_W_EXT_DEFLATE);
2136 if (((struct _websocket_deflate_context *)*context)->server_no_context_takeover) {
2137 *extension_server = mstrcatf(*extension_server, "; server_no_context_takeover");
2138 }
2139 if (((struct _websocket_deflate_context *)*context)->client_no_context_takeover) {
2140 *extension_server = mstrcatf(*extension_server, "; client_no_context_takeover");
2141 }
2142 if (server_bits_changed) {
2143 *extension_server = mstrcatf(*extension_server, "; server_max_window_bits=%u", ((struct _websocket_deflate_context *)*context)->server_max_window_bits);
2144 }
2145 if (client_bits_changed) {
2146 *extension_server = mstrcatf(*extension_server, "; client_max_window_bits=%u", ((struct _websocket_deflate_context *)*context)->client_max_window_bits);
2147 }
2148 }
2149 } else {
2150 o_free(*context);
2151 *context = NULL;
2152 }
2153 } else {
2154 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_server_match_deflate - Error allocating resources for context");
2155 ret = U_ERROR;
2156 }
2157 } else {
2158 ret = U_ERROR;
2159 }
2160 } else {
2161 ret = U_ERROR;
2162 }
2163 return ret;
2164 }
2165
2166 void websocket_extension_deflate_free_context(void * user_data, void * context) {
2167 (void)user_data;
2168 inflateEnd(&((struct _websocket_deflate_context *)context)->infstream);
2169 deflateEnd(&((struct _websocket_deflate_context *)context)->defstream);
2170 o_free(context);
2171 }
2172
2173 int ulfius_add_websocket_deflate_extension(struct _u_response * response) {
2174 return ulfius_add_websocket_extension_message_perform(response, _U_W_EXT_DEFLATE, U_WEBSOCKET_RSV1, websocket_extension_message_out_deflate, NULL, websocket_extension_message_in_inflate, NULL, &websocket_extension_server_match_deflate, NULL, websocket_extension_deflate_free_context, NULL);
14022175 }
14032176
14042177 /**
14112184 */
14122185 int ulfius_websocket_send_close_signal(struct _websocket_manager * websocket_manager) {
14132186 if (websocket_manager != NULL) {
2187
14142188 websocket_manager->close_flag = 1;
14152189 return U_OK;
14162190 } else {
14402214 int ulfius_websocket_wait_close(struct _websocket_manager * websocket_manager, unsigned int timeout) {
14412215 struct timespec abstime;
14422216 int ret;
1443
2217
14442218 if (websocket_manager != NULL) {
14452219 if (websocket_manager->connected) {
14462220 if (timeout) {
14932267 static char * rand_string(char * str, size_t str_size) {
14942268 const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
14952269 size_t n;
1496
2270
14972271 if (str_size > 0 && str != NULL) {
14982272 for (n = 0; n < str_size; n++) {
14992273 long key = random_at_most((sizeof(charset)) - 2);
15182292 int ret;
15192293 char rand_str[17] = {0}, rand_str_base64[25] = {0};
15202294 size_t out_len;
1521
2295
15222296 if (request != NULL && url != NULL) {
15232297 o_free(request->http_protocol);
15242298 o_free(request->http_verb);
15482322 } else {
15492323 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_set_websocket_request input parameters");
15502324 ret = U_ERROR;
2325 }
2326 return ret;
2327 }
2328
2329 int ulfius_add_websocket_client_extension_message_perform(struct _websocket_client_handler * websocket_client_handler,
2330 const char * extension,
2331 uint8_t rsv,
2332 int (* websocket_extension_message_out_perform)(const uint8_t opcode,
2333 const uint64_t data_len_in,
2334 const char * data_in,
2335 uint64_t * data_len_out,
2336 char ** data_out,
2337 const uint64_t fragment_len,
2338 void * user_data,
2339 void * context),
2340 void * websocket_extension_message_out_perform_user_data,
2341 int (* websocket_extension_message_in_perform)(const uint8_t opcode,
2342 const uint64_t data_len_in,
2343 const char * data_in,
2344 uint64_t * data_len_out,
2345 char ** data_out,
2346 const uint64_t fragment_len,
2347 void * user_data,
2348 void * context),
2349 void * websocket_extension_message_in_perform_user_data,
2350 int (* websocket_extension_client_match)(const char * extension_server,
2351 void * user_data,
2352 void ** context),
2353 void * websocket_extension_client_match_user_data,
2354 void (* websocket_extension_free_context)(void * user_data,
2355 void * context),
2356 void * websocket_extension_free_context_user_data) {
2357 int ret;
2358 struct _websocket_extension * w_extension;
2359
2360 if (websocket_client_handler != NULL && o_strlen(extension) &&
2361 (websocket_extension_message_out_perform != NULL || websocket_extension_message_in_perform != NULL) &&
2362 (rsv == U_WEBSOCKET_RSV1 || rsv == U_WEBSOCKET_RSV2 || rsv == U_WEBSOCKET_RSV3)) {
2363 if (websocket_client_handler->websocket == NULL) {
2364 websocket_client_handler->websocket = o_malloc(sizeof(struct _websocket));
2365 if (websocket_client_handler->websocket == NULL || ulfius_init_websocket(websocket_client_handler->websocket) != U_OK) {
2366 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_client_extension_message_perform - Error ulfius_init_websocket");
2367 return U_ERROR;
2368 }
2369 }
2370 if ((w_extension = o_malloc(sizeof(struct _websocket_extension))) != NULL) {
2371 if (ulfius_init_websocket_extension(w_extension) == U_OK) {
2372 w_extension->extension_client = o_strdup(extension);
2373 w_extension->rsv = rsv;
2374 w_extension->websocket_extension_message_out_perform = websocket_extension_message_out_perform;
2375 w_extension->websocket_extension_message_out_perform_user_data = websocket_extension_message_out_perform_user_data;
2376 w_extension->websocket_extension_message_in_perform = websocket_extension_message_in_perform;
2377 w_extension->websocket_extension_message_in_perform_user_data = websocket_extension_message_in_perform_user_data;
2378 w_extension->websocket_extension_client_match = websocket_extension_client_match;
2379 w_extension->websocket_extension_client_match_user_data = websocket_extension_client_match_user_data;
2380 w_extension->websocket_extension_free_context = websocket_extension_free_context;
2381 w_extension->websocket_extension_free_context_user_data = websocket_extension_free_context_user_data;
2382 if (websocket_client_handler->websocket->websocket_manager->websocket_extension_list == NULL) {
2383 websocket_client_handler->websocket->websocket_manager->websocket_extension_list = o_malloc(sizeof(struct _pointer_list));
2384 if (websocket_client_handler->websocket->websocket_manager->websocket_extension_list != NULL) {
2385 pointer_list_init(websocket_client_handler->websocket->websocket_manager->websocket_extension_list);
2386 } else {
2387 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_client_extension_message_perform - Error allocating resources for struct websocket_extension_list");
2388 return U_ERROR_MEMORY;
2389 }
2390 }
2391 if (pointer_list_append(websocket_client_handler->websocket->websocket_manager->websocket_extension_list, w_extension)) {
2392 ret = U_OK;
2393 } else {
2394 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_client_extension_message_perform - Error pointer_list_append");
2395 ret = U_ERROR;
2396 }
2397 } else {
2398 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_client_extension_message_perform - Error ulfius_init_websocket_extension");
2399 ret = U_ERROR;
2400 }
2401 } else {
2402 y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_add_websocket_client_extension_message_perform - Error allocating resources for struct _websocket_extension");
2403 ret = U_ERROR_MEMORY;
2404 }
2405 } else {
2406 ret = U_ERROR_PARAMS;
15512407 }
15522408 return ret;
15532409 }
15762432 struct yuarel y_url;
15772433 char * url, * basic_auth_encoded_header, * basic_auth, * basic_auth_encoded;
15782434 size_t basic_auth_encoded_len;
1579 struct _websocket * websocket;
2435 struct _websocket * websocket = NULL;
15802436 pthread_t thread_websocket;
15812437 int thread_ret_websocket = 0, thread_detach_websocket = 0;
1582
2438
15832439 if (request != NULL && response != NULL && (websocket_manager_callback != NULL || websocket_incoming_message_callback != NULL)) {
15842440 url = o_strdup(request->http_url);
15852441 if (!yuarel_parse(&y_url, url)) {
16062462 }
16072463 o_free(basic_auth);
16082464 }
1609
1610 websocket = o_malloc(sizeof(struct _websocket));
1611 if (websocket != NULL && ulfius_init_websocket(websocket) == U_OK) {
2465
2466 if (websocket_client_handler->websocket == NULL) {
2467 websocket = o_malloc(sizeof(struct _websocket));
2468 if (ulfius_init_websocket(websocket) != U_OK) {
2469 o_free(websocket);
2470 websocket = NULL;
2471 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for websocket");
2472 ret = U_ERROR_MEMORY;
2473 } else {
2474 websocket_client_handler->websocket = websocket;
2475 }
2476 } else {
2477 websocket = websocket_client_handler->websocket;
2478 }
2479 if (websocket != NULL) {
16122480 websocket->request = ulfius_duplicate_request(request);
16132481 websocket->websocket_manager->type = U_WEBSOCKET_CLIENT;
16142482 websocket->websocket_manager_callback = websocket_manager_callback;
16462514 ulfius_clear_websocket(websocket);
16472515 ret = U_ERROR;
16482516 }
1649 websocket_client_handler->websocket = websocket;
16502517 }
16512518 } else {
1652 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for websocket");
16532519 ret = U_ERROR_MEMORY;
16542520 }
16552521 } else {
16672533 return ret;
16682534 }
16692535
2536 int websocket_extension_client_match_deflate(const char * extension_server, void * user_data, void ** context) {
2537 (void)user_data;
2538 size_t i;
2539 int ret;
2540 char ** parameters = NULL, ** param_value = NULL;
2541 long int window_bits;
2542
2543 if (0 == o_strncmp(extension_server, _U_W_EXT_DEFLATE, o_strlen(_U_W_EXT_DEFLATE))) {
2544 if ((*context = o_malloc(sizeof(struct _websocket_deflate_context))) != NULL) {
2545 ((struct _websocket_deflate_context *)*context)->server_no_context_takeover = 0;
2546 ((struct _websocket_deflate_context *)*context)->client_no_context_takeover = 0;
2547 ((struct _websocket_deflate_context *)*context)->server_max_window_bits = WEBSOCKET_DEFLATE_WINDOWS_BITS;
2548 ((struct _websocket_deflate_context *)*context)->client_max_window_bits = WEBSOCKET_DEFLATE_WINDOWS_BITS;
2549 ((struct _websocket_deflate_context *)*context)->deflate_mask = Z_SYNC_FLUSH;
2550 ((struct _websocket_deflate_context *)*context)->inflate_mask = Z_SYNC_FLUSH;
2551 // Parse extension parameters
2552 ret = U_OK;
2553 if (o_strlen(extension_server) > o_strlen(_U_W_EXT_DEFLATE)) {
2554 if (split_string(extension_server+o_strlen(_U_W_EXT_DEFLATE), ";", &parameters)) {
2555 for (i=0; parameters[i] != NULL; i++) {
2556 if (0 == o_strcmp("server_no_context_takeover", trimwhitespace(parameters[i]))) {
2557 ((struct _websocket_deflate_context *)*context)->server_no_context_takeover = 1;
2558 ((struct _websocket_deflate_context *)*context)->inflate_mask = Z_FULL_FLUSH;
2559 } else if (0 == o_strcmp("client_no_context_takeover", trimwhitespace(parameters[i]))) {
2560 ((struct _websocket_deflate_context *)*context)->client_no_context_takeover = 1;
2561 ((struct _websocket_deflate_context *)*context)->deflate_mask = Z_FULL_FLUSH;
2562 } else if (0 == o_strncmp("server_max_window_bits", trimwhitespace(parameters[i]), o_strlen("server_max_window_bits"))) {
2563 if (split_string(trimwhitespace(parameters[i]), "=", &param_value) == 2) {
2564 window_bits = strtol(param_value[1], NULL, 10);
2565 if (window_bits >= 8 && window_bits <= 15) {
2566 if (window_bits == 8) {
2567 window_bits = 9; // Is really 8, but zlib does not support value 8 so increase to 9 - Morten Houmøller Nygaard
2568 }
2569 ((struct _websocket_deflate_context *)*context)->server_max_window_bits = (uint)window_bits;
2570 } else {
2571 y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_extension_client_match_deflate - Error server_max_window_bits value");
2572 ret = U_ERROR;
2573 }
2574 free_string_array(param_value);
2575 } else {
2576 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_client_match_deflate - Error split_string param_value server_max_window_bits");
2577 ret = U_ERROR;
2578 }
2579 } else if (0 == o_strncmp("client_max_window_bits", trimwhitespace(parameters[i]), o_strlen("client_max_window_bits"))) {
2580 if (split_string(trimwhitespace(parameters[i]), "=", &param_value)) {
2581 if (o_strlen(trimwhitespace(param_value[1]))) {
2582 window_bits = strtol(trimwhitespace(param_value[1]), NULL, 10);
2583 } else {
2584 window_bits = WEBSOCKET_DEFLATE_WINDOWS_BITS;
2585 }
2586 if (window_bits >= 8 && window_bits <= 15) {
2587 if (window_bits == 8) {
2588 window_bits = 9; // Is really 8, but zlib does not support value 8 so increase to 9 - Morten Houmøller Nygaard
2589 }
2590 ((struct _websocket_deflate_context *)*context)->client_max_window_bits = (uint)window_bits;
2591 } else {
2592 y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_extension_client_match_deflate - Error client_max_window_bits value");
2593 ret = U_ERROR;
2594 }
2595 free_string_array(param_value);
2596 } else {
2597 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_client_match_deflate - Error split_string param_value client_max_window_bits");
2598 ret = U_ERROR;
2599 }
2600 } else if (o_strlen(trimwhitespace(parameters[i]))) {
2601 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_client_match_deflate - Invalid parameter");
2602 ret = U_ERROR;
2603 }
2604 }
2605 } else {
2606 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_client_match_deflate - Error split_string parameters");
2607 ret = U_ERROR;
2608 }
2609 free_string_array(parameters);
2610 }
2611 if (ret == U_OK) {
2612 ((struct _websocket_deflate_context *)*context)->defstream.zalloc = u_zalloc;
2613 ((struct _websocket_deflate_context *)*context)->defstream.zfree = u_zfree;
2614 ((struct _websocket_deflate_context *)*context)->defstream.opaque = Z_NULL;
2615 ((struct _websocket_deflate_context *)*context)->infstream.zalloc = u_zalloc;
2616 ((struct _websocket_deflate_context *)*context)->infstream.zfree = u_zfree;
2617 ((struct _websocket_deflate_context *)*context)->infstream.opaque = Z_NULL;
2618
2619 if (deflateInit2(&((struct _websocket_deflate_context *)*context)->defstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -((struct _websocket_deflate_context *)*context)->client_max_window_bits, U_WEBSOCKET_DEFAULT_MEMORY_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) {
2620 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_client_match_deflate - Error deflateInit2");
2621 deflateEnd(&((struct _websocket_deflate_context *)*context)->defstream);
2622 o_free(*context);
2623 *context = NULL;
2624 ret = U_ERROR;
2625 } else if (inflateInit2(&((struct _websocket_deflate_context *)*context)->infstream, -((struct _websocket_deflate_context *)*context)->server_max_window_bits) != Z_OK) {
2626 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_client_match_deflate - Error inflateInit2");
2627 inflateEnd(&((struct _websocket_deflate_context *)*context)->infstream);
2628 deflateEnd(&((struct _websocket_deflate_context *)*context)->defstream);
2629 o_free(*context);
2630 *context = NULL;
2631 ret = U_ERROR;
2632 }
2633 } else {
2634 o_free(*context);
2635 *context = NULL;
2636 }
2637 } else {
2638 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_client_match_deflate - Error allocating resources for context");
2639 ret = U_ERROR;
2640 }
2641 } else {
2642 ret = U_ERROR;
2643 }
2644 return ret;
2645 }
2646
2647 int ulfius_add_websocket_client_deflate_extension(struct _websocket_client_handler * websocket_client_handler) {
2648 return ulfius_add_websocket_client_extension_message_perform(websocket_client_handler, _U_W_EXT_DEFLATE, U_WEBSOCKET_RSV1, websocket_extension_message_out_deflate, NULL, websocket_extension_message_in_inflate, NULL, websocket_extension_client_match_deflate, NULL, websocket_extension_deflate_free_context, NULL);
2649 }
2650
16702651 /**
16712652 * Send a close signal to the websocket
16722653 * return U_OK when the signal is sent
16872668 */
16882669 int ulfius_websocket_client_connection_close(struct _websocket_client_handler * websocket_client_handler) {
16892670 if (websocket_client_handler != NULL) {
1690 if (ulfius_websocket_send_close_signal(websocket_client_handler->websocket->websocket_manager) == U_OK) {
1691 if (ulfius_websocket_wait_close(websocket_client_handler->websocket->websocket_manager, 0) != U_WEBSOCKET_STATUS_CLOSE) {
2671 if (websocket_client_handler->websocket != NULL) {
2672 if (ulfius_websocket_send_close_signal(websocket_client_handler->websocket->websocket_manager) == U_OK) {
2673 if (ulfius_websocket_wait_close(websocket_client_handler->websocket->websocket_manager, 0) != U_WEBSOCKET_STATUS_CLOSE) {
2674 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_send_close_signal");
2675 return U_ERROR;
2676 }
2677 ulfius_clear_websocket(websocket_client_handler->websocket);
2678 return U_OK;
2679 } else {
16922680 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_send_close_signal");
16932681 return U_ERROR;
16942682 }
1695 ulfius_clear_websocket(websocket_client_handler->websocket);
2683 } else {
16962684 return U_OK;
1697 } else {
1698 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_send_close_signal");
1699 return U_ERROR;
17002685 }
17012686 } else {
17022687 return U_ERROR_PARAMS;
17242709 */
17252710 int ulfius_websocket_client_connection_wait_close(struct _websocket_client_handler * websocket_client_handler, unsigned int timeout) {
17262711 int ret;
1727
2712
17282713 if (websocket_client_handler != NULL) {
17292714 ret = ulfius_websocket_wait_close(websocket_client_handler->websocket->websocket_manager, timeout);
17302715 if (ret == U_WEBSOCKET_STATUS_CLOSE && websocket_client_handler->websocket != NULL) {
17312716 ulfius_clear_websocket(websocket_client_handler->websocket);
2717 websocket_client_handler->websocket = NULL;
17322718 }
17332719 return ret;
17342720 } else {
00 /**
1 *
1 *
22 * Ulfius Framework
3 *
3 *
44 * REST framework library
5 *
5 *
66 * ulfius.c: framework functions definitions
7 *
7 *
88 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
99 *
1010 * This program is free software; you can redistribute it and/or
1919 *
2020 * You should have received a copy of the GNU General Public
2121 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 *
22 *
2323 */
2424
2525 #include <string.h>
7272 char * tmp;
7373 int res;
7474 UNUSED(kind);
75
75
7676 if (cls == NULL || key == NULL) {
7777 // Invalid parameters
7878 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid parameters for ulfius_fill_map_check_utf8");
7979 return MHD_NO;
80 } else if (utf8_check(key) == NULL && (value == NULL || utf8_check(value) == NULL)) {
80 } else if (utf8_check(key, o_strlen(key)) == NULL && (value == NULL || utf8_check(value, o_strlen(value)) == NULL)) {
8181 if (u_map_get(((struct _u_map *)cls), key) != NULL) {
8282 // u_map already has a value with this this key, appending value separated with a comma ',')
8383 tmp = msprintf("%s,%s", u_map_get(((struct _u_map *)cls), key), (value==NULL?"":value));
109109 char * tmp;
110110 int res;
111111 UNUSED(kind);
112
112
113113 if (cls == NULL || key == NULL) {
114114 // Invalid parameters
115115 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid parameters for ulfius_fill_map");
204204 void * ulfius_uri_logger (void * cls, const char * uri) {
205205 struct connection_info_struct * con_info = o_malloc (sizeof (struct connection_info_struct));
206206 UNUSED(cls);
207
207
208208 if (con_info != NULL) {
209209 con_info->callback_first_iteration = 1;
210210 con_info->u_instance = NULL;
215215 o_free(con_info);
216216 return NULL;
217217 }
218
218
219219 if (NULL == con_info->request || ulfius_init_request(con_info->request) != U_OK) {
220220 ulfius_clean_request_full(con_info->request);
221221 o_free(con_info);
284284 UNUSED(toe);
285285 UNUSED(connection);
286286 UNUSED(cls);
287
287
288288 if (NULL == con_info) {
289289 return;
290290 }
318318 size_t cur_size = size;
319319 char * data_dup, * filename_param;
320320 UNUSED(kind);
321
321
322322 if (filename != NULL && con_info->u_instance != NULL && con_info->u_instance->file_upload_callback != NULL) {
323323 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) {
324324 return MHD_YES;
327327 }
328328 } else {
329329 if (con_info->u_instance) {
330 if (con_info->u_instance->check_utf8 && (utf8_check(key) != NULL || data == NULL || utf8_check(data) != NULL || (filename != NULL && utf8_check(filename) != NULL))) {
330 if (con_info->u_instance->check_utf8 && (utf8_check(key, o_strlen(key)) != NULL || data == NULL || utf8_check(data, o_strlen(data)) != NULL || (filename != NULL && utf8_check(filename, o_strlen(filename)) != NULL))) {
331331 return MHD_YES;
332332 } else {
333333 data_dup = o_strndup(data, size); // Force value to end with a NULL character
342342 } else {
343343 return MHD_NO;
344344 }
345
345
346346 if (filename != NULL) {
347347 filename_param = msprintf("%s_filename", key);
348348 if (!u_map_has_key((struct _u_map *)con_info->request->map_post_body, filename_param) && u_map_put((struct _u_map *)con_info->request->map_post_body, filename_param, filename) != U_OK) {
350350 }
351351 o_free(filename_param);
352352 }
353
353
354354 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) {
355355 o_free(data_dup);
356356 return MHD_YES;
398398 #ifndef U_DISABLE_WEBSOCKET
399399 // Websocket variables
400400 int upgrade_protocol = 0;
401 char * protocol = NULL, * extension = NULL;
401 char * protocol = NULL, * extension = NULL, ** extension_list = NULL;
402 size_t extension_len, x, y;
402403 #endif
403404
404405 #ifndef U_DISABLE_GNUTLS
412413 char * content_type, * auth_realm = NULL;
413414 struct _u_response * response = NULL;
414415 struct sockaddr * so_client;
415
416
416417 void * response_buffer = NULL;
417418 size_t response_buffer_len = 0;
418
419
419420 // Response variables
420421 struct MHD_Response * mhd_response = NULL;
421
422
422423 UNUSED(url);
423
424
424425 // Prepare for POST or PUT input data
425426 // Initialize the input maps
426427 if (con_info == NULL) {
427428 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error con_info is NULL");
428429 return MHD_NO;
429430 }
430
431
431432 if (con_info->u_instance == NULL) {
432433 con_info->u_instance = (struct _u_instance *)cls;
433434 }
475476 MHD_get_connection_values (connection, MHD_COOKIE_KIND, ulfius_fill_map, con_info->request->map_cookie);
476477 }
477478 content_type = (char*)u_map_get_case(con_info->request->map_header, ULFIUS_HTTP_HEADER_CONTENT);
478
479
479480 // Set POST Processor if content-type is properly set
480 if (content_type != NULL && (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, o_strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) ||
481 if (content_type != NULL && (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, o_strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) ||
481482 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, o_strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))) {
482483 con_info->has_post_processor = 1;
483484 con_info->post_processor = MHD_create_post_processor (connection, ULFIUS_POSTBUFFERSIZE, mhd_iterate_post_data, (void *) con_info);
491492 return MHD_YES;
492493 } else if (*upload_data_size != 0) {
493494 size_t body_len = con_info->request->binary_body_length + *upload_data_size, upload_data_size_current = *upload_data_size;
494
495
495496 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) {
496497 body_len = ((struct _u_instance *)cls)->max_post_body_size;
497498 upload_data_size_current = ((struct _u_instance *)cls)->max_post_body_size - con_info->request->binary_body_length;
498499 }
499
500
500501 if (body_len >= con_info->request->binary_body_length) {
501502 con_info->request->binary_body = o_realloc(con_info->request->binary_body, body_len);
502503 if (con_info->request->binary_body == NULL) {
507508 con_info->request->binary_body_length += upload_data_size_current;
508509 // Handles request binary_body
509510 const char * content_type = u_map_get_case(con_info->request->map_header, ULFIUS_HTTP_HEADER_CONTENT);
510 if (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, o_strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) ||
511 if (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, o_strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) ||
511512 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, o_strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA))) {
512513 MHD_post_process (con_info->post_processor, upload_data, *upload_data_size);
513514 }
520521 } else {
521522 // Check if the endpoint has one or more matches
522523 current_endpoint_list = ulfius_endpoint_match(method, con_info->request->url_path, endpoint_list);
523
524
524525 // Set to default_endpoint if no match
525526 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) {
526527 current_endpoint_list = o_realloc(current_endpoint_list, 2*sizeof(struct _u_endpoint *));
537538 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for current_endpoint_list of default endpoint");
538539 }
539540 }
540
541
541542 #if MHD_VERSION >= 0x00096100
542543 mhd_response_flag = ((struct _u_instance *)cls)->mhd_response_copy_data?MHD_RESPMEM_MUST_COPY:MHD_RESPMEM_MUST_FREE;
543544 #else
558559 u_map_clean_full(response->map_header);
559560 response->map_header = u_map_copy(((struct _u_instance *)cls)->default_headers);
560561 }
561
562
562563 // Initialize auth variables
563564 con_info->request->auth_basic_user = MHD_basic_auth_get_username_password(connection, &con_info->request->auth_basic_password);
564
565
565566 for (i=0; current_endpoint_list[i] != NULL && !close_loop; i++) {
566567 current_endpoint = current_endpoint_list[i];
567568 u_map_empty(con_info->request->map_url);
573574 }
574575 // Run callback function with the input parameters filled for the current callback
575576 callback_ret = current_endpoint->callback_function(con_info->request, response, current_endpoint->user_data);
576 con_info->request->callback_position++;
577 if (callback_ret != U_CALLBACK_IGNORE) {
578 con_info->request->callback_position++;
579 }
577580 if (response->timeout > 0 && MHD_set_connection_option(connection, MHD_CONNECTION_OPTION_TIMEOUT, response->timeout) != MHD_YES) {
578581 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting connection response timeout value");
579582 }
604607 0 == o_strcmp(con_info->request->http_protocol, "HTTP/1.1") &&
605608 0 == o_strcmp(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Version"), "13") &&
606609 0 == o_strcmp(con_info->request->http_verb, "GET")) {
607 int ret_protocol = 0, ret_extensions = 0;
610 int ret_protocol = U_ERROR, ret_extensions = U_OK;
608611 // Check websocket_protocol and websocket_extensions to match ours
609 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 &&
610 (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) {
612 if (u_map_has_key(con_info->request->map_header, "Sec-WebSocket-Extensions") && (extension_len = pointer_list_size(((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list))) {
613 if (split_string(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Extensions"), ",", &extension_list)) {
614 for (x=0; extension_list[x]!=NULL; x++) {
615 for (y=0; y<extension_len; y++) {
616 struct _websocket_extension * ws_ext = (struct _websocket_extension *)pointer_list_get_at(((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list, y);
617 if (ws_ext != NULL && !ws_ext->enabled) {
618 if (ws_ext->websocket_extension_server_match != NULL) {
619 if (ws_ext->websocket_extension_server_match(trimwhitespace(extension_list[x]), (const char **)extension_list, &ws_ext->extension_client, ws_ext->websocket_extension_server_match_user_data, &ws_ext->context) == U_OK) {
620 if (!(ws_ext->rsv & ((struct _websocket_handle *)response->websocket_handle)->rsv_expected)) {
621 ((struct _websocket_handle *)response->websocket_handle)->rsv_expected |= ws_ext->rsv;
622 ws_ext->enabled = 1;
623 if (extension != NULL) {
624 extension = mstrcatf(extension, ", %s", ws_ext->extension_client);
625 } else {
626 extension = o_strdup(ws_ext->extension_client);
627 }
628 }
629 break;
630 }
631 } else {
632 if (0 == o_strcmp(extension_list[x], ws_ext->extension_server)) {
633 if (!(ws_ext->rsv & ((struct _websocket_handle *)response->websocket_handle)->rsv_expected)) {
634 ws_ext->extension_client = o_strdup(extension_list[x]);
635 ((struct _websocket_handle *)response->websocket_handle)->rsv_expected |= ws_ext->rsv;
636 ws_ext->enabled = 1;
637 if (extension != NULL) {
638 extension = mstrcatf(extension, ", %s", ws_ext->extension_client);
639 } else {
640 extension = o_strdup(ws_ext->extension_client);
641 }
642 break;
643 }
644 }
645 }
646 }
647 }
648 }
649 } else {
650 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error split_string Sec-WebSocket-Extensions");
651 }
652 free_string_array(extension_list);
653 } else {
654 ret_extensions = ulfius_check_list_match(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Extensions"), ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions, ",", &extension);
655 }
656 if (ret_extensions == U_OK &&
657 (ret_protocol = ulfius_check_first_match(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Protocol"), ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol, ",", &protocol)) == U_OK) {
611658 char websocket_accept[32] = {0};
612 if (ulfius_generate_handshake_answer(u_map_get(con_info->request->map_header, "Sec-WebSocket-Key"), websocket_accept)) {
659 if (ulfius_generate_handshake_answer(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Key"), websocket_accept)) {
613660 websocket->request = ulfius_duplicate_request(con_info->request);
614661 if (websocket->request != NULL) {
615662 websocket->instance = (struct _u_instance *)cls;
619666 websocket->websocket_incoming_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data;
620667 websocket->websocket_onclose_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback;
621668 websocket->websocket_onclose_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data;
669 websocket->websocket_manager->rsv_expected = ((struct _websocket_handle *)response->websocket_handle)->rsv_expected;
670 websocket->websocket_manager->websocket_extension_list = ((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list;
671 ((struct _websocket_handle *)response->websocket_handle)->websocket_extension_list = NULL;
622672 mhd_response = MHD_create_response_for_upgrade(ulfius_start_websocket_cb, websocket);
623673 if (mhd_response == NULL) {
624674 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_for_upgrade");
633683 MHD_add_response_header (mhd_response,
634684 "Sec-WebSocket-Protocol",
635685 protocol);
686 if (o_strlen(extension)) {
687 MHD_add_response_header (mhd_response,
688 "Sec-WebSocket-Extensions",
689 extension);
690 }
636691 if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) {
637692 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies");
638693 mhd_ret = MHD_NO;
712767 }
713768 #endif
714769 } else {
715 if (callback_ret == U_CALLBACK_CONTINUE && current_endpoint_list[i+1] == NULL) {
716 // If callback_ret is U_CALLBACK_CONTINUE but callback function is the last one on the list
770 if ((callback_ret == U_CALLBACK_CONTINUE || callback_ret == U_CALLBACK_IGNORE) && current_endpoint_list[i+1] == NULL) {
771 // If callback_ret is U_CALLBACK_CONTINUE or U_CALLBACK_IGNORE but callback function is the last one on the list
717772 callback_ret = U_CALLBACK_COMPLETE;
718773 }
719774 // Test callback_ret to know what to do
720775 switch (callback_ret) {
721776 case U_CALLBACK_CONTINUE:
777 case U_CALLBACK_IGNORE:
722778 break;
723779 case U_CALLBACK_COMPLETE:
724780 close_loop = 1;
799855 }
800856 }
801857 }
858
859 if (!con_info->request->callback_position && ((struct _u_instance *)cls)->default_endpoint != NULL && ((struct _u_instance *)cls)->default_endpoint->callback_function != NULL) {
860 callback_ret = ((struct _u_instance *)cls)->default_endpoint->callback_function(con_info->request, response, ((struct _u_instance *)cls)->default_endpoint->user_data);
861 // Test callback_ret to know what to do
862 switch (callback_ret) {
863 case U_CALLBACK_UNAUTHORIZED:
864 // Wrong credentials, send status 401 and realm value if set
865 if (ulfius_get_body_from_response(response, &response_buffer, &response_buffer_len) == U_OK) {
866 mhd_response = MHD_CREATE_RESPONSE_FROM_BUFFER_PIMPED (response_buffer_len, response_buffer, mhd_response_flag );
867 if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) {
868 inner_error = U_ERROR_PARAMS;
869 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies");
870 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR;
871 response->binary_body = o_strdup(ULFIUS_HTTP_ERROR_BODY);
872 response->binary_body_length = o_strlen(ULFIUS_HTTP_ERROR_BODY);
873 if (response->binary_body == NULL) {
874 inner_error = U_ERROR_MEMORY;
875 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body");
876 mhd_ret = MHD_NO;
877 }
878 } else {
879 inner_error = U_CALLBACK_UNAUTHORIZED;
880 }
881 } else {
882 // Error building response, sending error 500
883 response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY);
884 if (response_buffer == NULL) {
885 inner_error = U_ERROR_MEMORY;
886 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer");
887 mhd_ret = MHD_NO;
888 } else {
889 response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY);
890 mhd_response = MHD_CREATE_RESPONSE_FROM_BUFFER_PIMPED (response_buffer_len, response_buffer, mhd_response_flag );
891 inner_error = U_CALLBACK_UNAUTHORIZED;
892 }
893 }
894 if (response->auth_realm != NULL) {
895 auth_realm = response->auth_realm;
896 } else if (((struct _u_instance *)cls)->default_auth_realm != NULL) {
897 auth_realm = ((struct _u_instance *)cls)->default_auth_realm;
898 }
899 break;
900 case U_CALLBACK_ERROR:
901 close_loop = 1;
902 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR;
903 response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY);
904 if (response_buffer == NULL) {
905 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer");
906 mhd_ret = MHD_NO;
907 } else {
908 response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY);
909 mhd_response = MHD_CREATE_RESPONSE_FROM_BUFFER_PIMPED (response_buffer_len, response_buffer, mhd_response_flag );
910 }
911 break;
912 case U_CALLBACK_CONTINUE:
913 case U_CALLBACK_IGNORE:
914 case U_CALLBACK_COMPLETE:
915 if (ulfius_get_body_from_response(response, &response_buffer, &response_buffer_len) == U_OK) {
916 // Build the response binary_body
917 mhd_response = MHD_CREATE_RESPONSE_FROM_BUFFER_PIMPED (response_buffer_len, response_buffer, mhd_response_flag );
918 if (mhd_response == NULL) {
919 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_from_buffer");
920 mhd_ret = MHD_NO;
921 } else if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) {
922 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies");
923 mhd_ret = MHD_NO;
924 }
925 } else {
926 // Error building response, sending error 500
927 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR;
928 response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY);
929 if (response_buffer == NULL) {
930 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer");
931 mhd_ret = MHD_NO;
932 } else {
933 response_buffer_len = o_strlen(ULFIUS_HTTP_ERROR_BODY);
934 mhd_response = MHD_CREATE_RESPONSE_FROM_BUFFER_PIMPED (response_buffer_len, response_buffer, mhd_response_flag );
935 }
936 }
937 break;
938 default:
939 break;
940 }
941 }
942
802943 if (mhd_response != NULL) {
803944 if (auth_realm != NULL && inner_error == U_CALLBACK_UNAUTHORIZED) {
804945 mhd_ret = MHD_queue_basic_auth_fail_response (connection, auth_realm, mhd_response);
851992 * ulfius_run_mhd_daemon
852993 * Starts a mhd daemon for the specified instance
853994 * return a pointer to the mhd_daemon on success, NULL on error
854 *
995 *
855996 */
856997 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) {
857998 unsigned int mhd_flags = MHD_USE_THREAD_PER_CONNECTION;
8661007 #ifndef U_DISABLE_WEBSOCKET
8671008 mhd_flags |= MHD_ALLOW_UPGRADE;
8681009 #endif
869
1010
8701011 if (u_instance->mhd_daemon == NULL) {
8711012 struct MHD_OptionItem mhd_ops[8];
872
1013
8731014 // Default options
8741015 mhd_ops[0].option = MHD_OPTION_NOTIFY_COMPLETED;
8751016 mhd_ops[0].value = (intptr_t)mhd_request_completed;
8761017 mhd_ops[0].ptr_value = NULL;
877
1018
8781019 #if MHD_VERSION >= 0x00095208
8791020 // If bind_address6 is specified, listen only to IPV6 addresses
8801021 if (u_instance->bind_address6 != NULL) {
9001041 mhd_ops[1].value = 0;
9011042 mhd_ops[1].ptr_value = (void *)u_instance->bind_address;
9021043 #endif
903
1044
9041045 mhd_ops[2].option = MHD_OPTION_URI_LOG_CALLBACK;
9051046 mhd_ops[2].value = (intptr_t)ulfius_uri_logger;
9061047 mhd_ops[2].ptr_value = NULL;
907
1048
9081049 index = 3;
9091050
9101051 if (key_pem != NULL && cert_pem != NULL) {
9131054 mhd_ops[index].option = MHD_OPTION_HTTPS_MEM_KEY;
9141055 mhd_ops[index].value = 0;
9151056 mhd_ops[index].ptr_value = (void*)key_pem;
916
1057
9171058 mhd_ops[index + 1].option = MHD_OPTION_HTTPS_MEM_CERT;
9181059 mhd_ops[index + 1].value = 0;
9191060 mhd_ops[index + 1].ptr_value = (void*)cert_pem;
920
1061
9211062 index += 2;
9221063
9231064 if (root_ca_perm != NULL) {
9321073 mhd_ops[index].option = MHD_OPTION_CONNECTION_TIMEOUT;
9331074 mhd_ops[index].value = u_instance->timeout;
9341075 mhd_ops[index].ptr_value = NULL;
935
1076
9361077 index++;
9371078 }
9381079
9411082 mhd_ops[index].ptr_value = NULL;
9421083
9431084 return MHD_start_daemon (
944 mhd_flags, u_instance->port, NULL, NULL, &ulfius_webservice_dispatcher, (void *)u_instance,
1085 mhd_flags, u_instance->port, NULL, NULL, &ulfius_webservice_dispatcher, (void *)u_instance,
9451086 MHD_OPTION_ARRAY, mhd_ops,
9461087 MHD_OPTION_END
9471088 );
9551096 * ulfius_start_framework
9561097 * Initializes the framework and run the webservice based on the parameters given
9571098 * return true if no error
958 *
1099 *
9591100 * u_instance: pointer to a struct _u_instance that describe its port and bind address
9601101 * return U_OK on success
9611102 */
9701111 /**
9711112 * ulfius_start_secure_framework
9721113 * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection
973 *
1114 *
9741115 * u_instance: pointer to a struct _u_instance that describe its port and bind address
9751116 * key_pem: private key for the server
9761117 * cert_pem: server certificate
9831124 #ifndef U_DISABLE_JANSSON
9841125 o_malloc_t malloc_fn;
9851126 o_free_t free_fn;
986
1127
9871128 o_get_alloc_funcs(&malloc_fn, NULL, &free_fn);
9881129 json_set_alloc_funcs((json_malloc_t)malloc_fn, (json_free_t)free_fn);
9891130 #endif
9971138 }
9981139 if (ulfius_validate_instance(u_instance) == U_OK) {
9991140 u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, key_pem, cert_pem, NULL);
1000
1141
10011142 if (u_instance->mhd_daemon == NULL) {
10021143 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting");
10031144 u_instance->status = U_STATUS_ERROR;
10181159 * ulfius_start_secure_ca_trust_framework
10191160 * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection
10201161 * And using a root server to authenticate client connections
1021 *
1162 *
10221163 * u_instance: pointer to a struct _u_instance that describe its port and bind address
10231164 * key_pem: private key for the server
10241165 * cert_pem: server certificate
10291170 #ifndef U_DISABLE_JANSSON
10301171 o_malloc_t malloc_fn;
10311172 o_free_t free_fn;
1032
1173
10331174 o_get_alloc_funcs(&malloc_fn, NULL, &free_fn);
10341175 json_set_alloc_funcs((json_malloc_t)malloc_fn, (json_free_t)free_fn);
10351176 #endif
10511192 }
10521193 if (ulfius_validate_instance(u_instance) == U_OK) {
10531194 u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, key_pem, cert_pem, root_ca_pem);
1054
1195
10551196 if (u_instance->mhd_daemon == NULL) {
10561197 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting");
10571198 u_instance->status = U_STATUS_ERROR;
11071248
11081249 /**
11091250 * ulfius_stop_framework
1110 *
1251 *
11111252 * Stop the webservice
11121253 * u_instance: pointer to a struct _u_instance that describe its port and bind address
11131254 * return U_OK on success
11171258 #ifndef U_DISABLE_WEBSOCKET
11181259 int i;
11191260 // Loop in all active websockets and send close signal
1120 for (i=((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active-1; i>=0; i--) {
1121 ((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active[i]->websocket_manager->close_flag = 1;
1261 if (pthread_mutex_lock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active_lock)) {
1262 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error locking websocket websocket_active_lock");
1263 } else {
1264 for (i=((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active-1; i>=0; i--) {
1265 ((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active[i]->websocket_manager->close_flag = 1;
1266 }
1267 pthread_mutex_unlock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active_lock);
11221268 }
11231269 pthread_mutex_lock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock);
11241270 while (((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active > 0) {
11251271 pthread_cond_wait(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, &((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock);
11261272 }
11271273 pthread_mutex_unlock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock);
1128 #endif
1274 #endif
11291275 MHD_stop_daemon (u_instance->mhd_daemon);
11301276 u_instance->mhd_daemon = NULL;
11311277 u_instance->status = U_STATUS_STOP;
11671313 struct _u_endpoint * ulfius_duplicate_endpoint_list(const struct _u_endpoint * endpoint_list) {
11681314 struct _u_endpoint * to_return = NULL;
11691315 int i;
1170
1316
11711317 if (endpoint_list != NULL) {
11721318 for (i=0; endpoint_list[i].http_method != NULL; i++) {
11731319 if ((to_return = o_realloc(to_return, (i+1)*sizeof(struct _u_endpoint *))) == NULL) {
12021348 */
12031349 void ulfius_clean_endpoint_list(struct _u_endpoint * endpoint_list) {
12041350 int i;
1205
1351
12061352 if (endpoint_list != NULL) {
12071353 for (i=0; endpoint_list[i].http_method != NULL; i++) {
12081354 ulfius_clean_endpoint(&endpoint_list[i]);
12111357 }
12121358 }
12131359
1214 /**
1215 * Add a struct _u_endpoint * to the specified u_instance
1216 * Can be done during the execution of the webservice for injection
1217 * u_instance: pointer to a struct _u_instance that describe its port and bind address
1218 * u_endpoint: pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list
1219 * return U_OK on success
1220 */
12211360 int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint) {
12221361 int res;
1223
1362
12241363 if (u_instance != NULL && u_endpoint != NULL) {
12251364 if (ulfius_is_valid_endpoint(u_endpoint, 0)) {
12261365 if (u_instance->endpoint_list == NULL) {
12581397 return U_ERROR;
12591398 }
12601399
1261 /**
1262 * Add a struct _u_endpoint * list to the specified u_instance
1263 * Can be done during the execution of the webservice for injection
1264 * u_instance: pointer to a struct _u_instance that describe its port and bind address
1265 * u_endpoint_list: pointer to a struct _u_endpoint that will be copied in the u_instance endpoint_list
1266 * return U_OK on success
1267 */
12681400 int ulfius_add_endpoint_list(struct _u_instance * u_instance, const struct _u_endpoint ** u_endpoint_list) {
12691401 int i, res;
12701402 if (u_instance != NULL && u_endpoint_list != NULL) {
12821414 return U_ERROR;
12831415 }
12841416
1285 /**
1286 * Remove a struct _u_endpoint * from the specified u_instance
1287 * Can be done during the execution of the webservice for injection
1288 * u_instance: pointer to a struct _u_instance that describe its port and bind address
1289 * u_endpoint: pointer to a struct _u_endpoint that will be removed in the u_instance endpoint_list
1290 * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match
1291 * If no endpoint is found, return U_ERROR_NOT_FOUND
1292 * return U_OK on success
1293 */
12941417 int ulfius_remove_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint) {
12951418 int i, j, found = 0, ret = U_OK;
12961419 char * trim_prefix = NULL, * trim_prefix_save = NULL, * trim_format = NULL, * trim_format_save = NULL,
13051428 trim_cur_prefix = trimcharacter(trim_cur_prefix_save, '/');
13061429 trim_cur_format_save = o_strdup(u_instance->endpoint_list[i].url_format);
13071430 trim_cur_format = trimcharacter(trim_cur_format_save, '/');
1308
1431
13091432 // Compare u_endpoint with u_instance->endpoint_list[i]
13101433 if (0 == o_strcmp(u_instance->endpoint_list[i].http_method, u_endpoint->http_method) &&
13111434 0 == o_strcmp(trim_cur_prefix, trim_prefix) &&
13451468 return ret;
13461469 }
13471470
1348 /**
1349 * ulfius_empty_endpoint
1350 * return an empty endpoint that goes at the end of an endpoint list
1351 */
13521471 const struct _u_endpoint * ulfius_empty_endpoint() {
13531472 static struct _u_endpoint empty_endpoint;
1354
1473
13551474 empty_endpoint.http_method = NULL;
13561475 empty_endpoint.url_prefix = NULL;
13571476 empty_endpoint.url_format = NULL;
13601479 return &empty_endpoint;
13611480 }
13621481
1363 /**
1364 * ulfius_equals_endpoints
1365 * Compare 2 endpoints and return true if their method, prefix and format are the same or if both are NULL
1366 */
13671482 int ulfius_equals_endpoints(const struct _u_endpoint * endpoint1, const struct _u_endpoint * endpoint2) {
13681483 if (endpoint1 != NULL && endpoint2 != NULL) {
13691484 if (endpoint1 == endpoint2) {
13821497 }
13831498 }
13841499
1385 /**
1386 * Add a struct _u_endpoint * to the specified u_instance with its values specified
1387 * Can be done during the execution of the webservice for injection
1388 * u_instance: pointer to a struct _u_instance that describe its port and bind address
1389 * http_method: http verb (GET, POST, PUT, etc.) in upper case
1390 * url_prefix: prefix for the url (optional)
1391 * url_format: string used to define the endpoint format
1392 * separate words with /
1393 * to define a variable in the url, prefix it with @ or :
1394 * example: /test/resource/:name/elements
1395 * on an url_format that ends with '*', the rest of the url will not be tested
1396 * priority: endpoint priority in descending order (0 is the higher priority)
1397 * callback_function: a pointer to a function that will be executed each time the endpoint is called
1398 * you must declare the function as described.
1399 * user_data: a pointer to a data or a structure that will be available in callback_function
1400 * return U_OK on success
1401 */
14021500 int ulfius_add_endpoint_by_val(struct _u_instance * u_instance,
14031501 const char * http_method,
14041502 const char * url_prefix,
14221520 }
14231521 }
14241522
1425 /**
1426 * Remove a struct _u_endpoint * from the specified u_instance
1427 * using the specified values used to identify an endpoint
1428 * Can be done during the execution of the webservice for injection
1429 * u_instance: pointer to a struct _u_instance that describe its port and bind address
1430 * http_method: http_method used by the endpoint
1431 * url_prefix: url_prefix used by the endpoint
1432 * url_format: url_format used by the endpoint
1433 * The parameters _u_endpoint.http_method, _u_endpoint.url_prefix and _u_endpoint.url_format are strictly compared for the match
1434 * If no endpoint is found, return U_ERROR_NOT_FOUND
1435 * return U_OK on success
1436 */
14371523 int ulfius_remove_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format) {
14381524 struct _u_endpoint endpoint;
14391525 if (u_instance != NULL) {
14471533 }
14481534 }
14491535
1450 /**
1451 * ulfius_set_default_endpoint
1452 * Set the default endpoint
1453 * This endpoint will be called if no endpoint match the url called
1454 * callback_function: a pointer to a function that will be executed each time the endpoint is called
1455 * you must declare the function as described.
1456 * user_data: a pointer to a data or a structure that will be available in callback_function
1457 * to remove a default endpoint function, call ulfius_set_default_endpoint with NULL parameter for callback_function
1458 * return U_OK on success
1459 */
14601536 int ulfius_set_default_endpoint(struct _u_instance * u_instance,
14611537 int (* callback_function)(const struct _u_request * request, struct _u_response * response, void * user_data),
14621538 void * user_data) {
14801556 }
14811557 }
14821558
1483 /**
1484 * ulfius_set_upload_file_callback_function
1485 *
1486 * Set the callback function to handle file upload
1487 * Used to facilitate large files upload management
1488 * The callback function file_upload_callback will be called
1489 * multiple times, with the uploaded file in striped in parts
1490 *
1491 * Warning: If this function is used, all the uploaded files
1492 * for the instance will be managed via this function, and they
1493 * will no longer be available in the struct _u_request in the
1494 * ulfius callback function afterwards.
1495 *
1496 * Thanks to Thad Phetteplace for the help on this feature
1497 *
1498 * u_instance: pointer to a struct _u_instance that describe its port and bind address
1499 * file_upload_callback: Pointer to a callback function that will handle all file uploads
1500 * cls: a pointer that will be passed to file_upload_callback each tim it's called
1501 */
15021559 int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance,
1503 int (* file_upload_callback) (const struct _u_request * request,
1504 const char * key,
1505 const char * filename,
1506 const char * content_type,
1507 const char * transfer_encoding,
1508 const char * data,
1509 uint64_t off,
1510 size_t size,
1560 int (* file_upload_callback) (const struct _u_request * request,
1561 const char * key,
1562 const char * filename,
1563 const char * content_type,
1564 const char * transfer_encoding,
1565 const char * data,
1566 uint64_t off,
1567 size_t size,
15111568 void * cls),
15121569 void * cls) {
15131570 if (u_instance != NULL && file_upload_callback != NULL) {
15191576 }
15201577 }
15211578
1522 /**
1523 * ulfius_clean_instance
1524 *
1525 * Clean memory allocated by a struct _u_instance *
1526 */
15271579 void ulfius_clean_instance(struct _u_instance * u_instance) {
15281580 if (u_instance != NULL) {
15291581 ulfius_clean_endpoint_list(u_instance->endpoint_list);
15381590 #ifndef U_DISABLE_WEBSOCKET
15391591 /* ulfius_clean_instance might be called without websocket_handler being initialized */
15401592 if ((struct _websocket_handler *)u_instance->websocket_handler) {
1541 if (((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init &&
1542 (pthread_mutex_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock) ||
1543 pthread_cond_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond))) {
1544 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error destroying websocket_close_lock or websocket_close_cond");
1545 }
1546 o_free(u_instance->websocket_handler);
1547 u_instance->websocket_handler = NULL;
1548 }
1549 #endif
1550 }
1551 }
1552
1553 /**
1554 * internal_ulfius_init_instance
1555 *
1556 * Initialize a struct _u_instance * with default values
1557 * internal function used by both ulfius_init_instance and ulfius_init_instance_ipv6
1558 * port: tcp port to bind to, must be between 1 and 65535
1559 * bind_address4: IPv4 address to listen to, optional, the reference is borrowed, the structure isn't copied
1560 * bind_address6: IPv6 address to listen to, optional, the reference is borrowed, the structure isn't copied
1561 * network_type: Type of network to listen to, values available are U_USE_IPV4, U_USE_IPV6 or U_USE_ALL
1562 * default_auth_realm: default realm to send to the client on authentication error
1563 * return U_OK on success
1564 */
1565 static int internal_ulfius_init_instance(struct _u_instance * u_instance, unsigned int port, struct sockaddr_in * bind_address4, struct sockaddr_in6 * bind_address6, unsigned short network_type, const char * default_auth_realm) {
1593 if (((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init &&
1594 (pthread_mutex_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock) ||
1595 pthread_cond_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond) ||
1596 pthread_mutex_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active_lock))) {
1597 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error destroying websocket_close_lock or websocket_close_cond or websocket_active_lock");
1598 }
1599 o_free(u_instance->websocket_handler);
1600 u_instance->websocket_handler = NULL;
1601 }
1602 #endif
1603 }
1604 }
1605
1606 static int internal_ulfius_init_instance(struct _u_instance * u_instance,
1607 unsigned int port,
1608 struct sockaddr_in * bind_address4,
1609 struct sockaddr_in6 * bind_address6,
1610 unsigned short network_type,
1611 const char * default_auth_realm) {
1612 #ifndef U_DISABLE_WEBSOCKET
1613 pthread_mutexattr_t mutexattr;
1614 #endif
15661615 #if MHD_VERSION >= 0x00095208
15671616 if (u_instance != NULL && port > 0 && port < 65536 && (bind_address4 == NULL || bind_address6 == NULL) && (network_type & U_USE_ALL)) {
15681617 #else
16051654 ulfius_clean_instance(u_instance);
16061655 return U_ERROR_MEMORY;
16071656 }
1657 pthread_mutexattr_init ( &mutexattr );
1658 pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE );
1659 if (pthread_mutex_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active_lock, &mutexattr) != 0) {
1660 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing websocket_active_lock");
1661 ulfius_clean_instance(u_instance);
1662 return U_ERROR;
1663 }
1664 pthread_mutexattr_destroy(&mutexattr);
16081665 ((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init = 0;
16091666 ((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active = 0;
16101667 ((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active = NULL;
1611 if (pthread_mutex_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock, NULL) ||
1668 if (pthread_mutex_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock, NULL) ||
16121669 pthread_cond_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, NULL)) {
16131670 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing websocket_close_lock or websocket_close_cond");
16141671 ulfius_clean_instance(u_instance);
16241681 }
16251682 }
16261683
1627 /**
1628 * ulfius_init_instance
1629 *
1630 * Initialize a struct _u_instance * with default values
1631 * Binds to IPV4 addresses only
1632 * port: tcp port to bind to, must be between 1 and 65535
1633 * bind_address: IPv4 address to listen to, optional, the reference is borrowed, the structure isn't copied
1634 * default_auth_realm: default realm to send to the client on authentication error
1635 * return U_OK on success
1636 */
16371684 int ulfius_init_instance(struct _u_instance * u_instance, unsigned int port, struct sockaddr_in * bind_address, const char * default_auth_realm) {
16381685 return internal_ulfius_init_instance(u_instance, port, bind_address, NULL, U_USE_IPV4, default_auth_realm);
16391686 }
16401687
16411688 #if MHD_VERSION >= 0x00095208
1642 /**
1643 * ulfius_init_instance_ipv6
1644 *
1645 * Initialize a struct _u_instance * with default values
1646 * Binds to IPV6 and IPV4 or IPV6 addresses only
1647 * port: tcp port to bind to, must be between 1 and 65535
1648 * bind_address: IPv6 address to listen to, optional, the reference is borrowed, the structure isn't copied
1649 * network_type: Type of network to listen to, values available are U_USE_IPV6 or U_USE_ALL
1650 * default_auth_realm: default realm to send to the client on authentication error
1651 * return U_OK on success
1652 */
16531689 int ulfius_init_instance_ipv6(struct _u_instance * u_instance, unsigned int port, struct sockaddr_in6 * bind_address, unsigned short network_type, const char * default_auth_realm) {
16541690 if (network_type & U_USE_IPV6) {
16551691 return internal_ulfius_init_instance(u_instance, port, NULL, bind_address, bind_address!=NULL?U_USE_IPV6:network_type, default_auth_realm);
16591695 }
16601696 #endif
16611697
1662 /**
1663 * free data allocated by ulfius functions
1664 */
16651698 void u_free(void * data) {
16661699 o_free(data);
16671700 }
16681701
16691702 /**
1670 * The utf8_check() function scans the '\0'-terminated string starting
1703 * The utf8_check() function scans the string starting
16711704 * at s. It returns a pointer to the first byte of the first malformed
16721705 * or overlong UTF-8 sequence found, or NULL if the string contains
16731706 * only correct UTF-8. It also spots UTF-8 sequences that could cause
16821715 * are no doubt performance optimizations possible for certain CPUs.
16831716 *
16841717 * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30
1718 * Nicolas Mora <mail@babelouest.org>
16851719 * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html
16861720 */
1687 const unsigned char * utf8_check(const char * s_orig) {
1721 const unsigned char * utf8_check(const char * s_orig, size_t len) {
16881722 const unsigned char * s = (unsigned char *)s_orig;
1689 while (*s) {
1723 size_t i = 0;
1724
1725 while (i<len) {
16901726 if (*s < 0x80) {
16911727 /* 0xxxxxxx */
16921728 s++;
1729 i++;
16931730 } else if ((s[0] & 0xe0) == 0xc0) {
16941731 /* 110XXXXx 10xxxxxx */
1695 if ((s[1] & 0xc0) != 0x80 ||
1732 if ((i+1 >= len) ||
1733 (s[1] & 0xc0) != 0x80 ||
16961734 (s[0] & 0xfe) == 0xc0) { /* overlong? */
16971735 return s;
16981736 } else {
16991737 s += 2;
1738 i += 2;
17001739 }
17011740 } else if ((s[0] & 0xf0) == 0xe0) {
17021741 /* 1110XXXX 10Xxxxxx 10xxxxxx */
1703 if ((s[1] & 0xc0) != 0x80 ||
1704 (s[2] & 0xc0) != 0x80 ||
1705 (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
1706 (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
1707 (s[0] == 0xef && s[1] == 0xbf &&
1708 (s[2] & 0xfe) == 0xbe)) { /* U+FFFE or U+FFFF? */
1742 if ((i+2 >= len) ||
1743 (s[1] & 0xc0) != 0x80 ||
1744 (s[2] & 0xc0) != 0x80 ||
1745 (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
1746 (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
1747 (s[0] == 0xef && s[1] == 0xbf && (s[2] & 0xfe) == 0xbe && /* U+FFFE or U+FFFF? */
1748 s[2] != 0xbf && s[2] != 0xbe)) { /* Hideous hack to comply with autobahn testsuite, TODO: fix that one day (and other jokes I tell myself) */
17091749 return s;
17101750 } else {
17111751 s += 3;
1752 i += 3;
17121753 }
17131754 } else if ((s[0] & 0xf8) == 0xf0) {
17141755 /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
1715 if ((s[1] & 0xc0) != 0x80 ||
1716 (s[2] & 0xc0) != 0x80 ||
1717 (s[3] & 0xc0) != 0x80 ||
1718 (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
1719 (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) { /* > U+10FFFF? */
1756 if ((i+3 >= len) ||
1757 (s[1] & 0xc0) != 0x80 ||
1758 (s[2] & 0xc0) != 0x80 ||
1759 (s[3] & 0xc0) != 0x80 ||
1760 (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
1761 (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) { /* > U+10FFFF? */
17201762 return s;
17211763 } else {
17221764 s += 4;
1765 i += 4;
17231766 }
17241767 } else {
17251768 return s;
17261769 }
17271770 }
1728
17291771 return NULL;
17301772 }
17311773
17601802 // "$-_.+!*'(),"
17611803 if (isalnum(* pstr) || * pstr == '$' || * pstr == '-' || * pstr == '_' ||
17621804 * pstr == '.' || * pstr == '!' || * pstr == '*' ||
1763 * pstr == '\'' || * pstr == '(' || * pstr == ')' || * pstr == ',')
1805 * pstr == '\'' || * pstr == '(' || * pstr == ')' || * pstr == ',')
17641806 * pbuf++ = * pstr;
1765 else if (* pstr == ' ')
1807 else if (* pstr == ' ')
17661808 * pbuf++ = '+';
1767 else
1809 else
17681810 * pbuf++ = '%', * pbuf++ = to_hex(* pstr >> 4), * pbuf++ = to_hex(* pstr & 15);
17691811 pstr++;
17701812 }
17961838 * pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
17971839 pstr += 2;
17981840 }
1799 } else if (* pstr == '+') {
1841 } else if (* pstr == '+') {
18001842 * pbuf++ = ' ';
18011843 } else {
18021844 * pbuf++ = * pstr;
18181860 o_malloc_t malloc_fn;
18191861 o_realloc_t realloc_fn;
18201862 o_free_t free_fn;
1821
1863
18221864 o_get_alloc_funcs(&malloc_fn, &realloc_fn, &free_fn);
18231865 #ifndef U_DISABLE_CURL
18241866 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
66 ULFIUS_LIBRARY=$(ULFIUS_LOCATION)/libulfius.so
77 CC=gcc
88 CFLAGS+=-Wall -D_REENTRANT -I$(ULFIUS_INCLUDE) -DDEBUG -g -O0 $(CPPFLAGS)
9 LIBS=-lc -lorcania -lulfius -lyder -ljansson -lgnutls $(shell pkg-config --libs check) -L$(ULFIUS_LOCATION)
9 LIBS=-lc -lorcania -lulfius -lyder -ljansson -lgnutls -lz $(shell pkg-config --libs check) -L$(ULFIUS_LOCATION)
1010 # Use this LIBS below if you don't have/need gnutls
1111 #LIBS=-lc -lorcania -lyder -lulfius -lcheck -lpthread -lm -lrt -lsubunit -L$(ULFIUS_LOCATION)
1212 # Use this LIBS below if you use yder logs
5151 memcheck: $(ULFIUS_LIBRARY) u_map core framework websocket
5252 -CK_FORK=no LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} $(VALGRIND_COMMAND) ./u_map 2>valgrind-u_map.txt
5353 -CK_FORK=no LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} $(VALGRIND_COMMAND) ./core 2>valgrind-core.txt
54 # test framework seems not to work properly when CK_FORK=no is used, so using it old school with forks then
54 # framework ans websocket don't work properly when CK_FORK=no is used, so using them old school with forks then
5555 -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} $(VALGRIND_COMMAND) ./framework 2>valgrind-framework.txt
56 -CK_FORK=no LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} $(VALGRIND_COMMAND) ./websocket 2>valgrind-websocket.txt
56 -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} $(VALGRIND_COMMAND) ./websocket 2>valgrind-websocket.txt
0 ulfius_ws_echo_client
1 ulfius_ws_echo_server
2 *.pid
3 generated/
0 ulfius_ws_echo_client
1 ulfius_ws_echo_server
2 *.pid
3 generated/
0 FROM pypy:2
1
2 MAINTAINER WSServer Project <morten@mortz.dk>
3
4 # Application home
5 ENV HOME /app
6 ENV DEBIAN_FRONTEND noninteractive
7 ENV NODE_PATH /usr/local/lib/node_modules/
8
9 # make "pypy" available as "python"
10 RUN ln -s /usr/local/bin/pypy /usr/local/bin/python
11
12 # install Autobahn|Testsuite
13 RUN pip install -U pip && pip install autobahntestsuite
0 FROM ulfius/autobahn:base
1
2 MAINTAINER WSServer Project <morten@mortz.dk>
3
4 # make volumes for input configuration and output generated
5 VOLUME /config
6 VOLUME /generated
7
8 WORKDIR /
9 EXPOSE 9001 9001
10
11 CMD ["wstest", "--mode", "fuzzingserver", "--spec", "/config/fuzzingserver-http.json"]
0 FROM ulfius/autobahn:base
1
2 MAINTAINER WSServer Project <morten@mortz.dk>
3
4 # make volumes for input configuration and output generated
5 VOLUME /config
6 VOLUME /generated
7
8 WORKDIR /
9 EXPOSE 9001 9001
10
11 CMD ["wstest", "--mode", "fuzzingserver", "--spec", "/config/fuzzingserver-https.json"]
0 FROM ulfius/autobahn:base
1
2 MAINTAINER WSServer Project <morten@mortz.dk>
3
4 # make volumes for input configuration and output generated
5 VOLUME /config
6 VOLUME /generated
7
8 WORKDIR /
9 EXPOSE 9001 9001
10
11 CMD ["wstest", "--mode", "fuzzingclient", "--spec", "/config/fuzzingclient.json"]
0 SHELL := /bin/bash # Use bash syntax
1 CC=gcc
2 ROOT = $(shell pwd)
3 CONF_FOLDER = $(ROOT)/conf
4 GEN_FOLDER = $(ROOT)/generated
5
6 ULFIUS_INCLUDE=../../include
7 ULFIUS_LOCATION=../../src
8 CERT_LOCATION=../cert
9
10 KEY_FILE=server.key
11 CERT_FILE=server.crt
12
13 CFLAGS+=-Wall -I$(ULFIUS_INCLUDE) $(ADDITIONALFLAGS) $(CPPFLAGS)
14 LIBS=-lc -lulfius -lorcania -lyder -lpthread $(LYDER) -L$(ULFIUS_LOCATION)
15
16 clean:
17 rm -f ulfius_ws_echo_server ulfius_ws_echo_client ulfius_ws_echo_server.log ulfius_ws_echo_client.log $(CONF_FOLDER)/$(KEY_FILE) $(CONF_FOLDER)/$(CERT_FILE) $(GEN_FOLDER)/server $(GEN_FOLDER)/client-http $(GEN_FOLDER)/client-https
18 -docker rmi -f pypy:2 crossbario/autobahn-testsuite:latest ulfius/autobahn:base ulfius/autobahn:server ulfius/autobahn:client-http ulfius/autobahn:client-https
19 -docker system prune -f
20
21 test: autobahn-server autobahn-client-http
22
23 all: test
24
25 $(ULFIUS_LOCATION)/libulfius.so:
26 cd $(ULFIUS_LOCATION) && $(MAKE) debug $*
27
28 ulfius_ws_echo_server: ulfius_ws_echo_server.c $(ULFIUS_LOCATION)/libulfius.so
29 $(CC) -o ulfius_ws_echo_server ulfius_ws_echo_server.c $(CFLAGS) $(LIBS)
30
31 ulfius_ws_echo_client: ulfius_ws_echo_client.c $(ULFIUS_LOCATION)/libulfius.so
32 $(CC) -o ulfius_ws_echo_client ulfius_ws_echo_client.c $(CFLAGS) $(LIBS)
33
34 docker-autobahn:
35 docker build -t ulfius/autobahn:base -f Dockerfile .
36
37 docker-autobahn-server: docker-autobahn
38 docker build -t ulfius/autobahn:server -f Dockerfile.server .
39
40 docker-autobahn-client-http: docker-autobahn
41 docker build -t ulfius/autobahn:client-http -f Dockerfile.client-http .
42
43 docker-autobahn-client-https: docker-autobahn
44 docker build -t ulfius/autobahn:client-https -f Dockerfile.client-https .
45
46 autobahn-server: docker-autobahn-server ulfius_ws_echo_server
47 #rm -rf $(GEN_FOLDER)/server
48 #./ulfius_ws_echo_server $(CERT_LOCATION)/$(KEY_FILE) $(CERT_LOCATION)/$(CERT_FILE) & echo $$! > ulfius_ws_echo_server.pid
49 #sleep 2
50 #docker run -it --rm \
51 #--network="host" \
52 # -v ${CONF_FOLDER}:/config \
53 # -v ${GEN_FOLDER}:/generated \
54 # -p 9001:9001 \
55 # --name fuzzingclient \
56 # ulfius/autobahn:server
57 #kill `cat ulfius_ws_echo_server.pid` && rm -f ulfius_ws_echo_server.pid
58 @echo
59 @echo ===============================================================
60 @echo = MANUAL RUN UNTIL I FIGURE OUT HOW TO RUN THIS AUTOMATICALLY =
61 @echo ===============================================================
62 @echo "To run Autobahn websocket server tests, execute the following commands"
63 @echo "Remove ./generated/server directory if exists"
64 @echo "In a console, run ulfius_ws_echo_server using https certificate in test/cert"
65 @echo '$$ ./ulfius_ws_echo_server ../cert/server.key ../cert/server.crt'
66 @echo "In another console, run docker image ulfius/autobahn:server to execute the tests"
67 @echo '$$ docker run -it --rm --network="host" -v $${PWD}/conf:/config -v $${PWD}/generated:/generated -p 9001:9001 --name fuzzingclient ulfius/autobahn:server'
68
69 autobahn-client-http: docker-autobahn-client-http ulfius_ws_echo_client
70 #rm -rf $(GEN_FOLDER)/client-http
71 #docker run -d --rm \
72 # -v ${CONF_FOLDER}:/config \
73 # -v ${GEN_FOLDER}:/generated \
74 # -p 9001:9001 \
75 # --name client_http \
76 # ulfius/autobahn:client-http
77 #sleep 5
78 #I=1 ; while [[ $$I -le 10 ]] ; do \
79 # echo "Run test case $$I"; \
80 # ./ulfius_ws_echo_client $$I; \
81 # ((I = I + 1)) ; \
82 #done;
83 #uwsc -s -q -i http://localhost:9001/updateReports?agent=ulfius
84 #docker kill client_http
85 @echo
86 @echo ===============================================================
87 @echo = MANUAL RUN UNTIL I FIGURE OUT HOW TO RUN THIS AUTOMATICALLY =
88 @echo ===============================================================
89 @echo "To run Autobahn websocket client HTTP tests, execute the following commands"
90 @echo "Remove ./generated/client-http directory if exists"
91 @echo "In a console, run docker image ulfius/autobahn:client-http to start the websocket server test"
92 @echo '$$ docker run -it --rm -v $${PWD}/conf:/config -v $${PWD}/generated:/generated -p 9001:9001 --name client_http ulfius/autobahn:client-http'
93 @echo "In another console, run ulfius_ws_echo_client with the test case number as argument (between 1 and 519)"
94 @echo "To run all test cases run the following command"
95 @echo '$$ for I in {1..519}; do echo Run test $$I && ./ulfius_ws_echo_client $$I; done'
96 @echo "After executing all test cases, generate the report with the uwsc command:"
97 @echo '$$ uwsc -s -q -i http://localhost:9001/updateReports?agent=ulfius'
98 @echo "Then close the websocket server"
99 @echo "docker kill client_http"
100
101 autobahn-client-https: docker-autobahn-client-https ulfius_ws_echo_client
102 #rm -rf $(GEN_FOLDER)/client-https
103 #if [[ ! -f $(CONF_FOLDER)/$(KEY_FILE) ]]; then \
104 # cp $(CERT_LOCATION)/$(KEY_FILE) $(CONF_FOLDER); \
105 #fi
106 #if [[ ! -f $(CONF_FOLDER)/$(CERT_FILE) ]]; then \
107 # cp $(CERT_LOCATION)/$(CERT_FILE) $(CONF_FOLDER); \
108 #fi
109 #docker run -d --rm \
110 # -v ${CONF_FOLDER}:/config \
111 # -v ${GEN_FOLDER}:/generated \
112 # -p 9001:9001 \
113 # --name client_https \
114 # ulfius/autobahn:client-https
115 #sleep 5
116 #I=1 ; while [[ $$I -le 519 ]] ; do \
117 # echo "Run test case $$I"; \
118 # ./ulfius_ws_echo_client $$I -https; \
119 # ((I = I + 1)) ; \
120 #done;
121 #uwsc -s -q -i https://localhost:9001/updateReports?agent=ulfius
122 #docker kill client_https
123 @echo
124 @echo ===============================================================
125 @echo = MANUAL RUN UNTIL I FIGURE OUT HOW TO RUN THIS AUTOMATICALLY =
126 @echo ===============================================================
127 @echo "To run Autobahn websocket client HTTPS tests, execute the following commands"
128 @echo "Remove ./generated/client-https directory if exists"
129 @echo "Copy test/cert/server.* files to test/autobahn/conf/"
130 @echo '$$ cp ../cert/server.* conf/'
131 @echo "In a console, run docker image ulfius/autobahn:client-https to start the websocket server test"
132 @echo '$$ docker run -it --rm -v $${PWD}/conf:/config -v $${PWD}/generated:/generated -p 9001:9001 --name client_https ulfius/autobahn:client-https'
133 @echo "In another console, run ulfius_ws_echo_client with the test case number as argument (between 1 and 519) and the argument -https"
134 @echo "To run all test cases run the following command"
135 @echo '$$ for I in {1..519}; do echo Run test $$I && ./ulfius_ws_echo_client $$I -https; done'
136 @echo "After executing all test cases, generate the report with the uwsc command:"
137 @echo '$$ uwsc -s -q -i https://localhost:9001/updateReports?agent=ulfius'
138 @echo "Then close the websocket server"
139 @echo "docker kill client_https"
0 {
1 "outdir": "./generated/server",
2 "servers": [
3 {
4 "agent": "WSServer (HTTP)",
5 "url": "ws://127.0.0.1:9010",
6 "desc" : "WSServer through the HTTP protocol",
7 "options": {"version": 18}
8 },
9 {
10 "agent": "WSServer (HTTPS)",
11 "url": "wss://127.0.0.1:9011",
12 "desc" : "WSServer through the HTTPS protocol",
13 "options": {"version": 18}
14 }
15 ],
16 "cases": [
17 "*"
18 ],
19 "exclude-cases": [],
20 "exclude-agent-cases": {}
21 }
0 {
1 "url": "ws://127.0.0.1:9001",
2
3 "options": {"failByDrop": false},
4 "outdir": "./generated/client-http",
5 "webport": 8080,
6
7 "cases": ["*"],
8 "exclude-cases": [],
9 "exclude-agent-cases": {}
10 }
0 {
1 "url": "wss://127.0.0.1:9001",
2 "key": "/config/server.key",
3 "cert": "/config/server.crt",
4
5 "options": {"failByDrop": false},
6 "outdir": "./generated/client-https",
7 "webport": 8080,
8
9 "cases": ["*"],
10 "exclude-cases": [],
11 "exclude-agent-cases": {}
12 }
0 #include <ulfius.h>
1
2 #define PORT "9001"
3 #define MESSAGE "Message without fragmentation from client"
4
5 static void websocket_echo_message_callback (const struct _u_request * request,
6 struct _websocket_manager * websocket_manager,
7 const struct _websocket_message * last_message,
8 void * websocket_incoming_message_user_data) {
9 //y_log_message(Y_LOG_LEVEL_DEBUG, "Got message opcode %d, %zu", last_message->opcode, last_message->data_len);
10 if (ulfius_websocket_send_message(websocket_manager, last_message->opcode, last_message->data_len, last_message->data) != U_OK) {
11 y_log_message(Y_LOG_LEVEL_ERROR, "Error send message from incoming");
12 }
13 }
14
15 int main(int argc, char ** argv) {
16 char * url;
17 struct _u_request request;
18 struct _u_response response;
19 struct _websocket_client_handler websocket_client_handler;
20 const char * url_pattern = (argc>2&&0==o_strcmp("-https", argv[2]))?"wss://localhost:" PORT "/runCase?case=%s&agent=ulfius":"ws://localhost:" PORT "/runCase?case=%s&agent=ulfius", * extensions;
21 int test_case = atoi(argv[1]);
22
23 y_init_logs("autobahn client tester", Y_LOG_MODE_FILE, Y_LOG_LEVEL_DEBUG, "ulfius_ws_echo_client.log", "Starting websocket client echo for autobahn testsuite");
24
25 ulfius_init_request(&request);
26 request.check_server_certificate = 0;
27
28 if (test_case >= 394 && test_case <= 411) { // test cases 13.1.*
29 extensions = "permessage-deflate; client_max_window_bits";
30 } else if (test_case >= 412 && test_case <= 429) { // test cases 13.2.*
31 extensions = "permessage-deflate; client_no_context_takeover; client_max_window_bits";
32 } else if (test_case >= 430 && test_case <= 447) { // test cases 13.3.*
33 extensions = "permessage-deflate; client_max_window_bits=8";
34 } else if (test_case >= 448 && test_case <= 465) { // test cases 13.4.*
35 extensions = "permessage-deflate; client_max_window_bits=15";
36 } else if (test_case >= 466 && test_case <= 483) { // test cases 13.5.*
37 extensions = "permessage-deflate; client_no_context_takeover; client_max_window_bits=8";
38 } else if (test_case >= 484 && test_case <= 501) { // test cases 13.6.*
39 extensions = "permessage-deflate; client_no_context_takeover; client_max_window_bits=15";
40 } else if (test_case >= 502 && test_case <= 519) { // test cases 13.7.*
41 extensions = "permessage-deflate; client_no_context_takeover; client_max_window_bits=8, permessage-deflate; client_no_context_takeover; client_max_window_bits, permessage-deflate; client_max_window_bits";
42 } else {
43 extensions = "permessage-deflate";
44 }
45 url = msprintf(url_pattern, argv[1]!=NULL?argv[1]:0);
46 if (ulfius_set_websocket_request(&request, url, NULL, extensions) == U_OK) {
47 websocket_client_handler.websocket = NULL;
48 websocket_client_handler.response = NULL;
49 ulfius_add_websocket_client_deflate_extension(&websocket_client_handler);
50 ulfius_init_response(&response);
51 y_log_message(Y_LOG_LEVEL_INFO, "Test case %s started", argv[1]);
52 if (ulfius_open_websocket_client_connection(&request, NULL, NULL, &websocket_echo_message_callback, NULL, NULL, NULL, &websocket_client_handler, &response) == U_OK) {
53 if (ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0) == U_WEBSOCKET_STATUS_ERROR) {
54 y_log_message(Y_LOG_LEVEL_ERROR, "Test case %s error", argv[1]);
55 } else {
56 y_log_message(Y_LOG_LEVEL_INFO, "Test case %s closed", argv[1]);
57 }
58 } else {
59 y_log_message(Y_LOG_LEVEL_ERROR, "Error connecting to websocket");
60 }
61 ulfius_clean_response(&response);
62 } else {
63 y_log_message(Y_LOG_LEVEL_ERROR, "Error setting request parameters");
64 }
65 o_free(url);
66 ulfius_clean_request(&request);
67
68 y_close_logs();
69 }
0 #include <signal.h>
1 #include <string.h>
2 #include <ulfius.h>
3
4 #define PORT_HTTP 9010
5 #define PORT_HTTPS 9011
6
7 static pthread_mutex_t global_handler_close_lock;
8 static pthread_cond_t global_handler_close_cond;
9
10 static char * read_file(const char * filename) {
11 char * buffer = NULL;
12 long length;
13 FILE * f;
14 if (filename != NULL) {
15 f = fopen (filename, "rb");
16 if (f) {
17 fseek (f, 0, SEEK_END);
18 length = ftell (f);
19 fseek (f, 0, SEEK_SET);
20 buffer = o_malloc (length + 1);
21 if (buffer) {
22 fread (buffer, 1, length, f);
23 buffer[length] = '\0';
24 }
25 fclose (f);
26 }
27 return buffer;
28 } else {
29 return NULL;
30 }
31 }
32
33 void websocket_echo_message_callback (const struct _u_request * request,
34 struct _websocket_manager * websocket_manager,
35 const struct _websocket_message * last_message,
36 void * websocket_incoming_message_user_data) {
37 if (ulfius_websocket_send_message(websocket_manager, last_message->opcode, last_message->data_len, last_message->data) != U_OK) {
38 y_log_message(Y_LOG_LEVEL_ERROR, "Error sending echo message");
39 }
40 }
41
42 int callback_websocket_echo (const struct _u_request * request, struct _u_response * response, void * user_data) {
43 y_log_message(Y_LOG_LEVEL_INFO, "Client connected to echo websocket");
44 if (ulfius_set_websocket_response(response, NULL, NULL, NULL, NULL, &websocket_echo_message_callback, NULL, NULL, NULL) == U_OK) {
45 ulfius_add_websocket_deflate_extension(response);
46 return U_CALLBACK_CONTINUE;
47 } else {
48 return U_CALLBACK_ERROR;
49 }
50 }
51
52 void* signal_thread(void *arg) {
53 sigset_t *sigs = arg;
54 int res, signum;
55
56 res = sigwait(sigs, &signum);
57 if (res) {
58 fprintf(stderr, "Glewlwyd - Waiting for signals failed\n");
59 exit(1);
60 }
61 if (signum == SIGQUIT || signum == SIGINT || signum == SIGTERM || signum == SIGHUP) {
62 y_log_message(Y_LOG_LEVEL_INFO, "Glewlwyd - Received close signal: %s", strsignal(signum));
63 pthread_mutex_lock(&global_handler_close_lock);
64 pthread_cond_signal(&global_handler_close_cond);
65 pthread_mutex_unlock(&global_handler_close_lock);
66 return NULL;
67 } else if (signum == SIGBUS) {
68 fprintf(stderr, "Glewlwyd - Received bus error signal\n");
69 exit(256-signum);
70 } else if (signum == SIGSEGV) {
71 fprintf(stderr, "Glewlwyd - Received segmentation fault signal\n");
72 exit(256-signum);
73 } else if (signum == SIGILL) {
74 fprintf(stderr, "Glewlwyd - Received illegal instruction signal\n");
75 exit(256-signum);
76 } else {
77 y_log_message(Y_LOG_LEVEL_WARNING, "Glewlwyd - Received unexpected signal: %s", strsignal(signum));
78 }
79
80 return NULL;
81 }
82
83 int main(int argc, char ** argv) {
84 struct _u_instance instance_http, instance_https;
85 char * key_file, * cert_file;
86 pthread_t signal_thread_id;
87 static sigset_t close_signals;
88
89 y_init_logs("websocket_example", Y_LOG_MODE_FILE, Y_LOG_LEVEL_DEBUG, "ulfius_ws_echo_server.log", "Starting websocket echo for autobahn testsuite");
90
91 if (pthread_mutex_init(&global_handler_close_lock, NULL) ||
92 pthread_cond_init(&global_handler_close_cond, NULL)) {
93 y_log_message(Y_LOG_LEVEL_ERROR, "Error initializing global_handler_close_lock or global_handler_close_cond");
94 return 1;
95 }
96
97 // Process end signals on dedicated thread
98 if (sigemptyset(&close_signals) == -1 ||
99 sigaddset(&close_signals, SIGQUIT) == -1 ||
100 sigaddset(&close_signals, SIGINT) == -1 ||
101 sigaddset(&close_signals, SIGTERM) == -1 ||
102 sigaddset(&close_signals, SIGHUP) == -1 ||
103 sigaddset(&close_signals, SIGBUS) == -1 ||
104 sigaddset(&close_signals, SIGSEGV) == -1 ||
105 sigaddset(&close_signals, SIGILL) == -1) {
106 fprintf(stderr, "init - Error creating signal mask\n");
107 return 1;
108 }
109 if (pthread_sigmask(SIG_BLOCK, &close_signals, NULL)) {
110 fprintf(stderr, "init - Error setting signal mask\n");
111 return 1;
112 }
113 if (pthread_sigmask(SIG_BLOCK, &close_signals, NULL)) {
114 fprintf(stderr, "init - Error setting signal mask\n");
115 return 1;
116 }
117 if (pthread_create(&signal_thread_id, NULL, &signal_thread, &close_signals)) {
118 fprintf(stderr, "init - Error creating signal thread\n");
119 return 1;
120 }
121
122 if (ulfius_init_instance(&instance_http, PORT_HTTP, NULL, NULL) != U_OK) {
123 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance http, abort");
124 return 1;
125 }
126
127 if (ulfius_init_instance(&instance_https, PORT_HTTPS, NULL, NULL) != U_OK) {
128 y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance https, abort");
129 return 1;
130 }
131
132 ulfius_add_endpoint_by_val(&instance_http, "GET", NULL, "*", 0, &callback_websocket_echo, NULL);
133 ulfius_start_framework(&instance_http);
134 y_log_message(Y_LOG_LEVEL_INFO, "Start http websocket echo server on port %d", instance_http.port);
135
136 if (argc > 2) {
137 key_file = read_file(argv[1]);
138 cert_file = read_file(argv[2]);
139 if (key_file != NULL && cert_file != NULL) {
140 ulfius_add_endpoint_by_val(&instance_https, "GET", NULL, "*", 0, &callback_websocket_echo, NULL);
141 ulfius_start_secure_framework(&instance_https, key_file, cert_file);
142 y_log_message(Y_LOG_LEVEL_INFO, "Start https websocket echo server on port %d", instance_https.port);
143 o_free(key_file);
144 o_free(cert_file);
145 pthread_mutex_lock(&global_handler_close_lock);
146 pthread_cond_wait(&global_handler_close_cond, &global_handler_close_lock);
147 pthread_mutex_unlock(&global_handler_close_lock);
148 ulfius_stop_framework(&instance_https);
149 } else {
150 y_log_message(Y_LOG_LEVEL_ERROR, "Error https key or certificate file");
151 }
152 } else {
153 pthread_mutex_lock(&global_handler_close_lock);
154 pthread_cond_wait(&global_handler_close_cond, &global_handler_close_lock);
155 pthread_mutex_unlock(&global_handler_close_lock);
156 }
157 ulfius_clean_instance(&instance_https);
158 ulfius_stop_framework(&instance_http);
159 ulfius_clean_instance(&instance_http);
160 y_log_message(Y_LOG_LEVEL_INFO, "Stop http websocket echo server");
161 y_close_logs();
162 }
+0
-32
test/client.crt less more
0 -----BEGIN CERTIFICATE-----
1 MIIFjjCCA3YCAQEwDQYJKoZIhvcNAQELBQAwgYoxCzAJBgNVBAYTAkNBMQ8wDQYD
2 VQQIDAZRdWViZWMxDzANBgNVBAcMBlF1ZWJlYzEPMA0GA1UECgwGVWxmaXVzMRAw
3 DgYDVQQLDAd0ZXN0LWNhMRIwEAYDVQQDDAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0B
4 CQEWE3dlYm1hc3RlckBsb2NhbGhvc3QwHhcNMTgxMjAyMTkzNzI0WhcNMTkxMjAy
5 MTkzNzI0WjCBjjELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzEPMA0GA1UE
6 BwwGUXVlYmVjMQ8wDQYDVQQKDAZVbGZpdXMxFDASBgNVBAsMC3Rlc3QtY2xpZW50
7 MRIwEAYDVQQDDAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0BCQEWE3dlYm1hc3RlckBs
8 b2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC7sYBRUgCA
9 bD9jueGjJKh3CNbtdKsppsi7N4h02bh3cbY0wrrPaVzM4+23rSbGUuvZBJPIRFZn
10 XvIanQalcJQqXSmWIqzmz+ovwlWrNzpkEN1AwpQ9h61rk9V6wUKusBJSpqtIS/YH
11 KXNt8hjzbKIwoAKRefuM1lxL91N3ENPRgh2skbRfA2yOog7uhQRbFIZYC1vJ9tzw
12 luLwNOz5juGEl60IDvpV4yuq3oK5eE+YvIalE/uQh5mvih6Gz9ti0ihTsIYf370c
13 h1IStd5+mD36uX0WnEijkKDGPZvbnMQLhq71M5VC0MMGd7Zbj7d2NMxBNOL6H3rm
14 BxaRi0jMMIMDKMhoDR76OKbnDr/90XmgzTebNcq6JA2XL7QBiVriUykej7brDln3
15 G/afGC8ShCj6yohCmPyvIRMrmFWzCzbr4ZQvdprNXPqlHOzYuRFINl/Fnv3Sx7V/
16 8hJMaG9c+BoJ29cTAnvX5zp7lszb7JoTB0IXdutT6eDm8A+zSQbdFWfNzr7pgQOw
17 fX1bZZeJHEO0YTNYHcFSz0YDOkWEFgy4KEPzDvjEh/fh9F56/MoaEF4Uj8vL/Cer
18 oj98eYCTODuR1jreetiLiFafwZNI6OGe7L1YuR0TIWJRUTetNsAPSoIt+DQVGMoi
19 TdTK8nb/0xGt3Z0CEg7b7Om83CFsedvluwIDAQABMA0GCSqGSIb3DQEBCwUAA4IC
20 AQB5/RdG4e+NvRy9RG2ls+yEJdljPWV9KrdEoD9WX9MauHfvxpW3j9hi7lCohBR5
21 6oOIsWl+YrkHkgz6RRNlArJBtGhR4tQg3kB5ZTp5PcUUvI9VFVmgGoU3O9rnOtTi
22 +AaEn3Sbx/KkgLnDEgPRMDj1BEVx4O6CQcnRdx9qZCbKXnGAAe5uHSAOOj7ujz9/
23 TW5W+KlN1RYMlWAH7SWuW2Y+kPKLOK/HpXKuxHZfFfiddya072Ufm0pnapBmvi50
24 rUmrTnk0DQN/o1vQgrGyg2C48QlUX3IEvl5z6fD8FKC1q2v0L2e0A5jmPWqsQe2B
25 jnp8VYEgD1jIX8Pchy8GE8/NsHNkYEOqtkc/k3oT8I/J4mcHewFlKXu83X+lUhmh
26 vGwTGJEAHFJX7pBdqqoK9EeHrGj0MiwkfqZnBcjspXTYPPLqwWR+iyU20XCwaOcV
27 iYox45FK4XqYiQqciarkQP18vgwtDcMfRfwtRFUTtTRgSULsdrWsMzwX+0lxEab0
28 Sb4BkU0+YTknpyI8KtyRkQzezsaRVekfHow1pdNWYYrW5P0jmi/V6tezt/609ohL
29 3+AThUfrgdPSQlV9OrINbMA2cPruHcyqgj+Uh8JBvsIShPxKa17ZLkTWVUgJ+ZJb
30 T6q4HuclYAw3AERGHSM1KPi9OuVw51TzTrxReqrvlL5UxQ==
31 -----END CERTIFICATE-----
+0
-51
test/client.key less more
0 -----BEGIN RSA PRIVATE KEY-----
1 MIIJKQIBAAKCAgEAu7GAUVIAgGw/Y7nhoySodwjW7XSrKabIuzeIdNm4d3G2NMK6
2 z2lczOPtt60mxlLr2QSTyERWZ17yGp0GpXCUKl0pliKs5s/qL8JVqzc6ZBDdQMKU
3 PYeta5PVesFCrrASUqarSEv2BylzbfIY82yiMKACkXn7jNZcS/dTdxDT0YIdrJG0
4 XwNsjqIO7oUEWxSGWAtbyfbc8Jbi8DTs+Y7hhJetCA76VeMrqt6CuXhPmLyGpRP7
5 kIeZr4oehs/bYtIoU7CGH9+9HIdSErXefpg9+rl9FpxIo5Cgxj2b25zEC4au9TOV
6 QtDDBne2W4+3djTMQTTi+h965gcWkYtIzDCDAyjIaA0e+jim5w6//dF5oM03mzXK
7 uiQNly+0AYla4lMpHo+26w5Z9xv2nxgvEoQo+sqIQpj8ryETK5hVsws26+GUL3aa
8 zVz6pRzs2LkRSDZfxZ790se1f/ISTGhvXPgaCdvXEwJ71+c6e5bM2+yaEwdCF3br
9 U+ng5vAPs0kG3RVnzc6+6YEDsH19W2WXiRxDtGEzWB3BUs9GAzpFhBYMuChD8w74
10 xIf34fReevzKGhBeFI/Ly/wnq6I/fHmAkzg7kdY63nrYi4hWn8GTSOjhnuy9WLkd
11 EyFiUVE3rTbAD0qCLfg0FRjKIk3UyvJ2/9MRrd2dAhIO2+zpvNwhbHnb5bsCAwEA
12 AQKCAgEArd0K5El1tYCQZ6y0Ww+GDl3d2jCMrRSWNPaILNXRjrOC+PS5X5BWKmWo
13 utBmbqg5WNpomsbS0wacdhKnKXFTjrql3zFXwKcwY/U8rlGGgVMt4ONdtnGML27N
14 /dwwDOmEz5McbugOiyvWhS4R9svWofxW5LN+VUPsU2JTqqOn0xcYrdZ6otjlImr0
15 MfZ9xnEI0V4UNM28dBERPzmHmtTECrMF0cUuF4lL1ggwR+YutWlNwHT2PzedIYJs
16 LK02qB/Mw1ltpPD4mQiw+iwGYnZcU3inat5yWzGJ/JjtW02oPJUSVIjn5vDC96bF
17 AaPralAWNQDskI3ApnxCZ6gCU0NM13LZmah1TEtm065rlkA10zTMquS+2SYZHzpX
18 IYz05yAiRQ6ljkz/BaPGBNXhDeiTB09voaOYJB0aWr/61pjH8yzElX3599LOGse8
19 Si+3rIbblDfJSUoGsapJ2Ri21HBA1frKJEi1a67l9AhZIiGPScOAErZHcnVHDEnm
20 Q247yugBIalIpeUA/8ThIaFs5Ofn35RHpPzCoLwRcpJZbwwzjL3uE2Iuv/hjfMTR
21 NTQXhJbkbzFYRM1OhJqkuyiljAEbgDGeOn6LKcRm7ZaiWJTHz5WAfCp8S3BrF4Le
22 gfzruEWulFQkXBAgCV/A0lCHlnbg5Wrh//VZR1jhOVYglGSJ9yECggEBAPl4IzY/
23 qFRCS+mE3gagrtCVHZd0ZmK+ZN1xzJNVJSx1pVmlP86O/Ds4Gz6h8gGQDTm4vOEM
24 NSI5b85IfLNG9ihp1rDVN/sofObP9xOmU0V2l8D+5qLiZGUNfrGant0fCA5TuLLE
25 Z1PQvV4j3JB5ymvGYlER3vVLUceUX2vA2FfNV/XBV+YS7L0qJJPKJ+ZgfQM+QdBi
26 uD5udcsOR6hwVIxFYRQJrf2vpFJd8c0yzJbQRtqoe15Hj5IIID3qJRX+zmSxVVuW
27 LG6Mmvqx9cLQznC7Tm7ip3KmLg9TNtBgM+pwobdYSdq5Se2XW9/jF1sQpX/yALoV
28 ritzaQ81nok0zG0CggEBAMCbXIeZUOkv5ghOWbd3WgPsDT2LHWdT7keSS3qbxq1Q
29 05nYzd4frZhzc5cAIaInGxBrMGwUAEGvwCo9siL6p3RYtNNEqBoXdaBDxSPMkZjP
30 j6bIHW8Xxuv8TgyWG7Ut0d3XJ7JM+DlVQN5HSixeLH5/NO1VcpuVl8kVpNf/Samh
31 xhxIWK/YGdXyEBj5Fz4ZS8sG1+g7nS+Oo7NYFpqi8lZExIxbjgAWhueOTL5jqv7j
32 YY/LmBynx9JOccimDADotOHMR8YOZqjF6VOfutrEVcRRt0FFAWE3rTFNk/RTQHUB
33 kNRyif8U52w5PfLGNirjT4gGanLWmN93ojiHS+cc0ccCggEANjtWtElcZ4zOAeLf
34 fWNa/X8dIrqsM1UeikFd7r14ylR88KMK3vWYgastyQzdlldheKXfcbD8sAb6dkat
35 zS9k/d4cTO7dL51aobaeCMiEm7ovPUZsjwZWOt2XKDbryDghwTOUGFC4AbGEKT30
36 ifff+FP1NZiD9qE8Ev3/TGZs4ZGDSKqSWDLPAkRyFSrCR8O8CYN0PT/ou7G5q73b
37 +BMLXj8K3pdIGrkfKMTBdxFPBo4aiRhNUW6PGHjpwWvwSfrNlXgzjSh1Wkb4vf0H
38 5yULa9hcMaDWNAdKRgyaAuyW/KUlkrz/uSvElYDs+RveDO7ue4T5gXqLFWkOcr7+
39 SueOdQKCAQA+2fPl4+f9UD4Wc8rfDbl4Ei136c+cikz1Wg37Yp4ArKGAULlNRLUX
40 TIVpvwc6rNwuDWxtuFElHj7tCdj5hSXj15aaYosgiIs/0x2fwv+4B/Nuj1rbh7zI
41 ATtO7CT6iIs1gyJXErrlqqZSYp0XwVtvo/8Xe4y8rKItYjy6p3nYOww06n7WUwSL
42 RkRugcpN319WT1NhWyK+BCPzQEmQCKBf+mzRCIdcC/3Vzh/I+Skxp+2MSPnIUA/q
43 1uJWgajlGwr7q/e377ccWAj92t/Ux8DdIXVVfTkoCk0gC5q+XfRouiwSx5W0gZPL
44 QxquAHRopmirkLxn4RNdyOJM5Ammz0a9AoIBAQC2zxu/rdE6T/049GWx8Pr539pe
45 Js/Vs4GxadjdwcYxHQsN+uigfzUqTi4Rdsen+os/3FAbNZOjbWDFQKeGS+ifI8j1
46 PQzkCL6/ax7aevDmgvnyt7LvVLcuWbJcWVFEk2VytlKK+sOJHI0iuyPqrD4FAEOV
47 gR3IpTQ9PfiUS429LrR1zB2/vSMebQz2kqSZT8oKZosB4MiqZiCPssAEAAEvOdro
48 kFaKqoc8q2rj1qKHshBND764tUHNz0OSeXPM4Dal0RJobCfUPO2bIamm34vsKH2u
49 wmDwRFprLP39jqrO8/CGwoU2NFSqD4Fpzv28sriibucXqP5K3dk/XeSbEXd+
50 -----END RSA PRIVATE KEY-----
458458 return U_CALLBACK_CONTINUE;
459459 }
460460
461 int callback_function_continue(const struct _u_request * request, struct _u_response * response, void * user_data) {
462 return U_CALLBACK_CONTINUE;
463 }
464
465 int callback_function_ignore(const struct _u_request * request, struct _u_response * response, void * user_data) {
466 return U_CALLBACK_IGNORE;
467 }
468
469 int callback_function_default_because_ignore(const struct _u_request * request, struct _u_response * response, void * user_data) {
470 response->status = 205;
471 return U_CALLBACK_CONTINUE;
472 }
473
474 int callback_function_complete_after_flow(const struct _u_request * request, struct _u_response * response, void * user_data) {
475 response->status = 200 + request->callback_position;
476 return U_CALLBACK_CONTINUE;
477 }
478
461479 int via_free_with_test = 0;
462480
463481 void free_with_test(void * ptr) {
9981016 }
9991017 END_TEST
10001018
1019 START_TEST(test_ulfius_endpoint_ignored)
1020 {
1021 struct _u_instance u_instance;
1022 struct _u_request request;
1023 struct _u_response response;
1024
1025 ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK);
1026 ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "ignore", NULL, 0, &callback_function_ignore, NULL), U_OK);
1027 ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "continue", NULL, 0, &callback_function_continue, NULL), U_OK);
1028 ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "continue", NULL, 1, &callback_function_ignore, NULL), U_OK);
1029 ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "continue", NULL, 2, &callback_function_complete_after_flow, NULL), U_OK);
1030 ck_assert_int_eq(ulfius_set_default_endpoint(&u_instance, &callback_function_default_because_ignore, NULL), U_OK);
1031 ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK);
1032
1033 ulfius_init_request(&request);
1034 ulfius_init_response(&response);
1035 ck_assert_int_eq(ulfius_set_request_properties(&request, U_OPT_HTTP_VERB, "GET", U_OPT_HTTP_URL, "http://localhost:8080/ignore", U_OPT_NONE), U_OK);
1036 ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK);
1037 ck_assert_int_eq(response.status, 205);
1038 ulfius_clean_request(&request);
1039 ulfius_clean_response(&response);
1040
1041 ulfius_init_request(&request);
1042 ulfius_init_response(&response);
1043 ck_assert_int_eq(ulfius_set_request_properties(&request, U_OPT_HTTP_VERB, "GET", U_OPT_HTTP_URL, "http://localhost:8080/continue", U_OPT_NONE), U_OK);
1044 ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK);
1045 ck_assert_int_eq(response.status, 201);
1046 ulfius_clean_request(&request);
1047 ulfius_clean_response(&response);
1048
1049 ulfius_stop_framework(&u_instance);
1050 ulfius_clean_instance(&u_instance);
1051 }
1052 END_TEST
1053
10011054 START_TEST(test_ulfius_utf8_not_ignored)
10021055 {
10031056 char * invalid_utf8_seq2 = msprintf("value %c%c", 0xC3, 0x28);
13301383 tcase_add_test(tc_core, test_ulfius_endpoint_injection);
13311384 tcase_add_test(tc_core, test_ulfius_endpoint_multiple);
13321385 tcase_add_test(tc_core, test_ulfius_endpoint_stream);
1386 tcase_add_test(tc_core, test_ulfius_endpoint_ignored);
13331387 tcase_add_test(tc_core, test_ulfius_utf8_not_ignored);
13341388 tcase_add_test(tc_core, test_ulfius_utf8_ignored);
13351389 tcase_add_test(tc_core, test_ulfius_endpoint_callback_position);
44 #include <string.h>
55 #include <errno.h>
66 #include <time.h>
7 #include <zlib.h>
78
89 #include <check.h>
910 #include <ulfius.h>
11
12 #define MIN(A, B) ((A)>(B)?(B):(A))
1013
1114 #define WEBSOCKET_URL "http://localhost:8378/websocket"
1215 #define DEFAULT_PROTOCOL "proto"
1316 #define DEFAULT_EXTENSION "ext"
1417 #define DEFAULT_MESSAGE "message content with a few characters"
15 #define PORT 9275
18 #define PORT_1 9275
19 #define PORT_2 9276
20 #define PORT_3 9277
21 #define PORT_4 9278
22 #define PORT_5 9279
23 #define PORT_6 9280
24 #define PORT_7 9281
25 #define PORT_8 9282
1626 #define PREFIX_WEBSOCKET "/websocket"
27 #define MESSAGE "HelloFrom"
28 #define MESSAGE_CLIENT "HelloFromClient"
29 #define MESSAGE_SERVER "HelloFromServer"
30 #define MESSAGE_EXT1 "Grut"
31 #define MESSAGE_EXT2 "Plop"
32 #define MESSAGE_EXT3 "Gna"
33 #define MESSAGE_EXT4 "Glop"
34 #define _U_W_BUFF_LEN 256
35
36 #define U_W_FLAG_SERVER 0x00000001
37 #define U_W_FLAG_CONTEXT 0x00000010
1738
1839 #ifndef U_DISABLE_WEBSOCKET
1940 void websocket_manager_callback_empty (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) {
3455 const struct _websocket_message * last_message,
3556 void * websocket_incoming_message_user_data) {
3657 if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) {
37 ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, last_message->data_len, last_message->data), U_OK);
58 ck_assert_int_eq(ulfius_websocket_send_fragmented_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, last_message->data_len, last_message->data, last_message->fragment_len), U_OK);
3859 }
3960 }
4061
5677 }
5778 }
5879
80 void websocket_extension_callback_client (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) {
81 ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(MESSAGE_CLIENT), MESSAGE_CLIENT), U_OK);
82 y_log_message(Y_LOG_LEVEL_DEBUG, "client message 1 '%s' sent", MESSAGE_CLIENT);
83 ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(MESSAGE_CLIENT), MESSAGE_CLIENT), U_OK);
84 y_log_message(Y_LOG_LEVEL_DEBUG, "client message 2 '%s' sent", MESSAGE_CLIENT);
85 if (ulfius_websocket_wait_close(websocket_manager, 50) == U_WEBSOCKET_STATUS_OPEN) {
86 ck_assert_int_eq(ulfius_websocket_send_close_signal(websocket_manager), U_OK);
87 }
88 }
89
5990 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) {
6091 ck_assert_int_eq(0, o_strncmp(message->data, DEFAULT_MESSAGE, message->data_len));
92 }
93
94 void websocket_manager_extension_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) {
95 y_log_message(Y_LOG_LEVEL_DEBUG, "server message '%s' sent", MESSAGE_SERVER);
96 ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(MESSAGE_SERVER), MESSAGE_SERVER), U_OK);
97 while (ulfius_websocket_wait_close(websocket_manager, 50) == U_WEBSOCKET_STATUS_OPEN);
98 }
99
100 void websocket_manager_extension_deflate_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) {
101 ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(MESSAGE_SERVER), MESSAGE_SERVER), U_OK);
102 y_log_message(Y_LOG_LEVEL_DEBUG, "server message 1 sent");
103 ck_assert_int_eq(ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(MESSAGE_SERVER), MESSAGE_SERVER), U_OK);
104 y_log_message(Y_LOG_LEVEL_DEBUG, "server message 2 sent");
105 while (ulfius_websocket_wait_close(websocket_manager, 50) == U_WEBSOCKET_STATUS_OPEN);
106 }
107
108 void websocket_incoming_extension_callback (const struct _u_request * request,
109 struct _websocket_manager * websocket_manager,
110 const struct _websocket_message * last_message,
111 void * user_data) {
112 int * rsv = (int *)user_data;
113
114 if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT || last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
115 ck_assert_ptr_ne(NULL, o_strnstr(last_message->data, MESSAGE, last_message->data_len));
116 if ((*rsv)&U_WEBSOCKET_RSV1) {
117 ck_assert_int_ne(last_message->rsv&U_WEBSOCKET_RSV1, 0);
118 ck_assert_ptr_ne(NULL, o_strnstr(last_message->data, MESSAGE_EXT1, last_message->data_len));
119 }
120 if ((*rsv)&U_WEBSOCKET_RSV2) {
121 ck_assert_int_ne(last_message->rsv&U_WEBSOCKET_RSV2, 0);
122 ck_assert_ptr_ne(NULL, o_strnstr(last_message->data, MESSAGE_EXT2, last_message->data_len));
123 }
124 if ((*rsv)&U_WEBSOCKET_RSV3) {
125 ck_assert_int_ne(last_message->rsv&U_WEBSOCKET_RSV3, 0);
126 ck_assert_ptr_ne(NULL, o_strnstr(last_message->data, MESSAGE_EXT3, last_message->data_len));
127 }
128 }
129 }
130
131 void websocket_incoming_extension_deflate_client_callback (const struct _u_request * request,
132 struct _websocket_manager * websocket_manager,
133 const struct _websocket_message * last_message,
134 void * user_data) {
135 ck_assert_int_eq(0, o_strncmp(last_message->data, MESSAGE_SERVER, last_message->data_len));
136 if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT || last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
137 ck_assert_int_ne(last_message->rsv&U_WEBSOCKET_RSV1, 0);
138 }
139 }
140
141 void websocket_incoming_extension_deflate_client_callback_disabled (const struct _u_request * request,
142 struct _websocket_manager * websocket_manager,
143 const struct _websocket_message * last_message,
144 void * user_data) {
145 ck_assert_int_eq(0, o_strncmp(last_message->data, MESSAGE_SERVER, last_message->data_len));
146 ck_assert_int_eq(last_message->rsv&U_WEBSOCKET_RSV1, 0);
147 }
148
149 void websocket_incoming_extension_deflate_server_callback (const struct _u_request * request,
150 struct _websocket_manager * websocket_manager,
151 const struct _websocket_message * last_message,
152 void * user_data) {
153 ck_assert_int_eq(0, o_strncmp(last_message->data, MESSAGE_CLIENT, last_message->data_len));
154 if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT || last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
155 ck_assert_int_ne(last_message->rsv&U_WEBSOCKET_RSV1, 0);
156 }
157 }
158
159 void websocket_incoming_extension_deflate_server_callback_disabled (const struct _u_request * request,
160 struct _websocket_manager * websocket_manager,
161 const struct _websocket_message * last_message,
162 void * user_data) {
163 ck_assert_int_eq(0, o_strncmp(last_message->data, MESSAGE_CLIENT, last_message->data_len));
164 ck_assert_int_eq(last_message->rsv&U_WEBSOCKET_RSV1, 0);
165 }
166
167 int websocket_extension1_message_out_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
168 ck_assert_int_eq(opcode, U_WEBSOCKET_OPCODE_TEXT);
169 if (((uintptr_t)user_data)&U_W_FLAG_CONTEXT) {
170 ck_assert_ptr_ne(context, NULL);
171 }
172 *data_out = msprintf("%.*s%s", (size_t)data_len_in, data_in, MESSAGE_EXT1);
173 ck_assert_ptr_ne(*data_out, NULL);
174 *data_len_out = o_strlen(*data_out);
175 y_log_message(Y_LOG_LEVEL_DEBUG, "inside websocket_extension1_message_out_perform");
176 return U_OK;
177 }
178
179 int websocket_extension1_message_in_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
180 ck_assert_int_eq(opcode, U_WEBSOCKET_OPCODE_TEXT);
181 if (((uintptr_t)user_data)&U_W_FLAG_CONTEXT) {
182 ck_assert_ptr_ne(context, NULL);
183 }
184 *data_out = msprintf("%.*s%s", (size_t)data_len_in, data_in, MESSAGE_EXT1);
185 *data_len_out = o_strlen(*data_out);
186 y_log_message(Y_LOG_LEVEL_DEBUG, "inside websocket_extension1_message_in_perform: '%.*s'", (size_t)data_len_in, data_in);
187 return U_OK;
188 }
189
190 int websocket_extension2_message_out_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
191 ck_assert_int_eq(opcode, U_WEBSOCKET_OPCODE_TEXT);
192 if (((uintptr_t)user_data)&U_W_FLAG_CONTEXT) {
193 ck_assert_ptr_ne(context, NULL);
194 }
195 *data_out = msprintf("%.*s%s", (size_t)data_len_in, data_in, MESSAGE_EXT2);
196 *data_len_out = o_strlen(*data_out);
197 y_log_message(Y_LOG_LEVEL_DEBUG, "inside websocket_extension2_message_out_perform");
198 return U_OK;
199 }
200
201 int websocket_extension2_message_in_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
202 ck_assert_int_eq(opcode, U_WEBSOCKET_OPCODE_TEXT);
203 if (((uintptr_t)user_data)&U_W_FLAG_CONTEXT) {
204 ck_assert_ptr_ne(context, NULL);
205 }
206 *data_out = msprintf("%.*s%s", (size_t)data_len_in, data_in, MESSAGE_EXT2);
207 *data_len_out = o_strlen(*data_out);
208 y_log_message(Y_LOG_LEVEL_DEBUG, "inside websocket_extension2_message_in_perform");
209 return U_OK;
210 }
211
212 int websocket_extension3_message_out_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
213 if (o_strlen(MESSAGE_SERVER) == data_len_in) {
214 ck_assert_int_ne(0, memcmp(MESSAGE_SERVER, data_in, MIN(data_len_in, o_strlen(MESSAGE_SERVER))));
215 }
216 *data_out = o_malloc(data_len_in);
217 memcpy(*data_out, data_in, data_len_in);
218 *data_len_out = data_len_in;
219 return U_OK;
220 }
221
222 int websocket_extension3_message_in_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
223 if (o_strlen(MESSAGE_SERVER) == data_len_in) {
224 ck_assert_int_ne(0, memcmp(MESSAGE_SERVER, data_in, MIN(data_len_in, o_strlen(MESSAGE_SERVER))));
225 }
226 *data_out = o_malloc(data_len_in);
227 memcpy(*data_out, data_in, data_len_in);
228 *data_len_out = data_len_in;
229 return U_OK;
230 }
231
232 int websocket_extension4_message_out_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
233 ck_assert_int_eq(o_strlen(MESSAGE_SERVER), data_len_in);
234 ck_assert_int_eq(0, memcmp(MESSAGE_SERVER, data_in, MIN(data_len_in, o_strlen(MESSAGE_SERVER))));
235 *data_out = o_malloc(data_len_in);
236 memcpy(*data_out, data_in, data_len_in);
237 *data_len_out = data_len_in;
238 return U_OK;
239 }
240
241 int websocket_extension4_message_in_perform(const uint8_t opcode, const uint64_t data_len_in, const char * data_in, uint64_t * data_len_out, char ** data_out, const uint64_t fragment_len, void * user_data, void * context) {
242 ck_assert_int_eq(o_strlen(MESSAGE_SERVER), data_len_in);
243 ck_assert_int_eq(0, memcmp(MESSAGE_SERVER, data_in, MIN(data_len_in, o_strlen(MESSAGE_SERVER))));
244 *data_out = o_malloc(data_len_in);
245 memcpy(*data_out, data_in, data_len_in);
246 *data_len_out = data_len_in;
247 return U_OK;
61248 }
62249
63250 int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data) {
77264 return (ret == U_OK)?U_CALLBACK_CONTINUE:U_CALLBACK_ERROR;
78265 }
79266
80 START_TEST(test_websocket_ulfius_set_websocket_response)
267 int callback_websocket_extension_no_match (const struct _u_request * request, struct _u_response * response, void * user_data) {
268 ck_assert_int_eq(ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_extension_callback, NULL, &websocket_incoming_extension_callback, user_data, NULL, NULL), U_OK);
269 ck_assert_int_eq(ulfius_add_websocket_extension_message_perform(response, MESSAGE_EXT1, U_WEBSOCKET_RSV1, &websocket_extension1_message_out_perform, (void *)U_W_FLAG_SERVER, &websocket_extension1_message_in_perform, (void *)U_W_FLAG_SERVER, NULL, NULL, NULL, NULL), U_OK);
270 ck_assert_int_eq(ulfius_add_websocket_extension_message_perform(response, MESSAGE_EXT2, U_WEBSOCKET_RSV2, &websocket_extension2_message_out_perform, (void *)U_W_FLAG_SERVER, &websocket_extension2_message_in_perform, (void *)U_W_FLAG_SERVER, NULL, NULL, NULL, NULL), U_OK);
271 return U_CALLBACK_CONTINUE;
272 }
273
274 void websocket_extension_match_free_context(void * user_data, void * context) {
275 (void)user_data;
276 y_log_message(Y_LOG_LEVEL_DEBUG, "inside websocket_extension_match_free_context");
277 ck_assert_ptr_ne(context, NULL);
278 o_free(context);
279 }
280
281 int websocket_extension_server_match_ext1(const char * extension_server, const char ** extension_client_list, char ** extension_client, void * user_data, void ** context) {
282 y_log_message(Y_LOG_LEVEL_DEBUG, "check match server extension "MESSAGE_EXT1" with extension '%s'", extension_server);
283 if (0 == o_strncmp(extension_server, MESSAGE_EXT1, o_strlen(MESSAGE_EXT1))) {
284 *extension_client = o_strdup(MESSAGE_EXT1);
285 *context = o_strdup("context for " MESSAGE_EXT1);
286 return U_OK;
287 } else {
288 return U_ERROR;
289 }
290 }
291
292 int websocket_extension_server_match_ext2(const char * extension_server, const char ** extension_client_list, char ** extension_client, void * user_data, void ** context) {
293 y_log_message(Y_LOG_LEVEL_DEBUG, "check match server extension "MESSAGE_EXT2" with extension '%s'", extension_server);
294 if (0 == o_strncmp(extension_server, MESSAGE_EXT2, o_strlen(MESSAGE_EXT2))) {
295 *extension_client = o_strdup(MESSAGE_EXT2);
296 *context = o_strdup("context for " MESSAGE_EXT1);
297 return U_OK;
298 } else {
299 return U_ERROR;
300 }
301 }
302
303 int websocket_extension_client_match_ext1(const char * extension_server, void * user_data, void ** context) {
304 y_log_message(Y_LOG_LEVEL_DEBUG, "check match client extension "MESSAGE_EXT1" with extension '%s'", extension_server);
305 if (0 == o_strncmp(extension_server, MESSAGE_EXT1, o_strlen(MESSAGE_EXT1))) {
306 *context = o_strdup("context for " MESSAGE_EXT1);
307 return U_OK;
308 } else {
309 return U_ERROR;
310 }
311 }
312
313 int websocket_extension_client_match_ext2(const char * extension_server, void * user_data, void ** context) {
314 y_log_message(Y_LOG_LEVEL_DEBUG, "check match client extension "MESSAGE_EXT2" with extension '%s'", extension_server);
315 if (0 == o_strncmp(extension_server, MESSAGE_EXT2, o_strlen(MESSAGE_EXT2))) {
316 *context = o_strdup("context for " MESSAGE_EXT2);
317 return U_OK;
318 } else {
319 return U_ERROR;
320 }
321 }
322
323 int callback_websocket_extension_match (const struct _u_request * request, struct _u_response * response, void * user_data) {
324 ck_assert_int_eq(ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_extension_callback, NULL, &websocket_incoming_extension_callback, user_data, NULL, NULL), U_OK);
325 ck_assert_int_eq(ulfius_add_websocket_extension_message_perform(response, MESSAGE_EXT1, U_WEBSOCKET_RSV1, &websocket_extension1_message_out_perform, (void *)(U_W_FLAG_SERVER|U_W_FLAG_CONTEXT), &websocket_extension1_message_in_perform, (void *)(U_W_FLAG_SERVER|U_W_FLAG_CONTEXT), &websocket_extension_server_match_ext1, NULL, &websocket_extension_match_free_context, NULL), U_OK);
326 ck_assert_int_eq(ulfius_add_websocket_extension_message_perform(response, MESSAGE_EXT2, U_WEBSOCKET_RSV2, &websocket_extension2_message_out_perform, (void *)(U_W_FLAG_SERVER|U_W_FLAG_CONTEXT), &websocket_extension2_message_in_perform, (void *)(U_W_FLAG_SERVER|U_W_FLAG_CONTEXT), &websocket_extension_server_match_ext2, NULL, &websocket_extension_match_free_context, NULL), U_OK);
327 return U_CALLBACK_CONTINUE;
328 }
329
330 int websocket_extension_message_out_deflate_test(const uint8_t opcode,
331 uint8_t * rsv,
332 const uint64_t data_len_in,
333 const char * data_in,
334 uint64_t * data_len_out,
335 char ** data_out,
336 const uint64_t fragment_len,
337 void * user_data,
338 void * context) {
339 struct _websocket_deflate_context * deflate_context = (struct _websocket_deflate_context *)context;
340 int ret;
341 (void)opcode;
342 (void)fragment_len;
343 (void)user_data;
344
345 if (data_len_in) {
346 if (deflate_context != NULL) {
347 *data_out = NULL;
348 *data_len_out = 0;
349
350 deflate_context->defstream.avail_in = (uInt)data_len_in;
351 deflate_context->defstream.next_in = (Bytef *)data_in;
352
353 ret = U_OK;
354 do {
355 if ((*data_out = o_realloc(*data_out, (*data_len_out)+_U_W_BUFF_LEN)) != NULL) {
356 deflate_context->defstream.avail_out = _U_W_BUFF_LEN;
357 deflate_context->defstream.next_out = ((Bytef *)*data_out)+(*data_len_out);
358 int res;
359 switch ((res = deflate(&deflate_context->defstream, deflate_context->deflate_mask))) {
360 case Z_OK:
361 case Z_STREAM_END:
362 case Z_BUF_ERROR:
363 break;
364 default:
365 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error deflate");
366 ret = U_ERROR;
367 break;
368 }
369 (*data_len_out) += _U_W_BUFF_LEN - deflate_context->defstream.avail_out;
370 } else {
371 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error allocating resources for data_in_suffix");
372 ret = U_ERROR;
373 }
374 } while (U_OK == ret && deflate_context->defstream.avail_out == 0);
375
376 // https://github.com/madler/zlib/issues/149
377 if (U_OK == ret && Z_BLOCK == deflate_context->deflate_mask) {
378 if ((*data_out = o_realloc(*data_out, (*data_len_out)+_U_W_BUFF_LEN)) != NULL) {
379 deflate_context->defstream.avail_out = _U_W_BUFF_LEN;
380 deflate_context->defstream.next_out = ((Bytef *)*data_out)+(*data_len_out);
381 switch (deflate(&deflate_context->defstream, Z_FULL_FLUSH)) {
382 case Z_OK:
383 case Z_STREAM_END:
384 case Z_BUF_ERROR:
385 break;
386 default:
387 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error inflate (2)");
388 ret = U_ERROR;
389 break;
390 }
391 (*data_len_out) += _U_W_BUFF_LEN - deflate_context->defstream.avail_out;
392 } else {
393 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error allocating resources for data_in_suffix (2)");
394 ret = U_ERROR;
395 }
396 }
397
398 if (U_OK != ret) {
399 o_free(*data_out);
400 *data_out = NULL;
401 *data_len_out = 0;
402 } else {
403 if ((*(unsigned char **)data_out)[*data_len_out-1] == 0xff && (*(unsigned char **)data_out)[*data_len_out-2] == 0xff && (*(unsigned char **)data_out)[*data_len_out-3] == 0x00 && (*(unsigned char **)data_out)[*data_len_out-4] == 0x00) {
404 *data_len_out -= 4;
405 } else {
406 (*(unsigned char **)data_out)[*data_len_out] = '\0';
407 (*data_len_out)++;
408 }
409 *rsv |= U_WEBSOCKET_RSV1;
410 }
411 } else {
412 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_out_deflate - Error context is NULL");
413 ret = U_ERROR;
414 }
415 } else {
416 *data_len_out = 0;
417 ret = U_OK;
418 }
419 ck_assert_int_eq(U_OK, ret);
420 return ret;
421 }
422
423 int websocket_extension_message_in_inflate_test(const uint8_t opcode,
424 uint8_t rsv,
425 const uint64_t data_len_in,
426 const char * data_in,
427 uint64_t * data_len_out,
428 char ** data_out,
429 const uint64_t fragment_len,
430 void * user_data,
431 void * context) {
432 struct _websocket_deflate_context * deflate_context = (struct _websocket_deflate_context *)context;
433 unsigned char * data_in_suffix;
434 unsigned char suffix[4] = {0x00, 0x00, 0xff, 0xff};
435 int ret;
436 (void)opcode;
437 (void)fragment_len;
438 (void)user_data;
439
440 if (data_len_in && rsv&U_WEBSOCKET_RSV1) {
441 if (deflate_context != NULL) {
442 *data_out = NULL;
443 *data_len_out = 0;
444 if ((data_in_suffix = o_malloc(data_len_in+4)) != NULL) {
445 memcpy(data_in_suffix, data_in, data_len_in);
446 memcpy(data_in_suffix+data_len_in, suffix, 4);
447
448 deflate_context->infstream.avail_in = (uInt)data_len_in+4;
449 deflate_context->infstream.next_in = (Bytef *)data_in_suffix;
450
451 ret = U_OK;
452 do {
453 if ((*data_out = o_realloc(*data_out, (*data_len_out)+_U_W_BUFF_LEN)) != NULL) {
454 deflate_context->infstream.avail_out = _U_W_BUFF_LEN;
455 deflate_context->infstream.next_out = ((Bytef *)*data_out)+(*data_len_out);
456 switch (inflate(&deflate_context->infstream, deflate_context->inflate_mask)) {
457 case Z_OK:
458 case Z_STREAM_END:
459 case Z_BUF_ERROR:
460 break;
461 default:
462 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error inflate");
463 ret = U_ERROR;
464 break;
465 }
466 (*data_len_out) += _U_W_BUFF_LEN - deflate_context->infstream.avail_out;
467 } else {
468 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error allocating resources for data_in_suffix");
469 ret = U_ERROR;
470 }
471 } while (U_OK == ret && deflate_context->infstream.avail_out == 0);
472
473 o_free(data_in_suffix);
474 if (U_OK != ret) {
475 o_free(*data_out);
476 *data_out = NULL;
477 *data_len_out = 0;
478 }
479 } else {
480 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error allocating resources for data_in_suffix");
481 ret = U_ERROR;
482 }
483 } else {
484 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error context is NULL");
485 ret = U_ERROR;
486 }
487 } else {
488 // If RSV1 flag isn't set, copy data as is (as seen in firefox)
489 *data_len_out = data_len_in;
490 *data_out = o_malloc(data_len_in);
491 if (*data_out != NULL) {
492 memcpy(*data_out, data_in, data_len_in);
493 ret = U_OK;
494 } else {
495 y_log_message(Y_LOG_LEVEL_ERROR, "websocket_extension_message_in_inflate - Error allocating resources for data_out");
496 ret = U_ERROR;
497 }
498 }
499 ck_assert_int_eq(U_OK, ret);
500 return ret;
501 }
502
503 int callback_websocket_extension_deflate (const struct _u_request * request, struct _u_response * response, void * user_data) {
504 ck_assert_int_eq(ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_extension_deflate_callback, NULL, &websocket_incoming_extension_deflate_server_callback, NULL, NULL, NULL), U_OK);
505 ck_assert_int_eq(ulfius_add_websocket_deflate_extension(response), U_OK);
506 ck_assert_int_eq(ulfius_add_websocket_extension_message_perform(response, MESSAGE_EXT3, U_WEBSOCKET_RSV3, websocket_extension3_message_out_perform, NULL, websocket_extension3_message_in_perform, NULL, NULL, NULL, NULL, NULL), U_OK);
507 return U_CALLBACK_CONTINUE;
508 }
509
510 int callback_websocket_extension_deflate_disabled (const struct _u_request * request, struct _u_response * response, void * user_data) {
511 ck_assert_int_eq(ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_extension_deflate_callback, NULL, &websocket_incoming_extension_deflate_server_callback_disabled, NULL, NULL, NULL), U_OK);
512 ck_assert_int_eq(ulfius_add_websocket_deflate_extension(response), U_OK);
513 ck_assert_int_eq(ulfius_add_websocket_extension_message_perform(response, MESSAGE_EXT4, U_WEBSOCKET_RSV3, websocket_extension4_message_out_perform, NULL, websocket_extension4_message_in_perform, NULL, NULL, NULL, NULL, NULL), U_OK);
514 return U_CALLBACK_CONTINUE;
515 }
516
517 START_TEST(test_ulfius_websocket_set_websocket_response)
81518 {
82519 struct _u_response response;
83520 ulfius_init_response(&response);
92529 }
93530 END_TEST
94531
95 START_TEST(test_websocket_ulfius_set_websocket_request)
532 START_TEST(test_ulfius_websocket_set_websocket_request)
96533 {
97534 struct _u_request request;
98535 ulfius_init_request(&request);
108545 }
109546 END_TEST
110547
111 START_TEST(test_websocket_ulfius_open_websocket_client_connection_error)
548 START_TEST(test_ulfius_websocket_open_websocket_client_connection_error)
112549 {
113550 struct _u_request request;
114551 struct _u_response response;
126563 }
127564 END_TEST
128565
129 START_TEST(test_websocket_ulfius_websocket_client)
566 START_TEST(test_ulfius_websocket_client)
130567 {
131568 struct _u_instance instance;
132569 struct _u_request request;
133570 struct _u_response response;
134 struct _websocket_client_handler websocket_client_handler;
571 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
135572 char url[64], * allocated_data = o_strdup("plop");
136573
137 ck_assert_int_eq(ulfius_init_instance(&instance, PORT, NULL, NULL), U_OK);
574 ck_assert_int_eq(ulfius_init_instance(&instance, PORT_1, NULL, NULL), U_OK);
138575 ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, allocated_data), U_OK);
139576 ck_assert_int_eq(ulfius_start_framework(&instance), U_OK);
140577
141578 ulfius_init_request(&request);
142579 ulfius_init_response(&response);
143 sprintf(url, "ws://localhost:%d/%s", PORT, PREFIX_WEBSOCKET);
580 sprintf(url, "ws://localhost:%d/%s", PORT_1, PREFIX_WEBSOCKET);
144581
145582 // Test correct websocket connection on correct websocket service
146583 ck_assert_int_eq(ulfius_set_websocket_request(&request, url, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_OK);
167604 ulfius_clean_request(&request);
168605 ulfius_clean_response(&response);
169606
607 usleep(50);
170608 ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK);
609
171610 ulfius_clean_instance(&instance);
172611 o_free(allocated_data);
173612 }
174613 END_TEST
175614
176 START_TEST(test_websocket_ulfius_websocket_client_no_onclose)
615 START_TEST(test_ulfius_websocket_client_no_onclose)
177616 {
178617 struct _u_instance instance;
179618 struct _u_request request;
180619 struct _u_response response;
181 struct _websocket_client_handler websocket_client_handler;
620 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
182621 char url[64];
183 ck_assert_int_eq(ulfius_init_instance(&instance, PORT, NULL, NULL), U_OK);
622 ck_assert_int_eq(ulfius_init_instance(&instance, PORT_2, NULL, NULL), U_OK);
184623 ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket_onclose, NULL), U_OK);
185624 ck_assert_int_eq(ulfius_start_framework(&instance), U_OK);
186625
187626 ulfius_init_request(&request);
188627 ulfius_init_response(&response);
189 sprintf(url, "ws://localhost:%d/%s", PORT, PREFIX_WEBSOCKET);
628 sprintf(url, "ws://localhost:%d/%s", PORT_2, PREFIX_WEBSOCKET);
190629
191630 // Test correct websocket connection on correct websocket service
192631 ck_assert_int_eq(ulfius_set_websocket_request(&request, url, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_OK);
213652 ulfius_clean_request(&request);
214653 ulfius_clean_response(&response);
215654
655 usleep(50);
216656 ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK);
657
658 ulfius_clean_instance(&instance);
659 }
660 END_TEST
661
662 START_TEST(test_ulfius_websocket_extension_no_match_function)
663 {
664 struct _u_instance instance;
665 struct _u_request request;
666 struct _u_response response;
667 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
668 char url[64];
669 int user_data = U_WEBSOCKET_RSV1;
670
671 ck_assert_int_eq(ulfius_init_instance(&instance, PORT_3, NULL, NULL), U_OK);
672 ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket_extension_no_match, &user_data), U_OK);
673 ck_assert_int_eq(ulfius_start_framework(&instance), U_OK);
674
675 ulfius_init_request(&request);
676 ulfius_init_response(&response);
677 sprintf(url, "ws://localhost:%d/%s", PORT_3, PREFIX_WEBSOCKET);
678
679 ck_assert_int_eq(ulfius_set_websocket_request(&request, url, NULL, MESSAGE_EXT1), U_OK);
680 ck_assert_int_eq(ulfius_add_websocket_client_extension_message_perform(&websocket_client_handler, MESSAGE_EXT1, U_WEBSOCKET_RSV1, &websocket_extension1_message_out_perform, NULL, &websocket_extension1_message_in_perform, NULL, NULL, NULL, NULL, NULL), U_OK);
681 ck_assert_int_eq(ulfius_add_websocket_client_extension_message_perform(&websocket_client_handler, MESSAGE_EXT2, U_WEBSOCKET_RSV2, &websocket_extension2_message_out_perform, NULL, &websocket_extension2_message_in_perform, NULL, NULL, NULL, NULL, NULL), U_OK);
682 ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, &websocket_extension_callback_client, NULL, &websocket_incoming_extension_callback, &user_data, NULL, NULL, &websocket_client_handler, &response), U_OK);
683 ck_assert_int_eq(ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0), U_WEBSOCKET_STATUS_CLOSE);
684 ulfius_clean_request(&request);
685 ulfius_clean_response(&response);
686
687 usleep(50);
688 ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK);
689
690 ulfius_clean_instance(&instance);
691 }
692 END_TEST
693
694 START_TEST(test_ulfius_websocket_extension_match_function)
695 {
696 struct _u_instance instance;
697 struct _u_request request;
698 struct _u_response response;
699 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
700 char url[64];
701 int user_data = U_WEBSOCKET_RSV1|U_WEBSOCKET_RSV2;
702
703 ck_assert_int_eq(ulfius_init_instance(&instance, PORT_4, NULL, NULL), U_OK);
704 ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket_extension_match, &user_data), U_OK);
705 ck_assert_int_eq(ulfius_start_framework(&instance), U_OK);
706
707 ulfius_init_request(&request);
708 ulfius_init_response(&response);
709 sprintf(url, "ws://localhost:%d/%s", PORT_4, PREFIX_WEBSOCKET);
710
711 ck_assert_int_eq(ulfius_set_websocket_request(&request, url, NULL, MESSAGE_EXT1 ", " MESSAGE_EXT2), U_OK);
712 ck_assert_int_eq(ulfius_add_websocket_client_extension_message_perform(&websocket_client_handler, MESSAGE_EXT1, U_WEBSOCKET_RSV1, &websocket_extension1_message_out_perform, (void *)U_W_FLAG_CONTEXT, &websocket_extension1_message_in_perform, (void *)U_W_FLAG_CONTEXT, &websocket_extension_client_match_ext1, NULL, &websocket_extension_match_free_context, NULL), U_OK);
713 ck_assert_int_eq(ulfius_add_websocket_client_extension_message_perform(&websocket_client_handler, MESSAGE_EXT2, U_WEBSOCKET_RSV2, &websocket_extension2_message_out_perform, (void *)U_W_FLAG_CONTEXT, &websocket_extension2_message_in_perform, (void *)U_W_FLAG_CONTEXT, &websocket_extension_client_match_ext2, NULL, &websocket_extension_match_free_context, NULL), U_OK);
714 ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, &websocket_extension_callback_client, NULL, &websocket_incoming_extension_callback, &user_data, NULL, NULL, &websocket_client_handler, &response), U_OK);
715 ck_assert_int_eq(ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0), U_WEBSOCKET_STATUS_CLOSE);
716 ulfius_clean_request(&request);
717 ulfius_clean_response(&response);
718
719 usleep(50);
720 ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK);
721
722 ulfius_clean_instance(&instance);
723 }
724 END_TEST
725
726 START_TEST(test_ulfius_websocket_extension_deflate)
727 {
728 struct _u_instance instance;
729 struct _u_request request;
730 struct _u_response response;
731 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
732 char url[64];
733
734 ck_assert_int_eq(ulfius_init_instance(&instance, PORT_5, NULL, NULL), U_OK);
735 ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket_extension_deflate, NULL), U_OK);
736 ck_assert_int_eq(ulfius_start_framework(&instance), U_OK);
737
738 ulfius_init_request(&request);
739 ulfius_init_response(&response);
740 sprintf(url, "ws://localhost:%d/%s", PORT_5, PREFIX_WEBSOCKET);
741
742 ck_assert_int_eq(ulfius_set_websocket_request(&request, url, NULL, "permessage-deflate,"MESSAGE_EXT3), U_OK);
743 ck_assert_int_eq(ulfius_add_websocket_client_deflate_extension(&websocket_client_handler), U_OK);
744 ck_assert_int_eq(ulfius_add_websocket_client_extension_message_perform(&websocket_client_handler, MESSAGE_EXT3, U_WEBSOCKET_RSV3, &websocket_extension3_message_out_perform, NULL, websocket_extension3_message_in_perform, NULL, NULL, NULL, NULL, NULL), U_OK);
745 ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, &websocket_extension_callback_client, NULL, &websocket_incoming_extension_deflate_client_callback, NULL, NULL, NULL, &websocket_client_handler, &response), U_OK);
746 ck_assert_int_eq(ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0), U_WEBSOCKET_STATUS_CLOSE);
747 ulfius_clean_request(&request);
748 ulfius_clean_response(&response);
749
750 usleep(50);
751 ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK);
752
753 ulfius_clean_instance(&instance);
754 }
755 END_TEST
756
757 START_TEST(test_ulfius_websocket_extension_deflate_with_all_params)
758 {
759 struct _u_instance instance;
760 struct _u_request request;
761 struct _u_response response;
762 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
763 char url[64];
764
765 ck_assert_int_eq(ulfius_init_instance(&instance, PORT_6, NULL, NULL), U_OK);
766 ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket_extension_deflate, NULL), U_OK);
767 ck_assert_int_eq(ulfius_start_framework(&instance), U_OK);
768
769 ulfius_init_request(&request);
770 ulfius_init_response(&response);
771 sprintf(url, "ws://localhost:%d/%s", PORT_6, PREFIX_WEBSOCKET);
772
773 ck_assert_int_eq(ulfius_set_websocket_request(&request, url, NULL, "permessage-deflate; server_max_window_bits=10; client_max_window_bits=12; server_no_context_takeover; client_no_context_takeover, "MESSAGE_EXT3), U_OK);
774 ck_assert_int_eq(ulfius_add_websocket_client_deflate_extension(&websocket_client_handler), U_OK);
775 ck_assert_int_eq(ulfius_add_websocket_client_extension_message_perform(&websocket_client_handler, MESSAGE_EXT3, U_WEBSOCKET_RSV3, &websocket_extension3_message_out_perform, NULL, websocket_extension3_message_in_perform, NULL, NULL, NULL, NULL, NULL), U_OK);
776 ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, &websocket_extension_callback_client, NULL, &websocket_incoming_extension_deflate_client_callback, NULL, NULL, NULL, &websocket_client_handler, &response), U_OK);
777 ck_assert_int_eq(ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0), U_WEBSOCKET_STATUS_CLOSE);
778 ulfius_clean_request(&request);
779 ulfius_clean_response(&response);
780
781 usleep(50);
782 ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK);
783
784 ulfius_clean_instance(&instance);
785 }
786 END_TEST
787
788 START_TEST(test_ulfius_websocket_extension_deflate_error_params)
789 {
790 struct _u_instance instance;
791 struct _u_request request;
792 struct _u_response response;
793 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
794 char url[64];
795
796 ck_assert_int_eq(ulfius_init_instance(&instance, PORT_7, NULL, NULL), U_OK);
797 ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket_extension_deflate_disabled, NULL), U_OK);
798 ck_assert_int_eq(ulfius_start_framework(&instance), U_OK);
799
800 sprintf(url, "ws://localhost:%d/%s", PORT_7, PREFIX_WEBSOCKET);
801
802 ulfius_init_request(&request);
803 ulfius_init_response(&response);
804 ck_assert_int_eq(ulfius_set_websocket_request(&request, url, NULL, "permessage-deflate; server_max_window_bits=1; client_max_window_bits=12; server_no_context_takeover; client_no_context_takeover"), U_OK);
805 ck_assert_int_eq(ulfius_add_websocket_client_deflate_extension(&websocket_client_handler), U_OK);
806 ck_assert_int_eq(ulfius_open_websocket_client_connection(&request, &websocket_extension_callback_client, NULL, &websocket_incoming_extension_deflate_client_callback_disabled, NULL, NULL, NULL, &websocket_client_handler, &response), U_OK);
807 ck_assert_int_eq(ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0), U_WEBSOCKET_STATUS_CLOSE);
808 ulfius_clean_request(&request);
809 ulfius_clean_response(&response);
810
811 usleep(50);
812 ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK);
813
217814 ulfius_clean_instance(&instance);
218815 }
219816 END_TEST
228825 s = suite_create("Ulfius websocket functions tests");
229826 tc_websocket = tcase_create("test_ulfius_websocket");
230827 #ifndef U_DISABLE_WEBSOCKET
231 tcase_add_test(tc_websocket, test_websocket_ulfius_set_websocket_response);
232 tcase_add_test(tc_websocket, test_websocket_ulfius_set_websocket_request);
233 tcase_add_test(tc_websocket, test_websocket_ulfius_open_websocket_client_connection_error);
234 tcase_add_test(tc_websocket, test_websocket_ulfius_websocket_client);
235 tcase_add_test(tc_websocket, test_websocket_ulfius_websocket_client_no_onclose);
828 tcase_add_test(tc_websocket, test_ulfius_websocket_set_websocket_response);
829 tcase_add_test(tc_websocket, test_ulfius_websocket_set_websocket_request);
830 tcase_add_test(tc_websocket, test_ulfius_websocket_open_websocket_client_connection_error);
831 tcase_add_test(tc_websocket, test_ulfius_websocket_client);
832 tcase_add_test(tc_websocket, test_ulfius_websocket_client_no_onclose);
833 tcase_add_test(tc_websocket, test_ulfius_websocket_extension_no_match_function);
834 tcase_add_test(tc_websocket, test_ulfius_websocket_extension_match_function);
835 tcase_add_test(tc_websocket, test_ulfius_websocket_extension_deflate);
836 tcase_add_test(tc_websocket, test_ulfius_websocket_extension_deflate_with_all_params);
837 tcase_add_test(tc_websocket, test_ulfius_websocket_extension_deflate_error_params);
236838 #endif
237839 tcase_set_timeout(tc_websocket, 30);
238840 suite_add_tcase(s, tc_websocket);
3636 Specify the Websocket extensions values, default none
3737 -s --non-secure
3838 Do not check server certificate
39 -s --non-secure
40 Do not check server certificate
41 -q --quiet
42 Quiet mode, show only websocket messages
3943 -v --version
4044 Print Glewlwyd's current version
4145
00 .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8.
1 .TH UWSC "1" "July 2020" "uwsc 0.10" "User Commands"
1 .TH UWSC "1" "December 2020" "uwsc 0.11" "User Commands"
22 .SH NAME
33 uwsc \- Command-line websocket client
44 .SH DESCRIPTION
55 uwsc \- Ulfius Websocket Client
66 .PP
7 Version 0.10
7 Version 0.11
88 .SH COPYRIGHT
99 Copyright 2018\-2020 Nicolas Mora <mail@babelouest.org>
1010 .PP
5353 .IP
5454 Do not check server certificate
5555 .PP
56 \fB\-q\fR \fB\-\-quiet\fR
57 .IP
58 Quiet mode, show only websocket messages
59 .PP
5660 \fB\-v\fR \fB\-\-version\fR
5761 .IP
5862 Print uwsc's current version
5353 }
5454 #endif
5555
56 #define _UWSC_VERSION_ "0.10"
56 #define _UWSC_VERSION_ "0.11"
5757
5858 #ifndef U_DISABLE_WEBSOCKET
5959
6363 char * text_file_send;
6464 int non_interactive;
6565 int non_listening;
66 int quiet;
6667 unsigned int fragmentation;
6768 char * protocol;
6869 char * extensions;
103104 struct _config * config = (struct _config *)websocket_manager_user_data;
104105 char * file_content;
105106 size_t file_len;
106
107
107108 if (config->text_file_send != NULL) {
108109 file_content = read_file(config->text_file_send, &file_len);
109110 if (file_content != NULL && file_len > 0) {
140141 if (fgets(message, 256, stdin) != NULL) {
141142 if (o_strlen(message)) {
142143 if (0 == o_strncmp(message, "!q", o_strlen("!q"))) {
143 fprintf(stdout, "\b\bQuit uwsc\n");
144 if (!config->quiet) {
145 fprintf(stdout, "\b\bQuit uwsc\n");
146 }
144147 if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) {
145148 y_log_message(Y_LOG_LEVEL_ERROR, "Error sending close message");
146149 }
147150 } else {
148 fprintf(stdout, "\b\bSend '%.*s'\n> ", (int)(o_strlen(message)-1), message);
149 fflush(stdout);
151 if (!config->quiet) {
152 fprintf(stdout, "\b\bSend '%.*s'\n> ", (int)(o_strlen(message)-1), message);
153 fflush(stdout);
154 }
150155 if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(message)-1, message) != U_OK) {
151156 y_log_message(Y_LOG_LEVEL_ERROR, "Error sending message '%.*s'", (int)(o_strlen(message)-1), message);
152157 } else {
165170 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) {
166171 struct _websocket_message * last_message;
167172 struct _config * config = (struct _config *)websocket_incoming_user_data;
168
169 if (message->opcode == U_WEBSOCKET_OPCODE_CLOSE) {
170 fprintf(stdout, "\b\bConnection closed by the server, press <enter> to exit\n> ");
171 fflush(stdout);
172 }
173
173174 if (!config->non_listening) {
174175 if (message->opcode == U_WEBSOCKET_OPCODE_TEXT) {
175 fprintf(stdout, "\b\bServer message: '%.*s'\n> ", (int)message->data_len, message->data);
176 fflush(stdout);
176 if (!config->quiet) {
177 fprintf(stdout, "\b\bServer message: '%.*s'\n> ", (int)message->data_len, message->data);
178 fflush(stdout);
179 } else {
180 fprintf(stdout, "\b\b%.*s\n", (int)message->data_len, message->data);
181 }
177182 } else if (message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
178 fprintf(stdout, "\b\bServer sent binary message, length %zu\n> ", message->data_len);
179 fflush(stdout);
183 if (!config->quiet) {
184 fprintf(stdout, "\b\bServer sent binary message, length %zu\n> ", message->data_len);
185 fflush(stdout);
186 } else {
187 fprintf(stdout, "\b\bServer sent binary message, length %zu\n", message->data_len);
188 }
180189 }
181190 }
182191 last_message = ulfius_websocket_pop_first_message(websocket_manager->message_list_incoming);
217226 fprintf(output, "\tSpecify the Websocket extensions values, default none\n");
218227 fprintf(output, "-s --non-secure\n");
219228 fprintf(output, "\tDo not check server certificate\n");
229 fprintf(output, "-q --quiet\n");
230 fprintf(output, "\tQuiet mode, show only websocket messages\n");
220231 fprintf(output, "-v --version\n");
221232 fprintf(output, "\tPrint uwsc's current version\n\n");
222233 fprintf(output, "-h --help\n");
230241 config->text_file_send = NULL;
231242 config->non_interactive = 0;
232243 config->non_listening = 0;
244 config->quiet = 0;
233245 config->fragmentation = 0;
234246 config->protocol = NULL;
235247 config->extensions = NULL;
270282 int main (int argc, char ** argv) {
271283 struct _config * config;
272284 int next_option;
273 const char * short_options = "o:x:b:t:i::l::f:p:e::s::v::h::";
285 const char * short_options = "o:x:b:t:i::l::f:p:e::s::q::v::h::";
274286 static const struct option long_options[]= {
275287 {"output-log-file", required_argument, NULL, 'o'}, // Sets an output file for logging messages
276288 {"add-header", required_argument, NULL, 'x'}, // Add the specified header of the form 'key:value'
282294 {"protocol", required_argument, NULL, 'p'}, // Websocket protocol
283295 {"extensions", required_argument, NULL, 'e'}, // Websocket extensions
284296 {"non-secure", no_argument, NULL, 's'}, // Do not check server certificate
297 {"quiet", no_argument, NULL, 'v'}, // Quiet mode
285298 {"version", no_argument, NULL, 'v'}, // Show version
286299 {"help", no_argument, NULL, 'h'}, // print help
287300 {NULL, 0, NULL, 0}
288301 };
289 char * url = NULL;
290 struct _websocket_client_handler websocket_client_handler;
291
302 char * url = NULL, * extensions = NULL;
303 struct _websocket_client_handler websocket_client_handler = {NULL, NULL};
304
292305 config = o_malloc(sizeof(struct _config));
293306 if (config == NULL || !init_config(config)) {
294307 fprintf(stderr, "Error initialize configuration\n");
299312 do {
300313 char * key, * value;
301314 next_option = getopt_long(argc, argv, short_options, long_options, NULL);
302
315
303316 switch (next_option) {
304317 case 'o':
305318 config->log_path = o_strdup(optarg);
347360 case 's':
348361 config->request->check_server_certificate = 0;
349362 break;
363 case 'q':
364 config->quiet = 1;
365 break;
350366 case 'v':
351367 // Print version and exit
352368 fprintf(stdout, "%s\n", _UWSC_VERSION_);
361377 break;
362378 }
363379 } while (next_option != -1);
364
380
365381 if (config->log_path != NULL) {
366382 y_init_logs("Ulfius Websocket Client", Y_LOG_MODE_FILE, Y_LOG_LEVEL_DEBUG, config->log_path, "Start uwsc");
367383 }
373389 print_help(stderr);
374390 exit_program(&config, 1);
375391 }
376
377 if (ulfius_set_websocket_request(config->request, url, config->protocol, config->extensions) == U_OK) {
378 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) {
379 fprintf(stdout, "Websocket connected, you can send text messages of maximum 256 characters.\nTo exit uwsc, type !q<enter>\n> ");
380 fflush(stdout);
381 ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0);
382 fprintf(stdout, "Websocket closed\n");
383 ulfius_websocket_client_connection_close(&websocket_client_handler);
392
393 if (!o_strlen(config->extensions)) {
394 extensions = o_strdup("permessage-deflate");
395 } else {
396 extensions = msprintf("%s,permessage-deflate", config->extensions);
397 }
398 if (ulfius_set_websocket_request(config->request, url, config->protocol, extensions) == U_OK) {
399 if (ulfius_add_websocket_client_deflate_extension(&websocket_client_handler) == U_OK) {
400 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) {
401 if (!config->quiet) {
402 fprintf(stdout, "Websocket connected, you can send text messages of maximum 256 characters.\nTo exit uwsc, type !q<enter>\n> ");
403 fflush(stdout);
404 }
405 ulfius_websocket_client_connection_wait_close(&websocket_client_handler, 0);
406 if (!config->quiet) {
407 fprintf(stdout, "Websocket closed\n");
408 }
409 ulfius_websocket_client_connection_close(&websocket_client_handler);
410 } else {
411 fprintf(stderr, "Error connecting to websocket\n");
412 y_log_message(Y_LOG_LEVEL_ERROR, "Error connecting to websocket");
413 }
384414 } else {
385 fprintf(stderr, "Error connecting to websocket\n");
386 y_log_message(Y_LOG_LEVEL_ERROR, "Error connecting to websocket");
415 fprintf(stderr, "Error initializing websocket request\n");
416 y_log_message(Y_LOG_LEVEL_ERROR, "Error initializing websocket request");
387417 }
388418 } else {
389 fprintf(stderr, "Error initializing websocket request\n");
419 fprintf(stderr, "Error ulfius_add_websocket_client_deflate_extension\n");
390420 y_log_message(Y_LOG_LEVEL_ERROR, "Error initializing websocket request");
391421 }
392
422 o_free(extensions);
423
393424 if (config->log_path != NULL) {
394425 y_close_logs();
395426 }