New upstream version 2.7.0
Nicolas Mora
3 years ago
9 | 9 | - name: install dependencies |
10 | 10 | run: | |
11 | 11 | 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 | |
13 | 13 | sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0 |
14 | 14 | sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0 |
15 | 15 | - name: cppcheck |
393 | 393 | |
394 | 394 | # run tests with Makefile |
395 | 395 | sudo ldconfig |
396 | make clean debug check | |
396 | make clean debug |
47 | 47 | |
48 | 48 | - run: | |
49 | 49 | 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 | |
51 | 51 | |
52 | 52 | # prepare build folders |
53 | 53 | mkdir build |
5 | 5 | dist: bionic |
6 | 6 | addons: |
7 | 7 | 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 ] | |
9 | 9 | |
10 | 10 | jobs: |
11 | 11 | include: |
12 | 12 | - env: LABEL=gcc |
13 | 13 | compiler: gcc |
14 | - env: LABEL=cland | |
14 | - env: LABEL=clang | |
15 | 15 | compiler: clang |
16 | 16 | - 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 | |
17 | 28 | script: |
18 | 29 | - cppcheck --force --enable=warning,missingInclude --error-exitcode=1 . >build.log 2>&1 || (cat build.log && exit 1) |
19 | 30 | |
716 | 727 | - make clean debug GNUTLSFLAG=1 CURLFLAG=1 JANSSONFLAG=1 |
717 | 728 | - make clean debug GNUTLSFLAG=1 CURLFLAG=1 WEBSOCKETFLAG=1 |
718 | 729 | - make clean debug GNUTLSFLAG=1 CURLFLAG=1 |
719 | ||
720 | # run tests with Makefile | |
721 | - sudo ldconfig | |
722 | - make clean debug check | |
723 |
29 | 29 | - [Messages manipulation](#messages-manipulation) |
30 | 30 | - [Server-side websocket](#server-side-websocket) |
31 | 31 | - [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) | |
32 | 34 | - [Close a websocket communication](#close-a-websocket-communication) |
33 | 35 | - [Websocket status](#websocket-status) |
34 | 36 | - [Client-side websocket](#client-side-websocket) |
35 | 37 | - [Prepare the request](#prepare-the-request) |
38 | - [Built-in client extension permessage-deflate](#built-in-client-extension-permessage-deflate) | |
36 | 39 | - [Open the websocket](#open-the-websocket) |
37 | 40 | - [Websocket status](#websocket-status-1) |
38 | 41 | - [Outgoing request functions](#outgoing-request-functions) |
39 | 42 | - [Send HTTP request API](#send-http-request-api) |
40 | 43 | - [Send SMTP request API](#send-http-request-api) |
41 | 44 | - [struct _u_map API](#struct-_u_map-api) |
45 | - [What's new in Ulfius 2.7?](#whats-new-in-ulfius-27) | |
42 | 46 | - [What's new in Ulfius 2.6?](#whats-new-in-ulfius-26) |
43 | 47 | - [What's new in Ulfius 2.5?](#whats-new-in-ulfius-25) |
44 | 48 | - [What's new in Ulfius 2.4?](#whats-new-in-ulfius-24) |
859 | 863 | The callback returned value can have the following values: |
860 | 864 | |
861 | 865 | - `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. | |
862 | 867 | - `U_CALLBACK_COMPLETE`: The framework must complete the transaction and send the response to the client without calling any further callback function. |
863 | 868 | - `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. |
864 | 869 | - `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. |
1217 | 1222 | */ |
1218 | 1223 | struct _websocket_message { |
1219 | 1224 | 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 | |
1220 | 1226 | uint8_t opcode; // opcode of the message: U_WEBSOCKET_OPCODE_TEXT, U_WEBSOCKET_OPCODE_BINARY, U_WEBSOCKET_OPCODE_PING, U_WEBSOCKET_OPCODE_PONG |
1221 | 1227 | uint8_t has_mask; // Flag to specify if the message has a mask |
1222 | 1228 | uint8_t mask[4]; // mask |
1344 | 1350 | |
1345 | 1351 | For each of these callback function, you can specify a `*_user_data` pointer containing any data you need. |
1346 | 1352 | |
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 | ||
1347 | 1508 | #### Close a websocket communication <a name="close-a-websocket-communication"></a> |
1348 | 1509 | |
1349 | 1510 | To close a websocket communication from the server, you can do one of the following: |
1423 | 1584 | Any body parameter or body raw value will be ignored, the header `Content-Length` will be set to 0. |
1424 | 1585 | |
1425 | 1586 | 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) | |
1426 | 1604 | |
1427 | 1605 | #### Opening the websocket connection <a name="opening-the-websocket-connection"></a> |
1428 | 1606 | |
1804 | 1982 | int u_map_count(const struct _u_map * source); |
1805 | 1983 | ``` |
1806 | 1984 | |
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 | ||
1807 | 2002 | ## What's new in Ulfius 2.6? <a name="whats-new-in-ulfius-26"></a> |
1808 | 2003 | |
1809 | 2004 | Add IPv6 support. |
2087 | 2282 | |
2088 | 2283 | ```C |
2089 | 2284 | #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 | |
2093 | 2289 | ``` |
2094 | 2290 | |
2095 | 2291 | If you want more details on the multiple callback functions, check the [documentation](#callback-functions-return-value). |
0 | 0 | # 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). | |
1 | 11 | |
2 | 12 | ## 2.6.9 |
3 | 13 |
28 | 28 | set(PROJECT_HOMEPAGE_URL "https://github.com/babelouest/ulfius/") |
29 | 29 | set(PROJECT_BUGREPORT_PATH "https://github.com/babelouest/ulfius/issues") |
30 | 30 | 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") | |
33 | 33 | |
34 | 34 | set(PROJECT_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}") |
35 | 35 | set(PROJECT_VERSION_MAJOR ${LIBRARY_VERSION_MAJOR}) |
153 | 153 | |
154 | 154 | if (WITH_WEBSOCKET) |
155 | 155 | 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 () | |
156 | 162 | else () |
157 | 163 | set(U_DISABLE_WEBSOCKET ON) |
158 | 164 | endif () |
0 | libelous# Install Ulfius | |
0 | # Install Ulfius | |
1 | 1 | |
2 | 2 | - [Distribution packages](#distribution-packages) |
3 | 3 | - [Pre-compiled packages](#pre-compiled-packages) |
21 | 21 | |
22 | 22 | ## Pre-compiled packages |
23 | 23 | |
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`. | |
25 | 25 | |
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: | |
27 | 27 | |
28 | 28 | ```shell |
29 | 29 | $ 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 | |
32 | 32 | $ 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 | |
35 | 35 | ``` |
36 | 36 | |
37 | 37 | If there's no package available for your distribution, you can recompile it manually using `CMake` or `Makefile`. |
47 | 47 | - libgnutls, libgcrypt (optional), required for Websockets and https support |
48 | 48 | - libcurl (optional), required to send http/smtp requests |
49 | 49 | - libsystemd (optional), required for [Yder](https://github.com/babelouest/yder) to log messages in journald |
50 | - zlib (optional), required for Websockets support | |
50 | 51 | |
51 | 52 | Note: the build stacks require a compiler (`gcc` or `clang`), `make`, `cmake` (if using CMake build), and `pkg-config`. |
52 | 53 | |
53 | 54 | For example, to install all the external dependencies on Debian Stretch, run as root: |
54 | 55 | |
55 | 56 | ```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 | |
57 | 58 | ``` |
58 | 59 | |
59 | 60 | ### Good ol' Makefile |
36 | 36 | cd $(EXAMPLES_LOCATION) && $(MAKE) clean |
37 | 37 | cd $(UWSC_LOCATION) && $(MAKE) clean |
38 | 38 | 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* | |
40 | 41 | |
41 | 42 | examples: |
42 | 43 | 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); |
0 | 0 | # Token validation for resource service based on [Ulfius](https://github.com/babelouest/ulfius) framework |
1 | 1 | |
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. | |
3 | 3 | |
4 | [libjwt](https://github.com/benmcollins/libjwt) is required. | |
4 | [rhonabwy](https://github.com/babelouest/rhonabwy) is required. | |
5 | 5 | |
6 | 6 | To use this file, you must create a `struct _glewlwyd_resource_config` with your specific parameters: |
7 | 7 | |
9 | 9 | struct _glewlwyd_resource_config { |
10 | 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 | 11 | 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/ | |
14 | 14 | char * realm; // Optional, a realm value that will be sent back to the client |
15 | 15 | unsigned short accept_access_token; // required, accept type access_token |
16 | 16 | unsigned short accept_client_token; // required, accept type client_token |
21 | 21 | |
22 | 22 | ```C |
23 | 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\"}"); | |
24 | 28 | g_config.method = G_METHOD_HEADER; |
25 | 29 | 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; | |
28 | 32 | g_config.realm = "example"; |
29 | 33 | g_config.accept_access_token = 1; |
30 | 34 | g_config.accept_client_token = 0; |
1 | 1 | * |
2 | 2 | * Glewlwyd SSO Access Token token check |
3 | 3 | * |
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 | |
7 | 7 | * |
8 | 8 | * The MIT License (MIT) |
9 | 9 | * |
31 | 31 | #include <time.h> |
32 | 32 | #include <orcania.h> |
33 | 33 | #include <ulfius.h> |
34 | #include <jansson.h> | |
35 | 34 | |
36 | 35 | #include "glewlwyd_resource.h" |
37 | 36 | |
47 | 46 | } |
48 | 47 | |
49 | 48 | /** |
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 | /** | |
50 | 159 | * check if bearer token has some of the specified scope |
51 | 160 | */ |
52 | 161 | int callback_check_glewlwyd_access_token (const struct _u_request * request, struct _u_response * response, void * user_data) { |
66 | 175 | } |
67 | 176 | break; |
68 | 177 | 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) { | |
70 | 179 | token_value = u_map_get(request->map_post_body, BODY_URL_PARAMETER); |
71 | 180 | } |
72 | 181 | break; |
119 | 228 | } |
120 | 229 | return res; |
121 | 230 | } |
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 | } |
1 | 1 | * |
2 | 2 | * Glewlwyd SSO Access Token token check |
3 | 3 | * |
4 | * Copyright 2016-2019 Nicolas Mora <mail@babelouest.org> | |
4 | * Copyright 2016-2020 Nicolas Mora <mail@babelouest.org> | |
5 | 5 | * |
6 | * Version 20190810 | |
6 | * Version 20200508 | |
7 | 7 | * |
8 | 8 | * The MIT License (MIT) |
9 | 9 | * |
26 | 26 | * SOFTWARE. |
27 | 27 | * |
28 | 28 | */ |
29 | #include <jwt.h> | |
29 | #include <jansson.h> | |
30 | #include <rhonabwy.h> | |
30 | 31 | |
31 | 32 | #define G_TOKEN_OK 0 |
32 | 33 | #define G_TOKEN_ERROR 1 |
47 | 48 | struct _glewlwyd_resource_config { |
48 | 49 | int method; |
49 | 50 | char * oauth_scope; |
50 | char * jwt_decode_key; | |
51 | jwt_alg_t jwt_alg; | |
51 | jwt_t * jwt; | |
52 | jwa_alg alg; | |
52 | 53 | char * realm; |
53 | 54 | unsigned short accept_access_token; |
54 | 55 | unsigned short accept_client_token; |
57 | 58 | /** |
58 | 59 | * |
59 | 60 | * 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 | |
62 | 61 | * |
63 | 62 | */ |
64 | 63 | 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 | ``` |
+452
-0
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 | } |
+118
-0
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 | |
1 | 1 | |
2 | 2 | Provides a simple static file server. `user_data` must be initialized with a `struct static_file_config` containing the following informations: |
3 | 3 | |
4 | 4 | - `files_path`: path to the DocumentRoot folder, can be relative or absolute |
5 | - `url_prefix`: prefix used to access the callback function | |
5 | 6 | - `mime_types`: a `struct _u_map` containing a set of mime-types with file extension as key and mime-type as value |
6 | 7 | - `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 | ``` |
1 | 1 | * |
2 | 2 | * Static file server Ulfius callback |
3 | 3 | * |
4 | * Copyright 2017-2018 Nicolas Mora <mail@babelouest.org> | |
4 | * Copyright 2017-2020 Nicolas Mora <mail@babelouest.org> | |
5 | 5 | * |
6 | * Version 20181110 | |
7 | * | |
6 | * Version 20201028 | |
7 | * | |
8 | 8 | * The MIT License (MIT) |
9 | 9 | * |
10 | 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
25 | 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
26 | 26 | * SOFTWARE. |
27 | 27 | * |
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 | * | |
28 | 80 | */ |
29 | 81 | |
30 | 82 | #include <string.h> |
83 | #include <orcania.h> | |
84 | #include <yder.h> | |
31 | 85 | #include <ulfius.h> |
32 | 86 | |
33 | 87 | #include "static_file_callback.h" |
48 | 102 | * Streaming callback function to ease sending large files |
49 | 103 | */ |
50 | 104 | static ssize_t callback_static_file_stream(void * cls, uint64_t pos, char * buf, size_t max) { |
105 | (void)(pos); | |
51 | 106 | if (cls != NULL) { |
52 | 107 | return fread (buf, 1, max, (FILE *)cls); |
53 | 108 | } else { |
74 | 129 | const char * content_type; |
75 | 130 | |
76 | 131 | /* |
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 | |
78 | 133 | */ |
79 | if (response->shared_data != NULL) { | |
134 | if (request->callback_position > 0) { | |
80 | 135 | 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) { | |
84 | 137 | file_requested = o_strdup(request->http_url); |
85 | 138 | url_dup_save = file_requested; |
86 | 139 |
0 | 0 | /** |
1 | 1 | * |
2 | * Version 20181110 | |
2 | * Static file server Ulfius callback | |
3 | 3 | * |
4 | * Copyright 2017-2020 Nicolas Mora <mail@babelouest.org> | |
5 | * | |
6 | * Version 20201028 | |
7 | * | |
4 | 8 | * The MIT License (MIT) |
5 | 9 | * |
6 | 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
20 | 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | 26 | * SOFTWARE. |
23 | * | |
27 | * | |
24 | 28 | * struct static_file_config must be initialized with proper values |
25 | 29 | * files_path: path (relative or absolute) to the DocumentRoot folder |
26 | 30 | * url_prefix: prefix used to access the callback function |
75 | 79 | * |
76 | 80 | */ |
77 | 81 | |
78 | #ifndef _STATIC_FILE | |
79 | #define _STATIC_FILE | |
82 | #ifndef _U_STATIC_FILE | |
83 | #define _U_STATIC_FILE | |
80 | 84 | |
81 | 85 | #define STATIC_FILE_CHUNK 256 |
82 | 86 |
47 | 47 | add_definitions(-D_GNU_SOURCE) |
48 | 48 | endif () |
49 | 49 | |
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 | ||
50 | 60 | include(FindUlfius) |
51 | 61 | set(ULFIUS_MIN_VERSION "2.6") |
52 | 62 | find_package(Ulfius ${ULFIUS_MIN_VERSION} REQUIRED) |
62 | 72 | |
63 | 73 | # examples |
64 | 74 | |
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/) | |
66 | 76 | 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}) | |
67 | 80 | |
68 | 81 | add_executable(simple_example ${CMAKE_CURRENT_SOURCE_DIR}/simple_example/simple_example.c) |
69 | 82 | target_link_libraries(simple_example ${LIBS}) |
86 | 99 | add_executable(injection_example ${CMAKE_CURRENT_SOURCE_DIR}/injection_example/injection_example.c) |
87 | 100 | target_link_libraries(injection_example ${LIBS}) |
88 | 101 | |
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) | |
90 | 103 | target_link_libraries(sheep_counter ${LIBS}) |
91 | 104 | endif () |
92 | 105 | |
105 | 118 | endif () |
106 | 119 | |
107 | 120 | 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/") | |
109 | 123 | 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/") | |
111 | 126 | target_link_libraries(websocket_client ${LIBS}) |
112 | 127 | |
113 | 128 | add_executable(auth_server ${CMAKE_CURRENT_SOURCE_DIR}/auth_example/auth_server.c) |
123 | 138 | message(STATUS "Outgoing requests support: ${WITH_CURL}") |
124 | 139 | message(STATUS "Jansson library support: ${WITH_JANSSON}") |
125 | 140 | message(STATUS "Yder library support: ${WITH_YDER}") |
126 |
15 | 15 | ULFIUS_LOCATION=../../src |
16 | 16 | ULFIUS_INCLUDE=../../include |
17 | 17 | 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) | |
20 | 22 | |
21 | 23 | ifndef YDERFLAG |
22 | 24 | LIBS+= -lyder |
34 | 36 | ../../src/libulfius.so: |
35 | 37 | cd $(ULFIUS_LOCATION) && $(MAKE) debug CURLFLAG=1 GNUTLSFLAG=1 |
36 | 38 | |
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 | ||
37 | 45 | sheep_counter.o: sheep_counter.c |
38 | 46 | $(CC) $(CFLAGS) sheep_counter.c -DDEBUG -g -O0 |
39 | 47 | |
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) | |
42 | 50 | |
43 | 51 | test: sheep_counter |
44 | 52 | LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./sheep_counter |
17 | 17 | |
18 | 18 | #include <ulfius.h> |
19 | 19 | #include <u_example.h> |
20 | ||
21 | #include "static_compressed_inmemory_website_callback.h" | |
22 | #include "http_compression_callback.h" | |
20 | 23 | |
21 | 24 | #define PORT 7437 |
22 | 25 | #define PREFIX "/sheep" |
23 | 26 | #define FILE_PREFIX "/upload" |
24 | 27 | #define STATIC_FOLDER "static" |
25 | 28 | |
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); | |
51 | 29 | /** |
52 | 30 | * decode a u_map into a string |
53 | 31 | */ |
82 | 60 | } |
83 | 61 | |
84 | 62 | /** |
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 | /** | |
165 | 63 | * Start a new counter by setting the number of sheeps to a specific value |
166 | 64 | * return the current number of sheeps in a json object |
167 | 65 | */ |
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) { | |
169 | 67 | json_t * json_nb_sheep = ulfius_get_json_body_request(request, NULL), * json_body = NULL; |
170 | 68 | |
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; | |
176 | 70 | |
177 | 71 | if (json_nb_sheep != NULL) { |
178 | 72 | * nb_sheep = json_integer_value(json_object_get(json_nb_sheep,"nbsheep")); |
192 | 86 | * Reset the number of sheeps to 0 |
193 | 87 | * return the current number of sheeps in a json object |
194 | 88 | */ |
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) { | |
196 | 90 | 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; | |
202 | 92 | * nb_sheep = 0; |
203 | 93 | |
204 | 94 | json_body = json_object(); |
213 | 103 | * Adds one sheep |
214 | 104 | * return the current number of sheeps in a json object |
215 | 105 | */ |
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) { | |
217 | 107 | 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; | |
223 | 109 | |
224 | 110 | (*nb_sheep)++; |
225 | 111 | |
232 | 118 | } |
233 | 119 | |
234 | 120 | /** |
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 | /** | |
274 | 121 | * upload a file |
275 | 122 | */ |
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) { | |
277 | 124 | char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie), |
278 | 125 | * post_params = print_map(request->map_post_body); |
279 | 126 | |
291 | 138 | /** |
292 | 139 | * File upload callback function |
293 | 140 | */ |
294 | int file_upload_callback (const struct _u_request * request, | |
141 | static int file_upload_callback (const struct _u_request * request, | |
295 | 142 | const char * key, |
296 | 143 | const char * filename, |
297 | 144 | const char * content_type, |
303 | 150 | 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); |
304 | 151 | return U_OK; |
305 | 152 | } |
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 | } |
36 | 36 | $("#counter").val(counter); |
37 | 37 | var left = (Math.random() * ($(document).width())).toFixed()-300; |
38 | 38 | 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'>"); | |
40 | 40 | $("#sheep-"+counter).css({"position":"absolute","top": top + "px", "left": left + "px"}); |
41 | 41 | } |
42 | 42 | }); |
2 | 2 | # |
3 | 3 | # Makefile used to build the software |
4 | 4 | # |
5 | # Copyright 2017 Nicolas Mora <mail@babelouest.org> | |
5 | # Copyright 2017-2020 Nicolas Mora <mail@babelouest.org> | |
6 | 6 | # |
7 | 7 | # This program is free software; you can redistribute it and/or |
8 | 8 | # modify it under the terms of the MIT License |
15 | 15 | ULFIUS_LOCATION=../../src |
16 | 16 | ULFIUS_INCLUDE=../../include |
17 | 17 | 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) | |
19 | 19 | 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 | |
21 | 23 | #SECUREFLAG=-https test.key test.pem |
22 | 24 | |
23 | 25 | ifndef YDERFLAG |
33 | 35 | |
34 | 36 | debug: websocket_server websocket_client |
35 | 37 | |
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 | |
38 | 40 | |
39 | 41 | static_file_callback.o: $(STATIC_FILE_LOCATION)/static_file_callback.c |
40 | 42 | $(CC) $(CFLAGS) $(STATIC_FILE_LOCATION)/static_file_callback.c |
41 | 43 | |
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 | |
43 | 48 | $(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) | |
45 | 50 | |
46 | websocket_client: ../../src/libulfius.so websocket_client.c | |
51 | websocket_client: $(LIBULFIUS) websocket_client.c | |
47 | 52 | $(CC) $(CFLAGS) websocket_client.c |
48 | 53 | $(CC) -o websocket_client websocket_client.o $(LIBS) |
49 | 54 |
23 | 23 | |
24 | 24 | function connectSocket(echo) { |
25 | 25 | 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":"")); | |
27 | 27 | } 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":"")); | |
29 | 29 | } |
30 | 30 | mySocket.onmessage = function (event) { |
31 | 31 | if (event.data instanceof Blob) { |
97 | 97 | var curFileData = ev2.target.result; |
98 | 98 | if (curFileData) { |
99 | 99 | 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"); | |
101 | 101 | } else { |
102 | mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file", ["file"]); | |
102 | mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file"); | |
103 | 103 | } |
104 | 104 | mySocket.binaryType = "arraybuffer"; |
105 | 105 | mySocket.onopen = function () { |
125 | 125 | var curFileText = ev2.target.result; |
126 | 126 | if (curFileText) { |
127 | 127 | 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"); | |
129 | 129 | } else { |
130 | mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file", ["file"]); | |
130 | mySocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/websocket/file"); | |
131 | 131 | } |
132 | 132 | mySocket.binaryType = "arraybuffer"; |
133 | 133 | mySocket.onopen = function () { |
111 | 111 | int main(int argc, char ** argv) { |
112 | 112 | struct _u_request request; |
113 | 113 | struct _u_response response; |
114 | struct _websocket_client_handler websocket_client_handler; | |
114 | struct _websocket_client_handler websocket_client_handler = {NULL, NULL}; | |
115 | 115 | char * websocket_user_data = o_strdup("my user data"); |
116 | 116 | char * url = (argc>1&&0==o_strcmp("-https", argv[1]))?"wss://localhost:" PORT PREFIX_WEBSOCKET:"ws://localhost:" PORT PREFIX_WEBSOCKET; |
117 | 117 | |
118 | 118 | y_init_logs("websocket_client", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_client"); |
119 | 119 | ulfius_init_request(&request); |
120 | 120 | 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); | |
122 | 123 | request.check_server_certificate = 0; |
123 | 124 | 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) { |
124 | 125 | y_log_message(Y_LOG_LEVEL_DEBUG, "Wait for user to press <enter> to close the program"); |
18 | 18 | #include <ulfius.h> |
19 | 19 | #include <u_example.h> |
20 | 20 | |
21 | #include "static_file_callback.h" | |
21 | #include "static_compressed_inmemory_website_callback.h" | |
22 | 22 | |
23 | 23 | #define PORT 9275 |
24 | 24 | #define PREFIX_WEBSOCKET "/websocket" |
67 | 67 | int main(int argc, char ** argv) { |
68 | 68 | int ret; |
69 | 69 | struct _u_instance instance; |
70 | struct _static_file_config file_config; | |
70 | struct _u_compressed_inmemory_website_config file_config; | |
71 | 71 | char * cert_file = NULL, * key_file = NULL; |
72 | 72 | |
73 | 73 | y_init_logs("websocket_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_example"); |
74 | 74 | |
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); | |
113 | 116 | } 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 | ||
136 | 135 | y_close_logs(); |
137 | 136 | |
138 | 137 | return 0; |
272 | 271 | int ret; |
273 | 272 | |
274 | 273 | 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); | |
275 | 275 | return U_CALLBACK_CONTINUE; |
276 | 276 | } else { |
277 | 277 | return U_CALLBACK_ERROR; |
284 | 284 | |
285 | 285 | y_log_message(Y_LOG_LEVEL_DEBUG, "Client connected to echo websocket"); |
286 | 286 | 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); | |
287 | 288 | return U_CALLBACK_CONTINUE; |
288 | 289 | } else { |
289 | 290 | return U_CALLBACK_ERROR; |
294 | 295 | int ret; |
295 | 296 | |
296 | 297 | 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); | |
297 | 299 | return U_CALLBACK_CONTINUE; |
298 | 300 | } else { |
299 | 301 | return U_CALLBACK_ERROR; |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * u_private.h: private structures and functions declarations |
7 | * | |
7 | * | |
8 | 8 | * Copyright 2015-2017 Nicolas Mora <mail@babelouest.org> |
9 | 9 | * |
10 | 10 | * This program is free software; you can redistribute it and/or |
19 | 19 | * |
20 | 20 | * You should have received a copy of the GNU General Public |
21 | 21 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
22 | * | |
22 | * | |
23 | 23 | */ |
24 | 24 | |
25 | 25 | #ifndef __U_PRIVATE_H__ |
79 | 79 | int ulfius_set_response_cookie(struct MHD_Response * mhd_response, const struct _u_response * response); |
80 | 80 | |
81 | 81 | /** |
82 | * The utf8_check() function scans the '\0'-terminated string starting | |
82 | * The utf8_check() function scans the string starting | |
83 | 83 | * at s. It returns a pointer to the first byte of the first malformed |
84 | 84 | * or overlong UTF-8 sequence found, or NULL if the string contains |
85 | 85 | * only correct UTF-8. It also spots UTF-8 sequences that could cause |
94 | 94 | * are no doubt performance optimizations possible for certain CPUs. |
95 | 95 | * |
96 | 96 | * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30 |
97 | * Nicolas Mora <mail@babelouest.org> | |
97 | 98 | * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html |
98 | 99 | */ |
99 | const unsigned char * utf8_check(const char * s_orig); | |
100 | const unsigned char * utf8_check(const char * s_orig, size_t len); | |
100 | 101 | |
101 | 102 | #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); | |
102 | 114 | |
103 | 115 | /** |
104 | 116 | * Websocket callback function for MHD |
167 | 179 | int ulfius_close_websocket(struct _websocket * websocket); |
168 | 180 | |
169 | 181 | /** |
170 | * Run the websocket manager in a separated detached thread | |
171 | */ | |
172 | void * ulfius_thread_websocket_manager_run(void * args); | |
173 | ||
174 | /** | |
175 | 182 | * Add a websocket in the list of active websockets of the instance |
176 | 183 | */ |
177 | 184 | int ulfius_instance_add_websocket_active(struct _u_instance * instance, struct _websocket * websocket); |
179 | 186 | /** |
180 | 187 | * Remove a websocket from the list of active websockets of the instance |
181 | 188 | */ |
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); | |
183 | 190 | |
184 | 191 | /** |
185 | 192 | * Initialize a struct _websocket |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * ulfius-cfg.h.in: configuration file |
7 | * | |
7 | * | |
8 | 8 | * Copyright 2018-2020 Nicolas Mora <mail@babelouest.org> |
9 | 9 | * |
10 | 10 | * This program is free software; you can redistribute it and/or |
19 | 19 | * |
20 | 20 | * You should have received a copy of the GNU General Public |
21 | 21 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
22 | * | |
22 | * | |
23 | 23 | */ |
24 | 24 | #ifndef _ULFIUS_CFG_H_ |
25 | 25 | #define _ULFIUS_CFG_H_ |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * @file ulfius.h |
3 | 3 | * @brief Ulfius framework |
4 | * | |
4 | * | |
5 | 5 | * REST framework library |
6 | * | |
6 | * | |
7 | 7 | * ulfius.h: public structures and functions declarations |
8 | * | |
8 | * | |
9 | 9 | * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org> |
10 | 10 | * |
11 | 11 | * This program is free software; you can redistribute it and/or |
20 | 20 | * |
21 | 21 | * You should have received a copy of the GNU General Public |
22 | 22 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
23 | * | |
23 | * | |
24 | 24 | */ |
25 | 25 | |
26 | 26 | #ifndef __ULFIUS_H__ |
45 | 45 | |
46 | 46 | #ifndef U_DISABLE_WEBSOCKET |
47 | 47 | #include <poll.h> |
48 | #include <zlib.h> | |
48 | 49 | #ifndef POLLRDHUP |
49 | 50 | #define POLLRDHUP 0x2000 |
50 | 51 | #endif |
115 | 116 | #define U_ERROR_DISCONNECTED 7 ///< Connection closed |
116 | 117 | |
117 | 118 | #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 | |
121 | 123 | |
122 | 124 | #define U_COOKIE_SAME_SITE_NONE 0 ///< Set same_site cookie property to 0 |
123 | 125 | #define U_COOKIE_SAME_SITE_STRICT 1 ///< Set same_site cookie property to strict |
215 | 217 | }; |
216 | 218 | |
217 | 219 | /** |
218 | * | |
219 | * @struct _u_request request parameters | |
220 | * | |
221 | * @struct _u_request | |
220 | 222 | * @brief definition of the parameters available in a struct _u_request |
221 | * | |
223 | * | |
222 | 224 | */ |
223 | 225 | struct _u_request { |
224 | 226 | char * http_protocol; /* !< http protocol used (1.0 or 1.1) */ |
255 | 257 | }; |
256 | 258 | |
257 | 259 | /** |
258 | * | |
259 | * @struct _u_response response parameters | |
260 | * | |
261 | * @struct _u_response | |
260 | 262 | * @brief definition of the parameters available in a struct _u_response |
261 | * | |
263 | * | |
262 | 264 | */ |
263 | 265 | struct _u_response { |
264 | 266 | long status; /* !< HTTP status code (200, 404, 500, etc) */ |
280 | 282 | }; |
281 | 283 | |
282 | 284 | /** |
283 | * | |
284 | * @struct _u_endpoint endpoint definition | |
285 | * | |
286 | * @struct _u_endpoint | |
285 | 287 | * @brief Contains all informations needed for an endpoint |
286 | * | |
288 | * | |
287 | 289 | */ |
288 | 290 | struct _u_endpoint { |
289 | 291 | char * http_method; /* !< http verb (GET, POST, PUT, etc.) in upper case */ |
297 | 299 | }; |
298 | 300 | |
299 | 301 | /** |
300 | * | |
301 | * @struct _u_instance Ulfius instance definition | |
302 | * | |
303 | * @struct _u_instance | |
302 | 304 | * @brief Contains the needed data for an ulfius instance to work |
303 | * | |
305 | * | |
304 | 306 | */ |
305 | 307 | struct _u_instance { |
306 | 308 | struct MHD_Daemon * mhd_daemon; /* !< pointer to the libmicrohttpd daemon */ |
321 | 323 | size_t max_post_body_size; /* !< maximum size for the entire post body, 0 means no limit, default 0 */ |
322 | 324 | void * websocket_handler; /* !< handler for the websocket structure */ |
323 | 325 | 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, | |
331 | 333 | void * cls); |
332 | 334 | void * file_upload_cls; /* !< any pointer to pass to the file_upload_callback function */ |
333 | 335 | 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 */ |
359 | 361 | **********************************/ |
360 | 362 | |
361 | 363 | /** |
362 | * @defgroup memory | |
364 | * @defgroup mem memory | |
363 | 365 | * memory management function |
364 | 366 | * @{ |
365 | 367 | */ |
396 | 398 | |
397 | 399 | /** |
398 | 400 | * ulfius_init_instance |
399 | * | |
401 | * | |
400 | 402 | * Initialize a struct _u_instance * with default values |
401 | 403 | * Binds to IPV4 addresses only |
404 | * @param u_instance the ulfius instance to initialize | |
402 | 405 | * @param port tcp port to bind to, must be between 1 and 65535 |
403 | 406 | * @param bind_address IPv4 address to listen to, optional, the reference is borrowed, the structure isn't copied |
404 | 407 | * @param default_auth_realm default realm to send to the client on authentication error |
409 | 412 | #if MHD_VERSION >= 0x00095208 |
410 | 413 | /** |
411 | 414 | * ulfius_init_instance_ipv6 |
412 | * | |
415 | * | |
413 | 416 | * Initialize a struct _u_instance * with default values |
414 | 417 | * Binds to IPV6 and IPV4 addresses or IPV6 addresses only |
415 | 418 | * @param port tcp port to bind to, must be between 1 and 65535 |
423 | 426 | |
424 | 427 | /** |
425 | 428 | * ulfius_clean_instance |
426 | * | |
429 | * | |
427 | 430 | * Clean memory allocated by a struct _u_instance * |
428 | 431 | * @param u_instance an Ulfius instance |
429 | 432 | */ |
432 | 435 | /** |
433 | 436 | * ulfius_start_framework |
434 | 437 | * Initializes the framework and run the webservice based on the parameters given |
435 | * | |
438 | * | |
436 | 439 | * @param u_instance pointer to a struct _u_instance that describe its port and bind address |
437 | 440 | * @return U_OK on success |
438 | 441 | */ |
441 | 444 | /** |
442 | 445 | * ulfius_start_secure_framework |
443 | 446 | * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection |
444 | * | |
447 | * | |
445 | 448 | * @param u_instance pointer to a struct _u_instance that describe its port and bind address |
446 | 449 | * @param key_pem private key for the server |
447 | 450 | * @param cert_pem server certificate |
454 | 457 | * ulfius_start_secure_ca_trust_framework |
455 | 458 | * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection |
456 | 459 | * And using a root server to authenticate client connections |
457 | * | |
460 | * | |
458 | 461 | * @param u_instance pointer to a struct _u_instance that describe its port and bind address |
459 | 462 | * @param key_pem private key for the server |
460 | 463 | * @param cert_pem server certificate |
476 | 479 | * i.e.: you're on your own |
477 | 480 | * @param u_instance pointer to a struct _u_instance that describe its port and bind address |
478 | 481 | * @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, | |
480 | 483 | * - MUST contain an option with the fllowing value: {.option = MHD_OPTION_NOTIFY_COMPLETED; .value = (intptr_t)mhd_request_completed; .ptr_value = NULL;} |
481 | 484 | * - MUST contain an option with the fllowing value: {.option = MHD_OPTION_URI_LOG_CALLBACK; .value = (intptr_t)ulfius_uri_logger; .ptr_value = NULL;} |
482 | 485 | * - MUST end with a terminal struct MHD_OptionItem: {.option = MHD_OPTION_END; .value = 0; .ptr_value = NULL;} |
492 | 495 | |
493 | 496 | /** |
494 | 497 | * ulfius_stop_framework |
495 | * | |
498 | * | |
496 | 499 | * Stop the webservice |
497 | 500 | * @param u_instance pointer to a struct _u_instance that describe its port and bind address |
498 | 501 | * @return U_OK on success |
501 | 504 | |
502 | 505 | /** |
503 | 506 | * ulfius_set_upload_file_callback_function |
504 | * | |
507 | * | |
505 | 508 | * Set the callback function to handle file upload |
506 | 509 | * Used to facilitate large files upload management |
507 | 510 | * The callback function file_upload_callback will be called |
508 | 511 | * multiple times, with the uploaded file in striped in parts |
509 | * | |
512 | * | |
510 | 513 | * Warning: If this function is used, all the uploaded files |
511 | 514 | * for the instance will be managed via this function, and they |
512 | 515 | * will no longer be available in the struct _u_request in the |
513 | 516 | * ulfius callback function afterwards. |
514 | * | |
517 | * | |
515 | 518 | * Thanks to Thad Phetteplace for the help on this feature |
516 | * | |
519 | * | |
517 | 520 | * @param u_instance pointer to a struct _u_instance that describe its port and bind address |
518 | 521 | * @param file_upload_callback Pointer to a callback function that will handle all file uploads |
519 | 522 | * @param cls a pointer that will be passed to file_upload_callback each tim it's called |
520 | 523 | * @return U_OK on success |
521 | 524 | */ |
522 | 525 | 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, | |
531 | 534 | void * cls), |
532 | 535 | void * cls); |
533 | 536 | |
550 | 553 | * Can be done during the execution of the webservice for injection |
551 | 554 | * @param u_instance pointer to a struct _u_instance that describe its port and bind address |
552 | 555 | * @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 | |
554 | 557 | */ |
555 | 558 | int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint); |
556 | 559 | |
606 | 609 | * Set the default endpoint |
607 | 610 | * This endpoint will be called if no endpoint match the url called |
608 | 611 | * @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. | |
615 | 614 | * @param user_data a pointer to a data or a structure that will be available in callback_function |
616 | 615 | * to remove a default endpoint, call ulfius_set_default_endpoint with NULL parameter for callback_function |
617 | 616 | * @return U_OK on success |
735 | 734 | * @param mail_body email body (mandatory) |
736 | 735 | * @return U_OK on success |
737 | 736 | */ |
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, | |
749 | 748 | const char * mail_body); |
750 | 749 | |
751 | 750 | /** |
767 | 766 | * @return U_OK on success |
768 | 767 | */ |
769 | 768 | |
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, | |
780 | 779 | const char * content_type, |
781 | const char * subject, | |
780 | const char * subject, | |
782 | 781 | const char * mail_body); |
783 | 782 | #endif |
784 | 783 | |
801 | 800 | * @param expires the expiration date of the ccokie in ISO format (optional) |
802 | 801 | * @param max_age the maximum age of the cookie in seconds (optional) |
803 | 802 | * @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) | |
805 | 804 | * @param secure wether the cookie must be secure or not (optional) |
806 | 805 | * @param http_only wether the cookie must be used only for http requests or not (optional) |
807 | 806 | * @return U_OK on success |
808 | 807 | */ |
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, | |
810 | 809 | const char * domain, const char * path, const int secure, const int http_only); |
811 | 810 | |
812 | 811 | /** |
818 | 817 | * @param expires the expiration date of the ccokie in ISO format (optional) |
819 | 818 | * @param max_age the maximum age of the cookie in seconds (optional) |
820 | 819 | * @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) | |
822 | 821 | * @param secure wether the cookie must be secure or not (optional) |
823 | 822 | * @param http_only wether the cookie must be used only for http requests or not (optional) |
824 | 823 | * @param same_site parameter must have one of the following values: |
827 | 826 | * - U_COOKIE_SAME_SITE_LAX - SameSite attribute set to 'Lax' |
828 | 827 | * @return U_OK on success |
829 | 828 | */ |
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, | |
831 | 830 | const char * domain, const char * path, const int secure, const int http_only, const int same_site); |
832 | 831 | |
833 | 832 | /** |
929 | 928 | * @param stream_user_data a user-defined pointer that will be available in stream_callback and stream_callback_free |
930 | 929 | * @return U_OK on success |
931 | 930 | */ |
932 | int ulfius_set_stream_response(struct _u_response * response, | |
931 | int ulfius_set_stream_response(struct _u_response * response, | |
933 | 932 | const unsigned int status, |
934 | 933 | ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max), |
935 | 934 | void (* stream_callback_free) (void * stream_user_data), |
1141 | 1140 | * ulfius_set_json_body_response |
1142 | 1141 | * Add a json_t j_body to a response |
1143 | 1142 | * @param response the response to retrieve the JSON data |
1143 | * @param status the HTTP status for the response | |
1144 | 1144 | * @param j_body a json_t to stringify in the body |
1145 | 1145 | * @return U_OK on success |
1146 | 1146 | */ |
1158 | 1158 | */ |
1159 | 1159 | |
1160 | 1160 | /************************************************************************ |
1161 | * _u_map declarations * | |
1161 | * _u_map declarations * | |
1162 | 1162 | * _u_map is a simple map structure that handles sets of key/value maps * |
1163 | 1163 | ************************************************************************/ |
1164 | 1164 | |
1405 | 1405 | */ |
1406 | 1406 | |
1407 | 1407 | /** |
1408 | * @defgroup websocket | |
1408 | * @defgroup websocket Websockets | |
1409 | 1409 | * Websocket management functions |
1410 | 1410 | * @{ |
1411 | 1411 | */ |
1445 | 1445 | #define U_WEBSOCKET_STATUS_CLOSE 1 |
1446 | 1446 | #define U_WEBSOCKET_STATUS_ERROR 2 |
1447 | 1447 | |
1448 | #define U_WEBSOCKET_RSV1 0x40 | |
1449 | #define U_WEBSOCKET_RSV2 0x20 | |
1450 | #define U_WEBSOCKET_RSV3 0x10 | |
1451 | ||
1448 | 1452 | #define WEBSOCKET_RESPONSE_HTTP 0x0001 |
1449 | 1453 | #define WEBSOCKET_RESPONSE_UPGRADE 0x0002 |
1450 | 1454 | #define WEBSOCKET_RESPONSE_CONNECTION 0x0004 |
1452 | 1456 | #define WEBSOCKET_RESPONSE_PROTCOL 0x0010 |
1453 | 1457 | #define WEBSOCKET_RESPONSE_EXTENSION 0x0020 |
1454 | 1458 | |
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 | ||
1455 | 1519 | /** |
1456 | 1520 | * @struct _websocket_manager Websocket manager structure |
1457 | 1521 | * contains among other things the socket |
1462 | 1526 | struct _websocket_message_list * message_list_incoming; /* !< list of incoming messages */ |
1463 | 1527 | struct _websocket_message_list * message_list_outcoming; /* !< list of outcoming messages */ |
1464 | 1528 | 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 */ | |
1465 | 1530 | int close_flag; /* !< flag to set before closing a websocket */ |
1466 | 1531 | MHD_socket mhd_sock; /* !< reference to libmicrohttpd's socket for websocket server */ |
1467 | 1532 | int tcp_sock; /* !< tcp socket for websocket client */ |
1474 | 1539 | pthread_mutex_t write_lock; /* !< mutex to write data in the socket */ |
1475 | 1540 | pthread_mutex_t status_lock; /* !< mutex to broadcast new status */ |
1476 | 1541 | pthread_cond_t status_cond; /* !< condition to broadcast new status */ |
1477 | struct pollfd fds; | |
1542 | struct pollfd fds_in; | |
1543 | struct pollfd fds_out; | |
1478 | 1544 | int type; |
1545 | int rsv_expected; | |
1546 | struct _pointer_list * websocket_extension_list; | |
1479 | 1547 | }; |
1480 | 1548 | |
1481 | 1549 | /** |
1485 | 1553 | */ |
1486 | 1554 | struct _websocket_message { |
1487 | 1555 | time_t datestamp; /* !< date stamp of the message */ |
1556 | uint8_t rsv; /* !< flags RSV1-3 of the message */ | |
1488 | 1557 | uint8_t opcode; /* !< opcode for the message (string or binary) */ |
1489 | 1558 | uint8_t has_mask; /* !< does the message contain a mask? */ |
1490 | 1559 | uint8_t mask[4]; /* !< mask used if any */ |
1491 | 1560 | size_t data_len; /* !< length of the data */ |
1492 | 1561 | 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) */ | |
1493 | 1564 | }; |
1494 | 1565 | |
1495 | 1566 | /** |
1615 | 1686 | */ |
1616 | 1687 | int ulfius_set_websocket_response(struct _u_response * response, |
1617 | 1688 | const char * websocket_protocol, |
1618 | const char * websocket_extensions, | |
1689 | const char * websocket_extensions, | |
1619 | 1690 | void (* websocket_manager_callback) (const struct _u_request * request, |
1620 | 1691 | struct _websocket_manager * websocket_manager, |
1621 | 1692 | void * websocket_manager_user_data), |
1629 | 1700 | struct _websocket_manager * websocket_manager, |
1630 | 1701 | void * websocket_onclose_user_data), |
1631 | 1702 | 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); | |
1632 | 1824 | |
1633 | 1825 | /** |
1634 | 1826 | * Sets the websocket in closing mode |
1693 | 1885 | void * websocket_onclose_user_data, |
1694 | 1886 | struct _websocket_client_handler * websocket_client_handler, |
1695 | 1887 | 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 | ||
1696 | 1951 | /** |
1697 | 1952 | * Send a close signal to the websocket |
1698 | 1953 | * @param websocket_client_handler the handler to the websocket connection |
1767 | 2022 | * @struct _websocket_handle handle for a websocket |
1768 | 2023 | */ |
1769 | 2024 | 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; | |
1785 | 2042 | }; |
1786 | 2043 | |
1787 | 2044 | /** |
1788 | 2045 | * @struct _websocket_handler handler for the websockets list |
1789 | 2046 | */ |
1790 | 2047 | struct _websocket_handler { |
2048 | pthread_mutex_t websocket_active_lock; /* !< mutex to change nb_websocket_active value */ | |
1791 | 2049 | size_t nb_websocket_active; /* !< number of active websocket */ |
1792 | 2050 | struct _websocket ** websocket_active; /* !< array of active websocket */ |
1793 | 2051 | pthread_mutex_t websocket_close_lock; /* !< mutex to broadcast close signal */ |
30 | 30 | DESTDIR=/usr/local |
31 | 31 | CC=gcc |
32 | 32 | 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) | |
34 | 34 | SONAME = -soname |
35 | 35 | ifeq ($(shell uname -s),Darwin) |
36 | 36 | SONAME = -install_name |
38 | 38 | OBJECTS=ulfius.o u_map.o u_request.o u_response.o u_send_request.o u_websocket.o yuarel.o |
39 | 39 | OUTPUT=libulfius.so |
40 | 40 | VERSION_MAJOR=2 |
41 | VERSION_MINOR=6 | |
42 | VERSION_PATCH=9 | |
41 | VERSION_MINOR=7 | |
42 | VERSION_PATCH=0 | |
43 | 43 | |
44 | 44 | ifndef JANSSONFLAG |
45 | 45 | DISABLE_JANSSON=0 |
101 | 101 | $(CONFIG_FILE): |
102 | 102 | @cp $(CONFIG_TEMPLATE) $(CONFIG_FILE) |
103 | 103 | @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) | |
109 | 109 | @if [ "$(DISABLE_JANSSON)" = "1" ]; then \ |
110 | 110 | sed -i -e 's/\#cmakedefine U_DISABLE_JANSSON/\#define U_DISABLE_JANSSON/g' $(CONFIG_FILE); \ |
111 | 111 | echo "JANSSON SUPPORT DISABLED"; \ |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * u_umap.c: Simple map structure functions definitions |
7 | 7 | * not memory friendly, all pointer returned must be freed after use |
8 | * | |
8 | * | |
9 | 9 | * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org> |
10 | 10 | * |
11 | 11 | * This program is free software; you can redistribute it and/or |
20 | 20 | * |
21 | 21 | * You should have received a copy of the GNU General Public |
22 | 22 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
23 | * | |
23 | * | |
24 | 24 | */ |
25 | 25 | |
26 | 26 | #include <stdio.h> |
29 | 29 | #include "u_private.h" |
30 | 30 | #include "ulfius.h" |
31 | 31 | |
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 | */ | |
37 | 32 | int u_map_init(struct _u_map * u_map) { |
38 | 33 | if (u_map != NULL) { |
39 | 34 | u_map->nb_values = 0; |
51 | 46 | return U_ERROR_MEMORY; |
52 | 47 | } |
53 | 48 | u_map->values[0] = NULL; |
54 | ||
49 | ||
55 | 50 | u_map->lengths = o_malloc(sizeof(size_t)); |
56 | 51 | if (u_map->lengths == NULL) { |
57 | 52 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths"); |
67 | 62 | } |
68 | 63 | } |
69 | 64 | |
70 | /** | |
71 | * free the struct _u_map's inner components | |
72 | * return U_OK on success | |
73 | */ | |
74 | 65 | int u_map_clean(struct _u_map * u_map) { |
75 | 66 | int i; |
76 | 67 | if (u_map != NULL) { |
87 | 78 | } |
88 | 79 | } |
89 | 80 | |
90 | /** | |
91 | * free the struct _u_map and its components | |
92 | * return U_OK on success | |
93 | */ | |
94 | 81 | int u_map_clean_full(struct _u_map * u_map) { |
95 | 82 | if (u_map_clean(u_map) == U_OK) { |
96 | 83 | o_free(u_map); |
100 | 87 | } |
101 | 88 | } |
102 | 89 | |
103 | /** | |
104 | * free an enum return by functions u_map_enum_keys or u_map_enum_values | |
105 | * return U_OK on success | |
106 | */ | |
107 | 90 | int u_map_clean_enum(char ** array) { |
108 | 91 | int i; |
109 | 92 | if (array != NULL) { |
118 | 101 | } |
119 | 102 | } |
120 | 103 | |
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 | */ | |
125 | 104 | const char ** u_map_enum_keys(const struct _u_map * u_map) { |
126 | 105 | return (const char **)u_map->keys; |
127 | 106 | } |
128 | 107 | |
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 | */ | |
133 | 108 | const char ** u_map_enum_values(const struct _u_map * u_map) { |
134 | 109 | return (const char **)u_map->values; |
135 | 110 | } |
136 | 111 | |
137 | /** | |
138 | * return true if the sprcified u_map contains the specified key | |
139 | * false otherwise | |
140 | * search is case sensitive | |
141 | */ | |
142 | 112 | int u_map_has_key(const struct _u_map * u_map, const char * key) { |
143 | 113 | int i; |
144 | 114 | if (u_map != NULL && key != NULL) { |
151 | 121 | return 0; |
152 | 122 | } |
153 | 123 | |
154 | /** | |
155 | * return true if the sprcified u_map contains the specified value | |
156 | * false otherwise | |
157 | * search is case sensitive | |
158 | */ | |
159 | 124 | int u_map_has_value(const struct _u_map * u_map, const char * value) { |
160 | 125 | return u_map_has_value_binary(u_map, value, o_strlen(value)); |
161 | 126 | } |
162 | 127 | |
163 | /** | |
164 | * return true if the sprcified u_map contains the specified value | |
165 | * false otherwise | |
166 | * search is case sensitive | |
167 | */ | |
168 | 128 | int u_map_has_value_binary(const struct _u_map * u_map, const char * value, size_t length) { |
169 | 129 | int i; |
170 | 130 | if (u_map != NULL && value != NULL) { |
177 | 137 | return 0; |
178 | 138 | } |
179 | 139 | |
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 | */ | |
185 | 140 | int u_map_put(struct _u_map * u_map, const char * key, const char * value) { |
186 | 141 | if (value != NULL) { |
187 | 142 | return u_map_put_binary(u_map, key, value, 0, o_strlen(value)+1); |
190 | 145 | } |
191 | 146 | } |
192 | 147 | |
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 | */ | |
199 | 148 | int u_map_put_binary(struct _u_map * u_map, const char * key, const char * value, uint64_t offset, size_t length) { |
200 | 149 | int i; |
201 | 150 | char * dup_key, * dup_value; |
241 | 190 | } else { |
242 | 191 | dup_value = o_strdup(""); |
243 | 192 | } |
244 | ||
193 | ||
245 | 194 | // Append key |
246 | 195 | for (i = 0; u_map->keys[i] != NULL; i++); |
247 | 196 | u_map->keys = o_realloc(u_map->keys, (i + 2)*sizeof(char *)); |
253 | 202 | } |
254 | 203 | u_map->keys[i] = (char *)dup_key; |
255 | 204 | u_map->keys[i+1] = NULL; |
256 | ||
205 | ||
257 | 206 | // Append value |
258 | 207 | u_map->values = o_realloc(u_map->values, (i + 2)*sizeof(char *)); |
259 | 208 | if (u_map->values == NULL) { |
264 | 213 | } |
265 | 214 | u_map->values[i] = (char *)dup_value; |
266 | 215 | u_map->values[i+1] = NULL; |
267 | ||
216 | ||
268 | 217 | // Append length |
269 | 218 | u_map->lengths = o_realloc(u_map->lengths, (i + 2)*sizeof(size_t)); |
270 | 219 | if (u_map->lengths == NULL) { |
275 | 224 | } |
276 | 225 | u_map->lengths[i] = (offset + length); |
277 | 226 | u_map->lengths[i+1] = 0; |
278 | ||
227 | ||
279 | 228 | u_map->nb_values++; |
280 | 229 | } |
281 | 230 | return U_OK; |
284 | 233 | } |
285 | 234 | } |
286 | 235 | |
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 | */ | |
291 | 236 | int u_map_remove_from_key(struct _u_map * u_map, const char * key) { |
292 | 237 | int i, res, found = 0; |
293 | ||
238 | ||
294 | 239 | if (u_map == NULL || key == NULL) { |
295 | 240 | return U_ERROR_PARAMS; |
296 | 241 | } else { |
311 | 256 | } |
312 | 257 | } |
313 | 258 | |
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 | */ | |
318 | 259 | int u_map_remove_from_key_case(struct _u_map * u_map, const char * key) { |
319 | 260 | int i, res, found = 0; |
320 | ||
261 | ||
321 | 262 | if (u_map == NULL || key == NULL) { |
322 | 263 | return U_ERROR_PARAMS; |
323 | 264 | } else { |
338 | 279 | } |
339 | 280 | } |
340 | 281 | |
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 | */ | |
345 | 282 | int u_map_remove_from_value(struct _u_map * u_map, const char * value) { |
346 | 283 | return u_map_remove_from_value_binary(u_map, value, o_strlen(value)); |
347 | 284 | } |
348 | 285 | |
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 | */ | |
353 | 286 | int u_map_remove_from_value_binary(struct _u_map * u_map, const char * value, size_t length) { |
354 | 287 | int i, res, found = 0; |
355 | ||
288 | ||
356 | 289 | if (u_map == NULL || value == NULL) { |
357 | 290 | return U_ERROR_PARAMS; |
358 | 291 | } else { |
373 | 306 | } |
374 | 307 | } |
375 | 308 | |
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 | */ | |
380 | 309 | int u_map_remove_from_value_case(struct _u_map * u_map, const char * value) { |
381 | 310 | int i, res, found = 0; |
382 | ||
311 | ||
383 | 312 | if (u_map == NULL || value == NULL) { |
384 | 313 | return U_ERROR_PARAMS; |
385 | 314 | } else { |
400 | 329 | } |
401 | 330 | } |
402 | 331 | |
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 | */ | |
407 | 332 | int u_map_remove_at(struct _u_map * u_map, const int index) { |
408 | 333 | int i; |
409 | 334 | if (u_map == NULL || index < 0) { |
433 | 358 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths"); |
434 | 359 | return U_ERROR_MEMORY; |
435 | 360 | } |
436 | ||
361 | ||
437 | 362 | u_map->nb_values--; |
438 | 363 | return U_OK; |
439 | 364 | } |
440 | 365 | } |
441 | 366 | |
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 | */ | |
447 | 367 | const char * u_map_get(const struct _u_map * u_map, const char * key) { |
448 | 368 | int i; |
449 | 369 | if (u_map != NULL && key != NULL) { |
462 | 382 | } |
463 | 383 | } |
464 | 384 | |
465 | /** | |
466 | * return true if the sprcified u_map contains the specified key | |
467 | * false otherwise | |
468 | * search is case insensitive | |
469 | */ | |
470 | 385 | int u_map_has_key_case(const struct _u_map * u_map, const char * key) { |
471 | 386 | int i; |
472 | 387 | if (u_map != NULL && key != NULL) { |
479 | 394 | return 0; |
480 | 395 | } |
481 | 396 | |
482 | /** | |
483 | * return true if the sprcified u_map contains the specified value | |
484 | * false otherwise | |
485 | * search is case insensitive | |
486 | */ | |
487 | 397 | int u_map_has_value_case(const struct _u_map * u_map, const char * value) { |
488 | 398 | int i; |
489 | 399 | if (u_map != NULL && value != NULL) { |
496 | 406 | return 0; |
497 | 407 | } |
498 | 408 | |
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 | */ | |
504 | 409 | const char * u_map_get_case(const struct _u_map * u_map, const char * key) { |
505 | 410 | int i; |
506 | 411 | if (u_map != NULL && key != NULL) { |
515 | 420 | } |
516 | 421 | } |
517 | 422 | |
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 | */ | |
523 | 423 | ssize_t u_map_get_length(const struct _u_map * u_map, const char * key) { |
524 | 424 | int i; |
525 | 425 | if (u_map != NULL && key != NULL) { |
534 | 434 | } |
535 | 435 | } |
536 | 436 | |
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 | */ | |
542 | 437 | ssize_t u_map_get_case_length(const struct _u_map * u_map, const char * key) { |
543 | 438 | int i; |
544 | 439 | if (u_map != NULL && key != NULL) { |
553 | 448 | } |
554 | 449 | } |
555 | 450 | |
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 | */ | |
561 | 451 | struct _u_map * u_map_copy(const struct _u_map * source) { |
562 | 452 | struct _u_map * copy = NULL; |
563 | 453 | const char ** keys, * value; |
583 | 473 | return copy; |
584 | 474 | } |
585 | 475 | |
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 | */ | |
591 | 476 | int u_map_copy_into(struct _u_map * dest, const struct _u_map * source) { |
592 | 477 | const char ** keys; |
593 | 478 | int i, res; |
594 | ||
479 | ||
595 | 480 | if (source != NULL && dest != NULL) { |
596 | 481 | keys = u_map_enum_keys(source); |
597 | 482 | for (i=0; keys != NULL && keys[i] != NULL; i++) { |
606 | 491 | } |
607 | 492 | } |
608 | 493 | |
609 | /** | |
610 | * Return the number of key/values pair in the specified struct _u_map | |
611 | * Return -1 on error | |
612 | */ | |
613 | 494 | int u_map_count(const struct _u_map * source) { |
614 | 495 | if (source != NULL) { |
615 | 496 | if (source->nb_values >= 0) { |
619 | 500 | return -1; |
620 | 501 | } |
621 | 502 | |
622 | /** | |
623 | * Empty a struct u_map of all its elements | |
624 | * return U_OK on success, error otherwise | |
625 | */ | |
626 | 503 | int u_map_empty(struct _u_map * u_map) { |
627 | 504 | int ret = u_map_clean(u_map); |
628 | 505 | if (ret == U_OK) { |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * u_request.c: request related functions defintions |
7 | * | |
7 | * | |
8 | 8 | * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org> |
9 | 9 | * |
10 | 10 | * This program is free software; you can redistribute it and/or |
19 | 19 | * |
20 | 20 | * You should have received a copy of the GNU General Public |
21 | 21 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
22 | * | |
22 | * | |
23 | 23 | */ |
24 | 24 | #include <stdlib.h> |
25 | 25 | #include <stdarg.h> |
39 | 39 | static char ** ulfius_split_url(const char * prefix, const char * url) { |
40 | 40 | char * saveptr = NULL, * cur_word = NULL, ** to_return = o_malloc(sizeof(char*)), * url_cpy = NULL, * url_cpy_addr = NULL; |
41 | 41 | int counter = 1; |
42 | ||
42 | ||
43 | 43 | if (to_return != NULL) { |
44 | 44 | to_return[0] = NULL; |
45 | 45 | if (prefix != NULL) { |
98 | 98 | */ |
99 | 99 | static int compare_endpoint_priorities(const void * a, const void * b) { |
100 | 100 | struct _u_endpoint * e1 = *(struct _u_endpoint **)a, * e2 = *(struct _u_endpoint **)b; |
101 | ||
101 | ||
102 | 102 | if (e1->priority < e2->priority) { |
103 | 103 | return -1; |
104 | 104 | } else if (e1->priority > e2->priority) { |
115 | 115 | */ |
116 | 116 | static int ulfius_url_format_match(const char ** splitted_url, const char ** splitted_url_format) { |
117 | 117 | int i; |
118 | ||
118 | ||
119 | 119 | for (i=0; splitted_url_format[i] != NULL; i++) { |
120 | 120 | if (splitted_url_format[i][0] == '*' && splitted_url_format[i+1] == NULL) { |
121 | 121 | return 1; |
149 | 149 | * pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); |
150 | 150 | pstr += 2; |
151 | 151 | } |
152 | } else if (* pstr == '+') { | |
152 | } else if (* pstr == '+') { | |
153 | 153 | * pbuf++ = ' '; |
154 | 154 | } else { |
155 | 155 | * pbuf++ = * pstr; |
175 | 175 | struct _u_endpoint ** endpoint_returned = o_malloc(sizeof(struct _u_endpoint *)); |
176 | 176 | int i; |
177 | 177 | size_t count = 0; |
178 | ||
178 | ||
179 | 179 | if (endpoint_returned == NULL) { |
180 | 180 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for endpoint_returned"); |
181 | 181 | } else { |
212 | 212 | splitted_url = NULL; |
213 | 213 | } |
214 | 214 | } |
215 | /* | |
215 | /* | |
216 | 216 | * only sort if endpoint_returned is != NULL |
217 | 217 | * can be NULL either after initial o_malloc() or after o_realloc() |
218 | 218 | */ |
247 | 247 | cur_word_format = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr_prefix ); |
248 | 248 | } |
249 | 249 | o_free(url_format_cpy_addr); |
250 | ||
250 | ||
251 | 251 | url_format_cpy = url_format_cpy_addr = o_strdup(endpoint->url_format); |
252 | 252 | if (endpoint->url_format != NULL && url_format_cpy == NULL) { |
253 | 253 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for url_format_cpy"); |
256 | 256 | cur_word_format = strtok_r( url_format_cpy, ULFIUS_URL_SEPARATOR, &saveptr_format ); |
257 | 257 | } |
258 | 258 | 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)) { | |
260 | 260 | if (u_map_has_key(map, cur_word_format+1)) { |
261 | 261 | concat_url_param = msprintf("%s,%s", u_map_get(map, cur_word_format+1), cur_word); |
262 | 262 | if (concat_url_param == NULL) { |
306 | 306 | request->map_header = o_malloc(sizeof(struct _u_map)); |
307 | 307 | request->map_cookie = o_malloc(sizeof(struct _u_map)); |
308 | 308 | 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 || | |
310 | 310 | request->map_url == NULL || request->map_header == NULL) { |
311 | 311 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for request->map*"); |
312 | 312 | ulfius_clean_request(request); |
432 | 432 | dest->auth_basic_user = o_strdup(source->auth_basic_user); |
433 | 433 | dest->auth_basic_password = o_strdup(source->auth_basic_password); |
434 | 434 | dest->callback_position = source->callback_position; |
435 | ||
435 | ||
436 | 436 | if (source->client_address != NULL) { |
437 | 437 | dest->client_address = o_malloc(sizeof(struct sockaddr)); |
438 | 438 | if (dest->client_address != NULL) { |
442 | 442 | ret = U_ERROR_MEMORY; |
443 | 443 | } |
444 | 444 | } |
445 | ||
445 | ||
446 | 446 | if (ret == U_OK && u_map_clean(dest->map_url) == U_OK && u_map_init(dest->map_url) == U_OK) { |
447 | 447 | if (u_map_copy_into(dest->map_url, source->map_url) != U_OK) { |
448 | 448 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_url"); |
452 | 452 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_url"); |
453 | 453 | ret = U_ERROR_MEMORY; |
454 | 454 | } |
455 | ||
455 | ||
456 | 456 | if (ret == U_OK && u_map_clean(dest->map_header) == U_OK && u_map_init(dest->map_header) == U_OK) { |
457 | 457 | if (u_map_copy_into(dest->map_header, source->map_header) != U_OK) { |
458 | 458 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_header"); |
462 | 462 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_header"); |
463 | 463 | ret = U_ERROR_MEMORY; |
464 | 464 | } |
465 | ||
465 | ||
466 | 466 | if (ret == U_OK && u_map_clean(dest->map_cookie) == U_OK && u_map_init(dest->map_cookie) == U_OK) { |
467 | 467 | if (u_map_copy_into(dest->map_cookie, source->map_cookie) != U_OK) { |
468 | 468 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_cookie"); |
472 | 472 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_cookie"); |
473 | 473 | ret = U_ERROR_MEMORY; |
474 | 474 | } |
475 | ||
475 | ||
476 | 476 | if (ret == U_OK && u_map_clean(dest->map_post_body) == U_OK && u_map_init(dest->map_post_body) == U_OK) { |
477 | 477 | if (u_map_copy_into(dest->map_post_body, source->map_post_body) != U_OK) { |
478 | 478 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error u_map_copy_into dest->map_post_body"); |
482 | 482 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reinit dest->map_post_body"); |
483 | 483 | ret = U_ERROR_MEMORY; |
484 | 484 | } |
485 | ||
485 | ||
486 | 486 | if (ret == U_OK) { |
487 | 487 | if (source->binary_body_length) { |
488 | 488 | dest->binary_body_length = source->binary_body_length; |
834 | 834 | * Get JSON structure from the request body if the request is valid |
835 | 835 | * request: struct _u_request used |
836 | 836 | * json_error: structure to store json_error_t if specified |
837 | */ | |
837 | */ | |
838 | 838 | json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error) { |
839 | 839 | 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)) { |
840 | 840 | return json_loadb(request->binary_body, request->binary_body_length, JSON_DECODE_ANY, json_error); |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * u_response.c: response related functions defintions |
7 | * | |
7 | * | |
8 | 8 | * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org> |
9 | 9 | * |
10 | 10 | * This program is free software; you can redistribute it and/or |
19 | 19 | * |
20 | 20 | * You should have received a copy of the GNU General Public |
21 | 21 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
22 | * | |
22 | * | |
23 | 23 | */ |
24 | 24 | #include <string.h> |
25 | 25 | |
26 | 26 | #include "u_private.h" |
27 | 27 | #include "ulfius.h" |
28 | 28 | |
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 | */ | |
33 | 29 | static char * ulfius_generate_cookie_header(const struct _u_cookie * cookie) { |
34 | 30 | char * attr_expires = NULL, * attr_max_age = NULL, * attr_domain = NULL, * attr_path = NULL; |
35 | 31 | char * attr_secure = NULL, * attr_http_only = NULL, * cookie_header_value = NULL, * same_site = NULL; |
127 | 123 | } else { |
128 | 124 | same_site = o_strdup(""); |
129 | 125 | } |
130 | ||
126 | ||
131 | 127 | if (attr_expires == NULL || attr_max_age == NULL || attr_domain == NULL || attr_path == NULL || attr_secure == NULL || attr_http_only == NULL) { |
132 | 128 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_generate_cookie_header"); |
133 | 129 | } else { |
156 | 152 | } |
157 | 153 | } |
158 | 154 | |
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 | */ | |
164 | 155 | int ulfius_set_response_header(struct MHD_Response * response, const struct _u_map * response_map_header) { |
165 | 156 | const char ** header_keys = u_map_enum_keys(response_map_header); |
166 | 157 | const char * header_value; |
180 | 171 | return i; |
181 | 172 | } |
182 | 173 | |
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 | */ | |
188 | 174 | int ulfius_set_response_cookie(struct MHD_Response * mhd_response, const struct _u_response * response) { |
189 | 175 | int ret; |
190 | 176 | unsigned int i; |
210 | 196 | } |
211 | 197 | } |
212 | 198 | |
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, | |
219 | 200 | const char * domain, const char * path, const int secure, const int http_only) { |
220 | 201 | return ulfius_add_same_site_cookie_to_response(response, key, value, expires, max_age, domain, path, secure, http_only, U_COOKIE_SAME_SITE_NONE); |
221 | 202 | } |
222 | 203 | |
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, | |
233 | 205 | const char * domain, const char * path, const int secure, const int http_only, const int same_site) { |
234 | 206 | unsigned int i; |
235 | 207 | 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)) { |
264 | 236 | } |
265 | 237 | } |
266 | 238 | } |
267 | ||
239 | ||
268 | 240 | // Key not found, inserting a new cookie |
269 | 241 | if (response->nb_cookies == 0) { |
270 | 242 | response->map_cookie = o_malloc(sizeof(struct _u_cookie)); |
288 | 260 | response->map_cookie[response->nb_cookies].secure = secure; |
289 | 261 | response->map_cookie[response->nb_cookies].http_only = http_only; |
290 | 262 | 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) || | |
292 | 264 | (expires != NULL && response->map_cookie[response->nb_cookies].expires == NULL) || (domain != NULL && response->map_cookie[response->nb_cookies].domain == NULL) || |
293 | 265 | (path != NULL && response->map_cookie[response->nb_cookies].path == NULL)) { |
294 | 266 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_add_cookie_to_response"); |
307 | 279 | } |
308 | 280 | } |
309 | 281 | |
310 | /** | |
311 | * ulfius_clean_cookie | |
312 | * clean the cookie's elements | |
313 | * return U_OK on success | |
314 | */ | |
315 | 282 | int ulfius_clean_cookie(struct _u_cookie * cookie) { |
316 | 283 | if (cookie != NULL) { |
317 | 284 | o_free(cookie->key); |
330 | 297 | } |
331 | 298 | } |
332 | 299 | |
333 | /** | |
334 | * Copy the cookie source elements into dest elements | |
335 | * return U_OK on success | |
336 | */ | |
337 | 300 | int ulfius_copy_cookie(struct _u_cookie * dest, const struct _u_cookie * source) { |
338 | 301 | if (source != NULL && dest != NULL) { |
339 | 302 | dest->key = o_strdup(source->key); |
359 | 322 | return U_ERROR_PARAMS; |
360 | 323 | } |
361 | 324 | |
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 | */ | |
369 | 325 | int ulfius_clean_response(struct _u_response * response) { |
370 | 326 | unsigned int i; |
371 | 327 | if (response != NULL) { |
387 | 343 | if ((struct _websocket_handle *)response->websocket_handle) { |
388 | 344 | o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); |
389 | 345 | 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); | |
390 | 348 | o_free(response->websocket_handle); |
391 | 349 | } |
392 | 350 | #endif |
396 | 354 | } |
397 | 355 | } |
398 | 356 | |
399 | /** | |
400 | * ulfius_clean_response_full | |
401 | * clean the specified response and all its elements | |
402 | * return U_OK on success | |
403 | */ | |
404 | 357 | int ulfius_clean_response_full(struct _u_response * response) { |
405 | 358 | if (ulfius_clean_response(response) == U_OK) { |
406 | 359 | o_free(response); |
410 | 363 | } |
411 | 364 | } |
412 | 365 | |
413 | /** | |
414 | * ulfius_init_response | |
415 | * Initialize a response structure by allocating inner elements | |
416 | * return U_OK on success | |
417 | */ | |
418 | 366 | int ulfius_init_response(struct _u_response * response) { |
419 | 367 | if (response != NULL) { |
420 | 368 | response->status = 200; |
453 | 401 | ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = NULL; |
454 | 402 | ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = NULL; |
455 | 403 | ((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); | |
456 | 411 | #endif |
457 | 412 | return U_OK; |
458 | 413 | } else { |
460 | 415 | } |
461 | 416 | } |
462 | 417 | |
463 | /** | |
464 | * ulfius_copy_response | |
465 | * Copy the source response elements into the des response | |
466 | * return U_OK on success | |
467 | */ | |
468 | 418 | int ulfius_copy_response(struct _u_response * dest, const struct _u_response * source) { |
469 | 419 | unsigned int i; |
470 | 420 | if (dest != NULL && source != NULL) { |
493 | 443 | } else { |
494 | 444 | dest->map_cookie = NULL; |
495 | 445 | } |
496 | ||
446 | ||
497 | 447 | if (source->binary_body != NULL && source->binary_body_length > 0) { |
498 | 448 | dest->binary_body = o_malloc(source->binary_body_length); |
499 | 449 | if (dest->binary_body == NULL) { |
503 | 453 | dest->binary_body_length = source->binary_body_length; |
504 | 454 | memcpy(dest->binary_body, source->binary_body, source->binary_body_length); |
505 | 455 | } |
506 | ||
456 | ||
507 | 457 | if (source->stream_callback != NULL) { |
508 | 458 | dest->stream_callback = source->stream_callback; |
509 | 459 | dest->stream_callback_free = source->stream_callback_free; |
511 | 461 | dest->stream_block_size = source->stream_block_size; |
512 | 462 | dest->stream_user_data = source->stream_user_data; |
513 | 463 | } |
514 | ||
464 | ||
515 | 465 | dest->shared_data = source->shared_data; |
516 | 466 | dest->timeout = source->timeout; |
517 | 467 | #ifndef U_DISABLE_WEBSOCKET |
532 | 482 | } |
533 | 483 | } |
534 | 484 | |
535 | /** | |
536 | * create a new response based on the source elements | |
537 | * return value must be free'd after use | |
538 | */ | |
539 | 485 | struct _u_response * ulfius_duplicate_response(const struct _u_response * response) { |
540 | 486 | struct _u_response * new_response = NULL; |
541 | 487 | if (response != NULL) { |
559 | 505 | return new_response; |
560 | 506 | } |
561 | 507 | |
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 | */ | |
568 | 508 | int ulfius_set_string_body_response(struct _u_response * response, const unsigned int status, const char * string_body) { |
569 | 509 | if (response != NULL && string_body != NULL) { |
570 | 510 | // Free all the bodies available |
583 | 523 | } |
584 | 524 | } |
585 | 525 | |
586 | /** | |
587 | * ulfius_set_binary_body_response | |
588 | * Add a binary binary_body to a response | |
589 | * return U_OK on success | |
590 | */ | |
591 | 526 | int ulfius_set_binary_body_response(struct _u_response * response, const unsigned int status, const char * binary_body, const size_t length) { |
592 | 527 | if (response != NULL && binary_body != NULL && length > 0) { |
593 | 528 | // Free all the bodies available |
609 | 544 | } |
610 | 545 | } |
611 | 546 | |
612 | /** | |
613 | * ulfius_set_empty_body_response | |
614 | * Set an empty response with only a status | |
615 | * return U_OK on success | |
616 | */ | |
617 | 547 | int ulfius_set_empty_body_response(struct _u_response * response, const unsigned int status) { |
618 | 548 | if (response != NULL) { |
619 | 549 | // Free all the bodies available |
620 | 550 | o_free(response->binary_body); |
621 | 551 | response->binary_body = NULL; |
622 | 552 | response->binary_body_length = 0; |
623 | ||
553 | ||
624 | 554 | response->status = status; |
625 | 555 | return U_OK; |
626 | 556 | } else { |
628 | 558 | } |
629 | 559 | } |
630 | 560 | |
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, | |
639 | 562 | const unsigned int status, |
640 | 563 | ssize_t (* stream_callback) (void * stream_user_data, uint64_t offset, char * out_buf, size_t max), |
641 | 564 | void (* stream_callback_free) (void * stream_user_data), |
647 | 570 | o_free(response->binary_body); |
648 | 571 | response->binary_body = NULL; |
649 | 572 | response->binary_body_length = 0; |
650 | ||
573 | ||
651 | 574 | response->status = status; |
652 | 575 | response->stream_callback = stream_callback; |
653 | 576 | response->stream_callback_free = stream_callback_free; |
660 | 583 | } |
661 | 584 | } |
662 | 585 | |
663 | /** | |
664 | * ulfius_set_response_properties | |
665 | * Set a list of properties to a response | |
666 | * return U_OK on success | |
667 | */ | |
668 | 586 | int ulfius_set_response_properties(struct _u_response * response, ...) { |
669 | 587 | u_option option; |
670 | 588 | int ret = U_OK; |
775 | 693 | } |
776 | 694 | #endif |
777 | 695 | |
778 | /** | |
779 | * ulfius_add_header_to_response | |
780 | * add a header to the response | |
781 | * return U_OK on success | |
782 | */ | |
783 | 696 | int ulfius_add_header_to_response(struct _u_response * response, const char * key, const char * value) { |
784 | 697 | if (response != NULL && key != NULL && value != NULL) { |
785 | 698 | return u_map_put(response->map_header, key, value); |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * u_send_request.c: send request related functions defintions |
7 | * | |
7 | * | |
8 | 8 | * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org> |
9 | 9 | * |
10 | 10 | * This program is free software; you can redistribute it and/or |
40 | 40 | #include <curl/curl.h> |
41 | 41 | #include <string.h> |
42 | 42 | |
43 | #define U_ACCEPT_HEADER "Accept-Encoding" | |
44 | ||
43 | 45 | #ifdef _MSC_VER |
44 | 46 | #define strtok_r strtok_s |
45 | 47 | |
79 | 81 | static size_t ulfius_write_body(void * contents, size_t size, size_t nmemb, void * user_data) { |
80 | 82 | size_t realsize = size * nmemb; |
81 | 83 | struct _u_body * body_data = (struct _u_body *) user_data; |
82 | ||
84 | ||
83 | 85 | body_data->data = o_realloc(body_data->data, body_data->size + realsize + 1); |
84 | 86 | if(body_data->data == NULL) { |
85 | 87 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for body_data->data"); |
86 | 88 | return 0; |
87 | 89 | } |
88 | ||
90 | ||
89 | 91 | memcpy(&(body_data->data[body_data->size]), contents, realsize); |
90 | 92 | body_data->size += realsize; |
91 | 93 | body_data->data[body_data->size] = 0; |
92 | ||
94 | ||
93 | 95 | return realsize; |
94 | 96 | } |
95 | 97 | |
99 | 101 | * return the size_t of the header written |
100 | 102 | */ |
101 | 103 | static size_t write_header(void * buffer, size_t size, size_t nitems, void * user_data) { |
102 | ||
104 | ||
103 | 105 | struct _u_response * response = (struct _u_response *) user_data; |
104 | 106 | char * header = (char *)buffer, * key, * value, * saveptr, * tmp; |
105 | ||
107 | ||
106 | 108 | if (o_strchr(header, ':') != NULL) { |
107 | 109 | if (response->map_header != NULL) { |
108 | 110 | // Expecting a header (key: value) |
109 | 111 | key = trimwhitespace(strtok_r(header, ":", &saveptr)); |
110 | 112 | value = trimwhitespace(strtok_r(NULL, "", &saveptr)); |
111 | ||
113 | ||
112 | 114 | if (!u_map_has_key_case(response->map_header, key)) { |
113 | 115 | u_map_put(response->map_header, key, value); |
114 | 116 | } else { |
130 | 132 | return 0; |
131 | 133 | } |
132 | 134 | } |
133 | ||
135 | ||
134 | 136 | return nitems * size; |
135 | 137 | } |
136 | 138 | |
137 | 139 | static size_t smtp_payload_source(void * ptr, size_t size, size_t nmemb, void * userp) { |
138 | 140 | struct _u_smtp_payload *upload_ctx = (struct _u_smtp_payload *)userp; |
139 | 141 | size_t len; |
140 | ||
142 | ||
141 | 143 | if ((size * nmemb) < (upload_ctx->len - upload_ctx->offset)) { |
142 | 144 | len = size*nmemb; |
143 | 145 | } else { |
158 | 160 | body_data.size = 0; |
159 | 161 | body_data.data = NULL; |
160 | 162 | int res; |
161 | ||
163 | ||
162 | 164 | res = ulfius_send_http_streaming_request(request, response, ulfius_write_body, (void *)&body_data); |
163 | 165 | if (res == U_OK && response != NULL) { |
164 | 166 | if (body_data.data != NULL && body_data.size > 0) { |
186 | 188 | * return U_OK on success |
187 | 189 | */ |
188 | 190 | 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), | |
191 | 193 | void * write_body_data) { |
192 | 194 | CURLcode res; |
193 | 195 | CURL * curl_handle = NULL; |
200 | 202 | if (request != NULL) { |
201 | 203 | // Duplicate the request and work on it |
202 | 204 | if ((copy_request = ulfius_duplicate_request(request)) != NULL) { |
203 | ||
205 | ||
204 | 206 | if ((curl_handle = curl_easy_init()) != NULL) { |
205 | 207 | ret = U_OK; |
206 | 208 | |
207 | 209 | // Here comes the fake loop with breaks to exit smoothly |
208 | 210 | 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 | ||
209 | 220 | // Set basic auth if defined |
210 | 221 | if (copy_request->auth_basic_user != NULL && copy_request->auth_basic_password != NULL) { |
211 | 222 | if (curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC) == CURLE_OK) { |
246 | 257 | ret = U_ERROR_LIBCURL; |
247 | 258 | break; |
248 | 259 | } |
249 | ||
260 | ||
250 | 261 | // Set proxy if defined |
251 | 262 | if (copy_request->proxy != NULL) { |
252 | 263 | if (curl_easy_setopt(curl_handle, CURLOPT_PROXY, copy_request->proxy) != CURLE_OK) { |
688 | 699 | * mail_body: email body (mandatory) |
689 | 700 | * return U_OK on success |
690 | 701 | */ |
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, | |
701 | 712 | const char * content_type, |
702 | const char * subject, | |
713 | const char * subject, | |
703 | 714 | const char * mail_body) { |
704 | 715 | CURL * curl_handle; |
705 | 716 | CURLcode res = CURLE_OK; |
891 | 902 | * mail_body: email body (mandatory) |
892 | 903 | * return U_OK on success |
893 | 904 | */ |
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, | |
905 | 916 | const char * mail_body) { |
906 | 917 | 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); |
907 | 918 | } |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * u_websocket.c: websocket implementation |
7 | * | |
7 | * | |
8 | 8 | * Copyright 2017-2020 Nicolas Mora <mail@babelouest.org> |
9 | 9 | * |
10 | 10 | * This program is free software; you can redistribute it and/or |
19 | 19 | * |
20 | 20 | * You should have received a copy of the GNU General Public |
21 | 21 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
22 | * | |
22 | * | |
23 | 23 | */ |
24 | 24 | |
25 | 25 | #include "u_private.h" |
43 | 43 | #include <netdb.h> |
44 | 44 | #include <stdlib.h> |
45 | 45 | #include <gnutls/crypto.h> |
46 | #include <zlib.h> | |
47 | #include <errno.h> | |
46 | 48 | |
47 | 49 | #define STR_HELPER(x) #x |
48 | 50 | #define STR(x) STR_HELPER(x) |
51 | ||
52 | #define U_WEBSOCKET_DEFAULT_MEMORY_LEVEL 4 | |
53 | #define U_WEBSOCKET_DEFAULT_WINDOWS_BITS 15 | |
49 | 54 | |
50 | 55 | /**********************************/ |
51 | 56 | /** Internal websocket functions **/ |
52 | 57 | /**********************************/ |
53 | 58 | |
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 | ||
54 | 90 | static int is_websocket_data_available(struct _websocket_manager * websocket_manager) { |
55 | 91 | int ret = 0, poll_ret = 0; |
56 | ||
92 | ||
57 | 93 | if (websocket_manager->tls) { |
58 | 94 | ret = gnutls_record_check_pending(websocket_manager->gnutls_session); |
59 | 95 | if (ret) |
60 | 96 | return ret; |
61 | 97 | } |
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); | |
63 | 99 | if (poll_ret == -1) { |
64 | 100 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error poll websocket read"); |
65 | 101 | 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)) { | |
67 | 103 | websocket_manager->connected = 0; |
68 | 104 | } else if (poll_ret > 0) { |
69 | 105 | ret = 1; |
71 | 107 | return ret; |
72 | 108 | } |
73 | 109 | |
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 | ||
74 | 130 | static ssize_t read_data_from_socket(struct _websocket_manager * websocket_manager, uint8_t * data, size_t len) { |
75 | 131 | ssize_t ret = 0, data_len; |
76 | ||
132 | ||
77 | 133 | if (len > 0) { |
78 | 134 | do { |
79 | 135 | if (is_websocket_data_available(websocket_manager)) { |
91 | 147 | break; |
92 | 148 | } |
93 | 149 | } |
94 | } while (ret < (ssize_t)len); | |
150 | } while (websocket_manager->connected && ret < (ssize_t)len); | |
95 | 151 | } |
96 | 152 | return ret; |
97 | 153 | } |
104 | 160 | if (data != NULL && len > 0) { |
105 | 161 | for (off = 0; (size_t)off < len; off += ret) { |
106 | 162 | 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 { | |
109 | 169 | break; |
110 | 170 | } |
111 | 171 | } else { |
155 | 215 | *frame = o_malloc(*frame_len); |
156 | 216 | if (*frame != NULL) { |
157 | 217 | if (has_fin) { |
158 | (*frame)[0] = (message->opcode | U_WEBSOCKET_BIT_FIN); | |
218 | (*frame)[0] = (message->opcode | message->rsv | U_WEBSOCKET_BIT_FIN); | |
159 | 219 | } else { |
160 | (*frame)[0] = 0; | |
220 | (*frame)[0] = message->rsv; | |
161 | 221 | } |
162 | 222 | if (message->data_len >= 65536) { |
163 | 223 | (*frame)[1] = 127; |
207 | 267 | * returned value must be free'd after use |
208 | 268 | */ |
209 | 269 | static struct _websocket_message * ulfius_build_message (const uint8_t opcode, |
270 | const uint8_t rsv, | |
210 | 271 | const short int has_mask, |
211 | 272 | const char * data, |
212 | 273 | const uint64_t data_len) { |
213 | 274 | struct _websocket_message * new_message = NULL; |
214 | if (( | |
275 | if ( | |
215 | 276 | opcode == U_WEBSOCKET_OPCODE_TEXT || |
216 | 277 | opcode == U_WEBSOCKET_OPCODE_BINARY || |
217 | 278 | opcode == U_WEBSOCKET_OPCODE_CLOSE || |
218 | 279 | opcode == U_WEBSOCKET_OPCODE_PING || |
219 | 280 | opcode == U_WEBSOCKET_OPCODE_PONG |
220 | ) && | |
221 | (data_len == 0 || data != NULL)) { | |
281 | ) { | |
222 | 282 | new_message = o_malloc(sizeof(struct _websocket_message)); |
223 | 283 | if (new_message != NULL) { |
224 | 284 | if (data_len) { |
228 | 288 | } |
229 | 289 | if (!data_len || new_message->data != NULL) { |
230 | 290 | new_message->opcode = opcode; |
291 | new_message->rsv = rsv; | |
231 | 292 | new_message->data_len = data_len; |
232 | 293 | if (!has_mask) { |
233 | 294 | new_message->has_mask = 0; |
248 | 309 | } else { |
249 | 310 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for new_message"); |
250 | 311 | } |
312 | } else { | |
313 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Send invalid opcode error"); | |
251 | 314 | } |
252 | 315 | return new_message; |
253 | 316 | } |
260 | 323 | */ |
261 | 324 | static int ulfius_send_websocket_message_managed(struct _websocket_manager * websocket_manager, |
262 | 325 | const uint8_t opcode, |
326 | const uint8_t rsv, | |
263 | 327 | const uint64_t data_len, |
264 | 328 | const char * data, |
265 | 329 | const uint64_t fragment_len) { |
268 | 332 | uint8_t * frame = NULL; |
269 | 333 | size_t frame_len = 0; |
270 | 334 | 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) { | |
281 | 345 | 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 | } | |
283 | 351 | if ((ret = ulfius_build_frame(message, offset, cur_len, &frame, &frame_len)) != U_OK) { |
284 | 352 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_build_frame"); |
285 | 353 | break; |
290 | 358 | frame = NULL; |
291 | 359 | frame_len = 0; |
292 | 360 | } |
361 | message->opcode = U_WEBSOCKET_OPCODE_CONTINUE; | |
293 | 362 | } |
294 | 363 | } 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 { | |
302 | 404 | ret = U_ERROR_PARAMS; |
303 | 405 | } |
304 | 406 | return ret; |
310 | 412 | * Sets the new message in the message variable |
311 | 413 | */ |
312 | 414 | 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; | |
314 | 416 | uint8_t header[2] = {0}, payload_len[8] = {0}, masking_key[4] = {0}; |
315 | 417 | uint8_t * payload_data = NULL; |
316 | 418 | size_t msg_len = 0; |
317 | 419 | 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 | ||
327 | 483 | 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"); | |
339 | 491 | ret = U_ERROR; |
340 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reading websocket message length"); | |
341 | } else { | |
492 | } else if (len < 0) { | |
342 | 493 | ret = U_ERROR_DISCONNECTED; |
343 | 494 | } |
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 | } | |
365 | 499 | } 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) { | |
394 | 509 | len = read_data_from_socket(websocket_manager, payload_data, msg_len); |
395 | 510 | if (len < 0) { |
396 | 511 | ret = U_ERROR_DISCONNECTED; |
399 | 514 | ret = U_ERROR; |
400 | 515 | } else { |
401 | 516 | // 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)); | |
403 | 518 | if ((*message)->has_mask) { |
404 | 519 | for (i = (*message)->data_len; (unsigned int)i < (*message)->data_len + msg_len; i++) { |
405 | 520 | (*message)->data[i] = payload_data[i-(*message)->data_len] ^ masking_key[(i-(*message)->data_len)%4]; |
410 | 525 | (*message)->data_len += msg_len; |
411 | 526 | } |
412 | 527 | 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); | |
421 | 537 | } |
422 | 538 | if (ret != U_OK) { |
423 | 539 | ulfius_clear_websocket_message(*message); |
428 | 544 | /** |
429 | 545 | * Run the websocket manager in a separated detached thread |
430 | 546 | */ |
431 | void * ulfius_thread_websocket_manager_run(void * args) { | |
547 | static void * ulfius_thread_websocket_manager_run(void * args) { | |
432 | 548 | struct _websocket * websocket = (struct _websocket *)args; |
433 | 549 | if (websocket != NULL) { |
434 | 550 | websocket->websocket_manager_callback(websocket->request, websocket->websocket_manager, websocket->websocket_manager_user_data); |
435 | ||
551 | ||
436 | 552 | // Send close message if the websocket is still open |
437 | 553 | 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 | } | |
439 | 560 | } |
440 | 561 | } |
441 | 562 | 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; | |
442 | 623 | } |
443 | 624 | |
444 | 625 | /** |
447 | 628 | * Complete the callback when the websocket is closed |
448 | 629 | * The websocket can be closed by the client, the manager, the program, or on network disconnect |
449 | 630 | */ |
450 | void * ulfius_thread_websocket(void * data) { | |
631 | static void * ulfius_thread_websocket(void * data) { | |
451 | 632 | struct _websocket * websocket = (struct _websocket*)data; |
452 | struct _websocket_message * message = NULL; | |
633 | struct _websocket_message * message = NULL, * message_previous = NULL; | |
453 | 634 | pthread_t thread_websocket_manager; |
454 | int thread_ret_websocket_manager = 1; | |
455 | ||
635 | int thread_ret_websocket_manager = 1, ret = U_OK; | |
636 | ||
456 | 637 | if (websocket != NULL && websocket->websocket_manager != NULL) { |
457 | 638 | if (websocket->websocket_manager_callback != NULL) { |
458 | 639 | thread_ret_websocket_manager = pthread_create(&thread_websocket_manager, NULL, ulfius_thread_websocket_manager_run, (void *)websocket); |
461 | 642 | websocket->websocket_manager->connected = 0; |
462 | 643 | } |
463 | 644 | } |
464 | while (websocket->websocket_manager->connected) { | |
465 | message = NULL; | |
645 | while (websocket->websocket_manager->connected && ret == U_OK) { | |
466 | 646 | if (websocket->websocket_manager->close_flag) { |
467 | 647 | if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) { |
468 | 648 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending close message on close_flag"); |
470 | 650 | websocket->websocket_manager->connected = 0; |
471 | 651 | } else { |
472 | 652 | 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) { | |
481 | 659 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending close command"); |
482 | 660 | } |
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 { | |
485 | 667 | // 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 { | |
488 | 674 | websocket->websocket_manager->connected = 0; |
489 | 675 | } |
676 | pthread_mutex_unlock(&websocket->websocket_manager->write_lock); | |
490 | 677 | } |
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; | |
493 | 681 | } |
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"); | |
496 | 740 | websocket->websocket_manager->connected = 0; |
497 | 741 | } |
498 | 742 | } 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"); | |
500 | 745 | websocket->websocket_manager->connected = 0; |
501 | 746 | } |
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); | |
507 | 756 | // Wait for thread manager to close if exists |
508 | 757 | if (!thread_ret_websocket_manager) { |
509 | 758 | pthread_join(thread_websocket_manager, NULL); |
538 | 787 | size_t offset = 0; |
539 | 788 | int eol = 0, ret = U_ERROR; |
540 | 789 | uint8_t car; |
541 | ||
790 | ||
542 | 791 | *line_len = 0; |
543 | 792 | do { |
544 | 793 | if (read_data_from_socket(websocket->websocket_manager, &car, 1) == 1) { |
545 | 794 | buffer[offset] = car; |
546 | 795 | } |
547 | ||
796 | ||
548 | 797 | if (offset > 0 && buffer[offset-1] == '\r' && buffer[offset] == '\n') { |
549 | 798 | eol = 1; |
550 | 799 | buffer[offset-1] = '\0'; |
551 | 800 | *line_len = offset - 1; |
552 | 801 | ret = U_OK; |
553 | 802 | } |
554 | ||
803 | ||
555 | 804 | 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 | } | |
557 | 812 | return ret; |
558 | 813 | } |
559 | 814 | |
564 | 819 | * Return U_OK on success |
565 | 820 | */ |
566 | 821 | 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; | |
568 | 823 | 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; | |
570 | 825 | char buffer[4096] = {0}; |
571 | 826 | 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 | ||
574 | 830 | // Send HTTP Request |
575 | 831 | 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); |
576 | 832 | ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); |
577 | 833 | 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 | } | |
580 | 840 | ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); |
581 | 841 | o_free(http_line); |
582 | ||
842 | ||
583 | 843 | http_line = msprintf("Upgrade: websocket\r\n"); |
584 | 844 | ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); |
585 | 845 | o_free(http_line); |
586 | ||
846 | ||
587 | 847 | http_line = msprintf("Connection: Upgrade\r\n"); |
588 | 848 | ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); |
589 | 849 | o_free(http_line); |
590 | ||
850 | ||
591 | 851 | http_line = msprintf("Origin: %s://%s\r\n", y_url->scheme, y_url->host); |
592 | 852 | ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)http_line, o_strlen(http_line)); |
593 | 853 | o_free(http_line); |
594 | ||
854 | ||
595 | 855 | keys = u_map_enum_keys(request->map_header); |
596 | 856 | for (i=0; keys[i] != NULL; i++) { |
597 | 857 | http_line = msprintf("%s: %s\r\n", keys[i], u_map_get_case(request->map_header, keys[i])); |
603 | 863 | check_websocket |= WEBSOCKET_RESPONSE_EXTENSION; |
604 | 864 | } |
605 | 865 | } |
606 | ||
866 | ||
607 | 867 | if (websocket->websocket_manager->tcp_sock >= 0) { |
608 | 868 | // Send empty line |
609 | 869 | const char * empty = "\r\n"; |
610 | 870 | ulfius_websocket_send_frame(websocket->websocket_manager, (uint8_t *)empty, o_strlen(empty)); |
611 | 871 | } |
612 | ||
872 | ||
613 | 873 | // Read and parse response |
614 | 874 | 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")) { | |
616 | 876 | websocket_response_http = 1; |
617 | 877 | response->status = strtol(split_line[1], NULL, 10); |
618 | 878 | response->protocol = o_strdup("1.1"); |
619 | 879 | } |
620 | 880 | free_string_array(split_line); |
621 | 881 | } |
622 | if (websocket_response_http) { | |
882 | if (websocket_response_http && response->status == 101) { | |
623 | 883 | do { |
624 | 884 | if (ulfius_get_next_line_from_http_response(websocket, buffer, buffer_len, &line_len) == U_OK) { |
625 | 885 | if (o_strlen(buffer) && (separator = o_strchr(buffer, ':')) != NULL) { |
628 | 888 | if (u_map_put(response->map_header, key, value) != U_OK) { |
629 | 889 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error adding header %s:%s to the response structure", key, value); |
630 | 890 | } |
631 | if (0 == o_strcmp(buffer, "Upgrade: websocket")) { | |
891 | if (0 == o_strcasecmp(buffer, "Upgrade: websocket")) { | |
632 | 892 | websocket_response |= WEBSOCKET_RESPONSE_UPGRADE; |
633 | } else if (0 == o_strcmp(buffer, "Connection: Upgrade")) { | |
893 | } else if (0 == o_strcasecmp(buffer, "Connection: Upgrade")) { | |
634 | 894 | 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")) { | |
636 | 896 | websocket->websocket_manager->protocol = o_strdup(value); |
637 | 897 | 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")) { | |
639 | 899 | websocket->websocket_manager->extensions = o_strdup(value); |
640 | 900 | 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) { | |
642 | 902 | websocket_response |= WEBSOCKET_RESPONSE_ACCEPT; |
643 | 903 | } |
644 | 904 | o_free(key); |
655 | 915 | } |
656 | 916 | } while (1); |
657 | 917 | } |
658 | ||
918 | ||
659 | 919 | if (!websocket_response_http || !(websocket_response & check_websocket) || response->status != 101) { |
660 | 920 | if (u_map_has_key(response->map_header, "Content-Length")) { |
661 | 921 | response->binary_body_length = strtol(u_map_get(response->map_header, "Content-Length"), NULL, 10); |
673 | 933 | ret = U_ERROR; |
674 | 934 | } else { |
675 | 935 | 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 | ||
678 | 986 | return ret; |
679 | 987 | } |
680 | 988 | |
686 | 994 | int ret; |
687 | 995 | struct sockaddr_in server; |
688 | 996 | struct hostent * he; |
689 | ||
997 | ||
690 | 998 | websocket->websocket_manager->tcp_sock = socket(AF_INET, SOCK_STREAM, 0); |
691 | 999 | if (websocket->websocket_manager->tcp_sock != -1) { |
692 | 1000 | if ((he = gethostbyname(y_url->host)) != NULL) { |
693 | 1001 | memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); |
694 | 1002 | server.sin_family = AF_INET; |
695 | 1003 | server.sin_port = htons(y_url->port); |
696 | ||
1004 | ||
697 | 1005 | 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; | |
699 | 1008 | websocket->websocket_manager->connected = 1; |
700 | 1009 | websocket->websocket_manager->close_flag = 0; |
701 | 1010 | websocket->urh = NULL; |
702 | 1011 | websocket->instance = NULL; |
703 | ||
1012 | ||
704 | 1013 | ret = ulfius_websocket_connection_handshake(request, y_url, websocket, response); |
705 | 1014 | } else { |
706 | 1015 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error connecting socket"); |
730 | 1039 | gnutls_datum_t out; |
731 | 1040 | int type; |
732 | 1041 | unsigned status; |
733 | ||
1042 | ||
734 | 1043 | if (gnutls_global_init() >= 0) { |
735 | 1044 | if (gnutls_certificate_allocate_credentials(&websocket->websocket_manager->xcred) >= 0 && |
736 | 1045 | gnutls_certificate_set_x509_system_trust(websocket->websocket_manager->xcred) >= 0 && |
738 | 1047 | gnutls_server_name_set(websocket->websocket_manager->gnutls_session, GNUTLS_NAME_DNS, y_url->host, o_strlen(y_url->host)) >= 0 && |
739 | 1048 | gnutls_set_default_priority(websocket->websocket_manager->gnutls_session) >= 0 && |
740 | 1049 | gnutls_credentials_set(websocket->websocket_manager->gnutls_session, GNUTLS_CRD_CERTIFICATE, websocket->websocket_manager->xcred) >= 0) { |
741 | ||
1050 | ||
742 | 1051 | if (request->check_server_certificate) { |
743 | 1052 | gnutls_session_set_verify_cert(websocket->websocket_manager->gnutls_session, y_url->host, 0); |
744 | 1053 | } |
750 | 1059 | server.sin_family = AF_INET; |
751 | 1060 | server.sin_port = htons(y_url->port); |
752 | 1061 | 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; | |
754 | 1064 | websocket->websocket_manager->connected = 1; |
755 | 1065 | websocket->websocket_manager->close_flag = 0; |
756 | 1066 | websocket->urh = NULL; |
757 | 1067 | websocket->instance = NULL; |
758 | ||
1068 | ||
759 | 1069 | gnutls_transport_set_int(websocket->websocket_manager->gnutls_session, websocket->websocket_manager->tcp_sock); |
760 | 1070 | gnutls_handshake_set_timeout(websocket->websocket_manager->gnutls_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); |
761 | 1071 | |
763 | 1073 | ret = gnutls_handshake(websocket->websocket_manager->gnutls_session); |
764 | 1074 | } |
765 | 1075 | while (ret < 0 && gnutls_error_is_fatal(ret) == 0); |
766 | ||
1076 | ||
767 | 1077 | if (ret < 0) { |
768 | 1078 | if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { |
769 | 1079 | /* check certificate verification status */ |
781 | 1091 | } else { |
782 | 1092 | char * desc = gnutls_session_get_desc(websocket->websocket_manager->gnutls_session); |
783 | 1093 | gnutls_free(desc); |
784 | ||
1094 | ||
785 | 1095 | ret = ulfius_websocket_connection_handshake(request, y_url, websocket, response); |
786 | 1096 | } |
787 | 1097 | } else { |
808 | 1118 | ret = U_ERROR; |
809 | 1119 | } |
810 | 1120 | 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); | |
811 | 1131 | } |
812 | 1132 | |
813 | 1133 | /** |
828 | 1148 | UNUSED(con_cls); |
829 | 1149 | UNUSED(extra_in); |
830 | 1150 | UNUSED(extra_in_size); |
831 | ||
1151 | ||
832 | 1152 | if (websocket != NULL) { |
833 | 1153 | websocket->urh = urh; |
834 | 1154 | // Run websocket manager in a thread |
835 | 1155 | websocket->websocket_manager->type = U_WEBSOCKET_SERVER; |
836 | 1156 | 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; | |
839 | 1161 | websocket->websocket_manager->connected = 1; |
840 | 1162 | websocket->websocket_manager->close_flag = 0; |
841 | 1163 | thread_ret_websocket = pthread_create(&thread_websocket, NULL, ulfius_thread_websocket, (void *)websocket); |
860 | 1182 | */ |
861 | 1183 | int ulfius_check_handshake_response(const char * key, const char * response) { |
862 | 1184 | char websocket_accept[32] = {0}; |
863 | ||
1185 | ||
864 | 1186 | if (key != NULL && response != NULL) { |
865 | 1187 | if (ulfius_generate_handshake_answer(key, websocket_accept) && 0 == o_strcmp(websocket_accept, response)) { |
866 | 1188 | return U_OK; |
880 | 1202 | unsigned char encoded_key[32] = {0}; |
881 | 1203 | size_t encoded_key_size = 32, encoded_key_size_base64; |
882 | 1204 | int res, to_return = 0; |
883 | ||
1205 | ||
884 | 1206 | key_data.data = (unsigned char*)msprintf("%s%s", key, U_WEBSOCKET_MAGIC_STRING); |
885 | 1207 | key_data.size = o_strlen((const char *)key_data.data); |
886 | ||
1208 | ||
887 | 1209 | if (key_data.data != NULL && out_digest != NULL && (res = gnutls_fingerprint(GNUTLS_DIG_SHA1, &key_data, encoded_key, &encoded_key_size)) == GNUTLS_E_SUCCESS) { |
888 | 1210 | if (o_base64_encode(encoded_key, encoded_key_size, (unsigned char *)out_digest, &encoded_key_size_base64)) { |
889 | 1211 | to_return = 1; |
938 | 1260 | int ulfius_check_list_match(const char * source, const char * match, const char * separator, char ** result) { |
939 | 1261 | char ** source_list = NULL, ** match_list = NULL; |
940 | 1262 | int i, ret = U_OK; |
941 | ||
1263 | ||
942 | 1264 | if (result != NULL) { |
943 | 1265 | *result = NULL; |
944 | 1266 | if (match == NULL) { |
979 | 1301 | int ulfius_check_first_match(const char * source, const char * match, const char * separator, char ** result) { |
980 | 1302 | char ** source_list = NULL, ** match_list = NULL; |
981 | 1303 | int i, ret = U_OK; |
982 | ||
1304 | ||
983 | 1305 | if (result != NULL) { |
984 | 1306 | *result = NULL; |
985 | 1307 | if (match == NULL) { |
1042 | 1364 | if (instance != NULL && websocket != NULL) { |
1043 | 1365 | ((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 *)); |
1044 | 1366 | 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 | } | |
1048 | 1376 | } else { |
1049 | 1377 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for instance->websocket_handler->websocket_active"); |
1050 | 1378 | return U_ERROR_MEMORY; |
1059 | 1387 | */ |
1060 | 1388 | int ulfius_instance_remove_websocket_active(struct _u_instance * instance, struct _websocket * websocket) { |
1061 | 1389 | 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; | |
1089 | 1425 | } |
1090 | 1426 | |
1091 | 1427 | /********************************/ |
1104 | 1440 | const uint64_t fragment_len) { |
1105 | 1441 | int ret = U_OK, ret_message, count = WEBSOCKET_MAX_CLOSE_TRY; |
1106 | 1442 | 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 | ||
1108 | 1449 | if (websocket_manager != NULL && websocket_manager->connected) { |
1109 | 1450 | 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) { | |
1111 | 1452 | // If message sent is U_WEBSOCKET_OPCODE_CLOSE, wait for the close response for WEBSOCKET_MAX_CLOSE_TRY messages max, then close the connection |
1112 | 1453 | do { |
1113 | 1454 | if (is_websocket_data_available(websocket_manager)) { |
1129 | 1470 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending U_WEBSOCKET_OPCODE_CLOSE message"); |
1130 | 1471 | } |
1131 | 1472 | 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); | |
1134 | 1515 | } |
1135 | 1516 | } else { |
1136 | 1517 | ret = U_ERROR_PARAMS; |
1146 | 1527 | const uint8_t opcode, |
1147 | 1528 | const uint64_t data_len, |
1148 | 1529 | 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); | |
1150 | 1531 | } |
1151 | 1532 | |
1152 | 1533 | /** |
1281 | 1662 | int ulfius_init_websocket_manager(struct _websocket_manager * websocket_manager) { |
1282 | 1663 | pthread_mutexattr_t mutexattr; |
1283 | 1664 | int ret = U_OK; |
1284 | ||
1665 | ||
1285 | 1666 | if (websocket_manager != NULL) { |
1286 | 1667 | websocket_manager->connected = 0; |
1287 | 1668 | websocket_manager->close_flag = 0; |
1669 | websocket_manager->ping_sent = 0; | |
1288 | 1670 | websocket_manager->mhd_sock = 0; |
1289 | 1671 | websocket_manager->tcp_sock = 0; |
1290 | 1672 | websocket_manager->protocol = NULL; |
1291 | 1673 | websocket_manager->extensions = NULL; |
1674 | websocket_manager->rsv_expected = 0; | |
1292 | 1675 | pthread_mutexattr_init ( &mutexattr ); |
1293 | 1676 | pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE ); |
1294 | 1677 | if (pthread_mutex_init(&(websocket_manager->read_lock), &mutexattr) != 0 || pthread_mutex_init(&(websocket_manager->write_lock), &mutexattr) != 0) { |
1304 | 1687 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing message_list_incoming or message_list_outcoming"); |
1305 | 1688 | ret = U_ERROR_MEMORY; |
1306 | 1689 | } |
1307 | websocket_manager->fds.events = POLLIN | POLLRDHUP; | |
1690 | websocket_manager->fds_in.events = POLLIN | POLLRDHUP; | |
1691 | websocket_manager->fds_out.events = POLLOUT | POLLRDHUP; | |
1308 | 1692 | websocket_manager->type = U_WEBSOCKET_NONE; |
1309 | 1693 | |
1310 | 1694 | if (ret != U_OK) { |
1311 | 1695 | o_free(websocket_manager->message_list_incoming); |
1312 | 1696 | o_free(websocket_manager->message_list_outcoming); |
1313 | 1697 | } |
1698 | websocket_manager->websocket_extension_list = NULL; | |
1314 | 1699 | pthread_mutexattr_destroy(&mutexattr); |
1315 | 1700 | } else { |
1316 | 1701 | ret = U_ERROR_PARAMS; |
1322 | 1707 | * Clear data of a websocket_manager |
1323 | 1708 | */ |
1324 | 1709 | void ulfius_clear_websocket_manager(struct _websocket_manager * websocket_manager) { |
1710 | size_t len, i; | |
1711 | struct _websocket_extension * extension; | |
1712 | ||
1325 | 1713 | if (websocket_manager != NULL) { |
1326 | 1714 | pthread_mutex_destroy(&websocket_manager->read_lock); |
1327 | 1715 | pthread_mutex_destroy(&websocket_manager->write_lock); |
1333 | 1721 | websocket_manager->message_list_outcoming = NULL; |
1334 | 1722 | o_free(websocket_manager->protocol); |
1335 | 1723 | 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); | |
1336 | 1734 | } |
1337 | 1735 | } |
1338 | 1736 | |
1340 | 1738 | /** Server websocket functions **/ |
1341 | 1739 | /********************************/ |
1342 | 1740 | |
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 | */ | |
1358 | 1741 | int ulfius_set_websocket_response(struct _u_response * response, |
1359 | 1742 | const char * websocket_protocol, |
1360 | const char * websocket_extensions, | |
1743 | const char * websocket_extensions, | |
1361 | 1744 | void (* websocket_manager_callback) (const struct _u_request * request, |
1362 | 1745 | struct _websocket_manager * websocket_manager, |
1363 | 1746 | void * websocket_manager_user_data), |
1395 | 1778 | ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = websocket_incoming_user_data; |
1396 | 1779 | ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = websocket_onclose_callback; |
1397 | 1780 | ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data = websocket_onclose_user_data; |
1781 | ((struct _websocket_handle *)response->websocket_handle)->rsv_expected = 0; | |
1398 | 1782 | return U_OK; |
1399 | 1783 | } else { |
1400 | 1784 | return U_ERROR_PARAMS; |
1401 | 1785 | } |
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), ";", ¶meters)) { | |
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]), "=", ¶m_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]), "=", ¶m_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); | |
1402 | 2175 | } |
1403 | 2176 | |
1404 | 2177 | /** |
1411 | 2184 | */ |
1412 | 2185 | int ulfius_websocket_send_close_signal(struct _websocket_manager * websocket_manager) { |
1413 | 2186 | if (websocket_manager != NULL) { |
2187 | ||
1414 | 2188 | websocket_manager->close_flag = 1; |
1415 | 2189 | return U_OK; |
1416 | 2190 | } else { |
1440 | 2214 | int ulfius_websocket_wait_close(struct _websocket_manager * websocket_manager, unsigned int timeout) { |
1441 | 2215 | struct timespec abstime; |
1442 | 2216 | int ret; |
1443 | ||
2217 | ||
1444 | 2218 | if (websocket_manager != NULL) { |
1445 | 2219 | if (websocket_manager->connected) { |
1446 | 2220 | if (timeout) { |
1493 | 2267 | static char * rand_string(char * str, size_t str_size) { |
1494 | 2268 | const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
1495 | 2269 | size_t n; |
1496 | ||
2270 | ||
1497 | 2271 | if (str_size > 0 && str != NULL) { |
1498 | 2272 | for (n = 0; n < str_size; n++) { |
1499 | 2273 | long key = random_at_most((sizeof(charset)) - 2); |
1518 | 2292 | int ret; |
1519 | 2293 | char rand_str[17] = {0}, rand_str_base64[25] = {0}; |
1520 | 2294 | size_t out_len; |
1521 | ||
2295 | ||
1522 | 2296 | if (request != NULL && url != NULL) { |
1523 | 2297 | o_free(request->http_protocol); |
1524 | 2298 | o_free(request->http_verb); |
1548 | 2322 | } else { |
1549 | 2323 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_set_websocket_request input parameters"); |
1550 | 2324 | 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; | |
1551 | 2407 | } |
1552 | 2408 | return ret; |
1553 | 2409 | } |
1576 | 2432 | struct yuarel y_url; |
1577 | 2433 | char * url, * basic_auth_encoded_header, * basic_auth, * basic_auth_encoded; |
1578 | 2434 | size_t basic_auth_encoded_len; |
1579 | struct _websocket * websocket; | |
2435 | struct _websocket * websocket = NULL; | |
1580 | 2436 | pthread_t thread_websocket; |
1581 | 2437 | int thread_ret_websocket = 0, thread_detach_websocket = 0; |
1582 | ||
2438 | ||
1583 | 2439 | if (request != NULL && response != NULL && (websocket_manager_callback != NULL || websocket_incoming_message_callback != NULL)) { |
1584 | 2440 | url = o_strdup(request->http_url); |
1585 | 2441 | if (!yuarel_parse(&y_url, url)) { |
1606 | 2462 | } |
1607 | 2463 | o_free(basic_auth); |
1608 | 2464 | } |
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) { | |
1612 | 2480 | websocket->request = ulfius_duplicate_request(request); |
1613 | 2481 | websocket->websocket_manager->type = U_WEBSOCKET_CLIENT; |
1614 | 2482 | websocket->websocket_manager_callback = websocket_manager_callback; |
1646 | 2514 | ulfius_clear_websocket(websocket); |
1647 | 2515 | ret = U_ERROR; |
1648 | 2516 | } |
1649 | websocket_client_handler->websocket = websocket; | |
1650 | 2517 | } |
1651 | 2518 | } else { |
1652 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for websocket"); | |
1653 | 2519 | ret = U_ERROR_MEMORY; |
1654 | 2520 | } |
1655 | 2521 | } else { |
1667 | 2533 | return ret; |
1668 | 2534 | } |
1669 | 2535 | |
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), ";", ¶meters)) { | |
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]), "=", ¶m_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]), "=", ¶m_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 | ||
1670 | 2651 | /** |
1671 | 2652 | * Send a close signal to the websocket |
1672 | 2653 | * return U_OK when the signal is sent |
1687 | 2668 | */ |
1688 | 2669 | int ulfius_websocket_client_connection_close(struct _websocket_client_handler * websocket_client_handler) { |
1689 | 2670 | 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 { | |
1692 | 2680 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_send_close_signal"); |
1693 | 2681 | return U_ERROR; |
1694 | 2682 | } |
1695 | ulfius_clear_websocket(websocket_client_handler->websocket); | |
2683 | } else { | |
1696 | 2684 | return U_OK; |
1697 | } else { | |
1698 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_websocket_send_close_signal"); | |
1699 | return U_ERROR; | |
1700 | 2685 | } |
1701 | 2686 | } else { |
1702 | 2687 | return U_ERROR_PARAMS; |
1724 | 2709 | */ |
1725 | 2710 | int ulfius_websocket_client_connection_wait_close(struct _websocket_client_handler * websocket_client_handler, unsigned int timeout) { |
1726 | 2711 | int ret; |
1727 | ||
2712 | ||
1728 | 2713 | if (websocket_client_handler != NULL) { |
1729 | 2714 | ret = ulfius_websocket_wait_close(websocket_client_handler->websocket->websocket_manager, timeout); |
1730 | 2715 | if (ret == U_WEBSOCKET_STATUS_CLOSE && websocket_client_handler->websocket != NULL) { |
1731 | 2716 | ulfius_clear_websocket(websocket_client_handler->websocket); |
2717 | websocket_client_handler->websocket = NULL; | |
1732 | 2718 | } |
1733 | 2719 | return ret; |
1734 | 2720 | } else { |
0 | 0 | /** |
1 | * | |
1 | * | |
2 | 2 | * Ulfius Framework |
3 | * | |
3 | * | |
4 | 4 | * REST framework library |
5 | * | |
5 | * | |
6 | 6 | * ulfius.c: framework functions definitions |
7 | * | |
7 | * | |
8 | 8 | * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org> |
9 | 9 | * |
10 | 10 | * This program is free software; you can redistribute it and/or |
19 | 19 | * |
20 | 20 | * You should have received a copy of the GNU General Public |
21 | 21 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
22 | * | |
22 | * | |
23 | 23 | */ |
24 | 24 | |
25 | 25 | #include <string.h> |
72 | 72 | char * tmp; |
73 | 73 | int res; |
74 | 74 | UNUSED(kind); |
75 | ||
75 | ||
76 | 76 | if (cls == NULL || key == NULL) { |
77 | 77 | // Invalid parameters |
78 | 78 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid parameters for ulfius_fill_map_check_utf8"); |
79 | 79 | 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)) { | |
81 | 81 | if (u_map_get(((struct _u_map *)cls), key) != NULL) { |
82 | 82 | // u_map already has a value with this this key, appending value separated with a comma ',') |
83 | 83 | tmp = msprintf("%s,%s", u_map_get(((struct _u_map *)cls), key), (value==NULL?"":value)); |
109 | 109 | char * tmp; |
110 | 110 | int res; |
111 | 111 | UNUSED(kind); |
112 | ||
112 | ||
113 | 113 | if (cls == NULL || key == NULL) { |
114 | 114 | // Invalid parameters |
115 | 115 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid parameters for ulfius_fill_map"); |
204 | 204 | void * ulfius_uri_logger (void * cls, const char * uri) { |
205 | 205 | struct connection_info_struct * con_info = o_malloc (sizeof (struct connection_info_struct)); |
206 | 206 | UNUSED(cls); |
207 | ||
207 | ||
208 | 208 | if (con_info != NULL) { |
209 | 209 | con_info->callback_first_iteration = 1; |
210 | 210 | con_info->u_instance = NULL; |
215 | 215 | o_free(con_info); |
216 | 216 | return NULL; |
217 | 217 | } |
218 | ||
218 | ||
219 | 219 | if (NULL == con_info->request || ulfius_init_request(con_info->request) != U_OK) { |
220 | 220 | ulfius_clean_request_full(con_info->request); |
221 | 221 | o_free(con_info); |
284 | 284 | UNUSED(toe); |
285 | 285 | UNUSED(connection); |
286 | 286 | UNUSED(cls); |
287 | ||
287 | ||
288 | 288 | if (NULL == con_info) { |
289 | 289 | return; |
290 | 290 | } |
318 | 318 | size_t cur_size = size; |
319 | 319 | char * data_dup, * filename_param; |
320 | 320 | UNUSED(kind); |
321 | ||
321 | ||
322 | 322 | if (filename != NULL && con_info->u_instance != NULL && con_info->u_instance->file_upload_callback != NULL) { |
323 | 323 | 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) { |
324 | 324 | return MHD_YES; |
327 | 327 | } |
328 | 328 | } else { |
329 | 329 | 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))) { | |
331 | 331 | return MHD_YES; |
332 | 332 | } else { |
333 | 333 | data_dup = o_strndup(data, size); // Force value to end with a NULL character |
342 | 342 | } else { |
343 | 343 | return MHD_NO; |
344 | 344 | } |
345 | ||
345 | ||
346 | 346 | if (filename != NULL) { |
347 | 347 | filename_param = msprintf("%s_filename", key); |
348 | 348 | 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) { |
350 | 350 | } |
351 | 351 | o_free(filename_param); |
352 | 352 | } |
353 | ||
353 | ||
354 | 354 | 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) { |
355 | 355 | o_free(data_dup); |
356 | 356 | return MHD_YES; |
398 | 398 | #ifndef U_DISABLE_WEBSOCKET |
399 | 399 | // Websocket variables |
400 | 400 | 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; | |
402 | 403 | #endif |
403 | 404 | |
404 | 405 | #ifndef U_DISABLE_GNUTLS |
412 | 413 | char * content_type, * auth_realm = NULL; |
413 | 414 | struct _u_response * response = NULL; |
414 | 415 | struct sockaddr * so_client; |
415 | ||
416 | ||
416 | 417 | void * response_buffer = NULL; |
417 | 418 | size_t response_buffer_len = 0; |
418 | ||
419 | ||
419 | 420 | // Response variables |
420 | 421 | struct MHD_Response * mhd_response = NULL; |
421 | ||
422 | ||
422 | 423 | UNUSED(url); |
423 | ||
424 | ||
424 | 425 | // Prepare for POST or PUT input data |
425 | 426 | // Initialize the input maps |
426 | 427 | if (con_info == NULL) { |
427 | 428 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error con_info is NULL"); |
428 | 429 | return MHD_NO; |
429 | 430 | } |
430 | ||
431 | ||
431 | 432 | if (con_info->u_instance == NULL) { |
432 | 433 | con_info->u_instance = (struct _u_instance *)cls; |
433 | 434 | } |
475 | 476 | MHD_get_connection_values (connection, MHD_COOKIE_KIND, ulfius_fill_map, con_info->request->map_cookie); |
476 | 477 | } |
477 | 478 | content_type = (char*)u_map_get_case(con_info->request->map_header, ULFIUS_HTTP_HEADER_CONTENT); |
478 | ||
479 | ||
479 | 480 | // 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)) || | |
481 | 482 | 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, o_strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))) { |
482 | 483 | con_info->has_post_processor = 1; |
483 | 484 | con_info->post_processor = MHD_create_post_processor (connection, ULFIUS_POSTBUFFERSIZE, mhd_iterate_post_data, (void *) con_info); |
491 | 492 | return MHD_YES; |
492 | 493 | } else if (*upload_data_size != 0) { |
493 | 494 | size_t body_len = con_info->request->binary_body_length + *upload_data_size, upload_data_size_current = *upload_data_size; |
494 | ||
495 | ||
495 | 496 | 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) { |
496 | 497 | body_len = ((struct _u_instance *)cls)->max_post_body_size; |
497 | 498 | upload_data_size_current = ((struct _u_instance *)cls)->max_post_body_size - con_info->request->binary_body_length; |
498 | 499 | } |
499 | ||
500 | ||
500 | 501 | if (body_len >= con_info->request->binary_body_length) { |
501 | 502 | con_info->request->binary_body = o_realloc(con_info->request->binary_body, body_len); |
502 | 503 | if (con_info->request->binary_body == NULL) { |
507 | 508 | con_info->request->binary_body_length += upload_data_size_current; |
508 | 509 | // Handles request binary_body |
509 | 510 | 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)) || | |
511 | 512 | 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, o_strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA))) { |
512 | 513 | MHD_post_process (con_info->post_processor, upload_data, *upload_data_size); |
513 | 514 | } |
520 | 521 | } else { |
521 | 522 | // Check if the endpoint has one or more matches |
522 | 523 | current_endpoint_list = ulfius_endpoint_match(method, con_info->request->url_path, endpoint_list); |
523 | ||
524 | ||
524 | 525 | // Set to default_endpoint if no match |
525 | 526 | 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) { |
526 | 527 | current_endpoint_list = o_realloc(current_endpoint_list, 2*sizeof(struct _u_endpoint *)); |
537 | 538 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for current_endpoint_list of default endpoint"); |
538 | 539 | } |
539 | 540 | } |
540 | ||
541 | ||
541 | 542 | #if MHD_VERSION >= 0x00096100 |
542 | 543 | mhd_response_flag = ((struct _u_instance *)cls)->mhd_response_copy_data?MHD_RESPMEM_MUST_COPY:MHD_RESPMEM_MUST_FREE; |
543 | 544 | #else |
558 | 559 | u_map_clean_full(response->map_header); |
559 | 560 | response->map_header = u_map_copy(((struct _u_instance *)cls)->default_headers); |
560 | 561 | } |
561 | ||
562 | ||
562 | 563 | // Initialize auth variables |
563 | 564 | con_info->request->auth_basic_user = MHD_basic_auth_get_username_password(connection, &con_info->request->auth_basic_password); |
564 | ||
565 | ||
565 | 566 | for (i=0; current_endpoint_list[i] != NULL && !close_loop; i++) { |
566 | 567 | current_endpoint = current_endpoint_list[i]; |
567 | 568 | u_map_empty(con_info->request->map_url); |
573 | 574 | } |
574 | 575 | // Run callback function with the input parameters filled for the current callback |
575 | 576 | 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 | } | |
577 | 580 | if (response->timeout > 0 && MHD_set_connection_option(connection, MHD_CONNECTION_OPTION_TIMEOUT, response->timeout) != MHD_YES) { |
578 | 581 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting connection response timeout value"); |
579 | 582 | } |
604 | 607 | 0 == o_strcmp(con_info->request->http_protocol, "HTTP/1.1") && |
605 | 608 | 0 == o_strcmp(u_map_get_case(con_info->request->map_header, "Sec-WebSocket-Version"), "13") && |
606 | 609 | 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; | |
608 | 611 | // 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) { | |
611 | 658 | 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)) { | |
613 | 660 | websocket->request = ulfius_duplicate_request(con_info->request); |
614 | 661 | if (websocket->request != NULL) { |
615 | 662 | websocket->instance = (struct _u_instance *)cls; |
619 | 666 | websocket->websocket_incoming_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data; |
620 | 667 | websocket->websocket_onclose_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback; |
621 | 668 | 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; | |
622 | 672 | mhd_response = MHD_create_response_for_upgrade(ulfius_start_websocket_cb, websocket); |
623 | 673 | if (mhd_response == NULL) { |
624 | 674 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_for_upgrade"); |
633 | 683 | MHD_add_response_header (mhd_response, |
634 | 684 | "Sec-WebSocket-Protocol", |
635 | 685 | protocol); |
686 | if (o_strlen(extension)) { | |
687 | MHD_add_response_header (mhd_response, | |
688 | "Sec-WebSocket-Extensions", | |
689 | extension); | |
690 | } | |
636 | 691 | if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { |
637 | 692 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); |
638 | 693 | mhd_ret = MHD_NO; |
712 | 767 | } |
713 | 768 | #endif |
714 | 769 | } 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 | |
717 | 772 | callback_ret = U_CALLBACK_COMPLETE; |
718 | 773 | } |
719 | 774 | // Test callback_ret to know what to do |
720 | 775 | switch (callback_ret) { |
721 | 776 | case U_CALLBACK_CONTINUE: |
777 | case U_CALLBACK_IGNORE: | |
722 | 778 | break; |
723 | 779 | case U_CALLBACK_COMPLETE: |
724 | 780 | close_loop = 1; |
799 | 855 | } |
800 | 856 | } |
801 | 857 | } |
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 | ||
802 | 943 | if (mhd_response != NULL) { |
803 | 944 | if (auth_realm != NULL && inner_error == U_CALLBACK_UNAUTHORIZED) { |
804 | 945 | mhd_ret = MHD_queue_basic_auth_fail_response (connection, auth_realm, mhd_response); |
851 | 992 | * ulfius_run_mhd_daemon |
852 | 993 | * Starts a mhd daemon for the specified instance |
853 | 994 | * return a pointer to the mhd_daemon on success, NULL on error |
854 | * | |
995 | * | |
855 | 996 | */ |
856 | 997 | 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) { |
857 | 998 | unsigned int mhd_flags = MHD_USE_THREAD_PER_CONNECTION; |
866 | 1007 | #ifndef U_DISABLE_WEBSOCKET |
867 | 1008 | mhd_flags |= MHD_ALLOW_UPGRADE; |
868 | 1009 | #endif |
869 | ||
1010 | ||
870 | 1011 | if (u_instance->mhd_daemon == NULL) { |
871 | 1012 | struct MHD_OptionItem mhd_ops[8]; |
872 | ||
1013 | ||
873 | 1014 | // Default options |
874 | 1015 | mhd_ops[0].option = MHD_OPTION_NOTIFY_COMPLETED; |
875 | 1016 | mhd_ops[0].value = (intptr_t)mhd_request_completed; |
876 | 1017 | mhd_ops[0].ptr_value = NULL; |
877 | ||
1018 | ||
878 | 1019 | #if MHD_VERSION >= 0x00095208 |
879 | 1020 | // If bind_address6 is specified, listen only to IPV6 addresses |
880 | 1021 | if (u_instance->bind_address6 != NULL) { |
900 | 1041 | mhd_ops[1].value = 0; |
901 | 1042 | mhd_ops[1].ptr_value = (void *)u_instance->bind_address; |
902 | 1043 | #endif |
903 | ||
1044 | ||
904 | 1045 | mhd_ops[2].option = MHD_OPTION_URI_LOG_CALLBACK; |
905 | 1046 | mhd_ops[2].value = (intptr_t)ulfius_uri_logger; |
906 | 1047 | mhd_ops[2].ptr_value = NULL; |
907 | ||
1048 | ||
908 | 1049 | index = 3; |
909 | 1050 | |
910 | 1051 | if (key_pem != NULL && cert_pem != NULL) { |
913 | 1054 | mhd_ops[index].option = MHD_OPTION_HTTPS_MEM_KEY; |
914 | 1055 | mhd_ops[index].value = 0; |
915 | 1056 | mhd_ops[index].ptr_value = (void*)key_pem; |
916 | ||
1057 | ||
917 | 1058 | mhd_ops[index + 1].option = MHD_OPTION_HTTPS_MEM_CERT; |
918 | 1059 | mhd_ops[index + 1].value = 0; |
919 | 1060 | mhd_ops[index + 1].ptr_value = (void*)cert_pem; |
920 | ||
1061 | ||
921 | 1062 | index += 2; |
922 | 1063 | |
923 | 1064 | if (root_ca_perm != NULL) { |
932 | 1073 | mhd_ops[index].option = MHD_OPTION_CONNECTION_TIMEOUT; |
933 | 1074 | mhd_ops[index].value = u_instance->timeout; |
934 | 1075 | mhd_ops[index].ptr_value = NULL; |
935 | ||
1076 | ||
936 | 1077 | index++; |
937 | 1078 | } |
938 | 1079 | |
941 | 1082 | mhd_ops[index].ptr_value = NULL; |
942 | 1083 | |
943 | 1084 | 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, | |
945 | 1086 | MHD_OPTION_ARRAY, mhd_ops, |
946 | 1087 | MHD_OPTION_END |
947 | 1088 | ); |
955 | 1096 | * ulfius_start_framework |
956 | 1097 | * Initializes the framework and run the webservice based on the parameters given |
957 | 1098 | * return true if no error |
958 | * | |
1099 | * | |
959 | 1100 | * u_instance: pointer to a struct _u_instance that describe its port and bind address |
960 | 1101 | * return U_OK on success |
961 | 1102 | */ |
970 | 1111 | /** |
971 | 1112 | * ulfius_start_secure_framework |
972 | 1113 | * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection |
973 | * | |
1114 | * | |
974 | 1115 | * u_instance: pointer to a struct _u_instance that describe its port and bind address |
975 | 1116 | * key_pem: private key for the server |
976 | 1117 | * cert_pem: server certificate |
983 | 1124 | #ifndef U_DISABLE_JANSSON |
984 | 1125 | o_malloc_t malloc_fn; |
985 | 1126 | o_free_t free_fn; |
986 | ||
1127 | ||
987 | 1128 | o_get_alloc_funcs(&malloc_fn, NULL, &free_fn); |
988 | 1129 | json_set_alloc_funcs((json_malloc_t)malloc_fn, (json_free_t)free_fn); |
989 | 1130 | #endif |
997 | 1138 | } |
998 | 1139 | if (ulfius_validate_instance(u_instance) == U_OK) { |
999 | 1140 | u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, key_pem, cert_pem, NULL); |
1000 | ||
1141 | ||
1001 | 1142 | if (u_instance->mhd_daemon == NULL) { |
1002 | 1143 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting"); |
1003 | 1144 | u_instance->status = U_STATUS_ERROR; |
1018 | 1159 | * ulfius_start_secure_ca_trust_framework |
1019 | 1160 | * Initializes the framework and run the webservice based on the parameters given using an HTTPS connection |
1020 | 1161 | * And using a root server to authenticate client connections |
1021 | * | |
1162 | * | |
1022 | 1163 | * u_instance: pointer to a struct _u_instance that describe its port and bind address |
1023 | 1164 | * key_pem: private key for the server |
1024 | 1165 | * cert_pem: server certificate |
1029 | 1170 | #ifndef U_DISABLE_JANSSON |
1030 | 1171 | o_malloc_t malloc_fn; |
1031 | 1172 | o_free_t free_fn; |
1032 | ||
1173 | ||
1033 | 1174 | o_get_alloc_funcs(&malloc_fn, NULL, &free_fn); |
1034 | 1175 | json_set_alloc_funcs((json_malloc_t)malloc_fn, (json_free_t)free_fn); |
1035 | 1176 | #endif |
1051 | 1192 | } |
1052 | 1193 | if (ulfius_validate_instance(u_instance) == U_OK) { |
1053 | 1194 | u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, key_pem, cert_pem, root_ca_pem); |
1054 | ||
1195 | ||
1055 | 1196 | if (u_instance->mhd_daemon == NULL) { |
1056 | 1197 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting"); |
1057 | 1198 | u_instance->status = U_STATUS_ERROR; |
1107 | 1248 | |
1108 | 1249 | /** |
1109 | 1250 | * ulfius_stop_framework |
1110 | * | |
1251 | * | |
1111 | 1252 | * Stop the webservice |
1112 | 1253 | * u_instance: pointer to a struct _u_instance that describe its port and bind address |
1113 | 1254 | * return U_OK on success |
1117 | 1258 | #ifndef U_DISABLE_WEBSOCKET |
1118 | 1259 | int i; |
1119 | 1260 | // 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); | |
1122 | 1268 | } |
1123 | 1269 | pthread_mutex_lock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); |
1124 | 1270 | while (((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active > 0) { |
1125 | 1271 | pthread_cond_wait(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, &((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); |
1126 | 1272 | } |
1127 | 1273 | pthread_mutex_unlock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); |
1128 | #endif | |
1274 | #endif | |
1129 | 1275 | MHD_stop_daemon (u_instance->mhd_daemon); |
1130 | 1276 | u_instance->mhd_daemon = NULL; |
1131 | 1277 | u_instance->status = U_STATUS_STOP; |
1167 | 1313 | struct _u_endpoint * ulfius_duplicate_endpoint_list(const struct _u_endpoint * endpoint_list) { |
1168 | 1314 | struct _u_endpoint * to_return = NULL; |
1169 | 1315 | int i; |
1170 | ||
1316 | ||
1171 | 1317 | if (endpoint_list != NULL) { |
1172 | 1318 | for (i=0; endpoint_list[i].http_method != NULL; i++) { |
1173 | 1319 | if ((to_return = o_realloc(to_return, (i+1)*sizeof(struct _u_endpoint *))) == NULL) { |
1202 | 1348 | */ |
1203 | 1349 | void ulfius_clean_endpoint_list(struct _u_endpoint * endpoint_list) { |
1204 | 1350 | int i; |
1205 | ||
1351 | ||
1206 | 1352 | if (endpoint_list != NULL) { |
1207 | 1353 | for (i=0; endpoint_list[i].http_method != NULL; i++) { |
1208 | 1354 | ulfius_clean_endpoint(&endpoint_list[i]); |
1211 | 1357 | } |
1212 | 1358 | } |
1213 | 1359 | |
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 | */ | |
1221 | 1360 | int ulfius_add_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint) { |
1222 | 1361 | int res; |
1223 | ||
1362 | ||
1224 | 1363 | if (u_instance != NULL && u_endpoint != NULL) { |
1225 | 1364 | if (ulfius_is_valid_endpoint(u_endpoint, 0)) { |
1226 | 1365 | if (u_instance->endpoint_list == NULL) { |
1258 | 1397 | return U_ERROR; |
1259 | 1398 | } |
1260 | 1399 | |
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 | */ | |
1268 | 1400 | int ulfius_add_endpoint_list(struct _u_instance * u_instance, const struct _u_endpoint ** u_endpoint_list) { |
1269 | 1401 | int i, res; |
1270 | 1402 | if (u_instance != NULL && u_endpoint_list != NULL) { |
1282 | 1414 | return U_ERROR; |
1283 | 1415 | } |
1284 | 1416 | |
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 | */ | |
1294 | 1417 | int ulfius_remove_endpoint(struct _u_instance * u_instance, const struct _u_endpoint * u_endpoint) { |
1295 | 1418 | int i, j, found = 0, ret = U_OK; |
1296 | 1419 | char * trim_prefix = NULL, * trim_prefix_save = NULL, * trim_format = NULL, * trim_format_save = NULL, |
1305 | 1428 | trim_cur_prefix = trimcharacter(trim_cur_prefix_save, '/'); |
1306 | 1429 | trim_cur_format_save = o_strdup(u_instance->endpoint_list[i].url_format); |
1307 | 1430 | trim_cur_format = trimcharacter(trim_cur_format_save, '/'); |
1308 | ||
1431 | ||
1309 | 1432 | // Compare u_endpoint with u_instance->endpoint_list[i] |
1310 | 1433 | if (0 == o_strcmp(u_instance->endpoint_list[i].http_method, u_endpoint->http_method) && |
1311 | 1434 | 0 == o_strcmp(trim_cur_prefix, trim_prefix) && |
1345 | 1468 | return ret; |
1346 | 1469 | } |
1347 | 1470 | |
1348 | /** | |
1349 | * ulfius_empty_endpoint | |
1350 | * return an empty endpoint that goes at the end of an endpoint list | |
1351 | */ | |
1352 | 1471 | const struct _u_endpoint * ulfius_empty_endpoint() { |
1353 | 1472 | static struct _u_endpoint empty_endpoint; |
1354 | ||
1473 | ||
1355 | 1474 | empty_endpoint.http_method = NULL; |
1356 | 1475 | empty_endpoint.url_prefix = NULL; |
1357 | 1476 | empty_endpoint.url_format = NULL; |
1360 | 1479 | return &empty_endpoint; |
1361 | 1480 | } |
1362 | 1481 | |
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 | */ | |
1367 | 1482 | int ulfius_equals_endpoints(const struct _u_endpoint * endpoint1, const struct _u_endpoint * endpoint2) { |
1368 | 1483 | if (endpoint1 != NULL && endpoint2 != NULL) { |
1369 | 1484 | if (endpoint1 == endpoint2) { |
1382 | 1497 | } |
1383 | 1498 | } |
1384 | 1499 | |
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 | */ | |
1402 | 1500 | int ulfius_add_endpoint_by_val(struct _u_instance * u_instance, |
1403 | 1501 | const char * http_method, |
1404 | 1502 | const char * url_prefix, |
1422 | 1520 | } |
1423 | 1521 | } |
1424 | 1522 | |
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 | */ | |
1437 | 1523 | int ulfius_remove_endpoint_by_val(struct _u_instance * u_instance, const char * http_method, const char * url_prefix, const char * url_format) { |
1438 | 1524 | struct _u_endpoint endpoint; |
1439 | 1525 | if (u_instance != NULL) { |
1447 | 1533 | } |
1448 | 1534 | } |
1449 | 1535 | |
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 | */ | |
1460 | 1536 | int ulfius_set_default_endpoint(struct _u_instance * u_instance, |
1461 | 1537 | int (* callback_function)(const struct _u_request * request, struct _u_response * response, void * user_data), |
1462 | 1538 | void * user_data) { |
1480 | 1556 | } |
1481 | 1557 | } |
1482 | 1558 | |
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 | */ | |
1502 | 1559 | 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, | |
1511 | 1568 | void * cls), |
1512 | 1569 | void * cls) { |
1513 | 1570 | if (u_instance != NULL && file_upload_callback != NULL) { |
1519 | 1576 | } |
1520 | 1577 | } |
1521 | 1578 | |
1522 | /** | |
1523 | * ulfius_clean_instance | |
1524 | * | |
1525 | * Clean memory allocated by a struct _u_instance * | |
1526 | */ | |
1527 | 1579 | void ulfius_clean_instance(struct _u_instance * u_instance) { |
1528 | 1580 | if (u_instance != NULL) { |
1529 | 1581 | ulfius_clean_endpoint_list(u_instance->endpoint_list); |
1538 | 1590 | #ifndef U_DISABLE_WEBSOCKET |
1539 | 1591 | /* ulfius_clean_instance might be called without websocket_handler being initialized */ |
1540 | 1592 | 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 | |
1566 | 1615 | #if MHD_VERSION >= 0x00095208 |
1567 | 1616 | if (u_instance != NULL && port > 0 && port < 65536 && (bind_address4 == NULL || bind_address6 == NULL) && (network_type & U_USE_ALL)) { |
1568 | 1617 | #else |
1605 | 1654 | ulfius_clean_instance(u_instance); |
1606 | 1655 | return U_ERROR_MEMORY; |
1607 | 1656 | } |
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); | |
1608 | 1665 | ((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init = 0; |
1609 | 1666 | ((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active = 0; |
1610 | 1667 | ((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) || | |
1612 | 1669 | pthread_cond_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, NULL)) { |
1613 | 1670 | y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing websocket_close_lock or websocket_close_cond"); |
1614 | 1671 | ulfius_clean_instance(u_instance); |
1624 | 1681 | } |
1625 | 1682 | } |
1626 | 1683 | |
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 | */ | |
1637 | 1684 | int ulfius_init_instance(struct _u_instance * u_instance, unsigned int port, struct sockaddr_in * bind_address, const char * default_auth_realm) { |
1638 | 1685 | return internal_ulfius_init_instance(u_instance, port, bind_address, NULL, U_USE_IPV4, default_auth_realm); |
1639 | 1686 | } |
1640 | 1687 | |
1641 | 1688 | #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 | */ | |
1653 | 1689 | 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) { |
1654 | 1690 | if (network_type & U_USE_IPV6) { |
1655 | 1691 | return internal_ulfius_init_instance(u_instance, port, NULL, bind_address, bind_address!=NULL?U_USE_IPV6:network_type, default_auth_realm); |
1659 | 1695 | } |
1660 | 1696 | #endif |
1661 | 1697 | |
1662 | /** | |
1663 | * free data allocated by ulfius functions | |
1664 | */ | |
1665 | 1698 | void u_free(void * data) { |
1666 | 1699 | o_free(data); |
1667 | 1700 | } |
1668 | 1701 | |
1669 | 1702 | /** |
1670 | * The utf8_check() function scans the '\0'-terminated string starting | |
1703 | * The utf8_check() function scans the string starting | |
1671 | 1704 | * at s. It returns a pointer to the first byte of the first malformed |
1672 | 1705 | * or overlong UTF-8 sequence found, or NULL if the string contains |
1673 | 1706 | * only correct UTF-8. It also spots UTF-8 sequences that could cause |
1682 | 1715 | * are no doubt performance optimizations possible for certain CPUs. |
1683 | 1716 | * |
1684 | 1717 | * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30 |
1718 | * Nicolas Mora <mail@babelouest.org> | |
1685 | 1719 | * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html |
1686 | 1720 | */ |
1687 | const unsigned char * utf8_check(const char * s_orig) { | |
1721 | const unsigned char * utf8_check(const char * s_orig, size_t len) { | |
1688 | 1722 | const unsigned char * s = (unsigned char *)s_orig; |
1689 | while (*s) { | |
1723 | size_t i = 0; | |
1724 | ||
1725 | while (i<len) { | |
1690 | 1726 | if (*s < 0x80) { |
1691 | 1727 | /* 0xxxxxxx */ |
1692 | 1728 | s++; |
1729 | i++; | |
1693 | 1730 | } else if ((s[0] & 0xe0) == 0xc0) { |
1694 | 1731 | /* 110XXXXx 10xxxxxx */ |
1695 | if ((s[1] & 0xc0) != 0x80 || | |
1732 | if ((i+1 >= len) || | |
1733 | (s[1] & 0xc0) != 0x80 || | |
1696 | 1734 | (s[0] & 0xfe) == 0xc0) { /* overlong? */ |
1697 | 1735 | return s; |
1698 | 1736 | } else { |
1699 | 1737 | s += 2; |
1738 | i += 2; | |
1700 | 1739 | } |
1701 | 1740 | } else if ((s[0] & 0xf0) == 0xe0) { |
1702 | 1741 | /* 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) */ | |
1709 | 1749 | return s; |
1710 | 1750 | } else { |
1711 | 1751 | s += 3; |
1752 | i += 3; | |
1712 | 1753 | } |
1713 | 1754 | } else if ((s[0] & 0xf8) == 0xf0) { |
1714 | 1755 | /* 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? */ | |
1720 | 1762 | return s; |
1721 | 1763 | } else { |
1722 | 1764 | s += 4; |
1765 | i += 4; | |
1723 | 1766 | } |
1724 | 1767 | } else { |
1725 | 1768 | return s; |
1726 | 1769 | } |
1727 | 1770 | } |
1728 | ||
1729 | 1771 | return NULL; |
1730 | 1772 | } |
1731 | 1773 | |
1760 | 1802 | // "$-_.+!*'()," |
1761 | 1803 | if (isalnum(* pstr) || * pstr == '$' || * pstr == '-' || * pstr == '_' || |
1762 | 1804 | * pstr == '.' || * pstr == '!' || * pstr == '*' || |
1763 | * pstr == '\'' || * pstr == '(' || * pstr == ')' || * pstr == ',') | |
1805 | * pstr == '\'' || * pstr == '(' || * pstr == ')' || * pstr == ',') | |
1764 | 1806 | * pbuf++ = * pstr; |
1765 | else if (* pstr == ' ') | |
1807 | else if (* pstr == ' ') | |
1766 | 1808 | * pbuf++ = '+'; |
1767 | else | |
1809 | else | |
1768 | 1810 | * pbuf++ = '%', * pbuf++ = to_hex(* pstr >> 4), * pbuf++ = to_hex(* pstr & 15); |
1769 | 1811 | pstr++; |
1770 | 1812 | } |
1796 | 1838 | * pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); |
1797 | 1839 | pstr += 2; |
1798 | 1840 | } |
1799 | } else if (* pstr == '+') { | |
1841 | } else if (* pstr == '+') { | |
1800 | 1842 | * pbuf++ = ' '; |
1801 | 1843 | } else { |
1802 | 1844 | * pbuf++ = * pstr; |
1818 | 1860 | o_malloc_t malloc_fn; |
1819 | 1861 | o_realloc_t realloc_fn; |
1820 | 1862 | o_free_t free_fn; |
1821 | ||
1863 | ||
1822 | 1864 | o_get_alloc_funcs(&malloc_fn, &realloc_fn, &free_fn); |
1823 | 1865 | #ifndef U_DISABLE_CURL |
1824 | 1866 | if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { |
6 | 6 | ULFIUS_LIBRARY=$(ULFIUS_LOCATION)/libulfius.so |
7 | 7 | CC=gcc |
8 | 8 | 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) | |
10 | 10 | # Use this LIBS below if you don't have/need gnutls |
11 | 11 | #LIBS=-lc -lorcania -lyder -lulfius -lcheck -lpthread -lm -lrt -lsubunit -L$(ULFIUS_LOCATION) |
12 | 12 | # Use this LIBS below if you use yder logs |
51 | 51 | memcheck: $(ULFIUS_LIBRARY) u_map core framework websocket |
52 | 52 | -CK_FORK=no LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} $(VALGRIND_COMMAND) ./u_map 2>valgrind-u_map.txt |
53 | 53 | -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 | |
55 | 55 | -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 | 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 | -----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 | -----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----- |
458 | 458 | return U_CALLBACK_CONTINUE; |
459 | 459 | } |
460 | 460 | |
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 | ||
461 | 479 | int via_free_with_test = 0; |
462 | 480 | |
463 | 481 | void free_with_test(void * ptr) { |
998 | 1016 | } |
999 | 1017 | END_TEST |
1000 | 1018 | |
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 | ||
1001 | 1054 | START_TEST(test_ulfius_utf8_not_ignored) |
1002 | 1055 | { |
1003 | 1056 | char * invalid_utf8_seq2 = msprintf("value %c%c", 0xC3, 0x28); |
1330 | 1383 | tcase_add_test(tc_core, test_ulfius_endpoint_injection); |
1331 | 1384 | tcase_add_test(tc_core, test_ulfius_endpoint_multiple); |
1332 | 1385 | tcase_add_test(tc_core, test_ulfius_endpoint_stream); |
1386 | tcase_add_test(tc_core, test_ulfius_endpoint_ignored); | |
1333 | 1387 | tcase_add_test(tc_core, test_ulfius_utf8_not_ignored); |
1334 | 1388 | tcase_add_test(tc_core, test_ulfius_utf8_ignored); |
1335 | 1389 | tcase_add_test(tc_core, test_ulfius_endpoint_callback_position); |
4 | 4 | #include <string.h> |
5 | 5 | #include <errno.h> |
6 | 6 | #include <time.h> |
7 | #include <zlib.h> | |
7 | 8 | |
8 | 9 | #include <check.h> |
9 | 10 | #include <ulfius.h> |
11 | ||
12 | #define MIN(A, B) ((A)>(B)?(B):(A)) | |
10 | 13 | |
11 | 14 | #define WEBSOCKET_URL "http://localhost:8378/websocket" |
12 | 15 | #define DEFAULT_PROTOCOL "proto" |
13 | 16 | #define DEFAULT_EXTENSION "ext" |
14 | 17 | #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 | |
16 | 26 | #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 | |
17 | 38 | |
18 | 39 | #ifndef U_DISABLE_WEBSOCKET |
19 | 40 | void websocket_manager_callback_empty (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { |
34 | 55 | const struct _websocket_message * last_message, |
35 | 56 | void * websocket_incoming_message_user_data) { |
36 | 57 | 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); | |
38 | 59 | } |
39 | 60 | } |
40 | 61 | |
56 | 77 | } |
57 | 78 | } |
58 | 79 | |
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 | ||
59 | 90 | 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) { |
60 | 91 | 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; | |
61 | 248 | } |
62 | 249 | |
63 | 250 | int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data) { |
77 | 264 | return (ret == U_OK)?U_CALLBACK_CONTINUE:U_CALLBACK_ERROR; |
78 | 265 | } |
79 | 266 | |
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) | |
81 | 518 | { |
82 | 519 | struct _u_response response; |
83 | 520 | ulfius_init_response(&response); |
92 | 529 | } |
93 | 530 | END_TEST |
94 | 531 | |
95 | START_TEST(test_websocket_ulfius_set_websocket_request) | |
532 | START_TEST(test_ulfius_websocket_set_websocket_request) | |
96 | 533 | { |
97 | 534 | struct _u_request request; |
98 | 535 | ulfius_init_request(&request); |
108 | 545 | } |
109 | 546 | END_TEST |
110 | 547 | |
111 | START_TEST(test_websocket_ulfius_open_websocket_client_connection_error) | |
548 | START_TEST(test_ulfius_websocket_open_websocket_client_connection_error) | |
112 | 549 | { |
113 | 550 | struct _u_request request; |
114 | 551 | struct _u_response response; |
126 | 563 | } |
127 | 564 | END_TEST |
128 | 565 | |
129 | START_TEST(test_websocket_ulfius_websocket_client) | |
566 | START_TEST(test_ulfius_websocket_client) | |
130 | 567 | { |
131 | 568 | struct _u_instance instance; |
132 | 569 | struct _u_request request; |
133 | 570 | struct _u_response response; |
134 | struct _websocket_client_handler websocket_client_handler; | |
571 | struct _websocket_client_handler websocket_client_handler = {NULL, NULL}; | |
135 | 572 | char url[64], * allocated_data = o_strdup("plop"); |
136 | 573 | |
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); | |
138 | 575 | ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, allocated_data), U_OK); |
139 | 576 | ck_assert_int_eq(ulfius_start_framework(&instance), U_OK); |
140 | 577 | |
141 | 578 | ulfius_init_request(&request); |
142 | 579 | 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); | |
144 | 581 | |
145 | 582 | // Test correct websocket connection on correct websocket service |
146 | 583 | ck_assert_int_eq(ulfius_set_websocket_request(&request, url, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_OK); |
167 | 604 | ulfius_clean_request(&request); |
168 | 605 | ulfius_clean_response(&response); |
169 | 606 | |
607 | usleep(50); | |
170 | 608 | ck_assert_int_eq(ulfius_stop_framework(&instance), U_OK); |
609 | ||
171 | 610 | ulfius_clean_instance(&instance); |
172 | 611 | o_free(allocated_data); |
173 | 612 | } |
174 | 613 | END_TEST |
175 | 614 | |
176 | START_TEST(test_websocket_ulfius_websocket_client_no_onclose) | |
615 | START_TEST(test_ulfius_websocket_client_no_onclose) | |
177 | 616 | { |
178 | 617 | struct _u_instance instance; |
179 | 618 | struct _u_request request; |
180 | 619 | struct _u_response response; |
181 | struct _websocket_client_handler websocket_client_handler; | |
620 | struct _websocket_client_handler websocket_client_handler = {NULL, NULL}; | |
182 | 621 | 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); | |
184 | 623 | ck_assert_int_eq(ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket_onclose, NULL), U_OK); |
185 | 624 | ck_assert_int_eq(ulfius_start_framework(&instance), U_OK); |
186 | 625 | |
187 | 626 | ulfius_init_request(&request); |
188 | 627 | 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); | |
190 | 629 | |
191 | 630 | // Test correct websocket connection on correct websocket service |
192 | 631 | ck_assert_int_eq(ulfius_set_websocket_request(&request, url, DEFAULT_PROTOCOL, DEFAULT_EXTENSION), U_OK); |
213 | 652 | ulfius_clean_request(&request); |
214 | 653 | ulfius_clean_response(&response); |
215 | 654 | |
655 | usleep(50); | |
216 | 656 | 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 | ||
217 | 814 | ulfius_clean_instance(&instance); |
218 | 815 | } |
219 | 816 | END_TEST |
228 | 825 | s = suite_create("Ulfius websocket functions tests"); |
229 | 826 | tc_websocket = tcase_create("test_ulfius_websocket"); |
230 | 827 | #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); | |
236 | 838 | #endif |
237 | 839 | tcase_set_timeout(tc_websocket, 30); |
238 | 840 | suite_add_tcase(s, tc_websocket); |
36 | 36 | Specify the Websocket extensions values, default none |
37 | 37 | -s --non-secure |
38 | 38 | 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 | |
39 | 43 | -v --version |
40 | 44 | Print Glewlwyd's current version |
41 | 45 |
0 | 0 | .\" 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" | |
2 | 2 | .SH NAME |
3 | 3 | uwsc \- Command-line websocket client |
4 | 4 | .SH DESCRIPTION |
5 | 5 | uwsc \- Ulfius Websocket Client |
6 | 6 | .PP |
7 | Version 0.10 | |
7 | Version 0.11 | |
8 | 8 | .SH COPYRIGHT |
9 | 9 | Copyright 2018\-2020 Nicolas Mora <mail@babelouest.org> |
10 | 10 | .PP |
53 | 53 | .IP |
54 | 54 | Do not check server certificate |
55 | 55 | .PP |
56 | \fB\-q\fR \fB\-\-quiet\fR | |
57 | .IP | |
58 | Quiet mode, show only websocket messages | |
59 | .PP | |
56 | 60 | \fB\-v\fR \fB\-\-version\fR |
57 | 61 | .IP |
58 | 62 | Print uwsc's current version |
53 | 53 | } |
54 | 54 | #endif |
55 | 55 | |
56 | #define _UWSC_VERSION_ "0.10" | |
56 | #define _UWSC_VERSION_ "0.11" | |
57 | 57 | |
58 | 58 | #ifndef U_DISABLE_WEBSOCKET |
59 | 59 | |
63 | 63 | char * text_file_send; |
64 | 64 | int non_interactive; |
65 | 65 | int non_listening; |
66 | int quiet; | |
66 | 67 | unsigned int fragmentation; |
67 | 68 | char * protocol; |
68 | 69 | char * extensions; |
103 | 104 | struct _config * config = (struct _config *)websocket_manager_user_data; |
104 | 105 | char * file_content; |
105 | 106 | size_t file_len; |
106 | ||
107 | ||
107 | 108 | if (config->text_file_send != NULL) { |
108 | 109 | file_content = read_file(config->text_file_send, &file_len); |
109 | 110 | if (file_content != NULL && file_len > 0) { |
140 | 141 | if (fgets(message, 256, stdin) != NULL) { |
141 | 142 | if (o_strlen(message)) { |
142 | 143 | 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 | } | |
144 | 147 | if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) { |
145 | 148 | y_log_message(Y_LOG_LEVEL_ERROR, "Error sending close message"); |
146 | 149 | } |
147 | 150 | } 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 | } | |
150 | 155 | if (ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(message)-1, message) != U_OK) { |
151 | 156 | y_log_message(Y_LOG_LEVEL_ERROR, "Error sending message '%.*s'", (int)(o_strlen(message)-1), message); |
152 | 157 | } else { |
165 | 170 | 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) { |
166 | 171 | struct _websocket_message * last_message; |
167 | 172 | 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 | ||
173 | 174 | if (!config->non_listening) { |
174 | 175 | 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 | } | |
177 | 182 | } 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 | } | |
180 | 189 | } |
181 | 190 | } |
182 | 191 | last_message = ulfius_websocket_pop_first_message(websocket_manager->message_list_incoming); |
217 | 226 | fprintf(output, "\tSpecify the Websocket extensions values, default none\n"); |
218 | 227 | fprintf(output, "-s --non-secure\n"); |
219 | 228 | fprintf(output, "\tDo not check server certificate\n"); |
229 | fprintf(output, "-q --quiet\n"); | |
230 | fprintf(output, "\tQuiet mode, show only websocket messages\n"); | |
220 | 231 | fprintf(output, "-v --version\n"); |
221 | 232 | fprintf(output, "\tPrint uwsc's current version\n\n"); |
222 | 233 | fprintf(output, "-h --help\n"); |
230 | 241 | config->text_file_send = NULL; |
231 | 242 | config->non_interactive = 0; |
232 | 243 | config->non_listening = 0; |
244 | config->quiet = 0; | |
233 | 245 | config->fragmentation = 0; |
234 | 246 | config->protocol = NULL; |
235 | 247 | config->extensions = NULL; |
270 | 282 | int main (int argc, char ** argv) { |
271 | 283 | struct _config * config; |
272 | 284 | 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::"; | |
274 | 286 | static const struct option long_options[]= { |
275 | 287 | {"output-log-file", required_argument, NULL, 'o'}, // Sets an output file for logging messages |
276 | 288 | {"add-header", required_argument, NULL, 'x'}, // Add the specified header of the form 'key:value' |
282 | 294 | {"protocol", required_argument, NULL, 'p'}, // Websocket protocol |
283 | 295 | {"extensions", required_argument, NULL, 'e'}, // Websocket extensions |
284 | 296 | {"non-secure", no_argument, NULL, 's'}, // Do not check server certificate |
297 | {"quiet", no_argument, NULL, 'v'}, // Quiet mode | |
285 | 298 | {"version", no_argument, NULL, 'v'}, // Show version |
286 | 299 | {"help", no_argument, NULL, 'h'}, // print help |
287 | 300 | {NULL, 0, NULL, 0} |
288 | 301 | }; |
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 | ||
292 | 305 | config = o_malloc(sizeof(struct _config)); |
293 | 306 | if (config == NULL || !init_config(config)) { |
294 | 307 | fprintf(stderr, "Error initialize configuration\n"); |
299 | 312 | do { |
300 | 313 | char * key, * value; |
301 | 314 | next_option = getopt_long(argc, argv, short_options, long_options, NULL); |
302 | ||
315 | ||
303 | 316 | switch (next_option) { |
304 | 317 | case 'o': |
305 | 318 | config->log_path = o_strdup(optarg); |
347 | 360 | case 's': |
348 | 361 | config->request->check_server_certificate = 0; |
349 | 362 | break; |
363 | case 'q': | |
364 | config->quiet = 1; | |
365 | break; | |
350 | 366 | case 'v': |
351 | 367 | // Print version and exit |
352 | 368 | fprintf(stdout, "%s\n", _UWSC_VERSION_); |
361 | 377 | break; |
362 | 378 | } |
363 | 379 | } while (next_option != -1); |
364 | ||
380 | ||
365 | 381 | if (config->log_path != NULL) { |
366 | 382 | y_init_logs("Ulfius Websocket Client", Y_LOG_MODE_FILE, Y_LOG_LEVEL_DEBUG, config->log_path, "Start uwsc"); |
367 | 383 | } |
373 | 389 | print_help(stderr); |
374 | 390 | exit_program(&config, 1); |
375 | 391 | } |
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 | } | |
384 | 414 | } 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"); | |
387 | 417 | } |
388 | 418 | } else { |
389 | fprintf(stderr, "Error initializing websocket request\n"); | |
419 | fprintf(stderr, "Error ulfius_add_websocket_client_deflate_extension\n"); | |
390 | 420 | y_log_message(Y_LOG_LEVEL_ERROR, "Error initializing websocket request"); |
391 | 421 | } |
392 | ||
422 | o_free(extensions); | |
423 | ||
393 | 424 | if (config->log_path != NULL) { |
394 | 425 | y_close_logs(); |
395 | 426 | } |