add cancellation support while connecting, to speed up WebSocket::stop

This commit is contained in:
Benjamin Sergeant 2018-12-09 17:56:20 -08:00
parent 5bf1b91528
commit 50b638f7fd
13 changed files with 69 additions and 25 deletions

View File

@ -8,6 +8,7 @@ RUN apt-get -y install gdb
RUN apt-get -y install screen 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
COPY . . COPY . .

View File

@ -13,8 +13,11 @@ g++ --std=c++11 \
../../ixwebsocket/IXSocket.cpp \ ../../ixwebsocket/IXSocket.cpp \
../../ixwebsocket/IXWebSocketTransport.cpp \ ../../ixwebsocket/IXWebSocketTransport.cpp \
../../ixwebsocket/IXWebSocket.cpp \ ../../ixwebsocket/IXWebSocket.cpp \
../../ixwebsocket/IXSocketConnect.cpp \
../../ixwebsocket/IXSocketOpenSSL.cpp \ ../../ixwebsocket/IXSocketOpenSSL.cpp \
../../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
../../ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp \
-I ../.. \ -I ../.. \
ws_connect.cpp \ ws_connect.cpp \
-o ws_connect \ -o ws_connect \
-lcrypto -lssl -lpthread -lcrypto -lssl -lz -lpthread

View File

@ -5,7 +5,6 @@
*/ */
#include "IXSocket.h" #include "IXSocket.h"
#include "IXSocketConnect.h"
#ifdef _WIN32 #ifdef _WIN32
# include <basetsd.h> # include <basetsd.h>
@ -79,13 +78,14 @@ 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)
{ {
std::lock_guard<std::mutex> lock(_socketMutex); std::lock_guard<std::mutex> lock(_socketMutex);
if (!_eventfd.clear()) return false; if (!_eventfd.clear()) return false;
_sockfd = SocketConnect::connect(host, port, errMsg); _sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
return _sockfd != -1; return _sockfd != -1;
} }

View File

@ -12,6 +12,7 @@
#include <atomic> #include <atomic>
#include "IXEventFd.h" #include "IXEventFd.h"
#include "IXSocketConnect.h"
struct addrinfo; struct addrinfo;
@ -32,7 +33,8 @@ namespace ix
// Virtual methods // Virtual methods
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);
virtual void close(); virtual void close();
virtual int send(char* buffer, size_t length); virtual int send(char* buffer, size_t length);

View File

@ -6,7 +6,6 @@
* 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,13 +156,14 @@ namespace ix
// No wait support // No wait support
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)
{ {
OSStatus status; OSStatus status;
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
_sockfd = SocketConnect::connect(host, port, errMsg); _sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1) return false; if (_sockfd == -1) return false;
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);

View File

@ -7,6 +7,7 @@
#pragma once #pragma once
#include "IXSocket.h" #include "IXSocket.h"
#include "IXSocketConnect.h"
#include <Security/Security.h> #include <Security/Security.h>
#include <Security/SecureTransport.h> #include <Security/SecureTransport.h>
@ -23,7 +24,8 @@ namespace ix
virtual bool connect(const std::string& host, virtual bool connect(const std::string& host,
int port, int port,
std::string& errMsg) final; std::string& errMsg,
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;

View File

@ -23,6 +23,7 @@
# include <sys/stat.h> # include <sys/stat.h>
#endif #endif
#include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/types.h> #include <sys/types.h>
@ -32,7 +33,6 @@
# include <linux/tcp.h> # include <linux/tcp.h>
#endif #endif
namespace namespace
{ {
void closeSocket(int fd) void closeSocket(int fd)
@ -49,7 +49,8 @@ namespace ix
{ {
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)
{ {
sockfd = -1; sockfd = -1;
@ -75,10 +76,30 @@ namespace ix
return false; return false;
} }
// 10 seconds timeout, each time we wait for 50ms with select -> 200 attempts // std::cout << "I WAS HERE A" << std::endl;
int maxRetries = 200;
//
// If during a connection attempt the request remains idle for longer
// than the timeout interval, the request is considered to have timed
// out. The default timeout interval is 60 seconds.
//
// See https://developer.apple.com/documentation/foundation/nsmutableurlrequest/1414063-timeoutinterval?language=objc
//
// 60 seconds timeout, each time we wait for 50ms with select -> 1200 attempts
//
int selectTimeOut = 50 * 1000; // In micro-seconds => 50ms
int maxRetries = 60 * 1000 * 1000 / selectTimeOut;
for (int i = 0; i < maxRetries; ++i) for (int i = 0; i < maxRetries; ++i)
{ {
if (isCancellationRequested())
{
closeSocket(fd);
sockfd = -1;
errMsg = "Cancelled";
return false;
}
fd_set wfds; fd_set wfds;
FD_ZERO(&wfds); FD_ZERO(&wfds);
FD_SET(fd, &wfds); FD_SET(fd, &wfds);
@ -86,7 +107,7 @@ namespace ix
// 50ms timeout // 50ms timeout
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 1000 * 50; timeout.tv_usec = selectTimeOut;
select(fd + 1, nullptr, &wfds, nullptr, &timeout); select(fd + 1, nullptr, &wfds, nullptr, &timeout);
@ -94,8 +115,8 @@ 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 = -1; int optval;
socklen_t optlen; 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
// means no-error. // means no-error.
@ -123,7 +144,8 @@ namespace ix
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)
{ {
// //
// First do DNS resolution // First do DNS resolution
@ -155,7 +177,7 @@ namespace ix
// //
// Second try to connect to the remote host // Second try to connect to the remote host
// //
success = connectToAddress(address, sockfd, errMsg); success = connectToAddress(address, sockfd, errMsg, isCancellationRequested);
if (success) if (success)
{ {
break; break;

View File

@ -7,20 +7,25 @@
#pragma once #pragma once
#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);
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);
static void configure(int sockfd); static void configure(int sockfd);
}; };

View File

@ -7,7 +7,6 @@
*/ */
#include "IXSocketOpenSSL.h" #include "IXSocketOpenSSL.h"
#include "IXSocketConnect.h"
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
@ -275,7 +274,8 @@ namespace ix
// No wait support // No wait support
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)
{ {
bool handshakeSuccessful = false; bool handshakeSuccessful = false;
{ {
@ -286,7 +286,7 @@ namespace ix
return false; return false;
} }
_sockfd = SocketConnect::connect(host, port, errMsg); _sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1) return false; if (_sockfd == -1) return false;
_ssl_context = openSSLCreateContext(errMsg); _ssl_context = openSSLCreateContext(errMsg);

View File

@ -7,6 +7,7 @@
#pragma once #pragma once
#include "IXSocket.h" #include "IXSocket.h"
#include "IXSocketConnect.h"
#include <openssl/bio.h> #include <openssl/bio.h>
#include <openssl/hmac.h> #include <openssl/hmac.h>
@ -26,7 +27,8 @@ namespace ix
virtual bool connect(const std::string& host, virtual bool connect(const std::string& host,
int port, int port,
std::string& errMsg) final; std::string& errMsg,
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;

View File

@ -50,6 +50,7 @@
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>
#include <string.h>
namespace namespace
{ {

View File

@ -36,6 +36,7 @@
#include "zlib.h" #include "zlib.h"
#include <string> #include <string>
#include <memory>
namespace ix namespace ix
{ {

View File

@ -191,7 +191,12 @@ namespace ix
} }
std::string errMsg; std::string errMsg;
bool success = _socket->connect(host, port, errMsg); bool success = _socket->connect(host, port, errMsg,
[this]
{
return _readyState == CLOSING;
}
);
if (!success) if (!success)
{ {
std::stringstream ss; std::stringstream ss;