Skip to content

Instantly share code, notes, and snippets.

@elbakramer
Last active February 20, 2019 09:36
Show Gist options
  • Save elbakramer/1b03be04eecdbf39ab5fd9366df8c7f4 to your computer and use it in GitHub Desktop.
Save elbakramer/1b03be04eecdbf39ab5fd9366df8c7f4 to your computer and use it in GitHub Desktop.
#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 &params):
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