///////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2016-2019 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler //////////////////////////////////////////////////////////////////////////////// #include "GeneralServerFeature.h" #include #include "Actions/RestActionHandler.h" #include "Agency/AgencyFeature.h" #include "Agency/RestAgencyHandler.h" #include "Agency/RestAgencyPrivHandler.h" #include "Aql/RestAqlHandler.h" #include "Basics/StringUtils.h" #include "Cluster/AgencyCallbackRegistry.h" #include "Cluster/ClusterFeature.h" #include "Cluster/MaintenanceRestHandler.h" #include "Cluster/RestAgencyCallbacksHandler.h" #include "Cluster/RestClusterHandler.h" #include "Cluster/TraverserEngineRegistry.h" #include "GeneralServer/AuthenticationFeature.h" #include "GeneralServer/GeneralServer.h" #include "GeneralServer/RestHandlerFactory.h" #include "Graph/Graph.h" #include "InternalRestHandler/InternalRestTraverserHandler.h" #include "ProgramOptions/Parameters.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" #include "RestHandler/RestAdminDatabaseHandler.h" #include "RestHandler/RestAdminExecuteHandler.h" #include "RestHandler/RestAdminLogHandler.h" #include "RestHandler/RestAdminRoutingHandler.h" #include "RestHandler/RestAdminServerHandler.h" #include "RestHandler/RestAdminStatisticsHandler.h" #include "RestHandler/RestAnalyzerHandler.h" #include "RestHandler/RestAqlFunctionsHandler.h" #include "RestHandler/RestAqlReloadHandler.h" #include "RestHandler/RestAqlUserFunctionsHandler.h" #include "RestHandler/RestAuthHandler.h" #include "RestHandler/RestAuthReloadHandler.h" #include "RestHandler/RestBatchHandler.h" #include "RestHandler/RestCollectionHandler.h" #include "RestHandler/RestControlPregelHandler.h" #include "RestHandler/RestCursorHandler.h" #include "RestHandler/RestDatabaseHandler.h" #include "RestHandler/RestDebugHandler.h" #include "RestHandler/RestDocumentHandler.h" #include "RestHandler/RestEdgesHandler.h" #include "RestHandler/RestEndpointHandler.h" #include "RestHandler/RestEngineHandler.h" #include "RestHandler/RestExplainHandler.h" #include "RestHandler/RestGraphHandler.h" #include "RestHandler/RestHandlerCreator.h" #ifdef USE_ENTERPRISE #include "Enterprise/RestHandler/RestHotBackupHandler.h" #endif #include "RestHandler/RestImportHandler.h" #include "RestHandler/RestIndexHandler.h" #include "RestHandler/RestJobHandler.h" #include "RestHandler/RestPleaseUpgradeHandler.h" #include "RestHandler/RestPregelHandler.h" #include "RestHandler/RestQueryCacheHandler.h" #include "RestHandler/RestQueryHandler.h" #include "RestHandler/RestRepairHandler.h" #include "RestHandler/RestShutdownHandler.h" #include "RestHandler/RestSimpleHandler.h" #include "RestHandler/RestSimpleQueryHandler.h" #include "RestHandler/RestStatusHandler.h" #include "RestHandler/RestTasksHandler.h" #include "RestHandler/RestTestHandler.h" #include "RestHandler/RestTimeHandler.h" #include "RestHandler/RestTransactionHandler.h" #include "RestHandler/RestTtlHandler.h" #include "RestHandler/RestUploadHandler.h" #include "RestHandler/RestUsersHandler.h" #include "RestHandler/RestVersionHandler.h" #include "RestHandler/RestViewHandler.h" #include "RestHandler/RestWalAccessHandler.h" #include "RestServer/EndpointFeature.h" #include "RestServer/QueryRegistryFeature.h" #include "RestServer/ServerFeature.h" #include "RestServer/TraverserEngineRegistryFeature.h" #include "Scheduler/Scheduler.h" #include "Scheduler/SchedulerFeature.h" #include "Ssl/SslServerFeature.h" #include "StorageEngine/EngineSelectorFeature.h" #include "StorageEngine/StorageEngine.h" #ifdef USE_ENTERPRISE #include "Enterprise/StorageEngine/HotBackupFeature.h" #endif #include "V8Server/V8DealerFeature.h" using namespace arangodb::rest; using namespace arangodb::options; namespace arangodb { static uint64_t const _maxIoThreads = 64; rest::RestHandlerFactory* GeneralServerFeature::HANDLER_FACTORY = nullptr; rest::AsyncJobManager* GeneralServerFeature::JOB_MANAGER = nullptr; GeneralServerFeature* GeneralServerFeature::GENERAL_SERVER = nullptr; GeneralServerFeature::GeneralServerFeature(application_features::ApplicationServer& server) : ApplicationFeature(server, "GeneralServer"), _allowMethodOverride(false), _proxyCheck(true), _numIoThreads(0) { setOptional(true); startsAfter("AQLPhase"); startsAfter("Endpoint"); startsAfter("Upgrade"); startsAfter("SslServer"); _numIoThreads = (std::max)(static_cast(1), static_cast(TRI_numberProcessors() / 4)); if (_numIoThreads > _maxIoThreads) { _numIoThreads = _maxIoThreads; } } void GeneralServerFeature::collectOptions(std::shared_ptr options) { options->addSection("server", "Server features"); options->addOldOption("server.allow-method-override", "http.allow-method-override"); options->addOldOption("server.hide-product-header", "http.hide-product-header"); options->addOldOption("server.keep-alive-timeout", "http.keep-alive-timeout"); options->addOldOption("server.default-api-compatibility", ""); options->addOldOption("no-server", "server.rest-server"); options->addOption("--server.io-threads", "Number of threads used to handle IO", new UInt64Parameter(&_numIoThreads), arangodb::options::makeFlags(arangodb::options::Flags::Dynamic)); options->addSection("http", "HttpServer features"); options->addOption("--http.allow-method-override", "allow HTTP method override using special headers", new BooleanParameter(&_allowMethodOverride), arangodb::options::makeFlags(arangodb::options::Flags::Hidden)); options->addOption("--http.keep-alive-timeout", "keep-alive timeout in seconds", new DoubleParameter(&_keepAliveTimeout)); options->addOption( "--http.hide-product-header", "do not expose \"Server: ArangoDB\" header in HTTP responses", new BooleanParameter(&HttpResponse::HIDE_PRODUCT_HEADER)); options->addOption("--http.trusted-origin", "trusted origin URLs for CORS requests with credentials", new VectorParameter(&_accessControlAllowOrigins)); options->addSection("frontend", "Frontend options"); options->addOption("--frontend.proxy-request-check", "enable proxy request checking", new BooleanParameter(&_proxyCheck)); options->addOption("--frontend.trusted-proxy", "list of proxies to trust (may be IP or network). Make " "sure --frontend.proxy-request-check is enabled", new VectorParameter(&_trustedProxies)); } void GeneralServerFeature::validateOptions(std::shared_ptr) { if (!_accessControlAllowOrigins.empty()) { // trim trailing slash from all members for (auto& it : _accessControlAllowOrigins) { if (it == "*" || it == "all") { // special members "*" or "all" means all origins are allowed _accessControlAllowOrigins.clear(); _accessControlAllowOrigins.push_back("*"); break; } else if (it == "none") { // "none" means no origins are allowed _accessControlAllowOrigins.clear(); break; } else if (it[it.size() - 1] == '/') { // strip trailing slash it = it.substr(0, it.size() - 1); } } // remove empty members _accessControlAllowOrigins.erase( std::remove_if(_accessControlAllowOrigins.begin(), _accessControlAllowOrigins.end(), [](std::string const& value) { return basics::StringUtils::trim(value).empty(); }), _accessControlAllowOrigins.end()); } // we need at least one io thread and context if (_numIoThreads == 0) { LOG_TOPIC("1ade3", WARN, Logger::FIXME) << "Need at least one io-context thread."; _numIoThreads = 1; } else if (_numIoThreads > _maxIoThreads) { LOG_TOPIC("80dcf", WARN, Logger::FIXME) << "IO-contexts are limited to " << _maxIoThreads; _numIoThreads = _maxIoThreads; } } void GeneralServerFeature::prepare() { ServerState::instance()->setServerMode(ServerState::Mode::MAINTENANCE); GENERAL_SERVER = this; } void GeneralServerFeature::start() { _jobManager.reset(new AsyncJobManager); JOB_MANAGER = _jobManager.get(); _handlerFactory.reset(new RestHandlerFactory()); HANDLER_FACTORY = _handlerFactory.get(); defineHandlers(); buildServers(); for (auto& server : _servers) { server->startListening(); } } void GeneralServerFeature::beginShutdown() { for (auto& server : _servers) { server->stopListening(); } } void GeneralServerFeature::stop() { for (auto& server : _servers) { server->stopWorking(); } _jobManager->deleteJobs(); } void GeneralServerFeature::unprepare() { for (auto& server : _servers) { server->stopWorking(); } _servers.clear(); _jobManager.reset(); GENERAL_SERVER = nullptr; JOB_MANAGER = nullptr; HANDLER_FACTORY = nullptr; } void GeneralServerFeature::buildServers() { TRI_ASSERT(_jobManager != nullptr); EndpointFeature* endpoint = application_features::ApplicationServer::getFeature( "Endpoint"); auto const& endpointList = endpoint->endpointList(); // check if endpointList contains ssl featured server if (endpointList.hasSsl()) { SslServerFeature* ssl = application_features::ApplicationServer::getFeature( "SslServer"); if (ssl == nullptr) { LOG_TOPIC("8df10", FATAL, arangodb::Logger::FIXME) << "no ssl context is known, cannot create https server, " "please enable SSL"; FATAL_ERROR_EXIT(); } ssl->SSL->verifySslOptions(); } auto server = std::make_unique(_numIoThreads); server->setEndpointList(&endpointList); _servers.push_back(std::move(server)); } void GeneralServerFeature::defineHandlers() { TRI_ASSERT(_jobManager != nullptr); AgencyFeature* agency = application_features::ApplicationServer::getFeature( "Agency"); TRI_ASSERT(agency != nullptr); ClusterFeature* cluster = application_features::ApplicationServer::getFeature( "Cluster"); TRI_ASSERT(cluster != nullptr); AuthenticationFeature* authentication = application_features::ApplicationServer::getFeature( "Authentication"); TRI_ASSERT(authentication != nullptr); #ifdef USE_ENTERPRISE #ifndef _WIN32 HotBackupFeature* backup = application_features::ApplicationServer::getFeature( "HotBackup"); TRI_ASSERT(backup != nullptr); #endif #endif auto queryRegistry = QueryRegistryFeature::registry(); auto traverserEngineRegistry = TraverserEngineRegistryFeature::registry(); if (_combinedRegistries == nullptr) { _combinedRegistries = std::make_unique>( queryRegistry, traverserEngineRegistry); } else { TRI_ASSERT(false); } // ........................................................................... // /_msg // ........................................................................... _handlerFactory->addPrefixHandler("/_msg/please-upgrade", RestHandlerCreator::createNoData); // ........................................................................... // /_api // ........................................................................... _handlerFactory->addPrefixHandler( // add handler RestVocbaseBaseHandler::ANALYZER_PATH, // base URL RestHandlerCreator::createNoData // handler ); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::BATCH_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::CONTROL_PREGEL_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::CURSOR_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::DATABASE_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::DOCUMENT_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::EDGES_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::GHARIAL_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::ENDPOINT_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::IMPORT_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::INDEX_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_QUERY_ALL_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_QUERY_ALL_KEYS_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_QUERY_BY_EXAMPLE, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::SIMPLE_LOOKUP_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::SIMPLE_REMOVE_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::TASKS_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::UPLOAD_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::USERS_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::VIEW_PATH, RestHandlerCreator::createNoData); // This is the only handler were we need to inject // more than one data object. So we created the combinedRegistries // for it. _handlerFactory->addPrefixHandler( "/_api/aql", RestHandlerCreator::createData*>, _combinedRegistries.get()); _handlerFactory->addPrefixHandler("/_api/aql-builtin", RestHandlerCreator::createNoData); if (server()->isEnabled("V8Dealer")) { _handlerFactory->addPrefixHandler("/_api/aqlfunction", RestHandlerCreator::createNoData); } _handlerFactory->addPrefixHandler("/_api/explain", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_api/query", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_api/query-cache", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_api/pregel", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_api/wal", RestHandlerCreator::createNoData); if (agency->isEnabled()) { _handlerFactory->addPrefixHandler(RestVocbaseBaseHandler::AGENCY_PATH, RestHandlerCreator::createData, agency->agent()); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::AGENCY_PRIV_PATH, RestHandlerCreator::createData, agency->agent()); } if (cluster->isEnabled()) { // add "/agency-callbacks" handler _handlerFactory->addPrefixHandler( cluster->agencyCallbacksPath(), RestHandlerCreator::createData, cluster->agencyCallbackRegistry()); // add "_api/cluster" handler _handlerFactory->addPrefixHandler(cluster->clusterRestPath(), RestHandlerCreator::createNoData); } _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::INTERNAL_TRAVERSER_PATH, RestHandlerCreator::createData, traverserEngineRegistry); // And now some handlers which are registered in both /_api and /_admin _handlerFactory->addHandler("/_admin/actions", RestHandlerCreator::createNoData); _handlerFactory->addHandler("/_admin/aql/reload", RestHandlerCreator::createNoData); _handlerFactory->addHandler("/_admin/auth/reload", RestHandlerCreator::createNoData); if (V8DealerFeature::DEALER && V8DealerFeature::DEALER->allowAdminExecute()) { _handlerFactory->addHandler("/_admin/execute", RestHandlerCreator::createNoData); } _handlerFactory->addHandler("/_admin/time", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_api/job", RestHandlerCreator::createData, _jobManager.get()); _handlerFactory->addPrefixHandler("/_api/engine", RestHandlerCreator::createNoData); _handlerFactory->addHandler("/_api/version", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_api/transaction", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_api/ttl", RestHandlerCreator::createNoData); // ........................................................................... // /_admin // ........................................................................... _handlerFactory->addHandler("/_admin/status", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_admin/job", RestHandlerCreator::createData, _jobManager.get()); _handlerFactory->addHandler("/_admin/version", RestHandlerCreator::createNoData); // further admin handlers _handlerFactory->addPrefixHandler("/_admin/database/target-version", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler("/_admin/log", RestHandlerCreator::createNoData); if (server()->isEnabled("V8Dealer")) { _handlerFactory->addPrefixHandler("/_admin/routing", RestHandlerCreator::createNoData); } #ifdef ARANGODB_ENABLE_FAILURE_TESTS // This handler is to activate SYS_DEBUG_FAILAT on DB servers _handlerFactory->addPrefixHandler("/_admin/debug", RestHandlerCreator::createNoData); #endif _handlerFactory->addPrefixHandler("/_admin/shutdown", RestHandlerCreator::createNoData); if (authentication->isActive()) { _handlerFactory->addPrefixHandler("/_open/auth", RestHandlerCreator::createNoData); } _handlerFactory->addPrefixHandler("/_admin/server", RestHandlerCreator::createNoData); _handlerFactory->addHandler("/_admin/statistics", RestHandlerCreator::createNoData); _handlerFactory->addHandler("/_admin/statistics-description", RestHandlerCreator::createNoData); if (cluster->isEnabled()) { _handlerFactory->addPrefixHandler("/_admin/repair", RestHandlerCreator::createNoData); } #ifdef USE_ENTERPRISE #ifndef _WIN32 if (backup->isAPIEnabled()) { _handlerFactory->addPrefixHandler("/_admin/backup", RestHandlerCreator::createNoData); } #endif #endif // ........................................................................... // test handler // ........................................................................... #ifdef ARANGODB_ENABLE_MAINTAINER_MODE _handlerFactory->addPrefixHandler("/_api/test", RestHandlerCreator::createNoData); #endif // ........................................................................... // actions defined in v8 // ........................................................................... _handlerFactory->addPrefixHandler("/", RestHandlerCreator::createNoData); // engine specific handlers StorageEngine* engine = EngineSelectorFeature::ENGINE; TRI_ASSERT(engine != nullptr); // Engine not loaded. Startup broken engine->addRestHandlers(*_handlerFactory); } } // namespace arangodb