From 09e2039aff90197209c8ac4a59227514236231d8 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Wed, 22 Nov 2017 11:52:46 +0000 Subject: [PATCH 1/4] Rename variable request_context to request. --- src/server/kiwix-serve.cpp | 134 ++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/src/server/kiwix-serve.cpp b/src/server/kiwix-serve.cpp index 232adaf..ecd3198 100644 --- a/src/server/kiwix-serve.cpp +++ b/src/server/kiwix-serve.cpp @@ -285,18 +285,18 @@ 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) { std::string content = "\nContent not found

Not " "Found

The requested URL \"" - + request_context->urlStr + "\" was not found on this server.

"; + + request->urlStr + "\" was not found on this server.

"; auto mimeType = "text/html"; - request_context->httpResponseCode = MHD_HTTP_NOT_FOUND; - introduceTaskbar(content, request_context->humanReadableBookId); + request->httpResponseCode = MHD_HTTP_NOT_FOUND; + introduceTaskbar(content, request->humanReadableBookId); bool deflated - = request_context->acceptEncodingDeflate && compress_content(content, mimeType); + = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, false); } @@ -380,7 +380,7 @@ static struct MHD_Response* build_callback_response_from_article( return response; } -static struct MHD_Response* handle_suggest(RequestContext* request_context) +static struct MHD_Response* handle_suggest(RequestContext* request) { std::string content; std::string mimeType; @@ -390,7 +390,7 @@ static struct MHD_Response* handle_suggest(RequestContext* request_context) /* Get the suggestion pattern from the HTTP request */ const char* cTerm = MHD_lookup_connection_value( - request_context->connection, MHD_GET_ARGUMENT_KIND, "term"); + request->connection, MHD_GET_ARGUMENT_KIND, "term"); std::string term = cTerm == NULL ? "" : cTerm; if (isVerbose.load()) { printf("Searching suggestions for: \"%s\"\n", term.c_str()); @@ -399,10 +399,10 @@ static struct MHD_Response* handle_suggest(RequestContext* request_context) pthread_mutex_lock(&searchLock); /* Get the suggestions */ content = "["; - if (request_context->reader != NULL) { + if (request->reader != NULL) { /* Get the suggestions */ - request_context->reader->searchSuggestionsSmart(term, maxSuggestionCount); - while (request_context->reader->getNextSuggestion(suggestion)) { + request->reader->searchSuggestionsSmart(term, maxSuggestionCount); + while (request->reader->getNextSuggestion(suggestion)) { kiwix::stringReplacement(suggestion, "\"", "\\\""); content += (content == "[" ? "" : ","); content += "{\"value\":\"" + suggestion + "\",\"label\":\"" + suggestion @@ -413,7 +413,7 @@ static struct MHD_Response* handle_suggest(RequestContext* request_context) pthread_mutex_unlock(&searchLock); /* Propose the fulltext search if possible */ - if (request_context->searcher != NULL) { + if (request->searcher != NULL) { content += (suggestionCount == 0 ? "" : ","); content += "{\"value\":\"" + std::string(term) + " \", \"label\":\"containing '" + std::string(term) @@ -422,26 +422,26 @@ static struct MHD_Response* handle_suggest(RequestContext* request_context) content += "]"; mimeType = "application/json; charset=utf-8"; - bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType); + bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response( 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; try { - content = getResource(request_context->urlStr.substr(rootLocation.size() + 6)); + content = getResource(request->urlStr.substr(rootLocation.size() + 6)); } catch (const ResourceNotFound& e) { - return build_404(request_context); + return build_404(request); } - std::string mimeType = getMimeTypeForFile(request_context->urlStr); - bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType); + std::string mimeType = getMimeTypeForFile(request->urlStr); + bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, true); } -static struct MHD_Response* handle_search(RequestContext* request_context) +static struct MHD_Response* handle_search(RequestContext* request) { std::string content; std::string mimeType; @@ -449,7 +449,7 @@ static struct MHD_Response* handle_search(RequestContext* request_context) /* Retrieve the pattern to search */ const char* pattern = MHD_lookup_connection_value( - request_context->connection, MHD_GET_ARGUMENT_KIND, "pattern"); + request->connection, MHD_GET_ARGUMENT_KIND, "pattern"); std::string patternString = kiwix::urlDecode(pattern == NULL ? "" : string(pattern)); @@ -457,11 +457,11 @@ static struct MHD_Response* handle_search(RequestContext* request_context) /* Retrive geo search */ bool has_geo_query = false; const char* latitude = MHD_lookup_connection_value( - request_context->connection, MHD_GET_ARGUMENT_KIND, "latitude"); + request->connection, MHD_GET_ARGUMENT_KIND, "latitude"); const char* longitude = MHD_lookup_connection_value( - request_context->connection, MHD_GET_ARGUMENT_KIND, "longitude"); + request->connection, MHD_GET_ARGUMENT_KIND, "longitude"); const char* distance = MHD_lookup_connection_value( - request_context->connection, MHD_GET_ARGUMENT_KIND, "distance"); + request->connection, MHD_GET_ARGUMENT_KIND, "distance"); float latitudeFloat(0), longitudeFloat(0), distanceFloat(0); if (latitude != nullptr && longitude != nullptr && distance != nullptr) { @@ -476,35 +476,35 @@ static struct MHD_Response* handle_search(RequestContext* request_context) /* Search results for searches from the welcome page should not be cached */ - bool cacheEnabled = !(request_context->searcher == globalSearcher); + bool cacheEnabled = !(request->searcher == globalSearcher); std::string patternCorrespondingUrl; /* Try first to load directly the article */ - if (request_context->reader != NULL) { - std::vector variants = request_context->reader->getTitleVariants(patternString); + if (request->reader != NULL) { + std::vector variants = request->reader->getTitleVariants(patternString); std::vector::iterator variantsItr = variants.begin(); while (patternCorrespondingUrl.empty() && variantsItr != variants.end()) { - request_context->reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl); + request->reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl); variantsItr++; } /* If article found then redirect directly to it */ if (!patternCorrespondingUrl.empty()) { httpRedirection - = rootLocation + "/" + request_context->humanReadableBookId + "/" + patternCorrespondingUrl; - request_context->httpResponseCode = MHD_HTTP_FOUND; + = rootLocation + "/" + request->humanReadableBookId + "/" + patternCorrespondingUrl; + request->httpResponseCode = MHD_HTTP_FOUND; return build_response("", 0, httpRedirection, "", false, true); } } /* Make the search */ - if (request_context->searcher != NULL) { + if (request->searcher != NULL) { const char* start = MHD_lookup_connection_value( - request_context->connection, MHD_GET_ARGUMENT_KIND, "start"); + request->connection, MHD_GET_ARGUMENT_KIND, "start"); const char* end = MHD_lookup_connection_value( - request_context->connection, MHD_GET_ARGUMENT_KIND, "end"); + request->connection, MHD_GET_ARGUMENT_KIND, "end"); unsigned int startNumber = start != NULL ? atoi(start) : 0; unsigned int endNumber = end != NULL ? atoi(end) : 25; @@ -512,27 +512,27 @@ static struct MHD_Response* handle_search(RequestContext* request_context) pthread_mutex_lock(&searchLock); try { if (patternString.empty() && has_geo_query) { - request_context->searcher->geo_search(latitudeFloat, longitudeFloat, distanceFloat, + request->searcher->geo_search(latitudeFloat, longitudeFloat, distanceFloat, startNumber, endNumber, isVerbose.load()); } else { - request_context->searcher->search(patternString, + request->searcher->search(patternString, startNumber, endNumber, isVerbose.load()); } - content = request_context->searcher->getHtml(); + content = request->searcher->getHtml(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } pthread_mutex_unlock(&searchLock); } else { content = "\nFulltext search unavailable

Not Found

There is no article with the title \"" + kiwix::encodeDiples(patternString) + "\" and the fulltext search engine is not available for this content.

"; - request_context->httpResponseCode = MHD_HTTP_NOT_FOUND; + request->httpResponseCode = MHD_HTTP_NOT_FOUND; } mimeType = "text/html; charset=utf-8"; - introduceTaskbar(content, request_context->humanReadableBookId); + introduceTaskbar(content, request->humanReadableBookId); - bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType); + bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response(content.data(), content.size(), httpRedirection, @@ -541,19 +541,19 @@ static struct MHD_Response* handle_search(RequestContext* request_context) cacheEnabled); } -static struct MHD_Response* handle_random(RequestContext* request_context) +static struct MHD_Response* handle_random(RequestContext* request) { std::string httpRedirection; - request_context->httpResponseCode = MHD_HTTP_FOUND; - if (request_context->reader != NULL) { - std::string randomUrl = request_context->reader->getRandomPageUrl(); + request->httpResponseCode = MHD_HTTP_FOUND; + if (request->reader != NULL) { + std::string randomUrl = request->reader->getRandomPageUrl(); httpRedirection - = rootLocation + "/" + request_context->humanReadableBookId + "/" + kiwix::urlEncode(randomUrl); + = rootLocation + "/" + request->humanReadableBookId + "/" + kiwix::urlEncode(randomUrl); } return build_response("", 0, httpRedirection, "", false, false); } -static struct MHD_Response* handle_content(RequestContext* request_context) +static struct MHD_Response* handle_content(RequestContext* request) { std::string baseUrl; std::string content; @@ -562,7 +562,7 @@ static struct MHD_Response* handle_content(RequestContext* request_context) bool found = false; zim::Article article; try { - found = request_context->reader->getArticleObjectByDecodedUrl(request_context->urlStr, article); + found = request->reader->getArticleObjectByDecodedUrl(context->urlStr, article); if (found) { /* If redirect */ @@ -582,9 +582,9 @@ static struct MHD_Response* handle_content(RequestContext* request_context) if (!found) { if (isVerbose.load()) - printf("Failed to find %s\n", request_context->urlStr.c_str()); + printf("Failed to find %s\n", request->urlStr.c_str()); - return build_404(request_context); + return build_404(request); } try { @@ -594,7 +594,7 @@ static struct MHD_Response* handle_content(RequestContext* request_context) } if (isVerbose.load()) { - printf("Found %s\n", request_context->urlStr.c_str()); + printf("Found %s\n", request->urlStr.c_str()); printf("mimeType: %s\n", mimeType.c_str()); } @@ -611,51 +611,51 @@ static struct MHD_Response* handle_content(RequestContext* request_context) + article.getUrl(); pthread_mutex_lock(®exLock); content = replaceRegex(content, - "$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/", + "$1$2" + rootLocation + "/" + request->humanReadableBookId + "/$3/", "(href|src)(=[\"|\']{0,1})/([A-Z|\\-])/"); content = replaceRegex(content, - "$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/", + "$1$2" + rootLocation + "/" + request->humanReadableBookId + "/$3/", "(@import[ ]+)([\"|\']{0,1})/([A-Z|\\-])/"); content = replaceRegex( content, - "humanReadableBookId + baseUrl + "\" />", + "humanReadableBookId + baseUrl + "\" />", ""); pthread_mutex_unlock(®exLock); - introduceTaskbar(content, request_context->humanReadableBookId); + introduceTaskbar(content, request->humanReadableBookId); } else if (mimeType.find("text/css") != string::npos) { pthread_mutex_lock(®exLock); content = replaceRegex(content, - "$1$2" + rootLocation + "/" + request_context->humanReadableBookId + "/$3/", + "$1$2" + rootLocation + "/" + request->humanReadableBookId + "/$3/", "(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/"); pthread_mutex_unlock(®exLock); } bool deflated - = request_context->acceptEncodingDeflate && compress_content(content, mimeType); + = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, true); } else { int range_len; - if (request_context->range_end == -1) { - range_len = article.getArticleSize() - request_context->range_start; + if (request->range_end == -1) { + range_len = article.getArticleSize() - request->range_start; } else { - range_len = request_context->range_end - request_context->range_start; + range_len = request->range_end - request->range_start; } return build_callback_response_from_article( article, - request_context->range_start, + request->range_start, range_len, mimeType); } } -static struct MHD_Response* handle_default(RequestContext* request_context) +static struct MHD_Response* handle_default(RequestContext* request) { std::string content = welcomeHTML; std::string mimeType = "text/html; charset=utf-8"; - bool deflated = request_context->acceptEncodingDeflate && compress_content(content, mimeType); + bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, false); } @@ -769,7 +769,7 @@ static int accessHandlerCallback(void* cls, humanReadableBookId = ""; } - RequestContext request_context(connection, httpResponseCode, + RequestContext request(connection, httpResponseCode, reader, searcher, urlStr, humanReadableBookId, acceptEncodingDeflate, @@ -778,36 +778,36 @@ static int accessHandlerCallback(void* cls, /* Get suggestions */ if ((urlStr == (rootLocation + "/" + "suggest")) && reader != NULL) { - response = handle_suggest(&request_context); + response = handle_suggest(&request); } /* Get static skin stuff */ else if (urlStr.size() > rootLocation.size() + 5 && urlStr.substr(rootLocation.size() , 6) == "/skin/") { - response = handle_skin(&request_context); + response = handle_skin(&request); } /* Display the search restults */ else if (urlStr == (rootLocation + "/" + "search")) { - response = handle_search(&request_context); + response = handle_search(&request); } /* Display a random article */ else if (urlStr == (rootLocation + "/" + "random")) { - response = handle_random(&request_context); + response = handle_random(&request); } /* Display the content of a ZIM content (article, image, ...) */ else if (reader != NULL) { - response = handle_content(&request_context); + response = handle_content(&request); } /* Display the global Welcome page */ else { - response = handle_default(&request_context); + response = handle_default(&request); } /* Queue the response */ - int ret = MHD_queue_response(connection, request_context.httpResponseCode, response); + int ret = MHD_queue_response(connection, request.httpResponseCode, response); MHD_destroy_response(response); return ret; From c93617473d43fa073b878322f59da8d1a6be6150 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Wed, 22 Nov 2017 15:53:34 +0000 Subject: [PATCH 2/4] Move some variable out of the request's context. `humanReadableBookId`, `reader` and `searcher` are not so global that it seems. Let's move it out of the request object as handler may or not use them. --- src/server/kiwix-serve.cpp | 95 +++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/src/server/kiwix-serve.cpp b/src/server/kiwix-serve.cpp index ecd3198..3bad96d 100644 --- a/src/server/kiwix-serve.cpp +++ b/src/server/kiwix-serve.cpp @@ -127,26 +127,19 @@ static std::string getMimeTypeForFile(const std::string& filename) struct 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; RequestContext(struct MHD_Connection* connection, int httpResponseCode, - kiwix::Reader* reader, kiwix::Searcher* searcher, - const std::string& urlStr, const std::string& humanReadableBookId, + const std::string& urlStr, 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), @@ -285,7 +278,8 @@ static struct MHD_Response* build_response(const void* data, } -static struct MHD_Response* build_404(RequestContext* request) { +static struct MHD_Response* build_404(RequestContext* request, + const std::string& humanReadableBookId) { std::string content = "\nurlStr + "\" was not found on this server.

"; auto mimeType = "text/html"; request->httpResponseCode = MHD_HTTP_NOT_FOUND; - introduceTaskbar(content, request->humanReadableBookId); + introduceTaskbar(content, humanReadableBookId); bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response( @@ -380,7 +374,9 @@ static struct MHD_Response* build_callback_response_from_article( return response; } -static struct MHD_Response* handle_suggest(RequestContext* request) +static struct MHD_Response* handle_suggest(RequestContext* request, + const std::string& humanReadableBookId, + kiwix::Reader* reader, kiwix::Searcher* searcher) { std::string content; std::string mimeType; @@ -399,10 +395,10 @@ static struct MHD_Response* handle_suggest(RequestContext* request) pthread_mutex_lock(&searchLock); /* Get the suggestions */ content = "["; - if (request->reader != NULL) { + if (reader != nullptr) { /* Get the suggestions */ - request->reader->searchSuggestionsSmart(term, maxSuggestionCount); - while (request->reader->getNextSuggestion(suggestion)) { + reader->searchSuggestionsSmart(term, maxSuggestionCount); + while (reader->getNextSuggestion(suggestion)) { kiwix::stringReplacement(suggestion, "\"", "\\\""); content += (content == "[" ? "" : ","); content += "{\"value\":\"" + suggestion + "\",\"label\":\"" + suggestion @@ -413,7 +409,7 @@ static struct MHD_Response* handle_suggest(RequestContext* request) pthread_mutex_unlock(&searchLock); /* Propose the fulltext search if possible */ - if (request->searcher != NULL) { + if (searcher != NULL) { content += (suggestionCount == 0 ? "" : ","); content += "{\"value\":\"" + std::string(term) + " \", \"label\":\"containing '" + std::string(term) @@ -433,7 +429,7 @@ static struct MHD_Response* handle_skin(RequestContext* request) try { content = getResource(request->urlStr.substr(rootLocation.size() + 6)); } catch (const ResourceNotFound& e) { - return build_404(request); + return build_404(request, ""); } std::string mimeType = getMimeTypeForFile(request->urlStr); bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); @@ -441,7 +437,9 @@ static struct MHD_Response* handle_skin(RequestContext* request) content.data(), content.size(), "", mimeType, deflated, true); } -static struct MHD_Response* handle_search(RequestContext* request) +static struct MHD_Response* handle_search(RequestContext* request, + const std::string& humanReadableBookId, + kiwix::Reader* reader, kiwix::Searcher* searcher) { std::string content; std::string mimeType; @@ -476,31 +474,31 @@ static struct MHD_Response* handle_search(RequestContext* request) /* Search results for searches from the welcome page should not be cached */ - bool cacheEnabled = !(request->searcher == globalSearcher); + bool cacheEnabled = !(searcher == globalSearcher); std::string patternCorrespondingUrl; /* Try first to load directly the article */ - if (request->reader != NULL) { - std::vector variants = request->reader->getTitleVariants(patternString); + if (reader != nullptr) { + std::vector variants = reader->getTitleVariants(patternString); std::vector::iterator variantsItr = variants.begin(); while (patternCorrespondingUrl.empty() && variantsItr != variants.end()) { - request->reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl); + reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl); variantsItr++; } /* If article found then redirect directly to it */ if (!patternCorrespondingUrl.empty()) { httpRedirection - = rootLocation + "/" + request->humanReadableBookId + "/" + patternCorrespondingUrl; + = rootLocation + "/" + humanReadableBookId + "/" + patternCorrespondingUrl; request->httpResponseCode = MHD_HTTP_FOUND; return build_response("", 0, httpRedirection, "", false, true); } } /* Make the search */ - if (request->searcher != NULL) { + if (searcher != nullptr) { const char* start = MHD_lookup_connection_value( request->connection, MHD_GET_ARGUMENT_KIND, "start"); const char* end = MHD_lookup_connection_value( @@ -512,13 +510,13 @@ static struct MHD_Response* handle_search(RequestContext* request) pthread_mutex_lock(&searchLock); try { if (patternString.empty() && has_geo_query) { - request->searcher->geo_search(latitudeFloat, longitudeFloat, distanceFloat, - startNumber, endNumber, isVerbose.load()); + searcher->geo_search(latitudeFloat, longitudeFloat, distanceFloat, + startNumber, endNumber, isVerbose.load()); } else { - request->searcher->search(patternString, - startNumber, endNumber, isVerbose.load()); + searcher->search(patternString, + startNumber, endNumber, isVerbose.load()); } - content = request->searcher->getHtml(); + content = searcher->getHtml(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } @@ -530,7 +528,7 @@ static struct MHD_Response* handle_search(RequestContext* request) mimeType = "text/html; charset=utf-8"; - introduceTaskbar(content, request->humanReadableBookId); + introduceTaskbar(content, humanReadableBookId); bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); return build_response(content.data(), @@ -541,19 +539,23 @@ static struct MHD_Response* handle_search(RequestContext* request) cacheEnabled); } -static struct MHD_Response* handle_random(RequestContext* request) +static struct MHD_Response* handle_random(RequestContext* request, + const std::string& humanReadableBookId, + kiwix::Reader* reader, kiwix::Searcher* searcher) { std::string httpRedirection; request->httpResponseCode = MHD_HTTP_FOUND; - if (request->reader != NULL) { - std::string randomUrl = request->reader->getRandomPageUrl(); + if (reader != nullptr) { + std::string randomUrl = reader->getRandomPageUrl(); httpRedirection - = rootLocation + "/" + request->humanReadableBookId + "/" + kiwix::urlEncode(randomUrl); + = rootLocation + "/" + humanReadableBookId + "/" + kiwix::urlEncode(randomUrl); } return build_response("", 0, httpRedirection, "", false, false); } -static struct MHD_Response* handle_content(RequestContext* request) +static struct MHD_Response* handle_content(RequestContext* request, + const std::string& humanReadableBookId, + kiwix::Reader* reader, kiwix::Searcher* searcher) { std::string baseUrl; std::string content; @@ -562,7 +564,7 @@ static struct MHD_Response* handle_content(RequestContext* request) bool found = false; zim::Article article; try { - found = request->reader->getArticleObjectByDecodedUrl(context->urlStr, article); + found = reader->getArticleObjectByDecodedUrl(request->urlStr, article); if (found) { /* If redirect */ @@ -584,7 +586,7 @@ static struct MHD_Response* handle_content(RequestContext* request) if (isVerbose.load()) printf("Failed to find %s\n", request->urlStr.c_str()); - return build_404(request); + return build_404(request, humanReadableBookId); } try { @@ -611,21 +613,21 @@ static struct MHD_Response* handle_content(RequestContext* request) + article.getUrl(); pthread_mutex_lock(®exLock); content = replaceRegex(content, - "$1$2" + rootLocation + "/" + request->humanReadableBookId + "/$3/", + "$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/", "(href|src)(=[\"|\']{0,1})/([A-Z|\\-])/"); content = replaceRegex(content, - "$1$2" + rootLocation + "/" + request->humanReadableBookId + "/$3/", + "$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/", "(@import[ ]+)([\"|\']{0,1})/([A-Z|\\-])/"); content = replaceRegex( content, - "humanReadableBookId + baseUrl + "\" />", + "", ""); pthread_mutex_unlock(®exLock); - introduceTaskbar(content, request->humanReadableBookId); + introduceTaskbar(content, humanReadableBookId); } else if (mimeType.find("text/css") != string::npos) { pthread_mutex_lock(®exLock); content = replaceRegex(content, - "$1$2" + rootLocation + "/" + request->humanReadableBookId + "/$3/", + "$1$2" + rootLocation + "/" + humanReadableBookId + "/$3/", "(url|URL)(\\([\"|\']{0,1})/([A-Z|\\-])/"); pthread_mutex_unlock(®exLock); } @@ -770,15 +772,14 @@ static int accessHandlerCallback(void* cls, } RequestContext request(connection, httpResponseCode, - reader, searcher, - urlStr, humanReadableBookId, + urlStr, acceptEncodingDeflate, acceptRange, range_start, range_end); /* Get suggestions */ if ((urlStr == (rootLocation + "/" + "suggest")) && reader != NULL) { - response = handle_suggest(&request); + response = handle_suggest(&request, humanReadableBookId, reader, searcher); } /* Get static skin stuff */ @@ -788,17 +789,17 @@ static int accessHandlerCallback(void* cls, /* Display the search restults */ else if (urlStr == (rootLocation + "/" + "search")) { - response = handle_search(&request); + response = handle_search(&request, humanReadableBookId, reader, searcher); } /* Display a random article */ else if (urlStr == (rootLocation + "/" + "random")) { - response = handle_random(&request); + response = handle_random(&request, humanReadableBookId, reader, searcher); } /* Display the content of a ZIM content (article, image, ...) */ else if (reader != NULL) { - response = handle_content(&request); + response = handle_content(&request, humanReadableBookId, reader, searcher); } /* Display the global Welcome page */ From 234a9d971993ac4ffbc6a3c660219228377cfc41 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Wed, 22 Nov 2017 16:16:54 +0000 Subject: [PATCH 3/4] Let the different handlers look for the reader/searcher. It is to the handlers to get the right reader/searcher they want and create correct error/default page if the name doesn't correspond to a zim file. The `handle_default` function has also be renamed to `build_homepage` because it is what is done. (And it doesn't handle a request). --- src/server/kiwix-serve.cpp | 97 +++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/src/server/kiwix-serve.cpp b/src/server/kiwix-serve.cpp index 3bad96d..3302c4f 100644 --- a/src/server/kiwix-serve.cpp +++ b/src/server/kiwix-serve.cpp @@ -295,6 +295,17 @@ static struct MHD_Response* build_404(RequestContext* request, 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->acceptEncodingDeflate && compress_content(content, mimeType); + return build_response( + content.data(), content.size(), "", mimeType, deflated, false); +} + struct RunningResponse { zim::Article* article; int range_start; @@ -374,9 +385,20 @@ static struct MHD_Response* build_callback_response_from_article( return response; } +std::pair +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(reader, searcher); +} + static struct MHD_Response* handle_suggest(RequestContext* request, - const std::string& humanReadableBookId, - kiwix::Reader* reader, kiwix::Searcher* searcher) + const std::string& humanReadableBookId) { std::string content; std::string mimeType; @@ -392,6 +414,10 @@ static struct MHD_Response* handle_suggest(RequestContext* request, 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); /* Get the suggestions */ content = "["; @@ -438,8 +464,7 @@ static struct MHD_Response* handle_skin(RequestContext* request) } static struct MHD_Response* handle_search(RequestContext* request, - const std::string& humanReadableBookId, - kiwix::Reader* reader, kiwix::Searcher* searcher) + const std::string& humanReadableBookId) { std::string content; std::string mimeType; @@ -474,6 +499,9 @@ static struct MHD_Response* handle_search(RequestContext* request, /* Search results for searches from the welcome page should not be cached */ + auto reader_searcher = get_from_humanReadableBookId(humanReadableBookId); + auto reader = reader_searcher.first; + auto searcher = reader_searcher.second; bool cacheEnabled = !(searcher == globalSearcher); std::string patternCorrespondingUrl; @@ -540,22 +568,24 @@ static struct MHD_Response* handle_search(RequestContext* request, } static struct MHD_Response* handle_random(RequestContext* request, - const std::string& humanReadableBookId, - kiwix::Reader* reader, kiwix::Searcher* searcher) + const std::string& humanReadableBookId) { std::string httpRedirection; request->httpResponseCode = MHD_HTTP_FOUND; - if (reader != nullptr) { - std::string randomUrl = reader->getRandomPageUrl(); - httpRedirection - = rootLocation + "/" + humanReadableBookId + "/" + kiwix::urlEncode(randomUrl); + + 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); } static struct MHD_Response* handle_content(RequestContext* request, - const std::string& humanReadableBookId, - kiwix::Reader* reader, kiwix::Searcher* searcher) + const std::string& humanReadableBookId) { std::string baseUrl; std::string content; @@ -563,6 +593,12 @@ static struct MHD_Response* handle_content(RequestContext* request, bool found = false; zim::Article article; + + auto reader = get_from_humanReadableBookId(humanReadableBookId).first; + if (reader == nullptr) { + return build_homepage(request); + } + try { found = reader->getArticleObjectByDecodedUrl(request->urlStr, article); @@ -651,17 +687,6 @@ static struct MHD_Response* handle_content(RequestContext* request, } } -static struct MHD_Response* handle_default(RequestContext* request) -{ - std::string content = welcomeHTML; - - std::string mimeType = "text/html; charset=utf-8"; - - bool deflated = request->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) { @@ -760,17 +785,6 @@ static int accessHandlerCallback(void* cls, } } - 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(connection, httpResponseCode, urlStr, acceptEncodingDeflate, @@ -778,8 +792,8 @@ static int accessHandlerCallback(void* cls, /* Get suggestions */ - if ((urlStr == (rootLocation + "/" + "suggest")) && reader != NULL) { - response = handle_suggest(&request, humanReadableBookId, reader, searcher); + if (urlStr == (rootLocation + "/" + "suggest")) { + response = handle_suggest(&request, humanReadableBookId); } /* Get static skin stuff */ @@ -789,22 +803,17 @@ static int accessHandlerCallback(void* cls, /* Display the search restults */ else if (urlStr == (rootLocation + "/" + "search")) { - response = handle_search(&request, humanReadableBookId, reader, searcher); + response = handle_search(&request, humanReadableBookId); } /* Display a random article */ else if (urlStr == (rootLocation + "/" + "random")) { - response = handle_random(&request, humanReadableBookId, reader, searcher); + response = handle_random(&request, humanReadableBookId); } /* Display the content of a ZIM content (article, image, ...) */ - else if (reader != NULL) { - response = handle_content(&request, humanReadableBookId, reader, searcher); - } - - /* Display the global Welcome page */ else { - response = handle_default(&request); + response = handle_content(&request, humanReadableBookId); } /* Queue the response */ From a8e73aab98e2e685aff460c94145ab51469afb6d Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Wed, 22 Nov 2017 16:53:05 +0000 Subject: [PATCH 4/4] Move the request context out of kiwix-serve.cpp. RequestContext is now a "complex" class that handle a lot of thing for the handlers : - The rootLocation is correctly handle and remove from the url. So a handler doesn't have to care about it (when parsing the url) - Request arguments and header are stored in a map and there are access methods to get it. - Request arguments can be automatically convert to other type than string. - Better parsing of the `byte` header. Related to #91. --- src/server/kiwix-serve.cpp | 327 ++++++++++++++------------------- src/server/meson.build | 2 +- src/server/request_context.cpp | 213 +++++++++++++++++++++ src/server/request_context.h | 100 ++++++++++ 4 files changed, 450 insertions(+), 192 deletions(-) create mode 100644 src/server/request_context.cpp create mode 100644 src/server/request_context.h diff --git a/src/server/kiwix-serve.cpp b/src/server/kiwix-serve.cpp index 3302c4f..ca76026 100644 --- a/src/server/kiwix-serve.cpp +++ b/src/server/kiwix-serve.cpp @@ -81,6 +81,8 @@ extern "C" { #include #endif +#include "request_context.h" + #ifdef interface #undef interface #endif @@ -124,28 +126,11 @@ static std::string getMimeTypeForFile(const std::string& filename) } -struct RequestContext { - struct MHD_Connection* connection; - int httpResponseCode; - const std::string urlStr; - bool acceptEncodingDeflate; - bool acceptRange; - int range_start; - int range_end; - - RequestContext(struct MHD_Connection* connection, int httpResponseCode, - const std::string& urlStr, - bool acceptEncodingDeflate, - bool acceptRange, int range_start, int range_end) : - connection(connection), - httpResponseCode(httpResponseCode), - urlStr(urlStr), - acceptEncodingDeflate(acceptEncodingDeflate), - acceptRange(acceptRange), - range_start(range_start), - range_end(range_end) - {} -}; +static bool startswith(const std::string& base, const std::string& start) +{ + return start.length() <= base.length() + && std::equal(start.begin(), start.end(), base.begin()); +} void introduceTaskbar(string& content, const string& humanReadableBookId) @@ -285,12 +270,12 @@ static struct MHD_Response* build_404(RequestContext* request, "content=\"text/html;charset=UTF-8\" http-equiv=\"content-type\" " "/>Content not found

Not " "Found

The requested URL \"" - + request->urlStr + "\" was not found on this server.

"; + + request->get_full_url() + "\" was not found on this server.

"; auto mimeType = "text/html"; request->httpResponseCode = MHD_HTTP_NOT_FOUND; introduceTaskbar(content, humanReadableBookId); bool deflated - = request->acceptEncodingDeflate && compress_content(content, mimeType); + = request->can_compress() && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, false); } @@ -301,7 +286,7 @@ static struct MHD_Response* build_homepage(RequestContext* request) std::string mimeType = "text/html; charset=utf-8"; - bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); + bool deflated = request->can_compress() && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, false); } @@ -397,19 +382,27 @@ get_from_humanReadableBookId(const std::string& humanReadableBookId) { return std::pair(reader, searcher); } -static struct MHD_Response* handle_suggest(RequestContext* request, - const std::string& humanReadableBookId) +static struct MHD_Response* handle_suggest(RequestContext* request) { + if (isVerbose.load()) { + printf("** running handle_suggest\n"); + } + std::string content; std::string mimeType; unsigned int maxSuggestionCount = 10; unsigned int suggestionCount = 0; std::string suggestion; - /* Get the suggestion pattern from the HTTP request */ - const char* cTerm = MHD_lookup_connection_value( - request->connection, MHD_GET_ARGUMENT_KIND, "term"); - std::string term = cTerm == NULL ? "" : cTerm; + std::string humanReadableBookId; + std::string term; + try { + humanReadableBookId = request->get_argument("content"); + term = request->get_argument("term"); + } catch (const std::out_of_range&) { + return build_homepage(request); + } + if (isVerbose.load()) { printf("Searching suggestions for: \"%s\"\n", term.c_str()); } @@ -437,64 +430,68 @@ static struct MHD_Response* handle_suggest(RequestContext* request, /* Propose the fulltext search if possible */ if (searcher != NULL) { content += (suggestionCount == 0 ? "" : ","); - content += "{\"value\":\"" + std::string(term) - + " \", \"label\":\"containing '" + std::string(term) + content += "{\"value\":\"" + term + + " \", \"label\":\"containing '" + term + "'...\"}"; } content += "]"; mimeType = "application/json; charset=utf-8"; - bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); + bool deflated = request->can_compress() && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, true); } static struct MHD_Response* handle_skin(RequestContext* request) { + if (isVerbose.load()) { + printf("** running handle_skin\n"); + } + std::string content; + auto resourceName = request->get_url().substr(6); try { - content = getResource(request->urlStr.substr(rootLocation.size() + 6)); + content = getResource(resourceName); } catch (const ResourceNotFound& e) { return build_404(request, ""); } - std::string mimeType = getMimeTypeForFile(request->urlStr); - bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); + std::string mimeType = getMimeTypeForFile(resourceName); + bool deflated = request->can_compress() && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, true); } -static struct MHD_Response* handle_search(RequestContext* request, - const std::string& humanReadableBookId) +static struct MHD_Response* handle_search(RequestContext* request) { + if (isVerbose.load()) { + printf("** running handle_search\n"); + } + std::string content; std::string mimeType; std::string httpRedirection; - /* Retrieve the pattern to search */ - const char* pattern = MHD_lookup_connection_value( - request->connection, MHD_GET_ARGUMENT_KIND, "pattern"); - std::string patternString - = kiwix::urlDecode(pattern == NULL ? "" : string(pattern)); - + std::string humanReadableBookId; + std::string patternString; + try { + humanReadableBookId = request->get_argument("content"); + patternString = request->get_argument("pattern"); + } catch (const std::out_of_range&) { + return build_homepage(request); + } /* Retrive geo search */ bool has_geo_query = false; - const char* latitude = MHD_lookup_connection_value( - request->connection, MHD_GET_ARGUMENT_KIND, "latitude"); - const char* longitude = MHD_lookup_connection_value( - request->connection, MHD_GET_ARGUMENT_KIND, "longitude"); - const char* distance = MHD_lookup_connection_value( - request->connection, MHD_GET_ARGUMENT_KIND, "distance"); - - float latitudeFloat(0), longitudeFloat(0), distanceFloat(0); - if (latitude != nullptr && longitude != nullptr && distance != nullptr) { - try { - latitudeFloat = stof(string(latitude)); - longitudeFloat = stof(string(longitude)); - distanceFloat = stof(string(distance)); - has_geo_query = true; - } catch (...) {} - } + float latitude; + float longitude; + float distance; + try { + latitude = request->get_argument("latitude"); + longitude = request->get_argument("longitude"); + distance = request->get_argument("distance"); + has_geo_query = true; + } catch(const std::out_of_range&) {} + catch(const std::invalid_argument&) {} /* Search results for searches from the welcome page should not be cached @@ -504,12 +501,11 @@ static struct MHD_Response* handle_search(RequestContext* request, auto searcher = reader_searcher.second; bool cacheEnabled = !(searcher == globalSearcher); - std::string patternCorrespondingUrl; - /* Try first to load directly the article */ if (reader != nullptr) { - std::vector variants = reader->getTitleVariants(patternString); - std::vector::iterator variantsItr = variants.begin(); + std::string patternCorrespondingUrl; + auto variants = reader->getTitleVariants(patternString); + auto variantsItr = variants.begin(); while (patternCorrespondingUrl.empty() && variantsItr != variants.end()) { reader->getPageUrlFromTitle(*variantsItr, patternCorrespondingUrl); @@ -526,23 +522,26 @@ static struct MHD_Response* handle_search(RequestContext* request, } /* Make the search */ - if (searcher != nullptr) { - const char* start = MHD_lookup_connection_value( - request->connection, MHD_GET_ARGUMENT_KIND, "start"); - const char* end = MHD_lookup_connection_value( - request->connection, MHD_GET_ARGUMENT_KIND, "end"); - unsigned int startNumber = start != NULL ? atoi(start) : 0; - unsigned int endNumber = end != NULL ? atoi(end) : 25; + if (reader_searcher.second != nullptr && + (!patternString.empty() || has_geo_query)) { + auto start = 0; + try { + start = request->get_argument("start"); + } catch (const std::exception&) {} + auto end = 25; + try { + end = request->get_argument("end"); + } catch (const std::exception&) {} /* Get the results */ pthread_mutex_lock(&searchLock); try { - if (patternString.empty() && has_geo_query) { - searcher->geo_search(latitudeFloat, longitudeFloat, distanceFloat, - startNumber, endNumber, isVerbose.load()); + if (patternString.empty()) { + searcher->geo_search(latitude, longitude, distance, + start, end, isVerbose.load()); } else { searcher->search(patternString, - startNumber, endNumber, isVerbose.load()); + start, end, isVerbose.load()); } content = searcher->getHtml(); } catch (const std::exception& e) { @@ -558,7 +557,7 @@ static struct MHD_Response* handle_search(RequestContext* request, introduceTaskbar(content, humanReadableBookId); - bool deflated = request->acceptEncodingDeflate && compress_content(content, mimeType); + bool deflated = request->can_compress() && compress_content(content, mimeType); return build_response(content.data(), content.size(), httpRedirection, @@ -567,11 +566,20 @@ static struct MHD_Response* handle_search(RequestContext* request, cacheEnabled); } -static struct MHD_Response* handle_random(RequestContext* request, - const std::string& humanReadableBookId) +static struct MHD_Response* handle_random(RequestContext* request) { + if (isVerbose.load()) { + printf("** running handle_random\n"); + } + 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) { @@ -584,9 +592,12 @@ static struct MHD_Response* handle_random(RequestContext* request, return build_response("", 0, httpRedirection, "", false, false); } -static struct MHD_Response* handle_content(RequestContext* request, - const std::string& humanReadableBookId) +static struct MHD_Response* handle_content(RequestContext* request) { + if (isVerbose.load()) { + printf("** running handle_content\n"); + } + std::string baseUrl; std::string content; std::string mimeType; @@ -594,13 +605,22 @@ static struct MHD_Response* handle_content(RequestContext* request, bool found = false; zim::Article article; + std::string humanReadableBookId; + try { + 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(request->urlStr, article); + found = reader->getArticleObjectByDecodedUrl(urlStr, article); if (found) { /* If redirect */ @@ -620,7 +640,7 @@ static struct MHD_Response* handle_content(RequestContext* request, if (!found) { if (isVerbose.load()) - printf("Failed to find %s\n", request->urlStr.c_str()); + printf("Failed to find %s\n", urlStr.c_str()); return build_404(request, humanReadableBookId); } @@ -632,7 +652,7 @@ static struct MHD_Response* handle_content(RequestContext* request, } if (isVerbose.load()) { - printf("Found %s\n", request->urlStr.c_str()); + printf("Found %s\n", urlStr.c_str()); printf("mimeType: %s\n", mimeType.c_str()); } @@ -669,28 +689,28 @@ static struct MHD_Response* handle_content(RequestContext* request, } bool deflated - = request->acceptEncodingDeflate && compress_content(content, mimeType); + = request->can_compress() && compress_content(content, mimeType); return build_response( content.data(), content.size(), "", mimeType, deflated, true); } else { int range_len; - if (request->range_end == -1) { - range_len = article.getArticleSize() - request->range_start; + if (request->get_range().second == -1) { + range_len = article.getArticleSize() - request->get_range().first; } else { - range_len = request->range_end - request->range_start; + range_len = request->get_range().second - request->get_range().first; } return build_callback_response_from_article( article, - request->range_start, + request->get_range().first, range_len, mimeType); } } -int print_out_key (void *cls, enum MHD_ValueKind kind, +int print_key_value (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { - printf ("%s: %s\n", key, value); + printf (" - %s: '%s'\n", key, value); return MHD_YES; } @@ -703,120 +723,45 @@ static int accessHandlerCallback(void* cls, size_t* upload_data_size, void** ptr) { + RequestContext request(connection, rootLocation, url, method, version); /* 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; - - 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 */ - const char* acceptEncodingHeaderValue = MHD_lookup_connection_value( - connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT_ENCODING); - 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; - } - } - } + if (isVerbose.load()) { + printf("======================\n"); + request.print_debug_info(); } /* Prepare the variables */ struct MHD_Response* response; - int httpResponseCode = acceptRange ? MHD_HTTP_PARTIAL_CONTENT : MHD_HTTP_OK; - std::string urlStr = string(url); + request.httpResponseCode = request.has_range() ? MHD_HTTP_PARTIAL_CONTENT : MHD_HTTP_OK; - /* Get searcher and reader */ - std::string humanReadableBookId = ""; - - if (!rootLocation.empty() && urlStr.substr(0, rootLocation.size() + 1) != rootLocation + "/"){ - humanReadableBookId = ""; - } - - else if (!(urlStr.size() > rootLocation.size() + 5 && urlStr.substr(rootLocation.size() , 6) == "/skin/")) { - if ((urlStr == rootLocation + "/" + "search") || (urlStr == rootLocation + "/" + "suggest") - || (urlStr == rootLocation + "/" + "random")) { - const char* tmpGetValue = MHD_lookup_connection_value( - connection, MHD_GET_ARGUMENT_KIND, "content"); - humanReadableBookId = (tmpGetValue != NULL ? string(tmpGetValue) : ""); + if (! request.is_valid_url()) { + response = build_homepage(&request); + } else { + if (startswith(request.get_url(), "/skin/")) { + response = handle_skin(&request); + } else if (request.get_url() == "/search") { + response = handle_search(&request); + } else if (request.get_url() == "/suggest") { + response = handle_suggest(&request); + } else if (request.get_url() == "/random") { + response = handle_random(&request); } else { - humanReadableBookId = urlStr.substr(rootLocation.size() + 1, - 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()); - } + response = handle_content(&request); } } - RequestContext request(connection, httpResponseCode, - urlStr, - acceptEncodingDeflate, - acceptRange, range_start, range_end); - - - /* Get suggestions */ - if (urlStr == (rootLocation + "/" + "suggest")) { - response = handle_suggest(&request, humanReadableBookId); - } - - /* Get static skin stuff */ - else if (urlStr.size() > rootLocation.size() + 5 && urlStr.substr(rootLocation.size() , 6) == "/skin/") { - response = handle_skin(&request); - } - - /* Display the search restults */ - else if (urlStr == (rootLocation + "/" + "search")) { - response = handle_search(&request, humanReadableBookId); - } - - /* Display a random article */ - else if (urlStr == (rootLocation + "/" + "random")) { - response = handle_random(&request, humanReadableBookId); - } - - /* Display the content of a ZIM content (article, image, ...) */ - else { - response = handle_content(&request, humanReadableBookId); - } - /* Queue the 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); diff --git a/src/server/meson.build b/src/server/meson.build index 78bfa97..f337656 100644 --- a/src/server/meson.build +++ b/src/server/meson.build @@ -1,5 +1,5 @@ -sources = ['kiwix-serve.cpp'] +sources = ['kiwix-serve.cpp', 'request_context.cpp'] sources += server_resources executable('kiwix-serve', sources, diff --git a/src/server/request_context.cpp b/src/server/request_context.cpp new file mode 100644 index 0000000..9caa1dc --- /dev/null +++ b/src/server/request_context.cpp @@ -0,0 +1,213 @@ +/* + * Copyright 2009-2016 Emmanuel Engelhart + * Copyright 2017 Matthieu Gautier + * + * 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 +#include +#include + +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(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(__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(__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 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); +} diff --git a/src/server/request_context.h b/src/server/request_context.h new file mode 100644 index 0000000..687fd84 --- /dev/null +++ b/src/server/request_context.h @@ -0,0 +1,100 @@ +/* + * Copyright 2009-2016 Emmanuel Engelhart + * Copyright 2017 Matthieu Gautier + * + * 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 +#include +#include + +extern "C" { +#include +} + +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 + 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 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 range_pair; + std::map headers; + std::map 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