//////////////////////////////////////////////////////////////////////////////// /// 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 "RestServerFeature.h" #include "ApplicationFeatures/SslFeature.h" #include "Aql/RestAqlHandler.h" #include "Agency/AgencyFeature.h" #include "Agency/RestAgencyHandler.h" #include "Agency/RestAgencyPrivHandler.h" #include "Cluster/AgencyCallbackRegistry.h" #include "Cluster/ClusterComm.h" #include "Cluster/ClusterFeature.h" #include "Cluster/RestAgencyCallbacksHandler.h" #include "Cluster/RestShardHandler.h" #include "Dispatcher/DispatcherFeature.h" #include "HttpServer/HttpHandlerFactory.h" #include "HttpServer/HttpServer.h" #include "HttpServer/HttpsServer.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" #include "Rest/Version.h" #include "RestHandler/RestAdminLogHandler.h" #include "RestHandler/RestBatchHandler.h" #include "RestHandler/RestCursorHandler.h" #include "RestHandler/RestDebugHandler.h" #include "RestHandler/RestDocumentHandler.h" #include "RestHandler/RestEdgesHandler.h" #include "RestHandler/RestExportHandler.h" #include "RestHandler/RestHandlerCreator.h" #include "RestHandler/RestImportHandler.h" #include "RestHandler/RestJobHandler.h" #include "RestHandler/RestPleaseUpgradeHandler.h" #include "RestHandler/RestQueryCacheHandler.h" #include "RestHandler/RestQueryHandler.h" #include "RestHandler/RestReplicationHandler.h" #include "RestHandler/RestShutdownHandler.h" #include "RestHandler/RestSimpleHandler.h" #include "RestHandler/RestSimpleQueryHandler.h" #include "RestHandler/RestUploadHandler.h" #include "RestHandler/RestVersionHandler.h" #include "RestHandler/WorkMonitorHandler.h" #include "RestServer/DatabaseFeature.h" #include "RestServer/EndpointFeature.h" #include "RestServer/ServerFeature.h" #include "Scheduler/SchedulerFeature.h" #include "V8Server/V8DealerFeature.h" #include "VocBase/server.h" using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::rest; using namespace arangodb::options; RestServerFeature::RestServerFeature( application_features::ApplicationServer* server, std::string const& authenticationRealm) : ApplicationFeature(server, "RestServer"), _keepAliveTimeout(300.0), _authenticationRealm(authenticationRealm), _defaultApiCompatibility(Version::getNumericServerVersion()), _allowMethodOverride(false), _authentication(true), _authenticationUnixSockets(true), _authenticationSystemOnly(false), _handlerFactory(nullptr), _jobManager(nullptr) { setOptional(true); requiresElevatedPrivileges(false); startsAfter("Dispatcher"); startsAfter("Endpoint"); startsAfter("Scheduler"); startsAfter("Server"); startsAfter("Agency"); } void RestServerFeature::collectOptions( std::shared_ptr options) { LOG_TOPIC(TRACE, Logger::STARTUP) << name() << "::collectOptions"; options->addSection("server", "Server features"); options->addHiddenOption("--server.default-api-compatibility", "default API compatibility version", new Int32Parameter(&_defaultApiCompatibility)); options->addOption("--server.authentication", "enable or disable authentication for ALL client requests", new BooleanParameter(&_authentication)); options->addOption( "--server.authentication-system-only", "use HTTP authentication only for requests to /_api and /_admin", new BooleanParameter(&_authenticationSystemOnly)); #ifdef ARANGODB_HAVE_DOMAIN_SOCKETS options->addOption("--server.authentication-unix-sockets", "authentication for requests via UNIX domain sockets", new BooleanParameter(&_authenticationUnixSockets)); #endif 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)); } void RestServerFeature::validateOptions(std::shared_ptr) { LOG_TOPIC(TRACE, Logger::STARTUP) << name() << "::validateOptions"; if (_defaultApiCompatibility < HttpRequest::MIN_COMPATIBILITY) { LOG(FATAL) << "invalid value for --server.default-api-compatibility. " "minimum allowed value is " << HttpRequest::MIN_COMPATIBILITY; FATAL_ERROR_EXIT(); } } static TRI_vocbase_t* LookupDatabaseFromRequest(HttpRequest* request, TRI_server_t* server) { // get database name from request std::string dbName = request->databaseName(); if (dbName.empty()) { // if no databases was specified in the request, use system database name // as a fallback dbName = TRI_VOC_SYSTEM_DATABASE; request->setDatabaseName(dbName); } if (ServerState::instance()->isCoordinator()) { return TRI_UseCoordinatorDatabaseServer(server, dbName.c_str()); } return TRI_UseDatabaseServer(server, dbName.c_str()); } static bool SetRequestContext(HttpRequest* request, void* data) { TRI_server_t* server = static_cast(data); TRI_vocbase_t* vocbase = LookupDatabaseFromRequest(request, server); // invalid database name specified, database not found etc. if (vocbase == nullptr) { return false; } // database needs upgrade if (vocbase->_state == (sig_atomic_t)TRI_VOCBASE_STATE_FAILED_VERSION) { request->setRequestPath("/_msg/please-upgrade"); return false; } VocbaseContext* ctx = new arangodb::VocbaseContext(request, server, vocbase); request->setRequestContext(ctx, true); // the "true" means the request is the owner of the context return true; } void RestServerFeature::prepare() { LOG_TOPIC(TRACE, Logger::STARTUP) << name() << "::prepare"; HttpHandlerFactory::setMaintenance(true); } void RestServerFeature::start() { LOG_TOPIC(TRACE, Logger::STARTUP) << name() << "::start"; LOG(DEBUG) << "using default API compatibility: " << (long int)_defaultApiCompatibility; _jobManager.reset(new AsyncJobManager(ClusterCommRestCallback)); auto vocbase = DatabaseFeature::DATABASE->vocbase(); V8DealerFeature::DEALER->loadJavascript(vocbase, "server/server.js"); _httpOptions._vocbase = vocbase; auto server = DatabaseFeature::DATABASE->server(); _handlerFactory.reset( new HttpHandlerFactory(_authenticationRealm, _defaultApiCompatibility, _allowMethodOverride, &SetRequestContext, server)); defineHandlers(); buildServers(); for (auto& server : _servers) { server->startListening(); } // disabled maintenance mode HttpHandlerFactory::setMaintenance(false); LOG(INFO) << "Authentication is turned " << (_authentication ? "on" : "off"); if (_authentication) { if (_authenticationSystemOnly) { LOG(INFO) << "Authentication system only"; } #ifdef ARANGODB_HAVE_DOMAIN_SOCKETS LOG(INFO) << "Authentication for unix sockets is turned " << (_authenticationUnixSockets ? "on" : "off"); #endif } LOG(INFO) << "ArangoDB (version " << ARANGODB_VERSION_FULL << ") is ready for business. Have fun!"; } void RestServerFeature::stop() { LOG_TOPIC(TRACE, Logger::STARTUP) << name() << "::stop"; for (auto& server : _servers) { server->stopListening(); } for (auto& server : _servers) { server->stop(); } for (auto& server : _servers) { delete server; } _httpOptions._vocbase = nullptr; } void RestServerFeature::buildServers() { EndpointFeature* endpoint = dynamic_cast( application_features::ApplicationServer::lookupFeature("Endpoint")); HttpServer* httpServer; // unencrypted HTTP endpoints httpServer = new HttpServer( SchedulerFeature::SCHEDULER, DispatcherFeature::DISPATCHER, _handlerFactory.get(), _jobManager.get(), _keepAliveTimeout); // YYY #warning FRANK filter list auto const& endpointList = endpoint->endpointList(); httpServer->setEndpointList(&endpointList); _servers.push_back(httpServer); // ssl endpoints if (endpointList.hasSsl()) { SslFeature* ssl = dynamic_cast( application_features::ApplicationServer::lookupFeature("Ssl")); // check the ssl context if (ssl == nullptr || ssl->sslContext() == nullptr) { LOG(FATAL) << "no ssl context is known, cannot create https server, " "please use the '--ssl.keyfile' option"; FATAL_ERROR_EXIT(); } SSL_CTX* sslContext = ssl->sslContext(); // https httpServer = new HttpsServer(SchedulerFeature::SCHEDULER, DispatcherFeature::DISPATCHER, _handlerFactory.get(), _jobManager.get(), _keepAliveTimeout, sslContext); httpServer->setEndpointList(&endpointList); _servers.push_back(httpServer); } } void RestServerFeature::defineHandlers() { AgencyFeature* agency = dynamic_cast( application_features::ApplicationServer::lookupFeature("Agency")); TRI_ASSERT(agency != nullptr); ClusterFeature* cluster = dynamic_cast( application_features::ApplicationServer::lookupFeature("Cluster")); TRI_ASSERT(cluster != nullptr); auto queryRegistry = DatabaseFeature::DATABASE->queryRegistry(); // ........................................................................... // /_msg // ........................................................................... _handlerFactory->addPrefixHandler( "/_msg/please-upgrade", RestHandlerCreator::createNoData); // ........................................................................... // /_api // ........................................................................... _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::BATCH_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::CURSOR_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::DOCUMENT_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::EDGES_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::EXPORT_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::IMPORT_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::REPLICATION_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_LOOKUP_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::SIMPLE_REMOVE_PATH, RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler( RestVocbaseBaseHandler::UPLOAD_PATH, RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( "/_api/shard-comm", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( "/_api/aql", RestHandlerCreator::createData, queryRegistry); _handlerFactory->addPrefixHandler( "/_api/query", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( "/_api/query-cache", 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()); } // And now some handlers which are registered in both /_api and /_admin _handlerFactory->addPrefixHandler( "/_api/job", RestHandlerCreator::createData< AsyncJobManager*>, _jobManager.get()); _handlerFactory->addHandler( "/_api/version", RestHandlerCreator::createNoData); // ........................................................................... // /_admin // ........................................................................... _handlerFactory->addPrefixHandler( "/_admin/job", RestHandlerCreator::createData< AsyncJobManager*>, _jobManager.get()); _handlerFactory->addHandler( "/_admin/version", RestHandlerCreator::createNoData); // further admin handlers _handlerFactory->addHandler( "/_admin/log", RestHandlerCreator::createNoData); _handlerFactory->addPrefixHandler( "/_admin/work-monitor", 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); // ........................................................................... // /_admin // ........................................................................... _handlerFactory->addPrefixHandler( "/", RestHandlerCreator::createData< RestActionHandler::action_options_t*>, (void*)&_httpOptions); }