Initial commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
*.o.*
|
||||
*.d.*
|
||||
*.a.*
|
||||
.*.swp
|
||||
.AppleDouble
|
||||
lib
|
||||
Libraries
|
||||
fixPermissions.sh
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "Makefiles"]
|
||||
path = Makefiles
|
||||
url = https://gogs.dierkse.nl/JDierkse/Makefiles.git
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../Makefile
|
||||
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile.conf
|
||||
#
|
||||
|
||||
CFLAGS += -I$(ROOTPATH) -I$(ROOTPATH)/include -I$(ROOTPATH)/Libraries/asio/include -I$(ROOTPATH)/Libraries/Logging/include -I$(ROOTPATH)/Libraries/Utilities/include
|
||||
DEBUGDIR := .debug
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Makefile.target
|
||||
#
|
||||
|
||||
Http.a.$(ARCH) : $(OBJECTS)
|
||||
$(call build_target_library_arch,$@,$^)
|
||||
Http.a:
|
||||
$(call build_target,$@)
|
||||
|
||||
.DEFAULT_GOAL := Http.a
|
||||
|
||||
TARGETS += Http.a
|
||||
Submodule
+1
Submodule Makefiles added at d82c8a2047
@@ -0,0 +1,69 @@
|
||||
#ifndef HTTPCLIENT_H
|
||||
#define HTTPCLIENT_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Http {
|
||||
|
||||
class HttpClientImpl;
|
||||
|
||||
class HttpClient
|
||||
{
|
||||
public:
|
||||
HttpClient();
|
||||
~HttpClient();
|
||||
|
||||
HttpClient(const HttpClient&) = 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:
|
||||
std::unique_ptr<HttpClientImpl> m_pHttpClientImpl;
|
||||
};
|
||||
|
||||
} // namespace Http
|
||||
|
||||
#endif // HTTPCLIENT_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef HTTP_HTTPPOSTDATA_H
|
||||
#define HTTP_HTTPPOSTDATA_H
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Http {
|
||||
|
||||
struct HttpPostData
|
||||
{
|
||||
bool operator==(const std::string& other) const
|
||||
{
|
||||
return name == other;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
} // namespace Http
|
||||
|
||||
#endif // HTTP_HTTPPOSTDATA_H
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef HTTPSERVER_H
|
||||
#define HTTPSERVER_H
|
||||
|
||||
#include "HttpPostData.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Http {
|
||||
|
||||
class HttpServerImpl;
|
||||
|
||||
class HttpServer
|
||||
{
|
||||
public:
|
||||
HttpServer(unsigned short port, std::function<std::string(const std::string&, const std::vector<HttpPostData>&)> callback);
|
||||
~HttpServer();
|
||||
|
||||
void Wait();
|
||||
|
||||
private:
|
||||
std::unique_ptr<HttpServerImpl> m_pHttpServerImpl;
|
||||
};
|
||||
|
||||
} // namespace Http
|
||||
|
||||
#endif // HTTPSERVER_H
|
||||
Reference in New Issue
Block a user