diff --git a/src/czar/CMakeLists.txt b/src/czar/CMakeLists.txt index cd54e4777..6981302a2 100644 --- a/src/czar/CMakeLists.txt +++ b/src/czar/CMakeLists.txt @@ -6,9 +6,6 @@ target_sources(czar PRIVATE ChttpCzarSvc.cc ChttpModule.cc Czar.cc - HttpCzarIngestModule.cc - HttpCzarSvc.cc - HttpCzarQueryModule.cc HttpModule.cc HttpMonitorModule.cc HttpSvc.cc @@ -55,6 +52,5 @@ function(CZAR_UTILS) endfunction() czar_utils( - qserv-czar-http qserv-czar-https -) \ No newline at end of file +) diff --git a/src/czar/HttpCzarQueryModule.cc b/src/czar/HttpCzarQueryModule.cc deleted file mode 100644 index 5d45e77d0..000000000 --- a/src/czar/HttpCzarQueryModule.cc +++ /dev/null @@ -1,339 +0,0 @@ -/* - * LSST Data Management System - * - * This product includes software developed by the - * LSST Project (http://www.lsst.org/). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the LSST License Statement and - * the GNU General Public License along with this program. If not, - * see . - */ - -// Class header -#include "czar/HttpCzarQueryModule.h" - -// System headers -#include -#include -#include - -// Qserv headers -#include "cconfig/CzarConfig.h" -#include "czar/Czar.h" -#include "czar/SubmitResult.h" -#include "global/intTypes.h" -#include "http/Exceptions.h" -#include "http/RequestQuery.h" -#include "qdisp/CzarStats.h" -#include "sql/SqlConnection.h" -#include "sql/SqlConnectionFactory.h" -#include "sql/SqlResults.h" -#include "sql/Schema.h" -#include "util/String.h" - -using namespace std; -using json = nlohmann::json; - -namespace { -// NOTE: values of the MySQL type B(N) are too reported as binary strings where -// the number of characters is equal to CEIL(N/8). -vector const binTypes = {"BIT", "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB"}; -} // namespace - -namespace lsst::qserv::czar { - -void HttpCzarQueryModule::process(string const& context, shared_ptr const& req, - shared_ptr const& resp, string const& subModuleName, - http::AuthType const authType) { - HttpCzarQueryModule module(context, req, resp); - module.execute(subModuleName, authType); -} - -HttpCzarQueryModule::HttpCzarQueryModule(string const& context, shared_ptr const& req, - shared_ptr const& resp) - : HttpModule(context, req, resp) {} - -json HttpCzarQueryModule::executeImpl(string const& subModuleName) { - string const func = string(__func__) + "[sub-module='" + subModuleName + "']"; - debug(func); - if (subModuleName == "SUBMIT") - return _submit(); - else if (subModuleName == "SUBMIT-ASYNC") - return _submitAsync(); - else if (subModuleName == "CANCEL") - return _cancel(); - else if (subModuleName == "STATUS") - return _status(); - else if (subModuleName == "RESULT") - return _result(); - throw invalid_argument(context() + func + " unsupported sub-module"); -} - -json HttpCzarQueryModule::_submit() { - debug(__func__); - checkApiVersion(__func__, 35); - - string const binaryEncodingStr = body().optional("binary_encoding", "hex"); - http::BinaryEncodingMode const binaryEncoding = http::parseBinaryEncoding(binaryEncodingStr); - debug(__func__, "binary_encoding=" + http::binaryEncoding2string(binaryEncoding)); - - SubmitResult const submitResult = _getRequestParamsAndSubmit(__func__, false); - return _waitAndExtractResult(submitResult, binaryEncoding); -} - -json HttpCzarQueryModule::_submitAsync() { - debug(__func__); - checkApiVersion(__func__, 32); - SubmitResult const submitResult = _getRequestParamsAndSubmit(__func__, true); - return json::object({{"queryId", submitResult.queryId}}); -} - -SubmitResult HttpCzarQueryModule::_getRequestParamsAndSubmit(string const& func, bool async) { - string const userQuery = body().required("query"); - string const defaultDatabase = body().optional("database", string()); - debug(__func__, "query=" + userQuery); - debug(__func__, "database=" + defaultDatabase); - string const query = async ? "SUBMIT " + userQuery : userQuery; - map const hints{{"db", defaultDatabase}}; - SubmitResult const submitResult = Czar::getCzar()->submitQuery(query, hints); - if (!submitResult.errorMessage.empty()) { - _dropTable(submitResult.messageTable); - throw http::Error(context() + __func__, submitResult.errorMessage); - } - return submitResult; -} - -json HttpCzarQueryModule::_cancel() { - debug(__func__); - checkApiVersion(__func__, 30); - QueryId const queryId = _getQueryId(); - string const clientId; - Czar::getCzar()->killQuery("CANCEL " + to_string(queryId), clientId); - return json::object(); -} - -json HttpCzarQueryModule::_status() { - debug(__func__); - checkApiVersion(__func__, 30); - SubmitResult const submitResult = _getQueryInfo(); - json statusJson = json::object(); - statusJson["queryId"] = submitResult.queryId; - statusJson["status"] = submitResult.status; - statusJson["totalChunks"] = submitResult.totalChunks; - statusJson["completedChunks"] = submitResult.completedChunks; - statusJson["queryBeginEpoch"] = submitResult.queryBeginEpoch; - statusJson["lastUpdateEpoch"] = submitResult.lastUpdateEpoch; - return json::object({{"status", statusJson}}); -} - -json HttpCzarQueryModule::_result() { - debug(__func__); - checkApiVersion(__func__, 35); - string const binaryEncodingStr = query().optionalString("binary_encoding", "hex"); - http::BinaryEncodingMode const binaryEncoding = http::parseBinaryEncoding(binaryEncodingStr); - debug(__func__, "binary_encoding=" + http::binaryEncoding2string(binaryEncoding)); - return _waitAndExtractResult(_getQueryInfo(), binaryEncoding); -} - -QueryId HttpCzarQueryModule::_getQueryId() const { - // The input is going to sanitized by turning the string into a number of - // the corresponding type to ensure it's formally valid. - string const queryIdStr = params().at("qid"); - debug(__func__, "qid=" + queryIdStr); - return stoull(queryIdStr); -} - -SubmitResult HttpCzarQueryModule::_getQueryInfo() const { - QueryId const queryId = _getQueryId(); - SubmitResult submitResult; - try { - submitResult = Czar::getCzar()->getQueryInfo(queryId); - } catch (exception const& ex) { - string const msg = - "failed to obtain info for queryId=" + to_string(queryId) + ", ex: " + string(ex.what()); - error(__func__, msg); - throw http::Error(context() + __func__, msg); - } - if (!submitResult.errorMessage.empty()) { - throw http::Error(context() + __func__, submitResult.errorMessage); - } - return submitResult; -} - -json HttpCzarQueryModule::_waitAndExtractResult(SubmitResult const& submitResult, - http::BinaryEncodingMode binaryEncoding) const { - // Block the current thread before the query will finish or fail. - string const messageSelectQuery = - "SELECT chunkId, code, message, severity+0, timeStamp FROM " + submitResult.messageTable; - auto const conn = - sql::SqlConnectionFactory::make(cconfig::CzarConfig::instance()->getMySqlResultConfig()); - sql::SqlResults messageQueryResults; - sql::SqlErrorObject messageQueryErr; - if (!conn->runQuery(messageSelectQuery, messageQueryResults, messageQueryErr)) { - _dropTable(submitResult.messageTable); - _dropTable(submitResult.resultTable); - string const msg = "failed query=" + messageSelectQuery + " err=" + messageQueryErr.printErrMsg(); - error(__func__, msg); - throw http::Error(context() + __func__, msg); - } - - // Read thе message table to see if the user query suceeded or failed - vector chunkId; - vector code; - vector message; - vector severity; - sql::SqlErrorObject messageProcessErr; - if (!messageQueryResults.extractFirst4Columns(chunkId, code, message, severity, messageProcessErr)) { - messageQueryResults.freeResults(); - _dropTable(submitResult.messageTable); - _dropTable(submitResult.resultTable); - string const msg = "failed to extract results of query=" + messageSelectQuery + - " err=" + messageProcessErr.printErrMsg(); - error(__func__, msg); - throw http::Error(context() + __func__, msg); - } - string errorMsg; - for (size_t i = 0; i < chunkId.size(); ++i) { - if (stoi(code[i]) > 0) { - errorMsg += "[chunkId=" + chunkId[i] + " code=" + code[i] + " message=" + message[i] + - " severity=" + severity[i] + "], "; - } - } - if (!errorMsg.empty()) { - messageQueryResults.freeResults(); - _dropTable(submitResult.messageTable); - _dropTable(submitResult.resultTable); - error(__func__, errorMsg); - throw http::Error(context() + __func__, errorMsg); - } - messageQueryResults.freeResults(); - _dropTable(submitResult.messageTable); - - // Read a result set from the result table, package it into the JSON object - // and sent it back to a user. - sql::SqlResults resultQueryResults; - sql::SqlErrorObject resultQueryErr; - if (!conn->runQuery(submitResult.resultQuery, resultQueryResults, resultQueryErr)) { - _dropTable(submitResult.resultTable); - string const msg = - "failed query=" + submitResult.resultQuery + " err=" + resultQueryErr.printErrMsg(); - error(__func__, msg); - throw http::Error(context() + __func__, msg); - } - - sql::SqlErrorObject makeSchemaErr; - json const schemaJson = _schemaToJson(resultQueryResults.makeSchema(makeSchemaErr)); - if (makeSchemaErr.isSet()) { - resultQueryResults.freeResults(); - _dropTable(submitResult.resultTable); - string const msg = "failed to extract schema for query=" + submitResult.resultQuery + - " err=" + makeSchemaErr.printErrMsg(); - error(__func__, msg); - throw http::Error(context() + __func__, msg); - } - json rowsJson = _rowsToJson(resultQueryResults, schemaJson, binaryEncoding); - resultQueryResults.freeResults(); - _dropTable(submitResult.resultTable); - return json::object({{"schema", schemaJson}, {"rows", rowsJson}}); -} - -void HttpCzarQueryModule::_dropTable(string const& tableName) const { - if (tableName.empty()) return; - string const query = "DROP TABLE " + tableName; - debug(__func__, query); - auto const conn = - sql::SqlConnectionFactory::make(cconfig::CzarConfig::instance()->getMySqlResultConfig()); - sql::SqlErrorObject err; - if (!conn->runQuery(query, err)) { - error(__func__, "failed query=" + query + " err=" + err.printErrMsg()); - } -} - -json HttpCzarQueryModule::_schemaToJson(sql::Schema const& schema) const { - json schemaJson = json::array(); - for (auto const& colDef : schema.columns) { - json columnJson = json::object(); - columnJson["table"] = colDef.table; - columnJson["column"] = colDef.name; - columnJson["type"] = colDef.colType.sqlType; - int isBinary = 0; - for (size_t binTypeIdx = 0; binTypeIdx < ::binTypes.size(); ++binTypeIdx) { - string const& binType = ::binTypes[binTypeIdx]; - if (colDef.colType.sqlType.substr(0, binType.size()) == binType) { - isBinary = 1; - break; - } - } - columnJson["is_binary"] = isBinary; - schemaJson.push_back(columnJson); - } - return schemaJson; -} - -json HttpCzarQueryModule::_rowsToJson(sql::SqlResults& results, json const& schemaJson, - http::BinaryEncodingMode binaryEncoding) const { - // Extract the column binary attributes into the vector. Checkimg column type - // status in the vector should work significantly faster comparing with JSON. - size_t const numColumns = schemaJson.size(); - vector isBinary(numColumns, false); - for (size_t colIdx = 0; colIdx < numColumns; ++colIdx) { - isBinary[colIdx] = schemaJson[colIdx].at("is_binary"); - } - json rowsJson = json::array(); - for (sql::SqlResults::iterator itr = results.begin(); itr != results.end(); ++itr) { - sql::SqlResults::value_type const& row = *itr; - json rowJson = json::array(); - for (size_t i = 0; i < row.size(); ++i) { - if (row[i].first == nullptr) { - rowJson.push_back("NULL"); - } else { - if (isBinary[i]) { - switch (binaryEncoding) { - case http::BinaryEncodingMode::HEX: - rowJson.push_back(util::String::toHex(row[i].first, row[i].second)); - break; - case http::BinaryEncodingMode::B64: - rowJson.push_back(util::String::toBase64(row[i].first, row[i].second)); - break; - case http::BinaryEncodingMode::ARRAY: - // Notes on the std::u8string type and constructor: - // 1. This string type is required for encoding binary data which is only possible - // with the 8-bit encoding and not possible with the 7-bit ASCII - // representation. - // 2. This from of string construction allows the line termination symbols \0 - // within the binary data. - // - // ATTENTION: formally this way of type casting is wrong as it breaks strict - // aliasing. - // However, for all practical purposes, char8_t is basically a unsigned char - // which makes such operation possible. The problem could be addressed either by - // redesigning Qserv's SQL library to report data as char8_t, or by explicitly - // copying and translating each byte from char to char8_t representation (which - // would not be terribly efficient for the large result sets). - rowJson.push_back( - u8string(reinterpret_cast(row[i].first), row[i].second)); - break; - default: - throw http::Error(context() + __func__, "unsupported binary encoding"); - } - } else { - rowJson.push_back(string(row[i].first, row[i].second)); - } - } - } - rowsJson.push_back(rowJson); - } - return rowsJson; -} - -} // namespace lsst::qserv::czar diff --git a/src/czar/HttpCzarQueryModule.h b/src/czar/HttpCzarQueryModule.h deleted file mode 100644 index 0f267ba07..000000000 --- a/src/czar/HttpCzarQueryModule.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * LSST Data Management System - * - * This product includes software developed by the - * LSST Project (http://www.lsst.org/). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the LSST License Statement and - * the GNU General Public License along with this program. If not, - * see . - */ -#ifndef LSST_QSERV_CZAR_HTTPCZARQUERYMODULE_H -#define LSST_QSERV_CZAR_HTTPCZARQUERYMODULE_H - -// System headers -#include -#include - -// Third party headers -#include "nlohmann/json.hpp" - -// Qserv headers -#include "czar/HttpModule.h" -#include "global/intTypes.h" -#include "http/BinaryEncoding.h" - -// Forward declarations - -namespace lsst::qserv::czar { -struct SubmitResult; -} // namespace lsst::qserv::czar - -namespace lsst::qserv::qhttp { -class Request; -class Response; -} // namespace lsst::qserv::qhttp - -namespace lsst::qserv::sql { -class SqlResults; -struct Schema; -} // namespace lsst::qserv::sql - -// This header declarations -namespace lsst::qserv::czar { - -/** - * Class HttpCzarQueryModule implements a handler for processing user - * queries submitted to Czar via the HTTP-based frontend. - */ -class HttpCzarQueryModule : public czar::HttpModule { -public: - /** - * @note supported values for parameter 'subModuleName' are: - * 'SUBMIT' - submit a sync query - * 'SUBMIT-ASYNC' - submit an async query - * 'CANCEL' - cancel the previously submited async query - * 'STATUS' - return a status of the previously submited async query - * 'RESULT' - return data of the previously submited async query - * - * @throws std::invalid_argument for unknown values of parameter 'subModuleName' - */ - static void process(std::string const& context, std::shared_ptr const& req, - std::shared_ptr const& resp, std::string const& subModuleName, - http::AuthType const authType = http::AuthType::NONE); - - HttpCzarQueryModule() = delete; - HttpCzarQueryModule(HttpCzarQueryModule const&) = delete; - HttpCzarQueryModule& operator=(HttpCzarQueryModule const&) = delete; - - ~HttpCzarQueryModule() final = default; - -protected: - virtual nlohmann::json executeImpl(std::string const& subModuleName) final; - -private: - HttpCzarQueryModule(std::string const& context, std::shared_ptr const& req, - std::shared_ptr const& resp); - - nlohmann::json _submit(); - nlohmann::json _submitAsync(); - nlohmann::json _cancel(); - nlohmann::json _status(); - nlohmann::json _result(); - - SubmitResult _getRequestParamsAndSubmit(std::string const& func, bool async); - SubmitResult _getQueryInfo() const; - QueryId _getQueryId() const; - nlohmann::json _waitAndExtractResult(SubmitResult const& submitResult, - http::BinaryEncodingMode binaryEncoding) const; - void _dropTable(std::string const& tableName) const; - nlohmann::json _schemaToJson(sql::Schema const& schema) const; - nlohmann::json _rowsToJson(sql::SqlResults& results, nlohmann::json const& schemaJson, - http::BinaryEncodingMode binaryEncoding) const; -}; - -} // namespace lsst::qserv::czar - -#endif // LSST_QSERV_CZAR_HTTPCZARQUERYMODULE_H diff --git a/src/czar/HttpCzarSvc.cc b/src/czar/HttpCzarSvc.cc deleted file mode 100644 index ca4326904..000000000 --- a/src/czar/HttpCzarSvc.cc +++ /dev/null @@ -1,157 +0,0 @@ -/* - * LSST Data Management System - * - * This product includes software developed by the - * LSST Project (http://www.lsst.org/). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the LSST License Statement and - * the GNU General Public License along with this program. If not, - * see . - */ - -// Class header -#include "czar/HttpCzarSvc.h" - -// System headers -#include - -// Qserv headers -#include "cconfig/CzarConfig.h" -#include "czar/HttpCzarIngestModule.h" -#include "czar/HttpCzarQueryModule.h" -#include "http/MetaModule.h" -#include "qhttp/Server.h" - -// LSST headers -#include "lsst/log/Log.h" - -using namespace nlohmann; -using namespace std; - -namespace { - -LOG_LOGGER _log = LOG_GET("lsst.qserv.czar.HttpCzarSvc"); - -string const serviceName = "CZAR-FRONTEND "; - -} // namespace - -namespace lsst::qserv::czar { - -shared_ptr HttpCzarSvc::create(uint16_t port, unsigned int numThreads) { - return shared_ptr(new HttpCzarSvc(port, numThreads)); -} - -HttpCzarSvc::HttpCzarSvc(uint16_t port, unsigned int numThreads) : _port(port), _numThreads(numThreads) {} - -uint16_t HttpCzarSvc::start() { - string const context = "czar::HttpCzarSvc::" + string(__func__) + " "; - if (_httpServerPtr != nullptr) { - throw logic_error(context + "the service is already running."); - } - _httpServerPtr = qhttp::Server::create(_io_service, _port); - - auto const self = shared_from_this(); - - // Make sure the handlers are registered and the server is started before - // launching any BOOST ASIO threads. This will prevent threads from finishing - // due to a lack of work to be done. - _httpServerPtr->addHandlers( - {{"GET", "/meta/version", - [self](shared_ptr const& req, shared_ptr const& resp) { - json const info = json::object( - {{"kind", "qserv-czar-query-frontend"}, - {"id", cconfig::CzarConfig::instance()->id()}, - {"instance_id", cconfig::CzarConfig::instance()->replicationInstanceId()}}); - http::MetaModule::process(::serviceName, info, req, resp, "VERSION"); - }}}); - _httpServerPtr->addHandlers( - {{"POST", "/query", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarQueryModule::process(::serviceName, req, resp, "SUBMIT"); - }}}); - _httpServerPtr->addHandlers( - {{"POST", "/query-async", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarQueryModule::process(::serviceName, req, resp, "SUBMIT-ASYNC"); - }}}); - _httpServerPtr->addHandlers( - {{"DELETE", "/query-async/:qid", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarQueryModule::process(::serviceName, req, resp, "CANCEL"); - }}}); - _httpServerPtr->addHandlers( - {{"GET", "/query-async/status/:qid", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarQueryModule::process(::serviceName, req, resp, "STATUS"); - }}}); - _httpServerPtr->addHandlers( - {{"GET", "/query-async/result/:qid", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarQueryModule::process(::serviceName, req, resp, "RESULT"); - }}}); - _httpServerPtr->addHandlers( - {{"POST", "/ingest/data", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarIngestModule::process(self->_io_service, ::serviceName, req, resp, "INGEST-DATA"); - }}}); - _httpServerPtr->addHandlers( - {{"DELETE", "/ingest/database/:database", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarIngestModule::process(self->_io_service, ::serviceName, req, resp, - "DELETE-DATABASE"); - }}}); - _httpServerPtr->addHandlers( - {{"DELETE", "/ingest/table/:database/:table", - [self](shared_ptr const& req, shared_ptr const& resp) { - HttpCzarIngestModule::process(self->_io_service, ::serviceName, req, resp, "DELETE-TABLE"); - }}}); - _httpServerPtr->start(); - - // Initialize the I/O context and start the service threads. At this point - // the server will be ready to service incoming requests. - for (unsigned int i = 0; i < _numThreads; ++i) { - _threads.push_back(make_unique([self]() { self->_io_service.run(); })); - } - auto const actualPort = _httpServerPtr->getPort(); - LOGS(_log, LOG_LVL_INFO, context + "started on port " + to_string(actualPort)); - return actualPort; -} - -void HttpCzarSvc::stop() { - string const context = "czar::HttpCzarSvc::" + string(__func__) + " "; - if (_httpServerPtr == nullptr) { - throw logic_error(context + "the service is not running."); - } - - // Stopping the server and resetting the I/O context will abort the ongoing - // requests and unblock the service threads. - _httpServerPtr->stop(); - _httpServerPtr = nullptr; - _io_service.reset(); - - LOGS(_log, LOG_LVL_INFO, context + "stopped"); -} - -void HttpCzarSvc::wait() { - string const context = "czar::HttpCzarSvc::" + string(__func__) + " "; - if (_httpServerPtr == nullptr) { - throw logic_error(context + "the service is not running."); - } - for (auto&& t : _threads) { - t->join(); - } - LOGS(_log, LOG_LVL_INFO, context + "unlocked"); -} - -} // namespace lsst::qserv::czar diff --git a/src/czar/HttpCzarSvc.h b/src/czar/HttpCzarSvc.h deleted file mode 100644 index 3bcc78061..000000000 --- a/src/czar/HttpCzarSvc.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * LSST Data Management System - * - * This product includes software developed by the - * LSST Project (http://www.lsst.org/). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the LSST License Statement and - * the GNU General Public License along with this program. If not, - * see . - */ -#ifndef LSST_QSERV_CZAR_HTTPCZARSVC_H -#define LSST_QSERV_CZAR_HTTPCZARSVC_H - -// System headers -#include -#include -#include -#include - -// Third party headers -#include "boost/asio.hpp" - -namespace lsst::qserv::qhttp { -class Server; -} // namespace lsst::qserv::qhttp - -// This header declarations -namespace lsst::qserv::czar { - -/** - * Class HttpCzarSvc is the HTTP server for processing user requests. - * - * The server creates and manages its own collection of BOOST ASIO service threads. - * The number of threads is specified via the corresponding parameter of the class's - * constructor. - * - * Typical usage of the class: - * @code - * // Create the server. Note, it won't run yet until explicitly started. - * uint16_t const port = 0; // The port will be dynamically allocated at start - * unsigned int const numThreads = 2; // The number of BOOST ASIO threads - * auto const svc = czar::HttpCzarSvc::create(port, numThreads); - * - * // Start the server and get the actual port number. - * uint16_t const actualPort = svc->start(); - * std::cout << "HTTP server is running on port " << actualPort << std::endl; - * - * // Stop the server to release resources. - * svc->stop(); - * svc.reset(); - * @code - * Alternatively, one may wait before the service will finish. In this case - * the server would need to be stopped from some other thread. For example: - * @code - * auto const svc = ... - * svc->start(); - * std::thread([svc]() { - * std::this_thread::sleep_for(60s); - * svc->stop(); - * }); - * svc->wait(); - * @code - * @note The class implementation is NOT thread safe. A correct ordering of - * calls to the methods 'start -> {stop | wait}' is required. - */ -class HttpCzarSvc : public std::enable_shared_from_this { -public: - /** - * The factory will not initialize ASIO context and threads, or start - * the server. This has to be done by calling method HttpCzarSvc::start() - * - * @param port The number of a port to bind to. - * @param numThreads The number of BOOST ASIO threads. - * @return The shared pointer to the running server. - */ - static std::shared_ptr create(uint16_t port, unsigned int numThreads); - - HttpCzarSvc() = delete; - HttpCzarSvc(HttpCzarSvc const&) = delete; - HttpCzarSvc& operator=(HttpCzarSvc const&) = delete; - - ~HttpCzarSvc() = default; - - /** - * Initialize ASIO context and threads, and start the server. - * - * @note Once the server is started it has to be explicitly stopped - * using the counterpart method stop() to allow releasing allocated - * resources and letting the destructor to be executed. Note that - * service threads started by the curent method and the HTTP server - * incerement the reference counter on the shared pointer that is - * returned by the class's factory method. - * - * @return The actual port number on which the server is run. - * @throws std::logic_error If the server is already running. - */ - uint16_t start(); - - /** - * Stop the server and release the relevant resources. - * @throws std::logic_error If the server is not running. - */ - void stop(); - - /** - * Block the calling thread waiting before the server threads will finish. - * @throws std::logic_error If the server is not running. - */ - void wait(); - -private: - /** - * The constructor will not initialize ASIO context and threads, or start - * the server. This has to be done by calling method HttpCzarSvc::start() - * @param port The number of a port to bind to. - * @param numThreads The number of BOOST ASIO threads. - */ - HttpCzarSvc(uint16_t port, unsigned int numThreads); - - // Input parameters - - uint16_t const _port; ///< The input port number (could be 0 to allow autoallocation). - unsigned int const _numThreads; ///< The number of the BOOST ASIO service threads. - - /// Worker management requests are processed by this server. - std::shared_ptr _httpServerPtr; - - /// The BOOST ASIO I/O services. - boost::asio::io_service _io_service; - - /// The thread pool for running ASIO services. - std::vector> _threads; -}; - -} // namespace lsst::qserv::czar - -#endif // LSST_QSERV_CZAR_HTTPCZARSVC_H diff --git a/src/czar/qserv-czar-http.cc b/src/czar/qserv-czar-http.cc deleted file mode 100644 index 526a894b0..000000000 --- a/src/czar/qserv-czar-http.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* - * LSST Data Management System - * - * This product includes software developed by the - * LSST Project (http://www.lsst.org/). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the LSST License Statement and - * the GNU General Public License along with this program. If not, - * see . - */ - -/** - * The QHTTP-based frontend for Czar. - */ - -// System headers -#include -#include -#include -#include - -// Qserv headers -#include "czar/Czar.h" -#include "czar/HttpCzarSvc.h" -#include "global/stringUtil.h" - -using namespace std; -namespace czar = lsst::qserv::czar; -namespace qserv = lsst::qserv; - -namespace { -string const usage = "Usage: "; -} - -int main(int argc, char* argv[]) { - // Parse command-line parameters to get: - // - the name of Czar - // - a path to the configuration files - // - the port number (0 value would result in allocating the first available port) - // - the number of service threads (0 value would assume the number of host machine's - // hardware threads) - // - a location of the SSL/TSL certificate for the secure connections - // - a location of the SSL/TSL private key - if (argc != 7) { - cerr << __func__ << ": insufficient number of the command-line parameters\n" << ::usage << endl; - return 1; - } - int nextArg = 1; - string const czarName = argv[nextArg++]; - string const configFilePath = argv[nextArg++]; - uint16_t port = 0; - unsigned int numThreads = 0; - try { - int const portParsed = stoi(argv[nextArg++]); - if (portParsed < 0 || portParsed > numeric_limits::max()) { - cerr << __func__ << ": the port number is not valid\n" << ::usage << endl; - return 1; - } - port = static_cast(portParsed); - numThreads = qserv::stoui(argv[nextArg++]); - if (numThreads == 0) numThreads = thread::hardware_concurrency(); - } catch (exception const& ex) { - cerr << __func__ << ": failed to parse command line parameters\n" << ::usage << endl; - return 1; - } - string const sslCertFile = argv[nextArg++]; - string const sslPrivateKeyFile = argv[nextArg++]; - try { - auto const czar = czar::Czar::createCzar(configFilePath, czarName); - auto const svc = czar::HttpCzarSvc::create(port, numThreads); - port = svc->start(); - cout << __func__ << ": HTTP-based query processing service of Czar started on port " << port << endl; - svc->wait(); - } catch (exception const& ex) { - cerr << __func__ << ": the application failed, exception: " << ex.what() << endl; - return 1; - } - return 0; -} \ No newline at end of file