mirror of https://github.com/kiwix/libkiwix.git
Support for serving customized resources
During work on the kiwix-serve front-end, the edit-save-test cycle is a multistep procedure: 1. build and install libkiwix 2. build kiwix-tools 3. run kiwix-serve 4. reload the web-page in the browser When making changes in static resources that are served by kiwix-serve unmodified, the steps 1-3 can be eliminated if kiwix-serve is capable of serving resources from the file-system. This commit adds such a functionality to kiwix-serve. Now, if during startup of kiwix-serve the environment variable `KIWIX_SERVE_CUSTOMIZED_RESOURCES` is defined it is assumed to point to a file where every line has the following format: URL MIMETYPE RESOURCE_FILE_PATH When a request is received by kiwix-serve and its URL matches any of the URLs read from the customized resource file, then the resource data is read from the respective file RESOURCE_FILE_PATH and served with mime-type MIMETYPE. Though this feature was introduced in order to facilitate the development of the iframe-based content viewer, it can also be useful to users who would like to customize the kiwix-serve front-end on their own (without re-building all of kiwix-serve). There is some overlap with a feature of the kiwix-compile-resources script that also allows to override resources. The differences are: 1. The new way of customizing front-end resources has all such resources listed in a text file and there is a single environment variable from which the path of that file is read. kiwix-compile-resources associates a separate environment variable with each resource. 2. The new way uses regular paths to identify a resource. The kiwix-compile-resources method encodes the resource path by replacing any non-alphanumeric characters (including the path separator) with underscores (so that the resulting resource identifier can be used to construct the name of the environment variable controlling that resource). 3. The new method allows adding new front-end resources. The old method only allows to modify existing resources. 4. The new method allows (actually requires) to specify the URL at which the overriden resource should be served (similarly, the MIME-type can/must be specified, too). The old method only allows to override the contents of a resource. 5. The new method only allows to override front-end resources that are served without any preprocessing by kiwix-serve at runtime. The old method allows to override template resources as well (note that internationalization/translation resources cannot be overriden using the old method, either).
This commit is contained in:
parent
6938253e59
commit
e3e4bfa533
|
@ -68,6 +68,7 @@ extern "C" {
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include "kiwixlib-resources.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -212,6 +213,12 @@ void checkBookNumber(const Library::BookIdSet& bookIds, size_t limit) {
|
|||
}
|
||||
}
|
||||
|
||||
struct CustomizedResourceData
|
||||
{
|
||||
std::string mimeType;
|
||||
std::string resourceFilePath;
|
||||
};
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
std::pair<std::string, Library::BookIdSet> InternalServer::selectBooks(const RequestContext& request) const
|
||||
|
@ -327,7 +334,6 @@ zim::Query SearchInfo::getZimQuery(bool verbose) const {
|
|||
return query;
|
||||
}
|
||||
|
||||
|
||||
static IdNameMapper defaultNameMapper;
|
||||
|
||||
static MHD_Result staticHandlerCallback(void* cls,
|
||||
|
@ -339,6 +345,27 @@ static MHD_Result staticHandlerCallback(void* cls,
|
|||
size_t* upload_data_size,
|
||||
void** cont_cls);
|
||||
|
||||
class InternalServer::CustomizedResources : public std::map<std::string, CustomizedResourceData>
|
||||
{
|
||||
public:
|
||||
CustomizedResources()
|
||||
{
|
||||
const char* fname = ::getenv("KIWIX_SERVE_CUSTOMIZED_RESOURCES");
|
||||
if ( fname )
|
||||
{
|
||||
std::cout << "Populating customized resources" << std::endl;
|
||||
std::ifstream file(fname);
|
||||
std::string url, mimeType, resourceFilePath;
|
||||
while ( file >> url >> mimeType >> resourceFilePath )
|
||||
{
|
||||
std::cout << "Got " << url << " " << mimeType << " " << resourceFilePath << std::endl;
|
||||
(*this)[url] = CustomizedResourceData{mimeType, resourceFilePath};
|
||||
}
|
||||
std::cout << "Done populating customized resources" << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
InternalServer::InternalServer(Library* library,
|
||||
NameMapper* nameMapper,
|
||||
|
@ -368,9 +395,12 @@ InternalServer::InternalServer(Library* library,
|
|||
mp_library(library),
|
||||
mp_nameMapper(nameMapper ? nameMapper : &defaultNameMapper),
|
||||
searchCache(getEnvVar<int>("KIWIX_SEARCH_CACHE_SIZE", DEFAULT_CACHE_SIZE)),
|
||||
suggestionSearcherCache(getEnvVar<int>("KIWIX_SUGGESTION_SEARCHER_CACHE_SIZE", std::max((unsigned int) (mp_library->getBookCount(true, true)*0.1), 1U)))
|
||||
suggestionSearcherCache(getEnvVar<int>("KIWIX_SUGGESTION_SEARCHER_CACHE_SIZE", std::max((unsigned int) (mp_library->getBookCount(true, true)*0.1), 1U))),
|
||||
m_customizedResources(new CustomizedResources)
|
||||
{}
|
||||
|
||||
InternalServer::~InternalServer() = default;
|
||||
|
||||
bool InternalServer::start() {
|
||||
#ifdef _WIN32
|
||||
int flags = MHD_USE_SELECT_INTERNALLY;
|
||||
|
@ -507,6 +537,9 @@ std::unique_ptr<Response> InternalServer::handle_request(const RequestContext& r
|
|||
if ( etag )
|
||||
return Response::build_304(*this, etag);
|
||||
|
||||
if ( isLocallyCustomizedResource(request.get_url()) )
|
||||
return handle_locally_customized_resource(request);
|
||||
|
||||
if (startsWith(request.get_url(), "/skin/"))
|
||||
return handle_skin(request);
|
||||
|
||||
|
@ -1067,4 +1100,34 @@ std::unique_ptr<Response> InternalServer::handle_raw(const RequestContext& reque
|
|||
}
|
||||
}
|
||||
|
||||
bool InternalServer::isLocallyCustomizedResource(const std::string& url) const
|
||||
{
|
||||
return m_customizedResources->find(url) != m_customizedResources->end();
|
||||
}
|
||||
|
||||
std::unique_ptr<Response> InternalServer::handle_locally_customized_resource(const RequestContext& request)
|
||||
{
|
||||
if (m_verbose.load()) {
|
||||
printf("** running handle_locally_customized_resource\n");
|
||||
}
|
||||
|
||||
const CustomizedResourceData& crd = m_customizedResources->at(request.get_url());
|
||||
|
||||
if (m_verbose.load()) {
|
||||
std::cout << "Reading " << crd.resourceFilePath << std::endl;
|
||||
}
|
||||
const auto resourceData = getFileContent(crd.resourceFilePath);
|
||||
|
||||
auto byteRange = request.get_range().resolve(resourceData.size());
|
||||
if (byteRange.kind() != ByteRange::RESOLVED_FULL_CONTENT) {
|
||||
return Response::build_416(*this, resourceData.size());
|
||||
}
|
||||
|
||||
return ContentResponse::build(*this,
|
||||
resourceData,
|
||||
crd.mimeType,
|
||||
/*isHomePage=*/false,
|
||||
/*raw=*/true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ class InternalServer {
|
|||
bool blockExternalLinks,
|
||||
std::string indexTemplateString,
|
||||
int ipConnectionLimit);
|
||||
virtual ~InternalServer() = default;
|
||||
virtual ~InternalServer();
|
||||
|
||||
MHD_Result handlerCallback(struct MHD_Connection* connection,
|
||||
const char* url,
|
||||
|
@ -142,6 +142,7 @@ class InternalServer {
|
|||
std::unique_ptr<Response> handle_captured_external(const RequestContext& request);
|
||||
std::unique_ptr<Response> handle_content(const RequestContext& request);
|
||||
std::unique_ptr<Response> handle_raw(const RequestContext& request);
|
||||
std::unique_ptr<Response> handle_locally_customized_resource(const RequestContext& request);
|
||||
|
||||
std::vector<std::string> search_catalog(const RequestContext& request,
|
||||
kiwix::OPDSDumper& opdsDumper);
|
||||
|
@ -153,6 +154,8 @@ class InternalServer {
|
|||
std::pair<std::string, Library::BookIdSet> selectBooks(const RequestContext& r) const;
|
||||
SearchInfo getSearchInfo(const RequestContext& r) const;
|
||||
|
||||
bool isLocallyCustomizedResource(const std::string& url) const;
|
||||
|
||||
private: // data
|
||||
std::string m_addr;
|
||||
int m_port;
|
||||
|
@ -176,6 +179,9 @@ class InternalServer {
|
|||
std::string m_server_id;
|
||||
std::string m_library_id;
|
||||
|
||||
class CustomizedResources;
|
||||
std::unique_ptr<CustomizedResources> m_customizedResources;
|
||||
|
||||
friend std::unique_ptr<Response> Response::build(const InternalServer& server);
|
||||
friend std::unique_ptr<ContentResponse> ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype, bool isHomePage, bool raw);
|
||||
friend std::unique_ptr<Response> ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item, bool raw);
|
||||
|
|
Loading…
Reference in New Issue