non blocking dns lookup
This commit is contained in:
parent
8c079787f0
commit
cbadecab33
@ -14,6 +14,7 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
ixwebsocket/IXEventFd.cpp
|
ixwebsocket/IXEventFd.cpp
|
||||||
ixwebsocket/IXSocket.cpp
|
ixwebsocket/IXSocket.cpp
|
||||||
ixwebsocket/IXSocketConnect.cpp
|
ixwebsocket/IXSocketConnect.cpp
|
||||||
|
ixwebsocket/IXDNSLookup.cpp
|
||||||
ixwebsocket/IXWebSocket.cpp
|
ixwebsocket/IXWebSocket.cpp
|
||||||
ixwebsocket/IXWebSocketTransport.cpp
|
ixwebsocket/IXWebSocketTransport.cpp
|
||||||
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
|
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
|
||||||
@ -24,6 +25,7 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/IXEventFd.h
|
ixwebsocket/IXEventFd.h
|
||||||
ixwebsocket/IXSocket.h
|
ixwebsocket/IXSocket.h
|
||||||
ixwebsocket/IXSocketConnect.h
|
ixwebsocket/IXSocketConnect.h
|
||||||
|
ixwebsocket/IXDNSLookup.h
|
||||||
ixwebsocket/IXWebSocket.h
|
ixwebsocket/IXWebSocket.h
|
||||||
ixwebsocket/IXWebSocketTransport.h
|
ixwebsocket/IXWebSocketTransport.h
|
||||||
ixwebsocket/IXWebSocketSendInfo.h
|
ixwebsocket/IXWebSocketSendInfo.h
|
||||||
|
@ -9,6 +9,7 @@ RUN apt-get -y install screen
|
|||||||
RUN apt-get -y install procps
|
RUN apt-get -y install procps
|
||||||
RUN apt-get -y install lsof
|
RUN apt-get -y install lsof
|
||||||
RUN apt-get -y install libz-dev
|
RUN apt-get -y install libz-dev
|
||||||
|
RUN apt-get -y install vim
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ g++ --std=c++11 \
|
|||||||
../../ixwebsocket/IXSocket.cpp \
|
../../ixwebsocket/IXSocket.cpp \
|
||||||
../../ixwebsocket/IXWebSocketTransport.cpp \
|
../../ixwebsocket/IXWebSocketTransport.cpp \
|
||||||
../../ixwebsocket/IXWebSocket.cpp \
|
../../ixwebsocket/IXWebSocket.cpp \
|
||||||
|
../../ixwebsocket/IXDNSLookup.cpp \
|
||||||
../../ixwebsocket/IXSocketConnect.cpp \
|
../../ixwebsocket/IXSocketConnect.cpp \
|
||||||
../../ixwebsocket/IXSocketOpenSSL.cpp \
|
../../ixwebsocket/IXSocketOpenSSL.cpp \
|
||||||
../../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
|
../../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
|
||||||
|
15
ixwebsocket/IXCancellationRequest.h
Normal file
15
ixwebsocket/IXCancellationRequest.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* IXCancellationRequest.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
using CancellationRequest = std::function<bool()>;
|
||||||
|
}
|
||||||
|
|
148
ixwebsocket/IXDNSLookup.cpp
Normal file
148
ixwebsocket/IXDNSLookup.cpp
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* IXDNSLookup.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IXDNSLookup.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
// 60s timeout, see IXSocketConnect.cpp
|
||||||
|
const int64_t DNSLookup::kDefaultTimeout = 60 * 1000; // ms
|
||||||
|
const int64_t DNSLookup::kDefaultWait = 10; // ms
|
||||||
|
|
||||||
|
DNSLookup::DNSLookup(const std::string& hostname, int port, int wait) :
|
||||||
|
_hostname(hostname),
|
||||||
|
_port(port),
|
||||||
|
_res(nullptr),
|
||||||
|
_done(false),
|
||||||
|
_wait(wait)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSLookup::~DNSLookup()
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
||||||
|
int port,
|
||||||
|
std::string& errMsg)
|
||||||
|
{
|
||||||
|
struct addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
std::string sport = std::to_string(port);
|
||||||
|
|
||||||
|
struct addrinfo* res;
|
||||||
|
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(),
|
||||||
|
&hints, &res);
|
||||||
|
if (getaddrinfo_result)
|
||||||
|
{
|
||||||
|
errMsg = gai_strerror(getaddrinfo_result);
|
||||||
|
res = nullptr;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
|
||||||
|
const CancellationRequest& isCancellationRequested,
|
||||||
|
bool blocking)
|
||||||
|
{
|
||||||
|
return blocking ? resolveBlocking(errMsg, isCancellationRequested)
|
||||||
|
: resolveAsync(errMsg, isCancellationRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo* DNSLookup::resolveBlocking(std::string& errMsg,
|
||||||
|
const CancellationRequest& isCancellationRequested)
|
||||||
|
{
|
||||||
|
errMsg = "no error";
|
||||||
|
|
||||||
|
// Maybe a cancellation request got in before the background thread terminated ?
|
||||||
|
if (isCancellationRequested())
|
||||||
|
{
|
||||||
|
errMsg = "cancellation requested";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAddrInfo(_hostname, _port, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo* DNSLookup::resolveAsync(std::string& errMsg,
|
||||||
|
const CancellationRequest& isCancellationRequested)
|
||||||
|
{
|
||||||
|
errMsg = "no error";
|
||||||
|
|
||||||
|
// Can only be called once, otherwise we would have to manage a pool
|
||||||
|
// of background thread which is overkill for our usage.
|
||||||
|
if (_done)
|
||||||
|
{
|
||||||
|
return nullptr; // programming error, create a second DNSLookup instance
|
||||||
|
// if you need a second lookup.
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Good resource on thread forced termination
|
||||||
|
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
|
||||||
|
//
|
||||||
|
_thread = std::thread(&DNSLookup::run, this);
|
||||||
|
_thread.detach();
|
||||||
|
|
||||||
|
int64_t timeout = kDefaultTimeout;
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
|
while (!_done)
|
||||||
|
{
|
||||||
|
// Wait for 10 milliseconds on the condition variable, to see
|
||||||
|
// if the bg thread has terminated.
|
||||||
|
if (_condition.wait_for(lock, std::chrono::milliseconds(_wait)) == std::cv_status::no_timeout)
|
||||||
|
{
|
||||||
|
// Background thread has terminated, so we can break of this loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Were we cancelled ?
|
||||||
|
if (isCancellationRequested())
|
||||||
|
{
|
||||||
|
errMsg = "cancellation requested";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have we exceeded the timeout ?
|
||||||
|
timeout -= _wait;
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
errMsg = "dns lookup timed out after 60 seconds";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe a cancellation request got in before the bg terminated ?
|
||||||
|
if (isCancellationRequested())
|
||||||
|
{
|
||||||
|
errMsg = "cancellation requested";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSLookup::run()
|
||||||
|
{
|
||||||
|
_res = getAddrInfo(_hostname, _port, _errMsg);
|
||||||
|
_condition.notify_one();
|
||||||
|
_done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
ixwebsocket/IXDNSLookup.h
Normal file
61
ixwebsocket/IXDNSLookup.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* IXDNSLookup.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Resolve a hostname+port to a struct addrinfo obtained with getaddrinfo
|
||||||
|
* Does this in a background thread so that it can be cancelled, since
|
||||||
|
* getaddrinfo is a blocking call, and we don't want to block the main thread on Mobile.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
struct addrinfo;
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
class DNSLookup {
|
||||||
|
public:
|
||||||
|
DNSLookup(const std::string& hostname,
|
||||||
|
int port,
|
||||||
|
int wait = DNSLookup::kDefaultWait);
|
||||||
|
~DNSLookup();
|
||||||
|
|
||||||
|
struct addrinfo* resolve(std::string& errMsg,
|
||||||
|
const CancellationRequest& isCancellationRequested,
|
||||||
|
bool blocking = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct addrinfo* resolveAsync(std::string& errMsg,
|
||||||
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
struct addrinfo* resolveBlocking(std::string& errMsg,
|
||||||
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
|
||||||
|
static struct addrinfo* getAddrInfo(const std::string& hostname,
|
||||||
|
int port,
|
||||||
|
std::string& errMsg);
|
||||||
|
|
||||||
|
void run(); // thread runner
|
||||||
|
|
||||||
|
std::string _hostname;
|
||||||
|
int _port;
|
||||||
|
int _wait;
|
||||||
|
std::string _errMsg;
|
||||||
|
struct addrinfo* _res;
|
||||||
|
|
||||||
|
std::atomic<bool> _done;
|
||||||
|
std::thread _thread;
|
||||||
|
std::condition_variable _condition;
|
||||||
|
std::mutex _mutex;
|
||||||
|
|
||||||
|
const static int64_t kDefaultTimeout;
|
||||||
|
const static int64_t kDefaultWait;
|
||||||
|
};
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
|
#include "IXSocketConnect.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <basetsd.h>
|
# include <basetsd.h>
|
||||||
@ -79,7 +80,7 @@ namespace ix
|
|||||||
bool Socket::connect(const std::string& host,
|
bool Socket::connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
std::lock_guard<std::mutex> lock(_socketMutex);
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "IXEventFd.h"
|
#include "IXEventFd.h"
|
||||||
#include "IXSocketConnect.h"
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
struct addrinfo;
|
struct addrinfo;
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ namespace ix
|
|||||||
virtual bool connect(const std::string& url,
|
virtual bool connect(const std::string& url,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
virtual void close();
|
virtual void close();
|
||||||
|
|
||||||
virtual int send(char* buffer, size_t length);
|
virtual int send(char* buffer, size_t length);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* Adapted from Satori SDK Apple SSL code.
|
* Adapted from Satori SDK Apple SSL code.
|
||||||
*/
|
*/
|
||||||
#include "IXSocketAppleSSL.h"
|
#include "IXSocketAppleSSL.h"
|
||||||
|
#include "IXSocketConnect.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
@ -157,7 +158,7 @@ namespace ix
|
|||||||
bool SocketAppleSSL::connect(const std::string& host,
|
bool SocketAppleSSL::connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
OSStatus status;
|
OSStatus status;
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
#include "IXSocketConnect.h"
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
#include <Security/Security.h>
|
#include <Security/Security.h>
|
||||||
#include <Security/SecureTransport.h>
|
#include <Security/SecureTransport.h>
|
||||||
@ -25,7 +25,7 @@ namespace ix
|
|||||||
virtual bool connect(const std::string& host,
|
virtual bool connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested) final;
|
const CancellationRequest& isCancellationRequested) final;
|
||||||
virtual void close() final;
|
virtual void close() final;
|
||||||
|
|
||||||
virtual int send(char* buffer, size_t length) final;
|
virtual int send(char* buffer, size_t length) final;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXSocketConnect.h"
|
#include "IXSocketConnect.h"
|
||||||
|
#include "IXDNSLookup.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <basetsd.h>
|
# include <basetsd.h>
|
||||||
@ -47,10 +48,15 @@ namespace
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// This function can be cancelled every 50 ms
|
||||||
|
// This is important so that we don't block the main UI thread when shutting down a connection which is
|
||||||
|
// already trying to reconnect, and can be blocked waiting for ::connect to respond.
|
||||||
|
//
|
||||||
bool SocketConnect::connectToAddress(const struct addrinfo *address,
|
bool SocketConnect::connectToAddress(const struct addrinfo *address,
|
||||||
int& sockfd,
|
int& sockfd,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
sockfd = -1;
|
sockfd = -1;
|
||||||
|
|
||||||
@ -64,20 +70,17 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the socket to non blocking mode, so that slow responses cannot
|
// Set the socket to non blocking mode, so that slow responses cannot
|
||||||
// block us for too long while we are trying to shut-down.
|
// block us for too long
|
||||||
SocketConnect::configure(fd);
|
SocketConnect::configure(fd);
|
||||||
|
|
||||||
if (::connect(fd, address->ai_addr, address->ai_addrlen) == -1
|
if (::connect(fd, address->ai_addr, address->ai_addrlen) == -1
|
||||||
&& errno != EINPROGRESS)
|
&& errno != EINPROGRESS)
|
||||||
{
|
{
|
||||||
closeSocket(fd);
|
closeSocket(fd);
|
||||||
sockfd = -1;
|
|
||||||
errMsg = strerror(errno);
|
errMsg = strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "I WAS HERE A" << std::endl;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// If during a connection attempt the request remains idle for longer
|
// If during a connection attempt the request remains idle for longer
|
||||||
// than the timeout interval, the request is considered to have timed
|
// than the timeout interval, the request is considered to have timed
|
||||||
@ -95,7 +98,6 @@ namespace ix
|
|||||||
if (isCancellationRequested())
|
if (isCancellationRequested())
|
||||||
{
|
{
|
||||||
closeSocket(fd);
|
closeSocket(fd);
|
||||||
sockfd = -1;
|
|
||||||
errMsg = "Cancelled";
|
errMsg = "Cancelled";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -115,7 +117,7 @@ namespace ix
|
|||||||
if (!FD_ISSET(fd, &wfds)) continue;
|
if (!FD_ISSET(fd, &wfds)) continue;
|
||||||
|
|
||||||
// Something was written to the socket
|
// Something was written to the socket
|
||||||
int optval;
|
int optval = -1;
|
||||||
socklen_t optlen = sizeof(optval);
|
socklen_t optlen = sizeof(optval);
|
||||||
|
|
||||||
// getsockopt() puts the errno value for connect into optval so 0
|
// getsockopt() puts the errno value for connect into optval so 0
|
||||||
@ -124,7 +126,6 @@ namespace ix
|
|||||||
optval != 0)
|
optval != 0)
|
||||||
{
|
{
|
||||||
closeSocket(fd);
|
closeSocket(fd);
|
||||||
sockfd = -1;
|
|
||||||
errMsg = strerror(optval);
|
errMsg = strerror(optval);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -137,33 +138,22 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
closeSocket(fd);
|
closeSocket(fd);
|
||||||
sockfd = -1;
|
errMsg = "connect timed out after 60 seconds";
|
||||||
errMsg = strerror(errno);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SocketConnect::connect(const std::string& hostname,
|
int SocketConnect::connect(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// First do DNS resolution
|
// First do DNS resolution
|
||||||
//
|
//
|
||||||
struct addrinfo hints;
|
DNSLookup dnsLookup(hostname, port);
|
||||||
memset(&hints, 0, sizeof(hints));
|
struct addrinfo *res = dnsLookup.resolve(errMsg, isCancellationRequested);
|
||||||
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
|
if (res == nullptr)
|
||||||
hints.ai_family = AF_UNSPEC;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
|
|
||||||
std::string sport = std::to_string(port);
|
|
||||||
|
|
||||||
struct addrinfo *res = nullptr;
|
|
||||||
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(),
|
|
||||||
&hints, &res);
|
|
||||||
if (getaddrinfo_result)
|
|
||||||
{
|
{
|
||||||
errMsg = gai_strerror(getaddrinfo_result);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,15 +173,18 @@ namespace ix
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketConnect::configure(int sockfd)
|
void SocketConnect::configure(int sockfd)
|
||||||
{
|
{
|
||||||
|
// 1. disable Nagle's algorithm
|
||||||
int flag = 1;
|
int flag = 1;
|
||||||
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm
|
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag));
|
||||||
|
|
||||||
|
// 2. make socket non blocking
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
unsigned long nonblocking = 1;
|
unsigned long nonblocking = 1;
|
||||||
ioctlsocket(_sockfd, FIONBIO, &nonblocking);
|
ioctlsocket(_sockfd, FIONBIO, &nonblocking);
|
||||||
@ -199,6 +192,7 @@ namespace ix
|
|||||||
fcntl(sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking
|
fcntl(sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// 3. (apple) prevent SIGPIPE from being emitted when the remote end disconnect
|
||||||
#ifdef SO_NOSIGPIPE
|
#ifdef SO_NOSIGPIPE
|
||||||
int value = 1;
|
int value = 1;
|
||||||
setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
|
setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
|
||||||
|
@ -6,26 +6,26 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
struct addrinfo;
|
struct addrinfo;
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
using CancellationRequest = std::function<bool()>;
|
|
||||||
|
|
||||||
class SocketConnect {
|
class SocketConnect {
|
||||||
public:
|
public:
|
||||||
static int connect(const std::string& hostname,
|
static int connect(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
|
||||||
|
private:
|
||||||
static bool connectToAddress(const struct addrinfo *address,
|
static bool connectToAddress(const struct addrinfo *address,
|
||||||
int& sockfd,
|
int& sockfd,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
|
||||||
static void configure(int sockfd);
|
static void configure(int sockfd);
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXSocketOpenSSL.h"
|
#include "IXSocketOpenSSL.h"
|
||||||
|
#include "IXSocketConnect.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -275,7 +277,7 @@ namespace ix
|
|||||||
bool SocketOpenSSL::connect(const std::string& host,
|
bool SocketOpenSSL::connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
bool handshakeSuccessful = false;
|
bool handshakeSuccessful = false;
|
||||||
{
|
{
|
||||||
@ -415,10 +417,8 @@ namespace ix
|
|||||||
if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
|
if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
|
||||||
{
|
{
|
||||||
errno = EWOULDBLOCK;
|
errno = EWOULDBLOCK;
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
#include "IXSocketConnect.h"
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
#include <openssl/bio.h>
|
#include <openssl/bio.h>
|
||||||
#include <openssl/hmac.h>
|
#include <openssl/hmac.h>
|
||||||
@ -28,7 +28,7 @@ namespace ix
|
|||||||
virtual bool connect(const std::string& host,
|
virtual bool connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
CancellationRequest isCancellationRequested) final;
|
const CancellationRequest& isCancellationRequested) final;
|
||||||
virtual void close() final;
|
virtual void close() final;
|
||||||
|
|
||||||
virtual int send(char* buffer, size_t length) final;
|
virtual int send(char* buffer, size_t length) final;
|
||||||
|
@ -40,7 +40,10 @@ namespace ix
|
|||||||
{
|
{
|
||||||
WebSocketTransport::WebSocketTransport() :
|
WebSocketTransport::WebSocketTransport() :
|
||||||
_readyState(CLOSED),
|
_readyState(CLOSED),
|
||||||
_enablePerMessageDeflate(false)
|
_enablePerMessageDeflate(false),
|
||||||
|
_closeCode(0),
|
||||||
|
_closeWireSize(0),
|
||||||
|
_requestInitCancellation(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -90,6 +93,12 @@ namespace ix
|
|||||||
{
|
{
|
||||||
port = 443;
|
port = 443;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Invalid protocol. Should be caught by regex check
|
||||||
|
// but this missing branch trigger cpplint linter.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -164,6 +173,8 @@ namespace ix
|
|||||||
std::string protocol, host, path, query;
|
std::string protocol, host, path, query;
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
|
_requestInitCancellation = false;
|
||||||
|
|
||||||
if (!WebSocketTransport::parseUrl(_url, protocol, host,
|
if (!WebSocketTransport::parseUrl(_url, protocol, host,
|
||||||
path, query, port))
|
path, query, port))
|
||||||
{
|
{
|
||||||
@ -192,9 +203,9 @@ namespace ix
|
|||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
bool success = _socket->connect(host, port, errMsg,
|
bool success = _socket->connect(host, port, errMsg,
|
||||||
[this]
|
[this]() -> bool
|
||||||
{
|
{
|
||||||
return _readyState == CLOSING;
|
return _requestInitCancellation;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (!success)
|
if (!success)
|
||||||
@ -231,9 +242,7 @@ namespace ix
|
|||||||
|
|
||||||
ss << "\r\n";
|
ss << "\r\n";
|
||||||
|
|
||||||
std::string request = ss.str();
|
if (!writeBytes(ss.str()))
|
||||||
int requestSize = (int) request.size();
|
|
||||||
if (_socket->send(const_cast<char*>(request.c_str()), requestSize) != requestSize)
|
|
||||||
{
|
{
|
||||||
return WebSocketInitResult(false, 0, std::string("Failed sending GET request to ") + _url);
|
return WebSocketInitResult(false, 0, std::string("Failed sending GET request to ") + _url);
|
||||||
}
|
}
|
||||||
@ -369,6 +378,7 @@ namespace ix
|
|||||||
_onCloseCallback(_closeCode, _closeReason, _closeWireSize);
|
_onCloseCallback(_closeCode, _closeReason, _closeWireSize);
|
||||||
_closeCode = 0;
|
_closeCode = 0;
|
||||||
_closeReason = std::string();
|
_closeReason = std::string();
|
||||||
|
_closeWireSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_readyState = readyStateValue;
|
_readyState = readyStateValue;
|
||||||
@ -792,6 +802,8 @@ namespace ix
|
|||||||
|
|
||||||
void WebSocketTransport::close()
|
void WebSocketTransport::close()
|
||||||
{
|
{
|
||||||
|
_requestInitCancellation = true;
|
||||||
|
|
||||||
if (_readyState == CLOSING || _readyState == CLOSED) return;
|
if (_readyState == CLOSING || _readyState == CLOSED) return;
|
||||||
|
|
||||||
// See list of close events here:
|
// See list of close events here:
|
||||||
@ -810,7 +822,6 @@ namespace ix
|
|||||||
_socket->close();
|
_socket->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by init
|
|
||||||
bool WebSocketTransport::readByte(void* buffer)
|
bool WebSocketTransport::readByte(void* buffer)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
@ -839,4 +850,34 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WebSocketTransport::writeBytes(const std::string& str)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (_readyState == CLOSING) return false;
|
||||||
|
|
||||||
|
char* buffer = const_cast<char*>(str.c_str());
|
||||||
|
int len = (int) str.size();
|
||||||
|
|
||||||
|
int ret = _socket->send(buffer, len);
|
||||||
|
|
||||||
|
// We wrote some bytes, as needed, all good.
|
||||||
|
if (ret > 0)
|
||||||
|
{
|
||||||
|
return ret == len;
|
||||||
|
}
|
||||||
|
// There is possibly something to be write, try again
|
||||||
|
else if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
||||||
|
_socket->getErrno() == EAGAIN))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// There was an error during the write, abort
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -136,6 +136,9 @@ namespace ix
|
|||||||
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
|
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
|
||||||
std::atomic<bool> _enablePerMessageDeflate;
|
std::atomic<bool> _enablePerMessageDeflate;
|
||||||
|
|
||||||
|
// Used to cancel dns lookup + socket connect + http upgrade
|
||||||
|
std::atomic<bool> _requestInitCancellation;
|
||||||
|
|
||||||
void sendOnSocket();
|
void sendOnSocket();
|
||||||
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
@ -158,6 +161,8 @@ namespace ix
|
|||||||
void unmaskReceiveBuffer(const wsheader_type& ws);
|
void unmaskReceiveBuffer(const wsheader_type& ws);
|
||||||
std::string genRandomString(const int len);
|
std::string genRandomString(const int len);
|
||||||
|
|
||||||
|
// Non blocking versions of read/write, used during http upgrade
|
||||||
bool readByte(void* buffer);
|
bool readByte(void* buffer);
|
||||||
|
bool writeBytes(const std::string& str);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user