spdycat: Handle timeout in connect and SSL/TLS handshake
This commit is contained in:
parent
a28e1c6e7d
commit
22840dbfaf
|
@ -68,6 +68,7 @@ struct Config {
|
||||||
bool get_assets;
|
bool get_assets;
|
||||||
bool stat;
|
bool stat;
|
||||||
int spdy_version;
|
int spdy_version;
|
||||||
|
// milliseconds
|
||||||
int timeout;
|
int timeout;
|
||||||
std::string certfile;
|
std::string certfile;
|
||||||
std::string keyfile;
|
std::string keyfile;
|
||||||
|
@ -396,13 +397,6 @@ void on_stream_close_callback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t time_delta(const timeval& a, const timeval& b)
|
|
||||||
{
|
|
||||||
int64_t res = (a.tv_sec - b.tv_sec) * 1000;
|
|
||||||
res += (a.tv_usec - b.tv_usec) / 1000;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_stats(const SpdySession& spdySession)
|
void print_stats(const SpdySession& spdySession)
|
||||||
{
|
{
|
||||||
std::cout << "***** Statistics *****" << std::endl;
|
std::cout << "***** Statistics *****" << std::endl;
|
||||||
|
@ -440,11 +434,19 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
SpdySession& spdySession,
|
SpdySession& spdySession,
|
||||||
const spdylay_session_callbacks *callbacks)
|
const spdylay_session_callbacks *callbacks)
|
||||||
{
|
{
|
||||||
int fd = connect_to(host, port);
|
int rv;
|
||||||
|
int timeout = config.timeout;
|
||||||
|
int fd = nonblock_connect_to(host, port, timeout);
|
||||||
if(fd == -1) {
|
if(fd == -1) {
|
||||||
std::cerr << "Could not connect to the host" << std::endl;
|
std::cerr << "Could not connect to the host" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
|
} else if(fd == -2) {
|
||||||
|
std::cerr << "Request to " << spdySession.hostport << " timed out "
|
||||||
|
<< "during establishing connection."
|
||||||
|
<< std::endl;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
set_tcp_nodelay(fd);
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
ssl_ctx = SSL_CTX_new(TLSv1_client_method());
|
ssl_ctx = SSL_CTX_new(TLSv1_client_method());
|
||||||
if(!ssl_ctx) {
|
if(!ssl_ctx) {
|
||||||
|
@ -484,12 +486,17 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
|
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(ssl_handshake(ssl, fd) == -1) {
|
rv = ssl_nonblock_handshake(ssl, fd, timeout);
|
||||||
|
if(rv == -1) {
|
||||||
|
return -1;
|
||||||
|
} else if(rv == -2) {
|
||||||
|
std::cerr << "Request to " << spdySession.hostport
|
||||||
|
<< " timed out in SSL/TLS handshake."
|
||||||
|
<< std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdySession.record_handshake_time();
|
spdySession.record_handshake_time();
|
||||||
make_non_block(fd);
|
|
||||||
set_tcp_nodelay(fd);
|
|
||||||
int spdy_version = spdylay_npn_get_version(
|
int spdy_version = spdylay_npn_get_version(
|
||||||
reinterpret_cast<const unsigned char*>(next_proto.c_str()),
|
reinterpret_cast<const unsigned char*>(next_proto.c_str()),
|
||||||
next_proto.size());
|
next_proto.size());
|
||||||
|
@ -516,11 +523,13 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
}
|
}
|
||||||
pollfds[0].fd = fd;
|
pollfds[0].fd = fd;
|
||||||
ctl_poll(pollfds, &sc);
|
ctl_poll(pollfds, &sc);
|
||||||
int end_time = time(NULL) + config.timeout;
|
|
||||||
int timeout = config.timeout;
|
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
timeval tv1, tv2;
|
||||||
while(!sc.finish()) {
|
while(!sc.finish()) {
|
||||||
|
if(config.timeout != -1) {
|
||||||
|
gettimeofday(&tv1, 0);
|
||||||
|
}
|
||||||
int nfds = poll(pollfds, npollfds, timeout);
|
int nfds = poll(pollfds, npollfds, timeout);
|
||||||
if(nfds == -1) {
|
if(nfds == -1) {
|
||||||
perror("poll");
|
perror("poll");
|
||||||
|
@ -543,11 +552,15 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
ok = false;
|
ok = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
timeout = timeout == -1 ? timeout : end_time - time(NULL);
|
if(config.timeout != -1) {
|
||||||
if (config.timeout != -1 && timeout <= 0) {
|
gettimeofday(&tv2, 0);
|
||||||
std::cout << "Requests to " << spdySession.hostport << "timed out.";
|
timeout -= time_delta(tv2, tv1);
|
||||||
ok = false;
|
if (timeout <= 0) {
|
||||||
break;
|
std::cerr << "Requests to " << spdySession.hostport << " timed out."
|
||||||
|
<< std::endl;
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assert(ok);
|
assert(ok);
|
||||||
ctl_poll(pollfds, &sc);
|
ctl_poll(pollfds, &sc);
|
||||||
|
@ -700,7 +713,7 @@ int main(int argc, char **argv)
|
||||||
config.spdy_version = SPDYLAY_PROTO_SPDY3;
|
config.spdy_version = SPDYLAY_PROTO_SPDY3;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
config.timeout = atoi(optarg);
|
config.timeout = atoi(optarg) * 1000;
|
||||||
break;
|
break;
|
||||||
case 'w': {
|
case 'w': {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -234,6 +235,74 @@ int connect_to(const std::string& host, uint16_t port)
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nonblock_connect_to(const std::string& host, uint16_t port, int timeout)
|
||||||
|
{
|
||||||
|
struct addrinfo hints;
|
||||||
|
int fd = -1;
|
||||||
|
int r;
|
||||||
|
char service[10];
|
||||||
|
snprintf(service, sizeof(service), "%u", port);
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
struct addrinfo *res;
|
||||||
|
r = getaddrinfo(host.c_str(), service, &hints, &res);
|
||||||
|
if(r != 0) {
|
||||||
|
std::cerr << "getaddrinfo: " << gai_strerror(r) << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for(struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
||||||
|
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
|
if(fd == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(make_non_block(fd) == -1) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
while((r = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||||
|
errno == EINTR);
|
||||||
|
if(r == 0) {
|
||||||
|
break;
|
||||||
|
} else if(errno == EINPROGRESS) {
|
||||||
|
struct timeval tv1, tv2;
|
||||||
|
struct pollfd pfd = {fd, POLLOUT, 0};
|
||||||
|
if(timeout != -1) {
|
||||||
|
gettimeofday(&tv1, 0);
|
||||||
|
}
|
||||||
|
r = poll(&pfd, 1, timeout);
|
||||||
|
if(r == 0) {
|
||||||
|
return -2;
|
||||||
|
} else if(r == -1) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if(timeout != -1) {
|
||||||
|
gettimeofday(&tv2, 0);
|
||||||
|
timeout -= time_delta(tv2, tv1);
|
||||||
|
if(timeout <= 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int socket_error;
|
||||||
|
socklen_t optlen = sizeof(socket_error);
|
||||||
|
r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen);
|
||||||
|
if(r == 0 && socket_error == 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
int make_listen_socket(const std::string& host, uint16_t port, int family)
|
int make_listen_socket(const std::string& host, uint16_t port, int family)
|
||||||
{
|
{
|
||||||
addrinfo hints;
|
addrinfo hints;
|
||||||
|
@ -664,6 +733,65 @@ int ssl_handshake(SSL *ssl, int fd)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ssl_nonblock_handshake(SSL *ssl, int fd, int& timeout)
|
||||||
|
{
|
||||||
|
if(SSL_set_fd(ssl, fd) == 0) {
|
||||||
|
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ERR_clear_error();
|
||||||
|
pollfd pfd;
|
||||||
|
pfd.fd = fd;
|
||||||
|
pfd.events = POLLOUT;
|
||||||
|
timeval tv1, tv2;
|
||||||
|
while(1) {
|
||||||
|
if(timeout != -1) {
|
||||||
|
gettimeofday(&tv1, 0);
|
||||||
|
}
|
||||||
|
int rv = poll(&pfd, 1, timeout);
|
||||||
|
if(rv == 0) {
|
||||||
|
return -2;
|
||||||
|
} else if(rv == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ERR_clear_error();
|
||||||
|
rv = SSL_connect(ssl);
|
||||||
|
if(rv == 0) {
|
||||||
|
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
|
||||||
|
return -1;
|
||||||
|
} else if(rv < 0) {
|
||||||
|
if(timeout != -1) {
|
||||||
|
gettimeofday(&tv2, 0);
|
||||||
|
timeout -= time_delta(tv2, tv1);
|
||||||
|
if(timeout <= 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch(SSL_get_error(ssl, rv)) {
|
||||||
|
case SSL_ERROR_WANT_READ:
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
break;
|
||||||
|
case SSL_ERROR_WANT_WRITE:
|
||||||
|
pfd.events = POLLOUT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t time_delta(const timeval& a, const timeval& b)
|
||||||
|
{
|
||||||
|
int64_t res = (a.tv_sec - b.tv_sec) * 1000;
|
||||||
|
res += (a.tv_usec - b.tv_usec) / 1000;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
timeval base_tv;
|
timeval base_tv;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -73,6 +73,8 @@ private:
|
||||||
|
|
||||||
int connect_to(const std::string& host, uint16_t port);
|
int connect_to(const std::string& host, uint16_t port);
|
||||||
|
|
||||||
|
int nonblock_connect_to(const std::string& host, uint16_t port, int timeout);
|
||||||
|
|
||||||
int make_listen_socket(const std::string& host, uint16_t port, int family);
|
int make_listen_socket(const std::string& host, uint16_t port, int family);
|
||||||
|
|
||||||
int make_non_block(int fd);
|
int make_non_block(int fd);
|
||||||
|
@ -134,6 +136,12 @@ void setup_ssl_ctx(SSL_CTX *ssl_ctx, void *next_proto_select_cb_arg);
|
||||||
|
|
||||||
int ssl_handshake(SSL *ssl, int fd);
|
int ssl_handshake(SSL *ssl, int fd);
|
||||||
|
|
||||||
|
int ssl_nonblock_handshake(SSL *ssl, int fd, int& timeout);
|
||||||
|
|
||||||
|
// Returns difference between |a| and |b| in milliseconds, assuming
|
||||||
|
// |a| is more recent than |b|.
|
||||||
|
int64_t time_delta(const timeval& a, const timeval& b);
|
||||||
|
|
||||||
void reset_timer();
|
void reset_timer();
|
||||||
|
|
||||||
void get_timer(timeval *tv);
|
void get_timer(timeval *tv);
|
||||||
|
|
Loading…
Reference in New Issue