//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2016 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/RestAgencyCallbacksHandler.h" #include "Cluster/RestClusterHandler.h" #include "Cluster/TraverserEngineRegistry.h" #include "GeneralServer/AuthenticationFeature.h" #include "GeneralServer/GeneralServer.h" #include "GeneralServer/RestHandlerFactory.h" #include "InternalRestHandler/InternalRestTraverserHandler.h" #include "ProgramOptions/Parameters.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" #include "RestHandler/RestAdminLogHandler.h" #include "RestHandler/RestAdminRoutingHandler.h" #include "RestHandler/RestAdminServerHandler.h" #include "RestHandler/RestAdminStatisticsHandler.h" #include "RestHandler/RestAqlFunctionsHandler.h" #include "RestHandler/RestAqlUserFunctionsHandler.h" #include "RestHandler/RestAuthHandler.h" #include "RestHandler/RestBatchHandler.h" #include "RestHandler/RestCollectionHandler.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/RestHandlerCreator.h" #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/RestShutdownHandler.h" #include "RestHandler/RestSimpleHandler.h" #include "RestHandler/RestRepairHandler.h" #include "RestHandler/RestSimpleQueryHandler.h" #include "RestHandler/RestStatusHandler.h" #include "RestHandler/RestTransactionHandler.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" using namespace arangodb; using namespace arangodb::rest; using namespace arangodb::options; 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) { setOptional(true); startsAfter("Agency"); startsAfter("Authentication"); startsAfter("CheckVersion"); startsAfter("Database"); startsAfter("Endpoint"); startsAfter("FoxxQueues"); startsAfter("Random"); startsAfter("Scheduler"); startsAfter("Server"); startsAfter("Upgrade"); } 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->addSection("http", "HttpServer features"); options->addHiddenOption("--http.allow-method-override", "allow HTTP method override using special headers", new BooleanParameter(&_allowMethodOverride)); 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 or disable 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()); } } void GeneralServerFeature::prepare() { ServerState::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(); } // initially populate the authentication cache. otherwise no one // can access the new database auth::UserManager* um = AuthenticationFeature::instance()->userManager(); if (um != nullptr) { um->outdate(); } } void GeneralServerFeature::stop() { for (auto& server : _servers) { server->stopListening(); } } void GeneralServerFeature::unprepare() { for (auto& server : _servers) { delete server; } 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(FATAL, arangodb::Logger::FIXME) << "no ssl context is known, cannot create https server, " "please enable SSL"; FATAL_ERROR_EXIT(); } ssl->SSL->verifySslOptions(); } GeneralServer* server = new GeneralServer(); server->setEndpointList(&endpointList); _servers.push_back(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< AuthenticationFeature>("Authentication"); TRI_ASSERT(authentication != nullptr); auto queryRegistry = QueryRegistryFeature::QUERY_REGISTRY; auto traverserEngineRegistry = TraverserEngineRegistryFeature::TRAVERSER_ENGINE_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( RestVocbaseBaseHandler::BATCH_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::COLLECTION_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::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< aql::QueryRegistry*>, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_QUERY_ALL_KEYS_PATH, RestHandlerCreator::createData< aql::QueryRegistry*>, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_QUERY_BY_EXAMPLE, RestHandlerCreator::createData< aql::QueryRegistry*>, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_LOOKUP_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_REMOVE_PATH, RestHandlerCreator::createData, queryRegistry); _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< std::pair*>, _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< consensus::Agent*>, agency->agent()); } if (cluster->isEnabled()) { // add "/agency-callbacks" handler _handlerFactory->addPrefixHandler( cluster->agencyCallbacksPath(), RestHandlerCreator::createData< AgencyCallbackRegistry*>, cluster->agencyCallbackRegistry()); // add "_api/cluster" handler _handlerFactory->addPrefixHandler(cluster->clusterRestPath(), RestHandlerCreator::createNoData); } _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::INTERNAL_TRAVERSER_PATH, RestHandlerCreator::createData< traverser::TraverserEngineRegistry*>, traverserEngineRegistry); // And now some handlers which are registered in both /_api and /_admin _handlerFactory->addPrefixHandler( "/_api/job", RestHandlerCreator::createData< AsyncJobManager*>, _jobManager.get()); _handlerFactory->addPrefixHandler( "/_api/engine", RestHandlerCreator::createNoData); _handlerFactory->addHandler( "/_api/version", RestHandlerCreator::createNoData); _handlerFactory->addHandler( "/_api/transaction", RestHandlerCreator::createNoData); // ........................................................................... // /_admin // ........................................................................... _handlerFactory->addHandler( "/_admin/status", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( "/_admin/job", RestHandlerCreator::createData< AsyncJobManager*>, _jobManager.get()); _handlerFactory->addHandler( "/_admin/version", RestHandlerCreator::createNoData); // further admin handlers _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 ); } // ........................................................................... // 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.get()); }