From 14f0f7906157f381c8b133cf174a0b75fa4ba68d Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Mon, 28 Nov 2022 13:43:20 +0400 Subject: [PATCH] User language control via userlang cookie --- src/server/request_context.cpp | 20 ++++++++++++ src/server/request_context.h | 5 +++ test/server.cpp | 56 ++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/src/server/request_context.cpp b/src/server/request_context.cpp index db552a920..bc99610fd 100644 --- a/src/server/request_context.cpp +++ b/src/server/request_context.cpp @@ -97,6 +97,7 @@ RequestContext::RequestContext(struct MHD_Connection* connection, { 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); + MHD_get_connection_values(connection, MHD_COOKIE_KIND, &RequestContext::fill_cookie, this); try { acceptEncodingGzip = @@ -106,6 +107,8 @@ RequestContext::RequestContext(struct MHD_Connection* connection, try { byteRange_ = ByteRange::parse(get_header(MHD_HTTP_HEADER_RANGE)); } catch (const std::out_of_range&) {} + + userlang = determine_user_language(); } RequestContext::~RequestContext() @@ -135,6 +138,14 @@ MHD_Result RequestContext::fill_argument(void *__this, enum MHD_ValueKind kind, return MHD_YES; } +MHD_Result RequestContext::fill_cookie(void *__this, enum MHD_ValueKind kind, + const char *key, const char* value) +{ + RequestContext *_this = static_cast(__this); + _this->cookies[key] = value == nullptr ? "" : value; + return MHD_YES; +} + void RequestContext::print_debug_info() const { printf("method : %s (%d)\n", method==RequestMethod::GET ? "GET" : method==RequestMethod::POST ? "POST" : @@ -215,11 +226,20 @@ std::string RequestContext::get_header(const std::string& name) const { } std::string RequestContext::get_user_language() const +{ + return userlang; +} + +std::string RequestContext::determine_user_language() const { try { return get_argument("userlang"); } catch(const std::out_of_range&) {} + try { + return cookies.at("userlang"); + } catch(const std::out_of_range&) {} + try { return parseAcceptLanguageHeader(get_header("Accept-Language")); } catch(const std::out_of_range&) {} diff --git a/src/server/request_context.h b/src/server/request_context.h index de02d465f..07339324b 100644 --- a/src/server/request_context.h +++ b/src/server/request_context.h @@ -130,10 +130,15 @@ class RequestContext { ByteRange byteRange_; std::map headers; std::map> arguments; + std::map cookies; std::string queryString; + std::string userlang; private: // functions + std::string determine_user_language() const; + static MHD_Result fill_header(void *, enum MHD_ValueKind, const char*, const char*); + static MHD_Result fill_cookie(void *, enum MHD_ValueKind, const char*, const char*); static MHD_Result fill_argument(void *, enum MHD_ValueKind, const char*, const char*); }; diff --git a/test/server.cpp b/test/server.cpp index 09c73863a..7b7548bb4 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -1042,6 +1042,46 @@ TEST_F(ServerTest, UserLanguageControl) /*Response Set-Cookie:*/ "userlang=test", /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" }, + { + "userlang cookie is respected", + /*url*/ "/ROOT/content/zimfile/invalid-article", + /*Accept-Language:*/ "", + /*Request Cookie:*/ "userlang=test", + /*Response Set-Cookie:*/ "userlang=test", + /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" + }, + { + "userlang cookie is correctly parsed", + /*url*/ "/ROOT/content/zimfile/invalid-article", + /*Accept-Language:*/ "", + /*Request Cookie:*/ "anothercookie=123; userlang=test", + /*Response Set-Cookie:*/ "userlang=test", + /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" + }, + { + "userlang cookie is correctly parsed", + /*url*/ "/ROOT/content/zimfile/invalid-article", + /*Accept-Language:*/ "", + /*Request Cookie:*/ "userlang=test; anothercookie=abc", + /*Response Set-Cookie:*/ "userlang=test", + /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" + }, + { + "userlang cookie is correctly parsed", + /*url*/ "/ROOT/content/zimfile/invalid-article", + /*Accept-Language:*/ "", + /*Request Cookie:*/ "cookie1=abc; userlang=test; cookie2=xyz", + /*Response Set-Cookie:*/ "userlang=test", + /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" + }, + { + "Multiple userlang cookies are not a problem", + /*url*/ "/ROOT/content/zimfile/invalid-article", + /*Accept-Language:*/ "", + /*Request Cookie:*/ "cookie1=abc; userlang=en; userlang=test; cookie2=xyz", + /*Response Set-Cookie:*/ "userlang=test", + /* expected

*/ "[I18N TESTING] Content not found, but at least the server is alive" + }, { "userlang query parameter takes precedence over Accept-Language", /*url*/ "/ROOT/content/zimfile/invalid-article?userlang=en", @@ -1050,6 +1090,22 @@ TEST_F(ServerTest, UserLanguageControl) /*Response Set-Cookie:*/ "userlang=en", /* expected

*/ "Not Found" }, + { + "userlang query parameter takes precedence over its cookie counterpart", + /*url*/ "/ROOT/content/zimfile/invalid-article?userlang=en", + /*Accept-Language:*/ "", + /*Request Cookie:*/ "userlang=test", + /*Response Set-Cookie:*/ "userlang=en", + /* expected

*/ "Not Found" + }, + { + "userlang in cookies takes precedence over Accept-Language", + /*url*/ "/ROOT/content/zimfile/invalid-article", + /*Accept-Language:*/ "test", + /*Request Cookie:*/ "userlang=en", + /*Response Set-Cookie:*/ "userlang=en", + /* expected

*/ "Not Found" + }, { "The value of the Accept-Language header is not currently parsed.", // In case of a comma separated list of languages (optionally weighted