Split accessHandlerCallback into several functions.

Instead of having a big callback function doing almost everything to
handle a request, we split the code into several functions.

There are two new helper functions :
 - build_response that create a response object with correct headers set.
 - compress_content who compress the content if necessary.

All the different cases are handle by different functions :
 - handle_suggest
 - handle_skin
 - handle_search
 - handle_random
 - handle_content
 - handle_default

accesHandlerCallback now handle common stuff, delegate to the handle_*
functions everything else.

There is no special optimization made here. Only splitting code.
This commit is contained in:
Matthieu Gautier 2016-10-07 11:41:41 +02:00
parent 1a5f21f1b6
commit d14460a666

View File

@ -144,79 +144,108 @@ bool isVerbose() {
static Bytef *compr = (Bytef *)malloc(COMPRESSOR_BUFFER_SIZE);
static uLongf comprLen;
static int accessHandlerCallback(void *cls,
struct MHD_Connection * connection,
const char * url,
const char * method,
const char * version,
const char * upload_data,
size_t * upload_data_size,
void ** ptr) {
/* Unexpected method */
if (0 != strcmp(method, "GET") && 0 != strcmp(method, "POST"))
return MHD_NO;
static
bool compress_content(string &content,
const string &mimeType)
{
/* Compute the lengh */
unsigned int contentLength = content.size();
/* The first time only the headers are valid, do not respond in the first round... */
static int dummy;
if (&dummy != *ptr) {
*ptr = &dummy;
return MHD_YES;
/* Should be deflate */
bool deflated =
contentLength > KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE &&
contentLength < COMPRESSOR_BUFFER_SIZE &&
(mimeType.find("text/") != string::npos ||
mimeType.find("application/javascript") != string::npos ||
mimeType.find("application/json") != string::npos);
/* Compress the content if necessary */
if (deflated) {
pthread_mutex_lock(&compressorLock);
comprLen = COMPRESSOR_BUFFER_SIZE;
compress(compr, &comprLen, (const Bytef*)(content.data()), contentLength);
if (comprLen > 2 && comprLen < contentLength) {
/* /!\ Internet Explorer has a bug with deflate compression.
It can not handle the first two bytes (compression headers)
We need to chunk them off (move the content 2bytes)
It has no incidence on other browsers
See http://www.subbu.org/blog/2008/03/ie7-deflate-or-not and comments */
compr += 2;
content = string((char *)compr, comprLen);
contentLength = comprLen;
} else {
deflated = false;
}
/* Debug */
if (isVerbose()) {
std::cout << "Requesting " << url << std::endl;
pthread_mutex_unlock(&compressorLock);
}
return deflated;
}
static
struct MHD_Response* build_response(const void* data,
unsigned int length,
const std::string& httpRedirection,
const std::string& mimeType,
bool deflated,
bool cacheEnabled)
{
/* Create the response */
struct MHD_Response * response = MHD_create_response_from_data(length,
const_cast<void*>(data),
MHD_NO,
MHD_YES);
/* Make a redirection if necessary otherwise send the content */
if (!httpRedirection.empty()) {
MHD_add_response_header(response, MHD_HTTP_HEADER_LOCATION, httpRedirection.c_str());
} else {
/* Add if necessary the content-encoding */
if (deflated) {
MHD_add_response_header(response, MHD_HTTP_HEADER_VARY, "Accept-Encoding");
MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, "deflate");
}
/* Check if the response can be compressed */
const string acceptEncodingHeaderValue = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT_ENCODING) ?
MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT_ENCODING) : "";
const bool acceptEncodingDeflate = !acceptEncodingHeaderValue.empty() && acceptEncodingHeaderValue.find("deflate") != string::npos;
/* Tell the client that byte ranges are accepted */
MHD_add_response_header(response, MHD_HTTP_HEADER_ACCEPT_RANGES, "bytes");
/* Check if range is requested */
const string acceptRangeHeaderValue = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE) ?
MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE) : "";
const bool acceptRange = !acceptRangeHeaderValue.empty();
/* Specify the mime type */
MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, mimeType.c_str());
}
/* Prepare the variables */
struct MHD_Response *response;
/* Force to close the connection - cf. 100% CPU usage with v. 4.4 (in Lucid) */
//MHD_add_response_header(response, MHD_HTTP_HEADER_CONNECTION, "close");
/* Allow cross-domain requests */
//MHD_add_response_header(response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
if (cacheEnabled) { /* Force cache */
MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, "max-age=2723040, public");
} else { /* Prevent cache (for random page) */
MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, "no-cache, no-store, must-revalidate");
}
return response;
}
static
struct MHD_Response* handle_suggest(struct MHD_Connection * connection,
int& httpResponseCode,
kiwix::Reader *reader,
kiwix::Searcher *searcher,
const std::string& urlStr,
const std::string& humanReadableBookId,
bool acceptEncodingDeflate)
{
std::string content;
std::string mimeType;
std::string httpRedirection;
unsigned int contentLength = 0;
bool cacheEnabled = true;
int httpResponseCode = MHD_HTTP_OK;
std::string urlStr = string(url);
/* Get searcher and reader */
std::string humanReadableBookId = "";
if (!(urlStr.size() > 5 && urlStr.substr(0, 6) == "/skin/")) {
if (!strcmp(url, "/search") || !strcmp(url, "/suggest") || !strcmp(url, "/random")) {
const char* tmpGetValue = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "content");
humanReadableBookId = (tmpGetValue != NULL ? string(tmpGetValue) : "");
} else {
humanReadableBookId = urlStr.substr(1, urlStr.find("/", 1) != string::npos ?
urlStr.find("/", 1) - 1 : urlStr.size() - 2);
if (!humanReadableBookId.empty()) {
urlStr = urlStr.substr(urlStr.find("/", 1) != string::npos ?
urlStr.find("/", 1) : humanReadableBookId.size());
}
}
}
pthread_mutex_lock(&mapLock);
kiwix::Searcher *searcher = searchers.find(humanReadableBookId) != searchers.end() ?
searchers.find(humanReadableBookId)->second : NULL;
kiwix::Reader *reader = readers.find(humanReadableBookId) != readers.end() ?
readers.find(humanReadableBookId)->second : NULL;
if (reader == NULL) {
humanReadableBookId="";
}
pthread_mutex_unlock(&mapLock);
/* Get suggestions */
if (!strcmp(url, "/suggest") && reader != NULL) {
unsigned int maxSuggestionCount = 10;
unsigned int suggestionCount = 0;
std::string suggestion;
@ -246,16 +275,37 @@ static int accessHandlerCallback(void *cls,
content += "]";
mimeType = "application/json; charset=utf-8";
}
bool deflated = acceptEncodingDeflate && compress_content(content, mimeType);
return build_response(content.data(), content.size(), "", mimeType, deflated, true);
}
/* Get static skin stuff */
else if (urlStr.substr(0, 6) == "/skin/") {
content = getResourceAsString(urlStr.substr(6));
mimeType = getMimeTypeForFile(urlStr);
}
static
struct MHD_Response* handle_skin(struct MHD_Connection * connection,
int& httpResponseCode,
kiwix::Reader *reader,
kiwix::Searcher *searcher,
const std::string& urlStr,
const std::string& humanReadableBookId,
bool acceptEncodingDeflate)
{
std::string content = getResourceAsString(urlStr.substr(6));
std::string mimeType = getMimeTypeForFile(urlStr);
bool deflated = acceptEncodingDeflate && compress_content(content, mimeType);
return build_response(content.data(), content.size(), "", mimeType, deflated, true);
}
/* Display the search restults */
else if (!strcmp(url, "/search")) {
static
struct MHD_Response* handle_search(struct MHD_Connection * connection,
int& httpResponseCode,
kiwix::Reader *reader,
kiwix::Searcher *searcher,
const std::string& urlStr,
const std::string& humanReadableBookId,
bool acceptEncodingDeflate)
{
std::string content;
std::string mimeType;
std::string httpRedirection;
/* Retrieve the pattern to search */
const char* pattern = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "pattern");
@ -277,6 +327,8 @@ static int accessHandlerCallback(void *cls,
/* If article found then redirect directly to it */
if (!patternCorrespondingUrl.empty()) {
httpRedirection = "/" + humanReadableBookId + "/" + patternCorrespondingUrl;
httpResponseCode = MHD_HTTP_FOUND;
return build_response("", 0, httpRedirection, "", false, true);
}
}
@ -302,22 +354,47 @@ static int accessHandlerCallback(void *cls,
}
mimeType = "text/html; charset=utf-8";
}
/* Display a random article */
else if (!strcmp(url, "/random")) {
cacheEnabled = false;
introduceTaskbar(content, humanReadableBookId);
bool deflated = acceptEncodingDeflate && compress_content(content, mimeType);
return build_response(content.data(), content.size(), httpRedirection, mimeType, deflated, true);
}
static
struct MHD_Response* handle_random(struct MHD_Connection * connection,
int& httpResponseCode,
kiwix::Reader *reader,
kiwix::Searcher *searcher,
const std::string& urlStr,
const std::string& humanReadableBookId,
bool acceptEncodingDeflate)
{
std::string httpRedirection;
bool cacheEnabled = false;
httpResponseCode = MHD_HTTP_FOUND;
if (reader != NULL) {
pthread_mutex_lock(&readerLock);
std::string randomUrl = reader->getRandomPageUrl();
pthread_mutex_unlock(&readerLock);
httpRedirection = "/" + humanReadableBookId + "/" + kiwix::urlEncode(randomUrl);
}
}
return build_response("", 0, httpRedirection, "", false, false);
}
/* Display the content of a ZIM content (article, image, ...) */
else if (reader != NULL) {
static
struct MHD_Response* handle_content(struct MHD_Connection * connection,
int& httpResponseCode,
kiwix::Reader *reader,
kiwix::Searcher *searcher,
const std::string& urlStr,
const std::string& humanReadableBookId,
bool acceptEncodingDeflate)
{
std::string baseUrl;
std::string content;
std::string mimeType;
unsigned int contentLength;
try {
pthread_mutex_lock(&readerLock);
@ -351,100 +428,166 @@ static int accessHandlerCallback(void *cls,
content = replaceRegex(content,
"<head><base href=\"/" + humanReadableBookId + baseUrl + "\" />",
"<head>");
introduceTaskbar(content, humanReadableBookId);
} else if (mimeType.find("text/css") != string::npos) {
content = replaceRegex(content, "$1$2" + humanReadableBookId + "/$3/",
"(url|URL)(\\([\"|\']{0,1}/)([A-Z|\\-])/");
}
}
/* Display the global Welcome page */
else {
bool deflated = acceptEncodingDeflate && compress_content(content, mimeType);
return build_response(content.data(), content.size(), "", mimeType, deflated, true);
}
static
struct MHD_Response* handle_default(struct MHD_Connection * connection,
int& httpResponseCode,
kiwix::Reader *reader,
kiwix::Searcher *searcher,
const std::string& urlStr,
const std::string& humanReadableBookId,
bool acceptEncodingDeflate)
{
pthread_mutex_lock(&welcomeLock);
content = welcomeHTML;
std::string content = welcomeHTML;
pthread_mutex_unlock(&welcomeLock);
mimeType = "text/html; charset=utf-8";
}
/* Introduce Taskbar */
if (!humanReadableBookId.empty() && mimeType.find("text/html") != string::npos) {
introduceTaskbar(content, humanReadableBookId);
}
std::string mimeType = "text/html; charset=utf-8";
/* Compute the lengh */
contentLength = content.size();
bool deflated = acceptEncodingDeflate && compress_content(content, mimeType);
return build_response(content.data(), content.size(), "", mimeType, deflated, true);
}
/* Should be deflate */
bool deflated =
contentLength > KIWIX_MIN_CONTENT_SIZE_TO_DEFLATE &&
contentLength < COMPRESSOR_BUFFER_SIZE &&
acceptEncodingDeflate &&
(mimeType.find("text/") != string::npos ||
mimeType.find("application/javascript") != string::npos ||
mimeType.find("application/json") != string::npos);
static int accessHandlerCallback(void *cls,
struct MHD_Connection * connection,
const char * url,
const char * method,
const char * version,
const char * upload_data,
size_t * upload_data_size,
void ** ptr)
{
/* Unexpected method */
if (0 != strcmp(method, "GET") && 0 != strcmp(method, "POST"))
return MHD_NO;
/* Compress the content if necessary */
if (deflated) {
pthread_mutex_lock(&compressorLock);
comprLen = COMPRESSOR_BUFFER_SIZE;
compress(compr, &comprLen, (const Bytef*)(content.data()), contentLength);
if (comprLen > 2 && comprLen < contentLength) {
/* /!\ Internet Explorer has a bug with deflate compression.
It can not handle the first two bytes (compression headers)
We need to chunk them off (move the content 2bytes)
It has no incidence on other browsers
See http://www.subbu.org/blog/2008/03/ie7-deflate-or-not and comments */
compr += 2;
content = string((char *)compr, comprLen);
contentLength = comprLen;
} else {
deflated = false;
}
pthread_mutex_unlock(&compressorLock);
}
/* Create the response */
response = MHD_create_response_from_data(contentLength,
(void *)content.data(),
MHD_NO,
MHD_YES);
/* Make a redirection if necessary otherwise send the content */
if (!httpRedirection.empty()) {
MHD_add_response_header(response, MHD_HTTP_HEADER_LOCATION, httpRedirection.c_str());
httpResponseCode = MHD_HTTP_FOUND;
} else {
/* Add if necessary the content-encoding */
if (deflated) {
MHD_add_response_header(response, MHD_HTTP_HEADER_VARY, "Accept-Encoding");
MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_ENCODING, "deflate");
}
/* Tell the client that byte ranges are accepted */
MHD_add_response_header(response, MHD_HTTP_HEADER_ACCEPT_RANGES, "bytes");
/* Specify the mime type */
MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, mimeType.c_str());
/* The first time only the headers are valid, do not respond in the first round... */
static int dummy;
if (&dummy != *ptr) {
*ptr = &dummy;
return MHD_YES;
}
/* clear context pointer */
*ptr = NULL;
/* Force to close the connection - cf. 100% CPU usage with v. 4.4 (in Lucid) */
//MHD_add_response_header(response, MHD_HTTP_HEADER_CONNECTION, "close");
/* Debug */
if (isVerbose()) {
std::cout << "Requesting " << url << std::endl;
}
/* Allow cross-domain requests */
//MHD_add_response_header(response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
MHD_add_response_header(response, "Access-Control-Allow-Origin", "*");
/* 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;
if (cacheEnabled) { /* Force cache */
MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, "max-age=2723040, public");
} else { /* Prevent cache (for random page) */
MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, "no-cache, no-store, must-revalidate");
/* Check if range is requested */
const char* acceptRangeHeaderValue = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_RANGE);
const bool acceptRange = acceptRangeHeaderValue != NULL;
/* Prepare the variables */
struct MHD_Response *response;
int httpResponseCode = MHD_HTTP_OK;
std::string urlStr = string(url);
/* Get searcher and reader */
std::string humanReadableBookId = "";
if (!(urlStr.size() > 5 && urlStr.substr(0, 6) == "/skin/")) {
if (!strcmp(url, "/search") || !strcmp(url, "/suggest") || !strcmp(url, "/random")) {
const char* tmpGetValue = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "content");
humanReadableBookId = (tmpGetValue != NULL ? string(tmpGetValue) : "");
} else {
humanReadableBookId = urlStr.substr(1, urlStr.find("/", 1) != string::npos ?
urlStr.find("/", 1) - 1 : urlStr.size() - 2);
if (!humanReadableBookId.empty()) {
urlStr = urlStr.substr(urlStr.find("/", 1) != string::npos ?
urlStr.find("/", 1) : humanReadableBookId.size());
}
}
}
pthread_mutex_lock(&mapLock);
kiwix::Searcher *searcher = searchers.find(humanReadableBookId) != searchers.end() ?
searchers.find(humanReadableBookId)->second : NULL;
kiwix::Reader *reader = readers.find(humanReadableBookId) != readers.end() ?
readers.find(humanReadableBookId)->second : NULL;
if (reader == NULL) {
humanReadableBookId="";
}
pthread_mutex_unlock(&mapLock);
/* Get suggestions */
if (!strcmp(url, "/suggest") && reader != NULL) {
response = handle_suggest(connection,
httpResponseCode,
reader,
searcher,
urlStr,
humanReadableBookId,
acceptEncodingDeflate);
}
/* Get static skin stuff */
else if (urlStr.substr(0, 6) == "/skin/") {
response = handle_skin(connection,
httpResponseCode,
reader,
searcher,
urlStr,
humanReadableBookId,
acceptEncodingDeflate);
}
/* Display the search restults */
else if (!strcmp(url, "/search")) {
response = handle_search(connection,
httpResponseCode,
reader,
searcher,
urlStr,
humanReadableBookId,
acceptEncodingDeflate);
}
/* Display a random article */
else if (!strcmp(url, "/random")) {
response = handle_random(connection,
httpResponseCode,
reader,
searcher,
urlStr,
humanReadableBookId,
acceptEncodingDeflate);
}
/* Display the content of a ZIM content (article, image, ...) */
else if (reader != NULL) {
response = handle_content(connection,
httpResponseCode,
reader,
searcher,
urlStr,
humanReadableBookId,
acceptEncodingDeflate);
}
/* Display the global Welcome page */
else {
response = handle_default(connection,
httpResponseCode,
reader,
searcher,
urlStr,
humanReadableBookId,
acceptEncodingDeflate);
}
/* Queue the response */