Last active
February 20, 2019 09:36
-
-
Save elbakramer/1b03be04eecdbf39ab5fd9366df8c7f4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <cstring> | |
#include <sstream> | |
#include <vector> | |
#include <map> | |
#include <mutex> | |
#include <algorithm> | |
#include <curl/curl.h> | |
#include <boost/algorithm/string.hpp> | |
#include <rxcpp/rx.hpp> | |
using namespace std; | |
class HttpHeaders { | |
private: | |
std::map< std::string, std::vector<std::string> > headerMap; | |
public: | |
bool has(const std::string &name) { | |
std::map< std::string, std::vector<std::string> >::iterator found = headerMap.find(name); | |
return found != headerMap.end(); | |
} | |
std::string get(const std::string &name) { | |
std::map< std::string, std::vector<std::string> >::iterator found = headerMap.find(name); | |
if (found != headerMap.end()) { | |
if (found->second.size() > 0) { | |
return found->second[0]; | |
} | |
} | |
return ""; | |
} | |
std::vector<std::string> keys() { | |
std::vector<std::string> keysVector(headerMap.size()); | |
for (std::map< std::string, std::vector<std::string> >::iterator it = headerMap.begin(); it != headerMap.end(); it++) { | |
keysVector.push_back(it->first); | |
} | |
return keysVector; | |
} | |
std::vector<std::string> getAll(const std::string &name) { | |
std::map< std::string, std::vector<std::string> >::iterator found = headerMap.find(name); | |
if (found != headerMap.end()) { | |
return found->second; | |
} | |
return std::vector<std::string>(); | |
} | |
HttpHeaders append(const std::string &name, const std::vector<std::string> &values) { | |
HttpHeaders headers(*this); | |
if (headers.has(name)) { | |
for (std::vector<std::string>::const_iterator it = values.begin(); it != values.end(); it++) { | |
headers.headerMap[name].push_back(*it); | |
} | |
} else { | |
headers.headerMap[name] = values; | |
} | |
return headers; | |
} | |
HttpHeaders append(const std::string &name, const std::string &value) { | |
std::vector<std::string> values; | |
values.push_back(value); | |
return append(name, values); | |
} | |
HttpHeaders set(const std::string &name, const std::vector<std::string> &values) { | |
HttpHeaders headers(*this); | |
headers.headerMap[name] = values; | |
return headers; | |
} | |
HttpHeaders set(const std::string &name, const std::string &value) { | |
std::vector<std::string> values; | |
values.push_back(value); | |
return set(name, values); | |
} | |
HttpHeaders remove(const std::string &name, const std::vector<std::string> &values) { | |
HttpHeaders headers(*this); | |
std::vector<std::string> originalValues = headers.getAll(name); | |
for (std::vector<std::string>::const_iterator it = values.begin(); it != values.end(); it++) { | |
originalValues.erase(std::find(originalValues.begin(), originalValues.end(), *it)); | |
} | |
return headers.set(name, originalValues); | |
} | |
HttpHeaders remove(const std::string &name, const std::string &value) { | |
std::vector<std::string> values; | |
values.push_back(value); | |
return remove(name, values); | |
} | |
HttpHeaders remove(const std::string &name) { | |
HttpHeaders headers(*this); | |
headers.headerMap.erase(name); | |
return headers; | |
} | |
std::string toString() const { | |
std::ostringstream os; | |
for (std::map< std::string, std::vector<std::string> >::const_iterator it = headerMap.begin(); it != headerMap.end(); it++) { | |
const std::string &key = it->first; | |
const std::vector<std::string> &values = it->second; | |
os << key << ": "; | |
for (std::vector<std::string>::const_iterator vit = values.begin(); vit != values.end(); vit++) { | |
if (vit != values.begin()) { | |
os << ", "; | |
} | |
os << *vit; | |
} | |
os << "\n"; | |
} | |
std::string output = os.str(); | |
return output; | |
} | |
}; | |
class HttpParams { | |
private: | |
CURL *curl; | |
std::map< std::string, std::vector<std::string> > paramMap; | |
public: | |
HttpParams() { | |
curl = curl_easy_init(); | |
} | |
HttpParams(const HttpParams &other) { | |
this->curl = curl_easy_init(); | |
this->paramMap = other.paramMap; | |
} | |
~HttpParams() { | |
curl_easy_cleanup(curl); | |
} | |
bool has(const std::string &name) { | |
std::map< std::string, std::vector<std::string> >::iterator found = paramMap.find(name); | |
return found != paramMap.end(); | |
} | |
std::string get(const std::string &name) { | |
std::map< std::string, std::vector<std::string> >::iterator found = paramMap.find(name); | |
if (found != paramMap.end()) { | |
if (found->second.size() > 0) { | |
return found->second[0]; | |
} | |
} | |
return ""; | |
} | |
std::vector<std::string> keys() { | |
std::vector<std::string> keysVector(paramMap.size()); | |
for (std::map< std::string, std::vector<std::string> >::iterator it = paramMap.begin(); it != paramMap.end(); it++) { | |
keysVector.push_back(it->first); | |
} | |
return keysVector; | |
} | |
std::vector<std::string> getAll(const std::string &name) { | |
std::map< std::string, std::vector<std::string> >::iterator found = paramMap.find(name); | |
if (found != paramMap.end()) { | |
return found->second; | |
} | |
return std::vector<std::string>(); | |
} | |
HttpParams append(const std::string &name, const std::vector<std::string> &values) { | |
HttpParams params(*this); | |
if (has(name)) { | |
for (std::vector<std::string>::const_iterator it = values.begin(); it != values.end(); it++) { | |
params.paramMap[name].push_back(*it); | |
} | |
} else { | |
params.paramMap[name] = values; | |
} | |
return params; | |
} | |
HttpParams append(const std::string &name, const std::string &value) { | |
std::vector<std::string> values; | |
values.push_back(value); | |
return append(name, values); | |
} | |
HttpParams set(const std::string &name, const std::vector<std::string> &values) { | |
HttpParams params(*this); | |
params.paramMap[name] = values; | |
return params; | |
} | |
HttpParams set(const std::string &name, const std::string &value) { | |
std::vector<std::string> values; | |
values.push_back(value); | |
return set(name, values); | |
} | |
HttpParams remove(const std::string &name, const std::vector<std::string> &values) { | |
HttpParams params(*this); | |
std::vector<std::string> originalValues = params.getAll(name); | |
for (std::vector<std::string>::const_iterator it = values.begin(); it != values.end(); it++) { | |
originalValues.erase(std::find(originalValues.begin(), originalValues.end(), *it)); | |
} | |
params.set(name, originalValues); | |
return params; | |
} | |
HttpParams remove(const std::string &name, const std::string &value) { | |
std::vector<std::string> values; | |
values.push_back(value); | |
return remove(name, values); | |
} | |
HttpParams remove(const std::string &name) { | |
HttpParams params(*this); | |
params.paramMap.erase(name); | |
return params; | |
} | |
std::string toString() const { | |
std::ostringstream os; | |
for (std::map< std::string, std::vector<std::string> >::const_iterator it = paramMap.begin(); it != paramMap.end(); it++) { | |
const std::string &key = it->first; | |
const std::vector<std::string> &values = it->second; | |
for (std::vector<std::string>::const_iterator vit = values.begin(); vit != values.end(); vit++) { | |
const std::string &value = *vit; | |
char *escaped = curl_easy_escape(curl, value.c_str(), value.size()); | |
os << "&" << key << "=" << escaped; | |
} | |
} | |
std::string paramString = os.str(); | |
paramString[0] = '?'; | |
return paramString; | |
} | |
}; | |
class HttpRequest { | |
private: | |
std::string method; | |
std::string url; | |
HttpHeaders headers; | |
HttpParams params; | |
std::string body; | |
public: | |
HttpRequest(const std::string &method, const std::string &url, const HttpHeaders &headers, const HttpParams ¶ms): | |
method(method), url(url), headers(headers), params(params) { | |
} | |
const char* getBody() const { | |
return body.c_str(); | |
} | |
void setBody(const char *body) { | |
this->body = std::string(body); | |
} | |
HttpHeaders getHeaders() const { | |
return headers; | |
} | |
void setHeaders(HttpHeaders headers) { | |
this->headers = headers; | |
} | |
std::string getMethod() const { | |
return method; | |
} | |
void setMethod(std::string method) { | |
this->method = method; | |
} | |
HttpParams getParams() const { | |
return params; | |
} | |
void setParams(HttpParams params) { | |
this->params = params; | |
} | |
std::string getUrl() const { | |
return url; | |
} | |
void setUrl(std::string url) { | |
this->url = url; | |
} | |
std::string getUrlWithParams() const { | |
std::string urlWithoutParams = url.substr(0, url.find("?")); | |
std::string urlString = urlWithoutParams + params.toString(); | |
return urlString; | |
} | |
}; | |
class HttpResponse { | |
private: | |
std::string body; | |
HttpHeaders headers; | |
int status; | |
std::string statusText; | |
std::string url; | |
public: | |
HttpResponse(const char *body, const HttpHeaders &headers, int status, const std::string &statusText, const std::string &url): | |
body(std::string(body)), headers(headers), status(status), statusText(statusText), url(url) { | |
} | |
HttpResponse(const std::string &body, const HttpHeaders &headers, int status, const std::string &statusText, const std::string &url): | |
body(body), headers(headers), status(status), statusText(statusText), url(url) { | |
} | |
const char* getBody() { | |
return body.c_str(); | |
} | |
HttpHeaders getHeaders() { | |
return headers; | |
} | |
int getStatus() { | |
return status; | |
} | |
std::string getStatusText() { | |
return statusText; | |
} | |
std::string getUrl() { | |
return url; | |
} | |
}; | |
class MemoryStruct { | |
public: | |
char *memory; | |
size_t size; | |
public: | |
MemoryStruct() { | |
memory = (char*) malloc(0); | |
size = 0; | |
} | |
~MemoryStruct() { | |
free(memory); | |
} | |
}; | |
static size_t writeCallback(char *contents, size_t size, size_t nmemb, void *userdata) { | |
size_t realsize = size * nmemb; | |
MemoryStruct *mem = (MemoryStruct *) userdata; | |
char *ptr = (char*) realloc(mem->memory, mem->size + realsize + 1); | |
if (ptr == NULL) { | |
return 0; | |
} | |
mem->memory = ptr; | |
memcpy(&(mem->memory[mem->size]), contents, realsize); | |
mem->size += realsize; | |
mem->memory[mem->size] = 0; | |
return realsize; | |
} | |
class HttpClient { | |
private: | |
CURL *curl; | |
public: | |
HttpClient() { | |
curl = curl_easy_init(); | |
} | |
~HttpClient() { | |
curl_easy_cleanup(curl); | |
} | |
rxcpp::observable<HttpResponse> request(const HttpRequest &request) { | |
return rxcpp::observable<>::create<HttpResponse>( | |
[this, request](rxcpp::subscriber<HttpResponse> s) { | |
MemoryStruct bodyChunk; | |
MemoryStruct headerChunk; | |
std::string url = request.getUrlWithParams(); | |
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &bodyChunk); | |
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, writeCallback); | |
curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*) &headerChunk); | |
CURLcode res = curl_easy_perform(curl); | |
if (res != CURLE_OK) { | |
// std::cout << curl_easy_strerror(res) << std::endl; | |
} else { | |
// std::cout << bodyChunk.memory << std::endl; | |
} | |
std::string body = std::string(bodyChunk.memory); | |
int status = 200; | |
HttpHeaders headers; | |
std::string statusText = "OK"; | |
{ | |
std::string header(headerChunk.memory); | |
std::stringstream ss(header); | |
std::string protocolStr; | |
std::string statusCodeStr; | |
std::string statusTextStr; | |
ss >> protocolStr >> statusCodeStr >> statusTextStr; | |
status = std::stoi(statusCodeStr); | |
statusText = statusTextStr; | |
std::string line; | |
std::getline(ss, line, '\n'); | |
while (std::getline(ss, line, '\n')) { | |
std::vector<std::string> keyval; | |
boost::split(keyval, line, boost::is_any_of(":")); | |
if (keyval.size() == 2) { | |
std::string key = keyval[0]; | |
std::string val = keyval[1]; | |
boost::trim(key); | |
boost::trim(val); | |
std::vector<std::string> vals; | |
boost::split(vals, val, boost::is_any_of(",")); | |
for (std::vector<std::string>::iterator it = vals.begin(); it != vals.end(); it++) { | |
boost::trim(*it); | |
} | |
headers = headers.append(key, vals); | |
} | |
} | |
} | |
HttpResponse response(body, headers, status, statusText, url); | |
s.on_next(response); | |
s.on_completed(); | |
return; | |
}); | |
} | |
}; | |
int main(int argc, char* argv[]) { | |
curl_global_init(CURL_GLOBAL_ALL); | |
HttpClient httpClient; | |
HttpHeaders headers; | |
HttpParams params; | |
HttpRequest request = HttpRequest("GET", "https://www.google.com", headers, params); | |
rxcpp::observable<HttpResponse> response = httpClient.request(request); | |
response.subscribe([](HttpResponse r) { | |
std::cout << r.getStatus() << std::endl; | |
}); | |
curl_global_cleanup(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment