mirror of
https://github.com/kiwix/kiwix-tools.git
synced 2025-09-23 12:00:21 -04:00
Merge pull request #111 from kiwix/better_request_parsing
Better request parsing
This commit is contained in:
commit
04b05b7902
@ -81,6 +81,8 @@ extern "C" {
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "request_context.h"
|
||||||
|
|
||||||
#ifdef interface
|
#ifdef interface
|
||||||
#undef interface
|
#undef interface
|
||||||
#endif
|
#endif
|
||||||
@ -124,35 +126,11 @@ static std::string getMimeTypeForFile(const std::string& filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct RequestContext {
|
static bool startswith(const std::string& base, const std::string& start)
|
||||||
struct MHD_Connection* connection;
|
{
|
||||||
int httpResponseCode;
|
return start.length() <= base.length()
|
||||||
kiwix::Reader* reader;
|
&& std::equal(start.begin(), start.end(), base.begin());
|
||||||
kiwix::Searcher* searcher;
|
}
|
||||||
const std::string urlStr;
|
|
||||||
const std::string humanReadableBookId;
|
|
||||||
bool acceptEncodingDeflate;
|
|
||||||
bool acceptRange;
|
|
||||||
int range_start;
|
|
||||||
int range_end;
|
|
||||||
|
|
||||||
RequestContext(struct MHD_Connection* connection, int httpResponseCode,
|
|
||||||
kiwix::Reader* reader, kiwix::Searcher* searcher,
|
|
||||||
const std::string& urlStr, const std::string& humanReadableBookId,
|
|
||||||
bool acceptEncodingDeflate,
|
|
||||||
bool acceptRange, int range_start, int range_end) :
|
|
||||||
connection(connection),
|
|
||||||
httpResponseCode(httpResponseCode),
|
|
||||||
reader(reader),
|
|
||||||
searcher(searcher),
|
|
||||||
urlStr(urlStr),
|
|
||||||
humanReadableBookId(humanReadableBookId),
|
|
||||||
acceptEncodingDeflate(acceptEncodingDeflate),
|
|
||||||
acceptRange(acceptRange),
|
|
||||||
range_start(range_start),
|
|
||||||
range_end(range_end)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void introduceTaskbar(string& content, const string& humanReadableBookId)
|
void introduceTaskbar(string& content, const string& humanReadableBookId)
|
||||||
@ -285,22 +263,34 @@ static struct MHD_Response* build_response(const void* data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct MHD_Response* build_404(RequestContext* request_context) {
|
static struct MHD_Response* build_404(RequestContext* request,
|
||||||
|
const std::string& humanReadableBookId) {
|
||||||
std::string content
|
std::string content
|
||||||
= "<!DOCTYPE html>\n<html><head><meta "
|
= "<!DOCTYPE html>\n<html><head><meta "
|
||||||
"content=\"text/html;charset=UTF-8\" http-equiv=\"content-type\" "
|
"content=\"text/html;charset=UTF-8\" http-equiv=\"content-type\" "
|
||||||
"/><title>Content not found</title></head><body><h1>Not "
|
"/><title>Content not found</title></head><body><h1>Not "
|
||||||
"Found</h1><p>The requested URL \""
|
"Found</h1><p>The requested URL \""
|
||||||
+ request_context->urlStr + "\" was not found on this server.</p></body></html>";
|
+ request->get_full_url() + "\" was not found on this server.</p></body></html>";
|
||||||
auto mimeType = "text/html";
|
auto mimeType = "text/html";
|
||||||
request_context->httpResponseCode = MHD_HTTP_NOT_FOUND;
|
request->httpResponseCode = MHD_HTTP_NOT_FOUND;
|
||||||
introduceTaskbar(content, request_context->humanReadableBookId);
|
introduceTaskbar(content, humanReadableBookId);
|
||||||
bool deflated
|
bool deflated
|
||||||
= request_context->acceptEncodingDeflate && compress_content(content, mimeType);
|
= request->can_compress() && compress_content(content, mimeType);
|
||||||
return build_response(
|
return build_response(
|
||||||
content.data(), content.size(), "", mimeType, deflated, false);
|
content.data(), content.size(), "", mimeType, deflated, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct MHD_Response* build_homepage(RequestContext* request)
|
||||||
|
{
|
||||||
|
std::string content = welcomeHTML;
|
||||||
|
|
||||||
|
std::string mimeType = "text/html; charset=utf-8";
|
||||||
|
|
||||||
|
bool deflated = request->can_compress() && compress_content(content, mimeType);
|
||||||
|
return build_response(
|
||||||
|
content.data(), content.size(), "", mimeType, deflated, false);
|
||||||
|
}
|
||||||
|
|
||||||
struct RunningResponse {
|
struct RunningResponse {
|
||||||
zim::Article* article;
|
zim::Article* article;
|
||||||
int range_start;
|
int range_start;
|
||||||
@ -380,29 +370,54 @@ static struct MHD_Response* build_callback_response_from_article(
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct MHD_Response* handle_suggest(RequestContext* request_context)
|
std::pair<kiwix::Reader*, kiwix::Searcher*>
|
||||||
|
get_from_humanReadableBookId(const std::string& humanReadableBookId) {
|
||||||
|
kiwix::Searcher* searcher
|
||||||
|
= searchers.find(humanReadableBookId) != searchers.end()
|
||||||
|
? searchers.find(humanReadableBookId)->second
|
||||||
|
: globalSearcher;
|
||||||
|
kiwix::Reader* reader = readers.find(humanReadableBookId) != readers.end()
|
||||||
|
? readers.find(humanReadableBookId)->second
|
||||||
|
: NULL;
|
||||||
|
return std::pair<kiwix::Reader*, kiwix::Searcher*>(reader, searcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct MHD_Response* handle_suggest(RequestContext* request)
|
||||||
{
|
{
|
||||||
|
if (isVerbose.load()) {
|
||||||
|
printf("** running handle_suggest\n");
|
||||||
|
}
|
||||||
|
|
||||||
std::string content;
|
std::string content;
|
||||||
std::string mimeType;
|
std::string mimeType;
|
||||||
unsigned int maxSuggestionCount = 10;
|
unsigned int maxSuggestionCount = 10;
|
||||||
unsigned int suggestionCount = 0;
|
unsigned int suggestionCount = 0;
|
||||||
std::string suggestion;
|
std::string suggestion;
|
||||||
|
|
||||||
/* Get the suggestion pattern from the HTTP request */
|
std::string humanReadableBookId;
|
||||||
const char* cTerm = MHD_lookup_connection_value(
|
std::string term;
|
||||||
request_context->connection, MHD_GET_ARGUMENT_KIND, "term");
|
try {
|
||||||
std::string term = cTerm == NULL ? "" : cTerm;
|
humanReadableBookId = request->get_argument("content");
|
||||||
|
term = request->get_argument("term");
|
||||||
|
} catch (const std::out_of_range&) {
|
||||||
|
return build_homepage(request);
|
||||||
|
}
|
||||||
|
|
||||||
if (isVerbose.load()) {
|
if (isVerbose.load()) {
|
||||||
printf("Searching suggestions for: \"%s\"\n", term.c_str());
|
printf("Searching suggestions for: \"%s\"\n", term.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto reader_searcher = get_from_humanReadableBookId(humanReadableBookId);
|
||||||
|
auto reader = reader_searcher.first;
|
||||||
|
auto searcher = reader_searcher.second;
|
||||||
|
|
||||||
pthread_mutex_lock(&searchLock);
|
pthread_mutex_lock(&searchLock);
|
||||||
/* Get the suggestions */
|
/* Get the suggestions */
|
||||||
content = "[";
|
content = "[";
|
||||||
if (request_context->reader != NULL) {
|
if (reader != nullptr) {
|
||||||
/* Get the suggestions */
|
/* Get the suggestions */
|
||||||
request_context->reader->searchSuggestionsSmart(term, maxSuggestionCount);
|
reader->searchSuggestionsSmart(term, maxSuggestionCount);
|
||||||
while (request_context->reader->getNextSuggestion(suggestion)) {
|
while (reader->getNextSuggestion(suggestion)) {
|
||||||
kiwix::stringReplacement(suggestion, "\"", "\\\"");
|
kiwix::stringReplacement(suggestion, "\"", "\\\"");
|
||||||
content += (content == "[" ? "" : ",");
|
content += (content == "[" ? "" : ",");
|
||||||
content += "{\"value\":\"" + suggestion + "\",\"label\":\"" + suggestion
|
content += "{\"value\":\"" + suggestion + "\",\"label\":\"" + suggestion
|
||||||
@ -413,126 +428,136 @@ static struct MHD_Response* handle_suggest(RequestContext* request_context)
|
|||||||
pthread_mutex_unlock(&searchLock);
|
pthread_mutex_unlock(&searchLock);
|
||||||
|
|
||||||
/* Propose the fulltext search if possible */
|
/* Propose the fulltext search if possible */
|
||||||
if (request_context->searcher != NULL) {
|
if (searcher != NULL) {
|
||||||
content += (suggestionCount == 0 ? "" : ",");
|
content += (suggestionCount == 0 ? "" : ",");
|
||||||
content += "{\"value\":\"" + std::string(term)
|
content += "{\"value\":\"" + term
|
||||||
+ " \", \"label\":\"containing '" + std::string(term)
|
+ " \", \"label\":\"containing '" + term
|
||||||
+ "'...\"}";
|
+ "'...\"}";
|
||||||
}
|
}
|
||||||
|
|
||||||
content += "]";
|
content += "]";
|
||||||
mimeType = "application/json; charset=utf-8";
|
mimeType = "application/json; charset=utf-8";
|
||||||
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
|
bool deflated = request->can_compress() && compress_content(content, mimeType);
|
||||||
return build_response(
|
return build_response(
|
||||||
content.data(), content.size(), "", mimeType, deflated, true);
|
content.data(), content.size(), "", mimeType, deflated, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct MHD_Response* handle_skin(RequestContext* request_context)
|
static struct MHD_Response* handle_skin(RequestContext* request)
|
||||||
{
|
{
|
||||||
std::string content;
|
if (isVerbose.load()) {
|
||||||
try {
|
printf("** running handle_skin\n");
|
||||||
content = getResource(request_context->urlStr.substr(rootLocation.size() + 6));
|
|
||||||
} catch (const ResourceNotFound& e) {
|
|
||||||
return build_404(request_context);
|
|
||||||
}
|
}
|
||||||
std::string mimeType = getMimeTypeForFile(request_context->urlStr);
|
|
||||||
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
|
std::string content;
|
||||||
|
auto resourceName = request->get_url().substr(6);
|
||||||
|
try {
|
||||||
|
content = getResource(resourceName);
|
||||||
|
} catch (const ResourceNotFound& e) {
|
||||||
|
return build_404(request, "");
|
||||||
|
}
|
||||||
|
std::string mimeType = getMimeTypeForFile(resourceName);
|
||||||
|
bool deflated = request->can_compress() && compress_content(content, mimeType);
|
||||||
return build_response(
|
return build_response(
|
||||||
content.data(), content.size(), "", mimeType, deflated, true);
|
content.data(), content.size(), "", mimeType, deflated, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct MHD_Response* handle_search(RequestContext* request_context)
|
static struct MHD_Response* handle_search(RequestContext* request)
|
||||||
{
|
{
|
||||||
|
if (isVerbose.load()) {
|
||||||
|
printf("** running handle_search\n");
|
||||||
|
}
|
||||||
|
|
||||||
std::string content;
|
std::string content;
|
||||||
std::string mimeType;
|
std::string mimeType;
|
||||||
std::string httpRedirection;
|
std::string httpRedirection;
|
||||||
|
|
||||||
/* Retrieve the pattern to search */
|
std::string humanReadableBookId;
|
||||||
const char* pattern = MHD_lookup_connection_value(
|
std::string patternString;
|
||||||
request_context->connection, MHD_GET_ARGUMENT_KIND, "pattern");
|
try {
|
||||||
std::string patternString
|
humanReadableBookId = request->get_argument("content");
|
||||||
= kiwix::urlDecode(pattern == NULL ? "" : string(pattern));
|
patternString = request->get_argument("pattern");
|
||||||
|
} catch (const std::out_of_range&) {
|
||||||
|
return build_homepage(request);
|
||||||
|
}
|
||||||
|
|
||||||
/* Retrive geo search */
|
/* Retrive geo search */
|
||||||
bool has_geo_query = false;
|
bool has_geo_query = false;
|
||||||
const char* latitude = MHD_lookup_connection_value(
|
float latitude;
|
||||||
request_context->connection, MHD_GET_ARGUMENT_KIND, "latitude");
|
float longitude;
|
||||||
const char* longitude = MHD_lookup_connection_value(
|
float distance;
|
||||||
request_context->connection, MHD_GET_ARGUMENT_KIND, "longitude");
|
try {
|
||||||
const char* distance = MHD_lookup_connection_value(
|
latitude = request->get_argument<float>("latitude");
|
||||||
request_context->connection, MHD_GET_ARGUMENT_KIND, "distance");
|
longitude = request->get_argument<float>("longitude");
|
||||||
|
distance = request->get_argument<float>("distance");
|
||||||
float latitudeFloat(0), longitudeFloat(0), distanceFloat(0);
|
has_geo_query = true;
|
||||||
if (latitude != nullptr && longitude != nullptr && distance != nullptr) {
|
} catch(const std::out_of_range&) {}
|
||||||
try {
|
catch(const std::invalid_argument&) {}
|
||||||
latitudeFloat = stof(string(latitude));
|
|
||||||
longitudeFloat = stof(string(longitude));
|
|
||||||
distanceFloat = stof(string(distance));
|
|
||||||
has_geo_query = true;
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search results for searches from the welcome page should not
|
/* Search results for searches from the welcome page should not
|
||||||
be cached
|
be cached
|
||||||
*/
|
*/
|
||||||
bool cacheEnabled = !(request_context->searcher == globalSearcher);
|
auto reader_searcher = get_from_humanReadableBookId(humanReadableBookId);
|
||||||
|
auto reader = reader_searcher.first;
|
||||||
std::string patternCorrespondingUrl;
|
auto searcher = reader_searcher.second;
|
||||||
|
bool cacheEnabled = !(searcher == globalSearcher);
|
||||||
|
|
||||||
/* Try first to load directly the article */
|
/* Try first to load directly the article */
|
||||||
if (request_context->reader != NULL) {
|
if (reader != nullptr) {
|
||||||
std::vector<std::string> variants = request_context->reader->getTitleVariants(patternString);
|
std::string patternCorrespondingUrl;
|
||||||
std::vector<std::string>::iterator variantsItr = variants.begin();
|
auto variants = reader->getTitleVariants(patternString);
|
||||||
|
auto variantsItr = variants.begin();
|
||||||
|
|
||||||
while (patternCorrespondingUrl.empty() && variantsItr != variants.end()) {
|
while (patternCorrespondingUrl.empty() && variantsItr != variants.end()) {
|
||||||
request_context->reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl);
|
reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl);
|
||||||
variantsItr++;
|
variantsItr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If article found then redirect directly to it */
|
/* If article found then redirect directly to it */
|
||||||
if (!patternCorrespondingUrl.empty()) {
|
if (!patternCorrespondingUrl.empty()) {
|
||||||
httpRedirection
|
httpRedirection
|
||||||
= rootLocation + "/" + request_context->humanReadableBookId + "/" + patternCorrespondingUrl;
|
= rootLocation + "/" + humanReadableBookId + "/" + patternCorrespondingUrl;
|
||||||
request_context->httpResponseCode = MHD_HTTP_FOUND;
|
request->httpResponseCode = MHD_HTTP_FOUND;
|
||||||
return build_response("", 0, httpRedirection, "", false, true);
|
return build_response("", 0, httpRedirection, "", false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the search */
|
/* Make the search */
|
||||||
if (request_context->searcher != NULL) {
|
if (reader_searcher.second != nullptr &&
|
||||||
const char* start = MHD_lookup_connection_value(
|
(!patternString.empty() || has_geo_query)) {
|
||||||
request_context->connection, MHD_GET_ARGUMENT_KIND, "start");
|
auto start = 0;
|
||||||
const char* end = MHD_lookup_connection_value(
|
try {
|
||||||
request_context->connection, MHD_GET_ARGUMENT_KIND, "end");
|
start = request->get_argument<unsigned int>("start");
|
||||||
unsigned int startNumber = start != NULL ? atoi(start) : 0;
|
} catch (const std::exception&) {}
|
||||||
unsigned int endNumber = end != NULL ? atoi(end) : 25;
|
auto end = 25;
|
||||||
|
try {
|
||||||
|
end = request->get_argument<unsigned int>("end");
|
||||||
|
} catch (const std::exception&) {}
|
||||||
|
|
||||||
/* Get the results */
|
/* Get the results */
|
||||||
pthread_mutex_lock(&searchLock);
|
pthread_mutex_lock(&searchLock);
|
||||||
try {
|
try {
|
||||||
if (patternString.empty() && has_geo_query) {
|
if (patternString.empty()) {
|
||||||
request_context->searcher->geo_search(latitudeFloat, longitudeFloat, distanceFloat,
|
searcher->geo_search(latitude, longitude, distance,
|
||||||
startNumber, endNumber, isVerbose.load());
|
start, end, isVerbose.load());
|
||||||
} else {
|
} else {
|
||||||
request_context->searcher->search(patternString,
|
searcher->search(patternString,
|
||||||
startNumber, endNumber, isVerbose.load());
|
start, end, isVerbose.load());
|
||||||
}
|
}
|
||||||
content = request_context->searcher->getHtml();
|
content = searcher->getHtml();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&searchLock);
|
pthread_mutex_unlock(&searchLock);
|
||||||
} else {
|
} else {
|
||||||
content = "<!DOCTYPE html>\n<html><head><meta content=\"text/html;charset=UTF-8\" http-equiv=\"content-type\" /><title>Fulltext search unavailable</title></head><body><h1>Not Found</h1><p>There is no article with the title <b>\"" + kiwix::encodeDiples(patternString) + "\"</b> and the fulltext search engine is not available for this content.</p></body></html>";
|
content = "<!DOCTYPE html>\n<html><head><meta content=\"text/html;charset=UTF-8\" http-equiv=\"content-type\" /><title>Fulltext search unavailable</title></head><body><h1>Not Found</h1><p>There is no article with the title <b>\"" + kiwix::encodeDiples(patternString) + "\"</b> and the fulltext search engine is not available for this content.</p></body></html>";
|
||||||
request_context->httpResponseCode = MHD_HTTP_NOT_FOUND;
|
request->httpResponseCode = MHD_HTTP_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType = "text/html; charset=utf-8";
|
mimeType = "text/html; charset=utf-8";
|
||||||
|
|
||||||
introduceTaskbar(content, request_context->humanReadableBookId);
|
introduceTaskbar(content, humanReadableBookId);
|
||||||
|
|
||||||
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
|
bool deflated = request->can_compress() && compress_content(content, mimeType);
|
||||||
return build_response(content.data(),
|
return build_response(content.data(),
|
||||||
content.size(),
|
content.size(),
|
||||||
httpRedirection,
|
httpRedirection,
|
||||||
@ -541,28 +566,61 @@ static struct MHD_Response* handle_search(RequestContext* request_context)
|
|||||||
cacheEnabled);
|
cacheEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct MHD_Response* handle_random(RequestContext* request_context)
|
static struct MHD_Response* handle_random(RequestContext* request)
|
||||||
{
|
{
|
||||||
std::string httpRedirection;
|
if (isVerbose.load()) {
|
||||||
request_context->httpResponseCode = MHD_HTTP_FOUND;
|
printf("** running handle_random\n");
|
||||||
if (request_context->reader != NULL) {
|
|
||||||
std::string randomUrl = request_context->reader->getRandomPageUrl();
|
|
||||||
httpRedirection
|
|
||||||
= rootLocation + "/" + request_context->humanReadableBookId + "/" + kiwix::urlEncode(randomUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string httpRedirection;
|
||||||
|
request->httpResponseCode = MHD_HTTP_FOUND;
|
||||||
|
std::string humanReadableBookId;
|
||||||
|
try {
|
||||||
|
humanReadableBookId = request->get_argument("content");
|
||||||
|
} catch (const std::out_of_range&) {
|
||||||
|
return build_homepage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reader = get_from_humanReadableBookId(humanReadableBookId).first;
|
||||||
|
if (reader == nullptr) {
|
||||||
|
return build_homepage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string randomUrl = reader->getRandomPageUrl();
|
||||||
|
httpRedirection
|
||||||
|
= rootLocation + "/" + humanReadableBookId + "/" + kiwix::urlEncode(randomUrl);
|
||||||
return build_response("", 0, httpRedirection, "", false, false);
|
return build_response("", 0, httpRedirection, "", false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct MHD_Response* handle_content(RequestContext* request_context)
|
static struct MHD_Response* handle_content(RequestContext* request)
|
||||||
{
|
{
|
||||||
|
if (isVerbose.load()) {
|
||||||
|
printf("** running handle_content\n");
|
||||||
|
}
|
||||||
|
|
||||||
std::string baseUrl;
|
std::string baseUrl;
|
||||||
std::string content;
|
std::string content;
|
||||||
std::string mimeType;
|
std::string mimeType;
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
zim::Article article;
|
zim::Article article;
|
||||||
|
|
||||||
|
std::string humanReadableBookId;
|
||||||
try {
|
try {
|
||||||
found = request_context->reader->getArticleObjectByDecodedUrl(request_context->urlStr, article);
|
humanReadableBookId = request->get_url_part(0);
|
||||||
|
} catch (const std::out_of_range& e) {
|
||||||
|
return build_homepage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reader = get_from_humanReadableBookId(humanReadableBookId).first;
|
||||||
|
if (reader == nullptr) {
|
||||||
|
return build_homepage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto urlStr = request->get_url().substr(humanReadableBookId.size()+1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
found = reader->getArticleObjectByDecodedUrl(urlStr, article);
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
/* If redirect */
|
/* If redirect */
|
||||||
@ -582,9 +640,9 @@ static struct MHD_Response* handle_content(RequestContext* request_context)
|
|||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
if (isVerbose.load())
|
if (isVerbose.load())
|
||||||
printf("Failed to find %s\n", request_context->urlStr.c_str());
|
printf("Failed to find %s\n", urlStr.c_str());
|
||||||
|
|
||||||
return build_404(request_context);
|
return build_404(request, humanReadableBookId);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -594,7 +652,7 @@ static struct MHD_Response* handle_content(RequestContext* request_context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isVerbose.load()) {
|
if (isVerbose.load()) {
|
||||||
printf("Found %s\n", request_context->urlStr.c_str());
|
printf("Found %s\n", urlStr.c_str());
|
||||||
printf("mimeType: %s\n", mimeType.c_str());
|
printf("mimeType: %s\n", mimeType.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,59 +669,48 @@ static struct MHD_Response* handle_content(RequestContext* request_context)
|
|||||||
+ article.getUrl();
|
+ article.getUrl();
|
||||||
pthread_mutex_lock(®exLock);
|
pthread_mutex_lock(®exLock);
|
||||||
content = replaceRegex(content,
|
content = replaceRegex(content,
|
||||||
"$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/",
|
"$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/",
|
||||||
"(href|src)(=[\"|\']{0,1})/([A-Z|\\-])/");
|
"(href|src)(=[\"|\']{0,1})/([A-Z|\\-])/");
|
||||||
content = replaceRegex(content,
|
content = replaceRegex(content,
|
||||||
"$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/",
|
"$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/",
|
||||||
"(@import[ ]+)([\"|\']{0,1})/([A-Z|\\-])/");
|
"(@import[ ]+)([\"|\']{0,1})/([A-Z|\\-])/");
|
||||||
content = replaceRegex(
|
content = replaceRegex(
|
||||||
content,
|
content,
|
||||||
"<head><base href=\"" + rootLocation + "/" + request_context->humanReadableBookId + baseUrl + "\" />",
|
"<head><base href=\"" + rootLocation + "/" + humanReadableBookId + baseUrl + "\" />",
|
||||||
"<head>");
|
"<head>");
|
||||||
pthread_mutex_unlock(®exLock);
|
pthread_mutex_unlock(®exLock);
|
||||||
introduceTaskbar(content, request_context->humanReadableBookId);
|
introduceTaskbar(content, humanReadableBookId);
|
||||||
} else if (mimeType.find("text/css") != string::npos) {
|
} else if (mimeType.find("text/css") != string::npos) {
|
||||||
pthread_mutex_lock(®exLock);
|
pthread_mutex_lock(®exLock);
|
||||||
content = replaceRegex(content,
|
content = replaceRegex(content,
|
||||||
"$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/",
|
"$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/",
|
||||||
"(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/");
|
"(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/");
|
||||||
pthread_mutex_unlock(®exLock);
|
pthread_mutex_unlock(®exLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool deflated
|
bool deflated
|
||||||
= request_context->acceptEncodingDeflate && compress_content(content, mimeType);
|
= request->can_compress() && compress_content(content, mimeType);
|
||||||
return build_response(
|
return build_response(
|
||||||
content.data(), content.size(), "", mimeType, deflated, true);
|
content.data(), content.size(), "", mimeType, deflated, true);
|
||||||
} else {
|
} else {
|
||||||
int range_len;
|
int range_len;
|
||||||
if (request_context->range_end == -1) {
|
if (request->get_range().second == -1) {
|
||||||
range_len = article.getArticleSize() - request_context->range_start;
|
range_len = article.getArticleSize() - request->get_range().first;
|
||||||
} else {
|
} else {
|
||||||
range_len = request_context->range_end - request_context->range_start;
|
range_len = request->get_range().second - request->get_range().first;
|
||||||
}
|
}
|
||||||
return build_callback_response_from_article(
|
return build_callback_response_from_article(
|
||||||
article,
|
article,
|
||||||
request_context->range_start,
|
request->get_range().first,
|
||||||
range_len,
|
range_len,
|
||||||
mimeType);
|
mimeType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct MHD_Response* handle_default(RequestContext* request_context)
|
int print_key_value (void *cls, enum MHD_ValueKind kind,
|
||||||
{
|
|
||||||
std::string content = welcomeHTML;
|
|
||||||
|
|
||||||
std::string mimeType = "text/html; charset=utf-8";
|
|
||||||
|
|
||||||
bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType);
|
|
||||||
return build_response(
|
|
||||||
content.data(), content.size(), "", mimeType, deflated, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
int print_out_key (void *cls, enum MHD_ValueKind kind,
|
|
||||||
const char *key, const char *value)
|
const char *key, const char *value)
|
||||||
{
|
{
|
||||||
printf ("%s: %s\n", key, value);
|
printf (" - %s: '%s'\n", key, value);
|
||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,138 +723,46 @@ static int accessHandlerCallback(void* cls,
|
|||||||
size_t* upload_data_size,
|
size_t* upload_data_size,
|
||||||
void** ptr)
|
void** ptr)
|
||||||
{
|
{
|
||||||
|
RequestContext request(connection, rootLocation, url, method, version);
|
||||||
/* Unexpected method */
|
/* Unexpected method */
|
||||||
if (0 != strcmp(method, "GET") && 0 != strcmp(method, "POST"))
|
if (request.get_method() != RequestMethod::GET && request.get_method() != RequestMethod::POST) {
|
||||||
return MHD_NO;
|
return MHD_NO;
|
||||||
|
|
||||||
if (isVerbose.load()) {
|
|
||||||
printf("Requesting : \n");
|
|
||||||
printf("u : %s\n", url);
|
|
||||||
printf("m : %s\n", method);
|
|
||||||
printf("v : %s\n", version);
|
|
||||||
MHD_get_connection_values (connection, MHD_HEADER_KIND, &print_out_key, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the response can be compressed */
|
if (isVerbose.load()) {
|
||||||
const char* acceptEncodingHeaderValue = MHD_lookup_connection_value(
|
printf("======================\n");
|
||||||
connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT_ENCODING);
|
request.print_debug_info();
|
||||||
const bool acceptEncodingDeflate
|
|
||||||
= acceptEncodingHeaderValue
|
|
||||||
&& string(acceptEncodingHeaderValue).find("deflate") != string::npos;
|
|
||||||
|
|
||||||
/* Check if range is requested. */
|
|
||||||
const char* acceptRangeHeaderValue = MHD_lookup_connection_value(
|
|
||||||
connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE);
|
|
||||||
bool acceptRange = false;
|
|
||||||
int range_start = 0;
|
|
||||||
int range_end = -1;
|
|
||||||
if (acceptRangeHeaderValue != NULL) {
|
|
||||||
// [FIXME] This part is sub-optimal and potentially prone to fail
|
|
||||||
// because we don't check the string length before using substr
|
|
||||||
// The `range.length() >= 6` should mitigate the bug but we have to
|
|
||||||
// rewrite this part.
|
|
||||||
auto range = std::string(acceptRangeHeaderValue);
|
|
||||||
if (range.length() >= 6 && range.substr(0, 6) == "bytes=") {
|
|
||||||
range = range.substr(6);
|
|
||||||
std::istringstream iss(range);
|
|
||||||
iss >> range_start;
|
|
||||||
range = range.substr(iss.tellg());
|
|
||||||
if (range[0] == '-') {
|
|
||||||
range = range.substr(1);
|
|
||||||
if (! range.empty()) {
|
|
||||||
std::istringstream iss(range);
|
|
||||||
iss >> range_end;
|
|
||||||
range = range.substr(iss.tellg());
|
|
||||||
}
|
|
||||||
if (range.empty()) {
|
|
||||||
// Nothing left to read. We are OK.
|
|
||||||
acceptRange = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare the variables */
|
/* Prepare the variables */
|
||||||
struct MHD_Response* response;
|
struct MHD_Response* response;
|
||||||
int httpResponseCode = acceptRange ? MHD_HTTP_PARTIAL_CONTENT : MHD_HTTP_OK;
|
request.httpResponseCode = request.has_range() ? MHD_HTTP_PARTIAL_CONTENT : MHD_HTTP_OK;
|
||||||
std::string urlStr = string(url);
|
|
||||||
|
|
||||||
/* Get searcher and reader */
|
if (! request.is_valid_url()) {
|
||||||
std::string humanReadableBookId = "";
|
response = build_homepage(&request);
|
||||||
|
} else {
|
||||||
if (!rootLocation.empty() && urlStr.substr(0, rootLocation.size() + 1) != rootLocation + "/"){
|
if (startswith(request.get_url(), "/skin/")) {
|
||||||
humanReadableBookId = "";
|
response = handle_skin(&request);
|
||||||
}
|
} else if (request.get_url() == "/search") {
|
||||||
|
response = handle_search(&request);
|
||||||
else if (!(urlStr.size() > rootLocation.size() + 5 && urlStr.substr(rootLocation.size() , 6) == "/skin/")) {
|
} else if (request.get_url() == "/suggest") {
|
||||||
if ((urlStr == rootLocation + "/" + "search") || (urlStr == rootLocation + "/" + "suggest")
|
response = handle_suggest(&request);
|
||||||
|| (urlStr == rootLocation + "/" + "random")) {
|
} else if (request.get_url() == "/random") {
|
||||||
const char* tmpGetValue = MHD_lookup_connection_value(
|
response = handle_random(&request);
|
||||||
connection, MHD_GET_ARGUMENT_KIND, "content");
|
|
||||||
humanReadableBookId = (tmpGetValue != NULL ? string(tmpGetValue) : "");
|
|
||||||
} else {
|
} else {
|
||||||
humanReadableBookId = urlStr.substr(rootLocation.size() + 1,
|
response = handle_content(&request);
|
||||||
urlStr.find("/", rootLocation.size() + 1) != string::npos
|
|
||||||
? urlStr.find("/", rootLocation.size() + 1) - (rootLocation.size() + 1)
|
|
||||||
: urlStr.size() - (rootLocation.size() + 2));
|
|
||||||
if (!humanReadableBookId.empty()) {
|
|
||||||
urlStr = urlStr.substr(urlStr.find("/", rootLocation.size() + 1) != string::npos
|
|
||||||
? urlStr.find("/", rootLocation.size() + 1)
|
|
||||||
: humanReadableBookId.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kiwix::Searcher* searcher
|
|
||||||
= searchers.find(humanReadableBookId) != searchers.end()
|
|
||||||
? searchers.find(humanReadableBookId)->second
|
|
||||||
: globalSearcher;
|
|
||||||
kiwix::Reader* reader = readers.find(humanReadableBookId) != readers.end()
|
|
||||||
? readers.find(humanReadableBookId)->second
|
|
||||||
: NULL;
|
|
||||||
if (reader == NULL) {
|
|
||||||
humanReadableBookId = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestContext request_context(connection, httpResponseCode,
|
|
||||||
reader, searcher,
|
|
||||||
urlStr, humanReadableBookId,
|
|
||||||
acceptEncodingDeflate,
|
|
||||||
acceptRange, range_start, range_end);
|
|
||||||
|
|
||||||
|
|
||||||
/* Get suggestions */
|
|
||||||
if ((urlStr == (rootLocation + "/" + "suggest")) && reader != NULL) {
|
|
||||||
response = handle_suggest(&request_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get static skin stuff */
|
|
||||||
else if (urlStr.size() > rootLocation.size() + 5 && urlStr.substr(rootLocation.size() , 6) == "/skin/") {
|
|
||||||
response = handle_skin(&request_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display the search restults */
|
|
||||||
else if (urlStr == (rootLocation + "/" + "search")) {
|
|
||||||
response = handle_search(&request_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display a random article */
|
|
||||||
else if (urlStr == (rootLocation + "/" + "random")) {
|
|
||||||
response = handle_random(&request_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display the content of a ZIM content (article, image, ...) */
|
|
||||||
else if (reader != NULL) {
|
|
||||||
response = handle_content(&request_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display the global Welcome page */
|
|
||||||
else {
|
|
||||||
response = handle_default(&request_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Queue the response */
|
/* Queue the response */
|
||||||
int ret = MHD_queue_response(connection, request_context.httpResponseCode, response);
|
if (isVerbose.load()) {
|
||||||
|
printf("Response :\n");
|
||||||
|
printf("httpResponseCode : %d\n", request.httpResponseCode);
|
||||||
|
printf("headers :\n");
|
||||||
|
MHD_get_response_headers(response, print_key_value, nullptr);
|
||||||
|
printf("----------------------\n");
|
||||||
|
}
|
||||||
|
int ret = MHD_queue_response(connection, request.httpResponseCode, response);
|
||||||
MHD_destroy_response(response);
|
MHD_destroy_response(response);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
sources = ['kiwix-serve.cpp']
|
sources = ['kiwix-serve.cpp', 'request_context.cpp']
|
||||||
sources += server_resources
|
sources += server_resources
|
||||||
|
|
||||||
executable('kiwix-serve', sources,
|
executable('kiwix-serve', sources,
|
||||||
|
213
src/server/request_context.cpp
Normal file
213
src/server/request_context.cpp
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2016 Emmanuel Engelhart <kelson@kiwix.org>
|
||||||
|
* Copyright 2017 Matthieu Gautier<mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "request_context.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
RequestContext::RequestContext(struct MHD_Connection* connection,
|
||||||
|
std::string rootLocation,
|
||||||
|
const std::string& _url,
|
||||||
|
const std::string& method,
|
||||||
|
const std::string& version) :
|
||||||
|
connection(connection),
|
||||||
|
full_url(_url),
|
||||||
|
url(_url),
|
||||||
|
valid_url(true),
|
||||||
|
version(version),
|
||||||
|
acceptEncodingDeflate(false),
|
||||||
|
accept_range(false),
|
||||||
|
range_pair(0, -1)
|
||||||
|
{
|
||||||
|
if (method == "GET") {
|
||||||
|
this->method = RequestMethod::GET;
|
||||||
|
} else if (method == "HEAD") {
|
||||||
|
this->method = RequestMethod::HEAD;
|
||||||
|
} else if (method == "POST") {
|
||||||
|
this->method = RequestMethod::POST;
|
||||||
|
} else if (method == "PUT") {
|
||||||
|
this->method = RequestMethod::PUT;
|
||||||
|
} else if (method == "DELETE") {
|
||||||
|
this->method = RequestMethod::DELETE_;
|
||||||
|
} else if (method == "CONNECT") {
|
||||||
|
this->method = RequestMethod::CONNECT;
|
||||||
|
} else if (method == "OPTIONS") {
|
||||||
|
this->method = RequestMethod::OPTIONS;
|
||||||
|
} else if (method == "TRACE") {
|
||||||
|
this->method = RequestMethod::TRACE;
|
||||||
|
} else if (method == "PATCH") {
|
||||||
|
this->method = RequestMethod::PATCH;
|
||||||
|
} else {
|
||||||
|
this->method = RequestMethod::OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
MHD_get_connection_values(connection, MHD_HEADER_KIND, &RequestContext::fill_header, this);
|
||||||
|
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &RequestContext::fill_argument, this);
|
||||||
|
|
||||||
|
valid_url = true;
|
||||||
|
if (rootLocation.empty()) {
|
||||||
|
// nothing special to handle.
|
||||||
|
url = full_url;
|
||||||
|
} else {
|
||||||
|
if (full_url.size() > rootLocation.size() &&
|
||||||
|
full_url.substr(0, rootLocation.size()+1) == rootLocation + "/") {
|
||||||
|
url = full_url.substr(rootLocation.size());
|
||||||
|
} else {
|
||||||
|
valid_url = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
acceptEncodingDeflate =
|
||||||
|
(get_header(MHD_HTTP_HEADER_ACCEPT_ENCODING).find("deflate") != std::string::npos);
|
||||||
|
} catch (const std::out_of_range&) {}
|
||||||
|
|
||||||
|
/*Check if range is requested. */
|
||||||
|
try {
|
||||||
|
auto range = get_header(MHD_HTTP_HEADER_RANGE);
|
||||||
|
int start = 0;
|
||||||
|
int end = -1;
|
||||||
|
std::istringstream iss(range);
|
||||||
|
char c;
|
||||||
|
|
||||||
|
iss >> start >> c;
|
||||||
|
if (iss.good() && c=='-') {
|
||||||
|
iss >> end;
|
||||||
|
if (iss.fail()) {
|
||||||
|
// Something went wrong will extracting.
|
||||||
|
end = -1;
|
||||||
|
}
|
||||||
|
if (iss.eof()) {
|
||||||
|
accept_range = true;
|
||||||
|
range_pair = std::pair<int, int>(start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::out_of_range&) {}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestContext::~RequestContext()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
int RequestContext::fill_header(void *__this, enum MHD_ValueKind kind,
|
||||||
|
const char *key, const char *value)
|
||||||
|
{
|
||||||
|
RequestContext *_this = static_cast<RequestContext*>(__this);
|
||||||
|
_this->headers[key] = value;
|
||||||
|
return MHD_YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RequestContext::fill_argument(void *__this, enum MHD_ValueKind kind,
|
||||||
|
const char *key, const char* value)
|
||||||
|
{
|
||||||
|
RequestContext *_this = static_cast<RequestContext*>(__this);
|
||||||
|
_this->arguments[key] = value;
|
||||||
|
return MHD_YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestContext::print_debug_info() {
|
||||||
|
printf("Requesting : \n");
|
||||||
|
printf("full_url : %s\n", full_url.c_str());
|
||||||
|
printf("method : %s (%d)\n", method==RequestMethod::GET ? "GET" :
|
||||||
|
method==RequestMethod::POST ? "POST" :
|
||||||
|
"OTHER", method);
|
||||||
|
printf("version : %s\n", version.c_str());
|
||||||
|
printf("headers :\n");
|
||||||
|
for (auto it=headers.begin(); it!=headers.end(); it++) {
|
||||||
|
printf(" - %s : '%s'\n", it->first.c_str(), it->second.c_str());
|
||||||
|
}
|
||||||
|
printf("arguments :\n");
|
||||||
|
for (auto it=arguments.begin(); it!=arguments.end(); it++) {
|
||||||
|
printf(" - %s : '%s'\n", it->first.c_str(), it->second.c_str());
|
||||||
|
}
|
||||||
|
printf("Parsed : \n");
|
||||||
|
printf("url : %s\n", url.c_str());
|
||||||
|
printf("acceptEncodingDeflate : %d\n", acceptEncodingDeflate);
|
||||||
|
printf("has_range : %d\n", accept_range);
|
||||||
|
printf(".............\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RequestMethod RequestContext::get_method() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequestContext::get_url() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequestContext::get_url_part(int number) {
|
||||||
|
size_t start = 1;
|
||||||
|
while(true) {
|
||||||
|
auto found = url.find('/', start);
|
||||||
|
if (number == 0) {
|
||||||
|
if (found == std::string::npos) {
|
||||||
|
return url.substr(start);
|
||||||
|
} else {
|
||||||
|
return url.substr(start, found-start);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (found == std::string::npos) {
|
||||||
|
throw std::out_of_range("No parts");
|
||||||
|
}
|
||||||
|
start = found + 1;
|
||||||
|
number -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequestContext::get_full_url() {
|
||||||
|
return full_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestContext::is_valid_url() {
|
||||||
|
return valid_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestContext::has_range() {
|
||||||
|
return accept_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int> RequestContext::get_range() {
|
||||||
|
return range_pair;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string RequestContext::get_argument(const std::string& name) {
|
||||||
|
return arguments.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
unsigned int RequestContext::get_argument(const std::string& name) {
|
||||||
|
return std::stoi(arguments.at(name).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
float RequestContext::get_argument(const std::string& name) {
|
||||||
|
return std::stof(arguments.at(name).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequestContext::get_header(const std::string& name) {
|
||||||
|
return headers.at(name);
|
||||||
|
}
|
100
src/server/request_context.h
Normal file
100
src/server/request_context.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2016 Emmanuel Engelhart <kelson@kiwix.org>
|
||||||
|
* Copyright 2017 Matthieu Gautier<mgautier@kymeria.fr>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef REQUEST_CONTEXT_H
|
||||||
|
#define REQUEST_CONTEXT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <microhttpd.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RequestMethod {
|
||||||
|
GET,
|
||||||
|
HEAD,
|
||||||
|
POST,
|
||||||
|
PUT,
|
||||||
|
DELETE_,
|
||||||
|
CONNECT,
|
||||||
|
OPTIONS,
|
||||||
|
TRACE,
|
||||||
|
PATCH,
|
||||||
|
OTHER
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyError : public std::runtime_error {};
|
||||||
|
class IndexError: public std::runtime_error {};
|
||||||
|
|
||||||
|
|
||||||
|
class RequestContext {
|
||||||
|
public:
|
||||||
|
RequestContext(struct MHD_Connection* connection,
|
||||||
|
std::string rootLocation,
|
||||||
|
const std::string& url,
|
||||||
|
const std::string& method,
|
||||||
|
const std::string& version);
|
||||||
|
~RequestContext();
|
||||||
|
|
||||||
|
void print_debug_info();
|
||||||
|
|
||||||
|
bool is_valid_url();
|
||||||
|
|
||||||
|
std::string get_header(const std::string& name);
|
||||||
|
template<typename T=std::string>
|
||||||
|
T get_argument(const std::string& name);
|
||||||
|
|
||||||
|
RequestMethod get_method();
|
||||||
|
std::string get_url();
|
||||||
|
std::string get_url_part(int part);
|
||||||
|
std::string get_full_url();
|
||||||
|
|
||||||
|
bool has_range();
|
||||||
|
std::pair<int, int> get_range();
|
||||||
|
|
||||||
|
bool can_compress() { return acceptEncodingDeflate; }
|
||||||
|
|
||||||
|
// [TODO] Move this to the response builder
|
||||||
|
int httpResponseCode;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct MHD_Connection* connection;
|
||||||
|
std::string full_url;
|
||||||
|
std::string url;
|
||||||
|
bool valid_url;
|
||||||
|
RequestMethod method;
|
||||||
|
std::string version;
|
||||||
|
|
||||||
|
bool acceptEncodingDeflate;
|
||||||
|
|
||||||
|
bool accept_range;
|
||||||
|
std::pair<int, int> range_pair;
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
std::map<std::string, std::string> arguments;
|
||||||
|
|
||||||
|
static int fill_header(void *, enum MHD_ValueKind, const char*, const char*);
|
||||||
|
static int fill_argument(void *, enum MHD_ValueKind, const char*, const char*);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //REQUEST_CONTEXT_H
|
Loading…
x
Reference in New Issue
Block a user