Compare commits
	
		
			46 Commits
		
	
	
		
			v1.0.0
			...
			feature/ht
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					73f4ba7ee7 | ||
| 
						 | 
					6f39592c7b | ||
| 
						 | 
					38200fc5d7 | ||
| 
						 | 
					285c12775a | ||
| 
						 | 
					6d56f7223a | ||
| 
						 | 
					1db3568375 | ||
| 
						 | 
					0a752e7d18 | ||
| 
						 | 
					7c2bc2cf7e | ||
| 
						 | 
					79f601ac65 | ||
| 
						 | 
					069eccf415 | ||
| 
						 | 
					b563541b14 | ||
| 
						 | 
					3bcd6f97a6 | ||
| 
						 | 
					c04bc3cdfc | ||
| 
						 | 
					846f0c680a | ||
| 
						 | 
					c552a03ef0 | ||
| 
						 | 
					0f175143c9 | ||
| 
						 | 
					85569cb401 | ||
| 
						 | 
					bd854553d4 | ||
| 
						 | 
					38c57e1ed2 | ||
| 
						 | 
					26cc5025fb | ||
| 
						 | 
					806cf39efc | ||
| 
						 | 
					daaa7ec704 | ||
| 
						 | 
					3cffc6f9a5 | ||
| 
						 | 
					f8b1a03ee6 | ||
| 
						 | 
					a7ff3c41a1 | ||
| 
						 | 
					78dbba5521 | ||
| 
						 | 
					b211bdbe38 | ||
| 
						 | 
					a0a53ab986 | ||
| 
						 | 
					8d819053ff | ||
| 
						 | 
					e20ddc2a08 | ||
| 
						 | 
					c415ba9427 | ||
| 
						 | 
					e881b82511 | ||
| 
						 | 
					d5551e5d68 | ||
| 
						 | 
					e8583000b8 | ||
| 
						 | 
					d642ef1a89 | ||
| 
						 | 
					2df118022d | ||
| 
						 | 
					95457c8f4c | ||
| 
						 | 
					0a45b7787f | ||
| 
						 | 
					b8c397e180 | ||
| 
						 | 
					90105fa2b3 | ||
| 
						 | 
					24859fef8a | ||
| 
						 | 
					73d7280723 | ||
| 
						 | 
					262de49c3c | ||
| 
						 | 
					0b7c3ec235 | ||
| 
						 | 
					29c96f287f | ||
| 
						 | 
					2a17cad1bf | 
							
								
								
									
										1
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					build
 | 
				
			||||||
							
								
								
									
										1
									
								
								examples/ping_pong/.gitignore → .gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								examples/ping_pong/.gitignore → .gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1 @@
 | 
				
			|||||||
venv
 | 
					 | 
				
			||||||
build
 | 
					build
 | 
				
			||||||
@@ -10,6 +10,7 @@ set (CMAKE_CXX_STANDARD 14)
 | 
				
			|||||||
set (CXX_STANDARD_REQUIRED ON)
 | 
					set (CXX_STANDARD_REQUIRED ON)
 | 
				
			||||||
set (CMAKE_CXX_EXTENSIONS OFF)
 | 
					set (CMAKE_CXX_EXTENSIONS OFF)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -Wshorten-64-to-32 does not work with clang
 | 
				
			||||||
if (NOT WIN32)
 | 
					if (NOT WIN32)
 | 
				
			||||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 | 
					  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
@@ -19,6 +20,7 @@ set( IXWEBSOCKET_SOURCES
 | 
				
			|||||||
    ixwebsocket/IXSocket.cpp
 | 
					    ixwebsocket/IXSocket.cpp
 | 
				
			||||||
    ixwebsocket/IXSocketServer.cpp
 | 
					    ixwebsocket/IXSocketServer.cpp
 | 
				
			||||||
    ixwebsocket/IXSocketConnect.cpp
 | 
					    ixwebsocket/IXSocketConnect.cpp
 | 
				
			||||||
 | 
					    ixwebsocket/IXSocketFactory.cpp
 | 
				
			||||||
    ixwebsocket/IXDNSLookup.cpp
 | 
					    ixwebsocket/IXDNSLookup.cpp
 | 
				
			||||||
    ixwebsocket/IXCancellationRequest.cpp
 | 
					    ixwebsocket/IXCancellationRequest.cpp
 | 
				
			||||||
    ixwebsocket/IXWebSocket.cpp
 | 
					    ixwebsocket/IXWebSocket.cpp
 | 
				
			||||||
@@ -28,6 +30,9 @@ set( IXWEBSOCKET_SOURCES
 | 
				
			|||||||
    ixwebsocket/IXWebSocketPerMessageDeflate.cpp
 | 
					    ixwebsocket/IXWebSocketPerMessageDeflate.cpp
 | 
				
			||||||
    ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
 | 
					    ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
 | 
				
			||||||
    ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
 | 
					    ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
 | 
				
			||||||
 | 
					    ixwebsocket/IXWebSocketHttpHeaders.cpp
 | 
				
			||||||
 | 
					    ixwebsocket/IXHttpClient.cpp
 | 
				
			||||||
 | 
					    ixwebsocket/IXUrlParser.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set( IXWEBSOCKET_HEADERS
 | 
					set( IXWEBSOCKET_HEADERS
 | 
				
			||||||
@@ -35,6 +40,7 @@ set( IXWEBSOCKET_HEADERS
 | 
				
			|||||||
    ixwebsocket/IXSocket.h
 | 
					    ixwebsocket/IXSocket.h
 | 
				
			||||||
    ixwebsocket/IXSocketServer.h
 | 
					    ixwebsocket/IXSocketServer.h
 | 
				
			||||||
    ixwebsocket/IXSocketConnect.h
 | 
					    ixwebsocket/IXSocketConnect.h
 | 
				
			||||||
 | 
					    ixwebsocket/IXSocketFactory.h
 | 
				
			||||||
    ixwebsocket/IXSetThreadName.h
 | 
					    ixwebsocket/IXSetThreadName.h
 | 
				
			||||||
    ixwebsocket/IXDNSLookup.h
 | 
					    ixwebsocket/IXDNSLookup.h
 | 
				
			||||||
    ixwebsocket/IXCancellationRequest.h
 | 
					    ixwebsocket/IXCancellationRequest.h
 | 
				
			||||||
@@ -50,6 +56,8 @@ set( IXWEBSOCKET_HEADERS
 | 
				
			|||||||
    ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
 | 
					    ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
 | 
				
			||||||
    ixwebsocket/IXWebSocketHttpHeaders.h
 | 
					    ixwebsocket/IXWebSocketHttpHeaders.h
 | 
				
			||||||
    ixwebsocket/libwshandshake.hpp
 | 
					    ixwebsocket/libwshandshake.hpp
 | 
				
			||||||
 | 
					    ixwebsocket/IXHttpClient.h
 | 
				
			||||||
 | 
					    ixwebsocket/IXUrlParser.h
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Platform specific code
 | 
					# Platform specific code
 | 
				
			||||||
@@ -113,3 +121,5 @@ set( IXWEBSOCKET_INCLUDE_DIRS
 | 
				
			|||||||
    .
 | 
					    .
 | 
				
			||||||
    ../../shared/OpenSSL/include)
 | 
					    ../../shared/OpenSSL/include)
 | 
				
			||||||
target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} )
 | 
					target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_subdirectory(ws)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										91
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								README.md
									
									
									
									
									
								
							@@ -5,7 +5,7 @@
 | 
				
			|||||||
## Introduction
 | 
					## Introduction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex
 | 
					[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex
 | 
				
			||||||
communication channels over a single TCP connection. *IXWebSocket* is a C++ library for client and server Websocket communication. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient) and from the [Satori C SDK](https://github.com/satori-com/satori-rtm-sdk-c). It has been tested on the following platforms.
 | 
					communication channels over a single TCP connection. *IXWebSocket* is a C++ library for client and server Websocket communication, and for client HTTP communication. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient) and from the [Satori C SDK](https://github.com/satori-com/satori-rtm-sdk-c). It has been tested on the following platforms.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* macOS
 | 
					* macOS
 | 
				
			||||||
* iOS
 | 
					* iOS
 | 
				
			||||||
@@ -15,7 +15,7 @@ communication channels over a single TCP connection. *IXWebSocket* is a C++ libr
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Examples
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The examples folder countains a simple chat program, using a node.js broadcast server.
 | 
					The [*ws*](https://github.com/machinezone/IXWebSocket/tree/master/ws) folder countains many interactive programs for chat, [file transfers](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_send.cpp), [curl like](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_http_client.cpp) http clients, demonstrating client and server usage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here is what the client API looks like.
 | 
					Here is what the client API looks like.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,7 +25,7 @@ ix::WebSocket webSocket;
 | 
				
			|||||||
std::string url("ws://localhost:8080/");
 | 
					std::string url("ws://localhost:8080/");
 | 
				
			||||||
webSocket.setUrl(url);
 | 
					webSocket.setUrl(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Optional heart beat, sent every 45 seconds when there isn't any traffic
 | 
					// Optional heart beat, sent every 45 seconds when there is not any traffic
 | 
				
			||||||
// to make sure that load balancers do not kill an idle connection.
 | 
					// to make sure that load balancers do not kill an idle connection.
 | 
				
			||||||
webSocket.setHeartBeatPeriod(45);
 | 
					webSocket.setHeartBeatPeriod(45);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -110,6 +110,68 @@ server.wait();
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Here is what the HTTP client API looks like. Note that HTTP client support is very recent and subject to changes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Preparation
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					HttpClient httpClient;
 | 
				
			||||||
 | 
					HttpRequestArgs args;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Custom headers can be set
 | 
				
			||||||
 | 
					WebSocketHttpHeaders headers;
 | 
				
			||||||
 | 
					headers["Foo"] = "bar";
 | 
				
			||||||
 | 
					args.extraHeaders = parseHeaders(headersData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Timeout options
 | 
				
			||||||
 | 
					args.connectTimeout = connectTimeout;
 | 
				
			||||||
 | 
					args.transferTimeout = transferTimeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Redirect options
 | 
				
			||||||
 | 
					args.followRedirects = followRedirects;
 | 
				
			||||||
 | 
					args.maxRedirects = maxRedirects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Misc
 | 
				
			||||||
 | 
					args.compress = compress; // Enable gzip compression
 | 
				
			||||||
 | 
					args.verbose = verbose;
 | 
				
			||||||
 | 
					args.logger = [](const std::string& msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::cout << msg;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Request
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					HttpResponse out;
 | 
				
			||||||
 | 
					std::string url = "https://www.google.com";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HEAD request
 | 
				
			||||||
 | 
					out = httpClient.head(url, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GET request
 | 
				
			||||||
 | 
					out = httpClient.get(url, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// POST request with parameters
 | 
				
			||||||
 | 
					HttpParameters httpParameters;
 | 
				
			||||||
 | 
					httpParameters["foo"] = "bar";
 | 
				
			||||||
 | 
					out = httpClient.post(url, httpParameters, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// POST request with a body
 | 
				
			||||||
 | 
					out = httpClient.post(url, std::string("foo=bar"), args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Result
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					auto statusCode = std::get<0>(out);
 | 
				
			||||||
 | 
					auto errorCode = std::get<1>(out);
 | 
				
			||||||
 | 
					auto responseHeaders = std::get<2>(out);
 | 
				
			||||||
 | 
					auto payload = std::get<3>(out);
 | 
				
			||||||
 | 
					auto errorMsg = std::get<4>(out);
 | 
				
			||||||
 | 
					auto uploadSize = std::get<5>(out);
 | 
				
			||||||
 | 
					auto downloadSize = std::get<6>(out);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Build
 | 
					## 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.
 | 
					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.
 | 
				
			||||||
@@ -134,26 +196,19 @@ No manual polling to fetch data is required. Data is sent and received instantly
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds.
 | 
					If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Large messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Large frames are broken up into smaller chunks or messages to avoid filling up the os tcp buffers, which is permitted thanks to WebSocket [fragmentation](https://tools.ietf.org/html/rfc6455#section-5.4). Messages up to 500M were sent and received succesfully.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Limitations
 | 
					## Limitations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Sending large messages are not supported yet (see feature/send_large_message). This is a bug and will be fixed.
 | 
					 | 
				
			||||||
* There is no text support for sending data, only the binary protocol is supported. Sending json or text over the binary protocol works well.
 | 
					* There is no text support for sending data, only the binary protocol is supported. Sending json or text over the binary protocol works well.
 | 
				
			||||||
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
 | 
					* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
 | 
				
			||||||
* The server code is using select to detect incoming data, and creates one OS thread per connection. This isn't as scalable as strategies using epoll or kqueue.
 | 
					* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.
 | 
				
			||||||
 | 
					 | 
				
			||||||
## Examples
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1. Bring up a terminal and jump to the examples folder.
 | 
					 | 
				
			||||||
2. Compile the example C++ code. `sh build.sh`
 | 
					 | 
				
			||||||
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.
 | 
					 | 
				
			||||||
5. Bring up a second terminal. `./cmd_websocket_chat bob`
 | 
					 | 
				
			||||||
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.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## C++ code organization
 | 
					## C++ code organization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here's a simplistic diagram which explains how the code is structured in term of class/modules.
 | 
					Here is a simplistic diagram which explains how the code is structured in term of class/modules.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
+-----------------------+ --- Public
 | 
					+-----------------------+ --- Public
 | 
				
			||||||
@@ -205,7 +260,7 @@ If the connection was closed and sending failed, the return value will be set to
 | 
				
			|||||||
1. WebSocket_ReadyState_Connecting - The connection is not yet open.
 | 
					1. WebSocket_ReadyState_Connecting - The connection is not yet open.
 | 
				
			||||||
2. WebSocket_ReadyState_Open       - The connection is open and ready to communicate.
 | 
					2. WebSocket_ReadyState_Open       - The connection is open and ready to communicate.
 | 
				
			||||||
3. WebSocket_ReadyState_Closing    - The connection is in the process of closing.
 | 
					3. WebSocket_ReadyState_Closing    - The connection is in the process of closing.
 | 
				
			||||||
4. WebSocket_MessageType_Close     - The connection is closed or couldn't be opened.
 | 
					4. WebSocket_MessageType_Close     - The connection is closed or could not be opened.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Open and Close notifications
 | 
					### Open and Close notifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -315,7 +370,7 @@ websocket.ping("ping data, optional (empty string is ok): limited to 125 bytes l
 | 
				
			|||||||
### Heartbeat.
 | 
					### Heartbeat.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can configure an optional heart beat / keep-alive, sent every 45 seconds
 | 
					You can configure an optional heart beat / keep-alive, sent every 45 seconds
 | 
				
			||||||
when there isn't any traffic to make sure that load balancers do not kill an
 | 
					when there is not any traffic to make sure that load balancers do not kill an
 | 
				
			||||||
idle connection.
 | 
					idle connection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,5 +15,8 @@ RUN apt-get -y install cmake
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
COPY . .
 | 
					COPY . .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WORKDIR test
 | 
					WORKDIR ws
 | 
				
			||||||
RUN ["sh", "build_linux.sh"]
 | 
					RUN ["sh", "docker_build.sh"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXPOSE 8765
 | 
				
			||||||
 | 
					CMD ["/ws/ws", "transfer", "8765"]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								examples/broadcast_server/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								examples/broadcast_server/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,9 +0,0 @@
 | 
				
			|||||||
CMakeCache.txt
 | 
					 | 
				
			||||||
package-lock.json
 | 
					 | 
				
			||||||
CMakeFiles		
 | 
					 | 
				
			||||||
ixwebsocket_unittest	
 | 
					 | 
				
			||||||
cmake_install.cmake	
 | 
					 | 
				
			||||||
node_modules
 | 
					 | 
				
			||||||
ixwebsocket
 | 
					 | 
				
			||||||
Makefile
 | 
					 | 
				
			||||||
build
 | 
					 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
#
 | 
					 | 
				
			||||||
# Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cmake_minimum_required (VERSION 3.4.1)
 | 
					 | 
				
			||||||
project (broadcast_server)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# There's -Weverything too for clang
 | 
					 | 
				
			||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
set (OPENSSL_PREFIX /usr/local/opt/openssl) # Homebrew openssl
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
set (CMAKE_CXX_STANDARD 14)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
option(USE_TLS "Add TLS support" ON)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
include_directories(broadcast_server .)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
add_executable(broadcast_server 
 | 
					 | 
				
			||||||
  broadcast_server.cpp)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if (APPLE AND USE_TLS)
 | 
					 | 
				
			||||||
    target_link_libraries(broadcast_server "-framework foundation" "-framework security")
 | 
					 | 
				
			||||||
endif()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
target_link_libraries(broadcast_server ixwebsocket)
 | 
					 | 
				
			||||||
install(TARGETS broadcast_server DESTINATION bin)
 | 
					 | 
				
			||||||
@@ -1,74 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 *  broadcast_server.cpp
 | 
					 | 
				
			||||||
 *  Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <iostream>
 | 
					 | 
				
			||||||
#include <sstream>
 | 
					 | 
				
			||||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char** argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    int port = 8080;
 | 
					 | 
				
			||||||
    if (argc == 2)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::stringstream ss;
 | 
					 | 
				
			||||||
        ss << argv[1];
 | 
					 | 
				
			||||||
        ss >> port;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ix::WebSocketServer server(port);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    server.setOnConnectionCallback(
 | 
					 | 
				
			||||||
        [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            webSocket->setOnMessageCallback(
 | 
					 | 
				
			||||||
                [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
					 | 
				
			||||||
                   const std::string& str,
 | 
					 | 
				
			||||||
                   size_t wireSize,
 | 
					 | 
				
			||||||
                   const ix::WebSocketErrorInfo& error,
 | 
					 | 
				
			||||||
                   const ix::WebSocketOpenInfo& openInfo,
 | 
					 | 
				
			||||||
                   const ix::WebSocketCloseInfo& closeInfo)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (messageType == ix::WebSocket_MessageType_Open)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        std::cerr << "New connection" << std::endl;
 | 
					 | 
				
			||||||
                        std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
					 | 
				
			||||||
                        std::cerr << "Headers:" << std::endl;
 | 
					 | 
				
			||||||
                        for (auto it : openInfo.headers)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            std::cerr << it.first << ": " << it.second << std::endl;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (messageType == ix::WebSocket_MessageType_Close)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        std::cerr << "Closed connection" << std::endl;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (messageType == ix::WebSocket_MessageType_Message)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        for (auto&& client : server.getClients())
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            if (client != webSocket)
 | 
					 | 
				
			||||||
                            {
 | 
					 | 
				
			||||||
                                client->send(str);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto res = server.listen();
 | 
					 | 
				
			||||||
    if (!res.first)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::cerr << res.second << std::endl;
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    server.start();
 | 
					 | 
				
			||||||
    server.wait();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										3
									
								
								examples/chat/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								examples/chat/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +0,0 @@
 | 
				
			|||||||
build
 | 
					 | 
				
			||||||
venv
 | 
					 | 
				
			||||||
node_modules
 | 
					 | 
				
			||||||
@@ -1,23 +0,0 @@
 | 
				
			|||||||
#
 | 
					 | 
				
			||||||
# 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 14)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
target_link_libraries(cmd_websocket_chat ixwebsocket)
 | 
					 | 
				
			||||||
install(TARGETS cmd_websocket_chat DESTINATION bin)
 | 
					 | 
				
			||||||
@@ -1,39 +0,0 @@
 | 
				
			|||||||
# 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
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
#!/bin/sh
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 'manual' way of building. You can also use cmake.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
g++ --std=c++11 \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSocket.cpp	\
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocketTransport.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocket.cpp \
 | 
					 | 
				
			||||||
    -I ../.. \
 | 
					 | 
				
			||||||
    cmd_websocket_chat.cpp \
 | 
					 | 
				
			||||||
    -o cmd_websocket_chat
 | 
					 | 
				
			||||||
@@ -1,17 +0,0 @@
 | 
				
			|||||||
#!/bin/sh
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
# 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++ \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSocket.cpp	\
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocketTransport.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSocketAppleSSL.cpp	\
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocket.cpp \
 | 
					 | 
				
			||||||
    cmd_websocket_chat.cpp \
 | 
					 | 
				
			||||||
    -o cmd_websocket_chat \
 | 
					 | 
				
			||||||
    -framework Security \
 | 
					 | 
				
			||||||
    -framework Foundation
 | 
					 | 
				
			||||||
							
								
								
									
										31
									
								
								examples/chat/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										31
									
								
								examples/chat/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,31 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "requires": true,
 | 
					 | 
				
			||||||
  "lockfileVersion": 1,
 | 
					 | 
				
			||||||
  "dependencies": {
 | 
					 | 
				
			||||||
    "async-limiter": {
 | 
					 | 
				
			||||||
      "version": "1.0.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "safe-buffer": {
 | 
					 | 
				
			||||||
      "version": "5.1.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "ultron": {
 | 
					 | 
				
			||||||
      "version": "1.1.1",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "ws": {
 | 
					 | 
				
			||||||
      "version": "3.3.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
 | 
					 | 
				
			||||||
      "requires": {
 | 
					 | 
				
			||||||
        "async-limiter": "1.0.0",
 | 
					 | 
				
			||||||
        "safe-buffer": "5.1.2",
 | 
					 | 
				
			||||||
        "ultron": "1.1.1"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,6 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "dependencies": {
 | 
					 | 
				
			||||||
    "msgpack-js": "^0.3.0",
 | 
					 | 
				
			||||||
    "ws": "^3.3.3"
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
#
 | 
					 | 
				
			||||||
# Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cmake_minimum_required (VERSION 3.4.1)
 | 
					 | 
				
			||||||
project (echo_server)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# There's -Weverything too for clang
 | 
					 | 
				
			||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
set (OPENSSL_PREFIX /usr/local/opt/openssl) # Homebrew openssl
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
set (CMAKE_CXX_STANDARD 14)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
option(USE_TLS "Add TLS support" ON)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
include_directories(echo_server .)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
add_executable(echo_server 
 | 
					 | 
				
			||||||
  echo_server.cpp)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if (APPLE AND USE_TLS)
 | 
					 | 
				
			||||||
    target_link_libraries(echo_server "-framework foundation" "-framework security")
 | 
					 | 
				
			||||||
endif()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
target_link_libraries(echo_server ixwebsocket)
 | 
					 | 
				
			||||||
install(TARGETS echo_server DESTINATION bin)
 | 
					 | 
				
			||||||
@@ -1,68 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 *  echo_server.cpp
 | 
					 | 
				
			||||||
 *  Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <iostream>
 | 
					 | 
				
			||||||
#include <sstream>
 | 
					 | 
				
			||||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char** argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    int port = 8080;
 | 
					 | 
				
			||||||
    if (argc == 2)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::stringstream ss;
 | 
					 | 
				
			||||||
        ss << argv[1];
 | 
					 | 
				
			||||||
        ss >> port;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ix::WebSocketServer server(port);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    server.setOnConnectionCallback(
 | 
					 | 
				
			||||||
        [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            webSocket->setOnMessageCallback(
 | 
					 | 
				
			||||||
                [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
					 | 
				
			||||||
                   const std::string& str,
 | 
					 | 
				
			||||||
                   size_t wireSize,
 | 
					 | 
				
			||||||
                   const ix::WebSocketErrorInfo& error,
 | 
					 | 
				
			||||||
                   const ix::WebSocketOpenInfo& openInfo,
 | 
					 | 
				
			||||||
                   const ix::WebSocketCloseInfo& closeInfo)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (messageType == ix::WebSocket_MessageType_Open)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        std::cerr << "New connection" << std::endl;
 | 
					 | 
				
			||||||
                        std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
					 | 
				
			||||||
                        std::cerr << "Headers:" << std::endl;
 | 
					 | 
				
			||||||
                        for (auto it : openInfo.headers)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            std::cerr << it.first << ": " << it.second << std::endl;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (messageType == ix::WebSocket_MessageType_Close)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        std::cerr << "Closed connection" << std::endl;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (messageType == ix::WebSocket_MessageType_Message)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        webSocket->send(str);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto res = server.listen();
 | 
					 | 
				
			||||||
    if (!res.first)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::cerr << res.second << std::endl;
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    server.start();
 | 
					 | 
				
			||||||
    server.wait();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,27 +0,0 @@
 | 
				
			|||||||
#
 | 
					 | 
				
			||||||
# 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 14)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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)
 | 
					 | 
				
			||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
#!/bin/sh
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 'manual' way of building. You can also use cmake.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
g++ --std=c++11 \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSocket.cpp	\
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocketTransport.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocket.cpp \
 | 
					 | 
				
			||||||
    -I ../.. \
 | 
					 | 
				
			||||||
    cmd_websocket_chat.cpp \
 | 
					 | 
				
			||||||
    -o cmd_websocket_chat
 | 
					 | 
				
			||||||
@@ -1,17 +0,0 @@
 | 
				
			|||||||
#!/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'))
 | 
					 | 
				
			||||||
@@ -1,21 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import asyncio
 | 
					 | 
				
			||||||
import websockets
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async def echo(websocket, path):
 | 
					 | 
				
			||||||
    async for message in websocket:
 | 
					 | 
				
			||||||
        print(message)
 | 
					 | 
				
			||||||
        await websocket.send(message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if os.getenv('TEST_CLOSE'):
 | 
					 | 
				
			||||||
            print('Closing')
 | 
					 | 
				
			||||||
            # breakpoint()
 | 
					 | 
				
			||||||
            await websocket.close(1001, 'close message')
 | 
					 | 
				
			||||||
            # await websocket.close()
 | 
					 | 
				
			||||||
            break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
asyncio.get_event_loop().run_until_complete(
 | 
					 | 
				
			||||||
    websockets.serve(echo, 'localhost', 5678))
 | 
					 | 
				
			||||||
asyncio.get_event_loop().run_forever()
 | 
					 | 
				
			||||||
@@ -1,9 +0,0 @@
 | 
				
			|||||||
#!/bin/sh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test -d build || {
 | 
					 | 
				
			||||||
    mkdir -p build
 | 
					 | 
				
			||||||
    cd build
 | 
					 | 
				
			||||||
    cmake ..
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
(cd build ; make)
 | 
					 | 
				
			||||||
./build/ping_pong ws://localhost:5678
 | 
					 | 
				
			||||||
							
								
								
									
										3
									
								
								examples/ws_connect/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								examples/ws_connect/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +0,0 @@
 | 
				
			|||||||
build
 | 
					 | 
				
			||||||
venv
 | 
					 | 
				
			||||||
node_modules
 | 
					 | 
				
			||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
#
 | 
					 | 
				
			||||||
# 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 14)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
target_link_libraries(ws_connect ixwebsocket)
 | 
					 | 
				
			||||||
install(TARGETS ws_connect DESTINATION bin)
 | 
					 | 
				
			||||||
@@ -1,11 +0,0 @@
 | 
				
			|||||||
# Building
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1. mkdir build
 | 
					 | 
				
			||||||
2. cd build
 | 
					 | 
				
			||||||
3. cmake ..
 | 
					 | 
				
			||||||
4. make
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Disable TLS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* Enable: `cmake -DUSE_TLS=OFF ..`
 | 
					 | 
				
			||||||
* Disable: `cmake -DUSE_TLS=ON ..`
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
#!/bin/sh
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Author: Benjamin Sergeant
 | 
					 | 
				
			||||||
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 'manual' way of building. You can also use cmake.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
g++ --std=c++11 \
 | 
					 | 
				
			||||||
    -DIXWEBSOCKET_USE_TLS \
 | 
					 | 
				
			||||||
    -g \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXEventFd.cpp	\
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSocket.cpp	\
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSetThreadName.cpp	\
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocketTransport.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocket.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXDNSLookup.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSocketConnect.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXSocketOpenSSL.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
 | 
					 | 
				
			||||||
    ../../ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp \
 | 
					 | 
				
			||||||
    -I ../.. \
 | 
					 | 
				
			||||||
    ws_connect.cpp \
 | 
					 | 
				
			||||||
    -o ws_connect \
 | 
					 | 
				
			||||||
    -lcrypto -lssl -lz -lpthread
 | 
					 | 
				
			||||||
							
								
								
									
										471
									
								
								ixwebsocket/IXHttpClient.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										471
									
								
								ixwebsocket/IXHttpClient.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,471 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXHttpClient.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXHttpClient.h"
 | 
				
			||||||
 | 
					#include "IXUrlParser.h"
 | 
				
			||||||
 | 
					#include "IXWebSocketHttpHeaders.h"
 | 
				
			||||||
 | 
					#include "IXSocketFactory.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <zlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const std::string HttpClient::kPost = "POST";
 | 
				
			||||||
 | 
					    const std::string HttpClient::kGet = "GET";
 | 
				
			||||||
 | 
					    const std::string HttpClient::kHead = "HEAD";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpClient::HttpClient()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpClient::~HttpClient()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpResponse HttpClient::request(
 | 
				
			||||||
 | 
					        const std::string& url,
 | 
				
			||||||
 | 
					        const std::string& verb,
 | 
				
			||||||
 | 
					        const std::string& body,
 | 
				
			||||||
 | 
					        const HttpRequestArgs& args,
 | 
				
			||||||
 | 
					        int redirects)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        uint64_t uploadSize = 0;
 | 
				
			||||||
 | 
					        uint64_t downloadSize = 0;
 | 
				
			||||||
 | 
					        int code = 0;
 | 
				
			||||||
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
 | 
					        std::string payload;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string protocol, host, path, query;
 | 
				
			||||||
 | 
					        int port;
 | 
				
			||||||
 | 
					        bool websocket = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::stringstream ss;
 | 
				
			||||||
 | 
					            ss << "Cannot parse url: " << url;
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_UrlMalformed,
 | 
				
			||||||
 | 
					                                   headers, payload, ss.str(),
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool tls = protocol == "https";
 | 
				
			||||||
 | 
					        std::string errorMsg;
 | 
				
			||||||
 | 
					        _socket = createSocket(tls, errorMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!_socket)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_CannotCreateSocket,
 | 
				
			||||||
 | 
					                                   headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Build request string
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        ss << verb << " " << path << " HTTP/1.1\r\n";
 | 
				
			||||||
 | 
					        ss << "Host: " << host << "\r\n";
 | 
				
			||||||
 | 
					        ss << "User-Agent: ixwebsocket/1.0.0" << "\r\n";
 | 
				
			||||||
 | 
					        ss << "Accept: */*" << "\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (args.compress)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << "Accept-Encoding: gzip" << "\r\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Append extra headers
 | 
				
			||||||
 | 
					        for (auto&& it : args.extraHeaders)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << it.first << ": " << it.second << "\r\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (verb == kPost)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << "Content-Length: " << body.size() << "\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set default Content-Type if unspecified
 | 
				
			||||||
 | 
					            if (args.extraHeaders.find("Content-Type") == args.extraHeaders.end())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ss << "\r\n";
 | 
				
			||||||
 | 
					            ss << body;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << "\r\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string req(ss.str());
 | 
				
			||||||
 | 
					        std::string errMsg;
 | 
				
			||||||
 | 
					        std::atomic<bool> requestInitCancellation(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Make a cancellation object dealing with connection timeout
 | 
				
			||||||
 | 
					        auto isCancellationRequested =
 | 
				
			||||||
 | 
					            makeCancellationRequestWithTimeout(args.connectTimeout, requestInitCancellation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
 | 
				
			||||||
 | 
					        if (!success)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::stringstream ss;
 | 
				
			||||||
 | 
					            ss << "Cannot connect to url: " << url;
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_CannotConnect,
 | 
				
			||||||
 | 
					                                   headers, payload, ss.str(),
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Make a new cancellation object dealing with transfer timeout
 | 
				
			||||||
 | 
					        isCancellationRequested =
 | 
				
			||||||
 | 
					            makeCancellationRequestWithTimeout(args.transferTimeout, requestInitCancellation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (args.verbose)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::stringstream ss;
 | 
				
			||||||
 | 
					            ss << "Sending " << verb << " request "
 | 
				
			||||||
 | 
					               << "to " << host << ":" << port << std::endl
 | 
				
			||||||
 | 
					               << "request size: " << req.size() << " bytes" << std::endl
 | 
				
			||||||
 | 
					               << "=============" << std::endl
 | 
				
			||||||
 | 
					               << req
 | 
				
			||||||
 | 
					               << "=============" << std::endl
 | 
				
			||||||
 | 
					               << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            log(ss.str(), args);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!_socket->writeBytes(req, isCancellationRequested))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string errorMsg("Cannot send request");
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_SendError,
 | 
				
			||||||
 | 
					                                   headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        uploadSize = req.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto lineResult = _socket->readLine(isCancellationRequested);
 | 
				
			||||||
 | 
					        auto lineValid = lineResult.first;
 | 
				
			||||||
 | 
					        auto line = lineResult.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!lineValid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string errorMsg("Cannot retrieve status line");
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_CannotReadStatusLine,
 | 
				
			||||||
 | 
					                                   headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (args.verbose)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::stringstream ss;
 | 
				
			||||||
 | 
					            ss << "Status line " << line;
 | 
				
			||||||
 | 
					            log(ss.str(), args);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string errorMsg("Cannot parse response code from status line");
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_MissingStatus,
 | 
				
			||||||
 | 
					                                   headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto result = parseHttpHeaders(_socket, isCancellationRequested);
 | 
				
			||||||
 | 
					        auto headersValid = result.first;
 | 
				
			||||||
 | 
					        headers = result.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!headersValid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string errorMsg("Cannot parse http headers");
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_HeaderParsingError,
 | 
				
			||||||
 | 
					                                   headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Redirect ?
 | 
				
			||||||
 | 
					        if ((code >= 301 && code <= 308) && args.followRedirects)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (headers.find("Location") == headers.end())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::string errorMsg("Missing location header for redirect");
 | 
				
			||||||
 | 
					                return std::make_tuple(code, HttpErrorCode_MissingLocation,
 | 
				
			||||||
 | 
					                                       headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                       uploadSize, downloadSize);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (redirects >= args.maxRedirects)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::stringstream ss;
 | 
				
			||||||
 | 
					                ss << "Too many redirects: " << redirects;
 | 
				
			||||||
 | 
					                return std::make_tuple(code, HttpErrorCode_TooManyRedirects,
 | 
				
			||||||
 | 
					                                       headers, payload, ss.str(),
 | 
				
			||||||
 | 
					                                       uploadSize, downloadSize);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Recurse
 | 
				
			||||||
 | 
					            std::string location = headers["Location"];
 | 
				
			||||||
 | 
					            return request(location, verb, body, args, redirects+1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (verb == "HEAD")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_Ok,
 | 
				
			||||||
 | 
					                                   headers, payload, std::string(),
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Parse response:
 | 
				
			||||||
 | 
					        if (headers.find("Content-Length") != headers.end())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ssize_t contentLength = -1;
 | 
				
			||||||
 | 
					            ss.str("");
 | 
				
			||||||
 | 
					            ss << headers["Content-Length"];
 | 
				
			||||||
 | 
					            ss >> contentLength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            payload.reserve(contentLength);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // FIXME: very inefficient way to read bytes, but it works...
 | 
				
			||||||
 | 
					            for (int i = 0; i < contentLength; ++i)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                char c;
 | 
				
			||||||
 | 
					                if (!_socket->readByte(&c, isCancellationRequested))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return std::make_tuple(code, HttpErrorCode_ReadError,
 | 
				
			||||||
 | 
					                                           headers, payload, "Cannot read byte",
 | 
				
			||||||
 | 
					                                           uploadSize, downloadSize);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                payload += c;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (headers.find("Transfer-Encoding") != headers.end() &&
 | 
				
			||||||
 | 
					                 headers["Transfer-Encoding"] == "chunked")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::stringstream ss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            while (true)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                lineResult = _socket->readLine(isCancellationRequested);
 | 
				
			||||||
 | 
					                line = lineResult.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!lineResult.first)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return std::make_tuple(code, HttpErrorCode_ChunkReadError,
 | 
				
			||||||
 | 
					                                           headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                           uploadSize, downloadSize);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                uint64_t chunkSize;
 | 
				
			||||||
 | 
					                ss.str("");
 | 
				
			||||||
 | 
					                ss << std::hex << line;
 | 
				
			||||||
 | 
					                ss >> chunkSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (args.verbose)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    std::stringstream oss;
 | 
				
			||||||
 | 
					                    oss << "Reading " << chunkSize << " bytes"
 | 
				
			||||||
 | 
					                        << std::endl;
 | 
				
			||||||
 | 
					                    log(oss.str(), args);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                payload.reserve(payload.size() + chunkSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Read another line
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (uint64_t i = 0; i < chunkSize; ++i)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    char c;
 | 
				
			||||||
 | 
					                    if (!_socket->readByte(&c, isCancellationRequested))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        errorMsg = "Cannot read byte";
 | 
				
			||||||
 | 
					                        return std::make_tuple(code, HttpErrorCode_ChunkReadError,
 | 
				
			||||||
 | 
					                                               headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                               uploadSize, downloadSize);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    payload += c;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                lineResult = _socket->readLine(isCancellationRequested);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!lineResult.first)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return std::make_tuple(code, HttpErrorCode_ChunkReadError,
 | 
				
			||||||
 | 
					                                           headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                           uploadSize, downloadSize);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (chunkSize == 0) break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (code == 204)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ; // 204 is NoContent response code
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string errorMsg("Cannot read http body");
 | 
				
			||||||
 | 
					            return std::make_tuple(code, HttpErrorCode_CannotReadBody,
 | 
				
			||||||
 | 
					                                   headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                   uploadSize, downloadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        downloadSize = payload.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If the content was compressed with gzip, decode it
 | 
				
			||||||
 | 
					        if (headers["Content-Encoding"] == "gzip")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string decompressedPayload;
 | 
				
			||||||
 | 
					            if (!gzipInflate(payload, decompressedPayload))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::string errorMsg("Error decompressing payload");
 | 
				
			||||||
 | 
					                return std::make_tuple(code, HttpErrorCode_Gzip,
 | 
				
			||||||
 | 
					                                       headers, payload, errorMsg,
 | 
				
			||||||
 | 
					                                       uploadSize, downloadSize);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            payload = decompressedPayload;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return std::make_tuple(code, HttpErrorCode_Ok,
 | 
				
			||||||
 | 
					                               headers, payload, std::string(),
 | 
				
			||||||
 | 
					                               uploadSize, downloadSize);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpResponse HttpClient::get(const std::string& url,
 | 
				
			||||||
 | 
					                                 const HttpRequestArgs& args)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return request(url, kGet, std::string(), args);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpResponse HttpClient::head(const std::string& url,
 | 
				
			||||||
 | 
					                                  const HttpRequestArgs& args)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return request(url, kHead, std::string(), args);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpResponse HttpClient::post(const std::string& url,
 | 
				
			||||||
 | 
					                                  const HttpParameters& httpParameters,
 | 
				
			||||||
 | 
					                                  const HttpRequestArgs& args)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return request(url, kPost, serializeHttpParameters(httpParameters), args);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpResponse HttpClient::post(const std::string& url,
 | 
				
			||||||
 | 
					                                  const std::string& body,
 | 
				
			||||||
 | 
					                                  const HttpRequestArgs& args)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return request(url, kPost, body, args);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string HttpClient::urlEncode(const std::string& value)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::ostringstream escaped;
 | 
				
			||||||
 | 
					        escaped.fill('0');
 | 
				
			||||||
 | 
					        escaped << std::hex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (std::string::const_iterator i = value.begin(), n = value.end();
 | 
				
			||||||
 | 
					             i != n; ++i)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string::value_type c = (*i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Keep alphanumeric and other accepted characters intact
 | 
				
			||||||
 | 
					            if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                escaped << c;
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Any other characters are percent-encoded
 | 
				
			||||||
 | 
					            escaped << std::uppercase;
 | 
				
			||||||
 | 
					            escaped << '%' << std::setw(2) << int((unsigned char) c);
 | 
				
			||||||
 | 
					            escaped << std::nouppercase;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return escaped.str();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string HttpClient::serializeHttpParameters(const HttpParameters& httpParameters)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        size_t count = httpParameters.size();
 | 
				
			||||||
 | 
					        size_t i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (auto&& it : httpParameters)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ss << urlEncode(it.first)
 | 
				
			||||||
 | 
					               << "="
 | 
				
			||||||
 | 
					               << urlEncode(it.second);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (i++ < (count-1))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					               ss << "&";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ss.str();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool HttpClient::gzipInflate(
 | 
				
			||||||
 | 
					        const std::string& in,
 | 
				
			||||||
 | 
					        std::string& out)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        z_stream inflateState;
 | 
				
			||||||
 | 
					        std::memset(&inflateState, 0, sizeof(inflateState));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inflateState.zalloc = Z_NULL;
 | 
				
			||||||
 | 
					        inflateState.zfree = Z_NULL;
 | 
				
			||||||
 | 
					        inflateState.opaque = Z_NULL;
 | 
				
			||||||
 | 
					        inflateState.avail_in = 0;
 | 
				
			||||||
 | 
					        inflateState.next_in = Z_NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (inflateInit2(&inflateState, 16+MAX_WBITS) != Z_OK)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inflateState.avail_in = (uInt) in.size();
 | 
				
			||||||
 | 
					        inflateState.next_in = (unsigned char *)(const_cast<char *>(in.data()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const int kBufferSize = 1 << 14;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::unique_ptr<unsigned char[]> compressBuffer =
 | 
				
			||||||
 | 
					            std::make_unique<unsigned char[]>(kBufferSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            inflateState.avail_out = (uInt) kBufferSize;
 | 
				
			||||||
 | 
					            inflateState.next_out = compressBuffer.get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int ret = inflate(&inflateState, Z_SYNC_FLUSH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                inflateEnd(&inflateState);
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            out.append(
 | 
				
			||||||
 | 
					                reinterpret_cast<char *>(compressBuffer.get()),
 | 
				
			||||||
 | 
					                kBufferSize - inflateState.avail_out
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        } while (inflateState.avail_out == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inflateEnd(&inflateState);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void HttpClient::log(const std::string& msg,
 | 
				
			||||||
 | 
					                         const HttpRequestArgs& args)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (args.logger)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            args.logger(msg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										106
									
								
								ixwebsocket/IXHttpClient.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								ixwebsocket/IXHttpClient.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXHttpClient.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include <tuple>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXSocket.h"
 | 
				
			||||||
 | 
					#include "IXWebSocketHttpHeaders.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    enum HttpErrorCode
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        HttpErrorCode_Ok = 0,
 | 
				
			||||||
 | 
					        HttpErrorCode_CannotConnect = 1,
 | 
				
			||||||
 | 
					        HttpErrorCode_Timeout = 2,
 | 
				
			||||||
 | 
					        HttpErrorCode_Gzip = 3,
 | 
				
			||||||
 | 
					        HttpErrorCode_UrlMalformed = 4,
 | 
				
			||||||
 | 
					        HttpErrorCode_CannotCreateSocket = 5,
 | 
				
			||||||
 | 
					        HttpErrorCode_SendError = 6,
 | 
				
			||||||
 | 
					        HttpErrorCode_ReadError = 7,
 | 
				
			||||||
 | 
					        HttpErrorCode_CannotReadStatusLine = 8,
 | 
				
			||||||
 | 
					        HttpErrorCode_MissingStatus = 9,
 | 
				
			||||||
 | 
					        HttpErrorCode_HeaderParsingError = 10,
 | 
				
			||||||
 | 
					        HttpErrorCode_MissingLocation = 11,
 | 
				
			||||||
 | 
					        HttpErrorCode_TooManyRedirects = 12,
 | 
				
			||||||
 | 
					        HttpErrorCode_ChunkReadError = 13,
 | 
				
			||||||
 | 
					        HttpErrorCode_CannotReadBody = 14
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using HttpResponse = std::tuple<int, // status
 | 
				
			||||||
 | 
					                                    HttpErrorCode, // error code
 | 
				
			||||||
 | 
					                                    WebSocketHttpHeaders,
 | 
				
			||||||
 | 
					                                    std::string, // payload
 | 
				
			||||||
 | 
					                                    std::string, // error msg
 | 
				
			||||||
 | 
					                                    uint64_t,    // upload size
 | 
				
			||||||
 | 
					                                    uint64_t>;   // download size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using HttpParameters = std::map<std::string, std::string>;
 | 
				
			||||||
 | 
					    using Logger = std::function<void(const std::string&)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct HttpRequestArgs
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::string url;
 | 
				
			||||||
 | 
					        WebSocketHttpHeaders extraHeaders;
 | 
				
			||||||
 | 
					        std::string body;
 | 
				
			||||||
 | 
					        int connectTimeout;
 | 
				
			||||||
 | 
					        int transferTimeout;
 | 
				
			||||||
 | 
					        bool followRedirects;
 | 
				
			||||||
 | 
					        int maxRedirects;
 | 
				
			||||||
 | 
					        bool verbose;
 | 
				
			||||||
 | 
					        bool compress;
 | 
				
			||||||
 | 
					        Logger logger;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class HttpClient {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        HttpClient();
 | 
				
			||||||
 | 
					        ~HttpClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HttpResponse get(const std::string& url,
 | 
				
			||||||
 | 
					                         const HttpRequestArgs& args);
 | 
				
			||||||
 | 
					        HttpResponse head(const std::string& url,
 | 
				
			||||||
 | 
					                          const HttpRequestArgs& args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HttpResponse post(const std::string& url,
 | 
				
			||||||
 | 
					                          const HttpParameters& httpParameters,
 | 
				
			||||||
 | 
					                          const HttpRequestArgs& args);
 | 
				
			||||||
 | 
					        HttpResponse post(const std::string& url,
 | 
				
			||||||
 | 
					                          const std::string& body,
 | 
				
			||||||
 | 
					                          const HttpRequestArgs& args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
 | 
					        HttpResponse request(const std::string& url,
 | 
				
			||||||
 | 
					                             const std::string& verb,
 | 
				
			||||||
 | 
					                             const std::string& body,
 | 
				
			||||||
 | 
					                             const HttpRequestArgs& args,
 | 
				
			||||||
 | 
					                             int redirects = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string serializeHttpParameters(const HttpParameters& httpParameters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string urlEncode(const std::string& value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void log(const std::string& msg, const HttpRequestArgs& args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool gzipInflate(
 | 
				
			||||||
 | 
					            const std::string& in,
 | 
				
			||||||
 | 
					            std::string& out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::shared_ptr<Socket> _socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const static std::string kPost;
 | 
				
			||||||
 | 
					        const static std::string kGet;
 | 
				
			||||||
 | 
					        const static std::string kHead;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								ixwebsocket/IXSocketFactory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								ixwebsocket/IXSocketFactory.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXSocketFactory.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXSocketFactory.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(__APPLE__) or defined(__linux__)
 | 
				
			||||||
 | 
					# ifdef __APPLE__
 | 
				
			||||||
 | 
					#  include <ixwebsocket/IXSocketAppleSSL.h>
 | 
				
			||||||
 | 
					# else
 | 
				
			||||||
 | 
					#  include <ixwebsocket/IXSocketOpenSSL.h>
 | 
				
			||||||
 | 
					# endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::shared_ptr<Socket> createSocket(bool tls,
 | 
				
			||||||
 | 
					                                         std::string& errorMsg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        errorMsg.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!tls)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return std::make_shared<Socket>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					#ifdef IXWEBSOCKET_USE_TLS
 | 
				
			||||||
 | 
					# ifdef __APPLE__
 | 
				
			||||||
 | 
					            return std::make_shared<SocketAppleSSL>();
 | 
				
			||||||
 | 
					# else
 | 
				
			||||||
 | 
					            return std::make_shared<SocketOpenSSL>();
 | 
				
			||||||
 | 
					# endif
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					            errorMsg = "TLS support is not enabled on this platform.";
 | 
				
			||||||
 | 
					            return nullptr;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								ixwebsocket/IXSocketFactory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								ixwebsocket/IXSocketFactory.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXSocketFactory.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class Socket;
 | 
				
			||||||
 | 
					    std::shared_ptr<Socket> createSocket(bool tls,
 | 
				
			||||||
 | 
					                                         std::string& errorMsg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										104
									
								
								ixwebsocket/IXUrlParser.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								ixwebsocket/IXUrlParser.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXUrlParser.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXUrlParser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // The only difference between those 2 regex is the protocol
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    std::regex UrlParser::_httpRegex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
 | 
				
			||||||
 | 
					    std::regex UrlParser::_webSocketRegex("(ws|wss)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool UrlParser::parse(const std::string& url,
 | 
				
			||||||
 | 
					                          std::string& protocol,
 | 
				
			||||||
 | 
					                          std::string& host,
 | 
				
			||||||
 | 
					                          std::string& path,
 | 
				
			||||||
 | 
					                          std::string& query,
 | 
				
			||||||
 | 
					                          int& port,
 | 
				
			||||||
 | 
					                          bool websocket)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cmatch what;
 | 
				
			||||||
 | 
					        if (!regex_match(url.c_str(), what,
 | 
				
			||||||
 | 
					                         websocket ? _webSocketRegex : _httpRegex))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string portStr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protocol = std::string(what[1].first, what[1].second);
 | 
				
			||||||
 | 
					        host     = std::string(what[2].first, what[2].second);
 | 
				
			||||||
 | 
					        portStr  = std::string(what[3].first, what[3].second);
 | 
				
			||||||
 | 
					        path     = std::string(what[4].first, what[4].second);
 | 
				
			||||||
 | 
					        query    = std::string(what[5].first, what[5].second);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (portStr.empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (protocol == "ws" || protocol == "http")
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                port = 80;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (protocol == "wss" || protocol == "https")
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                port = 443;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // Invalid protocol. Should be caught by regex check
 | 
				
			||||||
 | 
					                // but this missing branch trigger cpplint linter.
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::stringstream ss;
 | 
				
			||||||
 | 
					            ss << portStr;
 | 
				
			||||||
 | 
					            ss >> port;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (path.empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            path = "/";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (path[0] != '/')
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            path = '/' + path;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!query.empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            path += "?";
 | 
				
			||||||
 | 
					            path += query;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void UrlParser::printUrl(const std::string& url, bool websocket)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::string protocol, host, path, query;
 | 
				
			||||||
 | 
					        int port {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!parse(url, protocol, host, path, query, port, websocket))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cout << "[" << url << "]" << std::endl;
 | 
				
			||||||
 | 
					        std::cout << protocol << std::endl;
 | 
				
			||||||
 | 
					        std::cout << host << std::endl;
 | 
				
			||||||
 | 
					        std::cout << port << std::endl;
 | 
				
			||||||
 | 
					        std::cout << path << std::endl;
 | 
				
			||||||
 | 
					        std::cout << query << std::endl;
 | 
				
			||||||
 | 
					        std::cout << "-------------------------------" << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								ixwebsocket/IXUrlParser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								ixwebsocket/IXUrlParser.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXUrlParser.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <regex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class UrlParser
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        static bool parse(const std::string& url,
 | 
				
			||||||
 | 
					                          std::string& protocol,
 | 
				
			||||||
 | 
					                          std::string& host,
 | 
				
			||||||
 | 
					                          std::string& path,
 | 
				
			||||||
 | 
					                          std::string& query,
 | 
				
			||||||
 | 
					                          int& port,
 | 
				
			||||||
 | 
					                          bool websocket);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static void printUrl(const std::string& url, bool websocket);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
 | 
					        static std::regex _httpRegex;
 | 
				
			||||||
 | 
					        static std::regex _webSocketRegex;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "IXWebSocketHandshake.h"
 | 
					#include "IXWebSocketHandshake.h"
 | 
				
			||||||
#include "IXSocketConnect.h"
 | 
					#include "IXSocketConnect.h"
 | 
				
			||||||
 | 
					#include "IXUrlParser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "libwshandshake.hpp"
 | 
					#include "libwshandshake.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,90 +33,6 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool WebSocketHandshake::parseUrl(const std::string& url,
 | 
					 | 
				
			||||||
                                      std::string& protocol,
 | 
					 | 
				
			||||||
                                      std::string& host,
 | 
					 | 
				
			||||||
                                      std::string& path,
 | 
					 | 
				
			||||||
                                      std::string& query,
 | 
					 | 
				
			||||||
                                      int& port)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::regex ex("(ws|wss)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
 | 
					 | 
				
			||||||
        std::cmatch what;
 | 
					 | 
				
			||||||
        if (!regex_match(url.c_str(), what, ex))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::string portStr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protocol = std::string(what[1].first, what[1].second);
 | 
					 | 
				
			||||||
        host     = std::string(what[2].first, what[2].second);
 | 
					 | 
				
			||||||
        portStr  = std::string(what[3].first, what[3].second);
 | 
					 | 
				
			||||||
        path     = std::string(what[4].first, what[4].second);
 | 
					 | 
				
			||||||
        query    = std::string(what[5].first, what[5].second);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (portStr.empty())
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (protocol == "ws")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                port = 80;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (protocol == "wss")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                port = 443;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Invalid protocol. Should be caught by regex check
 | 
					 | 
				
			||||||
                // but this missing branch trigger cpplint linter.
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            std::stringstream ss;
 | 
					 | 
				
			||||||
            ss << portStr;
 | 
					 | 
				
			||||||
            ss >> port;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (path.empty())
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            path = "/";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (path[0] != '/')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            path = '/' + path;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!query.empty())
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            path += "?";
 | 
					 | 
				
			||||||
            path += query;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void WebSocketHandshake::printUrl(const std::string& url)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::string protocol, host, path, query;
 | 
					 | 
				
			||||||
        int port {0};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!WebSocketHandshake::parseUrl(url, protocol, host,
 | 
					 | 
				
			||||||
                                          path, query, port))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::cout << "[" << url << "]" << std::endl;
 | 
					 | 
				
			||||||
        std::cout << protocol << std::endl;
 | 
					 | 
				
			||||||
        std::cout << host << std::endl;
 | 
					 | 
				
			||||||
        std::cout << port << std::endl;
 | 
					 | 
				
			||||||
        std::cout << path << std::endl;
 | 
					 | 
				
			||||||
        std::cout << query << std::endl;
 | 
					 | 
				
			||||||
        std::cout << "-------------------------------" << std::endl;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::string WebSocketHandshake::trim(const std::string& str)
 | 
					    std::string WebSocketHandshake::trim(const std::string& str)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::string out(str);
 | 
					        std::string out(str);
 | 
				
			||||||
@@ -192,61 +109,6 @@ namespace ix
 | 
				
			|||||||
        return s;
 | 
					        return s;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::pair<bool, WebSocketHttpHeaders> WebSocketHandshake::parseHttpHeaders(
 | 
					 | 
				
			||||||
        const CancellationRequest& isCancellationRequested)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        WebSocketHttpHeaders headers;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        char line[256];
 | 
					 | 
				
			||||||
        int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while (true)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            int colon = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (i = 0;
 | 
					 | 
				
			||||||
                 i < 2 || (i < 255 && line[i-2] != '\r' && line[i-1] != '\n');
 | 
					 | 
				
			||||||
                 ++i)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (!_socket->readByte(line+i, isCancellationRequested))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return std::make_pair(false, headers);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (line[i] == ':' && colon == 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    colon = i;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (line[0] == '\r' && line[1] == '\n')
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // line is a single header entry. split by ':', and add it to our
 | 
					 | 
				
			||||||
            // header map. ignore lines with no colon.
 | 
					 | 
				
			||||||
            if (colon > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                line[i] = '\0';
 | 
					 | 
				
			||||||
                std::string lineStr(line);
 | 
					 | 
				
			||||||
                // colon is ':', colon+1 is ' ', colon+2 is the start of the value.
 | 
					 | 
				
			||||||
                // i is end of string (\0), i-colon is length of string minus key;
 | 
					 | 
				
			||||||
                // subtract 1 for '\0', 1 for '\n', 1 for '\r',
 | 
					 | 
				
			||||||
                // 1 for the ' ' after the ':', and total is -4
 | 
					 | 
				
			||||||
                std::string name(lineStr.substr(0, colon));
 | 
					 | 
				
			||||||
                std::string value(lineStr.substr(colon + 2, i - colon - 4));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Make the name lower case.
 | 
					 | 
				
			||||||
                std::transform(name.begin(), name.end(), name.begin(), ::tolower);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                headers[name] = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return std::make_pair(true, headers);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    WebSocketInitResult WebSocketHandshake::sendErrorResponse(int code, const std::string& reason)
 | 
					    WebSocketInitResult WebSocketHandshake::sendErrorResponse(int code, const std::string& reason)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::stringstream ss;
 | 
					        std::stringstream ss;
 | 
				
			||||||
@@ -355,7 +217,7 @@ namespace ix
 | 
				
			|||||||
            return WebSocketInitResult(false, status, ss.str());
 | 
					            return WebSocketInitResult(false, status, ss.str());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto result = parseHttpHeaders(isCancellationRequested);
 | 
					        auto result = parseHttpHeaders(_socket, isCancellationRequested);
 | 
				
			||||||
        auto headersValid = result.first;
 | 
					        auto headersValid = result.first;
 | 
				
			||||||
        auto headers = result.second;
 | 
					        auto headers = result.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -450,7 +312,7 @@ namespace ix
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Retrieve and validate HTTP headers
 | 
					        // Retrieve and validate HTTP headers
 | 
				
			||||||
        auto result = parseHttpHeaders(isCancellationRequested);
 | 
					        auto result = parseHttpHeaders(_socket, isCancellationRequested);
 | 
				
			||||||
        auto headersValid = result.first;
 | 
					        auto headersValid = result.first;
 | 
				
			||||||
        auto headers = result.second;
 | 
					        auto headers = result.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,19 +59,10 @@ namespace ix
 | 
				
			|||||||
        WebSocketInitResult serverHandshake(int fd,
 | 
					        WebSocketInitResult serverHandshake(int fd,
 | 
				
			||||||
                                            int timeoutSecs);
 | 
					                                            int timeoutSecs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        static bool parseUrl(const std::string& url,
 | 
					 | 
				
			||||||
                             std::string& protocol,
 | 
					 | 
				
			||||||
                             std::string& host,
 | 
					 | 
				
			||||||
                             std::string& path,
 | 
					 | 
				
			||||||
                             std::string& query,
 | 
					 | 
				
			||||||
                             int& port);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        static void printUrl(const std::string& url);
 | 
					 | 
				
			||||||
        std::string genRandomString(const int len);
 | 
					        std::string genRandomString(const int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Parse HTTP headers
 | 
					        // Parse HTTP headers
 | 
				
			||||||
        std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(const CancellationRequest& isCancellationRequested);
 | 
					 | 
				
			||||||
        WebSocketInitResult sendErrorResponse(int code, const std::string& reason);
 | 
					        WebSocketInitResult sendErrorResponse(int code, const std::string& reason);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::tuple<std::string, std::string, std::string> parseRequestLine(const std::string& line);
 | 
					        std::tuple<std::string, std::string, std::string> parseRequestLine(const std::string& line);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								ixwebsocket/IXWebSocketHttpHeaders.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								ixwebsocket/IXWebSocketHttpHeaders.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXWebSocketHttpHeaders.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXWebSocketHttpHeaders.h"
 | 
				
			||||||
 | 
					#include "IXSocket.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(
 | 
				
			||||||
 | 
					        std::shared_ptr<Socket> socket,
 | 
				
			||||||
 | 
					        const CancellationRequest& isCancellationRequested)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        char line[1024];
 | 
				
			||||||
 | 
					        int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (true)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            int colon = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (i = 0;
 | 
				
			||||||
 | 
					                 i < 2 || (i < 1023 && line[i-2] != '\r' && line[i-1] != '\n');
 | 
				
			||||||
 | 
					                 ++i)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (!socket->readByte(line+i, isCancellationRequested))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    return std::make_pair(false, headers);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (line[i] == ':' && colon == 0)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    colon = i;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (line[0] == '\r' && line[1] == '\n')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // line is a single header entry. split by ':', and add it to our
 | 
				
			||||||
 | 
					            // header map. ignore lines with no colon.
 | 
				
			||||||
 | 
					            if (colon > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                line[i] = '\0';
 | 
				
			||||||
 | 
					                std::string lineStr(line);
 | 
				
			||||||
 | 
					                // colon is ':', colon+1 is ' ', colon+2 is the start of the value.
 | 
				
			||||||
 | 
					                // i is end of string (\0), i-colon is length of string minus key;
 | 
				
			||||||
 | 
					                // subtract 1 for '\0', 1 for '\n', 1 for '\r',
 | 
				
			||||||
 | 
					                // 1 for the ' ' after the ':', and total is -4
 | 
				
			||||||
 | 
					                std::string name(lineStr.substr(0, colon));
 | 
				
			||||||
 | 
					                std::string value(lineStr.substr(colon + 2, i - colon - 4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                headers[name] = value;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return std::make_pair(true, headers);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,10 +6,40 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXCancellationRequest.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <unordered_map>
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using WebSocketHttpHeaders = std::unordered_map<std::string, std::string>;
 | 
					    class Socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct CaseInsensitiveLess
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Case Insensitive compare_less binary function
 | 
				
			||||||
 | 
					        struct NocaseCompare
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            bool operator() (const unsigned char& c1, const unsigned char& c2) const
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return std::tolower(c1) < std::tolower(c2);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool operator() (const std::string & s1, const std::string & s2) const
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return std::lexicographical_compare
 | 
				
			||||||
 | 
					                (s1.begin(), s1.end(),   // source range
 | 
				
			||||||
 | 
					                 s2.begin(), s2.end(),   // dest range
 | 
				
			||||||
 | 
					                 NocaseCompare());  // comparison
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using WebSocketHttpHeaders = std::map<std::string, std::string, CaseInsensitiveLess>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(
 | 
				
			||||||
 | 
					        std::shared_ptr<Socket> socket,
 | 
				
			||||||
 | 
					        const CancellationRequest& isCancellationRequested);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,8 @@
 | 
				
			|||||||
#include "IXWebSocketTransport.h"
 | 
					#include "IXWebSocketTransport.h"
 | 
				
			||||||
#include "IXWebSocketHandshake.h"
 | 
					#include "IXWebSocketHandshake.h"
 | 
				
			||||||
#include "IXWebSocketHttpHeaders.h"
 | 
					#include "IXWebSocketHttpHeaders.h"
 | 
				
			||||||
 | 
					#include "IXUrlParser.h"
 | 
				
			||||||
 | 
					#include "IXSocketFactory.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef IXWEBSOCKET_USE_TLS
 | 
					#ifdef IXWEBSOCKET_USE_TLS
 | 
				
			||||||
# ifdef __APPLE__
 | 
					# ifdef __APPLE__
 | 
				
			||||||
@@ -70,31 +72,31 @@ namespace ix
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        std::string protocol, host, path, query;
 | 
					        std::string protocol, host, path, query;
 | 
				
			||||||
        int port;
 | 
					        int port;
 | 
				
			||||||
 | 
					        bool websocket = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!WebSocketHandshake::parseUrl(url, protocol, host,
 | 
					        if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
 | 
				
			||||||
                                          path, query, port))
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return WebSocketInitResult(false, 0,
 | 
					            return WebSocketInitResult(false, 0,
 | 
				
			||||||
                                       std::string("Could not parse URL ") + url);
 | 
					                                       std::string("Could not parse URL ") + url);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (protocol == "wss")
 | 
					        if (protocol != "ws" && protocol != "wss")
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _socket.reset();
 | 
					            std::stringstream ss;
 | 
				
			||||||
#ifdef IXWEBSOCKET_USE_TLS
 | 
					            ss << "Invalid protocol: " << protocol
 | 
				
			||||||
# ifdef __APPLE__
 | 
					               << " for url " << url
 | 
				
			||||||
             _socket = std::make_shared<SocketAppleSSL>();
 | 
					               << " . Supported protocols are ws and wss";
 | 
				
			||||||
# else
 | 
					
 | 
				
			||||||
             _socket = std::make_shared<SocketOpenSSL>();
 | 
					            return WebSocketInitResult(false, 0, ss.str());
 | 
				
			||||||
# endif
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
            return WebSocketInitResult(false, 0, "TLS is not supported.");
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					
 | 
				
			||||||
 | 
					        bool tls = protocol == "wss";
 | 
				
			||||||
 | 
					        std::string errorMsg;
 | 
				
			||||||
 | 
					        _socket = createSocket(tls, errorMsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!_socket)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _socket.reset();
 | 
					            return WebSocketInitResult(false, 0, errorMsg);
 | 
				
			||||||
            _socket = std::make_shared<Socket>();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        WebSocketHandshake webSocketHandshake(_requestInitCancellation,
 | 
					        WebSocketHandshake webSocketHandshake(_requestInitCancellation,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								makefile
									
									
									
									
									
								
							@@ -1,14 +1,17 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
# This makefile is just used to easily work with docker (linux build)
 | 
					# This makefile is just used to easily work with docker (linux build)
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
all: run
 | 
					all: brew
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					brew:
 | 
				
			||||||
 | 
						mkdir -p build && (cd build ; cmake .. ; make -j install)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: docker
 | 
					.PHONY: docker
 | 
				
			||||||
docker:
 | 
					docker:
 | 
				
			||||||
	docker build -t ws_connect:latest .
 | 
						docker build -t broadcast_server:latest .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
run: docker
 | 
					run:
 | 
				
			||||||
	docker run --cap-add sys_ptrace -it ws_connect:latest bash
 | 
						docker run --cap-add sys_ptrace -it broadcast_server:latest bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# this is helpful to remove trailing whitespaces
 | 
					# this is helpful to remove trailing whitespaces
 | 
				
			||||||
trail:
 | 
					trail:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,13 +18,14 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/.. ixwebsocket)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
include_directories(
 | 
					include_directories(
 | 
				
			||||||
  ${PROJECT_SOURCE_DIR}/Catch2/single_include
 | 
					  ${PROJECT_SOURCE_DIR}/Catch2/single_include
 | 
				
			||||||
 | 
					  ../third_party/msgpack11
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Shared sources
 | 
					# Shared sources
 | 
				
			||||||
set (SOURCES 
 | 
					set (SOURCES 
 | 
				
			||||||
  test_runner.cpp
 | 
					  test_runner.cpp
 | 
				
			||||||
  IXTest.cpp
 | 
					  IXTest.cpp
 | 
				
			||||||
  msgpack11.cpp
 | 
					  ../third_party/msgpack11/msgpack11.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  IXDNSLookupTest.cpp
 | 
					  IXDNSLookupTest.cpp
 | 
				
			||||||
  IXSocketTest.cpp
 | 
					  IXSocketTest.cpp
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4641
									
								
								third_party/cli11/CLI11.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4641
									
								
								third_party/cli11/CLI11.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								third_party/remove_trailing_whitespaces.sh
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								third_party/remove_trailing_whitespaces.sh
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					find . -type f -name '*.cpp' -exec sed -i '' 's/[[:space:]]*$//' {} \+
 | 
				
			||||||
 | 
					find . -type f -name '*.h' -exec sed -i '' 's/[[:space:]]*$//' {} \+
 | 
				
			||||||
							
								
								
									
										1
									
								
								ws/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ws/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					build
 | 
				
			||||||
							
								
								
									
										42
									
								
								ws/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								ws/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					# Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cmake_minimum_required (VERSION 3.4.1)
 | 
				
			||||||
 | 
					project (ws)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# There's -Weverything too for clang
 | 
				
			||||||
 | 
					set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set (CMAKE_CXX_STANDARD 14)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					option(USE_TLS "Add TLS support" ON)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include_directories(ws .)
 | 
				
			||||||
 | 
					include_directories(ws ..)
 | 
				
			||||||
 | 
					include_directories(ws ../third_party)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_executable(ws 
 | 
				
			||||||
 | 
					  ../third_party/msgpack11/msgpack11.cpp
 | 
				
			||||||
 | 
					  ixcrypto/IXBase64.cpp
 | 
				
			||||||
 | 
					  ixcrypto/IXHash.cpp
 | 
				
			||||||
 | 
					  ixcrypto/IXUuid.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ws_http_client.cpp
 | 
				
			||||||
 | 
					  ws_ping_pong.cpp
 | 
				
			||||||
 | 
					  ws_broadcast_server.cpp
 | 
				
			||||||
 | 
					  ws_echo_server.cpp
 | 
				
			||||||
 | 
					  ws_chat.cpp
 | 
				
			||||||
 | 
					  ws_connect.cpp
 | 
				
			||||||
 | 
					  ws_transfer.cpp
 | 
				
			||||||
 | 
					  ws_send.cpp
 | 
				
			||||||
 | 
					  ws_receive.cpp
 | 
				
			||||||
 | 
					  ws.cpp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (APPLE AND USE_TLS)
 | 
				
			||||||
 | 
					    target_link_libraries(ws "-framework foundation" "-framework security")
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target_link_libraries(ws ixwebsocket)
 | 
				
			||||||
 | 
					install(TARGETS ws RUNTIME DESTINATION bin)
 | 
				
			||||||
							
								
								
									
										10
									
								
								ws/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								ws/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					```
 | 
				
			||||||
 | 
					# Start transfer server, which is just a broadcast server at this point
 | 
				
			||||||
 | 
					./ws transfer # running on port 8080.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Start receiver first
 | 
				
			||||||
 | 
					./ws receive ws://localhost:8080 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Then send a file. File will be received and written to disk by the receiver process
 | 
				
			||||||
 | 
					./ws send ws://localhost:8080 /file/to/path
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
@@ -15,8 +15,6 @@ set (CMAKE_CXX_STANDARD 14)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
option(USE_TLS "Add TLS support" ON)
 | 
					option(USE_TLS "Add TLS support" ON)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
include_directories(cobra_publisher ${OPENSSL_PREFIX}/include)
 | 
					include_directories(cobra_publisher ${OPENSSL_PREFIX}/include)
 | 
				
			||||||
include_directories(cobra_publisher .)
 | 
					include_directories(cobra_publisher .)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										333
									
								
								ws/cobra_publisher/jsoncpp/json/json-forwards.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								ws/cobra_publisher/jsoncpp/json/json-forwards.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,333 @@
 | 
				
			|||||||
 | 
					/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
 | 
				
			||||||
 | 
					/// It is intended to be used with #include "json/json-forwards.h"
 | 
				
			||||||
 | 
					/// This header provides forward declaration for all JsonCpp types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// Beginning of content of file: LICENSE
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					The JsonCpp library's source code, including accompanying documentation,
 | 
				
			||||||
 | 
					tests and demonstration applications, are licensed under the following
 | 
				
			||||||
 | 
					conditions...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
 | 
				
			||||||
 | 
					jurisdictions which recognize such a disclaimer. In such jurisdictions,
 | 
				
			||||||
 | 
					this software is released into the Public Domain.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
 | 
				
			||||||
 | 
					2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
 | 
				
			||||||
 | 
					The JsonCpp Authors, and is released under the terms of the MIT License (see below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In jurisdictions which recognize Public Domain property, the user of this
 | 
				
			||||||
 | 
					software may choose to accept it either as 1) Public Domain, 2) under the
 | 
				
			||||||
 | 
					conditions of the MIT License (see below), or 3) under the terms of dual
 | 
				
			||||||
 | 
					Public Domain/MIT License conditions described here, as they choose.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The MIT License is about as close to Public Domain as a license can get, and is
 | 
				
			||||||
 | 
					described in clear, concise terms at:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   http://en.wikipedia.org/wiki/MIT_License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The full text of the MIT License follows:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					========================================================================
 | 
				
			||||||
 | 
					Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person
 | 
				
			||||||
 | 
					obtaining a copy of this software and associated documentation
 | 
				
			||||||
 | 
					files (the "Software"), to deal in the Software without
 | 
				
			||||||
 | 
					restriction, including without limitation the rights to use, copy,
 | 
				
			||||||
 | 
					modify, merge, publish, distribute, sublicense, and/or sell copies
 | 
				
			||||||
 | 
					of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be
 | 
				
			||||||
 | 
					included in all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
				
			||||||
 | 
					EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
				
			||||||
 | 
					MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
				
			||||||
 | 
					NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 | 
				
			||||||
 | 
					BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | 
				
			||||||
 | 
					ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
				
			||||||
 | 
					CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					SOFTWARE.
 | 
				
			||||||
 | 
					========================================================================
 | 
				
			||||||
 | 
					(END LICENSE TEXT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The MIT license is compatible with both the GPL and commercial
 | 
				
			||||||
 | 
					software, affording one all of the rights of Public Domain with the
 | 
				
			||||||
 | 
					minor nuisance of being required to keep the above copyright notice
 | 
				
			||||||
 | 
					and license text in the source code. Note also that by accepting the
 | 
				
			||||||
 | 
					Public Domain "license" you can re-license your copy using whatever
 | 
				
			||||||
 | 
					license you like.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// End of content of file: LICENSE
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
 | 
				
			||||||
 | 
					# define JSON_FORWARD_AMALGATED_H_INCLUDED
 | 
				
			||||||
 | 
					/// If defined, indicates that the source file is amalgated
 | 
				
			||||||
 | 
					/// to prevent private header inclusion.
 | 
				
			||||||
 | 
					#define JSON_IS_AMALGAMATION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// Beginning of content of file: include/json/config.h
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
 | 
				
			||||||
 | 
					// Distributed under MIT license, or public domain if desired and
 | 
				
			||||||
 | 
					// recognized in your jurisdiction.
 | 
				
			||||||
 | 
					// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_CONFIG_H_INCLUDED
 | 
				
			||||||
 | 
					#define JSON_CONFIG_H_INCLUDED
 | 
				
			||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					#include <string> //typedef String
 | 
				
			||||||
 | 
					#include <stdint.h> //typedef int64_t, uint64_t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// If defined, indicates that json library is embedded in CppTL library.
 | 
				
			||||||
 | 
					//# define JSON_IN_CPPTL 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// If defined, indicates that json may leverage CppTL library
 | 
				
			||||||
 | 
					//#  define JSON_USE_CPPTL 1
 | 
				
			||||||
 | 
					/// If defined, indicates that cpptl vector based map should be used instead of
 | 
				
			||||||
 | 
					/// std::map
 | 
				
			||||||
 | 
					/// as Value container.
 | 
				
			||||||
 | 
					//#  define JSON_USE_CPPTL_SMALLMAP 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// If non-zero, the library uses exceptions to report bad input instead of C
 | 
				
			||||||
 | 
					// assertion macros. The default is to use exceptions.
 | 
				
			||||||
 | 
					#ifndef JSON_USE_EXCEPTION
 | 
				
			||||||
 | 
					#define JSON_USE_EXCEPTION 1
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// If defined, indicates that the source file is amalgated
 | 
				
			||||||
 | 
					/// to prevent private header inclusion.
 | 
				
			||||||
 | 
					/// Remarks: it is automatically defined in the generated amalgated header.
 | 
				
			||||||
 | 
					// #define JSON_IS_AMALGAMATION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef JSON_IN_CPPTL
 | 
				
			||||||
 | 
					#include <cpptl/config.h>
 | 
				
			||||||
 | 
					#ifndef JSON_USE_CPPTL
 | 
				
			||||||
 | 
					#define JSON_USE_CPPTL 1
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef JSON_IN_CPPTL
 | 
				
			||||||
 | 
					#define JSON_API CPPTL_API
 | 
				
			||||||
 | 
					#elif defined(JSON_DLL_BUILD)
 | 
				
			||||||
 | 
					#if defined(_MSC_VER) || defined(__MINGW32__)
 | 
				
			||||||
 | 
					#define JSON_API __declspec(dllexport)
 | 
				
			||||||
 | 
					#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
 | 
				
			||||||
 | 
					#endif // if defined(_MSC_VER)
 | 
				
			||||||
 | 
					#elif defined(JSON_DLL)
 | 
				
			||||||
 | 
					#if defined(_MSC_VER) || defined(__MINGW32__)
 | 
				
			||||||
 | 
					#define JSON_API __declspec(dllimport)
 | 
				
			||||||
 | 
					#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
 | 
				
			||||||
 | 
					#endif // if defined(_MSC_VER)
 | 
				
			||||||
 | 
					#endif // ifdef JSON_IN_CPPTL
 | 
				
			||||||
 | 
					#if !defined(JSON_API)
 | 
				
			||||||
 | 
					#define JSON_API
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
 | 
				
			||||||
 | 
					// integer
 | 
				
			||||||
 | 
					// Storages, and 64 bits integer support is disabled.
 | 
				
			||||||
 | 
					// #define JSON_NO_INT64 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(_MSC_VER) // MSVC
 | 
				
			||||||
 | 
					#  if _MSC_VER <= 1200 // MSVC 6
 | 
				
			||||||
 | 
					    // Microsoft Visual Studio 6 only support conversion from __int64 to double
 | 
				
			||||||
 | 
					    // (no conversion from unsigned __int64).
 | 
				
			||||||
 | 
					#    define JSON_USE_INT64_DOUBLE_CONVERSION 1
 | 
				
			||||||
 | 
					    // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
 | 
				
			||||||
 | 
					    // characters in the debug information)
 | 
				
			||||||
 | 
					    // All projects I've ever seen with VS6 were using this globally (not bothering
 | 
				
			||||||
 | 
					    // with pragma push/pop).
 | 
				
			||||||
 | 
					#    pragma warning(disable : 4786)
 | 
				
			||||||
 | 
					#  endif // MSVC 6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#  if _MSC_VER >= 1500 // MSVC 2008
 | 
				
			||||||
 | 
					    /// Indicates that the following function is deprecated.
 | 
				
			||||||
 | 
					#    define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
 | 
				
			||||||
 | 
					#  endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // defined(_MSC_VER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// In c++11 the override keyword allows you to explicity define that a function
 | 
				
			||||||
 | 
					// is intended to override the base-class version.  This makes the code more
 | 
				
			||||||
 | 
					// managable and fixes a set of common hard-to-find bugs.
 | 
				
			||||||
 | 
					#if __cplusplus >= 201103L
 | 
				
			||||||
 | 
					# define JSONCPP_OVERRIDE override
 | 
				
			||||||
 | 
					# define JSONCPP_NOEXCEPT noexcept
 | 
				
			||||||
 | 
					#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
 | 
				
			||||||
 | 
					# define JSONCPP_OVERRIDE override
 | 
				
			||||||
 | 
					# define JSONCPP_NOEXCEPT throw()
 | 
				
			||||||
 | 
					#elif defined(_MSC_VER) && _MSC_VER >= 1900
 | 
				
			||||||
 | 
					# define JSONCPP_OVERRIDE override
 | 
				
			||||||
 | 
					# define JSONCPP_NOEXCEPT noexcept
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					# define JSONCPP_OVERRIDE
 | 
				
			||||||
 | 
					# define JSONCPP_NOEXCEPT throw()
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_HAS_RVALUE_REFERENCES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
 | 
				
			||||||
 | 
					#define JSON_HAS_RVALUE_REFERENCES 1
 | 
				
			||||||
 | 
					#endif // MSVC >= 2010
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __clang__
 | 
				
			||||||
 | 
					#if __has_feature(cxx_rvalue_references)
 | 
				
			||||||
 | 
					#define JSON_HAS_RVALUE_REFERENCES 1
 | 
				
			||||||
 | 
					#endif  // has_feature
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
 | 
				
			||||||
 | 
					#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
 | 
				
			||||||
 | 
					#define JSON_HAS_RVALUE_REFERENCES 1
 | 
				
			||||||
 | 
					#endif  // GXX_EXPERIMENTAL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // __clang__ || __GNUC__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // not defined JSON_HAS_RVALUE_REFERENCES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_HAS_RVALUE_REFERENCES
 | 
				
			||||||
 | 
					#define JSON_HAS_RVALUE_REFERENCES 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __clang__
 | 
				
			||||||
 | 
					#  if __has_extension(attribute_deprecated_with_message)
 | 
				
			||||||
 | 
					#    define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
 | 
				
			||||||
 | 
					#  endif
 | 
				
			||||||
 | 
					#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
 | 
				
			||||||
 | 
					#  if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
 | 
				
			||||||
 | 
					#    define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
 | 
				
			||||||
 | 
					#  elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
 | 
				
			||||||
 | 
					#    define JSONCPP_DEPRECATED(message)  __attribute__((__deprecated__))
 | 
				
			||||||
 | 
					#  endif  // GNUC version
 | 
				
			||||||
 | 
					#endif // __clang__ || __GNUC__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if !defined(JSONCPP_DEPRECATED)
 | 
				
			||||||
 | 
					#define JSONCPP_DEPRECATED(message)
 | 
				
			||||||
 | 
					#endif // if !defined(JSONCPP_DEPRECATED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if __GNUC__ >= 6
 | 
				
			||||||
 | 
					#  define JSON_USE_INT64_DOUBLE_CONVERSION 1
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if !defined(JSON_IS_AMALGAMATION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# include "version.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# if JSONCPP_USING_SECURE_MEMORY
 | 
				
			||||||
 | 
					#  include "allocator.h" //typedef Allocator
 | 
				
			||||||
 | 
					# endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // if !defined(JSON_IS_AMALGAMATION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Json {
 | 
				
			||||||
 | 
					typedef int Int;
 | 
				
			||||||
 | 
					typedef unsigned int UInt;
 | 
				
			||||||
 | 
					#if defined(JSON_NO_INT64)
 | 
				
			||||||
 | 
					typedef int LargestInt;
 | 
				
			||||||
 | 
					typedef unsigned int LargestUInt;
 | 
				
			||||||
 | 
					#undef JSON_HAS_INT64
 | 
				
			||||||
 | 
					#else                 // if defined(JSON_NO_INT64)
 | 
				
			||||||
 | 
					// For Microsoft Visual use specific types as long long is not supported
 | 
				
			||||||
 | 
					#if defined(_MSC_VER) // Microsoft Visual Studio
 | 
				
			||||||
 | 
					typedef __int64 Int64;
 | 
				
			||||||
 | 
					typedef unsigned __int64 UInt64;
 | 
				
			||||||
 | 
					#else                 // if defined(_MSC_VER) // Other platforms, use long long
 | 
				
			||||||
 | 
					typedef int64_t Int64;
 | 
				
			||||||
 | 
					typedef uint64_t UInt64;
 | 
				
			||||||
 | 
					#endif // if defined(_MSC_VER)
 | 
				
			||||||
 | 
					typedef Int64 LargestInt;
 | 
				
			||||||
 | 
					typedef UInt64 LargestUInt;
 | 
				
			||||||
 | 
					#define JSON_HAS_INT64
 | 
				
			||||||
 | 
					#endif // if defined(JSON_NO_INT64)
 | 
				
			||||||
 | 
					#if JSONCPP_USING_SECURE_MEMORY
 | 
				
			||||||
 | 
					#define JSONCPP_STRING        std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
 | 
				
			||||||
 | 
					#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
 | 
				
			||||||
 | 
					#define JSONCPP_OSTREAM       std::basic_ostream<char, std::char_traits<char>>
 | 
				
			||||||
 | 
					#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
 | 
				
			||||||
 | 
					#define JSONCPP_ISTREAM       std::istream
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define JSONCPP_STRING        std::string
 | 
				
			||||||
 | 
					#define JSONCPP_OSTRINGSTREAM std::ostringstream
 | 
				
			||||||
 | 
					#define JSONCPP_OSTREAM       std::ostream
 | 
				
			||||||
 | 
					#define JSONCPP_ISTRINGSTREAM std::istringstream
 | 
				
			||||||
 | 
					#define JSONCPP_ISTREAM       std::istream
 | 
				
			||||||
 | 
					#endif // if JSONCPP_USING_SECURE_MEMORY
 | 
				
			||||||
 | 
					} // end namespace Json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // JSON_CONFIG_H_INCLUDED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// End of content of file: include/json/config.h
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// Beginning of content of file: include/json/forwards.h
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
 | 
				
			||||||
 | 
					// Distributed under MIT license, or public domain if desired and
 | 
				
			||||||
 | 
					// recognized in your jurisdiction.
 | 
				
			||||||
 | 
					// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_FORWARDS_H_INCLUDED
 | 
				
			||||||
 | 
					#define JSON_FORWARDS_H_INCLUDED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if !defined(JSON_IS_AMALGAMATION)
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#endif // if !defined(JSON_IS_AMALGAMATION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Json {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// writer.h
 | 
				
			||||||
 | 
					class FastWriter;
 | 
				
			||||||
 | 
					class StyledWriter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// reader.h
 | 
				
			||||||
 | 
					class Reader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// features.h
 | 
				
			||||||
 | 
					class Features;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// value.h
 | 
				
			||||||
 | 
					typedef unsigned int ArrayIndex;
 | 
				
			||||||
 | 
					class StaticString;
 | 
				
			||||||
 | 
					class Path;
 | 
				
			||||||
 | 
					class PathArgument;
 | 
				
			||||||
 | 
					class Value;
 | 
				
			||||||
 | 
					class ValueIteratorBase;
 | 
				
			||||||
 | 
					class ValueIterator;
 | 
				
			||||||
 | 
					class ValueConstIterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // JSON_FORWARDS_H_INCLUDED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					// End of content of file: include/json/forwards.h
 | 
				
			||||||
 | 
					// //////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
 | 
				
			||||||
							
								
								
									
										2186
									
								
								ws/cobra_publisher/jsoncpp/json/json.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2186
									
								
								ws/cobra_publisher/jsoncpp/json/json.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5386
									
								
								ws/cobra_publisher/jsoncpp/jsoncpp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5386
									
								
								ws/cobra_publisher/jsoncpp/jsoncpp.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										44
									
								
								ws/docker_build.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								ws/docker_build.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 'manual' way of building. I cannot get CMake to work to build in a container.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g++ --std=c++14 \
 | 
				
			||||||
 | 
					    -DIXWEBSOCKET_USE_TLS \
 | 
				
			||||||
 | 
					    -g \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXEventFd.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXSocket.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXSocketServer.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXSocketConnect.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXDNSLookup.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXCancellationRequest.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXWebSocket.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXWebSocketServer.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXWebSocketTransport.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXWebSocketHandshake.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/IXSocketOpenSSL.cpp \
 | 
				
			||||||
 | 
					    ../ixwebsocket/linux/IXSetThreadName_linux.cpp \
 | 
				
			||||||
 | 
					    ../third_party/jsoncpp/jsoncpp.cpp \
 | 
				
			||||||
 | 
					    ixcrypto/IXBase64.cpp \
 | 
				
			||||||
 | 
					    ixcrypto/IXHash.cpp \
 | 
				
			||||||
 | 
					    ixcrypto/IXUuid.cpp \
 | 
				
			||||||
 | 
					    ws_ping_pong.cpp \
 | 
				
			||||||
 | 
					    ws_broadcast_server.cpp \
 | 
				
			||||||
 | 
					    ws_echo_server.cpp \
 | 
				
			||||||
 | 
					    ws_chat.cpp \
 | 
				
			||||||
 | 
					    ws_connect.cpp \
 | 
				
			||||||
 | 
					    ws_transfer.cpp \
 | 
				
			||||||
 | 
					    ws_send.cpp \
 | 
				
			||||||
 | 
					    ws_receive.cpp \
 | 
				
			||||||
 | 
					    ws.cpp \
 | 
				
			||||||
 | 
					    -I . \
 | 
				
			||||||
 | 
					    -I .. \
 | 
				
			||||||
 | 
					    -I ../third_party \
 | 
				
			||||||
 | 
					    -o ws \
 | 
				
			||||||
 | 
					    -lcrypto -lssl -lz -lpthread
 | 
				
			||||||
							
								
								
									
										22
									
								
								ws/ixcrypto/IXHash.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								ws/ixcrypto/IXHash.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXHash.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXHash.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint64_t djb2Hash(const std::vector<uint8_t>& data)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        uint64_t hashAddress = 5381;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (auto&& c : data)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            hashAddress = ((hashAddress << 5) + hashAddress) + c;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return hashAddress;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								ws/ixcrypto/IXHash.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								ws/ixcrypto/IXHash.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXHash.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint64_t djb2Hash(const std::vector<uint8_t>& data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										75
									
								
								ws/ixcrypto/IXUuid.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								ws/ixcrypto/IXUuid.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXUuid.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Generate a random uuid similar to the uuid python module
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * >>> import uuid
 | 
				
			||||||
 | 
					 * >>> uuid.uuid4().hex
 | 
				
			||||||
 | 
					 * 'bec08155b37d4050a1f3c3fa0276bf12'
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Code adapted from https://github.com/r-lyeh-archived/sole
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "IXUuid.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					#include <random>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class Uuid
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public:
 | 
				
			||||||
 | 
					            Uuid();
 | 
				
			||||||
 | 
					            std::string toString() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private:
 | 
				
			||||||
 | 
					            uint64_t _ab;
 | 
				
			||||||
 | 
					            uint64_t _cd;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Uuid::Uuid()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static std::random_device rd;
 | 
				
			||||||
 | 
					        static std::uniform_int_distribution<uint64_t> dist(0, (uint64_t)(~0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _ab = dist(rd);
 | 
				
			||||||
 | 
					        _cd = dist(rd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _ab = (_ab & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL;
 | 
				
			||||||
 | 
					        _cd = (_cd & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string Uuid::toString() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        ss << std::hex << std::nouppercase << std::setfill('0');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        uint32_t a = (_ab >> 32);
 | 
				
			||||||
 | 
					        uint32_t b = (_ab & 0xFFFFFFFF);
 | 
				
			||||||
 | 
					        uint32_t c = (_cd >> 32);
 | 
				
			||||||
 | 
					        uint32_t d = (_cd & 0xFFFFFFFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ss << std::setw(8) << (a);
 | 
				
			||||||
 | 
					        ss << std::setw(4) << (b >> 16);
 | 
				
			||||||
 | 
					        ss << std::setw(4) << (b & 0xFFFF);
 | 
				
			||||||
 | 
					        ss << std::setw(4) << (c >> 16 );
 | 
				
			||||||
 | 
					        ss << std::setw(4) << (c & 0xFFFF);
 | 
				
			||||||
 | 
					        ss << std::setw(8) << d;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ss.str();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string uuid4()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Uuid id;
 | 
				
			||||||
 | 
					        return id.toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								ws/ixcrypto/IXUuid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								ws/ixcrypto/IXUuid.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  IXUuid.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2017 Machine Zone. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   /**
 | 
				
			||||||
 | 
					    * Generate a random uuid
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					   std::string uuid4();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										131
									
								
								ws/ws.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								ws/ws.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Main driver for websocket utilities
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					#include "ws.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Main drive for websocket utilities
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cli11/CLI11.hpp>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXSocket.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char** argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CLI::App app{"ws is a websocket tool"};
 | 
				
			||||||
 | 
					    app.require_subcommand();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string url("ws://127.0.0.1:8080");
 | 
				
			||||||
 | 
					    std::string path;
 | 
				
			||||||
 | 
					    std::string user;
 | 
				
			||||||
 | 
					    std::string data;
 | 
				
			||||||
 | 
					    std::string headers;
 | 
				
			||||||
 | 
					    std::string output;
 | 
				
			||||||
 | 
					    bool headersOnly = false;
 | 
				
			||||||
 | 
					    bool followRedirects = false;
 | 
				
			||||||
 | 
					    bool verbose = false;
 | 
				
			||||||
 | 
					    bool save = false;
 | 
				
			||||||
 | 
					    bool compress = false;
 | 
				
			||||||
 | 
					    int port = 8080;
 | 
				
			||||||
 | 
					    int connectTimeOut = 60;
 | 
				
			||||||
 | 
					    int transferTimeout = 1800;
 | 
				
			||||||
 | 
					    int maxRedirects = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* sendApp = app.add_subcommand("send", "Send a file");
 | 
				
			||||||
 | 
					    sendApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
 | 
					    sendApp->add_option("path", path, "Path to the file to send")
 | 
				
			||||||
 | 
					        ->required()->check(CLI::ExistingPath);
 | 
				
			||||||
 | 
					    CLI::App* receiveApp = app.add_subcommand("receive", "Receive a file");
 | 
				
			||||||
 | 
					    receiveApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server");
 | 
				
			||||||
 | 
					    transferApp->add_option("--port", port, "Connection url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* connectApp = app.add_subcommand("connect", "Connect to a remote server");
 | 
				
			||||||
 | 
					    connectApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
 | 
				
			||||||
 | 
					    chatApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
 | 
					    chatApp->add_option("user", user, "User name")->required();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* echoServerApp = app.add_subcommand("echo_server", "Echo server");
 | 
				
			||||||
 | 
					    echoServerApp->add_option("--port", port, "Connection url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
 | 
				
			||||||
 | 
					    broadcastServerApp->add_option("--port", port, "Connection url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* pingPongApp = app.add_subcommand("ping", "Ping pong");
 | 
				
			||||||
 | 
					    pingPongApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI::App* httpClientApp = app.add_subcommand("curl", "HTTP Client");
 | 
				
			||||||
 | 
					    httpClientApp->add_option("url", url, "Connection url")->required();
 | 
				
			||||||
 | 
					    httpClientApp->add_option("-d", data, "Form data")->join();
 | 
				
			||||||
 | 
					    httpClientApp->add_option("-F", data, "Form data")->join();
 | 
				
			||||||
 | 
					    httpClientApp->add_option("-H", headers, "Header")->join();
 | 
				
			||||||
 | 
					    httpClientApp->add_option("--output", output, "Output file");
 | 
				
			||||||
 | 
					    httpClientApp->add_flag("-I", headersOnly, "Send a HEAD request");
 | 
				
			||||||
 | 
					    httpClientApp->add_flag("-L", followRedirects, "Follow redirects");
 | 
				
			||||||
 | 
					    httpClientApp->add_option("--max-redirects", maxRedirects, "Max Redirects");
 | 
				
			||||||
 | 
					    httpClientApp->add_flag("-v", verbose, "Verbose");
 | 
				
			||||||
 | 
					    httpClientApp->add_flag("-O", save, "Save output to disk");
 | 
				
			||||||
 | 
					    httpClientApp->add_flag("--compress", compress, "Enable gzip compression");
 | 
				
			||||||
 | 
					    httpClientApp->add_option("--connect-timeout", connectTimeOut, "Connection timeout");
 | 
				
			||||||
 | 
					    httpClientApp->add_option("--transfer-timeout", transferTimeout, "Transfer timeout");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLI11_PARSE(app, argc, argv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ix::Socket::init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (app.got_subcommand("transfer"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_transfer_main(port);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("send"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_send_main(url, path);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("receive"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        bool enablePerMessageDeflate = false;
 | 
				
			||||||
 | 
					        return ix::ws_receive_main(url, enablePerMessageDeflate);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("connect"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_connect_main(url);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("chat"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_chat_main(url, user);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("echo_server"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_echo_server_main(port);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("broadcast_server"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_broadcast_server_main(port);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("ping"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_ping_pong_main(url);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (app.got_subcommand("curl"))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ix::ws_http_client_main(url, headers, data, headersOnly,
 | 
				
			||||||
 | 
					                                       connectTimeOut, transferTimeout,
 | 
				
			||||||
 | 
					                                       followRedirects, maxRedirects, verbose,
 | 
				
			||||||
 | 
					                                       save, output, compress);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								ws/ws.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								ws/ws.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws.h
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ws_http_client_main(const std::string& url,
 | 
				
			||||||
 | 
					                            const std::string& headers,
 | 
				
			||||||
 | 
					                            const std::string& data,
 | 
				
			||||||
 | 
					                            bool headersOnly,
 | 
				
			||||||
 | 
					                            int connectTimeout,
 | 
				
			||||||
 | 
					                            int transferTimeout,
 | 
				
			||||||
 | 
					                            bool followRedirects,
 | 
				
			||||||
 | 
					                            int maxRedirects,
 | 
				
			||||||
 | 
					                            bool verbose,
 | 
				
			||||||
 | 
					                            bool save,
 | 
				
			||||||
 | 
					                            const std::string& output,
 | 
				
			||||||
 | 
					                            bool compress);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_ping_pong_main(const std::string& url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_echo_server_main(int port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_broadcast_server_main(int port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_chat_main(const std::string& url,
 | 
				
			||||||
 | 
					                     const std::string& user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_connect_main(const std::string& url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_receive_main(const std::string& url,
 | 
				
			||||||
 | 
					                        bool enablePerMessageDeflate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_transfer_main(int port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_send_main(const std::string& url,
 | 
				
			||||||
 | 
					                     const std::string& path);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										72
									
								
								ws/ws_broadcast_server.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								ws/ws_broadcast_server.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws_broadcast_server.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocketServer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ws_broadcast_server_main(int port)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << "Listening on port " << port << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ix::WebSocketServer server(port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.setOnConnectionCallback(
 | 
				
			||||||
 | 
					            [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                webSocket->setOnMessageCallback(
 | 
				
			||||||
 | 
					                    [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
				
			||||||
 | 
					                       const std::string& str,
 | 
				
			||||||
 | 
					                       size_t wireSize,
 | 
				
			||||||
 | 
					                       const ix::WebSocketErrorInfo& error,
 | 
				
			||||||
 | 
					                       const ix::WebSocketOpenInfo& openInfo,
 | 
				
			||||||
 | 
					                       const ix::WebSocketCloseInfo& closeInfo)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (messageType == ix::WebSocket_MessageType_Open)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "New connection" << std::endl;
 | 
				
			||||||
 | 
					                            std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
				
			||||||
 | 
					                            std::cerr << "Headers:" << std::endl;
 | 
				
			||||||
 | 
					                            for (auto it : openInfo.headers)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                std::cerr << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (messageType == ix::WebSocket_MessageType_Close)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "Closed connection" << std::endl;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (messageType == ix::WebSocket_MessageType_Message)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "Received " << wireSize << " bytes" << std::endl;
 | 
				
			||||||
 | 
					                            for (auto&& client : server.getClients())
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                if (client != webSocket)
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    client->send(str);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto res = server.listen();
 | 
				
			||||||
 | 
					        if (!res.first)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cerr << res.second << std::endl;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.start();
 | 
				
			||||||
 | 
					        server.wait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 *  cmd_websocket_chat.cpp
 | 
					 *  ws_chat.cpp
 | 
				
			||||||
 *  Author: Benjamin Sergeant
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
					 *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Simple chat program that talks to the node.js server at
 | 
					// Simple chat program that talks to a broadcast server
 | 
				
			||||||
// websocket_chat_server/broacast-server.js
 | 
					// Broadcast server can be ran with `ws broadcast_server`
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
@@ -20,19 +20,13 @@
 | 
				
			|||||||
// for convenience
 | 
					// for convenience
 | 
				
			||||||
using json = nlohmann::json;
 | 
					using json = nlohmann::json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace ix;
 | 
					namespace ix
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    void log(const std::string& msg)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::cout << msg << std::endl;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class WebSocketChat
 | 
					    class WebSocketChat
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public:
 | 
					        public:
 | 
				
			||||||
            WebSocketChat(const std::string& user);
 | 
					            WebSocketChat(const std::string& url,
 | 
				
			||||||
 | 
					                          const std::string& user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            void subscribe(const std::string& channel);
 | 
					            void subscribe(const std::string& channel);
 | 
				
			||||||
            void start();
 | 
					            void start();
 | 
				
			||||||
@@ -46,19 +40,27 @@ namespace
 | 
				
			|||||||
            std::pair<std::string, std::string> decodeMessage(const std::string& str);
 | 
					            std::pair<std::string, std::string> decodeMessage(const std::string& str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private:
 | 
					        private:
 | 
				
			||||||
 | 
					            std::string _url;
 | 
				
			||||||
            std::string _user;
 | 
					            std::string _user;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            ix::WebSocket _webSocket;
 | 
					            ix::WebSocket _webSocket;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            std::queue<std::string> _receivedQueue;
 | 
					            std::queue<std::string> _receivedQueue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void log(const std::string& msg);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WebSocketChat::WebSocketChat(const std::string& user) :
 | 
					    WebSocketChat::WebSocketChat(const std::string& url,
 | 
				
			||||||
 | 
					                                 const std::string& user) :
 | 
				
			||||||
 | 
					        _url(url),
 | 
				
			||||||
        _user(user)
 | 
					        _user(user)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ;
 | 
					        ;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketChat::log(const std::string& msg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << msg << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    size_t WebSocketChat::getReceivedMessagesCount() const
 | 
					    size_t WebSocketChat::getReceivedMessagesCount() const
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return _receivedQueue.size();
 | 
					        return _receivedQueue.size();
 | 
				
			||||||
@@ -76,11 +78,10 @@ namespace
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void WebSocketChat::start()
 | 
					    void WebSocketChat::start()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::string url("ws://localhost:8080/");
 | 
					        _webSocket.setUrl(_url);
 | 
				
			||||||
        _webSocket.setUrl(url);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::stringstream ss;
 | 
					        std::stringstream ss;
 | 
				
			||||||
        log(std::string("Connecting to url: ") + url);
 | 
					        log(std::string("Connecting to url: ") + _url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _webSocket.setOnMessageCallback(
 | 
					        _webSocket.setOnMessageCallback(
 | 
				
			||||||
            [this](ix::WebSocketMessageType messageType,
 | 
					            [this](ix::WebSocketMessageType messageType,
 | 
				
			||||||
@@ -164,10 +165,11 @@ namespace
 | 
				
			|||||||
        _webSocket.send(encodeMessage(text));
 | 
					        _webSocket.send(encodeMessage(text));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void interactiveMain(const std::string& user)
 | 
					    int ws_chat_main(const std::string& url,
 | 
				
			||||||
 | 
					                     const std::string& 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 webSocketChat(url, user);
 | 
				
			||||||
        webSocketChat.start();
 | 
					        webSocketChat.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while (true)
 | 
					        while (true)
 | 
				
			||||||
@@ -188,16 +190,3 @@ namespace
 | 
				
			|||||||
        webSocketChat.stop();
 | 
					        webSocketChat.stop();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char** argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    std::string user("user");
 | 
					 | 
				
			||||||
    if (argc == 2)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        user = argv[1];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Socket::init();
 | 
					 | 
				
			||||||
    interactiveMain(user);
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -9,15 +9,8 @@
 | 
				
			|||||||
#include <ixwebsocket/IXWebSocket.h>
 | 
					#include <ixwebsocket/IXWebSocket.h>
 | 
				
			||||||
#include <ixwebsocket/IXSocket.h>
 | 
					#include <ixwebsocket/IXSocket.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace ix;
 | 
					namespace ix
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    void log(const std::string& msg)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::cout << msg << std::endl;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class WebSocketConnect
 | 
					    class WebSocketConnect
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public:
 | 
					        public:
 | 
				
			||||||
@@ -32,6 +25,8 @@ namespace
 | 
				
			|||||||
        private:
 | 
					        private:
 | 
				
			||||||
            std::string _url;
 | 
					            std::string _url;
 | 
				
			||||||
            ix::WebSocket _webSocket;
 | 
					            ix::WebSocket _webSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void log(const std::string& msg);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WebSocketConnect::WebSocketConnect(const std::string& url) :
 | 
					    WebSocketConnect::WebSocketConnect(const std::string& url) :
 | 
				
			||||||
@@ -40,6 +35,11 @@ namespace
 | 
				
			|||||||
        ;
 | 
					        ;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketConnect::log(const std::string& msg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << msg << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void WebSocketConnect::stop()
 | 
					    void WebSocketConnect::stop()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _webSocket.stop();
 | 
					        _webSocket.stop();
 | 
				
			||||||
@@ -148,18 +148,12 @@ namespace
 | 
				
			|||||||
        std::cout << std::endl;
 | 
					        std::cout << std::endl;
 | 
				
			||||||
        webSocketChat.stop();
 | 
					        webSocketChat.stop();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char** argv)
 | 
					    int ws_connect_main(const std::string& url)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    if (argc != 2)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::cerr << "Usage: ws_connect <url>" << std::endl;
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    std::string url = argv[1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Socket::init();
 | 
					        Socket::init();
 | 
				
			||||||
        interactiveMain(url);
 | 
					        interactiveMain(url);
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								ws/ws_echo_server.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								ws/ws_echo_server.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws_broadcast_server.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocketServer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ws_echo_server_main(int port)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << "Listening on port " << port << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ix::WebSocketServer server(port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.setOnConnectionCallback(
 | 
				
			||||||
 | 
					            [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                webSocket->setOnMessageCallback(
 | 
				
			||||||
 | 
					                    [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
				
			||||||
 | 
					                       const std::string& str,
 | 
				
			||||||
 | 
					                       size_t wireSize,
 | 
				
			||||||
 | 
					                       const ix::WebSocketErrorInfo& error,
 | 
				
			||||||
 | 
					                       const ix::WebSocketOpenInfo& openInfo,
 | 
				
			||||||
 | 
					                       const ix::WebSocketCloseInfo& closeInfo)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (messageType == ix::WebSocket_MessageType_Open)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "New connection" << std::endl;
 | 
				
			||||||
 | 
					                            std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
				
			||||||
 | 
					                            std::cerr << "Headers:" << std::endl;
 | 
				
			||||||
 | 
					                            for (auto it : openInfo.headers)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                std::cerr << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (messageType == ix::WebSocket_MessageType_Close)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "Closed connection" << std::endl;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (messageType == ix::WebSocket_MessageType_Message)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "Received "
 | 
				
			||||||
 | 
					                                      << wireSize << " bytes"
 | 
				
			||||||
 | 
					                                      << std::endl;
 | 
				
			||||||
 | 
					                            webSocket->send(str);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto res = server.listen();
 | 
				
			||||||
 | 
					        if (!res.first)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cerr << res.second << std::endl;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.start();
 | 
				
			||||||
 | 
					        server.wait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										183
									
								
								ws/ws_http_client.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								ws/ws_http_client.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  http_client.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXHttpClient.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocketHttpHeaders.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::string extractFilename(const std::string& path)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::string::size_type idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        idx = path.rfind('/');
 | 
				
			||||||
 | 
					        if (idx != std::string::npos)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string filename = path.substr(idx+1);
 | 
				
			||||||
 | 
					            return filename;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return path;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    WebSocketHttpHeaders parseHeaders(const std::string& data)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Split by \n
 | 
				
			||||||
 | 
					        std::string token;
 | 
				
			||||||
 | 
					        std::stringstream tokenStream(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (std::getline(tokenStream, token))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::size_t pos = token.rfind(':');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Bail out if last '.' is found
 | 
				
			||||||
 | 
					            if (pos == std::string::npos) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto key = token.substr(0, pos);
 | 
				
			||||||
 | 
					            auto val = token.substr(pos+2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::cerr << key << ": " << val << std::endl;
 | 
				
			||||||
 | 
					            headers[key] = val;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return headers;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // Useful endpoint to test HTTP post
 | 
				
			||||||
 | 
					    // https://postman-echo.com/post
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    HttpParameters parsePostParameters(const std::string& data)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        HttpParameters httpParameters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Split by \n
 | 
				
			||||||
 | 
					        std::string token;
 | 
				
			||||||
 | 
					        std::stringstream tokenStream(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (std::getline(tokenStream, token))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::size_t pos = token.rfind('=');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Bail out if last '.' is found
 | 
				
			||||||
 | 
					            if (pos == std::string::npos) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto key = token.substr(0, pos);
 | 
				
			||||||
 | 
					            auto val = token.substr(pos+1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::cerr << key << ": " << val << std::endl;
 | 
				
			||||||
 | 
					            httpParameters[key] = val;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return httpParameters;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_http_client_main(const std::string& url,
 | 
				
			||||||
 | 
					                            const std::string& headersData,
 | 
				
			||||||
 | 
					                            const std::string& data,
 | 
				
			||||||
 | 
					                            bool headersOnly,
 | 
				
			||||||
 | 
					                            int connectTimeout,
 | 
				
			||||||
 | 
					                            int transferTimeout,
 | 
				
			||||||
 | 
					                            bool followRedirects,
 | 
				
			||||||
 | 
					                            int maxRedirects,
 | 
				
			||||||
 | 
					                            bool verbose,
 | 
				
			||||||
 | 
					                            bool save,
 | 
				
			||||||
 | 
					                            const std::string& output,
 | 
				
			||||||
 | 
					                            bool compress)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        HttpRequestArgs args;
 | 
				
			||||||
 | 
					        args.extraHeaders = parseHeaders(headersData);
 | 
				
			||||||
 | 
					        args.connectTimeout = connectTimeout;
 | 
				
			||||||
 | 
					        args.transferTimeout = transferTimeout;
 | 
				
			||||||
 | 
					        args.followRedirects = followRedirects;
 | 
				
			||||||
 | 
					        args.maxRedirects = maxRedirects;
 | 
				
			||||||
 | 
					        args.verbose = verbose;
 | 
				
			||||||
 | 
					        args.compress = compress;
 | 
				
			||||||
 | 
					        args.logger = [](const std::string& msg)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cout << msg;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HttpParameters httpParameters = parsePostParameters(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HttpClient httpClient;
 | 
				
			||||||
 | 
					        HttpResponse out;
 | 
				
			||||||
 | 
					        if (headersOnly)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            out = httpClient.head(url, args);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (data.empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            out = httpClient.get(url, args);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            out = httpClient.post(url, httpParameters, args);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto statusCode = std::get<0>(out);
 | 
				
			||||||
 | 
					        auto errorCode = std::get<1>(out);
 | 
				
			||||||
 | 
					        auto responseHeaders = std::get<2>(out);
 | 
				
			||||||
 | 
					        auto payload = std::get<3>(out);
 | 
				
			||||||
 | 
					        auto errorMsg = std::get<4>(out);
 | 
				
			||||||
 | 
					        auto uploadSize = std::get<5>(out);
 | 
				
			||||||
 | 
					        auto downloadSize = std::get<6>(out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (auto it : responseHeaders)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cerr << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cerr << "Upload size: " << uploadSize << std::endl;
 | 
				
			||||||
 | 
					        std::cerr << "Download size: " << downloadSize << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cerr << "Status: " << statusCode << std::endl;
 | 
				
			||||||
 | 
					        if (errorCode != HttpErrorCode_Ok)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cerr << "error message: " << errorMsg << std::endl;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!headersOnly && errorCode == HttpErrorCode_Ok)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (save || !output.empty())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // FIMXE we should decode the url first
 | 
				
			||||||
 | 
					                std::string filename = extractFilename(url);
 | 
				
			||||||
 | 
					                if (!output.empty())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    filename = output;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                std::cout << "Writing to disk: " << filename << std::endl;
 | 
				
			||||||
 | 
					                std::ofstream out(filename);
 | 
				
			||||||
 | 
					                out.write((char*)&payload.front(), payload.size());
 | 
				
			||||||
 | 
					                out.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (responseHeaders["Content-Type"] != "application/octet-stream")
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    std::cout << "payload: " << payload << std::endl;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    std::cerr << "Binary output can mess up your terminal." << std::endl;
 | 
				
			||||||
 | 
					                    std::cerr << "Use the -O flag to save the file to disk." << std::endl;
 | 
				
			||||||
 | 
					                    std::cerr << "You can also use the --output option to specify a filename." << std::endl;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 *  ping_pong.cpp
 | 
					 *  ws_ping_pong.cpp
 | 
				
			||||||
 *  Author: Benjamin Sergeant
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
					 *  Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
@@ -9,15 +9,8 @@
 | 
				
			|||||||
#include <ixwebsocket/IXWebSocket.h>
 | 
					#include <ixwebsocket/IXWebSocket.h>
 | 
				
			||||||
#include <ixwebsocket/IXSocket.h>
 | 
					#include <ixwebsocket/IXSocket.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace ix;
 | 
					namespace ix
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    void log(const std::string& msg)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        std::cout << msg << std::endl;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class WebSocketPingPong
 | 
					    class WebSocketPingPong
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public:
 | 
					        public:
 | 
				
			||||||
@@ -33,6 +26,8 @@ namespace
 | 
				
			|||||||
        private:
 | 
					        private:
 | 
				
			||||||
            std::string _url;
 | 
					            std::string _url;
 | 
				
			||||||
            ix::WebSocket _webSocket;
 | 
					            ix::WebSocket _webSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void log(const std::string& msg);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WebSocketPingPong::WebSocketPingPong(const std::string& url) :
 | 
					    WebSocketPingPong::WebSocketPingPong(const std::string& url) :
 | 
				
			||||||
@@ -41,6 +36,11 @@ namespace
 | 
				
			|||||||
        ;
 | 
					        ;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketPingPong::log(const std::string& msg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << msg << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void WebSocketPingPong::stop()
 | 
					    void WebSocketPingPong::stop()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        _webSocket.stop();
 | 
					        _webSocket.stop();
 | 
				
			||||||
@@ -124,7 +124,7 @@ namespace
 | 
				
			|||||||
        _webSocket.send(text);
 | 
					        _webSocket.send(text);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void interactiveMain(const std::string& url)
 | 
					    int ws_ping_pong_main(const std::string& url)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
 | 
					        std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
 | 
				
			||||||
        WebSocketPingPong webSocketPingPong(url);
 | 
					        WebSocketPingPong webSocketPingPong(url);
 | 
				
			||||||
@@ -155,17 +155,3 @@ namespace
 | 
				
			|||||||
        webSocketPingPong.stop();
 | 
					        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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										251
									
								
								ws/ws_receive.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								ws/ws_receive.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,251 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws_receiver.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <chrono>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocket.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXSocket.h>
 | 
				
			||||||
 | 
					#include <ixcrypto/IXUuid.h>
 | 
				
			||||||
 | 
					#include <ixcrypto/IXBase64.h>
 | 
				
			||||||
 | 
					#include <ixcrypto/IXHash.h>
 | 
				
			||||||
 | 
					#include <msgpack11/msgpack11.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using msgpack11::MsgPack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class WebSocketReceiver
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public:
 | 
				
			||||||
 | 
					            WebSocketReceiver(const std::string& _url,
 | 
				
			||||||
 | 
					                              bool enablePerMessageDeflate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void subscribe(const std::string& channel);
 | 
				
			||||||
 | 
					            void start();
 | 
				
			||||||
 | 
					            void stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void waitForConnection();
 | 
				
			||||||
 | 
					            void waitForMessage();
 | 
				
			||||||
 | 
					            void handleMessage(const std::string& str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private:
 | 
				
			||||||
 | 
					            std::string _url;
 | 
				
			||||||
 | 
					            std::string _id;
 | 
				
			||||||
 | 
					            ix::WebSocket _webSocket;
 | 
				
			||||||
 | 
					            bool _enablePerMessageDeflate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::mutex _conditionVariableMutex;
 | 
				
			||||||
 | 
					            std::condition_variable _condition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::string extractFilename(const std::string& path);
 | 
				
			||||||
 | 
					            void handleError(const std::string& errMsg, const std::string& id);
 | 
				
			||||||
 | 
					            void log(const std::string& msg);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    WebSocketReceiver::WebSocketReceiver(const std::string& url,
 | 
				
			||||||
 | 
					                                         bool enablePerMessageDeflate) :
 | 
				
			||||||
 | 
					        _url(url),
 | 
				
			||||||
 | 
					        _enablePerMessageDeflate(enablePerMessageDeflate)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketReceiver::stop()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _webSocket.stop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketReceiver::log(const std::string& msg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << msg << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketReceiver::waitForConnection()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << "Connecting..." << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::unique_lock<std::mutex> lock(_conditionVariableMutex);
 | 
				
			||||||
 | 
					        _condition.wait(lock);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketReceiver::waitForMessage()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << "Waiting for message..." << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::unique_lock<std::mutex> lock(_conditionVariableMutex);
 | 
				
			||||||
 | 
					        _condition.wait(lock);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We should cleanup the file name and full path further to remove .. as well
 | 
				
			||||||
 | 
					    std::string WebSocketReceiver::extractFilename(const std::string& path)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::string::size_type idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        idx = path.rfind('/');
 | 
				
			||||||
 | 
					        if (idx != std::string::npos)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::string filename = path.substr(idx+1);
 | 
				
			||||||
 | 
					            return filename;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return path;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketReceiver::handleError(const std::string& errMsg,
 | 
				
			||||||
 | 
					                                        const std::string& id)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::map<MsgPack, MsgPack> pdu;
 | 
				
			||||||
 | 
					        pdu["kind"] = "error";
 | 
				
			||||||
 | 
					        pdu["id"] = id;
 | 
				
			||||||
 | 
					        pdu["message"] = errMsg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MsgPack msg(pdu);
 | 
				
			||||||
 | 
					        _webSocket.send(msg.dump());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketReceiver::handleMessage(const std::string& str)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cerr << "Received message: " << str.size() << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string errMsg;
 | 
				
			||||||
 | 
					        MsgPack data = MsgPack::parse(str, errMsg);
 | 
				
			||||||
 | 
					        if (!errMsg.empty())
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            handleError("Invalid MsgPack", std::string());
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cout << "id: " << data["id"].string_value() << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::vector<uint8_t> content = data["content"].binary_items();
 | 
				
			||||||
 | 
					        std::cout << "Content size: " << content.size() << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Validate checksum
 | 
				
			||||||
 | 
					        uint64_t cksum = ix::djb2Hash(content);
 | 
				
			||||||
 | 
					        auto cksumRef = data["djb2_hash"].string_value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cout << "Computed hash: " << cksum << std::endl;
 | 
				
			||||||
 | 
					        std::cout << "Reference hash: " << cksumRef << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (std::to_string(cksum) != cksumRef)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            handleError("Hash mismatch.", std::string());
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string filename = data["filename"].string_value();
 | 
				
			||||||
 | 
					        filename = extractFilename(filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cout << "Writing to disk: " << filename << std::endl;
 | 
				
			||||||
 | 
					        std::ofstream out(filename);
 | 
				
			||||||
 | 
					        out.write((char*)&content.front(), content.size());
 | 
				
			||||||
 | 
					        out.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::map<MsgPack, MsgPack> pdu;
 | 
				
			||||||
 | 
					        pdu["ack"] = true;
 | 
				
			||||||
 | 
					        pdu["id"] = data["id"];
 | 
				
			||||||
 | 
					        pdu["filename"] = data["filename"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MsgPack msg(pdu);
 | 
				
			||||||
 | 
					        _webSocket.send(msg.dump());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketReceiver::start()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _webSocket.setUrl(_url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
 | 
				
			||||||
 | 
					            _enablePerMessageDeflate, false, false, 15, 15);
 | 
				
			||||||
 | 
					        _webSocket.setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        log(std::string("Connecting to url: ") + _url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _webSocket.setOnMessageCallback(
 | 
				
			||||||
 | 
					            [this](ix::WebSocketMessageType messageType,
 | 
				
			||||||
 | 
					               const std::string& str,
 | 
				
			||||||
 | 
					               size_t wireSize,
 | 
				
			||||||
 | 
					               const ix::WebSocketErrorInfo& error,
 | 
				
			||||||
 | 
					               const ix::WebSocketOpenInfo& openInfo,
 | 
				
			||||||
 | 
					               const ix::WebSocketCloseInfo& closeInfo)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::stringstream ss;
 | 
				
			||||||
 | 
					                if (messageType == ix::WebSocket_MessageType_Open)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _condition.notify_one();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    log("ws_receive: connected");
 | 
				
			||||||
 | 
					                    std::cout << "Uri: " << openInfo.uri << std::endl;
 | 
				
			||||||
 | 
					                    std::cout << "Handshake Headers:" << std::endl;
 | 
				
			||||||
 | 
					                    for (auto it : openInfo.headers)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        std::cout << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (messageType == ix::WebSocket_MessageType_Close)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ss << "ws_receive: connection closed:";
 | 
				
			||||||
 | 
					                    ss << " code " << closeInfo.code;
 | 
				
			||||||
 | 
					                    ss << " reason " << closeInfo.reason << std::endl;
 | 
				
			||||||
 | 
					                    log(ss.str());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (messageType == ix::WebSocket_MessageType_Message)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ss << "ws_receive: transfered " << wireSize << " bytes";
 | 
				
			||||||
 | 
					                    log(ss.str());
 | 
				
			||||||
 | 
					                    handleMessage(str);
 | 
				
			||||||
 | 
					                    _condition.notify_one();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                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 wsReceive(const std::string& url,
 | 
				
			||||||
 | 
					                   bool enablePerMessageDeflate)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        WebSocketReceiver webSocketReceiver(url, enablePerMessageDeflate);
 | 
				
			||||||
 | 
					        webSocketReceiver.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        webSocketReceiver.waitForConnection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        webSocketReceiver.waitForMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::chrono::duration<double, std::milli> duration(1000);
 | 
				
			||||||
 | 
					        std::this_thread::sleep_for(duration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cout << "Done !" << std::endl;
 | 
				
			||||||
 | 
					        webSocketReceiver.stop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_receive_main(const std::string& url,
 | 
				
			||||||
 | 
					                        bool enablePerMessageDeflate)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Socket::init();
 | 
				
			||||||
 | 
					        wsReceive(url, enablePerMessageDeflate);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										296
									
								
								ws/ws_send.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								ws/ws_send.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,296 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws_send.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <chrono>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocket.h>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXSocket.h>
 | 
				
			||||||
 | 
					#include <ixcrypto/IXUuid.h>
 | 
				
			||||||
 | 
					#include <ixcrypto/IXBase64.h>
 | 
				
			||||||
 | 
					#include <ixcrypto/IXHash.h>
 | 
				
			||||||
 | 
					#include <msgpack11/msgpack11.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using msgpack11::MsgPack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    class WebSocketSender
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public:
 | 
				
			||||||
 | 
					            WebSocketSender(const std::string& _url,
 | 
				
			||||||
 | 
					                            bool enablePerMessageDeflate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void subscribe(const std::string& channel);
 | 
				
			||||||
 | 
					            void start();
 | 
				
			||||||
 | 
					            void stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void waitForConnection();
 | 
				
			||||||
 | 
					            void waitForAck();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void sendMessage(const std::string& filename, bool throttle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private:
 | 
				
			||||||
 | 
					            std::string _url;
 | 
				
			||||||
 | 
					            std::string _id;
 | 
				
			||||||
 | 
					            ix::WebSocket _webSocket;
 | 
				
			||||||
 | 
					            bool _enablePerMessageDeflate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::mutex _conditionVariableMutex;
 | 
				
			||||||
 | 
					            std::condition_variable _condition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void log(const std::string& msg);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    WebSocketSender::WebSocketSender(const std::string& url,
 | 
				
			||||||
 | 
					                                     bool enablePerMessageDeflate) :
 | 
				
			||||||
 | 
					        _url(url),
 | 
				
			||||||
 | 
					        _enablePerMessageDeflate(enablePerMessageDeflate)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketSender::stop()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _webSocket.stop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketSender::log(const std::string& msg)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << msg << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketSender::waitForConnection()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << "Connecting..." << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::unique_lock<std::mutex> lock(_conditionVariableMutex);
 | 
				
			||||||
 | 
					        _condition.wait(lock);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketSender::waitForAck()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << "Waiting for ack..." << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::unique_lock<std::mutex> lock(_conditionVariableMutex);
 | 
				
			||||||
 | 
					        _condition.wait(lock);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<uint8_t> load(const std::string& path)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::vector<uint8_t> memblock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::ifstream file(path);
 | 
				
			||||||
 | 
					        if (!file.is_open()) return memblock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        file.seekg(0, file.end);
 | 
				
			||||||
 | 
					        std::streamoff size = file.tellg();
 | 
				
			||||||
 | 
					        file.seekg(0, file.beg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        memblock.resize(size);
 | 
				
			||||||
 | 
					        file.read((char*)&memblock.front(), static_cast<std::streamsize>(size));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return memblock;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketSender::start()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _webSocket.setUrl(_url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
 | 
				
			||||||
 | 
					            _enablePerMessageDeflate, false, false, 15, 15);
 | 
				
			||||||
 | 
					        _webSocket.setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        log(std::string("Connecting to url: ") + _url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _webSocket.setOnMessageCallback(
 | 
				
			||||||
 | 
					            [this](ix::WebSocketMessageType messageType,
 | 
				
			||||||
 | 
					               const std::string& str,
 | 
				
			||||||
 | 
					               size_t wireSize,
 | 
				
			||||||
 | 
					               const ix::WebSocketErrorInfo& error,
 | 
				
			||||||
 | 
					               const ix::WebSocketOpenInfo& openInfo,
 | 
				
			||||||
 | 
					               const ix::WebSocketCloseInfo& closeInfo)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::stringstream ss;
 | 
				
			||||||
 | 
					                if (messageType == ix::WebSocket_MessageType_Open)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _condition.notify_one();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    log("ws_send: connected");
 | 
				
			||||||
 | 
					                    std::cout << "Uri: " << openInfo.uri << std::endl;
 | 
				
			||||||
 | 
					                    std::cout << "Handshake Headers:" << std::endl;
 | 
				
			||||||
 | 
					                    for (auto it : openInfo.headers)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        std::cout << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (messageType == ix::WebSocket_MessageType_Close)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ss << "ws_send: connection closed:";
 | 
				
			||||||
 | 
					                    ss << " code " << closeInfo.code;
 | 
				
			||||||
 | 
					                    ss << " reason " << closeInfo.reason << std::endl;
 | 
				
			||||||
 | 
					                    log(ss.str());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (messageType == ix::WebSocket_MessageType_Message)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _condition.notify_one();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    ss << "ws_send: received message (" << wireSize << " bytes)";
 | 
				
			||||||
 | 
					                    log(ss.str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    std::string errMsg;
 | 
				
			||||||
 | 
					                    MsgPack data = MsgPack::parse(str, errMsg);
 | 
				
			||||||
 | 
					                    if (!errMsg.empty())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        std::cerr << "Invalid MsgPack response" << std::endl;
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    std::string id = data["id"].string_value();
 | 
				
			||||||
 | 
					                    if (_id != id)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        std::cerr << "Invalid id" << std::endl;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                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();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Bench
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public:
 | 
				
			||||||
 | 
					            Bench(const std::string& description) :
 | 
				
			||||||
 | 
					                _description(description),
 | 
				
			||||||
 | 
					                _start(std::chrono::system_clock::now()),
 | 
				
			||||||
 | 
					                _reported(false)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ~Bench()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (!_reported)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    report();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void report()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                auto now = std::chrono::system_clock::now();
 | 
				
			||||||
 | 
					                auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - _start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _ms = milliseconds.count();
 | 
				
			||||||
 | 
					                std::cout << _description << " completed in "
 | 
				
			||||||
 | 
					                          << _ms << "ms" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _reported = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            uint64_t getDuration() const
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return _ms;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private:
 | 
				
			||||||
 | 
					            std::string _description;
 | 
				
			||||||
 | 
					            std::chrono::time_point<std::chrono::system_clock> _start;
 | 
				
			||||||
 | 
					            uint64_t _ms;
 | 
				
			||||||
 | 
					            bool _reported;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void WebSocketSender::sendMessage(const std::string& filename,
 | 
				
			||||||
 | 
					                                      bool throttle)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::vector<uint8_t> content;
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Bench bench("load file from disk");
 | 
				
			||||||
 | 
					            content = load(filename);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _id = uuid4();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::map<MsgPack, MsgPack> pdu;
 | 
				
			||||||
 | 
					        pdu["kind"] = "send";
 | 
				
			||||||
 | 
					        pdu["id"] = _id;
 | 
				
			||||||
 | 
					        pdu["content"] = content;
 | 
				
			||||||
 | 
					        auto hash = djb2Hash(content);
 | 
				
			||||||
 | 
					        pdu["djb2_hash"] = std::to_string(hash);
 | 
				
			||||||
 | 
					        pdu["filename"] = filename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MsgPack msg(pdu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Bench bench("Sending file through websocket");
 | 
				
			||||||
 | 
					        _webSocket.send(msg.dump(),
 | 
				
			||||||
 | 
					                        [throttle](int current, int total) -> bool
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cout << "Step " << current << " out of " << total << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (throttle)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::chrono::duration<double, std::milli> duration(10);
 | 
				
			||||||
 | 
					                std::this_thread::sleep_for(duration);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bench.report();
 | 
				
			||||||
 | 
					        auto duration = bench.getDuration();
 | 
				
			||||||
 | 
					        auto transferRate = 1000 * content.size() / duration;
 | 
				
			||||||
 | 
					        transferRate /= (1024 * 1024);
 | 
				
			||||||
 | 
					        std::cout << "Send transfer rate: " << transferRate << "MB/s" << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void wsSend(const std::string& url,
 | 
				
			||||||
 | 
					                const std::string& path,
 | 
				
			||||||
 | 
					                bool enablePerMessageDeflate,
 | 
				
			||||||
 | 
					                bool throttle)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        WebSocketSender webSocketSender(url, enablePerMessageDeflate);
 | 
				
			||||||
 | 
					        webSocketSender.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        webSocketSender.waitForConnection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cout << "Sending..." << std::endl;
 | 
				
			||||||
 | 
					        webSocketSender.sendMessage(path, throttle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        webSocketSender.waitForAck();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cout << "Done !" << std::endl;
 | 
				
			||||||
 | 
					        webSocketSender.stop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ws_send_main(const std::string& url,
 | 
				
			||||||
 | 
					                     const std::string& path)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        bool throttle = false;
 | 
				
			||||||
 | 
					        bool enablePerMessageDeflate = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Socket::init();
 | 
				
			||||||
 | 
					        wsSend(url, path, enablePerMessageDeflate, throttle);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										72
									
								
								ws/ws_transfer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								ws/ws_transfer.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  ws_transfer.cpp
 | 
				
			||||||
 | 
					 *  Author: Benjamin Sergeant
 | 
				
			||||||
 | 
					 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <ixwebsocket/IXWebSocketServer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ix
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ws_transfer_main(int port)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        std::cout << "Listening on port " << port << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ix::WebSocketServer server(port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.setOnConnectionCallback(
 | 
				
			||||||
 | 
					            [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                webSocket->setOnMessageCallback(
 | 
				
			||||||
 | 
					                    [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
				
			||||||
 | 
					                       const std::string& str,
 | 
				
			||||||
 | 
					                       size_t wireSize,
 | 
				
			||||||
 | 
					                       const ix::WebSocketErrorInfo& error,
 | 
				
			||||||
 | 
					                       const ix::WebSocketOpenInfo& openInfo,
 | 
				
			||||||
 | 
					                       const ix::WebSocketCloseInfo& closeInfo)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (messageType == ix::WebSocket_MessageType_Open)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "New connection" << std::endl;
 | 
				
			||||||
 | 
					                            std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
				
			||||||
 | 
					                            std::cerr << "Headers:" << std::endl;
 | 
				
			||||||
 | 
					                            for (auto it : openInfo.headers)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                std::cerr << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (messageType == ix::WebSocket_MessageType_Close)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "Closed connection" << std::endl;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (messageType == ix::WebSocket_MessageType_Message)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            std::cerr << "Received " << wireSize << " bytes" << std::endl;
 | 
				
			||||||
 | 
					                            for (auto&& client : server.getClients())
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                if (client != webSocket)
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    client->send(str);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto res = server.listen();
 | 
				
			||||||
 | 
					        if (!res.first)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cerr << res.second << std::endl;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        server.start();
 | 
				
			||||||
 | 
					        server.wait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user