Compare commits
	
		
			7 Commits
		
	
	
		
			v5.0.4
			...
			feature/ht
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a788b31080 | ||
| 
						 | 
					03a2f1443b | ||
| 
						 | 
					6e0463c981 | ||
| 
						 | 
					e9399a0734 | ||
| 
						 | 
					7b2ddb5e7c | ||
| 
						 | 
					c7cb743a69 | ||
| 
						 | 
					3257ad1363 | 
							
								
								
									
										53
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								README.md
									
									
									
									
									
								
							@@ -129,33 +129,33 @@ Here is what the HTTP client API looks like. Note that HTTP client support is ve
 | 
				
			|||||||
// Preparation
 | 
					// Preparation
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
HttpClient httpClient;
 | 
					HttpClient httpClient;
 | 
				
			||||||
HttpRequestArgs args;
 | 
					HttpRequestArgsPtr args = httpClient.createRequest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Custom headers can be set
 | 
					// Custom headers can be set
 | 
				
			||||||
WebSocketHttpHeaders headers;
 | 
					WebSocketHttpHeaders headers;
 | 
				
			||||||
headers["Foo"] = "bar";
 | 
					headers["Foo"] = "bar";
 | 
				
			||||||
args.extraHeaders = headers;
 | 
					args->extraHeaders = headers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Timeout options
 | 
					// Timeout options
 | 
				
			||||||
args.connectTimeout = connectTimeout;
 | 
					args->connectTimeout = connectTimeout;
 | 
				
			||||||
args.transferTimeout = transferTimeout;
 | 
					args->transferTimeout = transferTimeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Redirect options
 | 
					// Redirect options
 | 
				
			||||||
args.followRedirects = followRedirects;
 | 
					args->followRedirects = followRedirects;
 | 
				
			||||||
args.maxRedirects = maxRedirects;
 | 
					args->maxRedirects = maxRedirects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Misc
 | 
					// Misc
 | 
				
			||||||
args.compress = compress; // Enable gzip compression
 | 
					args->compress = compress; // Enable gzip compression
 | 
				
			||||||
args.verbose = verbose;
 | 
					args->verbose = verbose;
 | 
				
			||||||
args.logger = [](const std::string& msg)
 | 
					args->logger = [](const std::string& msg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::cout << msg;
 | 
					    std::cout << msg;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Request
 | 
					// Synchronous Request
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
HttpResponse out;
 | 
					HttpResponsePtr out;
 | 
				
			||||||
std::string url = "https://www.google.com";
 | 
					std::string url = "https://www.google.com";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HEAD request
 | 
					// HEAD request
 | 
				
			||||||
@@ -175,13 +175,30 @@ out = httpClient.post(url, std::string("foo=bar"), args);
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// Result
 | 
					// Result
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
auto statusCode = std::get<0>(out);
 | 
					auto errorCode = response->errorCode; // Can be HttpErrorCode::Ok, HttpErrorCode::UrlMalformed, etc...
 | 
				
			||||||
auto errorCode = std::get<1>(out);
 | 
					auto errorCode = response->errorCode; // 200, 404, etc...
 | 
				
			||||||
auto responseHeaders = std::get<2>(out);
 | 
					auto responseHeaders = response->headers; // All the headers in a special case-insensitive unordered_map of (string, string)
 | 
				
			||||||
auto payload = std::get<3>(out);
 | 
					auto payload = response->payload; // All the bytes from the response as an std::string
 | 
				
			||||||
auto errorMsg = std::get<4>(out);
 | 
					auto errorMsg = response->errorMsg; // Descriptive error message in case of failure
 | 
				
			||||||
auto uploadSize = std::get<5>(out);
 | 
					auto uploadSize = response->uploadSize; // Byte count of uploaded data
 | 
				
			||||||
auto downloadSize = std::get<6>(out);
 | 
					auto downloadSize = response->downloadSize; // Byte count of downloaded data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Asynchronous Request
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					bool async = true;
 | 
				
			||||||
 | 
					HttpClient httpClient(async);
 | 
				
			||||||
 | 
					auto args = httpClient.createRequest(url, HttpClient::kGet);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Push the request to a queue,
 | 
				
			||||||
 | 
					bool ok = httpClient.performRequest(args, [](const HttpResponsePtr& response)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // This callback execute in a background thread. Make sure you uses appropriate protection such as mutex
 | 
				
			||||||
 | 
					        auto statusCode = response->statusCode; // acess results
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ok will be false if your httpClient is not async
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Build
 | 
					## Build
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								docker/Dockerfile.ubuntu_bionic
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								docker/Dockerfile.ubuntu_bionic
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					# Build time
 | 
				
			||||||
 | 
					FROM ubuntu:bionic as build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENV DEBIAN_FRONTEND noninteractive
 | 
				
			||||||
 | 
					RUN apt-get update 
 | 
				
			||||||
 | 
					RUN apt-get -y install wget 
 | 
				
			||||||
 | 
					RUN mkdir -p /tmp/cmake
 | 
				
			||||||
 | 
					WORKDIR /tmp/cmake
 | 
				
			||||||
 | 
					RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz 
 | 
				
			||||||
 | 
					RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN apt-get -y install g++ 
 | 
				
			||||||
 | 
					RUN apt-get -y install libssl-dev
 | 
				
			||||||
 | 
					RUN apt-get -y install libz-dev
 | 
				
			||||||
 | 
					RUN apt-get -y install make
 | 
				
			||||||
 | 
					RUN apt-get -y install python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY . .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
 | 
				
			||||||
 | 
					ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN ["make", "ws"]
 | 
				
			||||||
@@ -24,23 +24,95 @@ namespace ix
 | 
				
			|||||||
    const std::string HttpClient::kDel = "DEL";
 | 
					    const std::string HttpClient::kDel = "DEL";
 | 
				
			||||||
    const std::string HttpClient::kPut = "PUT";
 | 
					    const std::string HttpClient::kPut = "PUT";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpClient::HttpClient()
 | 
					    HttpClient::HttpClient(bool async) : _async(async), _stop(false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        if (!_async) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _thread = std::thread(&HttpClient::run, this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpClient::~HttpClient()
 | 
					    HttpClient::~HttpClient()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        if (!_thread.joinable()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _stop = true;
 | 
				
			||||||
 | 
					        _condition.notify_one();
 | 
				
			||||||
 | 
					        _thread.join();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::request(
 | 
					    HttpRequestArgsPtr HttpClient::createRequest(const std::string& url,
 | 
				
			||||||
 | 
					                                                 const std::string& verb)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        auto request = std::make_shared<HttpRequestArgs>();
 | 
				
			||||||
 | 
					        request->url = url;
 | 
				
			||||||
 | 
					        request->verb = verb;
 | 
				
			||||||
 | 
					        return request;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool HttpClient::performRequest(HttpRequestArgsPtr args,
 | 
				
			||||||
 | 
					                                    const OnResponseCallback& onResponseCallback)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!_async) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Enqueue the task
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // acquire lock
 | 
				
			||||||
 | 
					            std::unique_lock<std::mutex> lock(_queueMutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // add the task
 | 
				
			||||||
 | 
					            _queue.push(std::make_pair(args, onResponseCallback));
 | 
				
			||||||
 | 
					        } // release lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // wake up one thread
 | 
				
			||||||
 | 
					        _condition.notify_one();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void HttpClient::run()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        while (true)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            HttpRequestArgsPtr args;
 | 
				
			||||||
 | 
					            OnResponseCallback onResponseCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::unique_lock<std::mutex> lock(_queueMutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (!_stop && _queue.empty())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _condition.wait(lock);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (_stop) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                auto p = _queue.front();
 | 
				
			||||||
 | 
					                _queue.pop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                args = p.first;
 | 
				
			||||||
 | 
					                onResponseCallback = p.second;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (_stop) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            HttpResponsePtr response = request(args->url, args->verb, args->body, args);
 | 
				
			||||||
 | 
					            onResponseCallback(response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (_stop) return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    HttpResponsePtr HttpClient::request(
 | 
				
			||||||
        const std::string& url,
 | 
					        const std::string& url,
 | 
				
			||||||
        const std::string& verb,
 | 
					        const std::string& verb,
 | 
				
			||||||
        const std::string& body,
 | 
					        const std::string& body,
 | 
				
			||||||
        const HttpRequestArgs& args,
 | 
					        HttpRequestArgsPtr args,
 | 
				
			||||||
        int redirects)
 | 
					        int redirects)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        // We only have one socket connection, so we cannot 
 | 
				
			||||||
 | 
					        // make multiple requests concurrently.
 | 
				
			||||||
 | 
					        std::lock_guard<std::mutex> lock(_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        uint64_t uploadSize = 0;
 | 
					        uint64_t uploadSize = 0;
 | 
				
			||||||
        uint64_t downloadSize = 0;
 | 
					        uint64_t downloadSize = 0;
 | 
				
			||||||
        int code = 0;
 | 
					        int code = 0;
 | 
				
			||||||
@@ -54,7 +126,7 @@ namespace ix
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            std::stringstream ss;
 | 
					            std::stringstream ss;
 | 
				
			||||||
            ss << "Cannot parse url: " << url;
 | 
					            ss << "Cannot parse url: " << url;
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::UrlMalformed,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::UrlMalformed,
 | 
				
			||||||
                                headers, payload, ss.str(),
 | 
					                                headers, payload, ss.str(),
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -65,7 +137,7 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (!_socket)
 | 
					        if (!_socket)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::CannotCreateSocket,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotCreateSocket,
 | 
				
			||||||
                                headers, payload, errorMsg,
 | 
					                                headers, payload, errorMsg,
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -75,13 +147,13 @@ namespace ix
 | 
				
			|||||||
        ss << verb << " " << path << " HTTP/1.1\r\n";
 | 
					        ss << verb << " " << path << " HTTP/1.1\r\n";
 | 
				
			||||||
        ss << "Host: " << host << "\r\n";
 | 
					        ss << "Host: " << host << "\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (args.compress)
 | 
					        if (args->compress)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ss << "Accept-Encoding: gzip" << "\r\n";
 | 
					            ss << "Accept-Encoding: gzip" << "\r\n";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Append extra headers
 | 
					        // Append extra headers
 | 
				
			||||||
        for (auto&& it : args.extraHeaders)
 | 
					        for (auto&& it : args->extraHeaders)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ss << it.first << ": " << it.second << "\r\n";
 | 
					            ss << it.first << ": " << it.second << "\r\n";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -103,7 +175,7 @@ namespace ix
 | 
				
			|||||||
            ss << "Content-Length: " << body.size() << "\r\n";
 | 
					            ss << "Content-Length: " << body.size() << "\r\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Set default Content-Type if unspecified
 | 
					            // Set default Content-Type if unspecified
 | 
				
			||||||
            if (args.extraHeaders.find("Content-Type") == args.extraHeaders.end())
 | 
					            if (args->extraHeaders.find("Content-Type") == args->extraHeaders.end())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
 | 
					                ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -121,23 +193,23 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Make a cancellation object dealing with connection timeout
 | 
					        // Make a cancellation object dealing with connection timeout
 | 
				
			||||||
        auto isCancellationRequested =
 | 
					        auto isCancellationRequested =
 | 
				
			||||||
            makeCancellationRequestWithTimeout(args.connectTimeout, requestInitCancellation);
 | 
					            makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
 | 
					        bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
 | 
				
			||||||
        if (!success)
 | 
					        if (!success)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::stringstream ss;
 | 
					            std::stringstream ss;
 | 
				
			||||||
            ss << "Cannot connect to url: " << url << " / error : " << errMsg;
 | 
					            ss << "Cannot connect to url: " << url << " / error : " << errMsg;
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::CannotConnect,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotConnect,
 | 
				
			||||||
                                headers, payload, ss.str(),
 | 
					                                                  headers, payload, ss.str(),
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                                  uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Make a new cancellation object dealing with transfer timeout
 | 
					        // Make a new cancellation object dealing with transfer timeout
 | 
				
			||||||
        isCancellationRequested =
 | 
					        isCancellationRequested =
 | 
				
			||||||
            makeCancellationRequestWithTimeout(args.transferTimeout, requestInitCancellation);
 | 
					            makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (args.verbose)
 | 
					        if (args->verbose)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::stringstream ss;
 | 
					            std::stringstream ss;
 | 
				
			||||||
            ss << "Sending " << verb << " request "
 | 
					            ss << "Sending " << verb << " request "
 | 
				
			||||||
@@ -154,9 +226,9 @@ namespace ix
 | 
				
			|||||||
        if (!_socket->writeBytes(req, isCancellationRequested))
 | 
					        if (!_socket->writeBytes(req, isCancellationRequested))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::string errorMsg("Cannot send request");
 | 
					            std::string errorMsg("Cannot send request");
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::SendError,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::SendError,
 | 
				
			||||||
                                headers, payload, errorMsg,
 | 
					                                                  headers, payload, errorMsg,
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                                  uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        uploadSize = req.size();
 | 
					        uploadSize = req.size();
 | 
				
			||||||
@@ -168,12 +240,12 @@ namespace ix
 | 
				
			|||||||
        if (!lineValid)
 | 
					        if (!lineValid)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::string errorMsg("Cannot retrieve status line");
 | 
					            std::string errorMsg("Cannot retrieve status line");
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::CannotReadStatusLine,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotReadStatusLine,
 | 
				
			||||||
                                headers, payload, errorMsg,
 | 
					                                                  headers, payload, errorMsg,
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                                  uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (args.verbose)
 | 
					        if (args->verbose)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::stringstream ss;
 | 
					            std::stringstream ss;
 | 
				
			||||||
            ss << "Status line " << line;
 | 
					            ss << "Status line " << line;
 | 
				
			||||||
@@ -183,9 +255,9 @@ namespace ix
 | 
				
			|||||||
        if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
 | 
					        if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::string errorMsg("Cannot parse response code from status line");
 | 
					            std::string errorMsg("Cannot parse response code from status line");
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::MissingStatus,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::MissingStatus,
 | 
				
			||||||
                                headers, payload, errorMsg,
 | 
					                                                  headers, payload, errorMsg,
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                                  uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto result = parseHttpHeaders(_socket, isCancellationRequested);
 | 
					        auto result = parseHttpHeaders(_socket, isCancellationRequested);
 | 
				
			||||||
@@ -195,29 +267,29 @@ namespace ix
 | 
				
			|||||||
        if (!headersValid)
 | 
					        if (!headersValid)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::string errorMsg("Cannot parse http headers");
 | 
					            std::string errorMsg("Cannot parse http headers");
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::HeaderParsingError,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::HeaderParsingError,
 | 
				
			||||||
                                headers, payload, errorMsg,
 | 
					                                                  headers, payload, errorMsg,
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                                  uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Redirect ?
 | 
					        // Redirect ?
 | 
				
			||||||
        if ((code >= 301 && code <= 308) && args.followRedirects)
 | 
					        if ((code >= 301 && code <= 308) && args->followRedirects)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (headers.find("Location") == headers.end())
 | 
					            if (headers.find("Location") == headers.end())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                std::string errorMsg("Missing location header for redirect");
 | 
					                std::string errorMsg("Missing location header for redirect");
 | 
				
			||||||
                return HttpResponse(code, HttpErrorCode::MissingLocation,
 | 
					                return std::make_shared<HttpResponse>(code, HttpErrorCode::MissingLocation,
 | 
				
			||||||
                                    headers, payload, errorMsg,
 | 
					                                                      headers, payload, errorMsg,
 | 
				
			||||||
                                    uploadSize, downloadSize);
 | 
					                                                      uploadSize, downloadSize);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (redirects >= args.maxRedirects)
 | 
					            if (redirects >= args->maxRedirects)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                std::stringstream ss;
 | 
					                std::stringstream ss;
 | 
				
			||||||
                ss << "Too many redirects: " << redirects;
 | 
					                ss << "Too many redirects: " << redirects;
 | 
				
			||||||
                return HttpResponse(code, HttpErrorCode::TooManyRedirects,
 | 
					                return std::make_shared<HttpResponse>(code, HttpErrorCode::TooManyRedirects,
 | 
				
			||||||
                                    headers, payload, ss.str(),
 | 
					                                                      headers, payload, ss.str(),
 | 
				
			||||||
                                    uploadSize, downloadSize);
 | 
					                                                      uploadSize, downloadSize);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Recurse
 | 
					            // Recurse
 | 
				
			||||||
@@ -227,9 +299,9 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (verb == "HEAD")
 | 
					        if (verb == "HEAD")
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::Ok,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::Ok,
 | 
				
			||||||
                                headers, payload, std::string(),
 | 
					                                                  headers, payload, std::string(),
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                                  uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Parse response:
 | 
					        // Parse response:
 | 
				
			||||||
@@ -243,14 +315,14 @@ namespace ix
 | 
				
			|||||||
            payload.reserve(contentLength);
 | 
					            payload.reserve(contentLength);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            auto chunkResult = _socket->readBytes(contentLength,
 | 
					            auto chunkResult = _socket->readBytes(contentLength,
 | 
				
			||||||
                                                  args.onProgressCallback,
 | 
					                                                  args->onProgressCallback,
 | 
				
			||||||
                                                  isCancellationRequested);
 | 
					                                                  isCancellationRequested);
 | 
				
			||||||
            if (!chunkResult.first)
 | 
					            if (!chunkResult.first)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                errorMsg = "Cannot read chunk";
 | 
					                errorMsg = "Cannot read chunk";
 | 
				
			||||||
                return HttpResponse(code, HttpErrorCode::ChunkReadError,
 | 
					                return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
 | 
				
			||||||
                                    headers, payload, errorMsg,
 | 
					                                                      headers, payload, errorMsg,
 | 
				
			||||||
                                    uploadSize, downloadSize);
 | 
					                                                      uploadSize, downloadSize);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            payload += chunkResult.second;
 | 
					            payload += chunkResult.second;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -266,9 +338,9 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (!lineResult.first)
 | 
					                if (!lineResult.first)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return HttpResponse(code, HttpErrorCode::ChunkReadError,
 | 
					                    return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
 | 
				
			||||||
                                        headers, payload, errorMsg,
 | 
					                                                          headers, payload, errorMsg,
 | 
				
			||||||
                                        uploadSize, downloadSize);
 | 
					                                                          uploadSize, downloadSize);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                uint64_t chunkSize;
 | 
					                uint64_t chunkSize;
 | 
				
			||||||
@@ -276,7 +348,7 @@ namespace ix
 | 
				
			|||||||
                ss << std::hex << line;
 | 
					                ss << std::hex << line;
 | 
				
			||||||
                ss >> chunkSize;
 | 
					                ss >> chunkSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (args.verbose)
 | 
					                if (args->verbose)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    std::stringstream oss;
 | 
					                    std::stringstream oss;
 | 
				
			||||||
                    oss << "Reading " << chunkSize << " bytes"
 | 
					                    oss << "Reading " << chunkSize << " bytes"
 | 
				
			||||||
@@ -288,14 +360,14 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                // Read a chunk
 | 
					                // Read a chunk
 | 
				
			||||||
                auto chunkResult = _socket->readBytes((size_t) chunkSize,
 | 
					                auto chunkResult = _socket->readBytes((size_t) chunkSize,
 | 
				
			||||||
                                                      args.onProgressCallback,
 | 
					                                                      args->onProgressCallback,
 | 
				
			||||||
                                                      isCancellationRequested);
 | 
					                                                      isCancellationRequested);
 | 
				
			||||||
                if (!chunkResult.first)
 | 
					                if (!chunkResult.first)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    errorMsg = "Cannot read chunk";
 | 
					                    errorMsg = "Cannot read chunk";
 | 
				
			||||||
                    return HttpResponse(code, HttpErrorCode::ChunkReadError,
 | 
					                    return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
 | 
				
			||||||
                                        headers, payload, errorMsg,
 | 
					                                                          headers, payload, errorMsg,
 | 
				
			||||||
                                        uploadSize, downloadSize);
 | 
					                                                          uploadSize, downloadSize);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                payload += chunkResult.second;
 | 
					                payload += chunkResult.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -304,9 +376,9 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (!lineResult.first)
 | 
					                if (!lineResult.first)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    return HttpResponse(code, HttpErrorCode::ChunkReadError,
 | 
					                    return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
 | 
				
			||||||
                                        headers, payload, errorMsg,
 | 
					                                                          headers, payload, errorMsg,
 | 
				
			||||||
                                        uploadSize, downloadSize);
 | 
					                                                          uploadSize, downloadSize);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (chunkSize == 0) break;
 | 
					                if (chunkSize == 0) break;
 | 
				
			||||||
@@ -319,9 +391,9 @@ namespace ix
 | 
				
			|||||||
        else
 | 
					        else
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::string errorMsg("Cannot read http body");
 | 
					            std::string errorMsg("Cannot read http body");
 | 
				
			||||||
            return HttpResponse(code, HttpErrorCode::CannotReadBody,
 | 
					            return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotReadBody,
 | 
				
			||||||
                                headers, payload, errorMsg,
 | 
					                                                  headers, payload, errorMsg,
 | 
				
			||||||
                                uploadSize, downloadSize);
 | 
					                                                  uploadSize, downloadSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        downloadSize = payload.size();
 | 
					        downloadSize = payload.size();
 | 
				
			||||||
@@ -333,60 +405,60 @@ namespace ix
 | 
				
			|||||||
            if (!gzipInflate(payload, decompressedPayload))
 | 
					            if (!gzipInflate(payload, decompressedPayload))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                std::string errorMsg("Error decompressing payload");
 | 
					                std::string errorMsg("Error decompressing payload");
 | 
				
			||||||
                return HttpResponse(code, HttpErrorCode::Gzip,
 | 
					                return std::make_shared<HttpResponse>(code, HttpErrorCode::Gzip,
 | 
				
			||||||
                                    headers, payload, errorMsg,
 | 
					                                                      headers, payload, errorMsg,
 | 
				
			||||||
                                    uploadSize, downloadSize);
 | 
					                                                      uploadSize, downloadSize);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            payload = decompressedPayload;
 | 
					            payload = decompressedPayload;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return HttpResponse(code, HttpErrorCode::Ok,
 | 
					        return std::make_shared<HttpResponse>(code, HttpErrorCode::Ok,
 | 
				
			||||||
                            headers, payload, std::string(),
 | 
					                                              headers, payload, std::string(),
 | 
				
			||||||
                            uploadSize, downloadSize);
 | 
					                                              uploadSize, downloadSize);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::get(const std::string& url,
 | 
					    HttpResponsePtr HttpClient::get(const std::string& url,
 | 
				
			||||||
                                 const HttpRequestArgs& args)
 | 
					                                    HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return request(url, kGet, std::string(), args);
 | 
					        return request(url, kGet, std::string(), args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::head(const std::string& url,
 | 
					    HttpResponsePtr HttpClient::head(const std::string& url,
 | 
				
			||||||
                                  const HttpRequestArgs& args)
 | 
					                                     HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return request(url, kHead, std::string(), args);
 | 
					        return request(url, kHead, std::string(), args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::del(const std::string& url,
 | 
					    HttpResponsePtr HttpClient::del(const std::string& url,
 | 
				
			||||||
                                 const HttpRequestArgs& args)
 | 
					                                    HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return request(url, kDel, std::string(), args);
 | 
					        return request(url, kDel, std::string(), args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::post(const std::string& url,
 | 
					    HttpResponsePtr HttpClient::post(const std::string& url,
 | 
				
			||||||
                                  const HttpParameters& httpParameters,
 | 
					                                     const HttpParameters& httpParameters,
 | 
				
			||||||
                                  const HttpRequestArgs& args)
 | 
					                                     HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return request(url, kPost, serializeHttpParameters(httpParameters), args);
 | 
					        return request(url, kPost, serializeHttpParameters(httpParameters), args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::post(const std::string& url,
 | 
					    HttpResponsePtr HttpClient::post(const std::string& url,
 | 
				
			||||||
                                  const std::string& body,
 | 
					                                     const std::string& body,
 | 
				
			||||||
                                  const HttpRequestArgs& args)
 | 
					                                     HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return request(url, kPost, body, args);
 | 
					        return request(url, kPost, body, args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::put(const std::string& url,
 | 
					    HttpResponsePtr HttpClient::put(const std::string& url,
 | 
				
			||||||
                                 const HttpParameters& httpParameters,
 | 
					                                    const HttpParameters& httpParameters,
 | 
				
			||||||
                                 const HttpRequestArgs& args)
 | 
					                                    HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return request(url, kPut, serializeHttpParameters(httpParameters), args);
 | 
					        return request(url, kPut, serializeHttpParameters(httpParameters), args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse HttpClient::put(const std::string& url,
 | 
					    HttpResponsePtr HttpClient::put(const std::string& url,
 | 
				
			||||||
                                 const std::string& body,
 | 
					                                    const std::string& body,
 | 
				
			||||||
                                 const HttpRequestArgs& args)
 | 
					                                    const HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return request(url, kPut, body, args);
 | 
					        return request(url, kPut, body, args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -488,11 +560,11 @@ namespace ix
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void HttpClient::log(const std::string& msg,
 | 
					    void HttpClient::log(const std::string& msg,
 | 
				
			||||||
                         const HttpRequestArgs& args)
 | 
					                         HttpRequestArgsPtr args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (args.logger)
 | 
					        if (args->logger)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            args.logger(msg);
 | 
					            args->logger(msg);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,11 +10,13 @@
 | 
				
			|||||||
#include "IXWebSocketHttpHeaders.h"
 | 
					#include "IXWebSocketHttpHeaders.h"
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
#include <atomic>
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
#include <functional>
 | 
					#include <functional>
 | 
				
			||||||
#include <map>
 | 
					#include <map>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
#include <tuple>
 | 
					#include <queue>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -34,7 +36,8 @@ namespace ix
 | 
				
			|||||||
        MissingLocation = 11,
 | 
					        MissingLocation = 11,
 | 
				
			||||||
        TooManyRedirects = 12,
 | 
					        TooManyRedirects = 12,
 | 
				
			||||||
        ChunkReadError = 13,
 | 
					        ChunkReadError = 13,
 | 
				
			||||||
        CannotReadBody = 14
 | 
					        CannotReadBody = 14,
 | 
				
			||||||
 | 
					        Invalid = 100
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct HttpResponse
 | 
					    struct HttpResponse
 | 
				
			||||||
@@ -66,12 +69,15 @@ namespace ix
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using HttpResponsePtr = std::shared_ptr<HttpResponse>;
 | 
				
			||||||
    using HttpParameters = std::map<std::string, std::string>;
 | 
					    using HttpParameters = std::map<std::string, std::string>;
 | 
				
			||||||
    using Logger = std::function<void(const std::string&)>;
 | 
					    using Logger = std::function<void(const std::string&)>;
 | 
				
			||||||
 | 
					    using OnResponseCallback = std::function<void(const HttpResponsePtr&)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct HttpRequestArgs
 | 
					    struct HttpRequestArgs
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::string url;
 | 
					        std::string url;
 | 
				
			||||||
 | 
					        std::string verb;
 | 
				
			||||||
        WebSocketHttpHeaders extraHeaders;
 | 
					        WebSocketHttpHeaders extraHeaders;
 | 
				
			||||||
        std::string body;
 | 
					        std::string body;
 | 
				
			||||||
        int connectTimeout;
 | 
					        int connectTimeout;
 | 
				
			||||||
@@ -84,51 +90,72 @@ namespace ix
 | 
				
			|||||||
        OnProgressCallback onProgressCallback;
 | 
					        OnProgressCallback onProgressCallback;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using HttpRequestArgsPtr = std::shared_ptr<HttpRequestArgs>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class HttpClient
 | 
					    class HttpClient
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    public:
 | 
					    public:
 | 
				
			||||||
        HttpClient();
 | 
					        HttpClient(bool async = false);
 | 
				
			||||||
        ~HttpClient();
 | 
					        ~HttpClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpResponse get(const std::string& url, const HttpRequestArgs& args);
 | 
					        HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args);
 | 
				
			||||||
        HttpResponse head(const std::string& url, const HttpRequestArgs& args);
 | 
					        HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args);
 | 
				
			||||||
        HttpResponse del(const std::string& url, const HttpRequestArgs& args);
 | 
					        HttpResponsePtr del(const std::string& url, HttpRequestArgsPtr args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpResponse post(const std::string& url,
 | 
					        HttpResponsePtr post(const std::string& url,
 | 
				
			||||||
                          const HttpParameters& httpParameters,
 | 
					                             const HttpParameters& httpParameters,
 | 
				
			||||||
                          const HttpRequestArgs& args);
 | 
					                             HttpRequestArgsPtr args);
 | 
				
			||||||
        HttpResponse post(const std::string& url,
 | 
					        HttpResponsePtr post(const std::string& url,
 | 
				
			||||||
                          const std::string& body,
 | 
					 | 
				
			||||||
                          const HttpRequestArgs& args);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        HttpResponse put(const std::string& url,
 | 
					 | 
				
			||||||
                         const HttpParameters& httpParameters,
 | 
					 | 
				
			||||||
                         const HttpRequestArgs& args);
 | 
					 | 
				
			||||||
        HttpResponse put(const std::string& url,
 | 
					 | 
				
			||||||
                         const std::string& body,
 | 
					 | 
				
			||||||
                         const HttpRequestArgs& args);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        HttpResponse request(const std::string& url,
 | 
					 | 
				
			||||||
                             const std::string& verb,
 | 
					 | 
				
			||||||
                             const std::string& body,
 | 
					                             const std::string& body,
 | 
				
			||||||
                             const HttpRequestArgs& args,
 | 
					                             HttpRequestArgsPtr args);
 | 
				
			||||||
                             int redirects = 0);
 | 
					
 | 
				
			||||||
 | 
					        HttpResponsePtr put(const std::string& url,
 | 
				
			||||||
 | 
					                            const HttpParameters& httpParameters,
 | 
				
			||||||
 | 
					                            HttpRequestArgsPtr args);
 | 
				
			||||||
 | 
					        HttpResponsePtr put(const std::string& url,
 | 
				
			||||||
 | 
					                            const std::string& body,
 | 
				
			||||||
 | 
					                            HttpRequestArgsPtr args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HttpResponsePtr request(const std::string& url,
 | 
				
			||||||
 | 
					                                const std::string& verb,
 | 
				
			||||||
 | 
					                                const std::string& body,
 | 
				
			||||||
 | 
					                                HttpRequestArgsPtr args,
 | 
				
			||||||
 | 
					                                int redirects = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Async API
 | 
				
			||||||
 | 
					        HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
 | 
				
			||||||
 | 
					                                         const std::string& verb = HttpClient::kGet);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool performRequest(HttpRequestArgsPtr request,
 | 
				
			||||||
 | 
					                            const OnResponseCallback& onResponseCallback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string serializeHttpParameters(const HttpParameters& httpParameters);
 | 
					        std::string serializeHttpParameters(const HttpParameters& httpParameters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string urlEncode(const std::string& value);
 | 
					        std::string urlEncode(const std::string& value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private:
 | 
					 | 
				
			||||||
        void log(const std::string& msg, const HttpRequestArgs& args);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        bool gzipInflate(const std::string& in, std::string& out);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::shared_ptr<Socket> _socket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const static std::string kPost;
 | 
					        const static std::string kPost;
 | 
				
			||||||
        const static std::string kGet;
 | 
					        const static std::string kGet;
 | 
				
			||||||
        const static std::string kHead;
 | 
					        const static std::string kHead;
 | 
				
			||||||
        const static std::string kDel;
 | 
					        const static std::string kDel;
 | 
				
			||||||
        const static std::string kPut;
 | 
					        const static std::string kPut;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
 | 
					        void log(const std::string& msg, HttpRequestArgsPtr args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool gzipInflate(const std::string& in, std::string& out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Async API background thread runner
 | 
				
			||||||
 | 
					        void run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Async API
 | 
				
			||||||
 | 
					        bool _async;
 | 
				
			||||||
 | 
					        std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue;
 | 
				
			||||||
 | 
					        mutable std::mutex _queueMutex;
 | 
				
			||||||
 | 
					        std::condition_variable _condition;
 | 
				
			||||||
 | 
					        std::atomic<bool> _stop;
 | 
				
			||||||
 | 
					        std::thread _thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::shared_ptr<Socket> _socket;
 | 
				
			||||||
 | 
					        std::mutex _mutex; // to protect accessing the _socket (only one socket per client)
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,85 +15,219 @@ TEST_CASE("http client", "[http]")
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    SECTION("Connect to a remote HTTP server")
 | 
					    SECTION("Connect to a remote HTTP server")
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::string url("http://httpbin.org/");
 | 
					        HttpClient httpClient;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        WebSocketHttpHeaders headers;
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
        headers["User-Agent"] = "ixwebsocket";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpRequestArgs args;
 | 
					        std::string url("http://httpbin.org/");
 | 
				
			||||||
        args.extraHeaders = headers;
 | 
					        auto args = httpClient.createRequest(url);
 | 
				
			||||||
        args.connectTimeout = 60;
 | 
					
 | 
				
			||||||
        args.transferTimeout = 60;
 | 
					        args->extraHeaders = headers;
 | 
				
			||||||
        args.followRedirects = true;
 | 
					        args->connectTimeout = 60;
 | 
				
			||||||
        args.maxRedirects = 10;
 | 
					        args->transferTimeout = 60;
 | 
				
			||||||
        args.verbose = true;
 | 
					        args->followRedirects = true;
 | 
				
			||||||
        args.compress = true;
 | 
					        args->maxRedirects = 10;
 | 
				
			||||||
        args.logger = [](const std::string& msg)
 | 
					        args->verbose = true;
 | 
				
			||||||
 | 
					        args->compress = true;
 | 
				
			||||||
 | 
					        args->logger = [](const std::string& msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cout << msg;
 | 
					            std::cout << msg;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        args.onProgressCallback = [](int current, int total) -> bool
 | 
					        args->onProgressCallback = [](int current, int total) -> bool
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cerr << "\r" << "Downloaded "
 | 
					            std::cerr << "\r" << "Downloaded "
 | 
				
			||||||
                      << current << " bytes out of " << total;
 | 
					                      << current << " bytes out of " << total;
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpClient httpClient;
 | 
					        auto response = httpClient.get(url, args);
 | 
				
			||||||
        HttpResponse response = httpClient.get(url, args);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (auto it : response.headers)
 | 
					        for (auto it : response->headers)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cerr << it.first << ": " << it.second << std::endl;
 | 
					            std::cerr << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::cerr << "Upload size: " << response.uploadSize << std::endl;
 | 
					        std::cerr << "Upload size: " << response->uploadSize << std::endl;
 | 
				
			||||||
        std::cerr << "Download size: " << response.downloadSize << std::endl;
 | 
					        std::cerr << "Download size: " << response->downloadSize << std::endl;
 | 
				
			||||||
        std::cerr << "Status: " << response.statusCode << std::endl;
 | 
					        std::cerr << "Status: " << response->statusCode << std::endl;
 | 
				
			||||||
 | 
					        std::cerr << "Error message: " << response->errorMsg << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        REQUIRE(response.errorCode == HttpErrorCode::Ok);
 | 
					        REQUIRE(response->errorCode == HttpErrorCode::Ok);
 | 
				
			||||||
 | 
					        REQUIRE(response->statusCode == 200);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(IXWEBSOCKET_USE_TLS)
 | 
					 | 
				
			||||||
    SECTION("Connect to a remote HTTPS server")
 | 
					    SECTION("Connect to a remote HTTPS server")
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::string url("https://httpbin.org/");
 | 
					        HttpClient httpClient;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        WebSocketHttpHeaders headers;
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
        headers["User-Agent"] = "ixwebsocket";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpRequestArgs args;
 | 
					        std::string url("https://httpbin.org/");
 | 
				
			||||||
        args.extraHeaders = headers;
 | 
					        auto args = httpClient.createRequest(url);
 | 
				
			||||||
        args.connectTimeout = 60;
 | 
					
 | 
				
			||||||
        args.transferTimeout = 60;
 | 
					        args->extraHeaders = headers;
 | 
				
			||||||
        args.followRedirects = true;
 | 
					        args->connectTimeout = 60;
 | 
				
			||||||
        args.maxRedirects = 10;
 | 
					        args->transferTimeout = 60;
 | 
				
			||||||
        args.verbose = true;
 | 
					        args->followRedirects = true;
 | 
				
			||||||
        args.compress = true;
 | 
					        args->maxRedirects = 10;
 | 
				
			||||||
        args.logger = [](const std::string& msg)
 | 
					        args->verbose = true;
 | 
				
			||||||
 | 
					        args->compress = true;
 | 
				
			||||||
 | 
					        args->logger = [](const std::string& msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cout << msg;
 | 
					            std::cout << msg;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        args.onProgressCallback = [](int current, int total) -> bool
 | 
					        args->onProgressCallback = [](int current, int total) -> bool
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cerr << "\r" << "Downloaded "
 | 
					            std::cerr << "\r" << "Downloaded "
 | 
				
			||||||
                      << current << " bytes out of " << total;
 | 
					                      << current << " bytes out of " << total;
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpClient httpClient;
 | 
					        auto response = httpClient.get(url, args);
 | 
				
			||||||
        HttpResponse response = httpClient.get(url, args);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (auto it : response.headers)
 | 
					        for (auto it : response->headers)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cerr << it.first << ": " << it.second << std::endl;
 | 
					            std::cerr << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::cerr << "Upload size: " << response.uploadSize << std::endl;
 | 
					        std::cerr << "Upload size: " << response->uploadSize << std::endl;
 | 
				
			||||||
        std::cerr << "Download size: " << response.downloadSize << std::endl;
 | 
					        std::cerr << "Download size: " << response->downloadSize << std::endl;
 | 
				
			||||||
        std::cerr << "Status: " << response.statusCode << std::endl;
 | 
					        std::cerr << "Status: " << response->statusCode << std::endl;
 | 
				
			||||||
 | 
					        std::cerr << "Error message: " << response->errorMsg << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        REQUIRE(response.errorCode == HttpErrorCode::Ok);
 | 
					        REQUIRE(response->errorCode == HttpErrorCode::Ok);
 | 
				
			||||||
 | 
					        REQUIRE(response->statusCode == 200);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SECTION("Async API, one call")
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        bool async = true;
 | 
				
			||||||
 | 
					        HttpClient httpClient(async);
 | 
				
			||||||
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string url("https://httpbin.org/");
 | 
				
			||||||
 | 
					        auto args = httpClient.createRequest(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        args->extraHeaders = headers;
 | 
				
			||||||
 | 
					        args->connectTimeout = 60;
 | 
				
			||||||
 | 
					        args->transferTimeout = 60;
 | 
				
			||||||
 | 
					        args->followRedirects = true;
 | 
				
			||||||
 | 
					        args->maxRedirects = 10;
 | 
				
			||||||
 | 
					        args->verbose = true;
 | 
				
			||||||
 | 
					        args->compress = true;
 | 
				
			||||||
 | 
					        args->logger = [](const std::string& msg)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cout << msg;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        args->onProgressCallback = [](int current, int total) -> bool
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cerr << "\r" << "Downloaded "
 | 
				
			||||||
 | 
					                      << current << " bytes out of " << total;
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::atomic<bool> requestCompleted(false);
 | 
				
			||||||
 | 
					        std::atomic<int> statusCode(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        httpClient.performRequest(args, [&requestCompleted, &statusCode]
 | 
				
			||||||
 | 
					            (const HttpResponsePtr& response)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                std::cerr << "Upload size: " << response->uploadSize << std::endl;
 | 
				
			||||||
 | 
					                std::cerr << "Download size: " << response->downloadSize << std::endl;
 | 
				
			||||||
 | 
					                std::cerr << "Status: " << response->statusCode << std::endl;
 | 
				
			||||||
 | 
					                std::cerr << "Error message: " << response->errorMsg << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // In case of failure, print response->errorMsg
 | 
				
			||||||
 | 
					                statusCode = response->statusCode;
 | 
				
			||||||
 | 
					                requestCompleted = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int wait = 0;
 | 
				
			||||||
 | 
					        while (wait < 5000)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (requestCompleted) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::chrono::duration<double, std::milli> duration(10);
 | 
				
			||||||
 | 
					            std::this_thread::sleep_for(duration);
 | 
				
			||||||
 | 
					            wait += 10;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cerr << "Done" << std::endl;
 | 
				
			||||||
 | 
					        REQUIRE(statusCode == 200);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SECTION("Async API, multiple calls")
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        bool async = true;
 | 
				
			||||||
 | 
					        HttpClient httpClient(async);
 | 
				
			||||||
 | 
					        WebSocketHttpHeaders headers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string url("http://httpbin.org/");
 | 
				
			||||||
 | 
					        auto args = httpClient.createRequest(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        args->extraHeaders = headers;
 | 
				
			||||||
 | 
					        args->connectTimeout = 60;
 | 
				
			||||||
 | 
					        args->transferTimeout = 60;
 | 
				
			||||||
 | 
					        args->followRedirects = true;
 | 
				
			||||||
 | 
					        args->maxRedirects = 10;
 | 
				
			||||||
 | 
					        args->verbose = true;
 | 
				
			||||||
 | 
					        args->compress = true;
 | 
				
			||||||
 | 
					        args->logger = [](const std::string& msg)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cout << msg;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        args->onProgressCallback = [](int current, int total) -> bool
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            std::cerr << "\r" << "Downloaded "
 | 
				
			||||||
 | 
					                      << current << " bytes out of " << total;
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::atomic<bool> requestCompleted(false);
 | 
				
			||||||
 | 
					        std::atomic<int> statusCode0(0);
 | 
				
			||||||
 | 
					        std::atomic<int> statusCode1(0);
 | 
				
			||||||
 | 
					        std::atomic<int> statusCode2(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < 3; ++i)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            httpClient.performRequest(args, [i, &requestCompleted, &statusCode0, &statusCode1, &statusCode2]
 | 
				
			||||||
 | 
					                (const HttpResponsePtr& response)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    std::cerr << "Upload size: " << response->uploadSize << std::endl;
 | 
				
			||||||
 | 
					                    std::cerr << "Download size: " << response->downloadSize << std::endl;
 | 
				
			||||||
 | 
					                    std::cerr << "Status: " << response->statusCode << std::endl;
 | 
				
			||||||
 | 
					                    std::cerr << "Error message: " << response->errorMsg << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // In case of failure, print response->errorMsg
 | 
				
			||||||
 | 
					                    if (i == 0)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        statusCode0 = response->statusCode;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else if (i == 1)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        statusCode1 = response->statusCode;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else if (i == 2)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        statusCode2 = response->statusCode;
 | 
				
			||||||
 | 
					                        requestCompleted = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int wait = 0;
 | 
				
			||||||
 | 
					        while (wait < 10000)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (requestCompleted) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::chrono::duration<double, std::milli> duration(10);
 | 
				
			||||||
 | 
					            std::this_thread::sleep_for(duration);
 | 
				
			||||||
 | 
					            wait += 10;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::cerr << "Done" << std::endl;
 | 
				
			||||||
 | 
					        REQUIRE(statusCode0 == 200);
 | 
				
			||||||
 | 
					        REQUIRE(statusCode1 == 200);
 | 
				
			||||||
 | 
					        REQUIRE(statusCode2 == 200);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <chrono>
 | 
					#include <chrono>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
 | 
					#include <ixwebsocket/IXWebSocketHttpHeaders.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -114,6 +115,7 @@ namespace ix
 | 
				
			|||||||
    std::string SentryClient::computePayload(const Json::Value& msg)
 | 
					    std::string SentryClient::computePayload(const Json::Value& msg)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Json::Value payload;
 | 
					        Json::Value payload;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        payload["platform"] = "python";
 | 
					        payload["platform"] = "python";
 | 
				
			||||||
        payload["sdk"]["name"] = "ws";
 | 
					        payload["sdk"]["name"] = "ws";
 | 
				
			||||||
        payload["sdk"]["version"] = "1.0.0";
 | 
					        payload["sdk"]["version"] = "1.0.0";
 | 
				
			||||||
@@ -132,51 +134,80 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Json::Value extra;
 | 
					        Json::Value extra;
 | 
				
			||||||
        extra["cobra_event"] = msg;
 | 
					        extra["cobra_event"] = msg;
 | 
				
			||||||
 | 
					        extra["cobra_event"] = msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        exception["extra"] = extra;
 | 
					        //
 | 
				
			||||||
 | 
					        // "tags": [
 | 
				
			||||||
 | 
					        //   [
 | 
				
			||||||
 | 
					        //     "a",
 | 
				
			||||||
 | 
					        //     "b"
 | 
				
			||||||
 | 
					        //   ],
 | 
				
			||||||
 | 
					        //  ]
 | 
				
			||||||
 | 
					        // 
 | 
				
			||||||
 | 
					        Json::Value tags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Json::Value gameTag;
 | 
				
			||||||
 | 
					        gameTag.append("game");
 | 
				
			||||||
 | 
					        gameTag.append(msg["device"]["game"]);
 | 
				
			||||||
 | 
					        tags.append(gameTag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Json::Value userIdTag;
 | 
				
			||||||
 | 
					        userIdTag.append("userid");
 | 
				
			||||||
 | 
					        userIdTag.append(msg["device"]["user_id"]);
 | 
				
			||||||
 | 
					        tags.append(userIdTag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Json::Value environmentTag;
 | 
				
			||||||
 | 
					        environmentTag.append("environment");
 | 
				
			||||||
 | 
					        environmentTag.append(msg["device"]["environment"]);
 | 
				
			||||||
 | 
					        tags.append(environmentTag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        payload["tags"] = tags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return _jsonWriter.write(payload);
 | 
					        return _jsonWriter.write(payload);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool SentryClient::send(const Json::Value& msg,
 | 
					    std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg,
 | 
				
			||||||
                            bool verbose)
 | 
					                                                               bool verbose)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        HttpRequestArgs args;
 | 
					        std::string log;
 | 
				
			||||||
        args.extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
 | 
					
 | 
				
			||||||
        args.connectTimeout = 60;
 | 
					        auto args = _httpClient.createRequest();
 | 
				
			||||||
        args.transferTimeout = 5 * 60;
 | 
					        args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
 | 
				
			||||||
        args.followRedirects = true;
 | 
					        args->connectTimeout = 60;
 | 
				
			||||||
        args.verbose = verbose;
 | 
					        args->transferTimeout = 5 * 60;
 | 
				
			||||||
        args.logger = [](const std::string& msg)
 | 
					        args->followRedirects = true;
 | 
				
			||||||
 | 
					        args->verbose = verbose;
 | 
				
			||||||
 | 
					        args->logger = [&log](const std::string& msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            log += msg;
 | 
				
			||||||
            std::cout << msg;
 | 
					            std::cout << msg;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string body = computePayload(msg);
 | 
					        std::string body = computePayload(msg);
 | 
				
			||||||
        HttpResponse response = _httpClient.post(_url, body, args);
 | 
					        HttpResponsePtr response = _httpClient.post(_url, body, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (verbose)
 | 
					        if (verbose)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            for (auto it : response.headers)
 | 
					            for (auto it : response->headers)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                std::cerr << it.first << ": " << it.second << std::endl;
 | 
					                spdlog::info("{}: {}", it.first, it.second);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            std::cerr << "Upload size: " << response.uploadSize << std::endl;
 | 
					            spdlog::info("Upload size: {}", response->uploadSize);
 | 
				
			||||||
            std::cerr << "Download size: " << response.downloadSize << std::endl;
 | 
					            spdlog::info("Download size: {}", response->downloadSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            std::cerr << "Status: " << response.statusCode << std::endl;
 | 
					            std::cerr << "Status: " << response->statusCode << std::endl;
 | 
				
			||||||
            if (response.errorCode != HttpErrorCode::Ok)
 | 
					            if (response->errorCode != HttpErrorCode::Ok)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                std::cerr << "error message: " << response.errorMsg << std::endl;
 | 
					                spdlog::info("error message: {}", response->errorMsg);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (response.headers["Content-Type"] != "application/octet-stream")
 | 
					            if (response->headers["Content-Type"] != "application/octet-stream")
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                std::cerr << "payload: " << response.payload << std::endl;
 | 
					                spdlog::info("payload: {}", response->payload);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return response.statusCode == 200;
 | 
					        return std::make_pair(response, log);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
} // namespace ix
 | 
					} // namespace ix
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@
 | 
				
			|||||||
#include <ixwebsocket/IXHttpClient.h>
 | 
					#include <ixwebsocket/IXHttpClient.h>
 | 
				
			||||||
#include <jsoncpp/json/json.h>
 | 
					#include <jsoncpp/json/json.h>
 | 
				
			||||||
#include <regex>
 | 
					#include <regex>
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ix
 | 
					namespace ix
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -18,7 +19,7 @@ namespace ix
 | 
				
			|||||||
        SentryClient(const std::string& dsn);
 | 
					        SentryClient(const std::string& dsn);
 | 
				
			||||||
        ~SentryClient() = default;
 | 
					        ~SentryClient() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool send(const Json::Value& msg, bool verbose);
 | 
					        std::pair<HttpResponsePtr, std::string> send(const Json::Value& msg, bool verbose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private:
 | 
					    private:
 | 
				
			||||||
        int64_t getTimestamp();
 | 
					        int64_t getTimestamp();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,7 +99,7 @@ namespace ix
 | 
				
			|||||||
    void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind,
 | 
					    void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind,
 | 
				
			||||||
                                                    const Json::Value& msg)
 | 
					                                                    const Json::Value& msg)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // Now actually enqueue the task
 | 
					        // Enqueue the task
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // acquire lock
 | 
					            // acquire lock
 | 
				
			||||||
            std::unique_lock<std::mutex> lock(_queue_mutex);
 | 
					            std::unique_lock<std::mutex> lock(_queue_mutex);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,6 @@ namespace ix
 | 
				
			|||||||
        ~CobraMetricsThreadedPublisher();
 | 
					        ~CobraMetricsThreadedPublisher();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// Configuration / set keys, etc...
 | 
					        /// Configuration / set keys, etc...
 | 
				
			||||||
        /// All input data but the channel name is encrypted with rc4
 | 
					 | 
				
			||||||
        void configure(const std::string& appkey,
 | 
					        void configure(const std::string& appkey,
 | 
				
			||||||
                       const std::string& endpoint,
 | 
					                       const std::string& endpoint,
 | 
				
			||||||
                       const std::string& channel,
 | 
					                       const std::string& channel,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
#include <condition_variable>
 | 
					#include <condition_variable>
 | 
				
			||||||
#include <ixcobra/IXCobraConnection.h>
 | 
					#include <ixcobra/IXCobraConnection.h>
 | 
				
			||||||
 | 
					#include <spdlog/spdlog.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "IXSentryClient.h"
 | 
					#include "IXSentryClient.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,9 +47,11 @@ namespace ix
 | 
				
			|||||||
        std::condition_variable progressCondition;
 | 
					        std::condition_variable progressCondition;
 | 
				
			||||||
        std::queue<Json::Value> queue;
 | 
					        std::queue<Json::Value> queue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SentryClient sentryClient(dsn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto sentrySender = [&condition, &progressCondition, &conditionVariableMutex,
 | 
					        auto sentrySender = [&condition, &progressCondition, &conditionVariableMutex,
 | 
				
			||||||
                             &queue, verbose, &errorSending, &sentCount,
 | 
					                             &queue, verbose, &errorSending, &sentCount,
 | 
				
			||||||
                             &stop, &dsn]
 | 
					                             &stop, &sentryClient]
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            while (true)
 | 
					            while (true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -62,10 +65,13 @@ namespace ix
 | 
				
			|||||||
                    queue.pop();
 | 
					                    queue.pop();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                SentryClient sc(dsn);
 | 
					                auto ret = sentryClient.send(msg, verbose);
 | 
				
			||||||
 | 
					                HttpResponsePtr response = ret.first;
 | 
				
			||||||
                if (!sc.send(msg, verbose))
 | 
					                if (response->statusCode != 200)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
					                    spdlog::error("Error sending data to sentry: {}", response->statusCode);
 | 
				
			||||||
 | 
					                    spdlog::error("Response: {}", response->payload);
 | 
				
			||||||
 | 
					                    spdlog::error("Log: {}", ret.second);
 | 
				
			||||||
                    errorSending = true;
 | 
					                    errorSending = true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
@@ -99,16 +105,16 @@ namespace ix
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                if (eventType == ix::CobraConnection_EventType_Open)
 | 
					                if (eventType == ix::CobraConnection_EventType_Open)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    std::cerr << "Subscriber: connected" << std::endl;
 | 
					                    spdlog::info("Subscriber connected");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    for (auto it : headers)
 | 
					                    for (auto it : headers)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        std::cerr << it.first << ": " << it.second << std::endl;
 | 
					                        spdlog::info("{}: {}", it.first, it.second);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (eventType == ix::CobraConnection_EventType_Closed)
 | 
					                if (eventType == ix::CobraConnection_EventType_Closed)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    std::cerr << "Subscriber: closed" << std::endl;
 | 
					                    spdlog::info("Subscriber closed");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (eventType == ix::CobraConnection_EventType_Authenticated)
 | 
					                else if (eventType == ix::CobraConnection_EventType_Authenticated)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@@ -122,7 +128,7 @@ namespace ix
 | 
				
			|||||||
                                   {
 | 
					                                   {
 | 
				
			||||||
                                       if (verbose)
 | 
					                                       if (verbose)
 | 
				
			||||||
                                       {
 | 
					                                       {
 | 
				
			||||||
                                           std::cerr << jsonWriter.write(msg) << std::endl;
 | 
					                                           spdlog::info(jsonWriter.write(msg));
 | 
				
			||||||
                                       }
 | 
					                                       }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                       // If we cannot send to sentry fast enough, drop the message
 | 
					                                       // If we cannot send to sentry fast enough, drop the message
 | 
				
			||||||
@@ -132,8 +138,7 @@ namespace ix
 | 
				
			|||||||
                                           receivedCount != 0 &&
 | 
					                                           receivedCount != 0 &&
 | 
				
			||||||
                                           (sentCount * scaleFactor < receivedCount))
 | 
					                                           (sentCount * scaleFactor < receivedCount))
 | 
				
			||||||
                                       {
 | 
					                                       {
 | 
				
			||||||
                                           std::cerr << "message dropped: sending is backlogged !"
 | 
					                                           spdlog::warn("message dropped: sending is backlogged !");
 | 
				
			||||||
                                                     << std::endl;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                           condition.notify_one();
 | 
					                                           condition.notify_one();
 | 
				
			||||||
                                           progressCondition.notify_one();
 | 
					                                           progressCondition.notify_one();
 | 
				
			||||||
@@ -153,15 +158,15 @@ namespace ix
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (eventType == ix::CobraConnection_EventType_Subscribed)
 | 
					                else if (eventType == ix::CobraConnection_EventType_Subscribed)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    std::cerr << "Subscriber: subscribed to channel " << subscriptionId << std::endl;
 | 
					                    spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
 | 
					                else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    std::cerr << "Subscriber: unsubscribed from channel " << subscriptionId << std::endl;
 | 
					                    spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (eventType == ix::CobraConnection_EventType_Error)
 | 
					                else if (eventType == ix::CobraConnection_EventType_Error)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    std::cerr << "Subscriber: error" << errMsg << std::endl;
 | 
					                    spdlog::error("Subscriber: error {}", errMsg);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@@ -172,17 +177,20 @@ namespace ix
 | 
				
			|||||||
            std::unique_lock<std::mutex> lock(progressConditionVariableMutex);
 | 
					            std::unique_lock<std::mutex> lock(progressConditionVariableMutex);
 | 
				
			||||||
            progressCondition.wait(lock);
 | 
					            progressCondition.wait(lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            std::cout << "messages"
 | 
					            spdlog::info("messages received {} sent {}", receivedCount, sentCount);
 | 
				
			||||||
                      << " received " << receivedCount
 | 
					 | 
				
			||||||
                      << " sent " << sentCount
 | 
					 | 
				
			||||||
                      << std::endl;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (strict && errorSending) break;
 | 
					            if (strict && errorSending) break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn.disconnect();
 | 
					        conn.disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // FIXME: join all the bg threads and stop them.
 | 
					        // join all the bg threads and stop them.
 | 
				
			||||||
 | 
					        stop = true;
 | 
				
			||||||
 | 
					        for (int i = 0; i < jobs; i++)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            spdlog::error("joining thread {}", i);
 | 
				
			||||||
 | 
					            pool[i].join();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,19 +95,20 @@ namespace ix
 | 
				
			|||||||
                            const std::string& output,
 | 
					                            const std::string& output,
 | 
				
			||||||
                            bool compress)
 | 
					                            bool compress)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        HttpRequestArgs args;
 | 
					        HttpClient httpClient;
 | 
				
			||||||
        args.extraHeaders = parseHeaders(headersData);
 | 
					        auto args = httpClient.createRequest();
 | 
				
			||||||
        args.connectTimeout = connectTimeout;
 | 
					        args->extraHeaders = parseHeaders(headersData);
 | 
				
			||||||
        args.transferTimeout = transferTimeout;
 | 
					        args->connectTimeout = connectTimeout;
 | 
				
			||||||
        args.followRedirects = followRedirects;
 | 
					        args->transferTimeout = transferTimeout;
 | 
				
			||||||
        args.maxRedirects = maxRedirects;
 | 
					        args->followRedirects = followRedirects;
 | 
				
			||||||
        args.verbose = verbose;
 | 
					        args->maxRedirects = maxRedirects;
 | 
				
			||||||
        args.compress = compress;
 | 
					        args->verbose = verbose;
 | 
				
			||||||
        args.logger = [](const std::string& msg)
 | 
					        args->compress = compress;
 | 
				
			||||||
 | 
					        args->logger = [](const std::string& msg)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cout << msg;
 | 
					            std::cout << msg;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        args.onProgressCallback = [](int current, int total) -> bool
 | 
					        args->onProgressCallback = [](int current, int total) -> bool
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cerr << "\r" << "Downloaded "
 | 
					            std::cerr << "\r" << "Downloaded "
 | 
				
			||||||
                      << current << " bytes out of " << total;
 | 
					                      << current << " bytes out of " << total;
 | 
				
			||||||
@@ -116,8 +117,7 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        HttpParameters httpParameters = parsePostParameters(data);
 | 
					        HttpParameters httpParameters = parsePostParameters(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpClient httpClient;
 | 
					        HttpResponsePtr response;
 | 
				
			||||||
        HttpResponse response;
 | 
					 | 
				
			||||||
        if (headersOnly)
 | 
					        if (headersOnly)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            response = httpClient.head(url, args);
 | 
					            response = httpClient.head(url, args);
 | 
				
			||||||
@@ -133,21 +133,21 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        std::cerr << std::endl;
 | 
					        std::cerr << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (auto it : response.headers)
 | 
					        for (auto it : response->headers)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cerr << it.first << ": " << it.second << std::endl;
 | 
					            std::cerr << it.first << ": " << it.second << std::endl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::cerr << "Upload size: " << response.uploadSize << std::endl;
 | 
					        std::cerr << "Upload size: " << response->uploadSize << std::endl;
 | 
				
			||||||
        std::cerr << "Download size: " << response.downloadSize << std::endl;
 | 
					        std::cerr << "Download size: " << response->downloadSize << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::cerr << "Status: " << response.statusCode << std::endl;
 | 
					        std::cerr << "Status: " << response->statusCode << std::endl;
 | 
				
			||||||
        if (response.errorCode != HttpErrorCode::Ok)
 | 
					        if (response->errorCode != HttpErrorCode::Ok)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            std::cerr << "error message: " << response.errorMsg << std::endl;
 | 
					            std::cerr << "error message: " << response->errorMsg << std::endl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!headersOnly && response.errorCode == HttpErrorCode::Ok)
 | 
					        if (!headersOnly && response->errorCode == HttpErrorCode::Ok)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (save || !output.empty())
 | 
					            if (save || !output.empty())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -160,14 +160,14 @@ namespace ix
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                std::cout << "Writing to disk: " << filename << std::endl;
 | 
					                std::cout << "Writing to disk: " << filename << std::endl;
 | 
				
			||||||
                std::ofstream out(filename);
 | 
					                std::ofstream out(filename);
 | 
				
			||||||
                out.write((char*)&response.payload.front(), response.payload.size());
 | 
					                out.write((char*)&response->payload.front(), response->payload.size());
 | 
				
			||||||
                out.close();
 | 
					                out.close();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (response.headers["Content-Type"] != "application/octet-stream")
 | 
					                if (response->headers["Content-Type"] != "application/octet-stream")
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    std::cout << "payload: " << response.payload << std::endl;
 | 
					                    std::cout << "payload: " << response->payload << std::endl;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user