Compare commits

...
16 Commits
Author SHA1 Message Date
Kelson 46b599fb2c Merge pull request #1297 from kiwix/libkiwix-14.2.1
Libkiwix 14.2.1
2026-05-09 17:31:46 +02:00
Emmanuel Engelhart 940abd2e8f Add 14.2.1 changelog 2026-05-09 17:00:35 +02:00
Emmanuel Engelhart 2f987ee727 Bump-up version to 14.2.1 2026-05-09 17:00:35 +02:00
Kelson d072e57f05 Merge pull request #1298 from kiwix/exact_version_of_libmicrohttpd_url_parsing_change
Exact version of libmicrohttpd affecting the server unit-test
2026-05-09 16:58:37 +02:00
Veloman Yunkan a7df774f54 More accurate value of MHD_VERSION in the server test
Identified the exact version of libmicrohttpd where the change in URL parsing
affects the server unit-test.
2026-05-09 17:37:25 +04:00
Kelson 7693557065 Merge pull request #1277 from BPerlakiH/1210-fix-docs
Update docs
2026-05-02 11:36:54 +02:00
Balazs Perlaki-Horvath 58e458b658 Update docs 2026-05-02 11:36:21 +02:00
Kelson fb1b8b85e0 Merge pull request #1295 from kiwix/fix-ifram-bottom-white-line
Fix white line at bottom of iframe
2026-05-02 11:28:25 +02:00
zeyad elkholy 5cc3ce1895 Fix white line at bottom of iframe 2026-05-02 11:22:01 +02:00
Kelson 7e50bf611b Merge pull request #1294 from kiwix/handling-of-requests-with-wrong-root-url-prefix
Correct error message for requests with wrong root URL prefix
2026-05-01 11:32:55 +02:00
Veloman Yunkan 59ba61fcd2 Fixed the error text for requests with wrong root 2026-04-30 19:08:08 +04:00
Veloman Yunkan 40b9e2b876 New test-points demonstrating a bug in kiwix-serve
When kiwix-serve is started with a non-empty --urlRootLocation parameter and
a request for a URL *not* starting with the specified URL prefix is
received, the message in the error response contains a wrong URL. For
example if the URL root location is set to '/abc', and '/klmn/xyz' is
requested the error message reads:

  The requested URL "/abcINVALID URL" was not found on this server.
2026-04-30 19:02:17 +04:00
Veloman Yunkan 90867968c1 Dropped (unused) RequestContext::get_root_path() 2026-04-30 19:02:17 +04:00
Veloman Yunkan fcebc2d4f6 Exposed MHD_VERSION to test/server.cpp
It turns out that the C preprocessor doesn't complain about undefined
macros used in the arithmetic expressions in `#if` directives (treating
those conditions simply as not satisfied). As a result, PR #1293 had
a slightly different effect from that which was intended. Now taking
the opportunity to correct that subtle mistake.
2026-04-30 18:55:42 +04:00
Kelson d24bd1ea3e Merge pull request #1293 from kiwix/workaround_for_libmicrohttpd_1.x
"Fixed" the server unit-test for libmicrohttpd 1.x
2026-04-30 15:23:56 +02:00
Veloman Yunkan 20b3dd77bc "Fixed" the server unit-test for libmicrohttpd 1.x
libmicrohttpd 1.0.5 (the version used at this point under the debian
testing distribution) seems to return an HTTP 400 (Bad Request) error
if the URL contains an invalid URI-encoding sequence, thus breaking
our unit-tests involving such URLs. Upgrading our libmicrohttpd
dependency is not easy, so this change is an optimistic attempt to fix
the build of the libkiwix package under debian testing.
2026-04-30 17:01:02 +04:00
10 changed files with 99 additions and 43 deletions

No files matched your search

+7
View File
@@ -1,3 +1,10 @@
libkiwix 14.2.1
===============
* Documentation fix (@bperlakih #1277)
* Fix kiwix-serve unit-test for libmicrohttpd 1.x (@veloman-yunkan #1293)
* Fix kiwix-server error message for requests with wrong root URL prefix (@veloman-yunkan #1294)
libkiwix 14.2.0
===============
+3 -3
View File
@@ -444,11 +444,11 @@ class Library: public std::enable_shared_from_this<Library>
/**
* Sort (in place) bookIds using the given comparator.
* Sort (in place) bookIds using the given arguments.
*
* @param bookIds the list of book Ids to sort
* @param comparator how to sort the books
* @return The sorted list of books
* @param sortBy how to sort the books (UNSORTED, TITLE, SIZE, DATE, CREATOR, PUBLISHER)
* @param ascending ascending or descending
*/
void sort(BookIdCollection& bookIds, supportedListSortBy sortBy, bool ascending) const;
+1 -1
View File
@@ -1,5 +1,5 @@
project('libkiwix', 'cpp',
version : '14.2.0',
version : '14.2.1',
license : 'GPLv3+',
default_options : ['c_std=c11', 'cpp_std=c++17', 'werror=true'])
+6 -12
View File
@@ -181,16 +181,6 @@ int getMHDFlags(IpMode ipMode, bool verbose)
return flags;
}
std::string
fullURL2LocalURL(const std::string& fullUrl, const std::string& rootLocation)
{
if ( kiwix::startsWith(fullUrl, rootLocation) ) {
return fullUrl.substr(rootLocation.size());
} else {
return "INVALID URL";
}
}
std::string getSearchComponent(const RequestContext& request)
{
const std::string query = request.get_query();
@@ -645,11 +635,15 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection,
printf("full_url : %s\n", fullUrl);
}
const auto url = fullURL2LocalURL(fullUrl, m_rootPrefixOfDecodedURL);
RequestContext::NameValuePairs headers, queryArgs;
MHD_get_connection_values(connection, MHD_HEADER_KIND, add_name_value_pair, &headers);
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, add_name_value_pair, &queryArgs);
RequestContext request(m_root, url, method, version, headers, queryArgs);
const int rootPrefixLen = kiwix::startsWith(fullUrl, m_rootPrefixOfDecodedURL)
? int(m_rootPrefixOfDecodedURL.size())
: -1;
RequestContext request(fullUrl, rootPrefixLen, method, version, headers, queryArgs);
if (m_verbose.load() ) {
request.print_debug_info();
+15 -11
View File
@@ -51,14 +51,14 @@ RequestMethod str2RequestMethod(const std::string& method) {
} // unnamed namespace
RequestContext::RequestContext(const std::string& _rootLocation, // URI-encoded
const std::string& unrootedUrl, // URI-decoded
RequestContext::RequestContext(const std::string& _fullUrl, // URI-decoded
int _rootPrefixLength,
const std::string& _method,
const std::string& version,
const NameValuePairs& headers,
const NameValuePairs& queryArgs) :
rootLocation(_rootLocation),
url(unrootedUrl),
fullUrl(_fullUrl),
rootPrefixLength(_rootPrefixLength),
method(str2RequestMethod(_method)),
version(version),
requestIndex(s_requestIndex++),
@@ -128,7 +128,8 @@ void RequestContext::print_debug_info() const {
printf("\n");
}
printf("Parsed : \n");
printf("url : %s\n", url.c_str());
printf("full url: %s\n", fullUrl.c_str());
printf("derooted url: %s\n", get_url().c_str());
printf("acceptEncodingGzip : %d\n", acceptEncodingGzip);
printf("has_range : %d\n", byteRange_.kind() != ByteRange::NONE);
printf("is_valid_url : %d\n", is_valid_url());
@@ -141,11 +142,14 @@ RequestMethod RequestContext::get_method() const {
}
std::string RequestContext::get_url() const {
return url;
return rootPrefixLength < 0
? ""
: fullUrl.substr(rootPrefixLength);
}
std::string RequestContext::get_url_part(int number) const {
size_t start = 1;
const std::string url = get_url();
while(true) {
auto found = url.find('/', start);
if (number == 0) {
@@ -165,14 +169,14 @@ std::string RequestContext::get_url_part(int number) const {
}
std::string RequestContext::get_full_url() const {
return rootLocation + urlEncode(url);
}
std::string RequestContext::get_root_path() const {
return rootLocation.empty() ? "/" : rootLocation;
return urlEncode(fullUrl);
}
bool RequestContext::is_valid_url() const {
if ( rootPrefixLength < 0 )
return false;
const std::string url = get_url();
return url.empty() || url[0] == '/';
}
+4 -5
View File
@@ -59,8 +59,8 @@ class RequestContext {
typedef std::vector<std::pair<const char*, const char*>> NameValuePairs;
public: // functions
RequestContext(const std::string& rootLocation, // URI-encoded
const std::string& unrootedUrl, // URI-decoded
RequestContext(const std::string& fullUrl, // URI-decoded
int rootPrefixLength,
const std::string& method,
const std::string& version,
const NameValuePairs& headers,
@@ -96,7 +96,6 @@ class RequestContext {
std::string get_url() const;
std::string get_url_part(int part) const;
std::string get_full_url() const;
std::string get_root_path() const;
std::string get_query() const { return queryString; }
@@ -139,8 +138,8 @@ class RequestContext {
};
private: // data
std::string rootLocation;
std::string url;
const std::string fullUrl; // URI-decoded
const int rootPrefixLength;
RequestMethod method;
std::string version;
unsigned long long requestIndex;
-8
View File
@@ -228,12 +228,6 @@ function updateToolbarVisibilityState() {
previousScrollTop = st;
}
function handle_visual_viewport_change() {
const wh = window.visualViewport
? window.visualViewport.height
: window.innerHeight;
contentIframe.height = wh - contentIframe.offsetTop - 4;
}
function setIframeUrl(path) {
try {
@@ -583,9 +577,7 @@ function setupViewer() {
// Defer the call of handle_visual_viewport_change() until after the
// presence or absence of the taskbar as determined by this function
// has been settled.
setTimeout(handle_visual_viewport_change, 0);
window.onresize = handle_visual_viewport_change;
const kiwixToolBarWrapper = document.getElementById('kiwixtoolbarwrapper');
if ( ! viewerSettings.toolbarEnabled ) {
+10
View File
@@ -7,6 +7,16 @@
frame-src 'self';
object-src 'none';">
<title>ZIM Viewer</title>
<style>
html {
height: 100%;
}
html, body, #content_iframe {
display: flex;
flex-direction: column;
flex: 1;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link type="text/css" href="./skin/kiwix.css?KIWIXCACHEID" rel="Stylesheet" />
<link type="text/css" href="./skin/taskbar.css?KIWIXCACHEID" rel="Stylesheet" />
+1 -1
View File
@@ -12,7 +12,7 @@ RequestContext makeHttpGetRequest(const std::string& url,
const RequestContext::NameValuePairs& headers,
const RequestContext::NameValuePairs& queryArgs)
{
return RequestContext("", url, "GET", "1.1", headers, queryArgs);
return RequestContext(url, 0, "GET", "1.1", headers, queryArgs);
}
std::string getResponseContent(const ContentResponseBlueprint& crb)
+52 -2
View File
@@ -9,6 +9,9 @@
#include "../src/tools/stringTools.h"
#include "testing_tools.h"
#include "../src/server/microhttpd_wrapper.h" // for MHD_VERSION
using namespace kiwix::testing;
const std::string ROOT_PREFIX("/ROOT%23%3F");
@@ -79,7 +82,7 @@ const ResourceCollection resources200Compressible{
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css?cacheid=42e90cb9" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/viewer.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/viewer.js?cacheid=6192cae1" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/viewer.js?cacheid=f78c03d9" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf?cacheid=af705837" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Roboto.ttf" },
@@ -340,7 +343,7 @@ R"EXPECTEDRESULT( <link type="text/css" href="./skin/kiwix.css?cacheid=b4e29e
<script type="text/javascript" src="./skin/polyfills.js?cacheid=a0e0343d"></script>
<script type="module" src="./skin/i18n.js?cacheid=e9a10ac1" defer></script>
<script type="text/javascript" src="./skin/languages.js?cacheid=d2d6933b" defer></script>
<script type="text/javascript" src="./skin/viewer.js?cacheid=6192cae1" defer></script>
<script type="text/javascript" src="./skin/viewer.js?cacheid=f78c03d9" defer></script>
<script type="text/javascript" src="./skin/autoComplete/autoComplete.min.js?cacheid=1191aaaf"></script>
const blankPageUrl = root + "/skin/blank.html?cacheid=6b1fa032";
<label for="kiwix_button_show_toggle"><img src="./skin/caret.png?cacheid=22b942b4" alt=""></label>
@@ -439,6 +442,10 @@ TEST_F(ServerTest, CacheIdsOfStaticResourcesMatchTheSha1HashOfResourceContent)
}
const char* urls400[] = {
#if MHD_VERSION >= 0x01000300
"/ROOT%23%",
"/ROOT%23%3",
#endif // MHD_VERSION
"/ROOT%23%3F/search",
"/ROOT%23%3F/search?content=non-existing-book&pattern=asdfqwerty",
"/ROOT%23%3F/search?content=non-existing-book&pattern=asd<qwerty",
@@ -461,8 +468,10 @@ const char* urls404[] = {
"/",
"/zimfile",
"/ROOT",
#if MHD_VERSION < 0x01000300
"/ROOT%23%",
"/ROOT%23%3",
#endif // MHD_VERSION
"/ROOT%23%3Fxyz",
"/ROOT%23%3F/skin/non-existent-skin-resource",
"/ROOT%23%3F/skin/autoComplete/autoComplete.min.js?cacheid=wrongcacheid",
@@ -779,6 +788,47 @@ TEST_F(ServerTest, Http404HtmlError)
{
using namespace TestingOfHtmlResponses;
const std::vector<TestContentIn404HtmlResponse> testData{
// wrong root URL (root URL missing completely)
{ /* url */ "/",
expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/" } } } ] })" &&
expected_body==R"(
<h1>Not Found</h1>
<p>
The requested URL "/" was not found on this server.
</p>
)" },
// (conspicuously) wrong root URL
{ /* url */ "/WRONGROOT",
expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/WRONGROOT" } } } ] })" &&
expected_body==R"(
<h1>Not Found</h1>
<p>
The requested URL "/WRONGROOT" was not found on this server.
</p>
)" },
// wrong root URL with the correct root URL appearing as a suffix
{ /* url */ "/WRONGROOT/ROOT%23%3F",
expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/WRONGROOT/ROOT%23%3F" } } } ] })" &&
expected_body==R"(
<h1>Not Found</h1>
<p>
The requested URL "/WRONGROOT/ROOT%23%3F" was not found on this server.
</p>
)" },
// wrong root URL (with the correct root URL appearing as a prefix)
{ /* url */ "/ROOT%23%3FWRONGROOT",
expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3FWRONGROOT" } } } ] })" &&
expected_body==R"(
<h1>Not Found</h1>
<p>
The requested URL "/ROOT%23%3FWRONGROOT" was not found on this server.
</p>
)" },
{ /* url */ "/ROOT%23%3F/random?content=non-existent-book",
expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "no-such-book", "params" : { "BOOK_NAME" : "non-existent-book" } } } ] })" &&
expected_body==R"(