mirror of https://gitee.com/bigwinds/arangodb
766 lines
20 KiB
C++
766 lines
20 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief simple http client
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2010-2011 triagens GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Dr. Frank Celler
|
|
/// @author Achim Brandt
|
|
/// @author Copyright 2009, triagens GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "SimpleHttpClient.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <errno.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <netdb.h>
|
|
|
|
#include "Basics/StringUtils.h"
|
|
#include "Logger/Logger.h"
|
|
|
|
#include "SimpleHttpResult.h"
|
|
|
|
using namespace triagens::basics;
|
|
using namespace std;
|
|
|
|
namespace triagens {
|
|
namespace httpclient {
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// constructors and destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
SimpleHttpClient::SimpleHttpClient (string const& hostname,
|
|
int port,
|
|
double requestTimeout,
|
|
double connectTimeout,
|
|
size_t connectRetries) :
|
|
_hostname(hostname),
|
|
_port(port),
|
|
_requestTimeout(requestTimeout),
|
|
_connectTimeout(connectTimeout),
|
|
_connectRetries(connectRetries),
|
|
_writeBuffer(TRI_UNKNOWN_MEM_ZONE),
|
|
_readBuffer(TRI_UNKNOWN_MEM_ZONE) {
|
|
|
|
_lastConnectTime = 0.0;
|
|
_numConnectRetries = 0;
|
|
_result = 0;
|
|
_errorMessage = "";
|
|
_written = 0;
|
|
|
|
// _writeBuffer.clear();
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
SimpleHttpClient::~SimpleHttpClient () {
|
|
if (_isConnected) {
|
|
::close(_socket);
|
|
}
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// public methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
SimpleHttpResult* SimpleHttpClient::request (int method,
|
|
const string& location,
|
|
const char* body,
|
|
size_t bodyLength,
|
|
const map<string, string>& headerFields) {
|
|
|
|
_result = new SimpleHttpResult;
|
|
_errorMessage = "";
|
|
|
|
// set body to all connections
|
|
setRequest(method, location, body, bodyLength, headerFields);
|
|
|
|
double endTime = now() + _requestTimeout;
|
|
double remainingTime = _requestTimeout;
|
|
|
|
while (isWorking() && remainingTime > 0.0) {
|
|
switch (_state) {
|
|
case (IN_CONNECT):
|
|
handleConnect();
|
|
break;
|
|
|
|
case (IN_WRITE):
|
|
handleWrite(remainingTime);
|
|
break;
|
|
|
|
case (IN_READ_HEADER):
|
|
case (IN_READ_BODY):
|
|
case (IN_READ_CHUNKED_HEADER):
|
|
case (IN_READ_CHUNKED_BODY):
|
|
handleRead(remainingTime);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
remainingTime = endTime - now();
|
|
}
|
|
|
|
if (isWorking() && _errorMessage=="" ) {
|
|
LOGGER_ERROR << "Request timeout reached.";
|
|
_errorMessage = "Request timeout reached.";
|
|
}
|
|
|
|
// set result type in getResult()
|
|
SimpleHttpResult* result = getResult();
|
|
|
|
_result = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// private methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void SimpleHttpClient::handleConnect () {
|
|
_isConnected = false;
|
|
_socket = -1;
|
|
|
|
if (_numConnectRetries < _connectRetries + 1) {
|
|
_numConnectRetries++;
|
|
}
|
|
else {
|
|
LOGGER_ERROR << "Could not connect to '" << _hostname << ":" << _port << "'! Connection is dead";
|
|
_state = DEAD;
|
|
return;
|
|
}
|
|
|
|
if (_hostname == "" || _port == 0) {
|
|
_errorMessage = "Could not connect to '" + _hostname + ":" + StringUtils::itoa(_port) + "'";
|
|
LOGGER_ERROR << "Could not connect to '" << _hostname << ":" << _port << "'! Connection is dead";
|
|
_state = DEAD;
|
|
return;
|
|
}
|
|
|
|
_lastConnectTime = now();
|
|
|
|
struct addrinfo *result, *aip;
|
|
struct addrinfo hints;
|
|
int error;
|
|
|
|
memset(&hints, 0, sizeof (struct addrinfo));
|
|
hints.ai_family = AF_INET; // Allow IPv4 or IPv6
|
|
hints.ai_flags = AI_NUMERICSERV | AI_ALL;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
string portString = StringUtils::itoa(_port);
|
|
|
|
error = getaddrinfo(_hostname.c_str(), portString.c_str(), &hints, &result);
|
|
|
|
if (error != 0) {
|
|
LOGGER_ERROR << "Could not connect to '" << _hostname << ":" << _port << "'! Connection is dead";
|
|
_errorMessage = "Could not connect to '" + _hostname + ":" + StringUtils::itoa(_port) +
|
|
"'! getaddrinfo() failed with: " + string(gai_strerror(error));
|
|
_state = DEAD;
|
|
return;
|
|
}
|
|
|
|
// Try all returned addresses until one works
|
|
for (aip = result; aip != NULL; aip = aip->ai_next) {
|
|
|
|
// try to connect the address info pointer
|
|
if (connectSocket(aip)) {
|
|
// is connected
|
|
LOGGER_TRACE << "Connected to '" << _hostname << ":" << _port << "'!";
|
|
_isConnected = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
|
|
if (_isConnected) {
|
|
// can write now
|
|
_state = IN_WRITE;
|
|
_written = 0;
|
|
}
|
|
}
|
|
|
|
void SimpleHttpClient::handleWrite (double timeout) {
|
|
struct timeval tv;
|
|
fd_set fdset;
|
|
|
|
tv.tv_sec = (uint64_t) timeout;
|
|
tv.tv_usec = ((uint64_t) (timeout * 1000000.0)) % 1000000;
|
|
|
|
FD_ZERO(&fdset);
|
|
FD_SET(_socket, &fdset);
|
|
|
|
if (select(_socket + 1, NULL, &fdset, NULL, &tv) > 0) {
|
|
write();
|
|
}
|
|
}
|
|
|
|
void SimpleHttpClient::handleRead (double timeout) {
|
|
struct timeval tv;
|
|
fd_set fdset;
|
|
|
|
tv.tv_sec = (uint64_t) timeout;
|
|
tv.tv_usec = ((uint64_t) (timeout * 1000000.0)) % 1000000;
|
|
|
|
FD_ZERO(&fdset);
|
|
FD_SET(_socket, &fdset);
|
|
|
|
if (select(_socket + 1, &fdset, NULL, NULL, &tv) > 0) {
|
|
read();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets username and password
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SimpleHttpClient::setUserNamePassword (
|
|
const string& prefix,
|
|
const string& username,
|
|
const string& password) {
|
|
|
|
string value = triagens::basics::StringUtils::encodeBase64(username + ":" + password);
|
|
|
|
_pathToBasicAuth.push_back(make_pair(prefix, value));
|
|
}
|
|
|
|
SimpleHttpResult* SimpleHttpClient::getResult () {
|
|
switch (_state) {
|
|
case (IN_CONNECT):
|
|
_result->setResultType(SimpleHttpResult::COULD_NOT_CONNECT);
|
|
break;
|
|
|
|
case (IN_WRITE):
|
|
_result->setResultType(SimpleHttpResult::WRITE_ERROR);
|
|
break;
|
|
|
|
case (IN_READ_HEADER):
|
|
case (IN_READ_BODY):
|
|
case (IN_READ_CHUNKED_HEADER):
|
|
case (IN_READ_CHUNKED_BODY):
|
|
_result->setResultType(SimpleHttpResult::READ_ERROR);
|
|
break;
|
|
|
|
case (FINISHED):
|
|
_result->setResultType(SimpleHttpResult::COMPLETE);
|
|
break;
|
|
|
|
default :
|
|
_result->setResultType(SimpleHttpResult::COULD_NOT_CONNECT);
|
|
}
|
|
|
|
return _result;
|
|
}
|
|
|
|
void SimpleHttpClient::setRequest (int method,
|
|
const string& location,
|
|
const char* body,
|
|
size_t bodyLength,
|
|
const map<string, string>& headerFields) {
|
|
|
|
if (_state == DEAD) {
|
|
_numConnectRetries = 0;
|
|
_lastConnectTime = 0.0;
|
|
}
|
|
|
|
///////////////////// fill the write buffer //////////////////////////////
|
|
_writeBuffer.clear();
|
|
|
|
switch (method) {
|
|
case GET:
|
|
_writeBuffer.appendText("GET ");
|
|
break;
|
|
case POST:
|
|
_writeBuffer.appendText("POST ");
|
|
break;
|
|
case PUT:
|
|
_writeBuffer.appendText("PUT ");
|
|
break;
|
|
case DELETE:
|
|
_writeBuffer.appendText("DELETE ");
|
|
break;
|
|
case HEAD:
|
|
_writeBuffer.appendText("HEAD ");
|
|
break;
|
|
default:
|
|
_writeBuffer.appendText("POST ");
|
|
break;
|
|
}
|
|
|
|
string l = location;
|
|
if (location.length() == 0 || location[0] != '/') {
|
|
l = "/" + location;
|
|
}
|
|
|
|
_writeBuffer.appendText(l);
|
|
_writeBuffer.appendText(" HTTP/1.1\r\n");
|
|
|
|
_writeBuffer.appendText("Host: ");
|
|
_writeBuffer.appendText(_hostname);
|
|
_writeBuffer.appendText(":");
|
|
_writeBuffer.appendInteger(_port);
|
|
_writeBuffer.appendText("\r\n");
|
|
_writeBuffer.appendText("Connection: Keep-Alive\r\n");
|
|
_writeBuffer.appendText("User-Agent: VOC-Client/1.0\r\n");
|
|
|
|
//requestBuffer << "Accept: application/json\r\n";
|
|
//if (bodyLength > 0) {
|
|
// requestBuffer << "Content-Type: application/json; charset=utf-8\r\n";
|
|
//}
|
|
|
|
// do basic authorization
|
|
if (_pathToBasicAuth.size() > 0) {
|
|
string foundPrefix;
|
|
string foundValue;
|
|
std::vector< std::pair<std::string, std::string> >::iterator i = _pathToBasicAuth.begin();
|
|
for (; i != _pathToBasicAuth.end(); ++i) {
|
|
string f = i->first;
|
|
if (l.find(f) == 0) {
|
|
// f is prefix of l
|
|
if (f.length() > foundPrefix.length()) {
|
|
foundPrefix = f;
|
|
foundValue = i->second;
|
|
}
|
|
}
|
|
}
|
|
if (foundValue.length() > 0) {
|
|
_writeBuffer.appendText("Authorization: Basic ");
|
|
_writeBuffer.appendText(foundValue);
|
|
_writeBuffer.appendText("\r\n");
|
|
}
|
|
}
|
|
|
|
for (map<string, string>::const_iterator i = headerFields.begin(); i != headerFields.end(); ++i) {
|
|
// TODO: check Header name and value
|
|
_writeBuffer.appendText(i->first);
|
|
_writeBuffer.appendText(": ");
|
|
_writeBuffer.appendText(i->second);
|
|
_writeBuffer.appendText("\r\n");
|
|
}
|
|
|
|
_writeBuffer.appendText("Content-Length: ");
|
|
_writeBuffer.appendInteger(bodyLength);
|
|
_writeBuffer.appendText("\r\n\r\n");
|
|
_writeBuffer.appendText(body, bodyLength);
|
|
|
|
LOGGER_TRACE << "Request: " << _writeBuffer.c_str();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
if (_state != FINISHED) {
|
|
// close connection to reset all read and write buffers
|
|
close();
|
|
}
|
|
|
|
if (_isConnected) {
|
|
// we are connected start with writing
|
|
_state = IN_WRITE;
|
|
_written = 0;
|
|
}
|
|
else {
|
|
// connect to server
|
|
_state = IN_CONNECT;
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// private methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bool SimpleHttpClient::close () {
|
|
if (_socket != -1) {
|
|
::close(_socket);
|
|
}
|
|
_isConnected = false;
|
|
|
|
reset();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SimpleHttpClient::write () {
|
|
if (!checkSocket()) {
|
|
return false;
|
|
}
|
|
|
|
//printf("write():\n%s\n", (_writeBuffer.c_str() + _written));
|
|
|
|
#ifdef __APPLE__
|
|
int status = ::send(_socket, _writeBuffer.c_str() + _written, _writeBuffer.length() - _written, 0);
|
|
#else
|
|
int status = ::send(_socket, _writeBuffer.c_str() + _written, _writeBuffer.length() - _written, MSG_NOSIGNAL);
|
|
#endif
|
|
|
|
if (status == -1) {
|
|
_errorMessage = "::send() failed with: " + string(strerror(errno));
|
|
//LOGGER_ERROR << "::send() failed with " << strerror(errno);
|
|
|
|
close();
|
|
|
|
return false;
|
|
}
|
|
|
|
_written += status;
|
|
|
|
if (_written == _writeBuffer.length()) {
|
|
_state = IN_READ_HEADER;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimpleHttpClient::read () {
|
|
if (!checkSocket()) {
|
|
return false;
|
|
}
|
|
|
|
do {
|
|
char buffer[READBUFFER_SIZE];
|
|
|
|
int len_read = ::read(_socket, buffer, READBUFFER_SIZE - 1);
|
|
|
|
if (len_read <= 0) {
|
|
// error: stop reading
|
|
break;
|
|
}
|
|
|
|
_readBuffer.appendText(buffer, len_read);
|
|
}
|
|
while(readable());
|
|
|
|
switch (_state) {
|
|
case (IN_READ_HEADER):
|
|
readHeader();
|
|
break;
|
|
|
|
case (IN_READ_BODY):
|
|
readBody();
|
|
break;
|
|
|
|
case (IN_READ_CHUNKED_HEADER):
|
|
readChunkedHeader();
|
|
break;
|
|
|
|
case (IN_READ_CHUNKED_BODY):
|
|
readChunkedBody();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimpleHttpClient::readHeader () {
|
|
char* pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length());
|
|
|
|
while (pos) {
|
|
size_t len = pos - _readBuffer.c_str();
|
|
string line(_readBuffer.c_str(), len);
|
|
_readBuffer.erase_front(len + 1);
|
|
|
|
//printf("found header line %s\n", line.c_str());
|
|
|
|
if (line == "\r" || line == "") {
|
|
// end of header found
|
|
if (_result->isChunked()) {
|
|
_state = IN_READ_CHUNKED_HEADER;
|
|
return readChunkedHeader();
|
|
}
|
|
else if (_result->getContentLength()) {
|
|
|
|
if (_result->getContentLength() > 5000000) {
|
|
_errorMessage = "Content length > 5000000 bytes found!";
|
|
LOGGER_ERROR << "Content length > 5000000 bytes found! Closing connection.";
|
|
|
|
// reset connection
|
|
close();
|
|
|
|
return false;
|
|
}
|
|
|
|
_state = IN_READ_BODY;
|
|
return readBody();
|
|
}
|
|
else {
|
|
_result->setResultType(SimpleHttpResult::COMPLETE);
|
|
_state = FINISHED;
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
_result->addHeaderField(line);
|
|
}
|
|
|
|
pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimpleHttpClient::readBody () {
|
|
if (_readBuffer.length() >= _result->getContentLength()) {
|
|
_result->getBody().write(_readBuffer.c_str(), _result->getContentLength());
|
|
_readBuffer.erase_front(_result->getContentLength());
|
|
_result->setResultType(SimpleHttpResult::COMPLETE);
|
|
_state = FINISHED;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SimpleHttpClient::readChunkedHeader () {
|
|
char* pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length());
|
|
|
|
while (pos) {
|
|
// got a line
|
|
size_t len = pos - _readBuffer.c_str();
|
|
string line(_readBuffer.c_str(), len);
|
|
_readBuffer.erase_front(len + 1);
|
|
|
|
string trimed = StringUtils::trim(line);
|
|
|
|
if (trimed == "\r" || trimed == "") {
|
|
// ignore empty lines
|
|
pos = (char*) memchr(_readBuffer.c_str(), '\n', _readBuffer.length());
|
|
continue;
|
|
}
|
|
|
|
uint32_t contentLength;
|
|
sscanf(trimed.c_str(), "%x", &contentLength);
|
|
|
|
if (contentLength == 0) {
|
|
// OK: last content length found
|
|
//printf("Last length found\n");
|
|
|
|
_result->setResultType(SimpleHttpResult::COMPLETE);
|
|
|
|
_state = FINISHED;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (contentLength > 5000000) {
|
|
// failed: too many bytes
|
|
|
|
_errorMessage = "Content length > 5000000 bytes found!";
|
|
LOGGER_ERROR << "Content length > 5000000 bytes found! Closing connection.";
|
|
|
|
// reset connection
|
|
close();
|
|
|
|
return false;
|
|
}
|
|
|
|
_state = IN_READ_CHUNKED_BODY;
|
|
_nextChunkedSize = contentLength;
|
|
return readChunkedBody();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SimpleHttpClient::readChunkedBody () {
|
|
|
|
if (_readBuffer.length() >= _nextChunkedSize) {
|
|
|
|
_result->getBody().write(_readBuffer.c_str(), (size_t) _nextChunkedSize);
|
|
_readBuffer.erase_front((size_t) _nextChunkedSize);
|
|
_state = IN_READ_CHUNKED_HEADER;
|
|
return readChunkedHeader();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SimpleHttpClient::readable () {
|
|
fd_set fdset;
|
|
FD_ZERO(&fdset);
|
|
FD_SET(_socket, &fdset);
|
|
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
if (select(_socket + 1, &fdset, NULL, NULL, &tv) == 1) {
|
|
return checkSocket();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SimpleHttpClient::checkSocket () {
|
|
int so_error = -1;
|
|
socklen_t len = sizeof so_error;
|
|
|
|
getsockopt(_socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
|
|
|
if (so_error == 0) {
|
|
return true;
|
|
}
|
|
|
|
_errorMessage = "getsockopt() failed with: " + string(strerror(errno));
|
|
LOGGER_ERROR << "getsockopt() failed with: " << strerror(errno) << ". Closing connection.";
|
|
|
|
//close and reset conection
|
|
close();
|
|
|
|
_state = IN_CONNECT;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SimpleHttpClient::connectSocket (struct addrinfo *aip) {
|
|
|
|
// create socket and connect socket here
|
|
|
|
_socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
|
|
|
|
// check socket and set the socket not blocking and close on exit
|
|
|
|
if (_socket == -1) {
|
|
//setErrorMessage("Socket not connected. socket() faild with: " + string(strerror(errno)), errno);
|
|
return false;
|
|
}
|
|
|
|
if (!setNonBlocking(_socket)) {
|
|
//setErrorMessage("Socket not connected. Set non blocking failed with: " + string(strerror(errno)), errno);
|
|
::close(_socket);
|
|
_socket = -1;
|
|
return false;
|
|
}
|
|
|
|
if (!setCloseOnExec(_socket)) {
|
|
//setErrorMessage("Socket not connected. Set close on exec failed with: " + string(strerror(errno)), errno);
|
|
::close(_socket);
|
|
_socket = -1;
|
|
return false;
|
|
}
|
|
|
|
::connect(_socket, (const struct sockaddr *) aip->ai_addr, aip->ai_addrlen);
|
|
|
|
struct timeval tv;
|
|
fd_set fdset;
|
|
|
|
tv.tv_sec = (uint64_t) _connectTimeout;
|
|
tv.tv_usec = ((uint64_t) (_connectTimeout * 1000000.0)) % 1000000;
|
|
|
|
FD_ZERO(&fdset);
|
|
FD_SET(_socket, &fdset);
|
|
|
|
if (select(_socket + 1, NULL, &fdset, NULL, &tv) > 0) {
|
|
|
|
if (checkSocket()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// connect timeout reached
|
|
_errorMessage = "Could not conect to server in " + StringUtils::ftoa(_connectTimeout) + " seconds.";
|
|
LOGGER_WARNING << "Could not conect to server in " << _connectTimeout << " seconds.";
|
|
|
|
::close(_socket);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SimpleHttpClient::setNonBlocking (socket_t fd) {
|
|
#ifdef TRI_HAVE_WIN32_NON_BLOCKING
|
|
DWORD ul = 1;
|
|
|
|
return ioctlsocket(fd, FIONBIO, &ul) == SOCKET_ERROR ? false : true;
|
|
#else
|
|
long flags = fcntl(fd, F_GETFL, 0);
|
|
|
|
if (flags < 0) {
|
|
return false;
|
|
}
|
|
|
|
flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
if (flags < 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool SimpleHttpClient::setCloseOnExec (socket_t fd) {
|
|
#ifdef TRI_HAVE_WIN32_CLOSE_ON_EXEC
|
|
#else
|
|
long flags = fcntl(fd, F_GETFD, 0);
|
|
|
|
if (flags < 0) {
|
|
return false;
|
|
}
|
|
|
|
flags = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
|
|
if (flags < 0) {
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void SimpleHttpClient::reset () {
|
|
_state = IN_CONNECT;
|
|
|
|
_isConnected = false;
|
|
_socket = -1;
|
|
|
|
if (_result) {
|
|
_result->clear();
|
|
}
|
|
|
|
_readBuffer.clear();
|
|
}
|
|
|
|
double SimpleHttpClient::now () {
|
|
struct timeval tv;
|
|
gettimeofday(&tv, 0);
|
|
|
|
double sec = tv.tv_sec; // seconds
|
|
double usc = tv.tv_usec; // microseconds
|
|
|
|
return sec + usc / 1000000.0;
|
|
}
|
|
|
|
}
|
|
|
|
}
|