diff --git a/src/qhttp/Server.h b/src/qhttp/Server.h index 8a3ead464..ba739c399 100644 --- a/src/qhttp/Server.h +++ b/src/qhttp/Server.h @@ -99,8 +99,8 @@ class Server : public std::enable_shared_from_this { // header files for details. Convenience functions are provided here to instantiate and install // these. - void addStaticContent(std::string const& path, std::string const& rootDirectory); - AjaxEndpoint::Ptr addAjaxEndpoint(std::string const& path); + void addStaticContent(std::string const& pattern, std::string const& rootDirectory); + AjaxEndpoint::Ptr addAjaxEndpoint(std::string const& pattern); //----- setRequestTimeout() allows the user to override the default 5 minute start-of-request to // end-of-response timeout. Must be called before start(), or between calls to stop() and start(). diff --git a/src/qhttp/StaticContent.h b/src/qhttp/StaticContent.h index 1b869a67f..79794b26e 100644 --- a/src/qhttp/StaticContent.h +++ b/src/qhttp/StaticContent.h @@ -39,13 +39,13 @@ class StaticContent { //----- StaticContent is a specialized Handler to handle the common case of serving a tree of static // content rooted beneath a single file system directory. add() will add an instance to the // specified Server which will responding to GET requests on URL's that prefix match the pattern - // specified in the "path" argument and postfix match paths to existing files under rootDirectory in - // the local filesystem. Content-Type of responses is inferred from the file extension for several - // common file extensions (see the file type map near the top of Response.cc for a complete list of - // these.) Note that the Server::addStaticContent() convenience method would typically be called in - // preference to calling the add() method here directly. + // specified in the "pattern" argument and postfix match paths to existing files under rootDirectory + // in the local filesystem. Content-Type of responses is inferred from the file extension for + // several common file extensions (see the file type map near the top of Response.cc for a complete + // list of these.) Note that the Server::addStaticContent() convenience method would typically be + // called in preference to calling the add() method here directly. - static void add(Server& server, std::string const& path, std::string const& rootDirectory); + static void add(Server& server, std::string const& pattern, std::string const& rootDirectory); }; } // namespace lsst::qserv::qhttp diff --git a/src/replica/apps/QhttpTestApp.cc b/src/replica/apps/QhttpTestApp.cc index 3f98caa0f..df397b291 100644 --- a/src/replica/apps/QhttpTestApp.cc +++ b/src/replica/apps/QhttpTestApp.cc @@ -227,6 +227,7 @@ int QhttpTestApp::runImpl() { cout << ::timestamp() << "Request: " << ::senderIpAddr(req) << " /body/dump" << endl; qhttp::MultiPartParser::parse(req, make_shared<::SimpleRequestProcessor>(resp)); }}}); + httpServer->addStaticContent("/static/*", "/tmp"); // Make sure the service started before launching any BOOST ASIO threads. // This will prevent threads from finishing due to a lack of work to be done. diff --git a/src/wbase/FileChannelShared.cc b/src/wbase/FileChannelShared.cc index 42a881482..dce1144e9 100644 --- a/src/wbase/FileChannelShared.cc +++ b/src/wbase/FileChannelShared.cc @@ -456,7 +456,7 @@ bool FileChannelShared::_writeToFile(lock_guard const& tMtxLock, shared_p LOGS(_log, LOG_LVL_TRACE, __func__ << " file write " << task->getIdStr() << " start"); // Create the file if not open. if (!_file.is_open()) { - _fileName = task->resultFilePath(); + _fileName = task->resultFileAbsPath(); _file.open(_fileName, ios::out | ios::trunc | ios::binary); if (!(_file.is_open() && _file.good())) { throw runtime_error("FileChannelShared::" + string(__func__) + diff --git a/src/wbase/Task.cc b/src/wbase/Task.cc index 05215eeca..b5dfc4eeb 100644 --- a/src/wbase/Task.cc +++ b/src/wbase/Task.cc @@ -68,14 +68,15 @@ namespace { LOG_LOGGER _log = LOG_GET("lsst.qserv.wbase.Task"); -string buildResultFilePath(shared_ptr const& taskMsg, - string const& resultsDirname) { +string buildResultFileName(shared_ptr const& taskMsg) { + return to_string(taskMsg->czarid()) + "-" + to_string(taskMsg->queryid()) + "-" + + to_string(taskMsg->jobid()) + "-" + to_string(taskMsg->chunkid()) + "-" + + to_string(taskMsg->attemptcount()) + ".proto"; +} + +string buildResultFilePath(string const& resultFileName, string const& resultsDirname) { if (resultsDirname.empty()) return resultsDirname; - fs::path path(resultsDirname); - path /= to_string(taskMsg->czarid()) + "-" + to_string(taskMsg->queryid()) + "-" + - to_string(taskMsg->jobid()) + "-" + to_string(taskMsg->chunkid()) + "-" + - to_string(taskMsg->attemptcount()) + ".proto"; - return path.string(); + return fs::weakly_canonical(fs::path(resultsDirname) / resultFileName).string(); } size_t const MB_SIZE_BYTES = 1024 * 1024; @@ -84,6 +85,8 @@ size_t const MB_SIZE_BYTES = 1024 * 1024; namespace lsst::qserv::wbase { +string const Task::_fqdn = util::get_current_host_fqdn(); + // Task::ChunkEqual functor bool Task::ChunkEqual::operator()(Task::Ptr const& x, Task::Ptr const& y) { if (!x || !y) { @@ -139,15 +142,15 @@ Task::Task(TaskMsgPtr const& t, int fragmentNumber, shared_ptr co // to advice which result delivery channel to use. auto const workerConfig = wconfig::WorkerConfig::instance(); auto const resultDeliveryProtocol = workerConfig->resultDeliveryProtocol(); - _resultFilePath = ::buildResultFilePath(t, workerConfig->resultsDirname()); - auto const fqdn = util::get_current_host_fqdn(); + _resultFileName = ::buildResultFileName(t); + _resultFileAbsPath = ::buildResultFilePath(_resultFileName, workerConfig->resultsDirname()); if (resultDeliveryProtocol == wconfig::ConfigValResultDeliveryProtocol::XROOT) { // NOTE: one extra '/' after the [:] spec is required to make // a "valid" XROOTD url. - _resultFileXrootUrl = "xroot://" + fqdn + ":" + to_string(workerConfig->resultsXrootdPort()) + "/" + - _resultFilePath; + _resultFileXrootUrl = "xroot://" + _fqdn + ":" + to_string(workerConfig->resultsXrootdPort()) + "/" + + _resultFileAbsPath; } else if (resultDeliveryProtocol == wconfig::ConfigValResultDeliveryProtocol::HTTP) { - _resultFileHttpUrl = "http://" + fqdn + ":" + to_string(resultsHttpPort) + _resultFilePath; + _resultFileHttpUrl = "http://" + _fqdn + ":" + to_string(resultsHttpPort) + "/" + _resultFileName; } else { throw runtime_error("wbase::Task::Task: unsupported results delivery protocol: " + wconfig::ConfigValResultDeliveryProtocol::toString(resultDeliveryProtocol)); diff --git a/src/wbase/Task.h b/src/wbase/Task.h index c9dda2671..a40dbf217 100644 --- a/src/wbase/Task.h +++ b/src/wbase/Task.h @@ -207,7 +207,7 @@ class Task : public util::CommandForThreadPool { TaskState state() const { return _state; } std::string getQueryString() const; int getQueryFragmentNum() { return _queryFragmentNum; } - std::string const& resultFilePath() const { return _resultFilePath; } + std::string const& resultFileAbsPath() const { return _resultFileAbsPath; } std::string const& resultFileXrootUrl() const { return _resultFileXrootUrl; } std::string const& resultFileHttpUrl() const { return _resultFileHttpUrl; } bool setTaskQueryRunner( @@ -334,13 +334,16 @@ class Task : public util::CommandForThreadPool { std::unique_ptr _dbTblsAndSubchunks; /// The path to the result file. - std::string _resultFilePath; + std::string _resultFileAbsPath; - /// The XROOTD URL for the result file: "xroot://:" + "/" + _resultFilePath + /// The name of the result file. + std::string _resultFileName; + + /// The XROOTD URL for the result file: "xroot://:" + "/" + _resultFileAbsPath /// @note an extra '/' after server:port spec is required to make a "valid" XROOTD url std::string _resultFileXrootUrl; - /// The HTTP URL for the result file: "http://:" + _resultFilePath + /// The HTTP URL for the result file: "http://:/" + _resultFileName std::string _resultFileHttpUrl; std::atomic _queryStarted{false}; ///< Set to true when the query is about to be run. @@ -378,6 +381,8 @@ class Task : public util::CommandForThreadPool { TIMEPOINT _bootedTime; bool _unitTest = false; ///< + + static std::string const _fqdn; ///< Fully qualified domain name of the host. Acquired once at startup. }; } // namespace lsst::qserv::wbase diff --git a/src/wcontrol/Foreman.cc b/src/wcontrol/Foreman.cc index df3ed4063..d5b3bf1a2 100644 --- a/src/wcontrol/Foreman.cc +++ b/src/wcontrol/Foreman.cc @@ -110,17 +110,9 @@ Foreman::Foreman(Scheduler::Ptr const& scheduler, unsigned int poolSize, unsigne _mark = make_shared(ERR_LOC, "Forman Test Msg"); // Read-only access to the result files via the HTTP protocol's method "GET" - // - // NOTE: The following config doesn't seem to work due to multiple instances - // of '/' that's present in a value passed for the pattern parameter - // (the first parameter) of the called method. - // - // _httpServer->addStaticContent(workerConfig->resultsDirname() + "/*", "/"); - // - // Using this insecure config instead. The problem will get fixed later. auto const workerConfig = wconfig::WorkerConfig::instance(); - _httpServer->addStaticContent("/*", "/"); - _httpServer->addHandler("DELETE", workerConfig->resultsDirname() + "/:file", + _httpServer->addStaticContent("/*", workerConfig->resultsDirname()); + _httpServer->addHandler("DELETE", "/:file", [](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { resp->sendStatus(::removeResultFile(req->path)); });