Commit 606aa9da authored by zhanxw's avatar zhanxw

add connect() timeout

parent 1ac2159d
......@@ -3,7 +3,7 @@
#include "TypeConversion.h"
#include "Utils.h"
Http::Http(const std::string& url) {
Http::Http(const std::string& url, double timeoutSeconds) {
// url format:
// scheme://[user:password@]domain:port/path?query_string#fragment_id
// ref: https://en.wikipedia.org/wiki/Uniform_resource_locator
......@@ -50,7 +50,11 @@ Http::Http(const std::string& url) {
}
this->proxy = this->proxy.substr(0, sepColon);
}
socket = new Socket(this->proxy, this->proxyPort);
if (timeoutSeconds > 0) {
socket = new Socket(this->proxy, this->proxyPort, timeoutSeconds);
} else {
socket = new Socket(this->proxy, this->proxyPort);
}
this->request = "GET ";
this->request += url;
......@@ -59,7 +63,11 @@ Http::Http(const std::string& url) {
this->request += "\r\n\r\n";
} else {
// no proxy
socket = new Socket(this->domain, port);
if (timeoutSeconds > 0) {
socket = new Socket(this->domain, port, timeoutSeconds);
} else {
socket = new Socket(this->domain, port);
}
this->request = "GET ";
this->request += path;
......
......@@ -10,7 +10,7 @@ class Http {
// Make a connect to @param url
// @param url must be in the format of:
// http://domain[:port][/path]
Http(const std::string& url);
Http(const std::string& url, double timeoutSeconds = -1);
virtual ~Http();
/**
* Read everything and store on lines of contents to @param content
......
#include "Socket.h"
#include <fcntl.h>
#include <netdb.h>
#include <string.h> // memset()
#include <sys/socket.h>
#include <sys/time.h> // struct timeval
#include <sys/types.h>
#include <unistd.h> // close()
......@@ -11,9 +13,15 @@ Socket::Socket(const std::string& host, int port)
connect(host, port);
}
Socket::Socket(const std::string& host, int port, double timeoutSeconds)
: servinfo(NULL), fd(-1), usable(false), quiet(false) {
timedConnect(host, port, timeoutSeconds);
}
Socket::~Socket() { this->close(); }
int Socket::connect(const std::string& host, int port) {
if (host.empty()) return -1;
if (port <= 0) return -1;
// 0. close
......@@ -60,6 +68,99 @@ int Socket::connect(const std::string& host, int port) {
return 0;
}
int Socket::timedConnect(const std::string& host, int port, double seconds) {
if (host.empty()) return -1;
if (port <= 0) return -1;
// 0. close
this->close();
// 1. Get IP
int status;
struct addrinfo hints;
this->servinfo = NULL; // will point to the results
memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
char strPort[128];
sprintf(strPort, "%d", port);
if ((status = getaddrinfo(host.c_str(), strPort, &hints, &servinfo)) != 0) {
if (!this->quiet) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
}
return -1;
}
// 2. get socket
// use the first servinfo TODO: will try second, third ... if the first fails
this->fd = ::socket(this->servinfo->ai_family, this->servinfo->ai_socktype,
this->servinfo->ai_protocol);
if (this->fd == -1) {
if (!this->quiet) {
perror("socket() error");
}
return -1;
}
// 3.1 Set non-blocking
// int socketFlag =fcntl(this->fd, F_GETFL, NULL);
// if( socketFlag < 0) {
// fprintf(stderr, "fcntl F_GETFL error: %s\n", strerror(errno));
// return -1;
// }
// socketFlag |= O_NONBLOCK;
if (fcntl(this->fd, F_SETFL, O_NONBLOCK) < 0) {
fprintf(stderr, "fcntl F_SETFL error: %s\n", strerror(errno));
return -1;
}
// 3.2 Trying to connect with timeout
status =
::connect(this->fd, this->servinfo->ai_addr, this->servinfo->ai_addrlen);
if ((status == -1) && (errno != EINPROGRESS)) {
fprintf(stderr, "connect() error: %s\n", strerror(errno));
close();
return -1;
}
if (status == 0) {
// connection has succeeded immediately
usable = true;
return 0;
}
fd_set rfds;
struct timeval tv;
tv.tv_sec = (int)seconds;
tv.tv_usec = (int)((seconds - (int)seconds) * 1000);
FD_ZERO(&rfds);
FD_SET(this->fd, &rfds);
status = select(this->fd + 1, NULL, &rfds, NULL, &tv);
if (status == 1) {
int so_error;
socklen_t len = sizeof so_error;
getsockopt(this->fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
// printf("%s:%d is open\n", host.c_str(), port);
} else { // error
fprintf(stderr, "getsockopt() error: %s\n", strerror(errno));
return -1;
}
} else if (status < 0) { // error happens
fprintf(stderr, "connect() error %d - %s\n", errno, strerror(errno));
return -1;
} else { // timeout
fprintf(stderr, "connect() timeout %s:%d\n", host.c_str(), port);
return -1;
}
usable = true;
return 0;
}
void Socket::close() {
if (this->fd && this->fd != -1) { // -1: error or not yet openned
::close(fd);
......
......@@ -9,8 +9,10 @@ struct addrinfo;
class Socket {
public:
Socket(const std::string& host, int port);
Socket(const std::string& host, int port, double timeoutSeconds);
virtual ~Socket();
int connect(const std::string& host, int port);
int timedConnect(const std::string& host, int port, double seconds);
void close();
int send(const std::string& msg);
int recv(void* buf, int len);
......
#include "VersionChecker.h"
#include "Http.h"
#include "TypeConversion.h"
int VersionChecker::retrieveRemoteVersion(const std::string& urlToVersion) {
Http http(urlToVersion);
Http http(urlToVersion, 1.0);
http.enableQuiet();
if (http.read(&this->remoteInformation) < 0) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment