Compare commits
	
		
			21 Commits
		
	
	
		
			v10.5.3
			...
			feature/wi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a6199f1009 | ||
|  | 69093953da | ||
|  | 64e88d617b | ||
|  | 5b4ca7f9df | ||
|  | 71bac1ea7d | ||
|  | 9c9e7f3206 | ||
|  | e691ac3704 | ||
|  | 6fcecb84eb | ||
|  | e26cd1faba | ||
|  | 1f659c34bd | ||
|  | 93f16018f7 | ||
|  | a3cfd6810b | ||
|  | f7b87be65b | ||
|  | 739a43988c | ||
|  | 16805759d3 | ||
|  | 88c2e1f6de | ||
|  | 1dc9b559e9 | ||
|  | d31ecfc64e | ||
|  | 4813a40f2a | ||
|  | ea81470f4a | ||
|  | 2a6b1d5f15 | 
							
								
								
									
										49
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | # | ||||||
|  | # cmd_websocket_chat.cpp | ||||||
|  | # Author: Benjamin Sergeant | ||||||
|  | # Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | cmake_minimum_required(VERSION 3.4.1) | ||||||
|  | project(ixwebsocket C CXX) | ||||||
|  |  | ||||||
|  | set (CMAKE_CXX_STANDARD 11) | ||||||
|  | set (CXX_STANDARD_REQUIRED ON) | ||||||
|  | set (CMAKE_CXX_EXTENSIONS OFF) | ||||||
|  |  | ||||||
|  | set( IXWEBSOCKET_SOURCES | ||||||
|  |     ixwebsocket/IXSocket.cpp | ||||||
|  |     ixwebsocket/IXWebSocket.cpp | ||||||
|  |     ixwebsocket/IXWebSocketTransport.cpp | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | set( IXWEBSOCKET_HEADERS | ||||||
|  |     ixwebsocket/IXSocket.h | ||||||
|  |     ixwebsocket/IXWebSocket.h | ||||||
|  |     ixwebsocket/IXWebSocketTransport.h | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | if (USE_TLS) | ||||||
|  |     add_definitions(-DIXWEBSOCKET_USE_TLS) | ||||||
|  |  | ||||||
|  |     if (APPLE) | ||||||
|  |         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketAppleSSL.h) | ||||||
|  |         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketAppleSSL.cpp) | ||||||
|  |     elseif (WIN32) | ||||||
|  |         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketSChannel.h) | ||||||
|  |         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketSChannel.cpp) | ||||||
|  |     else() | ||||||
|  |         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h) | ||||||
|  |         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketOpenSSL.cpp) | ||||||
|  |     endif() | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | add_library( ixwebsocket STATIC | ||||||
|  |     ${IXWEBSOCKET_SOURCES} | ||||||
|  |     ${IXWEBSOCKET_HEADERS} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | set( IXWEBSOCKET_INCLUDE_DIRS | ||||||
|  |     . | ||||||
|  |     ../../shared/OpenSSL/include) | ||||||
|  | target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} ) | ||||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @@ -36,14 +36,18 @@ webSocket.send("hello world"); | |||||||
| // ... finally ... | // ... finally ... | ||||||
|  |  | ||||||
| // Stop the connection | // Stop the connection | ||||||
| webSocket:stop() | webSocket.stop() | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | ## Build | ||||||
|  |  | ||||||
|  | CMakefiles for the library and the examples are available. This library has few dependencies, so it is possible to just add the source files into your project. | ||||||
|  |  | ||||||
| ## Implementation details | ## Implementation details | ||||||
|  |  | ||||||
| ### TLS/SSL | ### TLS/SSL | ||||||
|  |  | ||||||
| Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and OpenSSL is used on Android. | Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and macOS, and OpenSSL is used on Android and Linux. | ||||||
|  |  | ||||||
| ### Polling and background thread work | ### Polling and background thread work | ||||||
|  |  | ||||||
| @@ -65,8 +69,8 @@ If the remote end (server) breaks the connection, the code will try to perpetual | |||||||
| 2. Compile the example C++ code. `sh build.sh` | 2. Compile the example C++ code. `sh build.sh` | ||||||
| 3. Install node.js from [here](https://nodejs.org/en/download/). | 3. Install node.js from [here](https://nodejs.org/en/download/). | ||||||
| 4. Type `npm install` to install the node.js dependencies. Then `node broadcast-server.js` to run the server. | 4. Type `npm install` to install the node.js dependencies. Then `node broadcast-server.js` to run the server. | ||||||
| 5. Bring up a second terminal. `env USER=bob ./cmd_websocket_chat` | 5. Bring up a second terminal. `./cmd_websocket_chat bob` | ||||||
| 6. Bring up a third terminal. `env USER=bill ./cmd_websocket_chat` | 6. Bring up a third terminal. `./cmd_websocket_chat bill` | ||||||
| 7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end. | 7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end. | ||||||
|  |  | ||||||
| ## C++ code organization | ## C++ code organization | ||||||
| @@ -91,7 +95,7 @@ Here's a simplistic diagram which explains how the code is structured in term of | |||||||
| +-----------------------+ | +-----------------------+ | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ## Advanced usage | ## API | ||||||
|  |  | ||||||
| ### Sending messages | ### Sending messages | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								examples/chat/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								examples/chat/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | # | ||||||
|  | # cmd_websocket_chat.cpp | ||||||
|  | # Author: Benjamin Sergeant | ||||||
|  | # Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | cmake_minimum_required (VERSION 3.4.1) | ||||||
|  | project (cmd_websocket_chat) | ||||||
|  |  | ||||||
|  | set (CMAKE_CXX_STANDARD 11) | ||||||
|  |  | ||||||
|  | option(USE_TLS "Add TLS support" ON) | ||||||
|  |  | ||||||
|  | add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket) | ||||||
|  |  | ||||||
|  | add_executable(cmd_websocket_chat cmd_websocket_chat.cpp) | ||||||
|  |  | ||||||
|  | if (APPLE AND USE_TLS) | ||||||
|  |     target_link_libraries(cmd_websocket_chat "-framework foundation" "-framework security") | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | if (WIN32) | ||||||
|  |     target_link_libraries(cmd_websocket_chat wsock32 ws2_32) | ||||||
|  |     add_definitions(-D_CRT_SECURE_NO_WARNINGS) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | target_link_libraries(cmd_websocket_chat ixwebsocket) | ||||||
|  | install(TARGETS cmd_websocket_chat DESTINATION bin) | ||||||
							
								
								
									
										39
									
								
								examples/chat/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								examples/chat/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | # Building | ||||||
|  |  | ||||||
|  | 1. cmake -G . | ||||||
|  | 2. make | ||||||
|  |  | ||||||
|  | ## Disable TLS | ||||||
|  |  | ||||||
|  | chat$ cmake -DUSE_TLS=OFF . | ||||||
|  | -- Configuring done | ||||||
|  | -- Generating done | ||||||
|  | -- Build files have been written to: /Users/bsergeant/src/foss/ixwebsocket/examples/chat | ||||||
|  | chat$ make | ||||||
|  | Scanning dependencies of target ixwebsocket | ||||||
|  | [ 16%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o | ||||||
|  | [ 33%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o | ||||||
|  | [ 50%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o | ||||||
|  | [ 66%] Linking CXX static library libixwebsocket.a | ||||||
|  | [ 66%] Built target ixwebsocket | ||||||
|  | [ 83%] Linking CXX executable cmd_websocket_chat | ||||||
|  | [100%] Built target cmd_websocket_chat | ||||||
|  |  | ||||||
|  | ## Enable TLS (default) | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | chat$ cmake -DUSE_TLS=ON . | ||||||
|  | -- Configuring done | ||||||
|  | -- Generating done | ||||||
|  | -- Build files have been written to: /Users/bsergeant/src/foss/ixwebsocket/examples/chat | ||||||
|  | (venv) chat$ make | ||||||
|  | Scanning dependencies of target ixwebsocket | ||||||
|  | [ 14%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o | ||||||
|  | [ 28%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o | ||||||
|  | [ 42%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o | ||||||
|  | [ 57%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketAppleSSL.cpp.o | ||||||
|  | [ 71%] Linking CXX static library libixwebsocket.a | ||||||
|  | [ 71%] Built target ixwebsocket | ||||||
|  | [ 85%] Linking CXX executable cmd_websocket_chat | ||||||
|  | [100%] Built target cmd_websocket_chat | ||||||
|  | ``` | ||||||
| @@ -4,11 +4,13 @@ | |||||||
| # Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | # Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
|  | # 'manual' way of building. You can also use cmake. | ||||||
|  | 
 | ||||||
| clang++ --std=c++11 --stdlib=libc++ \ | clang++ --std=c++11 --stdlib=libc++ \ | ||||||
|     ../ixwebsocket/IXSocket.cpp	\ |     ../../ixwebsocket/IXSocket.cpp	\ | ||||||
|     ../ixwebsocket/IXWebSocketTransport.cpp \ |     ../../ixwebsocket/IXWebSocketTransport.cpp \ | ||||||
|     ../ixwebsocket/IXSocketAppleSSL.cpp	\ |     ../../ixwebsocket/IXSocketAppleSSL.cpp	\ | ||||||
|     ../ixwebsocket/IXWebSocket.cpp \ |     ../../ixwebsocket/IXWebSocket.cpp \ | ||||||
|     cmd_websocket_chat.cpp \ |     cmd_websocket_chat.cpp \ | ||||||
|     -o cmd_websocket_chat \ |     -o cmd_websocket_chat \ | ||||||
|     -framework Security \ |     -framework Security \ | ||||||
| @@ -12,7 +12,8 @@ | |||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <queue> | #include <queue> | ||||||
| #include "../ixwebsocket/IXWebSocket.h" | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXSocket.h> | ||||||
| 
 | 
 | ||||||
| #include "nlohmann/json.hpp" | #include "nlohmann/json.hpp" | ||||||
| 
 | 
 | ||||||
| @@ -158,13 +159,10 @@ namespace | |||||||
|         _webSocket.send(encodeMessage(text)); |         _webSocket.send(encodeMessage(text)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void interactiveMain() |     void interactiveMain(const std::string& user) | ||||||
|     { |     { | ||||||
|         std::string user(getenv("USER")); |  | ||||||
| 
 |  | ||||||
|         WebSocketChat webSocketChat(user); |  | ||||||
| 
 |  | ||||||
|         std::cout << "Type Ctrl-D to exit prompt..." << std::endl; |         std::cout << "Type Ctrl-D to exit prompt..." << std::endl; | ||||||
|  |         WebSocketChat webSocketChat(user); | ||||||
|         webSocketChat.start(); |         webSocketChat.start(); | ||||||
| 
 | 
 | ||||||
|         while (true) |         while (true) | ||||||
| @@ -186,8 +184,15 @@ namespace | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int main() | int main(int argc, char** argv) | ||||||
| { | { | ||||||
|     interactiveMain(); |     std::string user("user"); | ||||||
|  |     if (argc == 2) | ||||||
|  |     { | ||||||
|  |         user = argv[1]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Socket::init(); | ||||||
|  |     interactiveMain(user); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
							
								
								
									
										1
									
								
								examples/ping_pong/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/ping_pong/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | venv | ||||||
							
								
								
									
										27
									
								
								examples/ping_pong/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								examples/ping_pong/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | # | ||||||
|  | # Author: Benjamin Sergeant | ||||||
|  | # Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | cmake_minimum_required (VERSION 3.4.1) | ||||||
|  | project (ping_pong) | ||||||
|  |  | ||||||
|  | set (CMAKE_CXX_STANDARD 11) | ||||||
|  |  | ||||||
|  | option(USE_TLS "Add TLS support" ON) | ||||||
|  |  | ||||||
|  | add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket) | ||||||
|  |  | ||||||
|  | add_executable(ping_pong ping_pong.cpp) | ||||||
|  |  | ||||||
|  | if (APPLE AND USE_TLS) | ||||||
|  |     target_link_libraries(ping_pong "-framework foundation" "-framework security") | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | if (WIN32) | ||||||
|  |     target_link_libraries(ping_pong wsock32 ws2_32) | ||||||
|  |     add_definitions(-D_CRT_SECURE_NO_WARNINGS) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | target_link_libraries(ping_pong ixwebsocket) | ||||||
|  | install(TARGETS ping_pong DESTINATION bin) | ||||||
							
								
								
									
										17
									
								
								examples/ping_pong/client.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								examples/ping_pong/client.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  |  | ||||||
|  | import asyncio | ||||||
|  | import websockets | ||||||
|  |  | ||||||
|  | async def hello(uri): | ||||||
|  |     async with websockets.connect(uri) as websocket: | ||||||
|  |         await websocket.send("Hello world!") | ||||||
|  |         response = await websocket.recv() | ||||||
|  |         print(response) | ||||||
|  |  | ||||||
|  |         pong_waiter = await websocket.ping('coucou') | ||||||
|  |         ret = await pong_waiter   # only if you want to wait for the pong | ||||||
|  |         print(ret) | ||||||
|  |  | ||||||
|  | asyncio.get_event_loop().run_until_complete( | ||||||
|  |     hello('ws://localhost:5678')) | ||||||
							
								
								
									
										145
									
								
								examples/ping_pong/ping_pong.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								examples/ping_pong/ping_pong.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | |||||||
|  | /* | ||||||
|  |  *  ws_connect.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  | #include <sstream> | ||||||
|  | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXSocket.h> | ||||||
|  |  | ||||||
|  | using namespace ix; | ||||||
|  |  | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  |     void log(const std::string& msg) | ||||||
|  |     { | ||||||
|  |         std::cout << msg << std::endl; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     class WebSocketPingPong | ||||||
|  |     { | ||||||
|  |         public: | ||||||
|  |             WebSocketPingPong(const std::string& _url); | ||||||
|  |  | ||||||
|  |             void subscribe(const std::string& channel); | ||||||
|  |             void start(); | ||||||
|  |             void stop(); | ||||||
|  |  | ||||||
|  |             void ping(const std::string& text); | ||||||
|  |  | ||||||
|  |         private: | ||||||
|  |             std::string _url; | ||||||
|  |             ix::WebSocket _webSocket; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     WebSocketPingPong::WebSocketPingPong(const std::string& url) : | ||||||
|  |         _url(url) | ||||||
|  |     { | ||||||
|  |         ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketPingPong::stop() | ||||||
|  |     { | ||||||
|  |         _webSocket.stop(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketPingPong::start() | ||||||
|  |     { | ||||||
|  |         _webSocket.configure(_url); | ||||||
|  |  | ||||||
|  |         std::stringstream ss; | ||||||
|  |         log(std::string("Connecting to url: ") + _url); | ||||||
|  |  | ||||||
|  |         _webSocket.setOnMessageCallback( | ||||||
|  |             [this](ix::WebSocketMessageType messageType, const std::string& str, ix::WebSocketErrorInfo error) | ||||||
|  |             { | ||||||
|  |                 std::stringstream ss; | ||||||
|  |                 if (messageType == ix::WebSocket_MessageType_Open) | ||||||
|  |                 { | ||||||
|  |                     log("ws_connect: connected"); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Close) | ||||||
|  |                 { | ||||||
|  |                     log("ws_connect: disconnected"); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Message) | ||||||
|  |                 { | ||||||
|  |                     ss << "ws_connect: received message: " | ||||||
|  |                        << str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Ping) | ||||||
|  |                 { | ||||||
|  |                     ss << "ws_connect: received ping message: " | ||||||
|  |                        << str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Pong) | ||||||
|  |                 { | ||||||
|  |                     ss << "ws_connect: received pong message: " | ||||||
|  |                        << str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Error) | ||||||
|  |                 { | ||||||
|  |                     ss << "Connection error: " << error.reason      << std::endl; | ||||||
|  |                     ss << "#retries: "         << error.retries     << std::endl; | ||||||
|  |                     ss << "Wait time(ms): "    << error.wait_time   << std::endl; | ||||||
|  |                     ss << "HTTP Status: "      << error.http_status << std::endl; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     ss << "Invalid ix::WebSocketMessageType"; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         _webSocket.start(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketPingPong::ping(const std::string& text) | ||||||
|  |     { | ||||||
|  |         _webSocket.ping(text); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void interactiveMain(const std::string& url) | ||||||
|  |     { | ||||||
|  |         std::cout << "Type Ctrl-D to exit prompt..." << std::endl; | ||||||
|  |         WebSocketPingPong webSocketPingPong(url); | ||||||
|  |         webSocketPingPong.start(); | ||||||
|  |  | ||||||
|  |         while (true) | ||||||
|  |         { | ||||||
|  |             std::string text; | ||||||
|  |             std::cout << "> " << std::flush; | ||||||
|  |             std::getline(std::cin, text); | ||||||
|  |  | ||||||
|  |             if (!std::cin) | ||||||
|  |             { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             webSocketPingPong.ping(text); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::cout << std::endl; | ||||||
|  |         webSocketPingPong.stop(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char** argv) | ||||||
|  | { | ||||||
|  |     if (argc != 2) | ||||||
|  |     { | ||||||
|  |         std::cerr << "Usage: ping_pong <url>" << std::endl; | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |     std::string url = argv[1]; | ||||||
|  |  | ||||||
|  |     Socket::init(); | ||||||
|  |     interactiveMain(url); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								examples/ping_pong/server.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/ping_pong/server.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  |  | ||||||
|  | import asyncio | ||||||
|  | import websockets | ||||||
|  |  | ||||||
|  | async def echo(websocket, path): | ||||||
|  |     async for message in websocket: | ||||||
|  |         print(message) | ||||||
|  |         await websocket.send(message) | ||||||
|  |  | ||||||
|  | asyncio.get_event_loop().run_until_complete( | ||||||
|  |     websockets.serve(echo, 'localhost', 5678)) | ||||||
|  | asyncio.get_event_loop().run_forever() | ||||||
							
								
								
									
										9
									
								
								examples/ping_pong/test.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								examples/ping_pong/test.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | test -d build || { | ||||||
|  |     mkdir -p build | ||||||
|  |     cd build | ||||||
|  |     cmake .. | ||||||
|  | } | ||||||
|  | (cd build ; make) | ||||||
|  | ./build/ping_pong ws://localhost:5678 | ||||||
							
								
								
									
										27
									
								
								examples/ws_connect/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								examples/ws_connect/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | # | ||||||
|  | # Author: Benjamin Sergeant | ||||||
|  | # Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | cmake_minimum_required (VERSION 3.4.1) | ||||||
|  | project (ws_connect) | ||||||
|  |  | ||||||
|  | set (CMAKE_CXX_STANDARD 11) | ||||||
|  |  | ||||||
|  | option(USE_TLS "Add TLS support" ON) | ||||||
|  |  | ||||||
|  | add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket) | ||||||
|  |  | ||||||
|  | add_executable(ws_connect ws_connect.cpp) | ||||||
|  |  | ||||||
|  | if (APPLE AND USE_TLS) | ||||||
|  |     target_link_libraries(ws_connect "-framework foundation" "-framework security") | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | if (WIN32) | ||||||
|  |     target_link_libraries(ws_connect wsock32 ws2_32) | ||||||
|  |     add_definitions(-D_CRT_SECURE_NO_WARNINGS) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | target_link_libraries(ws_connect ixwebsocket) | ||||||
|  | install(TARGETS ws_connect DESTINATION bin) | ||||||
							
								
								
									
										11
									
								
								examples/ws_connect/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								examples/ws_connect/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | # Building | ||||||
|  |  | ||||||
|  | 1. mkdir build | ||||||
|  | 2. cd build | ||||||
|  | 3. cmake .. | ||||||
|  | 4. make | ||||||
|  |  | ||||||
|  | ## Disable TLS | ||||||
|  |  | ||||||
|  | * Enable: `cmake -DUSE_TLS=OFF ..` | ||||||
|  | * Disable: `cmake -DUSE_TLS=ON ..` | ||||||
							
								
								
									
										133
									
								
								examples/ws_connect/ws_connect.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								examples/ws_connect/ws_connect.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | |||||||
|  | /* | ||||||
|  |  *  ws_connect.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  | #include <sstream> | ||||||
|  | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXSocket.h> | ||||||
|  |  | ||||||
|  | using namespace ix; | ||||||
|  |  | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  |     void log(const std::string& msg) | ||||||
|  |     { | ||||||
|  |         std::cout << msg << std::endl; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     class WebSocketConnect | ||||||
|  |     { | ||||||
|  |         public: | ||||||
|  |             WebSocketConnect(const std::string& _url); | ||||||
|  |  | ||||||
|  |             void subscribe(const std::string& channel); | ||||||
|  |             void start(); | ||||||
|  |             void stop(); | ||||||
|  |  | ||||||
|  |             void sendMessage(const std::string& text); | ||||||
|  |  | ||||||
|  |         private: | ||||||
|  |             std::string _url; | ||||||
|  |             ix::WebSocket _webSocket; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     WebSocketConnect::WebSocketConnect(const std::string& url) : | ||||||
|  |         _url(url) | ||||||
|  |     { | ||||||
|  |         ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketConnect::stop() | ||||||
|  |     { | ||||||
|  |         _webSocket.stop(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketConnect::start() | ||||||
|  |     { | ||||||
|  |         _webSocket.configure(_url); | ||||||
|  |  | ||||||
|  |         std::stringstream ss; | ||||||
|  |         log(std::string("Connecting to url: ") + _url); | ||||||
|  |  | ||||||
|  |         _webSocket.setOnMessageCallback( | ||||||
|  |             [this](ix::WebSocketMessageType messageType, const std::string& str, ix::WebSocketErrorInfo error) | ||||||
|  |             { | ||||||
|  |                 std::stringstream ss; | ||||||
|  |                 if (messageType == ix::WebSocket_MessageType_Open) | ||||||
|  |                 { | ||||||
|  |                     log("ws_connect: connected"); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Close) | ||||||
|  |                 { | ||||||
|  |                     log("ws_connect: disconnected"); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Message) | ||||||
|  |                 { | ||||||
|  |                     ss << "ws_connect: received message: " | ||||||
|  |                        << str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocket_MessageType_Error) | ||||||
|  |                 { | ||||||
|  |                     ss << "Connection error: " << error.reason      << std::endl; | ||||||
|  |                     ss << "#retries: "         << error.retries     << std::endl; | ||||||
|  |                     ss << "Wait time(ms): "    << error.wait_time   << std::endl; | ||||||
|  |                     ss << "HTTP Status: "      << error.http_status << std::endl; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     ss << "Invalid ix::WebSocketMessageType"; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         _webSocket.start(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketConnect::sendMessage(const std::string& text) | ||||||
|  |     { | ||||||
|  |         _webSocket.send(text); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void interactiveMain(const std::string& url) | ||||||
|  |     { | ||||||
|  |         std::cout << "Type Ctrl-D to exit prompt..." << std::endl; | ||||||
|  |         WebSocketConnect webSocketChat(url); | ||||||
|  |         webSocketChat.start(); | ||||||
|  |  | ||||||
|  |         while (true) | ||||||
|  |         { | ||||||
|  |             std::string text; | ||||||
|  |             std::cout << "> " << std::flush; | ||||||
|  |             std::getline(std::cin, text); | ||||||
|  |  | ||||||
|  |             if (!std::cin) | ||||||
|  |             { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             webSocketChat.sendMessage(text); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::cout << std::endl; | ||||||
|  |         webSocketChat.stop(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char** argv) | ||||||
|  | { | ||||||
|  |     if (argc != 2) | ||||||
|  |     { | ||||||
|  |         std::cerr << "Usage: ws_connect <url>" << std::endl; | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |     std::string url = argv[1]; | ||||||
|  |  | ||||||
|  |     Socket::init(); | ||||||
|  |     interactiveMain(url); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
| @@ -6,22 +6,32 @@ | |||||||
|  |  | ||||||
| #include "IXSocket.h" | #include "IXSocket.h" | ||||||
|  |  | ||||||
| #include <netdb.h> | #ifdef _WIN32 | ||||||
| #include <netinet/tcp.h> | # include <basetsd.h> | ||||||
|  | # include <WinSock2.h> | ||||||
|  | # include <ws2def.h> | ||||||
|  | # include <WS2tcpip.h> | ||||||
|  | # include <io.h> | ||||||
|  | #else | ||||||
|  | # include <unistd.h> | ||||||
|  | # include <errno.h> | ||||||
|  | # include <netdb.h> | ||||||
|  | # include <netinet/tcp.h> | ||||||
|  | # include <sys/socket.h> | ||||||
|  | # include <sys/time.h> | ||||||
|  | # include <sys/select.h> | ||||||
|  | # include <sys/stat.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <sys/socket.h> |  | ||||||
| #include <sys/time.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <sys/select.h> |  | ||||||
| #include <errno.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/stat.h> |  | ||||||
| #include <fcntl.h> |  | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  |  | ||||||
| // | // | ||||||
| // Linux/Android has a special type of virtual files. select(2) will react | // Linux/Android has a special type of virtual files. select(2) will react | ||||||
| @@ -35,7 +45,7 @@ | |||||||
| // cf Android/Kernel table here  | // cf Android/Kernel table here  | ||||||
| // https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel | // https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel | ||||||
| // | // | ||||||
| #ifndef __APPLE__ | #ifdef __linux__ | ||||||
| # include <sys/eventfd.h> | # include <sys/eventfd.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -51,7 +61,7 @@ namespace ix | |||||||
|         _sockfd(-1), |         _sockfd(-1), | ||||||
|         _eventfd(-1) |         _eventfd(-1) | ||||||
|     { |     { | ||||||
| #ifndef __APPLE__ | #ifdef __linux__ | ||||||
|         _eventfd = eventfd(0, 0); |         _eventfd = eventfd(0, 0); | ||||||
|         assert(_eventfd != -1 && "Panic - eventfd not functioning on this platform"); |         assert(_eventfd != -1 && "Panic - eventfd not functioning on this platform"); | ||||||
| #endif | #endif | ||||||
| @@ -61,12 +71,12 @@ namespace ix | |||||||
|     { |     { | ||||||
|         close(); |         close(); | ||||||
|  |  | ||||||
| #ifndef __APPLE__ | #ifdef __linux__ | ||||||
|         ::close(_eventfd); |         ::close(_eventfd); | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool connectToAddress(const struct addrinfo *address,  |     bool Socket::connectToAddress(const struct addrinfo *address,  | ||||||
|                                   int& sockfd, |                                   int& sockfd, | ||||||
|                                   std::string& errMsg) |                                   std::string& errMsg) | ||||||
|     { |     { | ||||||
| @@ -84,7 +94,7 @@ namespace ix | |||||||
|         int maxRetries = 3; |         int maxRetries = 3; | ||||||
|         for (int i = 0; i < maxRetries; ++i) |         for (int i = 0; i < maxRetries; ++i) | ||||||
|         { |         { | ||||||
|             if (connect(fd, address->ai_addr, address->ai_addrlen) != -1) |             if (::connect(fd, address->ai_addr, address->ai_addrlen) != -1) | ||||||
|             { |             { | ||||||
|                 sockfd = fd; |                 sockfd = fd; | ||||||
|                 return true; |                 return true; | ||||||
| @@ -94,7 +104,7 @@ namespace ix | |||||||
|             if (errno != EINTR) break; |             if (errno != EINTR) break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ::close(fd); |         closeSocket(fd); | ||||||
|         sockfd = -1; |         sockfd = -1; | ||||||
|         errMsg = strerror(errno); |         errMsg = strerror(errno); | ||||||
|         return false; |         return false; | ||||||
| @@ -142,7 +152,13 @@ namespace ix | |||||||
|     { |     { | ||||||
|         int flag = 1; |         int flag = 1; | ||||||
|         setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm |         setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         unsigned long nonblocking = 1; | ||||||
|  |         ioctlsocket(_sockfd, FIONBIO, &nonblocking); | ||||||
|  | #else | ||||||
|         fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking |         fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef SO_NOSIGPIPE | #ifdef SO_NOSIGPIPE | ||||||
|         int value = 1; |         int value = 1; | ||||||
| @@ -163,12 +179,12 @@ namespace ix | |||||||
|         FD_ZERO(&rfds); |         FD_ZERO(&rfds); | ||||||
|         FD_SET(_sockfd, &rfds); |         FD_SET(_sockfd, &rfds); | ||||||
|  |  | ||||||
| #ifndef __APPLE__ | #ifdef __linux__ | ||||||
|         FD_SET(_eventfd, &rfds); |         FD_SET(_eventfd, &rfds); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|         int sockfd = _sockfd; |         int sockfd = _sockfd; | ||||||
|         int nfds = std::max(sockfd, _eventfd); |         int nfds = (std::max)(sockfd, _eventfd); | ||||||
|         select(nfds + 1, &rfds, nullptr, nullptr, nullptr); |         select(nfds + 1, &rfds, nullptr, nullptr, nullptr); | ||||||
|  |  | ||||||
|         onPollCallback(); |         onPollCallback(); | ||||||
| @@ -191,7 +207,7 @@ namespace ix | |||||||
|     { |     { | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|         wakeUpFromPollApple(); |         wakeUpFromPollApple(); | ||||||
| #else | #elif defined(__linux__) | ||||||
|         wakeUpFromPollLinux(); |         wakeUpFromPollLinux(); | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
| @@ -202,7 +218,7 @@ namespace ix | |||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_socketMutex); |         std::lock_guard<std::mutex> lock(_socketMutex); | ||||||
|  |  | ||||||
| #ifndef __APPLE__ | #ifdef __linux__ | ||||||
|         if (_eventfd == -1) |         if (_eventfd == -1) | ||||||
|         { |         { | ||||||
|             return false; // impossible to use this socket if eventfd is broken |             return false; // impossible to use this socket if eventfd is broken | ||||||
| @@ -219,7 +235,7 @@ namespace ix | |||||||
|  |  | ||||||
|         if (_sockfd == -1) return; |         if (_sockfd == -1) return; | ||||||
|  |  | ||||||
|         ::close(_sockfd); |         closeSocket(_sockfd); | ||||||
|         _sockfd = -1; |         _sockfd = -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -245,7 +261,49 @@ namespace ix | |||||||
|         flags = MSG_NOSIGNAL; |         flags = MSG_NOSIGNAL; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|         return (int) ::recv(_sockfd, buffer, length, flags); |         return (int) ::recv(_sockfd, (char*) buffer, length, flags); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     int Socket::getErrno() const | ||||||
|  |     { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         return WSAGetLastError(); | ||||||
|  | #else | ||||||
|  |         return errno; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Socket::closeSocket(int fd) | ||||||
|  |     { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         closesocket(fd); | ||||||
|  | #else | ||||||
|  |         ::close(fd); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool Socket::init() | ||||||
|  |     { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         INT rc; | ||||||
|  |         WSADATA wsaData; | ||||||
|  |          | ||||||
|  |         rc = WSAStartup(MAKEWORD(2, 2), &wsaData); | ||||||
|  |         return rc != 0; | ||||||
|  | #else | ||||||
|  |         return true; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Socket::cleanup() | ||||||
|  |     { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         WSACleanup(); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Socket::secureSocket() | ||||||
|  |     { | ||||||
|  |         ; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,6 +11,8 @@ | |||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  |  | ||||||
|  | struct addrinfo; | ||||||
|  |  | ||||||
| namespace ix  | namespace ix  | ||||||
| { | { | ||||||
|     class Socket { |     class Socket { | ||||||
| @@ -20,7 +22,7 @@ namespace ix | |||||||
|         Socket(); |         Socket(); | ||||||
|         virtual ~Socket(); |         virtual ~Socket(); | ||||||
|  |  | ||||||
|         static int hostname_connect(const std::string& hostname, |         int hostname_connect(const std::string& hostname, | ||||||
|                              int port, |                              int port, | ||||||
|                              std::string& errMsg); |                              std::string& errMsg); | ||||||
|         void configure(); |         void configure(); | ||||||
| @@ -38,13 +40,26 @@ namespace ix | |||||||
|         virtual int send(const std::string& buffer); |         virtual int send(const std::string& buffer); | ||||||
|         virtual int recv(void* buffer, size_t length); |         virtual int recv(void* buffer, size_t length); | ||||||
|  |  | ||||||
|  |         virtual void secureSocket(); // Windows | ||||||
|  |  | ||||||
|  |         int getErrno() const; | ||||||
|  |         static bool init(); // Required on Windows to initialize WinSocket | ||||||
|  |         static void cleanup(); // Required on Windows to cleanup WinSocket | ||||||
|  |  | ||||||
|     protected: |     protected: | ||||||
|         void wakeUpFromPollApple(); |         void wakeUpFromPollApple(); | ||||||
|         void wakeUpFromPollLinux(); |         void wakeUpFromPollLinux(); | ||||||
|  |  | ||||||
|  |         void closeSocket(int fd); | ||||||
|  |  | ||||||
|         std::atomic<int> _sockfd; |         std::atomic<int> _sockfd; | ||||||
|         int _eventfd; |         int _eventfd; | ||||||
|         std::mutex _socketMutex; |         std::mutex _socketMutex; | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         bool connectToAddress(const struct addrinfo *address,  | ||||||
|  |                               int& sockfd, | ||||||
|  |                               std::string& errMsg); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
|  |  | ||||||
| #include <openssl/x509v3.h> | #include <openssl/x509v3.h> | ||||||
|  |  | ||||||
|  | #include <fnmatch.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #define socketerrno errno | #define socketerrno errno | ||||||
|  |  | ||||||
| @@ -158,51 +159,10 @@ namespace ix | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Check whether a hostname matches a pattern |      * Check whether a hostname matches a pattern | ||||||
|      * |  | ||||||
|      * The pattern MUST contain at most a single, leading asterisk. This means that |  | ||||||
|      * this function cannot serve as a generic validation function, as that would |  | ||||||
|      * allow for partial wildcards, too. Also, this does not check whether the |  | ||||||
|      * wildcard covers multiple levels of labels. For RTM, this suffices, as we |  | ||||||
|      * are only interested in the main domain name. |  | ||||||
|      * |  | ||||||
|      * @param[in] hostname The hostname of the server |  | ||||||
|      * @param[in] pattern The hostname pattern from a SSL certificate |  | ||||||
|      * @return TRUE if the pattern matches, FALSE otherwise |  | ||||||
|      */ |      */ | ||||||
|     bool SocketOpenSSL::checkHost(const std::string& host, const char *pattern) |     bool SocketOpenSSL::checkHost(const std::string& host, const char *pattern) | ||||||
|     { |     { | ||||||
|         const char* hostname = host.c_str(); |         return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; | ||||||
|  |  | ||||||
|         while (*pattern && *hostname) |  | ||||||
|         { |  | ||||||
|             if (*pattern == '*') |  | ||||||
|             { |  | ||||||
|                 while (*hostname != '.' && *hostname) hostname++; |  | ||||||
|                 if (*(++pattern) != '.') |  | ||||||
|                 { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 char p = *pattern; |  | ||||||
|                 char h = *hostname; |  | ||||||
|                 if ((p & ~32) >= 'A' && (p & ~32) <= 'Z') |  | ||||||
|                 { |  | ||||||
|                     p &= ~32; |  | ||||||
|                     h &= ~32; |  | ||||||
|                 } |  | ||||||
|                 if (*pattern != *hostname) |  | ||||||
|                 { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             pattern++; |  | ||||||
|             hostname++; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         bool success = !(*hostname || *pattern); |  | ||||||
|         return success; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl, |     bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl, | ||||||
| @@ -354,6 +314,15 @@ namespace ix | |||||||
|             // SNI support |             // SNI support | ||||||
|             SSL_set_tlsext_host_name(_ssl_connection, host.c_str()); |             SSL_set_tlsext_host_name(_ssl_connection, host.c_str()); | ||||||
|  |  | ||||||
|  | #if OPENSSL_VERSION_NUMBER >= 0x10002000L | ||||||
|  |             // Support for server name verification | ||||||
|  |             // (The docs say that this should work from 1.0.2, and is the default from | ||||||
|  |             // 1.1.0, but it does not. To be on the safe side, the manual test below is | ||||||
|  |             // enabled for all versions prior to 1.1.0.) | ||||||
|  |             X509_VERIFY_PARAM *param = SSL_get0_param(_ssl_connection); | ||||||
|  |             X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|             handshakeSuccessful = openSSLHandshake(host, errMsg); |             handshakeSuccessful = openSSLHandshake(host, errMsg); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										108
									
								
								ixwebsocket/IXSocketSChannel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								ixwebsocket/IXSocketSChannel.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | /* | ||||||
|  |  *  IXSocketSChannel.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||||
|  |  * | ||||||
|  |  *  See https://docs.microsoft.com/en-us/windows/desktop/WinSock/using-secure-socket-extensions | ||||||
|  |  * | ||||||
|  |  *  https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/netds/winsock/securesocket/stcpclient/tcpclient.c | ||||||
|  |  * | ||||||
|  |  *  This is the right example to look at: | ||||||
|  |  *  https://www.codeproject.com/Articles/1000189/A-Working-TCP-Client-and-Server-With-SSL | ||||||
|  |  */ | ||||||
|  | #include "IXSocketSChannel.h" | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | # include <basetsd.h> | ||||||
|  | # include <WinSock2.h> | ||||||
|  | # include <ws2def.h> | ||||||
|  | # include <WS2tcpip.h> | ||||||
|  | # include <schannel.h> | ||||||
|  | # include <sslsock.h> | ||||||
|  | # include <io.h> | ||||||
|  |  | ||||||
|  | #define WIN32_LEAN_AND_MEAN | ||||||
|  |  | ||||||
|  | #ifndef UNICODE | ||||||
|  | #define UNICODE | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <windows.h> | ||||||
|  | #include <winsock2.h> | ||||||
|  | #include <mstcpip.h> | ||||||
|  | #include <ws2tcpip.h> | ||||||
|  | #include <rpc.h> | ||||||
|  | #include <ntdsapi.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <tchar.h> | ||||||
|  |  | ||||||
|  | #define RECV_DATA_BUF_SIZE 256 | ||||||
|  |  | ||||||
|  | // Link with ws2_32.lib | ||||||
|  | #pragma comment(lib, "Ws2_32.lib") | ||||||
|  |  | ||||||
|  | // link with fwpuclnt.lib for Winsock secure socket extensions | ||||||
|  | #pragma comment(lib, "fwpuclnt.lib") | ||||||
|  |  | ||||||
|  | // link with ntdsapi.lib for DsMakeSpn function | ||||||
|  | #pragma comment(lib, "ntdsapi.lib") | ||||||
|  |  | ||||||
|  | // The following function assumes that Winsock  | ||||||
|  | // has already been initialized | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #else | ||||||
|  | # error("This file should only be built on Windows") | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace ix  | ||||||
|  | { | ||||||
|  |     SocketSChannel::SocketSChannel() | ||||||
|  |     { | ||||||
|  |         ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SocketSChannel::~SocketSChannel() | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SocketSChannel::connect(const std::string& host, | ||||||
|  |                                  int port, | ||||||
|  |                                  std::string& errMsg) | ||||||
|  |     { | ||||||
|  |         return Socket::connect(host, port, errMsg); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |      | ||||||
|  |     void SocketSChannel::secureSocket() | ||||||
|  |     { | ||||||
|  |         // there will be a lot to do here ... | ||||||
|  |         // FIXME do something with sockerror | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void SocketSChannel::close() | ||||||
|  |     { | ||||||
|  |         Socket::close(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int SocketSChannel::send(char* buf, size_t nbyte) | ||||||
|  |     { | ||||||
|  |         return Socket::send(buf, nbyte); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int SocketSChannel::send(const std::string& buffer) | ||||||
|  |     { | ||||||
|  |         return Socket::send(buffer); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int SocketSChannel::recv(void* buf, size_t nbyte) | ||||||
|  |     { | ||||||
|  |         return Socket::recv(buf, nbyte); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								ixwebsocket/IXSocketSChannel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								ixwebsocket/IXSocketSChannel.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | /* | ||||||
|  |  *  IXSocketSChannel.h | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "IXSocket.h" | ||||||
|  |  | ||||||
|  | namespace ix  | ||||||
|  | { | ||||||
|  |     class SocketSChannel : public Socket  | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         SocketSChannel(); | ||||||
|  |         ~SocketSChannel(); | ||||||
|  |  | ||||||
|  |         virtual bool connect(const std::string& host,  | ||||||
|  |                              int port, | ||||||
|  |                              std::string& errMsg) final; | ||||||
|  |         virtual void close() final; | ||||||
|  |  | ||||||
|  |         // The important override | ||||||
|  |         virtual void secureSocket() final; | ||||||
|  |  | ||||||
|  |         virtual int send(char* buffer, size_t length) final; | ||||||
|  |         virtual int send(const std::string& buffer) final; | ||||||
|  |         virtual int recv(void* buffer, size_t length) final; | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -177,9 +177,29 @@ namespace ix { | |||||||
|  |  | ||||||
|             // 3. Dispatch the incoming messages |             // 3. Dispatch the incoming messages | ||||||
|             _ws.dispatch( |             _ws.dispatch( | ||||||
|                 [this](const std::string& msg) |                 [this](const std::string& msg, | ||||||
|  |                        WebSocketTransport::MessageKind messageKind) | ||||||
|                 { |                 { | ||||||
|                     _onMessageCallback(WebSocket_MessageType_Message, msg, WebSocketErrorInfo()); |                     WebSocketMessageType webSocketMessageType; | ||||||
|  |                     switch (messageKind) | ||||||
|  |                     { | ||||||
|  |                         case WebSocketTransport::MSG: | ||||||
|  |                         { | ||||||
|  |                             webSocketMessageType = WebSocket_MessageType_Message; | ||||||
|  |                         } break; | ||||||
|  |  | ||||||
|  |                         case WebSocketTransport::PING: | ||||||
|  |                         { | ||||||
|  |                             webSocketMessageType = WebSocket_MessageType_Ping; | ||||||
|  |                         } break; | ||||||
|  |  | ||||||
|  |                         case WebSocketTransport::PONG: | ||||||
|  |                         { | ||||||
|  |                             webSocketMessageType = WebSocket_MessageType_Pong; | ||||||
|  |                         } break; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     _onMessageCallback(webSocketMessageType, msg, WebSocketErrorInfo()); | ||||||
|  |  | ||||||
|                     WebSocket::invokeTrafficTrackerCallback(msg.size(), true); |                     WebSocket::invokeTrafficTrackerCallback(msg.size(), true); | ||||||
|                 }); |                 }); | ||||||
| @@ -210,6 +230,20 @@ namespace ix { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocket::send(const std::string& text) |     bool WebSocket::send(const std::string& text) | ||||||
|  |     { | ||||||
|  |         return sendMessage(text, false); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool WebSocket::ping(const std::string& text) | ||||||
|  |     { | ||||||
|  |         // Standard limit ping message size | ||||||
|  |         constexpr size_t pingMaxPayloadSize = 125; | ||||||
|  |         if (text.size() > pingMaxPayloadSize) return false; | ||||||
|  |  | ||||||
|  |         return sendMessage(text, true); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool WebSocket::sendMessage(const std::string& text, bool ping) | ||||||
|     { |     { | ||||||
|         if (!isConnected()) return false; |         if (!isConnected()) return false; | ||||||
|  |  | ||||||
| @@ -223,7 +257,15 @@ namespace ix { | |||||||
|         // incoming messages are arriving / there's data to be received. |         // incoming messages are arriving / there's data to be received. | ||||||
|         // |         // | ||||||
|         std::lock_guard<std::mutex> lock(_writeMutex); |         std::lock_guard<std::mutex> lock(_writeMutex); | ||||||
|  |  | ||||||
|  |         if (ping) | ||||||
|  |         { | ||||||
|  |             _ws.sendPing(text); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|             _ws.sendBinary(text); |             _ws.sendBinary(text); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         WebSocket::invokeTrafficTrackerCallback(text.size(), false); |         WebSocket::invokeTrafficTrackerCallback(text.size(), false); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,7 +32,9 @@ namespace ix | |||||||
|         WebSocket_MessageType_Message = 0, |         WebSocket_MessageType_Message = 0, | ||||||
|         WebSocket_MessageType_Open = 1, |         WebSocket_MessageType_Open = 1, | ||||||
|         WebSocket_MessageType_Close = 2, |         WebSocket_MessageType_Close = 2, | ||||||
|         WebSocket_MessageType_Error = 3 |         WebSocket_MessageType_Error = 3, | ||||||
|  |         WebSocket_MessageType_Ping = 4, | ||||||
|  |         WebSocket_MessageType_Pong = 5 | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     struct WebSocketErrorInfo |     struct WebSocketErrorInfo | ||||||
| @@ -56,6 +58,7 @@ namespace ix | |||||||
|         void start(); |         void start(); | ||||||
|         void stop(); |         void stop(); | ||||||
|         bool send(const std::string& text); |         bool send(const std::string& text); | ||||||
|  |         bool ping(const std::string& text); | ||||||
|         void close(); |         void close(); | ||||||
|  |  | ||||||
|         void setOnMessageCallback(const OnMessageCallback& callback); |         void setOnMessageCallback(const OnMessageCallback& callback); | ||||||
| @@ -70,6 +73,8 @@ namespace ix | |||||||
|     private: |     private: | ||||||
|         void run(); |         void run(); | ||||||
|  |  | ||||||
|  |         bool sendMessage(const std::string& text, bool ping); | ||||||
|  |  | ||||||
|         WebSocketInitResult connect(); |         WebSocketInitResult connect(); | ||||||
|         bool isConnected() const; |         bool isConnected() const; | ||||||
|         bool isClosing() const; |         bool isClosing() const; | ||||||
| @@ -90,7 +95,5 @@ namespace ix | |||||||
|         std::atomic<bool> _automaticReconnection; |         std::atomic<bool> _automaticReconnection; | ||||||
|         std::thread _thread; |         std::thread _thread; | ||||||
|         std::mutex _writeMutex; |         std::mutex _writeMutex; | ||||||
|  |  | ||||||
|         static int kHeartBeatPeriod; |  | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,15 +11,18 @@ | |||||||
| #include "IXWebSocketTransport.h" | #include "IXWebSocketTransport.h" | ||||||
|  |  | ||||||
| #include "IXSocket.h" | #include "IXSocket.h" | ||||||
| #ifdef __APPLE__ | #ifdef IXWEBSOCKET_USE_TLS | ||||||
|  | # ifdef __APPLE__ | ||||||
| #  include "IXSocketAppleSSL.h" | #  include "IXSocketAppleSSL.h" | ||||||
| #else | # elif defined(__linux__) | ||||||
| #  include "IXSocketOpenSSL.h" | #  include "IXSocketOpenSSL.h" | ||||||
|  | # elif defined(_WIN32) | ||||||
|  | #  include "IXSocketSChannel.h" | ||||||
|  | # endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <errno.h> |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <vector> | #include <vector> | ||||||
| @@ -140,10 +143,16 @@ namespace ix { | |||||||
|         if (protocol == "wss") |         if (protocol == "wss") | ||||||
|         { |         { | ||||||
|             _socket.reset(); |             _socket.reset(); | ||||||
| #ifdef __APPLE__ | #ifdef IXWEBSOCKET_USE_TLS | ||||||
|  | # ifdef __APPLE__ | ||||||
|              _socket = std::make_shared<SocketAppleSSL>(); |              _socket = std::make_shared<SocketAppleSSL>(); | ||||||
| #else | # elif defined(__linux__) | ||||||
|              _socket = std::make_shared<SocketOpenSSL>(); |              _socket = std::make_shared<SocketOpenSSL>(); | ||||||
|  | # elif defined(_WIN32) | ||||||
|  |              _socket = std::make_shared<SocketSChannel>(); | ||||||
|  | # endif | ||||||
|  | #else | ||||||
|  |             return WebSocketInitResult(false, 0, "TLS is not supported."); | ||||||
| #endif | #endif | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
| @@ -267,12 +276,12 @@ namespace ix { | |||||||
|                 { |                 { | ||||||
|                     int N = (int) _rxbuf.size(); |                     int N = (int) _rxbuf.size(); | ||||||
|  |  | ||||||
|                     ssize_t ret; |                     int ret; | ||||||
|                     _rxbuf.resize(N + 1500); |                     _rxbuf.resize(N + 1500); | ||||||
|                     ret = _socket->recv((char*)&_rxbuf[0] + N, 1500); |                     ret = _socket->recv((char*)&_rxbuf[0] + N, 1500); | ||||||
|  |  | ||||||
|                     if (ret < 0 && (errno == EWOULDBLOCK ||  |                     if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||  | ||||||
|                                     errno == EAGAIN)) { |                                     _socket->getErrno() == EAGAIN)) { | ||||||
|                         _rxbuf.resize(N); |                         _rxbuf.resize(N); | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
| @@ -443,7 +452,7 @@ namespace ix { | |||||||
|                     // fire callback with a string message |                     // fire callback with a string message | ||||||
|                     std::string stringMessage(_receivedData.begin(), |                     std::string stringMessage(_receivedData.begin(), | ||||||
|                                               _receivedData.end()); |                                               _receivedData.end()); | ||||||
|                     onMessageCallback(stringMessage); |                     onMessageCallback(stringMessage, MSG); | ||||||
|  |  | ||||||
|                     _receivedData.clear(); |                     _receivedData.clear(); | ||||||
|                 } |                 } | ||||||
| @@ -461,10 +470,27 @@ namespace ix { | |||||||
|                 std::string pingData(_rxbuf.begin()+ws.header_size, |                 std::string pingData(_rxbuf.begin()+ws.header_size, | ||||||
|                                      _rxbuf.begin()+ws.header_size + (size_t) ws.N); |                                      _rxbuf.begin()+ws.header_size + (size_t) ws.N); | ||||||
|  |  | ||||||
|  |                 // Reply back right away | ||||||
|                 sendData(wsheader_type::PONG, pingData.size(), |                 sendData(wsheader_type::PONG, pingData.size(), | ||||||
|                          pingData.begin(), pingData.end()); |                          pingData.begin(), pingData.end()); | ||||||
|  |  | ||||||
|  |                 onMessageCallback(pingData, PING); | ||||||
|  |             } | ||||||
|  |             else if (ws.opcode == wsheader_type::PONG) | ||||||
|  |             { | ||||||
|  |                 if (ws.mask) | ||||||
|  |                 { | ||||||
|  |                     for (size_t j = 0; j != ws.N; ++j) | ||||||
|  |                     { | ||||||
|  |                         _rxbuf[j+ws.header_size] ^= ws.masking_key[j&0x3]; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 std::string pongData(_rxbuf.begin()+ws.header_size, | ||||||
|  |                                      _rxbuf.begin()+ws.header_size + (size_t) ws.N); | ||||||
|  |  | ||||||
|  |                 onMessageCallback(pongData, PONG); | ||||||
|             } |             } | ||||||
|             else if (ws.opcode == wsheader_type::PONG) { } |  | ||||||
|             else if (ws.opcode == wsheader_type::CLOSE) { close(); } |             else if (ws.opcode == wsheader_type::CLOSE) { close(); } | ||||||
|             else { close(); } |             else { close(); } | ||||||
|  |  | ||||||
| @@ -550,10 +576,9 @@ namespace ix { | |||||||
|         sendOnSocket(); |         sendOnSocket(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void WebSocketTransport::sendPing() |     void WebSocketTransport::sendPing(const std::string& message) | ||||||
|     { |     { | ||||||
|         std::string empty; |         sendData(wsheader_type::PING, message.size(), message.begin(), message.end()); | ||||||
|         sendData(wsheader_type::PING, empty.size(), empty.begin(), empty.end()); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void WebSocketTransport::sendBinary(const std::string& message)  |     void WebSocketTransport::sendBinary(const std::string& message)  | ||||||
| @@ -569,8 +594,8 @@ namespace ix { | |||||||
|         { |         { | ||||||
|             int ret = _socket->send((char*)&_txbuf[0], _txbuf.size()); |             int ret = _socket->send((char*)&_txbuf[0], _txbuf.size()); | ||||||
|  |  | ||||||
|             if (ret < 0 && (errno == EWOULDBLOCK ||  |             if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||  | ||||||
|                             errno == EAGAIN)) |                             _socket->getErrno() == EAGAIN)) | ||||||
|             { |             { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -54,7 +54,15 @@ namespace ix | |||||||
|             OPEN |             OPEN | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         using OnMessageCallback = std::function<void(const std::string&)>; |         enum MessageKind | ||||||
|  |         { | ||||||
|  |             MSG, | ||||||
|  |             PING, | ||||||
|  |             PONG | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         using OnMessageCallback = std::function<void(const std::string&, | ||||||
|  |                                                      MessageKind)>; | ||||||
|         using OnStateChangeCallback = std::function<void(ReadyStateValues)>; |         using OnStateChangeCallback = std::function<void(ReadyStateValues)>; | ||||||
|  |  | ||||||
|         WebSocketTransport(); |         WebSocketTransport(); | ||||||
| @@ -67,7 +75,7 @@ namespace ix | |||||||
|         void send(const std::string& message); |         void send(const std::string& message); | ||||||
|         void sendBinary(const std::string& message); |         void sendBinary(const std::string& message); | ||||||
|         void sendBinary(const std::vector<uint8_t>& message); |         void sendBinary(const std::vector<uint8_t>& message); | ||||||
|         void sendPing(); |         void sendPing(const std::string& message); | ||||||
|         void close(); |         void close(); | ||||||
|         ReadyStateValues getReadyState() const; |         ReadyStateValues getReadyState() const; | ||||||
|         void setReadyState(ReadyStateValues readyStateValue); |         void setReadyState(ReadyStateValues readyStateValue); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user