Compare commits
26 Commits
bsergean-p
...
feature/or
Author | SHA1 | Date | |
---|---|---|---|
4027f9282b | |||
eb9a7bed76 | |||
d20864d7d1 | |||
f184a7adef | |||
dc7b986e10 | |||
1e3560014f | |||
9157873f5b | |||
aa2ca19895 | |||
6cc21f3658 | |||
679ce519dd | |||
a5d4911a16 | |||
b0fd119d14 | |||
472cf68c31 | |||
1e46466114 | |||
0b8b5608dc | |||
20a028e2ae | |||
8d7b557be6 | |||
e417e63605 | |||
7b1524d7ec | |||
e8048ad826 | |||
2b40a30c8f | |||
d7bfe89e43 | |||
84aa652846 | |||
edb6ded99f | |||
2f560ff4c0 | |||
002d9c8985 |
19
.github/workflows/stale.yml
vendored
19
.github/workflows/stale.yml
vendored
@ -1,19 +0,0 @@
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'Stale issue message'
|
||||
stale-pr-message: 'Stale pull request message'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
@ -11,6 +11,7 @@ project(ixwebsocket C CXX)
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
set (CXX_STANDARD_REQUIRED ON)
|
||||
set (CMAKE_CXX_EXTENSIONS OFF)
|
||||
set (CMAKE_EXPORT_COMPILE_COMMANDS yes)
|
||||
|
||||
option (BUILD_DEMO OFF)
|
||||
|
||||
@ -66,6 +67,7 @@ set( IXWEBSOCKET_SOURCES
|
||||
)
|
||||
|
||||
set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXBase64.h
|
||||
ixwebsocket/IXBench.h
|
||||
ixwebsocket/IXCancellationRequest.h
|
||||
ixwebsocket/IXConnectionState.h
|
||||
@ -249,7 +251,7 @@ if (WIN32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (UNIX)
|
||||
if (UNIX AND NOT APPLE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
find_package(Threads)
|
||||
target_link_libraries(ixwebsocket PRIVATE Threads::Threads)
|
||||
@ -285,8 +287,8 @@ if (IXWEBSOCKET_INSTALL)
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/
|
||||
)
|
||||
|
||||
configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY)
|
||||
install(FILES "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket")
|
||||
configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket")
|
||||
|
||||
install(EXPORT ixwebsocket
|
||||
FILE ixwebsocket-targets.cmake
|
||||
|
@ -35,6 +35,7 @@ int main()
|
||||
|
||||
// Connect to a server with encryption
|
||||
// See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration
|
||||
// https://github.com/machinezone/IXWebSocket/issues/386#issuecomment-1105235227 (self signed certificates)
|
||||
std::string url("wss://echo.websocket.org");
|
||||
webSocket.setUrl(url);
|
||||
|
||||
|
@ -2,6 +2,12 @@
|
||||
|
||||
All changes to this project will be documented in this file.
|
||||
|
||||
## [11.4.3] - 2022-05-13
|
||||
|
||||
Set shorter thread names
|
||||
BoringSSL fix with SNI
|
||||
Websocket computed header is valid Base64
|
||||
|
||||
## [11.4.1] - 2022-04-23
|
||||
|
||||
vckpg + cmake fix, to handle zlib as a dependency better
|
||||
|
@ -54,7 +54,7 @@ To use the installed package within a cmake project, use the following:
|
||||
# include headers
|
||||
include_directories(${IXWEBSOCKET_INCLUDE_DIR})
|
||||
# ...
|
||||
target_link_libraries(${PROJECT_NAME} ... ${IXWEBSOCKET_LIBRARY}) # Cmake will automatically fail the generation if the lib was not found, i.e is set to NOTFOUNS
|
||||
target_link_libraries(${PROJECT_NAME} ... ${IXWEBSOCKET_LIBRARY}) # Cmake will automatically fail the generation if the lib was not found, i.e is set to NOTFOUND
|
||||
|
||||
```
|
||||
|
||||
|
@ -301,7 +301,9 @@ This api was actually changed to take a weak_ptr<WebSocket> as the first argumen
|
||||
|
||||
// Run a server on localhost at a given port.
|
||||
// Bound host name, max connections and listen backlog can also be passed in as parameters.
|
||||
ix::WebSocketServer server(port);
|
||||
int port = 8008;
|
||||
std::string host("127.0.0.1"); // If you need this server to be accessible on a different machine, use "0.0.0.0"
|
||||
ix::WebSocketServer server(port, host);
|
||||
|
||||
server.setOnConnectionCallback(
|
||||
[&server](std::weak_ptr<WebSocket> webSocket,
|
||||
@ -384,7 +386,9 @@ The webSocket reference is guaranteed to be always valid ; by design the callbac
|
||||
|
||||
// Run a server on localhost at a given port.
|
||||
// Bound host name, max connections and listen backlog can also be passed in as parameters.
|
||||
ix::WebSocketServer server(port);
|
||||
int port = 8008;
|
||||
std::string host("127.0.0.1"); // If you need this server to be accessible on a different machine, use "0.0.0.0"
|
||||
ix::WebSocketServer server(port, host);
|
||||
|
||||
server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) {
|
||||
// The ConnectionState object contains information about the connection,
|
||||
@ -624,3 +628,5 @@ For a client, specifying `caFile` can be used if connecting to a server that use
|
||||
For a server, specifying `caFile` implies that:
|
||||
1. You require clients to present a certificate
|
||||
1. It must be signed by one of the trusted roots in the file
|
||||
|
||||
By default, a destination's hostname is always validated against the certificate that it presents. To accept certificates with any hostname, set `ix::SocketTLSOptions::disable_hostname_validation` to `true`.
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
if(@USE_ZLIB@)
|
||||
if (@USE_ZLIB@)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-targets.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-targets.cmake")
|
||||
|
125
ixwebsocket/IXBase64.h
Normal file
125
ixwebsocket/IXBase64.h
Normal file
@ -0,0 +1,125 @@
|
||||
#ifndef _MACARON_BASE64_H_
|
||||
#define _MACARON_BASE64_H_
|
||||
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2016 tomykaira
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace macaron {
|
||||
|
||||
class Base64 {
|
||||
public:
|
||||
|
||||
static std::string Encode(const std::string data) {
|
||||
static constexpr char sEncodingTable[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
size_t in_len = data.size();
|
||||
size_t out_len = 4 * ((in_len + 2) / 3);
|
||||
std::string ret(out_len, '\0');
|
||||
size_t i;
|
||||
char *p = const_cast<char*>(ret.c_str());
|
||||
|
||||
for (i = 0; i < in_len - 2; i += 3) {
|
||||
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)];
|
||||
*p++ = sEncodingTable[data[i + 2] & 0x3F];
|
||||
}
|
||||
if (i < in_len) {
|
||||
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
|
||||
if (i == (in_len - 1)) {
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4)];
|
||||
*p++ = '=';
|
||||
}
|
||||
else {
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)];
|
||||
}
|
||||
*p++ = '=';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string Decode(const std::string& input, std::string& out) {
|
||||
static constexpr unsigned char kDecodingTable[] = {
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
|
||||
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
|
||||
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
|
||||
};
|
||||
|
||||
size_t in_len = input.size();
|
||||
if (in_len % 4 != 0) return "Input data size is not a multiple of 4";
|
||||
|
||||
size_t out_len = in_len / 4 * 3;
|
||||
if (input[in_len - 1] == '=') out_len--;
|
||||
if (input[in_len - 2] == '=') out_len--;
|
||||
|
||||
out.resize(out_len);
|
||||
|
||||
for (size_t i = 0, j = 0; i < in_len;) {
|
||||
uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
|
||||
uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);
|
||||
|
||||
if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF;
|
||||
if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF;
|
||||
if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* _MACARON_BASE64_H_ */
|
@ -6,7 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
|
@ -7,9 +7,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <chrono>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
// mingw build quirks
|
||||
#if defined(_WIN32) && defined(__GNUC__)
|
||||
@ -44,7 +45,7 @@ namespace ix
|
||||
;
|
||||
}
|
||||
|
||||
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
||||
DNSLookup::AddrInfoPtr DNSLookup::getAddrInfo(const std::string& hostname,
|
||||
int port,
|
||||
std::string& errMsg)
|
||||
{
|
||||
@ -63,10 +64,10 @@ namespace ix
|
||||
errMsg = gai_strerror(getaddrinfo_result);
|
||||
res = nullptr;
|
||||
}
|
||||
return res;
|
||||
return AddrInfoPtr{ res, freeaddrinfo };
|
||||
}
|
||||
|
||||
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
|
||||
DNSLookup::AddrInfoPtr DNSLookup::resolve(std::string& errMsg,
|
||||
const CancellationRequest& isCancellationRequested,
|
||||
bool cancellable)
|
||||
{
|
||||
@ -74,12 +75,7 @@ namespace ix
|
||||
: resolveUnCancellable(errMsg, isCancellationRequested);
|
||||
}
|
||||
|
||||
void DNSLookup::release(struct addrinfo* addr)
|
||||
{
|
||||
freeaddrinfo(addr);
|
||||
}
|
||||
|
||||
struct addrinfo* DNSLookup::resolveUnCancellable(
|
||||
DNSLookup::AddrInfoPtr DNSLookup::resolveUnCancellable(
|
||||
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
||||
{
|
||||
errMsg = "no error";
|
||||
@ -94,7 +90,7 @@ namespace ix
|
||||
return getAddrInfo(_hostname, _port, errMsg);
|
||||
}
|
||||
|
||||
struct addrinfo* DNSLookup::resolveCancellable(
|
||||
DNSLookup::AddrInfoPtr DNSLookup::resolveCancellable(
|
||||
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
||||
{
|
||||
errMsg = "no error";
|
||||
@ -157,7 +153,7 @@ namespace ix
|
||||
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
||||
// getAddrInfo needs to work.
|
||||
std::string errMsg;
|
||||
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
|
||||
auto res = getAddrInfo(hostname, port, errMsg);
|
||||
|
||||
if (auto lock = self.lock())
|
||||
{
|
||||
@ -181,13 +177,13 @@ namespace ix
|
||||
return _errMsg;
|
||||
}
|
||||
|
||||
void DNSLookup::setRes(struct addrinfo* addr)
|
||||
void DNSLookup::setRes(DNSLookup::AddrInfoPtr addr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_resMutex);
|
||||
_res = addr;
|
||||
_res = std::move(addr);
|
||||
}
|
||||
|
||||
struct addrinfo* DNSLookup::getRes()
|
||||
DNSLookup::AddrInfoPtr DNSLookup::getRes()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_resMutex);
|
||||
return _res;
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "IXCancellationRequest.h"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
@ -24,22 +25,21 @@ namespace ix
|
||||
class DNSLookup : public std::enable_shared_from_this<DNSLookup>
|
||||
{
|
||||
public:
|
||||
using AddrInfoPtr = std::shared_ptr<addrinfo>;
|
||||
DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait);
|
||||
~DNSLookup() = default;
|
||||
|
||||
struct addrinfo* resolve(std::string& errMsg,
|
||||
AddrInfoPtr resolve(std::string& errMsg,
|
||||
const CancellationRequest& isCancellationRequested,
|
||||
bool cancellable = true);
|
||||
|
||||
void release(struct addrinfo* addr);
|
||||
|
||||
private:
|
||||
struct addrinfo* resolveCancellable(std::string& errMsg,
|
||||
AddrInfoPtr resolveCancellable(std::string& errMsg,
|
||||
const CancellationRequest& isCancellationRequested);
|
||||
struct addrinfo* resolveUnCancellable(std::string& errMsg,
|
||||
AddrInfoPtr resolveUnCancellable(std::string& errMsg,
|
||||
const CancellationRequest& isCancellationRequested);
|
||||
|
||||
static struct addrinfo* getAddrInfo(const std::string& hostname,
|
||||
AddrInfoPtr getAddrInfo(const std::string& hostname,
|
||||
int port,
|
||||
std::string& errMsg);
|
||||
|
||||
@ -48,15 +48,15 @@ namespace ix
|
||||
void setErrMsg(const std::string& errMsg);
|
||||
const std::string& getErrMsg();
|
||||
|
||||
void setRes(struct addrinfo* addr);
|
||||
struct addrinfo* getRes();
|
||||
void setRes(AddrInfoPtr addr);
|
||||
AddrInfoPtr getRes();
|
||||
|
||||
std::string _hostname;
|
||||
int _port;
|
||||
int64_t _wait;
|
||||
const static int64_t kDefaultWait;
|
||||
|
||||
struct addrinfo* _res;
|
||||
AddrInfoPtr _res;
|
||||
std::mutex _resMutex;
|
||||
|
||||
std::string _errMsg;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "IXProgressCallback.h"
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "IXUserAgent.h"
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include <assert.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
@ -139,8 +140,9 @@ namespace ix
|
||||
|
||||
std::string protocol, host, path, query;
|
||||
int port;
|
||||
bool isProtocolDefaultPort;
|
||||
|
||||
if (!UrlParser::parse(url, protocol, host, path, query, port))
|
||||
if (!UrlParser::parse(url, protocol, host, path, query, port, isProtocolDefaultPort))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Cannot parse url: " << url;
|
||||
@ -173,7 +175,12 @@ namespace ix
|
||||
// Build request string
|
||||
std::stringstream ss;
|
||||
ss << verb << " " << path << " HTTP/1.1\r\n";
|
||||
ss << "Host: " << host << "\r\n";
|
||||
ss << "Host: " << host;
|
||||
if (!isProtocolDefaultPort)
|
||||
{
|
||||
ss << ":" << port;
|
||||
}
|
||||
ss << "\r\n";
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
if (args->compress && !args->onChunkCallback)
|
||||
@ -202,6 +209,12 @@ namespace ix
|
||||
ss << "User-Agent: " << userAgent() << "\r\n";
|
||||
}
|
||||
|
||||
// Set an origin header if missing
|
||||
if (args->extraHeaders.find("Origin") == args->extraHeaders.end())
|
||||
{
|
||||
ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n";
|
||||
}
|
||||
|
||||
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
|
||||
{
|
||||
// Set request compression header
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "IXNetSystem.h"
|
||||
#include "IXSocketConnect.h"
|
||||
#include "IXUserAgent.h"
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@ -40,6 +41,29 @@ namespace
|
||||
auto vec = res.second;
|
||||
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
|
||||
}
|
||||
|
||||
std::string response_head_file(const std::string& file_name){
|
||||
|
||||
if (std::string::npos != file_name.find(".html") || std::string::npos != file_name.find(".htm"))
|
||||
return "text/html";
|
||||
else if (std::string::npos != file_name.find(".css"))
|
||||
return "text/css";
|
||||
else if (std::string::npos != file_name.find(".js") || std::string::npos != file_name.find(".mjs"))
|
||||
return "application/x-javascript";
|
||||
else if (std::string::npos != file_name.find(".ico"))
|
||||
return "image/x-icon";
|
||||
else if (std::string::npos != file_name.find(".png"))
|
||||
return "image/png";
|
||||
else if (std::string::npos != file_name.find(".jpg") || std::string::npos != file_name.find(".jpeg"))
|
||||
return "image/jpeg";
|
||||
else if (std::string::npos != file_name.find(".gif"))
|
||||
return "image/gif";
|
||||
else if (std::string::npos != file_name.find(".svg"))
|
||||
return "image/svg+xml";
|
||||
else
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ix
|
||||
@ -51,28 +75,14 @@ namespace ix
|
||||
int backlog,
|
||||
size_t maxConnections,
|
||||
int addressFamily,
|
||||
int timeoutSecs)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _connectedClientsCount(0)
|
||||
int timeoutSecs,
|
||||
int handshakeTimeoutSecs)
|
||||
: WebSocketServer(port, host, backlog, maxConnections, handshakeTimeoutSecs, addressFamily)
|
||||
, _timeoutSecs(timeoutSecs)
|
||||
{
|
||||
setDefaultConnectionCallback();
|
||||
}
|
||||
|
||||
HttpServer::~HttpServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void HttpServer::stop()
|
||||
{
|
||||
stopAcceptingConnections();
|
||||
|
||||
// FIXME: cancelling / closing active clients ...
|
||||
|
||||
SocketServer::stop();
|
||||
}
|
||||
|
||||
void HttpServer::setOnConnectionCallback(const OnConnectionCallback& callback)
|
||||
{
|
||||
_onConnectionCallback = callback;
|
||||
@ -81,34 +91,35 @@ namespace ix
|
||||
void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
_connectedClientsCount++;
|
||||
|
||||
auto ret = Http::parseRequest(socket, _timeoutSecs);
|
||||
// FIXME: handle errors in parseRequest
|
||||
|
||||
if (std::get<0>(ret))
|
||||
{
|
||||
auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
|
||||
if (!Http::sendResponse(response, socket))
|
||||
auto request = std::get<2>(ret);
|
||||
std::shared_ptr<ix::HttpResponse> response;
|
||||
if (request->headers["Upgrade"] == "websocket")
|
||||
{
|
||||
logError("Cannot send response");
|
||||
WebSocketServer::handleUpgrade(std::move(socket), connectionState, request);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto response = _onConnectionCallback(request, connectionState);
|
||||
if (!Http::sendResponse(response, socket))
|
||||
{
|
||||
logError("Cannot send response");
|
||||
}
|
||||
}
|
||||
}
|
||||
connectionState->setTerminated();
|
||||
|
||||
_connectedClientsCount--;
|
||||
}
|
||||
|
||||
size_t HttpServer::getConnectedClientsCount()
|
||||
{
|
||||
return _connectedClientsCount;
|
||||
}
|
||||
|
||||
void HttpServer::setDefaultConnectionCallback()
|
||||
{
|
||||
setOnConnectionCallback(
|
||||
[this](HttpRequestPtr request,
|
||||
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
|
||||
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
|
||||
{
|
||||
std::string uri(request->uri);
|
||||
if (uri.empty() || uri == "/")
|
||||
{
|
||||
@ -117,6 +128,7 @@ namespace ix
|
||||
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["Server"] = userAgent();
|
||||
headers["Content-Type"] = response_head_file(uri);
|
||||
|
||||
std::string path("." + uri);
|
||||
auto res = readAsString(path);
|
||||
@ -165,9 +177,9 @@ namespace ix
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
|
||||
//
|
||||
setOnConnectionCallback(
|
||||
[this,
|
||||
redirectUrl](HttpRequestPtr request,
|
||||
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
|
||||
[this, redirectUrl](HttpRequestPtr request,
|
||||
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
|
||||
{
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["Server"] = userAgent();
|
||||
|
||||
@ -198,7 +210,8 @@ namespace ix
|
||||
{
|
||||
setOnConnectionCallback(
|
||||
[this](HttpRequestPtr request,
|
||||
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
|
||||
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr
|
||||
{
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["Server"] = userAgent();
|
||||
|
||||
|
@ -7,8 +7,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "IXHttp.h"
|
||||
#include "IXSocketServer.h"
|
||||
#include "IXWebSocket.h"
|
||||
#include "IXWebSocketServer.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class HttpServer final : public SocketServer
|
||||
class HttpServer final : public WebSocketServer
|
||||
{
|
||||
public:
|
||||
using OnConnectionCallback =
|
||||
@ -30,9 +30,8 @@ namespace ix
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
||||
int addressFamily = SocketServer::kDefaultAddressFamily,
|
||||
int timeoutSecs = HttpServer::kDefaultTimeoutSecs);
|
||||
virtual ~HttpServer();
|
||||
virtual void stop() final;
|
||||
int timeoutSecs = HttpServer::kDefaultTimeoutSecs,
|
||||
int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs);
|
||||
|
||||
void setOnConnectionCallback(const OnConnectionCallback& callback);
|
||||
|
||||
@ -41,10 +40,10 @@ namespace ix
|
||||
void makeDebugServer();
|
||||
|
||||
int getTimeoutSecs();
|
||||
|
||||
private:
|
||||
// Member variables
|
||||
OnConnectionCallback _onConnectionCallback;
|
||||
std::atomic<int> _connectedClientsCount;
|
||||
|
||||
const static int kDefaultTimeoutSecs;
|
||||
int _timeoutSecs;
|
||||
@ -52,7 +51,6 @@ namespace ix
|
||||
// Methods
|
||||
virtual void handleConnection(std::unique_ptr<Socket>,
|
||||
std::shared_ptr<ConnectionState> connectionState) final;
|
||||
virtual size_t getConnectedClientsCount() final;
|
||||
|
||||
void setDefaultConnectionCallback();
|
||||
};
|
||||
|
@ -6,6 +6,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
|
@ -6,8 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
|
@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "IXSelectInterrupt.h"
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#ifdef _WIN32
|
||||
|
@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "IXSelectInterrupt.h"
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <array>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
@ -205,7 +205,9 @@ namespace ix
|
||||
_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
|
||||
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
|
||||
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
|
||||
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
||||
|
||||
if (!_tlsOptions.disable_hostname_validation)
|
||||
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
||||
|
||||
if (_tlsOptions.isPeerVerifyDisabled())
|
||||
{
|
||||
|
@ -102,7 +102,7 @@ namespace ix
|
||||
// First do DNS resolution
|
||||
//
|
||||
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
|
||||
struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
|
||||
auto res = dnsLookup->resolve(errMsg, isCancellationRequested);
|
||||
if (res == nullptr)
|
||||
{
|
||||
return -1;
|
||||
@ -112,7 +112,7 @@ namespace ix
|
||||
|
||||
// iterate through the records to find a working peer
|
||||
struct addrinfo* address;
|
||||
for (address = res; address != nullptr; address = address->ai_next)
|
||||
for (address = res.get(); address != nullptr; address = address->ai_next)
|
||||
{
|
||||
//
|
||||
// Second try to connect to the remote host
|
||||
@ -124,7 +124,6 @@ namespace ix
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "IXNetSystem.h"
|
||||
#include "IXSocket.h"
|
||||
#include "IXSocketConnect.h"
|
||||
#include <cstdint>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -48,7 +49,7 @@ namespace ix
|
||||
mbedtls_pk_init(&_pkey);
|
||||
}
|
||||
|
||||
bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg)
|
||||
bool SocketMbedTLS::loadSystemCertificates(std::string& /* errorMsg */)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG |
|
||||
@ -195,10 +196,13 @@ namespace ix
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
|
||||
if (!_tlsOptions.disable_hostname_validation)
|
||||
{
|
||||
errMsg = "SNI setup failed";
|
||||
return false;
|
||||
if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
|
||||
{
|
||||
errMsg = "SNI setup failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -301,7 +301,11 @@ namespace ix
|
||||
}
|
||||
|
||||
bool SocketOpenSSL::openSSLCheckServerCert(SSL* ssl,
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
const std::string& hostname,
|
||||
#else
|
||||
const std::string& /* hostname */,
|
||||
#endif
|
||||
std::string& errMsg)
|
||||
{
|
||||
X509* server_cert = SSL_get_peer_certificate(ssl);
|
||||
@ -390,6 +394,11 @@ namespace ix
|
||||
int connect_result = SSL_connect(_ssl_connection);
|
||||
if (connect_result == 1)
|
||||
{
|
||||
if (_tlsOptions.disable_hostname_validation)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return openSSLCheckServerCert(_ssl_connection, host, errMsg);
|
||||
}
|
||||
int reason = SSL_get_error(_ssl_connection, connect_result);
|
||||
@ -754,8 +763,11 @@ namespace ix
|
||||
// (The docs say that this should work from 1.0.2, and is the default from
|
||||
// 1.1.0, but it does not. To be on the safe side, the manual test
|
||||
// below is enabled for all versions prior to 1.1.0.)
|
||||
X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection);
|
||||
X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0);
|
||||
if (!_tlsOptions.disable_hostname_validation)
|
||||
{
|
||||
X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection);
|
||||
X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size());
|
||||
}
|
||||
#endif
|
||||
handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested);
|
||||
}
|
||||
|
@ -219,6 +219,10 @@ namespace ix
|
||||
if (_gcThread.joinable())
|
||||
{
|
||||
_stopGc = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
|
||||
_canContinueGC = true;
|
||||
}
|
||||
_conditionVariableGC.notify_one();
|
||||
_gcThread.join();
|
||||
_stopGc = false;
|
||||
@ -268,7 +272,10 @@ namespace ix
|
||||
// Set the socket to non blocking mode, so that accept calls are not blocking
|
||||
SocketConnect::configure(_serverFd);
|
||||
|
||||
setThreadName("SocketServer::accept");
|
||||
// Use a cryptic name to stay within the 16 bytes limit thread name limitation
|
||||
// $ echo Srv:gc:64000 | wc -c
|
||||
// 13
|
||||
setThreadName("Srv:ac:" + std::to_string(_port));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@ -425,7 +432,10 @@ namespace ix
|
||||
|
||||
void SocketServer::runGC()
|
||||
{
|
||||
setThreadName("SocketServer::GC");
|
||||
// Use a cryptic name to stay within the 16 bytes limit thread name limitation
|
||||
// $ echo Srv:gc:64000 | wc -c
|
||||
// 13
|
||||
setThreadName("Srv:gc:" + std::to_string(_port));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@ -445,7 +455,10 @@ namespace ix
|
||||
if (!_stopGc)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_conditionVariableMutexGC);
|
||||
_conditionVariableGC.wait(lock);
|
||||
if(!_canContinueGC) {
|
||||
_conditionVariableGC.wait(lock, [this]{ return _canContinueGC; });
|
||||
}
|
||||
_canContinueGC = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -459,6 +472,10 @@ namespace ix
|
||||
{
|
||||
// a connection got terminated, we can run the connection thread GC,
|
||||
// so wake up the thread responsible for that
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
|
||||
_canContinueGC = true;
|
||||
}
|
||||
_conditionVariableGC.notify_one();
|
||||
}
|
||||
|
||||
|
@ -126,5 +126,6 @@ namespace ix
|
||||
// as a connection
|
||||
std::condition_variable _conditionVariableGC;
|
||||
std::mutex _conditionVariableMutexGC;
|
||||
bool _canContinueGC{ false };
|
||||
};
|
||||
} // namespace ix
|
||||
|
@ -33,6 +33,9 @@ namespace ix
|
||||
// whether tls is enabled, used for server code
|
||||
bool tls = false;
|
||||
|
||||
// whether to skip validating the peer's hostname against the certificate presented
|
||||
bool disable_hostname_validation = false;
|
||||
|
||||
bool hasCertAndKey() const;
|
||||
|
||||
bool isUsingSystemDefaults() const;
|
||||
|
@ -333,6 +333,19 @@ namespace
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int getProtocolPort(const std::string& protocol)
|
||||
{
|
||||
if (protocol == "ws" || protocol == "http")
|
||||
{
|
||||
return 80;
|
||||
}
|
||||
else if (protocol == "wss" || protocol == "https")
|
||||
{
|
||||
return 443;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ix
|
||||
@ -343,6 +356,18 @@ namespace ix
|
||||
std::string& path,
|
||||
std::string& query,
|
||||
int& port)
|
||||
{
|
||||
bool isProtocolDefaultPort;
|
||||
return parse(url, protocol, host, path, query, port, isProtocolDefaultPort);
|
||||
}
|
||||
|
||||
bool UrlParser::parse(const std::string& url,
|
||||
std::string& protocol,
|
||||
std::string& host,
|
||||
std::string& path,
|
||||
std::string& query,
|
||||
int& port,
|
||||
bool& isProtocolDefaultPort)
|
||||
{
|
||||
clParseURL res = clParseURL::ParseURL(url);
|
||||
|
||||
@ -356,23 +381,12 @@ namespace ix
|
||||
path = res.m_Path;
|
||||
query = res.m_Query;
|
||||
|
||||
const auto protocolPort = getProtocolPort(protocol);
|
||||
if (!res.GetPort(&port))
|
||||
{
|
||||
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;
|
||||
}
|
||||
port = protocolPort;
|
||||
}
|
||||
isProtocolDefaultPort = port == protocolPort;
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
|
@ -19,5 +19,13 @@ namespace ix
|
||||
std::string& path,
|
||||
std::string& query,
|
||||
int& port);
|
||||
|
||||
static bool parse(const std::string& url,
|
||||
std::string& protocol,
|
||||
std::string& host,
|
||||
std::string& path,
|
||||
std::string& query,
|
||||
int& port,
|
||||
bool& isProtocolDefaultPort);
|
||||
};
|
||||
} // namespace ix
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "IXUuid.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "IXWebSocketHandshake.h"
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace
|
||||
@ -39,9 +40,11 @@ namespace ix
|
||||
, _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs)
|
||||
, _enablePong(kDefaultEnablePong)
|
||||
, _pingIntervalSecs(kDefaultPingIntervalSecs)
|
||||
, _pingType(SendMessageKind::Ping)
|
||||
{
|
||||
_ws.setOnCloseCallback(
|
||||
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) {
|
||||
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote)
|
||||
{
|
||||
_onMessageCallback(
|
||||
ix::make_unique<WebSocketMessage>(WebSocketMessageType::Close,
|
||||
emptyMsg,
|
||||
@ -100,6 +103,17 @@ namespace ix
|
||||
return _perMessageDeflateOptions;
|
||||
}
|
||||
|
||||
void WebSocket::setPingMessage(const std::string& sendMessage, SendMessageKind pingType)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
_pingMessage = sendMessage;
|
||||
_ws.setPingMessage(_pingMessage, pingType);
|
||||
}
|
||||
const std::string WebSocket::getPingMessage() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
return _pingMessage;
|
||||
}
|
||||
void WebSocket::setPingInterval(int pingIntervalSecs)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
@ -232,7 +246,7 @@ namespace ix
|
||||
if (_pingIntervalSecs > 0)
|
||||
{
|
||||
// Send a heart beat right away
|
||||
_ws.sendHeartBeat();
|
||||
_ws.sendHeartBeat(_pingType);
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -240,7 +254,8 @@ namespace ix
|
||||
|
||||
WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket,
|
||||
int timeoutSecs,
|
||||
bool enablePerMessageDeflate)
|
||||
bool enablePerMessageDeflate,
|
||||
HttpRequestPtr request)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
@ -249,7 +264,7 @@ namespace ix
|
||||
}
|
||||
|
||||
WebSocketInitResult status =
|
||||
_ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate);
|
||||
_ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate, request);
|
||||
if (!status.success)
|
||||
{
|
||||
return status;
|
||||
@ -266,7 +281,7 @@ namespace ix
|
||||
if (_pingIntervalSecs > 0)
|
||||
{
|
||||
// Send a heart beat right away
|
||||
_ws.sendHeartBeat();
|
||||
_ws.sendHeartBeat(_pingType);
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -384,8 +399,9 @@ namespace ix
|
||||
[this](const std::string& msg,
|
||||
size_t wireSize,
|
||||
bool decompressionError,
|
||||
WebSocketTransport::MessageKind messageKind) {
|
||||
WebSocketMessageType webSocketMessageType{WebSocketMessageType::Error};
|
||||
WebSocketTransport::MessageKind messageKind)
|
||||
{
|
||||
WebSocketMessageType webSocketMessageType {WebSocketMessageType::Error};
|
||||
switch (messageKind)
|
||||
{
|
||||
case WebSocketTransport::MessageKind::MSG_TEXT:
|
||||
@ -503,13 +519,13 @@ namespace ix
|
||||
return sendMessage(text, SendMessageKind::Text, onProgressCallback);
|
||||
}
|
||||
|
||||
WebSocketSendInfo WebSocket::ping(const std::string& text)
|
||||
WebSocketSendInfo WebSocket::ping(const std::string& text, SendMessageKind pingType)
|
||||
{
|
||||
// Standard limit ping message size
|
||||
constexpr size_t pingMaxPayloadSize = 125;
|
||||
if (text.size() > pingMaxPayloadSize) return WebSocketSendInfo(false);
|
||||
|
||||
return sendMessage(text, SendMessageKind::Ping);
|
||||
return sendMessage(text, pingType);
|
||||
}
|
||||
|
||||
WebSocketSendInfo WebSocket::sendMessage(const IXWebSocketSendData& message,
|
||||
|
@ -16,11 +16,12 @@
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include "IXWebSocketMessage.h"
|
||||
#include "IXWebSocketPerMessageDeflateOptions.h"
|
||||
#include "IXWebSocketSendInfo.h"
|
||||
#include "IXWebSocketSendData.h"
|
||||
#include "IXWebSocketSendInfo.h"
|
||||
#include "IXWebSocketTransport.h"
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@ -53,6 +54,8 @@ namespace ix
|
||||
void setPerMessageDeflateOptions(
|
||||
const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions);
|
||||
void setTLSOptions(const SocketTLSOptions& socketTLSOptions);
|
||||
void setPingMessage(const std::string& sendMessage,
|
||||
SendMessageKind pingType = SendMessageKind::Ping);
|
||||
void setPingInterval(int pingIntervalSecs);
|
||||
void enablePong();
|
||||
void disablePong();
|
||||
@ -88,7 +91,7 @@ namespace ix
|
||||
const OnProgressCallback& onProgressCallback = nullptr);
|
||||
WebSocketSendInfo sendText(const std::string& text,
|
||||
const OnProgressCallback& onProgressCallback = nullptr);
|
||||
WebSocketSendInfo ping(const std::string& text);
|
||||
WebSocketSendInfo ping(const std::string& text,SendMessageKind pingType = SendMessageKind::Ping);
|
||||
|
||||
void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode,
|
||||
const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage);
|
||||
@ -103,6 +106,7 @@ namespace ix
|
||||
|
||||
const std::string getUrl() const;
|
||||
const WebSocketPerMessageDeflateOptions getPerMessageDeflateOptions() const;
|
||||
const std::string getPingMessage() const;
|
||||
int getPingInterval() const;
|
||||
size_t bufferedAmount() const;
|
||||
|
||||
@ -128,7 +132,8 @@ namespace ix
|
||||
// Server
|
||||
WebSocketInitResult connectToSocket(std::unique_ptr<Socket>,
|
||||
int timeoutSecs,
|
||||
bool enablePerMessageDeflate);
|
||||
bool enablePerMessageDeflate,
|
||||
HttpRequestPtr request = nullptr);
|
||||
|
||||
WebSocketTransport _ws;
|
||||
|
||||
@ -169,6 +174,8 @@ namespace ix
|
||||
// Optional ping and pong timeout
|
||||
int _pingIntervalSecs;
|
||||
int _pingTimeoutSecs;
|
||||
std::string _pingMessage;
|
||||
SendMessageKind _pingType;
|
||||
static const int kDefaultPingIntervalSecs;
|
||||
static const int kDefaultPingTimeoutSecs;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "IXWebSocketHandshake.h"
|
||||
|
||||
#include "IXBase64.h"
|
||||
#include "IXHttp.h"
|
||||
#include "IXSocketConnect.h"
|
||||
#include "IXStrCaseCompare.h"
|
||||
@ -17,7 +18,6 @@
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace ix
|
||||
{
|
||||
WebSocketHandshake::WebSocketHandshake(
|
||||
@ -87,6 +87,7 @@ namespace ix
|
||||
WebSocketInitResult WebSocketHandshake::clientHandshake(
|
||||
const std::string& url,
|
||||
const WebSocketHttpHeaders& extraHeaders,
|
||||
const std::string& protocol,
|
||||
const std::string& host,
|
||||
const std::string& path,
|
||||
int port,
|
||||
@ -106,15 +107,10 @@ namespace ix
|
||||
return WebSocketInitResult(false, 0, ss.str());
|
||||
}
|
||||
|
||||
//
|
||||
// Generate a random 24 bytes string which looks like it is base64 encoded
|
||||
// y3JJHMbDL1EzLkh9GBhXDw==
|
||||
// 0cb3Vd9HkbpVVumoS3Noka==
|
||||
// Generate a random 16 bytes string and base64 encode it.
|
||||
//
|
||||
// See https://stackoverflow.com/questions/18265128/what-is-sec-websocket-key-for
|
||||
//
|
||||
std::string secWebSocketKey = genRandomString(22);
|
||||
secWebSocketKey += "==";
|
||||
std::string secWebSocketKey = macaron::Base64::Encode(genRandomString(16));
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "GET " << path << " HTTP/1.1\r\n";
|
||||
@ -130,6 +126,12 @@ namespace ix
|
||||
ss << "User-Agent: " << userAgent() << "\r\n";
|
||||
}
|
||||
|
||||
// Set an origin header if missing
|
||||
if (extraHeaders.find("Origin") == extraHeaders.end())
|
||||
{
|
||||
ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n";
|
||||
}
|
||||
|
||||
for (auto& it : extraHeaders)
|
||||
{
|
||||
ss << it.first << ": " << it.second << "\r\n";
|
||||
@ -245,28 +247,42 @@ namespace ix
|
||||
}
|
||||
|
||||
WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs,
|
||||
bool enablePerMessageDeflate)
|
||||
bool enablePerMessageDeflate,
|
||||
HttpRequestPtr request)
|
||||
{
|
||||
_requestInitCancellation = false;
|
||||
|
||||
auto isCancellationRequested =
|
||||
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
|
||||
|
||||
// Read first line
|
||||
auto lineResult = _socket->readLine(isCancellationRequested);
|
||||
auto lineValid = lineResult.first;
|
||||
auto line = lineResult.second;
|
||||
std::string method;
|
||||
std::string uri;
|
||||
std::string httpVersion;
|
||||
|
||||
if (!lineValid)
|
||||
if (request)
|
||||
{
|
||||
return sendErrorResponse(400, "Error reading HTTP request line");
|
||||
method = request->method;
|
||||
uri = request->uri;
|
||||
httpVersion = request->version;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read first line
|
||||
auto lineResult = _socket->readLine(isCancellationRequested);
|
||||
auto lineValid = lineResult.first;
|
||||
auto line = lineResult.second;
|
||||
|
||||
// Validate request line (GET /foo HTTP/1.1\r\n)
|
||||
auto requestLine = Http::parseRequestLine(line);
|
||||
auto method = std::get<0>(requestLine);
|
||||
auto uri = std::get<1>(requestLine);
|
||||
auto httpVersion = std::get<2>(requestLine);
|
||||
if (!lineValid)
|
||||
{
|
||||
return sendErrorResponse(400, "Error reading HTTP request line");
|
||||
}
|
||||
|
||||
// Validate request line (GET /foo HTTP/1.1\r\n)
|
||||
auto requestLine = Http::parseRequestLine(line);
|
||||
method = std::get<0>(requestLine);
|
||||
uri = std::get<1>(requestLine);
|
||||
httpVersion = std::get<2>(requestLine);
|
||||
}
|
||||
|
||||
if (method != "GET")
|
||||
{
|
||||
@ -279,14 +295,22 @@ namespace ix
|
||||
"Invalid HTTP version, need HTTP/1.1, got: " + httpVersion);
|
||||
}
|
||||
|
||||
// Retrieve and validate HTTP headers
|
||||
auto result = parseHttpHeaders(_socket, isCancellationRequested);
|
||||
auto headersValid = result.first;
|
||||
auto headers = result.second;
|
||||
|
||||
if (!headersValid)
|
||||
WebSocketHttpHeaders headers;
|
||||
if (request)
|
||||
{
|
||||
return sendErrorResponse(400, "Error parsing HTTP headers");
|
||||
headers = request->headers;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retrieve and validate HTTP headers
|
||||
auto result = parseHttpHeaders(_socket, isCancellationRequested);
|
||||
auto headersValid = result.first;
|
||||
headers = result.second;
|
||||
|
||||
if (!headersValid)
|
||||
{
|
||||
return sendErrorResponse(400, "Error parsing HTTP headers");
|
||||
}
|
||||
}
|
||||
|
||||
if (headers.find("sec-websocket-key") == headers.end())
|
||||
|
@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "IXCancellationRequest.h"
|
||||
#include "IXHttp.h"
|
||||
#include "IXSocket.h"
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include "IXWebSocketInitResult.h"
|
||||
@ -30,12 +31,15 @@ namespace ix
|
||||
|
||||
WebSocketInitResult clientHandshake(const std::string& url,
|
||||
const WebSocketHttpHeaders& extraHeaders,
|
||||
const std::string& protocol,
|
||||
const std::string& host,
|
||||
const std::string& path,
|
||||
int port,
|
||||
int timeoutSecs);
|
||||
|
||||
WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate);
|
||||
WebSocketInitResult serverHandshake(int timeoutSecs,
|
||||
bool enablePerMessageDeflate,
|
||||
HttpRequestPtr request = nullptr);
|
||||
|
||||
private:
|
||||
std::string genRandomString(const int len);
|
||||
|
@ -46,6 +46,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "IXWebSocketPerMessageDeflate.h"
|
||||
|
||||
#include "IXUniquePtr.h"
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "zlib.h"
|
||||
#endif
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "IXWebSocketSendData.h"
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
|
@ -1,128 +1,129 @@
|
||||
/*
|
||||
* IXWebSocketSendData.h
|
||||
*
|
||||
* WebSocket (Binary/Text) send data buffer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
/*
|
||||
* IXWebSocketSendData implements a wrapper for std::string, std:vector<char/uint8_t> and char*.
|
||||
* It removes the necessarity to copy the data or string into a std::string
|
||||
*/
|
||||
class IXWebSocketSendData {
|
||||
public:
|
||||
|
||||
template<typename T>
|
||||
struct IXWebSocketSendData_const_iterator
|
||||
//: public std::iterator<std::forward_iterator_tag, T>
|
||||
{
|
||||
typedef IXWebSocketSendData_const_iterator<T> const_iterator;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using reference = const value_type&;
|
||||
|
||||
pointer _ptr;
|
||||
public:
|
||||
IXWebSocketSendData_const_iterator() : _ptr(nullptr) {}
|
||||
IXWebSocketSendData_const_iterator(pointer ptr) : _ptr(ptr) {}
|
||||
~IXWebSocketSendData_const_iterator() {}
|
||||
|
||||
const_iterator operator++(int) { return const_iterator(_ptr++); }
|
||||
const_iterator& operator++() { ++_ptr; return *this; }
|
||||
reference operator* () const { return *_ptr; }
|
||||
pointer operator->() const { return _ptr; }
|
||||
const_iterator operator+ (const difference_type offset) const { return const_iterator(_ptr + offset); }
|
||||
const_iterator operator- (const difference_type offset) const { return const_iterator(_ptr - offset); }
|
||||
difference_type operator- (const const_iterator& rhs) const { return _ptr - rhs._ptr; }
|
||||
bool operator==(const const_iterator& rhs) const { return _ptr == rhs._ptr; }
|
||||
bool operator!=(const const_iterator& rhs) const { return _ptr != rhs._ptr; }
|
||||
const_iterator& operator+=(const difference_type offset) { _ptr += offset; return *this; }
|
||||
const_iterator& operator-=(const difference_type offset) { _ptr -= offset; return *this; }
|
||||
};
|
||||
|
||||
using const_iterator = IXWebSocketSendData_const_iterator<char>;
|
||||
|
||||
/* The assigned std::string must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const std::string& str)
|
||||
: _data(str.data())
|
||||
, _size(str.size())
|
||||
{
|
||||
}
|
||||
|
||||
/* The assigned std::vector must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const std::vector<char>& v)
|
||||
: _data(v.data())
|
||||
, _size(v.size())
|
||||
{
|
||||
}
|
||||
|
||||
/* The assigned std::vector must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const std::vector<uint8_t>& v)
|
||||
: _data(reinterpret_cast<const char*>(v.data()))
|
||||
, _size(v.size())
|
||||
{
|
||||
}
|
||||
|
||||
/* The assigned memory must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const char* data, size_t size)
|
||||
: _data(data)
|
||||
, _size(data == nullptr ? 0 : size)
|
||||
{
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return _data == nullptr || _size == 0;
|
||||
}
|
||||
|
||||
const char* c_str() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
inline const_iterator begin() const
|
||||
{
|
||||
return const_iterator(const_cast<char*>(_data));
|
||||
}
|
||||
|
||||
inline const_iterator end() const
|
||||
{
|
||||
return const_iterator(const_cast<char*>(_data) + _size);
|
||||
}
|
||||
|
||||
inline const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
inline const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
private:
|
||||
const char* _data;
|
||||
const size_t _size;
|
||||
};
|
||||
|
||||
/*
|
||||
* IXWebSocketSendData.h
|
||||
*
|
||||
* WebSocket (Binary/Text) send data buffer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
/*
|
||||
* IXWebSocketSendData implements a wrapper for std::string, std:vector<char/uint8_t> and char*.
|
||||
* It removes the necessarity to copy the data or string into a std::string
|
||||
*/
|
||||
class IXWebSocketSendData {
|
||||
public:
|
||||
|
||||
template<typename T>
|
||||
struct IXWebSocketSendData_const_iterator
|
||||
//: public std::iterator<std::forward_iterator_tag, T>
|
||||
{
|
||||
typedef IXWebSocketSendData_const_iterator<T> const_iterator;
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using reference = const value_type&;
|
||||
|
||||
pointer _ptr;
|
||||
public:
|
||||
IXWebSocketSendData_const_iterator() : _ptr(nullptr) {}
|
||||
IXWebSocketSendData_const_iterator(pointer ptr) : _ptr(ptr) {}
|
||||
~IXWebSocketSendData_const_iterator() {}
|
||||
|
||||
const_iterator operator++(int) { return const_iterator(_ptr++); }
|
||||
const_iterator& operator++() { ++_ptr; return *this; }
|
||||
reference operator* () const { return *_ptr; }
|
||||
pointer operator->() const { return _ptr; }
|
||||
const_iterator operator+ (const difference_type offset) const { return const_iterator(_ptr + offset); }
|
||||
const_iterator operator- (const difference_type offset) const { return const_iterator(_ptr - offset); }
|
||||
difference_type operator- (const const_iterator& rhs) const { return _ptr - rhs._ptr; }
|
||||
bool operator==(const const_iterator& rhs) const { return _ptr == rhs._ptr; }
|
||||
bool operator!=(const const_iterator& rhs) const { return _ptr != rhs._ptr; }
|
||||
const_iterator& operator+=(const difference_type offset) { _ptr += offset; return *this; }
|
||||
const_iterator& operator-=(const difference_type offset) { _ptr -= offset; return *this; }
|
||||
};
|
||||
|
||||
using const_iterator = IXWebSocketSendData_const_iterator<char>;
|
||||
|
||||
/* The assigned std::string must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const std::string& str)
|
||||
: _data(str.data())
|
||||
, _size(str.size())
|
||||
{
|
||||
}
|
||||
|
||||
/* The assigned std::vector must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const std::vector<char>& v)
|
||||
: _data(v.data())
|
||||
, _size(v.size())
|
||||
{
|
||||
}
|
||||
|
||||
/* The assigned std::vector must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const std::vector<uint8_t>& v)
|
||||
: _data(reinterpret_cast<const char*>(v.data()))
|
||||
, _size(v.size())
|
||||
{
|
||||
}
|
||||
|
||||
/* The assigned memory must be kept alive for the lifetime of the input buffer */
|
||||
IXWebSocketSendData(const char* data, size_t size)
|
||||
: _data(data)
|
||||
, _size(data == nullptr ? 0 : size)
|
||||
{
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return _data == nullptr || _size == 0;
|
||||
}
|
||||
|
||||
const char* c_str() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
inline const_iterator begin() const
|
||||
{
|
||||
return const_iterator(const_cast<char*>(_data));
|
||||
}
|
||||
|
||||
inline const_iterator end() const
|
||||
{
|
||||
return const_iterator(const_cast<char*>(_data) + _size);
|
||||
}
|
||||
|
||||
inline const_iterator cbegin() const
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
inline const_iterator cend() const
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
private:
|
||||
const char* _data;
|
||||
const size_t _size;
|
||||
};
|
||||
|
||||
}
|
@ -79,7 +79,16 @@ namespace ix
|
||||
void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket,
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
setThreadName("WebSocketServer::" + connectionState->getId());
|
||||
handleUpgrade(std::move(socket), connectionState);
|
||||
|
||||
connectionState->setTerminated();
|
||||
}
|
||||
|
||||
void WebSocketServer::handleUpgrade(std::unique_ptr<Socket> socket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
HttpRequestPtr request)
|
||||
{
|
||||
setThreadName("Srv:ws:" + connectionState->getId());
|
||||
|
||||
auto webSocket = std::make_shared<WebSocket>();
|
||||
if (_onConnectionCallback)
|
||||
@ -89,7 +98,7 @@ namespace ix
|
||||
if (!webSocket->isOnMessageCallbackRegistered())
|
||||
{
|
||||
logError("WebSocketServer Application developer error: Server callback improperly "
|
||||
"registerered.");
|
||||
"registered.");
|
||||
logError("Missing call to setOnMessageCallback inside setOnConnectionCallback.");
|
||||
connectionState->setTerminated();
|
||||
return;
|
||||
@ -99,9 +108,8 @@ namespace ix
|
||||
{
|
||||
WebSocket* webSocketRawPtr = webSocket.get();
|
||||
webSocket->setOnMessageCallback(
|
||||
[this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) {
|
||||
_onClientMessageCallback(connectionState, *webSocketRawPtr, msg);
|
||||
});
|
||||
[this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg)
|
||||
{ _onClientMessageCallback(connectionState, *webSocketRawPtr, msg); });
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -130,7 +138,7 @@ namespace ix
|
||||
}
|
||||
|
||||
auto status = webSocket->connectToSocket(
|
||||
std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate);
|
||||
std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate, request);
|
||||
if (status.success)
|
||||
{
|
||||
// Process incoming messages and execute callbacks
|
||||
@ -155,8 +163,6 @@ namespace ix
|
||||
logError("Cannot delete client");
|
||||
}
|
||||
}
|
||||
|
||||
connectionState->setTerminated();
|
||||
}
|
||||
|
||||
std::set<std::shared_ptr<WebSocket>> WebSocketServer::getClients()
|
||||
@ -176,28 +182,30 @@ namespace ix
|
||||
//
|
||||
void WebSocketServer::makeBroadcastServer()
|
||||
{
|
||||
setOnClientMessageCallback([this](std::shared_ptr<ConnectionState> connectionState,
|
||||
WebSocket& webSocket,
|
||||
const WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionState->getRemoteIp();
|
||||
if (msg->type == ix::WebSocketMessageType::Message)
|
||||
setOnClientMessageCallback(
|
||||
[this](std::shared_ptr<ConnectionState> connectionState,
|
||||
WebSocket& webSocket,
|
||||
const WebSocketMessagePtr& msg)
|
||||
{
|
||||
for (auto&& client : getClients())
|
||||
auto remoteIp = connectionState->getRemoteIp();
|
||||
if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
if (client.get() != &webSocket)
|
||||
for (auto&& client : getClients())
|
||||
{
|
||||
client->send(msg->str, msg->binary);
|
||||
|
||||
// Make sure the OS send buffer is flushed before moving on
|
||||
do
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
std::chrono::duration<double, std::milli> duration(500);
|
||||
std::this_thread::sleep_for(duration);
|
||||
} while (client->bufferedAmount() != 0);
|
||||
client->send(msg->str, msg->binary);
|
||||
|
||||
// Make sure the OS send buffer is flushed before moving on
|
||||
do
|
||||
{
|
||||
std::chrono::duration<double, std::milli> duration(500);
|
||||
std::this_thread::sleep_for(duration);
|
||||
} while (client->bufferedAmount() != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool WebSocketServer::listenAndStart()
|
||||
|
@ -55,6 +55,7 @@ namespace ix
|
||||
int getHandshakeTimeoutSecs();
|
||||
bool isPongEnabled();
|
||||
bool isPerMessageDeflateEnabled();
|
||||
|
||||
private:
|
||||
// Member variables
|
||||
int _handshakeTimeoutSecs;
|
||||
@ -73,5 +74,10 @@ namespace ix
|
||||
virtual void handleConnection(std::unique_ptr<Socket> socket,
|
||||
std::shared_ptr<ConnectionState> connectionState);
|
||||
virtual size_t getConnectedClientsCount() final;
|
||||
|
||||
protected:
|
||||
void handleUpgrade(std::unique_ptr<Socket> socket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
HttpRequestPtr request = nullptr);
|
||||
};
|
||||
} // namespace ix
|
||||
|
@ -45,7 +45,6 @@
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@ -54,7 +53,6 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
const std::string WebSocketTransport::kPingMessage("ixwebsocket::heartbeat");
|
||||
const int WebSocketTransport::kDefaultPingIntervalSecs(-1);
|
||||
const bool WebSocketTransport::kDefaultEnablePong(true);
|
||||
const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300);
|
||||
@ -74,6 +72,9 @@ namespace ix
|
||||
, _enablePong(kDefaultEnablePong)
|
||||
, _pingIntervalSecs(kDefaultPingIntervalSecs)
|
||||
, _pongReceived(false)
|
||||
, _setCustomMessage(false)
|
||||
, _kPingMessage("ixwebsocket::heartbeat")
|
||||
, _pingType(SendMessageKind::Ping)
|
||||
, _pingCount(0)
|
||||
, _lastSendPingTimePoint(std::chrono::steady_clock::now())
|
||||
{
|
||||
@ -139,7 +140,7 @@ namespace ix
|
||||
_enablePerMessageDeflate);
|
||||
|
||||
result = webSocketHandshake.clientHandshake(
|
||||
remoteUrl, headers, host, path, port, timeoutSecs);
|
||||
remoteUrl, headers, protocol, host, path, port, timeoutSecs);
|
||||
|
||||
if (result.http_status >= 300 && result.http_status < 400)
|
||||
{
|
||||
@ -170,7 +171,8 @@ namespace ix
|
||||
// Server
|
||||
WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket,
|
||||
int timeoutSecs,
|
||||
bool enablePerMessageDeflate)
|
||||
bool enablePerMessageDeflate,
|
||||
HttpRequestPtr request)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
||||
|
||||
@ -187,7 +189,8 @@ namespace ix
|
||||
_perMessageDeflateOptions,
|
||||
_enablePerMessageDeflate);
|
||||
|
||||
auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate);
|
||||
auto result =
|
||||
webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate, request);
|
||||
if (result.success)
|
||||
{
|
||||
setReadyState(ReadyState::OPEN);
|
||||
@ -248,13 +251,51 @@ namespace ix
|
||||
return now - _lastSendPingTimePoint > std::chrono::seconds(_pingIntervalSecs);
|
||||
}
|
||||
|
||||
WebSocketSendInfo WebSocketTransport::sendHeartBeat()
|
||||
void WebSocketTransport::setPingMessage(const std::string& message, SendMessageKind pingType)
|
||||
{
|
||||
_setCustomMessage = true;
|
||||
_kPingMessage = message;
|
||||
_pingType = pingType;
|
||||
}
|
||||
|
||||
WebSocketSendInfo WebSocketTransport::sendHeartBeat(SendMessageKind pingMessage)
|
||||
{
|
||||
_pongReceived = false;
|
||||
std::stringstream ss;
|
||||
ss << kPingMessage << "::" << _pingIntervalSecs << "s"
|
||||
<< "::" << _pingCount++;
|
||||
return sendPing(ss.str());
|
||||
|
||||
ss << _kPingMessage;
|
||||
if (!_setCustomMessage)
|
||||
{
|
||||
ss << "::" << _pingIntervalSecs << "s"
|
||||
<< "::" << _pingCount++;
|
||||
}
|
||||
if (pingMessage == SendMessageKind::Ping)
|
||||
{
|
||||
return sendPing(ss.str());
|
||||
}
|
||||
else if (pingMessage == SendMessageKind::Binary)
|
||||
{
|
||||
WebSocketSendInfo info = sendBinary(ss.str(), nullptr);
|
||||
if (info.success)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(_lastSendPingTimePointMutex);
|
||||
_lastSendPingTimePoint = std::chrono::steady_clock::now();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
else if (pingMessage == SendMessageKind::Text)
|
||||
{
|
||||
WebSocketSendInfo info = sendText(ss.str(), nullptr);
|
||||
if (info.success)
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(_lastSendPingTimePointMutex);
|
||||
_lastSendPingTimePoint = std::chrono::steady_clock::now();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
// unknow type ping message
|
||||
return {};
|
||||
}
|
||||
|
||||
bool WebSocketTransport::closingDelayExceeded()
|
||||
@ -270,7 +311,9 @@ namespace ix
|
||||
{
|
||||
if (pingIntervalExceeded())
|
||||
{
|
||||
if (!_pongReceived)
|
||||
// If it is not a 'ping' message of ping type, there is no need to judge whether
|
||||
// pong will receive it
|
||||
if (_pingType == SendMessageKind::Ping && !_pongReceived)
|
||||
{
|
||||
// ping response (PONG) exceeds the maximum delay, close the connection
|
||||
close(WebSocketCloseConstants::kInternalErrorCode,
|
||||
@ -278,7 +321,7 @@ namespace ix
|
||||
}
|
||||
else
|
||||
{
|
||||
sendHeartBeat();
|
||||
sendHeartBeat(_pingType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -657,6 +700,7 @@ namespace ix
|
||||
if (_readyState != ReadyState::CLOSING)
|
||||
{
|
||||
// send back the CLOSE frame
|
||||
setReadyState(ReadyState::CLOSING);
|
||||
sendCloseFrame(code, reason);
|
||||
|
||||
wakeUpFromPoll(SelectInterrupt::kCloseRequest);
|
||||
@ -1029,7 +1073,10 @@ namespace ix
|
||||
else if (ret <= 0)
|
||||
{
|
||||
closeSocket();
|
||||
setReadyState(ReadyState::CLOSED);
|
||||
if (_readyState != ReadyState::CLOSING)
|
||||
{
|
||||
setReadyState(ReadyState::CLOSED);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
@ -18,9 +18,10 @@
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include "IXWebSocketPerMessageDeflate.h"
|
||||
#include "IXWebSocketPerMessageDeflateOptions.h"
|
||||
#include "IXWebSocketSendInfo.h"
|
||||
#include "IXWebSocketSendData.h"
|
||||
#include "IXWebSocketSendInfo.h"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
@ -86,7 +87,8 @@ namespace ix
|
||||
// Server
|
||||
WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket,
|
||||
int timeoutSecs,
|
||||
bool enablePerMessageDeflate);
|
||||
bool enablePerMessageDeflate,
|
||||
HttpRequestPtr request = nullptr);
|
||||
|
||||
PollResult poll();
|
||||
WebSocketSendInfo sendBinary(const IXWebSocketSendData& message,
|
||||
@ -108,8 +110,12 @@ namespace ix
|
||||
void dispatch(PollResult pollResult, const OnMessageCallback& onMessageCallback);
|
||||
size_t bufferedAmount() const;
|
||||
|
||||
// set ping heartbeat message
|
||||
void setPingMessage(const std::string& message, SendMessageKind pingType);
|
||||
|
||||
// internal
|
||||
WebSocketSendInfo sendHeartBeat();
|
||||
// send any type of ping packet, not only 'ping' type
|
||||
WebSocketSendInfo sendHeartBeat(SendMessageKind pingType);
|
||||
|
||||
private:
|
||||
std::string _url;
|
||||
@ -214,7 +220,10 @@ namespace ix
|
||||
std::atomic<bool> _pongReceived;
|
||||
|
||||
static const int kDefaultPingIntervalSecs;
|
||||
static const std::string kPingMessage;
|
||||
|
||||
bool _setCustomMessage;
|
||||
std::string _kPingMessage;
|
||||
SendMessageKind _pingType;
|
||||
std::atomic<uint64_t> _pingCount;
|
||||
|
||||
// We record when ping are being sent so that we can know when to send the next one
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "11.4.1"
|
||||
#define IX_WEBSOCKET_VERSION "11.4.3"
|
||||
|
@ -13,16 +13,24 @@ all: brew
|
||||
|
||||
install: brew
|
||||
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/homebrew
|
||||
|
||||
# Use -DCMAKE_INSTALL_PREFIX= to install into another location
|
||||
# on osx it is good practice to make /usr/local user writable
|
||||
# sudo chown -R `whoami`/staff /usr/local
|
||||
#
|
||||
# Those days (since Apple Silicon mac shipped), on macOS homebrew installs in /opt/homebrew, and /usr/local is readonly
|
||||
#
|
||||
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
|
||||
#
|
||||
# Default rule does not use python as that requires first time users to have Python3 installed
|
||||
#
|
||||
brew:
|
||||
ifeq ($(shell uname),Darwin)
|
||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/homebrew -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
|
||||
else
|
||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
|
||||
endif
|
||||
|
||||
# Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the
|
||||
# server side ?) and I can't work-around it easily, so we're using mbedtls on
|
||||
|
20
test/.certs/wrong-name-server-crt.pem
Normal file
20
test/.certs/wrong-name-server-crt.pem
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDNDCCAhwCFCl+O/rR8flqYKKvD0iwkucFwMaLMA0GCSqGSIb3DQEBCwUAMEEx
|
||||
FDASBgNVBAoMC21hY2hpbmV6b25lMRQwEgYDVQQKDAtJWFdlYlNvY2tldDETMBEG
|
||||
A1UEAwwKdHJ1c3RlZC1jYTAgFw0yMjA4MjMyMDM2MjVaGA80MjgxMDYwMTIwMzYy
|
||||
NVowajELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMREwDwYDVQQHDAhCZXJrZWxl
|
||||
eTEbMBkGA1UECgwSRHVtbXkgT3JnYW5pemF0aW9uMR4wHAYDVQQDDBVub3QuYS52
|
||||
YWxpZC5ob3N0Lm5hbWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2
|
||||
9N806IjCvA82zfk9CPNwaEHOygNDJSXaZ38YDSI4C+Wf2imnMxrLQKaoccHUi+9L
|
||||
4rQN/hSCg+uTULQUZ0iyssGDaIh4IcAeoEcNlXYHTrgP+aAaby3q5zeZ80K3+6e4
|
||||
rqcuBPV2lLszJu3XXwEKbDSxW3De0gc2N8m9DN8Lx7i70DRf0F4m6RIMFF/kHXwa
|
||||
zZLeG7rZb4xL684lmmQsWtk5Z600CvrBtUE7fQ7bhy0QhSt66kdTSL8IKQrbWcGj
|
||||
q0tggFlOqeyZSi73gqUiAIuGO8/tRgmp4HygNyC24jpOB5nObOPPJTUEf5Mk1Bum
|
||||
kD5a+yL6YbVdhiaK17wbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKsLXGLfO1IZ
|
||||
LbofGc7/TCQwRayR3RdG4864PBy97KfJWyizg7Wm4X4yPFRG+6q3X5NKW32Ew9lI
|
||||
jXulXCTjWOiSxiG4Pk20uczkOhWVHFdnS9gZvykPC/ElxBKPalT6MMstZWxpElsk
|
||||
rCDKXj4LkD0po8bZrjlgSZQQQk6XMRkoRai2GWLJqIjaNCSF8nqb1wM/1OE1tAwi
|
||||
polO1eFMA24yypvlXcNrNXjqcQ7LaoQFQltmi/RV+uTk7EK2F2jgYVzJ/pe2ET0i
|
||||
RIMbGZTlAiemDGL9SpMsxftG6fSmP6QqDqYExmmPlZMLprb2da/2kelWFA+VkvdG
|
||||
zFrnIcyfMY4=
|
||||
-----END CERTIFICATE-----
|
27
test/.certs/wrong-name-server-key.pem
Normal file
27
test/.certs/wrong-name-server-key.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAtvTfNOiIwrwPNs35PQjzcGhBzsoDQyUl2md/GA0iOAvln9op
|
||||
pzMay0CmqHHB1IvvS+K0Df4UgoPrk1C0FGdIsrLBg2iIeCHAHqBHDZV2B064D/mg
|
||||
Gm8t6uc3mfNCt/unuK6nLgT1dpS7Mybt118BCmw0sVtw3tIHNjfJvQzfC8e4u9A0
|
||||
X9BeJukSDBRf5B18Gs2S3hu62W+MS+vOJZpkLFrZOWetNAr6wbVBO30O24ctEIUr
|
||||
eupHU0i/CCkK21nBo6tLYIBZTqnsmUou94KlIgCLhjvP7UYJqeB8oDcgtuI6TgeZ
|
||||
zmzjzyU1BH+TJNQbppA+Wvsi+mG1XYYmite8GwIDAQABAoIBAGRzAXG9EglI01mV
|
||||
sPfvyCi5NRhiFXRyGtxU4pTD8TuwXHxtfV0NU/KwJlBpVLBrvBCAAbeE/qHB6D9T
|
||||
metx4ZorRs/tPrAmZ6LpANnWa50LfUdYGK0qyZ0lIYPm6YS2KJnfWm6LznEyq60j
|
||||
/IW45YthaXTO7aOI0OjVrG+dd4CxU1g1NtCQ9bdDMDjfXFVnoOifXIl8W22eRMoZ
|
||||
LzCz+0sI0R0LenXCPT566de21F0NDkIK7NaQ1l5BMX4PA+RsN3cZlzyruA1woPKI
|
||||
aBR2LQGNrBfDVGMATtUm89RpWAftb8FmXqYUsM7zAzTO6dViitiB7OFlw7Ax15YH
|
||||
MTj5zGECgYEA35ocEEMfyahBN70bjyiqOHlzKwFjDl9DsUf8xqHsNhYAL+GrOK9A
|
||||
8lN5ByzcnbV3TJtU4WYbPgQJld8gXFx4h3eS+SkA/ASkAdtgHfdMImZ1v7k3TIPf
|
||||
DXOCsHzELsQY6OgiI572Nwzx/Tl+0dFwaOfLjU9iEmmqL667j1Y4NiMCgYEA0Xch
|
||||
9K/vwZ1I9gM3ySvG40R2TRriC9Bf8jwrEWeRsWNvBcqtMMrgwAMsMCKDugSZR7n6
|
||||
o3WZV6mpvYVLFx0b93v07i7EpFP27kMw3gLNBKX62snR9JbqwAMK7tktgLds5pKM
|
||||
DvLHuAQ9XMMXMLX7WlSyhmtFeU7IDulTSHHqdakCgYEAywITCpy2xpKRK7bwx4gH
|
||||
C6EQc/IdahYJ0nHmSL0IRY6x+sbrelp7H8ezcVVEs5bmylGYvc/DWgm2XjCnI9P8
|
||||
xhlFAhw9PZJFCT6QRISaxfy6WSgi0cBEieTeubd9MmxtpT/khuyy5AZHyj0iLAL4
|
||||
CPayMwjopIj0r7f3p8qC3HsCgYBmq2kmYVI4aZrIkv02CtIatYTy+DlSJxnQRvOp
|
||||
PUWpWB6kDRrk7pxJIYT4NwKwG+7xvFQA6PR3hn7fmUUcGDWMEeMVGDFkho9ja+W4
|
||||
/FB3dc/Gi+PwakS4RwWF20e1brLfNXeXICMKrHFTVYC5bIm+VgOHZW8RLa9bt7wN
|
||||
p2CPuQKBgQCjW+rCODmMzEues/I143mYMDdZ1IschtWGiXBNrpkHm/DcZSutbacm
|
||||
5C7Kwv44pfA90NHDTjuaIgRgfeUTawkrl8uPXEuj80mW72agf5oS8lJzD+2jibCj
|
||||
Q4K52G+0LaTxHLZxufil28Rgja01c0mTcuLbhKtCgHl5EHP19wOKEg==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -24,14 +24,13 @@ set (TEST_TARGET_NAMES
|
||||
# IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws
|
||||
IXStrCaseCompareTest
|
||||
IXExponentialBackoffTest
|
||||
IXWebSocketCloseTest
|
||||
)
|
||||
|
||||
# Some unittest don't work on windows yet
|
||||
# Windows without TLS does not have hmac yet
|
||||
if (UNIX)
|
||||
list(APPEND TEST_TARGET_NAMES
|
||||
IXWebSocketCloseTest
|
||||
|
||||
# Fail on Windows in CI probably because the pathing is wrong and
|
||||
# some resource files cannot be found
|
||||
IXHttpServerTest
|
||||
|
@ -19,13 +19,9 @@ TEST_CASE("dns", "[net]")
|
||||
auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80);
|
||||
|
||||
std::string errMsg;
|
||||
struct addrinfo* res;
|
||||
|
||||
res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||
auto res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||
std::cerr << "Error message: " << errMsg << std::endl;
|
||||
REQUIRE(res != nullptr);
|
||||
|
||||
dnsLookup->release(res);
|
||||
}
|
||||
|
||||
SECTION("Test resolving a non-existing hostname")
|
||||
@ -33,7 +29,7 @@ TEST_CASE("dns", "[net]")
|
||||
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
|
||||
|
||||
std::string errMsg;
|
||||
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||
auto res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||
std::cerr << "Error message: " << errMsg << std::endl;
|
||||
REQUIRE(res == nullptr);
|
||||
}
|
||||
@ -44,7 +40,7 @@ TEST_CASE("dns", "[net]")
|
||||
|
||||
std::string errMsg;
|
||||
// The callback returning true means we are requesting cancellation
|
||||
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; });
|
||||
auto res = dnsLookup->resolve(errMsg, [] { return true; });
|
||||
std::cerr << "Error message: " << errMsg << std::endl;
|
||||
REQUIRE(res == nullptr);
|
||||
}
|
||||
|
@ -7,7 +7,9 @@
|
||||
#include "catch.hpp"
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <ixwebsocket/IXGetFreePort.h>
|
||||
#include <ixwebsocket/IXHttpClient.h>
|
||||
#include <ixwebsocket/IXHttpServer.h>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
@ -95,6 +97,52 @@ TEST_CASE("http_client", "[http]")
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(IXWEBSOCKET_USE_TLS) && !defined(IXWEBSOCKET_USE_SECURE_TRANSPORT)
|
||||
SECTION("Disable hostname validation")
|
||||
{
|
||||
static auto test_cert_with_wrong_name = [](bool validate_hostname)
|
||||
{
|
||||
int port = getFreePort();
|
||||
ix::HttpServer server(port, "127.0.0.1");
|
||||
|
||||
SocketTLSOptions tlsOptionsServer;
|
||||
tlsOptionsServer.tls = true;
|
||||
tlsOptionsServer.caFile = "NONE";
|
||||
tlsOptionsServer.certFile = "./.certs/wrong-name-server-crt.pem";
|
||||
tlsOptionsServer.keyFile = "./.certs/wrong-name-server-key.pem";
|
||||
server.setTLSOptions(tlsOptionsServer);
|
||||
|
||||
auto res = server.listen();
|
||||
REQUIRE(res.first);
|
||||
server.start();
|
||||
|
||||
HttpClient httpClient;
|
||||
SocketTLSOptions tlsOptionsClient;
|
||||
tlsOptionsClient.caFile = "./.certs/trusted-ca-crt.pem";
|
||||
tlsOptionsClient.disable_hostname_validation = validate_hostname;
|
||||
httpClient.setTLSOptions(tlsOptionsClient);
|
||||
|
||||
std::string url("https://localhost:" + std::to_string(port));
|
||||
auto args = httpClient.createRequest(url);
|
||||
args->connectTimeout = 10;
|
||||
args->transferTimeout = 10;
|
||||
|
||||
auto response = httpClient.get(url, args);
|
||||
|
||||
std::cerr << "Status: " << response->statusCode << std::endl;
|
||||
std::cerr << "Error code: " << (int) response->errorCode << std::endl;
|
||||
std::cerr << "Error message: " << response->errorMsg << std::endl;
|
||||
|
||||
server.stop();
|
||||
return std::make_tuple(response->errorCode, response->statusCode);
|
||||
};
|
||||
|
||||
REQUIRE(test_cert_with_wrong_name(false) ==
|
||||
std::make_tuple(HttpErrorCode::CannotConnect, 0));
|
||||
REQUIRE(test_cert_with_wrong_name(true) == std::make_tuple(HttpErrorCode::Ok, 404));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("Async API, one call")
|
||||
{
|
||||
bool async = true;
|
||||
|
31
ws/ws.cpp
31
ws/ws.cpp
@ -77,24 +77,6 @@ namespace
|
||||
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
|
||||
}
|
||||
|
||||
// Assume the file exists
|
||||
std::string readBytes(const std::string& path)
|
||||
{
|
||||
std::vector<uint8_t> memblock;
|
||||
std::ifstream file(path);
|
||||
|
||||
file.seekg(0, file.end);
|
||||
std::streamoff size = file.tellg();
|
||||
file.seekg(0, file.beg);
|
||||
|
||||
memblock.reserve((size_t) size);
|
||||
memblock.insert(
|
||||
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
|
||||
|
||||
std::string bytes(memblock.begin(), memblock.end());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string truncate(const std::string& str, size_t n)
|
||||
{
|
||||
if (str.size() < n)
|
||||
@ -107,12 +89,6 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
bool fileExists(const std::string& fileName)
|
||||
{
|
||||
std::ifstream infile(fileName);
|
||||
return infile.good();
|
||||
}
|
||||
|
||||
std::string extractFilename(const std::string& path)
|
||||
{
|
||||
std::string::size_type idx;
|
||||
@ -916,9 +892,8 @@ namespace ix
|
||||
auto dnsLookup = std::make_shared<DNSLookup>(hostname, 80);
|
||||
|
||||
std::string errMsg;
|
||||
struct addrinfo* res;
|
||||
|
||||
res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||
auto res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||
|
||||
auto addr = res->ai_addr;
|
||||
|
||||
@ -2486,10 +2461,8 @@ int main(int argc, char** argv)
|
||||
bool verbose = false;
|
||||
bool save = false;
|
||||
bool quiet = false;
|
||||
bool fluentd = false;
|
||||
bool compress = false;
|
||||
bool compressRequest = false;
|
||||
bool stress = false;
|
||||
bool disableAutomaticReconnection = false;
|
||||
bool disablePerMessageDeflate = false;
|
||||
bool greetings = false;
|
||||
@ -2505,7 +2478,6 @@ int main(int argc, char** argv)
|
||||
int transferTimeout = 1800;
|
||||
int maxRedirects = 5;
|
||||
int delayMs = -1;
|
||||
int count = 1;
|
||||
int msgCount = 1000 * 1000;
|
||||
uint32_t maxWaitBetweenReconnectionRetries = 10 * 1000; // 10 seconds
|
||||
int pingIntervalSecs = 30;
|
||||
@ -2529,6 +2501,7 @@ int main(int argc, char** argv)
|
||||
"A (comma/space/colon) separated list of ciphers to use for TLS");
|
||||
app->add_flag("--tls", tlsOptions.tls, "Enable TLS (server only)");
|
||||
app->add_flag("--verify_none", verifyNone, "Disable peer cert verification");
|
||||
app->add_flag("--disable-hostname-validation", tlsOptions.disable_hostname_validation, "Disable validation of certificates' hostnames");
|
||||
};
|
||||
|
||||
app.add_flag("--version", version, "Print ws version");
|
||||
|
Reference in New Issue
Block a user