Initial commit

This commit is contained in:
2020-02-10 20:33:22 +01:00
commit a175bf5af9
31 changed files with 2646 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
#include "HttpClient.h"
#include "HttpClientImpl.h"
namespace Http {
HttpClient::HttpClient() :
m_pHttpClientImpl(new HttpClientImpl())
{
}
HttpClient::~HttpClient() = default;
int HttpClient::GetUrlReturnCode(const std::string& url) const
{
return m_pHttpClientImpl->GetUrlReturnCode(url);
}
std::string HttpClient::GetUrlContents(const std::string& url) const
{
return m_pHttpClientImpl->GetUrlContents(url);
}
void HttpClient::GetUrlSilent(const std::string& url) const
{
m_pHttpClientImpl->GetUrlSilent(url);
}
std::string HttpClient::GetUrlRedirect(const std::string& url) const
{
return m_pHttpClientImpl->GetUrlRedirect(url);
}
std::string HttpClient::GetUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders) const
{
return m_pHttpClientImpl->GetUrlContents(url, httpHeaders);
}
void HttpClient::GetUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders) const
{
m_pHttpClientImpl->GetUrlSilent(url, httpHeaders);
}
std::string HttpClient::GetUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders) const
{
return m_pHttpClientImpl->GetUrlRedirect(url, httpHeaders);
}
std::string HttpClient::GetUrlContents(const std::string& url, const std::string& cookieFile) const
{
return m_pHttpClientImpl->GetUrlContents(url, cookieFile);
}
void HttpClient::GetUrlSilent(const std::string& url, const std::string& cookieFile) const
{
m_pHttpClientImpl->GetUrlSilent(url, cookieFile);
}
std::string HttpClient::GetUrlRedirect(const std::string& url, const std::string& cookieFile) const
{
return m_pHttpClientImpl->GetUrlRedirect(url, cookieFile);
}
std::string HttpClient::GetUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const
{
return m_pHttpClientImpl->GetUrlContents(url, httpHeaders, cookieFile);
}
void HttpClient::GetUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const
{
m_pHttpClientImpl->GetUrlSilent(url, httpHeaders, cookieFile);
}
std::string HttpClient::GetUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const
{
return m_pHttpClientImpl->GetUrlRedirect(url, httpHeaders, cookieFile);
}
std::string HttpClient::PutUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return m_pHttpClientImpl->PutUrlContents(url, httpHeaders, data);
}
void HttpClient::PutUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
m_pHttpClientImpl->PutUrlSilent(url, httpHeaders, data);
}
std::string HttpClient::PutUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return m_pHttpClientImpl->PutUrlRedirect(url, httpHeaders, data);
}
std::string HttpClient::PutUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return m_pHttpClientImpl->PutUrlContents(url, httpHeaders, cookieFile, data);
}
void HttpClient::PutUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
m_pHttpClientImpl->PutUrlSilent(url, httpHeaders, cookieFile, data);
}
std::string HttpClient::PutUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return m_pHttpClientImpl->PutUrlRedirect(url, httpHeaders, cookieFile, data);
}
std::string HttpClient::GetUrlPostContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return m_pHttpClientImpl->GetUrlPostContents(url, httpHeaders, data);
}
void HttpClient::GetUrlPostSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
m_pHttpClientImpl->GetUrlPostSilent(url, httpHeaders, data);
}
std::string HttpClient::GetUrlPostRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return m_pHttpClientImpl->GetUrlPostRedirect(url, httpHeaders, data);
}
std::string HttpClient::GetUrlPostContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return m_pHttpClientImpl->GetUrlPostContents(url, httpHeaders, cookieFile, data);
}
void HttpClient::GetUrlPostSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
m_pHttpClientImpl->GetUrlPostSilent(url, httpHeaders, cookieFile, data);
}
std::string HttpClient::GetUrlPostRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return m_pHttpClientImpl->GetUrlPostRedirect(url, httpHeaders, cookieFile, data);
}
std::string HttpClient::GetUrlPostAttachmentContents(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
return m_pHttpClientImpl->GetUrlPostAttachmentContents(url, data, filename, fileFieldname);
}
void HttpClient::GetUrlPostAttachmentSilent(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
m_pHttpClientImpl->GetUrlPostAttachmentSilent(url, data, filename, fileFieldname);
}
std::string HttpClient::GetUrlPostAttachmentRedirect(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
return m_pHttpClientImpl->GetUrlPostAttachmentRedirect(url, data, filename, fileFieldname);
}
std::string HttpClient::GetUrlPostAttachmentContents(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
return m_pHttpClientImpl->GetUrlPostAttachmentContents(url, cookieFile, data, filename, fileFieldname);
}
void HttpClient::GetUrlPostAttachmentSilent(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
m_pHttpClientImpl->GetUrlPostAttachmentSilent(url, cookieFile, data, filename, fileFieldname);
}
std::string HttpClient::GetUrlPostAttachmentRedirect(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
return m_pHttpClientImpl->GetUrlPostAttachmentRedirect(url, cookieFile, data, filename, fileFieldname);
}
} // namespace Http
+45
View File
@@ -0,0 +1,45 @@
#ifndef HTTP_HTTPCLIENTHELPERS_H
#define HTTP_HTTPCLIENTHELPERS_H
#include <openssl/crypto.h>
#include <memory>
#include <mutex>
#include <thread>
namespace Http {
static std::vector<std::shared_ptr<std::mutex> > g_mutexes;
static void LockCallback(int mode, int type, const char* /*file*/, int /*line*/)
{
if (mode & CRYPTO_LOCK)
g_mutexes[type]->lock();
else
g_mutexes[type]->unlock();
}
static unsigned long ThreadId(void)
{
return static_cast<unsigned long>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
}
static void InitializeLocks(void)
{
g_mutexes.resize(CRYPTO_num_locks());
for (int i = 0; i < CRYPTO_num_locks(); ++i)
g_mutexes[i] = std::shared_ptr<std::mutex>(new std::mutex());
CRYPTO_set_locking_callback(LockCallback);
CRYPTO_set_id_callback(ThreadId);
}
static void FreeLocks(void)
{
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_id_callback(NULL);
}
} // namespace Http
#endif // HTTP_HTTPCLIENTHELPERS_H
+792
View File
@@ -0,0 +1,792 @@
#include "HttpClientImpl.h"
#include "HttpClientHelpers.h"
#include "Logging.h"
#include "StringAlgorithm.h"
#include <curl/curl.h>
#include <openssl/crypto.h>
#include <fstream>
#include <sstream>
namespace Http {
HttpClientImpl::HttpClientImpl()
{
curl_global_init(CURL_GLOBAL_ALL);
m_userAgents.push_back("Opera/9.80 (X11; Linux i686; U; en) Presto/2.7.62 Version/11.00");
InitializeLocks();
}
HttpClientImpl::~HttpClientImpl()
{
FreeLocks();
curl_global_cleanup();
}
int HttpClientImpl::GetUrlReturnCode(const std::string& url) const
{
return std::stoi(PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::GET, url, std::vector<std::string>(), std::string(), std::string(), true));
}
std::string HttpClientImpl::GetUrlContents(const std::string& url) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::GET, url);
}
void HttpClientImpl::GetUrlSilent(const std::string& url) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::GET, url);
}
std::string HttpClientImpl::GetUrlRedirect(const std::string& url) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::GET, url);
}
std::string HttpClientImpl::GetUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::GET, url, httpHeaders);
}
void HttpClientImpl::GetUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::GET, url, httpHeaders);
}
std::string HttpClientImpl::GetUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::GET, url, httpHeaders);
}
std::string HttpClientImpl::GetUrlContents(const std::string& url, const std::string& cookieFile) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::GET, url, std::vector<std::string>(), cookieFile);
}
void HttpClientImpl::GetUrlSilent(const std::string& url, const std::string& cookieFile) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::GET, url, std::vector<std::string>(), cookieFile);
}
std::string HttpClientImpl::GetUrlRedirect(const std::string& url, const std::string& cookieFile) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::GET, url, std::vector<std::string>(), cookieFile);
}
std::string HttpClientImpl::GetUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::GET, url, httpHeaders, cookieFile);
}
void HttpClientImpl::GetUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::GET, url, httpHeaders, cookieFile);
}
std::string HttpClientImpl::GetUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::GET, url, httpHeaders, cookieFile);
}
std::string HttpClientImpl::PutUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::PUT, url, httpHeaders, std::string(), data);
}
void HttpClientImpl::PutUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::PUT, url, httpHeaders, std::string(), data);
}
std::string HttpClientImpl::PutUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::PUT, url, httpHeaders, std::string(), data);
}
std::string HttpClientImpl::PutUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::PUT, url, httpHeaders, cookieFile, data);
}
void HttpClientImpl::PutUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::PUT, url, httpHeaders, cookieFile, data);
}
std::string HttpClientImpl::PutUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::PUT, url, httpHeaders, cookieFile, data);
}
std::string HttpClientImpl::GetUrlPostContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::POST, url, httpHeaders, std::string(), data);
}
void HttpClientImpl::GetUrlPostSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::POST, url, httpHeaders, std::string(), data);
}
std::string HttpClientImpl::GetUrlPostRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::POST, url, httpHeaders, std::string(), data);
}
std::string HttpClientImpl::GetUrlPostContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetContent, HttpClientImpl::Method::POST, url, httpHeaders, cookieFile, data);
}
void HttpClientImpl::GetUrlPostSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
PerformOperation(HttpClientImpl::Operation::Silent, HttpClientImpl::Method::POST, url, httpHeaders, cookieFile, data);
}
std::string HttpClientImpl::GetUrlPostRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const
{
return PerformOperation(HttpClientImpl::Operation::GetRedirect, HttpClientImpl::Method::POST, url, httpHeaders, cookieFile, data);
}
std::string HttpClientImpl::GetUrlPostAttachmentContents(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
std::string buffer;
std::string contents;
std::ifstream fileStream(filename, std::ios::in | std::ios::binary);
if (fileStream)
{
fileStream.seekg(0, std::ios::end);
contents.resize(fileStream.tellg());
fileStream.seekg(0, std::ios::beg);
fileStream.read(&contents[0], contents.size());
fileStream.close();
}
CURL* curl = curl_easy_init();
CURLcode result;
struct curl_httppost *formpost = nullptr;
struct curl_httppost *lastptr = nullptr;
struct curl_slist *headerlist = nullptr;
static const char headerBuffer[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "cache-control:",
CURLFORM_COPYCONTENTS, "no-cache",
CURLFORM_END);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "content-type:",
CURLFORM_COPYCONTENTS, "multipart/form-data",
CURLFORM_END);
std::vector<std::string> postTokens = Utilities::split(data, '&');
for (std::vector<std::string>::iterator it = postTokens.begin(); it != postTokens.end(); ++it)
{
std::vector<std::string> tokens = Utilities::split(*it, '=');
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, tokens[0].c_str(),
CURLFORM_COPYCONTENTS, tokens[1].c_str(),
CURLFORM_END);
}
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, fileFieldname.c_str(),
CURLFORM_BUFFER, "data",
CURLFORM_BUFFERPTR, contents.data(),
CURLFORM_BUFFERLENGTH, contents.size(),
CURLFORM_END);
headerlist = curl_slist_append(headerlist, headerBuffer);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
int code = 0;
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
if (code != 200)
{
std::stringstream error;
error << "Error (" << code << ") encountered while retrieving " << url << "\n";
error << "Data: " << buffer;
throw std::runtime_error(error.str());
}
return buffer;
}
void HttpClientImpl::GetUrlPostAttachmentSilent(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
std::string contents;
std::ifstream fileStream(filename, std::ios::in | std::ios::binary);
if (fileStream)
{
fileStream.seekg(0, std::ios::end);
contents.resize(fileStream.tellg());
fileStream.seekg(0, std::ios::beg);
fileStream.read(&contents[0], contents.size());
fileStream.close();
}
CURL* curl = curl_easy_init();
CURLcode result;
struct curl_httppost *formpost = nullptr;
struct curl_httppost *lastptr = nullptr;
struct curl_slist *headerlist = nullptr;
static const char headerBuffer[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "cache-control:",
CURLFORM_COPYCONTENTS, "no-cache",
CURLFORM_END);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "content-type:",
CURLFORM_COPYCONTENTS, "multipart/form-data",
CURLFORM_END);
std::vector<std::string> postTokens = Utilities::split(data, '&');
for (std::vector<std::string>::iterator it = postTokens.begin(); it != postTokens.end(); ++it)
{
std::vector<std::string> tokens = Utilities::split(*it, '=');
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, tokens[0].c_str(),
CURLFORM_COPYCONTENTS, tokens[1].c_str(),
CURLFORM_END);
}
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, fileFieldname.c_str(),
CURLFORM_BUFFER, "data",
CURLFORM_BUFFERPTR, contents.data(),
CURLFORM_BUFFERLENGTH, contents.size(),
CURLFORM_END);
headerlist = curl_slist_append(headerlist, headerBuffer);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
int code = 0;
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
if (code != 200)
{
std::stringstream error;
error << "Error (" << code << ") encountered while retrieving " << url << "\n";
throw std::runtime_error(error.str());
}
}
std::string HttpClientImpl::GetUrlPostAttachmentRedirect(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
std::string contents;
std::ifstream fileStream(filename, std::ios::in | std::ios::binary);
if (fileStream)
{
fileStream.seekg(0, std::ios::end);
contents.resize(fileStream.tellg());
fileStream.seekg(0, std::ios::beg);
fileStream.read(&contents[0], contents.size());
fileStream.close();
}
CURL* curl = curl_easy_init();
CURLcode result;
struct curl_httppost *formpost = nullptr;
struct curl_httppost *lastptr = nullptr;
struct curl_slist *headerlist = nullptr;
static const char headerBuffer[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "cache-control:",
CURLFORM_COPYCONTENTS, "no-cache",
CURLFORM_END);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "content-type:",
CURLFORM_COPYCONTENTS, "multipart/form-data",
CURLFORM_END);
std::vector<std::string> postTokens = Utilities::split(data, '&');
for (std::vector<std::string>::iterator it = postTokens.begin(); it != postTokens.end(); ++it)
{
std::vector<std::string> tokens = Utilities::split(*it, '=');
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, tokens[0].c_str(),
CURLFORM_COPYCONTENTS, tokens[1].c_str(),
CURLFORM_END);
}
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, fileFieldname.c_str(),
CURLFORM_BUFFER, "data",
CURLFORM_BUFFERPTR, contents.data(),
CURLFORM_BUFFERLENGTH, contents.size(),
CURLFORM_END);
headerlist = curl_slist_append(headerlist, headerBuffer);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
int code = 0;
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if (code < 300 || code >= 400)
{
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
if (code != 200)
{
std::stringstream error;
error << "Error (" << code << ") encountered while retrieving " << url << "\n";
throw std::runtime_error(error.str());
}
return std::string();
}
char* pRedirectUrl;
curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &pRedirectUrl);
std::string redirectUrl(pRedirectUrl);
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
return redirectUrl;
}
std::string HttpClientImpl::GetUrlPostAttachmentContents(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
std::string buffer;
std::string contents;
std::ifstream fileStream(filename, std::ios::in | std::ios::binary);
if (fileStream)
{
fileStream.seekg(0, std::ios::end);
contents.resize(fileStream.tellg());
fileStream.seekg(0, std::ios::beg);
fileStream.read(&contents[0], contents.size());
fileStream.close();
}
CURL* curl = curl_easy_init();
CURLcode result;
struct curl_httppost *formpost = nullptr;
struct curl_httppost *lastptr = nullptr;
struct curl_slist *headerlist = nullptr;
static const char headerBuffer[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "cache-control:",
CURLFORM_COPYCONTENTS, "no-cache",
CURLFORM_END);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "content-type:",
CURLFORM_COPYCONTENTS, "multipart/form-data",
CURLFORM_END);
std::vector<std::string> postTokens = Utilities::split(data, '&');
for (std::vector<std::string>::iterator it = postTokens.begin(); it != postTokens.end(); ++it)
{
std::vector<std::string> tokens = Utilities::split(*it, '=');
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, tokens[0].c_str(),
CURLFORM_COPYCONTENTS, tokens[1].c_str(),
CURLFORM_END);
}
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, fileFieldname.c_str(),
CURLFORM_BUFFER, "data",
CURLFORM_BUFFERPTR, contents.data(),
CURLFORM_BUFFERLENGTH, contents.size(),
CURLFORM_END);
headerlist = curl_slist_append(headerlist, headerBuffer);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookieFile.c_str());
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookieFile.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
int code = 0;
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
if (code != 200)
{
std::stringstream error;
error << "Error (" << code << ") encountered while retrieving " << url << "\n";
error << "Data: " << buffer;
throw std::runtime_error(error.str());
}
return buffer;
}
void HttpClientImpl::GetUrlPostAttachmentSilent(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
std::string contents;
std::ifstream fileStream(filename, std::ios::in | std::ios::binary);
if (fileStream)
{
fileStream.seekg(0, std::ios::end);
contents.resize(fileStream.tellg());
fileStream.seekg(0, std::ios::beg);
fileStream.read(&contents[0], contents.size());
fileStream.close();
}
CURL* curl = curl_easy_init();
CURLcode result;
struct curl_httppost *formpost = nullptr;
struct curl_httppost *lastptr = nullptr;
struct curl_slist *headerlist = nullptr;
static const char headerBuffer[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "cache-control:",
CURLFORM_COPYCONTENTS, "no-cache",
CURLFORM_END);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "content-type:",
CURLFORM_COPYCONTENTS, "multipart/form-data",
CURLFORM_END);
std::vector<std::string> postTokens = Utilities::split(data, '&');
for (std::vector<std::string>::iterator it = postTokens.begin(); it != postTokens.end(); ++it)
{
std::vector<std::string> tokens = Utilities::split(*it, '=');
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, tokens[0].c_str(),
CURLFORM_COPYCONTENTS, tokens[1].c_str(),
CURLFORM_END);
}
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, fileFieldname.c_str(),
CURLFORM_BUFFER, "data",
CURLFORM_BUFFERPTR, contents.data(),
CURLFORM_BUFFERLENGTH, contents.size(),
CURLFORM_END);
headerlist = curl_slist_append(headerlist, headerBuffer);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookieFile.c_str());
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookieFile.c_str());
int code = 0;
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
if (code != 200)
{
std::stringstream error;
error << "Error (" << code << ") encountered while retrieving " << url << "\n";
throw std::runtime_error(error.str());
}
}
std::string HttpClientImpl::GetUrlPostAttachmentRedirect(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const
{
std::string contents;
std::ifstream fileStream(filename, std::ios::in | std::ios::binary);
if (fileStream)
{
fileStream.seekg(0, std::ios::end);
contents.resize(fileStream.tellg());
fileStream.seekg(0, std::ios::beg);
fileStream.read(&contents[0], contents.size());
fileStream.close();
}
CURL* curl = curl_easy_init();
CURLcode result;
struct curl_httppost *formpost = nullptr;
struct curl_httppost *lastptr = nullptr;
struct curl_slist *headerlist = nullptr;
static const char headerBuffer[] = "Expect:";
curl_global_init(CURL_GLOBAL_ALL);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "cache-control:",
CURLFORM_COPYCONTENTS, "no-cache",
CURLFORM_END);
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, "content-type:",
CURLFORM_COPYCONTENTS, "multipart/form-data",
CURLFORM_END);
std::vector<std::string> postTokens = Utilities::split(data, '&');
for (std::vector<std::string>::iterator it = postTokens.begin(); it != postTokens.end(); ++it)
{
std::vector<std::string> tokens = Utilities::split(*it, '=');
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, tokens[0].c_str(),
CURLFORM_COPYCONTENTS, tokens[1].c_str(),
CURLFORM_END);
}
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, fileFieldname.c_str(),
CURLFORM_BUFFER, "data",
CURLFORM_BUFFERPTR, contents.data(),
CURLFORM_BUFFERLENGTH, contents.size(),
CURLFORM_END);
headerlist = curl_slist_append(headerlist, headerBuffer);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookieFile.c_str());
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookieFile.c_str());
int code = 0;
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if (code < 300 || code >= 400)
{
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
if (code != 200)
{
std::stringstream error;
error << "Error (" << code << ") encountered while retrieving " << url << "\n";
throw std::runtime_error(error.str());
}
return std::string();
}
char* pRedirectUrl;
curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &pRedirectUrl);
std::string redirectUrl(pRedirectUrl);
curl_easy_cleanup(curl);
curl_formfree(formpost);
curl_slist_free_all(headerlist);
return redirectUrl;
}
std::string HttpClientImpl::PerformOperation(HttpClientImpl::Operation::type type, HttpClientImpl::Method::type method, const std::string& url, const std::vector<std::string> httpHeaders, const std::string& cookieFile, const std::string& data, bool returnReturnCode) const
{
std::string buffer;
std::string error;
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_USERAGENT, m_userAgents[rand() % m_userAgents.size()].c_str());
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
if (type == HttpClientImpl::Operation::Silent)
{
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, nullptr);
}
else if (type == HttpClientImpl::Operation::GetContent)
{
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
}
else if (type == HttpClientImpl::Operation::GetRedirect)
{
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, nullptr);
}
//if (method == HttpClientImpl::Method::GET)
if (method == HttpClientImpl::Method::PUT)
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
else if (method == HttpClientImpl::Method::POST)
curl_easy_setopt(curl, CURLOPT_POST, 1);
if (method != HttpClientImpl::Method::GET && !data.empty())
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
if (!httpHeaders.empty())
{
struct curl_slist *list = nullptr;
for (auto it = httpHeaders.begin(); it != httpHeaders.end(); ++it)
list = curl_slist_append(list, it->c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
}
if (!cookieFile.empty())
{
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookieFile.c_str());
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookieFile.c_str());
}
int code = 0;
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if (!returnReturnCode)
{
if (code < 100)
{
// Non-Existent
std::stringstream err;
err << "Error (" << code << ") encountered while retrieving " << url << "\n";
error = err.str();
}
else if (code < 200)
{
// Informational
std::stringstream info;
info << "Informational (" << code << ") encountered while retrieving " << url << "\n";
Logging::Log(Logging::Severity::Info, info.str());
}
else if (code < 300)
{
// Success
if (code == 202)
buffer = PerformOperation(type, method, url, httpHeaders, cookieFile, data);
}
else if (code < 400)
{
// Redirection
if (type == HttpClientImpl::Operation::GetRedirect)
{
char* pRedirectUrl;
curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &pRedirectUrl);
buffer = std::string(pRedirectUrl);
}
else
{
std::stringstream redirect;
redirect << "Redirect (" << code << ") encountered while retrieving " << url << "\n";
Logging::Log(Logging::Severity::Info, redirect.str());
}
}
else if (code < 500)
{
// Client Error
std::stringstream err;
err << "Client Error (" << code << ") encountered while retrieving " << url << "\n";
err << "Response: " << buffer << "\n";
/*
if (code == 429)
{
// Too Many Requests
curl_off_t wait = 0;
curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &wait);
err << "Retry after " << wait << " seconds\n";
}
*/
error = err.str();
}
else if (code < 600)
{
// Server Error
std::stringstream err;
err << "Server Error (" << code << ") encountered while retrieving " << url << "\n";
err << "Response: " << buffer << "\n";
error = err.str();
}
else
{
// Non-Existent
std::stringstream err;
err << "Error (" << code << ") encountered while retrieving " << url << "\n";
err << "Response: " << buffer << "\n";
error = err.str();
}
}
else
{
buffer = std::to_string(code);
}
curl_easy_cleanup(curl);
if (!error.empty())
throw std::runtime_error(error);
return buffer;
}
size_t HttpClientImpl::WriteCallback(char* data, size_t size, size_t nmemb, std::string* writerData)
{
if (writerData == nullptr)
return size * nmemb;
writerData->append(data, size * nmemb);
return size * nmemb;
}
} // namespace Http
+94
View File
@@ -0,0 +1,94 @@
#ifndef HTTP_HTTPCLIENTIMPL_H
#define HTTP_HTTPCLIENTIMPL_H
#include <string>
#include <vector>
typedef void CURL;
namespace Http {
class HttpClientImpl
{
public:
HttpClientImpl();
~HttpClientImpl();
HttpClientImpl(const HttpClientImpl&) = delete;
int GetUrlReturnCode(const std::string& url) const;
std::string GetUrlContents(const std::string& url) const;
void GetUrlSilent(const std::string& url) const;
std::string GetUrlRedirect(const std::string& url) const;
std::string GetUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders) const;
void GetUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders) const;
std::string GetUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders) const;
std::string GetUrlContents(const std::string& url, const std::string& cookieFile) const;
void GetUrlSilent(const std::string& url, const std::string& cookieFile) const;
std::string GetUrlRedirect(const std::string& url, const std::string& cookieFile) const;
std::string GetUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const;
void GetUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const;
std::string GetUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile) const;
std::string PutUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const;
void PutUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const;
std::string PutUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const;
std::string PutUrlContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const;
void PutUrlSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const;
std::string PutUrlRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const;
std::string GetUrlPostContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const;
void GetUrlPostSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const;
std::string GetUrlPostRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& data) const;
std::string GetUrlPostContents(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const;
void GetUrlPostSilent(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const;
std::string GetUrlPostRedirect(const std::string& url, const std::vector<std::string>& httpHeaders, const std::string& cookieFile, const std::string& data) const;
std::string GetUrlPostAttachmentContents(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const;
void GetUrlPostAttachmentSilent(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const;
std::string GetUrlPostAttachmentRedirect(const std::string& url, const std::string& data, const std::string& filename, const std::string& fileFieldname) const;
std::string GetUrlPostAttachmentContents(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const;
void GetUrlPostAttachmentSilent(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const;
std::string GetUrlPostAttachmentRedirect(const std::string& url, const std::string& cookieFile, const std::string& data, const std::string& filename, const std::string& fileFieldname) const;
private:
struct Method
{
enum type
{
GET,
PUT,
POST
};
};
struct Operation
{
enum type
{
Silent,
GetContent,
GetRedirect
};
};
private:
std::string PerformOperation(HttpClientImpl::Operation::type type, HttpClientImpl::Method::type method, const std::string& url, const std::vector<std::string> httpHeaders = std::vector<std::string>(), const std::string& cookieFile = std::string(), const std::string& data = std::string(), bool returnCode = false) const;
void ThrowException(int code);
static size_t WriteCallback(char* data, size_t size, size_t nmemb, std::string* writerData);
private:
std::vector<std::string> m_userAgents;
};
} // namespace Http
#endif // HTTP_HTTPCLIENTIMPL_H
+92
View File
@@ -0,0 +1,92 @@
#include "HttpConnection.h"
#include <functional>
namespace Http {
HttpConnection::HttpConnection(asio::io_service& ioService, HttpRequestHandler& handler) :
m_strand(ioService),
m_socket(ioService),
m_requestHandler(handler),
m_dataState(false, 0),
m_dataIncomplete(false)
{
}
HttpConnection::~HttpConnection()
{
}
asio::ip::tcp::socket& HttpConnection::Socket()
{
return m_socket;
}
void HttpConnection::Start()
{
m_socket.async_read_some(asio::buffer(m_data), m_strand.wrap(std::bind(&HttpConnection::HandleRead, shared_from_this(), std::placeholders::_1, std::placeholders::_2)));
}
void HttpConnection::HandleRead(const asio::error_code& ec, std::size_t length)
{
if (!ec)
{
size_t dataLocation;
Utilities::Tribool result;
Buffer::iterator dataIterator;
if (!m_dataIncomplete)
{
m_dataState = m_requestParser.Parse(m_request, m_data.data(), m_data.data() + length);
dataLocation = std::get<1>(m_dataState) - m_data.data();
}
else
{
std::get<1>(m_dataState) = m_data.data();
dataLocation = 0;
}
std::tie(result, dataIterator) = m_dataState;
if (result && length < m_request.headers.GetContentLength() + dataLocation)
{
m_dataIncomplete = true;
result = Utilities::Tribool::Indeterminate;
}
else
{
m_dataIncomplete = false;
}
if (result)
{
std::string data;
while (dataIterator != m_data.data() + length)
data.push_back(*dataIterator++);
HttpReply reply = m_requestHandler.HandleRequest(m_request, data);
asio::async_write(m_socket, reply.ToBuffers(), m_strand.wrap(std::bind(&HttpConnection::HandleWrite, shared_from_this(), std::placeholders::_1)));
}
else if (!result)
{
HttpReply reply = HttpReply::StockReply(HttpReply::Status::BadRequest);
asio::async_write(m_socket, reply.ToBuffers(), m_strand.wrap(std::bind(&HttpConnection::HandleWrite, shared_from_this(), std::placeholders::_1)));
}
else
{
m_socket.async_read_some(asio::buffer(m_data), m_strand.wrap(std::bind(&HttpConnection::HandleRead, shared_from_this(), std::placeholders::_1, std::placeholders::_2)));
}
}
}
void HttpConnection::HandleWrite(const asio::error_code& ec)
{
if (!ec)
{
asio::error_code ignored_ec;
m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
}
}
} // namespace Http
+49
View File
@@ -0,0 +1,49 @@
#ifndef HTTP_HTTPCONNECTION_H
#define HTTP_HTTPCONNECTION_H
#include "HttpReply.h"
#include "HttpRequest.h"
#include "HttpRequestHandler.h"
#include "HttpRequestParser.h"
#include "asio.hpp"
#include "Tribool.h"
#include <array>
#include <memory>
#include <string>
#include <tuple>
namespace Http {
class HttpConnection :
public std::enable_shared_from_this<HttpConnection>
{
public:
HttpConnection(asio::io_service& ioService, HttpRequestHandler& handler);
~HttpConnection();
HttpConnection(const HttpConnection&) = delete;
asio::ip::tcp::socket& Socket();
void Start();
private:
void HandleRead(const asio::error_code& ec, std::size_t length);
void HandleWrite(const asio::error_code& ec);
typedef std::array<char, 8192> Buffer;
private:
asio::io_service::strand m_strand;
asio::ip::tcp::socket m_socket;
HttpRequestHandler& m_requestHandler;
Buffer m_data;
std::tuple<Utilities::Tribool, Buffer::iterator> m_dataState;
bool m_dataIncomplete;
HttpRequest m_request;
HttpRequestParser m_requestParser;
};
} // namespace Http
#endif // HTTP_HTTPCONNECTION_H
+23
View File
@@ -0,0 +1,23 @@
#ifndef HTTP_HTTPHEADER_H
#define HTTP_HTTPHEADER_H
#include <string>
namespace Http {
struct HttpHeader
{
public:
bool operator==(const std::string& other) const
{
return name == other;
}
std::string name;
std::string value;
};
} // namespace Http
#endif // HTTP_HTTPHEADER_H
+127
View File
@@ -0,0 +1,127 @@
#include "HttpHeaders.h"
#include <algorithm>
namespace Http {
HttpHeaders::HttpHeaders()
{
}
HttpHeaders::~HttpHeaders()
{
}
HttpHeaders::iterator HttpHeaders::begin()
{
return m_headers.begin();
}
HttpHeaders::const_iterator HttpHeaders::begin() const
{
return m_headers.begin();
}
HttpHeaders::iterator HttpHeaders::end()
{
return m_headers.end();
}
HttpHeaders::const_iterator HttpHeaders::end() const
{
return m_headers.end();
}
HttpHeaders::reference HttpHeaders::front()
{
return m_headers.front();
}
HttpHeaders::const_reference HttpHeaders::front() const
{
return m_headers.front();
}
HttpHeaders::reference HttpHeaders::back()
{
return m_headers.back();
}
HttpHeaders::const_reference HttpHeaders::back() const
{
return m_headers.back();
}
HttpHeaders::reference HttpHeaders::at(size_t n)
{
return m_headers.at(n);
}
HttpHeaders::const_reference HttpHeaders::at(size_t n) const
{
return m_headers.at(n);
}
HttpHeaders::reference HttpHeaders::operator[](size_t n)
{
return m_headers[n];
}
HttpHeaders::const_reference HttpHeaders::operator[](size_t n) const
{
return m_headers[n];
}
size_t HttpHeaders::size() const
{
return m_headers.size();
}
bool HttpHeaders::empty() const
{
return m_headers.empty();
}
void HttpHeaders::resize(size_t n, HttpHeader h)
{
m_headers.resize(n, h);
}
void HttpHeaders::push_back(const_reference h)
{
m_headers.push_back(h);
}
void HttpHeaders::pop_back()
{
m_headers.pop_back();
}
int HttpHeaders::GetContentLength() const
{
std::vector<HttpHeader>::const_iterator iterator = std::find(begin(), end(), "Content-Length");
if (iterator != end())
return std::stoi(iterator->value);
return 0;
}
std::string HttpHeaders::GetBoundary() const
{
std::vector<HttpHeader>::const_iterator iterator = std::find(begin(), end(), "Content-Type");
if (iterator != end())
{
std::string boundaryHeader = "boundary";
std::string value = iterator->value;
size_t location = value.find(boundaryHeader);
if (location != std::string::npos)
{
location = value.find("=", location) + 1;
return value.substr(location);
}
}
return std::string();
}
} // namespace Http
+55
View File
@@ -0,0 +1,55 @@
#ifndef HTTP_HTTPHEADERS_H
#define HTTP_HTTPHEADERS_H
#include "HttpHeader.h"
#include <string>
#include <vector>
namespace Http {
class HttpHeaders
{
public:
HttpHeaders();
~HttpHeaders();
public:
typedef std::vector<HttpHeader>::iterator iterator;
typedef std::vector<HttpHeader>::const_iterator const_iterator;
typedef HttpHeader& reference;
typedef const HttpHeader& const_reference;
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reference front();
const_reference front() const;
reference back();
const_reference back() const;
reference at(size_t n);
const_reference at(size_t n) const;
reference operator[](size_t n);
const_reference operator[](size_t n) const;
size_t size() const;
bool empty() const;
void resize(size_t n, HttpHeader h = HttpHeader());
void push_back(const_reference h);
void pop_back();
public:
int GetContentLength() const;
std::string GetBoundary() const;
private:
std::vector<HttpHeader> m_headers;
};
} // namespace Http
#endif // HTTP_HTTPHEADERS_H
+31
View File
@@ -0,0 +1,31 @@
#include "HttpMimeTypes.h"
namespace Http {
namespace MimeTypes {
struct Mapping
{
const char* extension;
const char* mime_type;
} mappings[] =
{
{ "gif", "image/gif" },
{ "htm", "text/html" },
{ "html", "text/html" },
{ "jpg", "image/jpeg" },
{ "png", "image/png" },
{ 0, 0 }
};
std::string ExtensionToType(const std::string& extension)
{
for (Mapping* m = mappings; m->extension; ++m)
if (m->extension == extension)
return m->mime_type;
return "text/plain";
}
} // namespace MimeTypes
} // namespace Http
+15
View File
@@ -0,0 +1,15 @@
#ifndef HTTP_HTTPMIMETYPES_H
#define HTTP_HTTPMIMETYPES_H
#include <string>
namespace Http {
namespace MimeTypes {
std::string ExtensionToType(const std::string& extension);
} // namespace MimeTypes
} // namespace Http
#endif // HTTP_HTTPMIMETYPES_H
+244
View File
@@ -0,0 +1,244 @@
#include "HttpReply.h"
namespace Http {
namespace StatusStrings {
const std::string Ok =
"HTTP/1.0 200 OK\r\n";
const std::string Created =
"HTTP/1.0 201 Created\r\n";
const std::string Accepted =
"HTTP/1.0 202 Accepted\r\n";
const std::string NoContent =
"HTTP/1.0 204 No Content\r\n";
const std::string MultipleChoices =
"HTTP/1.0 300 Multiple Choices\r\n";
const std::string MovedPermanently =
"HTTP/1.0 301 Moved Permanently\r\n";
const std::string MovedTemporarily =
"HTTP/1.0 302 Moved Temporarily\r\n";
const std::string NotModified =
"HTTP/1.0 304 Not Modified\r\n";
const std::string BadRequest =
"HTTP/1.0 400 Bad Request\r\n";
const std::string Unauthorized =
"HTTP/1.0 401 Unauthorized\r\n";
const std::string Forbidden =
"HTTP/1.0 403 Forbidden\r\n";
const std::string NotFound =
"HTTP/1.0 404 Not Found\r\n";
const std::string InternalServerError =
"HTTP/1.0 500 Internal Server Error\r\n";
const std::string NotImplemented =
"HTTP/1.0 501 Not Implemented\r\n";
const std::string BadGateway =
"HTTP/1.0 502 Bad Gateway\r\n";
const std::string ServiceUnavailable =
"HTTP/1.0 503 Service Unavailable\r\n";
asio::const_buffer ToBuffer(HttpReply::Status::type status)
{
switch (status)
{
case HttpReply::Status::Ok:
return asio::buffer(Ok);
case HttpReply::Status::Created:
return asio::buffer(Created);
case HttpReply::Status::Accepted:
return asio::buffer(Accepted);
case HttpReply::Status::NoContent:
return asio::buffer(NoContent);
case HttpReply::Status::MultipleChoices:
return asio::buffer(MultipleChoices);
case HttpReply::Status::MovedPermanently:
return asio::buffer(MovedPermanently);
case HttpReply::Status::MovedTemporarily:
return asio::buffer(MovedTemporarily);
case HttpReply::Status::NotModified:
return asio::buffer(NotModified);
case HttpReply::Status::BadRequest:
return asio::buffer(BadRequest);
case HttpReply::Status::Unauthorized:
return asio::buffer(Unauthorized);
case HttpReply::Status::Forbidden:
return asio::buffer(Forbidden);
case HttpReply::Status::NotFound:
return asio::buffer(NotFound);
case HttpReply::Status::InternalServerError:
return asio::buffer(InternalServerError);
case HttpReply::Status::NotImplemented:
return asio::buffer(NotImplemented);
case HttpReply::Status::BadGateway:
return asio::buffer(BadGateway);
case HttpReply::Status::ServiceUnavailable:
return asio::buffer(ServiceUnavailable);
default:
return asio::buffer(InternalServerError);
}
}
} // namespace StatusStrings
namespace miscellaneousStrings {
const char nameValueSeparator[] = { ':', ' ' };
const char crlf[] = { '\r', '\n' };
} // namespace miscellaneousStrings
std::vector<asio::const_buffer> HttpReply::ToBuffers()
{
std::vector<asio::const_buffer> buffers;
buffers.push_back(StatusStrings::ToBuffer(status));
for (std::size_t i = 0; i < headers.size(); ++i)
{
HttpHeader& h = headers[i];
buffers.push_back(asio::buffer(h.name));
buffers.push_back(asio::buffer(miscellaneousStrings::nameValueSeparator));
buffers.push_back(asio::buffer(h.value));
buffers.push_back(asio::buffer(miscellaneousStrings::crlf));
}
buffers.push_back(asio::buffer(miscellaneousStrings::crlf));
buffers.push_back(asio::buffer(content));
return buffers;
}
namespace StockReplies {
const char Ok[] = "";
const char Created[] =
"<html>"
"<head><title>Created</title></head>"
"<body><h1>201 Created</h1></body>"
"</html>";
const char Accepted[] =
"<html>"
"<head><title>Accepted</title></head>"
"<body><h1>202 Accepted</h1></body>"
"</html>";
const char NoContent[] =
"<html>"
"<head><title>No Content</title></head>"
"<body><h1>204 Content</h1></body>"
"</html>";
const char MultipleChoices[] =
"<html>"
"<head><title>Multiple Choices</title></head>"
"<body><h1>300 Multiple Choices</h1></body>"
"</html>";
const char MovedPermanently[] =
"<html>"
"<head><title>Moved Permanently</title></head>"
"<body><h1>301 Moved Permanently</h1></body>"
"</html>";
const char MovedTemporarily[] =
"<html>"
"<head><title>Moved Temporarily</title></head>"
"<body><h1>302 Moved Temporarily</h1></body>"
"</html>";
const char NotModified[] =
"<html>"
"<head><title>Not Modified</title></head>"
"<body><h1>304 Not Modified</h1></body>"
"</html>";
const char BadRequest[] =
"<html>"
"<head><title>Bad Request</title></head>"
"<body><h1>400 Bad Request</h1></body>"
"</html>";
const char Unauthorized[] =
"<html>"
"<head><title>Unauthorized</title></head>"
"<body><h1>401 Unauthorized</h1></body>"
"</html>";
const char Forbidden[] =
"<html>"
"<head><title>Forbidden</title></head>"
"<body><h1>403 Forbidden</h1></body>"
"</html>";
const char NotFound[] =
"<html>"
"<head><title>Not Found</title></head>"
"<body><h1>404 Not Found</h1></body>"
"</html>";
const char InternalServerError[] =
"<html>"
"<head><title>Internal Server Error</title></head>"
"<body><h1>500 Internal Server Error</h1></body>"
"</html>";
const char NotImplemented[] =
"<html>"
"<head><title>Not Implemented</title></head>"
"<body><h1>501 Not Implemented</h1></body>"
"</html>";
const char BadGateway[] =
"<html>"
"<head><title>Bad Gateway</title></head>"
"<body><h1>502 Bad Gateway</h1></body>"
"</html>";
const char ServiceUnavailable[] =
"<html>"
"<head><title>Service Unavailable</title></head>"
"<body><h1>503 Service Unavailable</h1></body>"
"</html>";
std::string ToString(HttpReply::Status::type status)
{
switch (status)
{
case HttpReply::Status::Ok:
return Ok;
case HttpReply::Status::Created:
return Created;
case HttpReply::Status::Accepted:
return Accepted;
case HttpReply::Status::NoContent:
return NoContent;
case HttpReply::Status::MultipleChoices:
return MultipleChoices;
case HttpReply::Status::MovedPermanently:
return MovedPermanently;
case HttpReply::Status::MovedTemporarily:
return MovedTemporarily;
case HttpReply::Status::NotModified:
return NotModified;
case HttpReply::Status::BadRequest:
return BadRequest;
case HttpReply::Status::Unauthorized:
return Unauthorized;
case HttpReply::Status::Forbidden:
return Forbidden;
case HttpReply::Status::NotFound:
return NotFound;
case HttpReply::Status::InternalServerError:
return InternalServerError;
case HttpReply::Status::NotImplemented:
return NotImplemented;
case HttpReply::Status::BadGateway:
return BadGateway;
case HttpReply::Status::ServiceUnavailable:
return ServiceUnavailable;
default:
return InternalServerError;
}
}
} // namespace StockReplies
HttpReply HttpReply::StockReply(HttpReply::Status::type status)
{
HttpReply reply;
reply.status = status;
reply.content = StockReplies::ToString(status);
reply.headers.resize(2);
reply.headers[0].name = "Content-Length";
reply.headers[0].value = std::to_string(reply.content.size());
reply.headers[1].name = "Content-Type";
reply.headers[1].value = "text/html";
return reply;
}
} // namespace Http
+46
View File
@@ -0,0 +1,46 @@
#ifndef HTTP_HTTPREPLY_H
#define HTTP_HTTPREPLY_H
#include "HttpHeaders.h"
#include "asio.hpp"
#include <string>
#include <vector>
namespace Http {
struct HttpReply
{
struct Status
{
enum type
{
Ok = 200,
Created = 201,
Accepted = 202,
NoContent = 204,
MultipleChoices = 300,
MovedPermanently = 301,
MovedTemporarily = 302,
NotModified = 304,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404,
InternalServerError = 500,
NotImplemented = 501,
BadGateway = 502,
ServiceUnavailable = 503
};
};
Status::type status;
HttpHeaders headers;
std::string content;
std::vector<asio::const_buffer> ToBuffers();
static HttpReply StockReply(Status::type status);
};
} // namespace Http
#endif // HTTP_HTTPREPLY_H
+21
View File
@@ -0,0 +1,21 @@
#ifndef HTTP_HTTPREQUEST_H
#define HTTP_HTTPREQUEST_H
#include "HttpHeaders.h"
#include <string>
namespace Http {
struct HttpRequest
{
std::string method;
std::string uri;
int httpVersionMajor;
int httpVersionMinor;
HttpHeaders headers;
};
} // namespace Http
#endif // HTTP_HTTPREQUEST_H
+138
View File
@@ -0,0 +1,138 @@
#include "HttpRequestHandler.h"
#include "HttpMimeTypes.h"
#include "StringAlgorithm.h"
#include <fstream>
#include <sstream>
namespace Http {
HttpRequestHandler::HttpRequestHandler(std::function<std::string(const std::string&, const std::vector<HttpPostData>&)> callback) :
m_callback(callback)
{
}
HttpReply HttpRequestHandler::HandleRequest(const HttpRequest& request, const std::string& data)
{
std::string requestPath;
if (!URLDecode(request.uri, requestPath))
return HttpReply::StockReply(HttpReply::Status::BadRequest);
if (requestPath.empty() || requestPath[0] != '/' || requestPath.find("..") != std::string::npos)
return HttpReply::StockReply(HttpReply::Status::BadRequest);
if (!Utilities::iequals(request.uri, "/api") && !Utilities::istarts_with(request.uri, "/api/"))
return HttpReply::StockReply(HttpReply::Status::NotFound);
std::vector<HttpPostData> postData = GetPostData(request.headers.GetBoundary(), data);
std::string answer = HandlePostData(request.uri.substr(4), postData);
HttpReply reply;
reply.status = HttpReply::Status::Ok;
reply.content = answer;
reply.headers.resize(3);
reply.headers[0].name = "Content-Length";
reply.headers[0].value = std::to_string(reply.content.size());
reply.headers[1].name = "Content-Type";
reply.headers[1].value = "application/json";
reply.headers[2].name = "Access-Control-Allow-Origin";
reply.headers[2].value = "*";
return reply;
}
std::vector<HttpPostData> HttpRequestHandler::GetPostData(const std::string& boundary, const std::string& data) const
{
size_t boundarySize = boundary.size();
size_t location = data.find(boundary);
size_t nextLocation, dataStart, dataSize;
if (boundarySize == 0)
return std::vector<HttpPostData>();
std::vector<HttpPostData> postDataCollection;
while (location != std::string::npos)
{
dataStart = location + boundarySize;
nextLocation = data.find(boundary, dataStart);
if (nextLocation == std::string::npos)
break;
dataSize = nextLocation - dataStart - 4;
std::string postItem = data.substr(dataStart, dataSize);
std::string fieldName = "";
size_t headerLocation = postItem.find("Content-Disposition");
size_t headerEnd = 0;
if (headerLocation != std::string::npos)
{
headerEnd = postItem.find('\n', postItem.find('\n', headerLocation + 1) + 1 ) + 1;
std::string nameString = "name";
size_t nameLocation = postItem.find(nameString, headerLocation);
if (nameLocation != std::string::npos && nameLocation < headerEnd)
{
size_t nameStart = nameLocation + nameString.size() + 2;
size_t nameSize = postItem.find('"', nameStart) - nameStart;
fieldName = postItem.substr(nameStart, nameSize);
}
}
postItem = postItem.substr(headerEnd);
HttpPostData postData;
postData.name = fieldName;
postData.value = postItem;
postDataCollection.push_back(postData);
location = nextLocation + boundarySize;
}
return postDataCollection;
}
std::string HttpRequestHandler::HandlePostData(const std::string& uri, const std::vector<Http::HttpPostData>& postData)
{
return m_callback(uri, postData);
}
bool HttpRequestHandler::URLDecode(const std::string& in, std::string& out)
{
out.clear();
out.reserve(in.size());
for (std::size_t i = 0; i < in.size(); ++i)
{
if (in[i] == '%')
{
if (i + 3 <= in.size())
{
int value = 0;
std::istringstream is(in.substr(i + 1, 2));
if (is >> std::hex >> value)
{
out += static_cast<char>(value);
i += 2;
}
else
{
return false;
}
}
else
{
return false;
}
}
else if (in[i] == '+')
{
out += ' ';
}
else
{
out += in[i];
}
}
return true;
}
} // namespace Http
+35
View File
@@ -0,0 +1,35 @@
#ifndef HTTP_HTTPREQUESTHANDLER_H
#define HTTP_HTTPREQUESTHANDLER_H
#include "HttpReply.h"
#include "HttpRequest.h"
#include "HttpPostData.h"
#include <functional>
#include <string>
#include <vector>
namespace Http {
class HttpRequestHandler
{
public:
explicit HttpRequestHandler(std::function<std::string(const std::string&, const std::vector<HttpPostData>&)> callback);
HttpRequestHandler(const HttpRequestHandler&) = delete;
HttpReply HandleRequest(const HttpRequest& request, const std::string& data);
private:
std::vector<HttpPostData> GetPostData(const std::string& boundary, const std::string& data) const;
std::string HandlePostData(const std::string& uri, const std::vector<HttpPostData>& postData);
static bool URLDecode(const std::string& in, std::string& out);
private:
std::function<std::string(const std::string&, const std::vector<HttpPostData>&)> m_callback;
};
} // namespace Http
#endif // HTTP_HTTPREQUESTHANDLER_H
+304
View File
@@ -0,0 +1,304 @@
#include "HttpRequestParser.h"
#include "HttpRequest.h"
namespace Http {
HttpRequestParser::HttpRequestParser() :
m_state(State::MethodStart)
{
}
void HttpRequestParser::Reset()
{
m_state = State::MethodStart;
}
Utilities::Tribool HttpRequestParser::Consume(HttpRequest& request, char input)
{
switch (m_state)
{
case State::MethodStart:
if (!IsChar(input) || IsCtl(input) || IsTSpecial(input))
{
return false;
}
else
{
m_state = State::Method;
request.method.push_back(input);
return Utilities::Tribool::Indeterminate;
}
case State::Method:
if (input == ' ')
{
m_state = State::URI;
return Utilities::Tribool::Indeterminate;
}
else if (!IsChar(input) || IsCtl(input) || IsTSpecial(input))
{
return false;
}
else
{
request.method.push_back(input);
return Utilities::Tribool::Indeterminate;
}
case State::URI:
if (input == ' ')
{
m_state = State::HttpVersionH;
return Utilities::Tribool::Indeterminate;
}
else if (IsCtl(input))
{
return false;
}
else
{
request.uri.push_back(input);
return Utilities::Tribool::Indeterminate;
}
case State::HttpVersionH:
if (input == 'H')
{
m_state = State::HttpVersionT1;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionT1:
if (input == 'T')
{
m_state = State::HttpVersionT2;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionT2:
if (input == 'T')
{
m_state = State::HttpVersionP;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionP:
if (input == 'P')
{
m_state = State::HttpVersionSlash;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionSlash:
if (input == '/')
{
request.httpVersionMajor = 0;
request.httpVersionMinor = 0;
m_state = State::HttpVersionMajorStart;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionMajorStart:
if (IsDigit(input))
{
request.httpVersionMajor = request.httpVersionMajor * 10 + input - '0';
m_state = State::HttpVersionMajor;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionMajor:
if (input == '.')
{
m_state = State::HttpVersionMinorStart;
return Utilities::Tribool::Indeterminate;
}
else if (IsDigit(input))
{
request.httpVersionMajor = request.httpVersionMajor * 10 + input - '0';
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionMinorStart:
if (IsDigit(input))
{
request.httpVersionMinor = request.httpVersionMinor * 10 + input - '0';
m_state = State::HttpVersionMinor;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HttpVersionMinor:
if (input == '\r')
{
m_state = State::ExpectingNewline1;
return Utilities::Tribool::Indeterminate;
}
else if (IsDigit(input))
{
request.httpVersionMinor = request.httpVersionMinor * 10 + input - '0';
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::ExpectingNewline1:
if (input == '\n')
{
m_state = State::HeaderLineStart;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HeaderLineStart:
if (input == '\r')
{
m_state = State::ExpectingNewline3;
return Utilities::Tribool::Indeterminate;
}
else if (!request.headers.empty() && (input == ' ' || input == '\t'))
{
m_state = State::HeaderLws;
return Utilities::Tribool::Indeterminate;
}
else if (!IsChar(input) || IsCtl(input) || IsTSpecial(input))
{
return false;
}
else
{
request.headers.push_back(HttpHeader());
request.headers.back().name.push_back(input);
m_state = State::HeaderName;
return Utilities::Tribool::Indeterminate;
}
case State::HeaderLws:
if (input == '\r')
{
m_state = State::ExpectingNewline2;
return Utilities::Tribool::Indeterminate;
}
else if (input == ' ' || input == '\t')
{
return Utilities::Tribool::Indeterminate;
}
else if (IsCtl(input))
{
return false;
}
else
{
m_state = State::HeaderValue;
request.headers.back().value.push_back(input);
return Utilities::Tribool::Indeterminate;
}
case State::HeaderName:
if (input == ':')
{
m_state = State::SpaceBeforeHeaderValue;
return Utilities::Tribool::Indeterminate;
}
else if (!IsChar(input) || IsCtl(input) || IsTSpecial(input))
{
return false;
}
else
{
request.headers.back().name.push_back(input);
return Utilities::Tribool::Indeterminate;
}
case State::SpaceBeforeHeaderValue:
if (input == ' ')
{
m_state = State::HeaderValue;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::HeaderValue:
if (input == '\r')
{
m_state = State::ExpectingNewline2;
return Utilities::Tribool::Indeterminate;
}
else if (IsCtl(input))
{
return false;
}
else
{
request.headers.back().value.push_back(input);
return Utilities::Tribool::Indeterminate;
}
case State::ExpectingNewline2:
if (input == '\n')
{
m_state = State::HeaderLineStart;
return Utilities::Tribool::Indeterminate;
}
else
{
return false;
}
case State::ExpectingNewline3:
return (input == '\n');
default:
return false;
}
}
bool HttpRequestParser::IsChar(int c)
{
return c >= 0 && c <= 127;
}
bool HttpRequestParser::IsCtl(int c)
{
return (c >= 0 && c <= 31) || (c == 127);
}
bool HttpRequestParser::IsTSpecial(int c)
{
switch (c)
{
case '(': case ')': case '<': case '>': case '@':
case ',': case ';': case ':': case '\\': case '"':
case '/': case '[': case ']': case '?': case '=':
case '{': case '}': case ' ': case '\t':
return true;
default:
return false;
}
}
bool HttpRequestParser::IsDigit(int c)
{
return c >= '0' && c <= '9';
}
} // namespace Http
+75
View File
@@ -0,0 +1,75 @@
#ifndef HTTP_HTTPREQUESTPARSER_H
#define HTTP_HTTPREQUESTPARSER_H
#include "HttpRequest.h"
#include "Tribool.h"
#include <tuple>
namespace Http {
struct HttpRequest;
class HttpRequestParser
{
public:
HttpRequestParser();
void Reset();
template <typename InputIterator>
std::tuple<Utilities::Tribool, InputIterator> Parse(HttpRequest& request, InputIterator begin, InputIterator end)
{
Utilities::Tribool result = Utilities::Tribool::Indeterminate;
while (begin != end)
{
result = Consume(request, *begin++);
if (result || !result)
return std::make_tuple(result, begin);
}
return std::make_tuple(result, begin);
}
private:
Utilities::Tribool Consume(HttpRequest& request, char input);
static bool IsChar(int c);
static bool IsCtl(int c);
static bool IsTSpecial(int c);
static bool IsDigit(int c);
private:
struct State
{
enum type
{
MethodStart,
Method,
URI,
HttpVersionH,
HttpVersionT1,
HttpVersionT2,
HttpVersionP,
HttpVersionSlash,
HttpVersionMajorStart,
HttpVersionMajor,
HttpVersionMinorStart,
HttpVersionMinor,
ExpectingNewline1,
HeaderLineStart,
HeaderLws,
HeaderName,
SpaceBeforeHeaderValue,
HeaderValue,
ExpectingNewline2,
ExpectingNewline3
};
};
private:
State::type m_state;
};
} // namespace Http
#endif // HTTP_HTTPREQUESTPARSER_H
+21
View File
@@ -0,0 +1,21 @@
#include "HttpServer.h"
#include "HttpServerImpl.h"
namespace Http {
HttpServer::HttpServer(unsigned short port, std::function<std::string(const std::string&, const std::vector<HttpPostData>&)> callback) :
m_pHttpServerImpl(new HttpServerImpl(port, callback))
{
}
HttpServer::~HttpServer()
{
}
void HttpServer::Wait()
{
m_pHttpServerImpl->Wait();
}
} // namespace Http
+79
View File
@@ -0,0 +1,79 @@
#include "HttpServerImpl.h"
#include "Logging.h"
#include <sys/wait.h>
#include <functional>
#include <sstream>
namespace Http {
HttpServerImpl::HttpServerImpl(unsigned short port, std::function<std::string(const std::string&, const std::vector<HttpPostData>&)> callback) :
m_threadPoolSize(3),
m_signals(m_ioService),
m_acceptor(m_ioService),
m_requestHandler(callback)
{
m_signals.add(SIGINT);
m_signals.add(SIGTERM);
m_signals.add(SIGQUIT);
m_signals.async_wait(std::bind(&HttpServerImpl::HandleStop, this));
asio::ip::tcp::resolver resolver(m_ioService);
asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
m_acceptor.open(endpoint.protocol());
m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
m_acceptor.bind(endpoint);
m_acceptor.listen();
for (std::size_t i = 0; i < m_threadPoolSize; ++i)
{
auto thread = std::make_shared<std::thread>([&] { m_ioService.run(); });
#ifdef __linux__
pthread_setname_np(thread->native_handle(), "HttpServer");
#endif
m_threads.push_back(thread);
}
StartAccept();
}
HttpServerImpl::~HttpServerImpl()
{
}
void HttpServerImpl::Wait()
{
for (std::size_t i = 0; i < m_threads.size(); ++i)
m_threads[i]->join();
}
void HttpServerImpl::StartAccept()
{
m_connection.reset(new HttpConnection(m_ioService, m_requestHandler));
m_acceptor.async_accept(m_connection->Socket(), std::bind(&HttpServerImpl::HandleAccept, this, std::placeholders::_1));
}
void HttpServerImpl::HandleAccept(const std::error_code& ec)
{
if (!ec)
{
m_connection->Start();
}
else
{
std::stringstream ss;
ss << "Http Accept error: " << ec.message() << std::endl;
Logging::Log(Logging::Severity::Error, ss.str());
}
StartAccept();
}
void HttpServerImpl::HandleStop()
{
m_ioService.stop();
}
} // namespace Http
+40
View File
@@ -0,0 +1,40 @@
#ifndef HTTP_HTTPSERVERIMPL_H
#define HTTP_HTTPSERVERIMPL_H
#include "HttpConnection.h"
#include "HttpRequestHandler.h"
#include "asio.hpp"
#include <functional>
#include <memory>
#include <thread>
#include <vector>
namespace Http {
class HttpServerImpl
{
public:
HttpServerImpl(unsigned short port, std::function<std::string(const std::string&, const std::vector<HttpPostData>&)> callback);
~HttpServerImpl();
void Wait();
private:
void StartAccept();
void HandleAccept(const asio::error_code& ec);
void HandleStop();
private:
std::size_t m_threadPoolSize;
std::vector<std::shared_ptr<std::thread> > m_threads;
asio::io_service m_ioService;
asio::signal_set m_signals;
asio::ip::tcp::acceptor m_acceptor;
std::shared_ptr<HttpConnection> m_connection;
HttpRequestHandler m_requestHandler;
};
} // namespace Http
#endif // HTTP_HTTPSERVERIMPL_H
+1
View File
@@ -0,0 +1 @@
../Makefile