summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2018-04-10 13:29:16 -0400
committerMark Benvenuto <mark.benvenuto@mongodb.com>2018-04-10 13:29:16 -0400
commite2327f7b9acd5016b419db6a9b9a6c150bfdd79b (patch)
tree9f247efc1ab649a1e4fe886306ef125a9138bd7d
parentb9cbfd8fd3cc8438a3e8e3986505f348416f5da4 (diff)
downloadmongo-e2327f7b9acd5016b419db6a9b9a6c150bfdd79b.tar.gz
SERVER-34220 Write Curl Networking Impl
-rw-r--r--SConstruct7
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/base/error_codes.err3
-rw-r--r--src/mongo/db/db.cpp4
-rw-r--r--src/mongo/db/free_mon/SConscript26
-rw-r--r--src/mongo/db/free_mon/free_mon_http.h60
-rw-r--r--src/mongo/db/free_mon/free_mon_mongod.cpp186
-rw-r--r--src/mongo/db/free_mon/free_mon_mongod.h46
-rw-r--r--src/mongo/db/free_mon/free_mon_network.h58
-rw-r--r--src/mongo/db/free_mon/free_mon_protocol.idl123
-rw-r--r--src/mongo/db/free_mon/free_mon_stub.cpp39
-rw-r--r--src/mongo/db/free_mon/http_client_curl.cpp230
12 files changed, 783 insertions, 0 deletions
diff --git a/SConstruct b/SConstruct
index 7e095394db6..f012e1a27d9 100644
--- a/SConstruct
+++ b/SConstruct
@@ -275,6 +275,13 @@ add_option('gcov',
nargs=0,
)
+add_option('enable-free-mon',
+ choices=["on", "off"],
+ default="off",
+ help='Disable support for Free Monitoring to avoid HTTP client library dependencies',
+ type='choice',
+)
+
add_option('use-sasl-client',
help='Support SASL authentication in the client library',
nargs=0,
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index c7d8a7ac98d..45135329615 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -295,6 +295,7 @@ env.Library(
'db/commands/mongod_fcv',
'db/dbdirectclient',
'db/ftdc/ftdc_mongod',
+ 'db/free_mon/free_mon_mongod',
'db/index_d',
'db/initialize_snmp',
'db/keys_collection_client_direct',
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 64b78b5a948..e1c5002a512 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -250,6 +250,9 @@ error_code("StaleDbVersion", 249, extra="StaleDbRoutingVersion");
error_code("StaleChunkHistory", 250);
error_code("NoSuchTransaction", 251)
error_code("ReentrancyNotAllowed", 252)
+error_code("FreeMonHttpInFlight", 253)
+error_code("FreeMonHttpTemporaryFailure", 254)
+error_code("FreeMonHttpPermanentFailure", 255)
# Error codes 4000-8999 are reserved.
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index beb87a5d086..52e368abb7a 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -70,6 +70,7 @@
#include "mongo/db/dbhelpers.h"
#include "mongo/db/dbmessage.h"
#include "mongo/db/exec/working_set_common.h"
+#include "mongo/db/free_mon/free_mon_mongod.h"
#include "mongo/db/ftdc/ftdc_mongod.h"
#include "mongo/db/global_settings.h"
#include "mongo/db/index_names.h"
@@ -505,6 +506,8 @@ ExitCode _initAndListen(int listenPort) {
startMongoDFTDC();
+ startFreeMonitoring(serviceContext);
+
restartInProgressIndexesFromLastShutdown(startupOpCtx.get());
if (serverGlobalParams.clusterRole == ClusterRole::ShardServer) {
@@ -910,6 +913,7 @@ void shutdownTask() {
}
}
#endif
+ stopFreeMonitoring();
// Shutdown Full-Time Data Capture
stopMongoDFTDC();
diff --git a/src/mongo/db/free_mon/SConscript b/src/mongo/db/free_mon/SConscript
index 6a2b57d4a05..0bfce9a48a8 100644
--- a/src/mongo/db/free_mon/SConscript
+++ b/src/mongo/db/free_mon/SConscript
@@ -12,6 +12,7 @@ fmEnv.Library(
source=[
'free_mon_queue.cpp',
'free_mon_storage.cpp',
+ env.Idlc('free_mon_protocol.idl')[0],
env.Idlc('free_mon_storage.idl')[0],
],
LIBDEPS_PRIVATE=[
@@ -24,6 +25,31 @@ fmEnv.Library(
],
)
+if get_option("enable-free-mon") == "on":
+ fmEnv.Library(
+ target='free_mon_mongod',
+ source=[
+ 'free_mon_mongod.cpp',
+ 'http_client_curl.cpp',
+ #'http_client_curl.cpp' if not env.TargetOSIs('windows') else 'http_client_winhttp.cpp',
+ ],
+ LIBDEPS=[
+ 'free_mon',
+ ],
+ SYSLIBDEPS=[
+ 'libcurld' if env.TargetOSIs('windows') else 'curl',
+ # 'winhttp' if env.TargetOSIs('windows') else 'curl',
+ ],
+ )
+else:
+ fmEnv.Library(
+ target='free_mon_mongod',
+ source=[
+ 'free_mon_stub.cpp',
+ ],
+ )
+
+
env.CppUnitTest(
target='free_mon_test',
source=[
diff --git a/src/mongo/db/free_mon/free_mon_http.h b/src/mongo/db/free_mon/free_mon_http.h
new file mode 100644
index 00000000000..cd162f910ea
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_http.h
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/executor/thread_pool_task_executor.h"
+#include "mongo/util/concurrency/thread_pool.h"
+#include "mongo/util/future.h"
+
+namespace mongo {
+
+constexpr uint64_t kConnectionTimeoutSeconds = 60L;
+constexpr uint64_t kTotalRequestTimeoutSeconds = 120L;
+
+/**
+ * Interface used to upload and receive binary payloads to HTTP servers.
+ */
+class FreeMonHttpClientInterface {
+public:
+ virtual ~FreeMonHttpClientInterface();
+
+ /**
+ * Url is a full URL with hostname and protocol specification.
+ */
+ virtual Future<std::vector<uint8_t>> postAsync(StringData url, BSONObj data) = 0;
+};
+
+std::unique_ptr<FreeMonHttpClientInterface> createFreeMonHttpClient(
+ std::unique_ptr<executor::ThreadPoolTaskExecutor> executor);
+} // namespace mongo
diff --git a/src/mongo/db/free_mon/free_mon_mongod.cpp b/src/mongo/db/free_mon/free_mon_mongod.cpp
new file mode 100644
index 00000000000..aa4008973d5
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_mongod.cpp
@@ -0,0 +1,186 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/free_mon/free_mon_mongod.h"
+
+#include <mutex>
+#include <string>
+
+#include "mongo/base/data_type_validated.h"
+#include "mongo/base/error_codes.h"
+#include "mongo/base/status.h"
+#include "mongo/bson/bsonelement.h"
+#include "mongo/bson/bsonmisc.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/bson/bsontypes.h"
+#include "mongo/db/commands/test_commands_enabled.h"
+#include "mongo/db/free_mon/free_mon_http.h"
+#include "mongo/db/free_mon/free_mon_message.h"
+#include "mongo/db/free_mon/free_mon_network.h"
+#include "mongo/db/free_mon/free_mon_protocol_gen.h"
+#include "mongo/db/free_mon/free_mon_storage.h"
+#include "mongo/db/ftdc/ftdc_server.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/repl/replication_coordinator.h"
+#include "mongo/db/server_parameters.h"
+#include "mongo/db/service_context.h"
+#include "mongo/executor/network_interface_factory.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/concurrency/thread_pool.h"
+#include "mongo/util/future.h"
+
+namespace mongo {
+
+namespace {
+
+/**
+ * Expose cloudFreeMonitoringEndpointURL set parameter to URL for free monitoring.
+ */
+class ExportedFreeMonEndpointURL : public LockedServerParameter<std::string> {
+public:
+ ExportedFreeMonEndpointURL()
+ : LockedServerParameter<std::string>("cloudFreeMonitoringEndpointURL",
+ "https://localhost:8080",
+ ServerParameterType::kStartupOnly) {}
+
+
+ Status setFromString(const std::string& str) final {
+ // Check for http, not https here because testEnabled may not be set yet
+ if (!str.compare(0, 4, "http")) {
+ return Status(ErrorCodes::BadValue,
+ "ExportedFreeMonEndpointURL only supports https:// URLs");
+ }
+
+ return setLocked(str);
+ }
+} exportedExportedFreeMonEndpointURL;
+
+
+class FreeMonNetworkHttp : public FreeMonNetworkInterface {
+public:
+ explicit FreeMonNetworkHttp(std::unique_ptr<FreeMonHttpClientInterface> client)
+ : _client(std::move(client)) {}
+ ~FreeMonNetworkHttp() final = default;
+
+ Future<FreeMonRegistrationResponse> sendRegistrationAsync(
+ const FreeMonRegistrationRequest& req) override {
+ BSONObj reqObj = req.toBSON();
+
+ return _client->postAsync(exportedExportedFreeMonEndpointURL.getURL() + "/register", reqObj)
+ .then([](std::vector<uint8_t> blob) {
+
+ if (blob.empty()) {
+ uassertStatusOK(
+ Status(ErrorCodes::FreeMonHttpTemporaryFailure, "Empty response received"));
+ }
+
+ ConstDataRange cdr(reinterpret_cast<char*>(blob.data()), blob.size());
+
+ auto swDoc = cdr.read<Validated<BSONObj>>();
+ uassertStatusOK(swDoc.getStatus());
+
+ BSONObj respObj(swDoc.getValue());
+
+ auto resp =
+ FreeMonRegistrationResponse::parse(IDLParserErrorContext("response"), respObj);
+
+ return resp;
+ });
+ }
+
+ Future<FreeMonMetricsResponse> sendMetricsAsync(const FreeMonMetricsRequest& req) override {
+ BSONObj reqObj = req.toBSON();
+
+ return _client->postAsync(exportedExportedFreeMonEndpointURL.getURL() + "/metrics", reqObj)
+ .then([](std::vector<uint8_t> blob) {
+
+ if (blob.empty()) {
+ uassertStatusOK(
+ Status(ErrorCodes::FreeMonHttpTemporaryFailure, "Empty response received"));
+ }
+
+ ConstDataRange cdr(reinterpret_cast<char*>(blob.data()), blob.size());
+
+ auto swDoc = cdr.read<Validated<BSONObj>>();
+ uassertStatusOK(swDoc.getStatus());
+
+ BSONObj respObj(swDoc.getValue());
+
+ auto resp =
+ FreeMonMetricsResponse::parse(IDLParserErrorContext("response"), respObj);
+
+ return resp;
+ });
+ }
+
+private:
+ std::unique_ptr<FreeMonHttpClientInterface> _client;
+};
+
+
+auto makeTaskExecutor(ServiceContext* /*serviceContext*/) {
+ ThreadPool::Options tpOptions;
+ tpOptions.poolName = "freemon";
+ tpOptions.maxThreads = 2;
+ tpOptions.onCreateThread = [](const std::string& threadName) {
+ Client::initThread(threadName.c_str());
+ };
+ return stdx::make_unique<executor::ThreadPoolTaskExecutor>(
+ std::make_unique<ThreadPool>(tpOptions),
+ executor::makeNetworkInterface("NetworkInterfaceASIO-FreeMon"));
+}
+
+} // namespace
+
+
+void startFreeMonitoring(ServiceContext* serviceContext) {
+
+ auto executor = makeTaskExecutor(serviceContext);
+
+ executor->startup();
+
+ auto http = createFreeMonHttpClient(std::move(executor));
+ if (http == nullptr) {
+ // HTTP init failed
+ return;
+ }
+
+ auto network =
+ std::unique_ptr<FreeMonNetworkInterface>(new FreeMonNetworkHttp(std::move(http)));
+}
+
+void stopFreeMonitoring() {}
+
+FreeMonHttpClientInterface::~FreeMonHttpClientInterface() = default;
+
+FreeMonNetworkInterface::~FreeMonNetworkInterface() = default;
+
+} // namespace mongo
diff --git a/src/mongo/db/free_mon/free_mon_mongod.h b/src/mongo/db/free_mon/free_mon_mongod.h
new file mode 100644
index 00000000000..b4638930cf6
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_mongod.h
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/service_context.h"
+
+namespace mongo {
+
+/**
+ * Start Free Monitoring
+ * Starts 1 thread.
+ */
+void startFreeMonitoring(ServiceContext* serviceContext);
+
+/**
+ * Stop Free Monitoring
+ */
+void stopFreeMonitoring();
+
+} // namespace mongo
diff --git a/src/mongo/db/free_mon/free_mon_network.h b/src/mongo/db/free_mon/free_mon_network.h
new file mode 100644
index 00000000000..f3a84901353
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_network.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/db/free_mon/free_mon_protocol_gen.h"
+#include "mongo/util/future.h"
+
+namespace mongo {
+
+/**
+ * Makes HTTPS calls to cloud endpoint.
+ */
+class FreeMonNetworkInterface {
+public:
+ virtual ~FreeMonNetworkInterface();
+
+ /**
+ * POSTs FreeMonRegistrationRequest to endpoint.
+ *
+ * Returns a FreeMonRegistrationResponse or throws an error on non-HTTP 200.
+ */
+ virtual Future<FreeMonRegistrationResponse> sendRegistrationAsync(
+ const FreeMonRegistrationRequest& req) = 0;
+
+ /**
+ * POSTs FreeMonMetricsRequest to endpoint.
+ *
+ * Returns a FreeMonMetricsResponse or throws an error on non-HTTP 200.
+ */
+ virtual Future<FreeMonMetricsResponse> sendMetricsAsync(const FreeMonMetricsRequest& req) = 0;
+};
+} // namespace mongo
diff --git a/src/mongo/db/free_mon/free_mon_protocol.idl b/src/mongo/db/free_mon/free_mon_protocol.idl
new file mode 100644
index 00000000000..0ebac53c4b8
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_protocol.idl
@@ -0,0 +1,123 @@
+# Copyright (C) 2017 MongoDB Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License, version 3,
+# as published by the Free Software Foundation.
+#
+# 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+global:
+ cpp_namespace: "mongo"
+
+imports:
+ - "mongo/idl/basic_types.idl"
+
+
+enums:
+ MetricsEncoding:
+ description: "Metrics Encoding Methods"
+ type: string
+ values:
+ snappy: "snappy"
+
+
+structs:
+ FreeMonRegistrationRequest:
+ description: "Registration Request to Cloud Server"
+ fields:
+ version:
+ description: "Protocol version, initial version is 1"
+ type: long
+ payload:
+ description: "Payload of registration information"
+ type: object
+ id:
+ description: "Existing Registration Id"
+ type: string
+ optional: true
+ tag:
+ description: "Tag"
+ type: array<string>
+ optional: true
+
+ FreeMonRegistrationResponse:
+ description: "Registration Response from Cloud Server"
+ fields:
+ version:
+ description: "Protocol version, initial version is 1"
+ type: long
+ haltMetricsUploading:
+ description: "True indicates it should not proceed to metrics uploading"
+ type: bool
+ id:
+ description: "Existing Registration Id"
+ type: string
+ informationalURL:
+ description: "Informational HTTP web page for metrics"
+ type: string
+ message:
+ description: "Informational message for shell to display to user"
+ type: string
+ reportingInterval:
+ description: "Metrics Reporting interval in seconds"
+ type: long
+ userReminder:
+ description: "Informational message to display to user to remind them about the service"
+ type: string
+ optional: true
+
+
+ FreeMonMetricsRequest:
+ description: "Metrics Request to Cloud Server"
+ fields:
+ version:
+ description: "Protocol version, initial version is 1"
+ type: long
+ id:
+ description: "Registration Id"
+ type: string
+ encoding:
+ description: "Compression Encoding"
+ type: MetricsEncoding
+ metrics:
+ description: "Metrics Blob"
+ type: bindata_generic
+
+
+ FreeMonMetricsResponse:
+ description: "Metrics Response from Cloud Server"
+ fields:
+ version:
+ description: "Protocol version, initial version is 1"
+ type: long
+ haltMetricsUploading:
+ description: "True indicates it should not proceed to metrics uploading"
+ type: bool
+ permanentlyDelete:
+ description: "True indicates it permanently delete the local state"
+ type: bool
+ reportingInterval:
+ description: "Metrics Reporting interval in seconds"
+ type: long
+ id:
+ description: "Existing Registration Id"
+ type: string
+ optional: true
+ message:
+ description: "Informational message for shell to display to user"
+ type: string
+ optional: true
+ informationalURL:
+ description: "Informational HTTP web page for metrics"
+ type: string
+ optional: true
+ userReminder:
+ description: "Message to display to user to remind them about service"
+ type: string
+ optional: true
diff --git a/src/mongo/db/free_mon/free_mon_stub.cpp b/src/mongo/db/free_mon/free_mon_stub.cpp
new file mode 100644
index 00000000000..bc44e1dd9e4
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_stub.cpp
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/db/free_mon/free_mon_mongod.h"
+
+#include "mongo/db/service_context.h"
+
+namespace mongo {
+
+void startFreeMonitoring(ServiceContext* serviceContext) {}
+
+void stopFreeMonitoring() {}
+
+} // namespace mongo
diff --git a/src/mongo/db/free_mon/http_client_curl.cpp b/src/mongo/db/free_mon/http_client_curl.cpp
new file mode 100644
index 00000000000..ad78618b9bd
--- /dev/null
+++ b/src/mongo/db/free_mon/http_client_curl.cpp
@@ -0,0 +1,230 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork
+
+#include "mongo/platform/basic.h"
+
+#include <cstddef>
+#include <curl/curl.h>
+#include <curl/easy.h>
+#include <string>
+
+#include "mongo/base/data_builder.h"
+#include "mongo/base/data_range.h"
+#include "mongo/base/data_range_cursor.h"
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/commands/test_commands_enabled.h"
+#include "mongo/db/free_mon/free_mon_http.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+
+namespace {
+
+class CurlLibraryManager {
+public:
+ ~CurlLibraryManager() {
+ curl_global_cleanup();
+ }
+
+ bool initialize() {
+ CURLcode ret = curl_global_init(CURL_GLOBAL_ALL);
+ if (ret != CURLE_OK) {
+ error() << "Failed to initialize CURL: " << static_cast<int64_t>(ret);
+ return false;
+ }
+
+ curl_version_info_data* version_data = curl_version_info(CURLVERSION_NOW);
+ if (!(version_data->features & CURL_VERSION_SSL)) {
+ error() << "Curl lacks SSL support, cannot continue";
+ return false;
+ }
+
+ return true;
+ }
+};
+
+CurlLibraryManager curlLibraryManager;
+
+/**
+ * Receives data from the remote side.
+ */
+size_t WriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) {
+ const size_t realsize = size * nmemb;
+
+ auto* mem = reinterpret_cast<DataBuilder*>(data);
+ if (!mem->writeAndAdvance(ConstDataRange(reinterpret_cast<const char*>(ptr),
+ reinterpret_cast<const char*>(ptr) + realsize))
+ .isOK()) {
+ // Cause curl to generate a CURLE_WRITE_ERROR by returning a different number than how much
+ // data there was to write.
+ return 0;
+ }
+
+ return realsize;
+}
+
+/**
+ * Sends data to the remote side
+ */
+size_t ReadMemoryCallback(char* buffer, size_t size, size_t nitems, void* instream) {
+
+ auto* cdrc = reinterpret_cast<ConstDataRangeCursor*>(instream);
+
+ size_t ret = 0;
+
+ if (cdrc->length() > 0) {
+ size_t readSize = std::min(size * nitems, cdrc->length());
+ memcpy(buffer, cdrc->data(), readSize);
+ invariant(cdrc->advance(readSize).isOK());
+ ret = readSize;
+ }
+
+ return ret;
+}
+
+class FreeMonCurlHttpClient : public FreeMonHttpClientInterface {
+public:
+ explicit FreeMonCurlHttpClient(std::unique_ptr<executor::ThreadPoolTaskExecutor> executor)
+ : _executor(std::move(executor)) {}
+
+ ~FreeMonCurlHttpClient() final = default;
+
+ Future<std::vector<uint8_t>> postAsync(StringData url, const BSONObj obj) final {
+
+ Promise<std::vector<uint8_t>> promise;
+ auto future = promise.getFuture();
+ auto shared_promise = promise.share();
+
+ std::string urlString(url.toString());
+
+ auto status = _executor->scheduleWork([shared_promise, urlString, obj](
+ const executor::TaskExecutor::CallbackArgs& cbArgs) mutable {
+ doPost(shared_promise, urlString, obj);
+ });
+
+ uassertStatusOK(status);
+ return future;
+ }
+
+private:
+ static void doPost(SharedPromise<std::vector<uint8_t>> shared_promise,
+ const std::string& urlString,
+ const BSONObj& obj) {
+ ConstDataRange data(obj.objdata(), obj.objdata() + obj.objsize());
+
+ ConstDataRangeCursor cdrc(data);
+
+ std::unique_ptr<CURL, void (*)(CURL*)> myHandle(curl_easy_init(), curl_easy_cleanup);
+
+ if (!myHandle) {
+ shared_promise.setError({ErrorCodes::InternalError, "Curl initialization failed"});
+ return;
+ }
+
+ curl_easy_setopt(myHandle.get(), CURLOPT_URL, urlString.c_str());
+ curl_easy_setopt(myHandle.get(), CURLOPT_POST, 1);
+
+ // Allow http only if test commands are enabled
+ if (getTestCommandsEnabled()) {
+ curl_easy_setopt(myHandle.get(), CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
+ } else {
+ curl_easy_setopt(myHandle.get(), CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+ }
+
+ curl_easy_setopt(myHandle.get(), CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
+ curl_easy_setopt(myHandle.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+
+ DataBuilder dataBuilder(4096);
+
+ curl_easy_setopt(myHandle.get(), CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(myHandle.get(), CURLOPT_WRITEDATA, &dataBuilder);
+
+ curl_easy_setopt(myHandle.get(), CURLOPT_READFUNCTION, ReadMemoryCallback);
+ curl_easy_setopt(myHandle.get(), CURLOPT_READDATA, &cdrc);
+ curl_easy_setopt(myHandle.get(), CURLOPT_POSTFIELDSIZE, (long)cdrc.length());
+
+ // CURLOPT_EXPECT_100_TIMEOUT_MS??
+ curl_easy_setopt(myHandle.get(), CURLOPT_CONNECTTIMEOUT, kConnectionTimeoutSeconds);
+ curl_easy_setopt(myHandle.get(), CURLOPT_TIMEOUT, kTotalRequestTimeoutSeconds);
+
+#if LIBCURL_VERSION_NUM > 0x072200
+ // Requires >= 7.34.0
+ curl_easy_setopt(myHandle.get(), CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
+#endif
+ curl_easy_setopt(myHandle.get(), CURLOPT_FOLLOWLOCATION, 0);
+
+ curl_easy_setopt(myHandle.get(), CURLOPT_NOSIGNAL, 1);
+ // TODO: consider making this configurable If server log level > 3
+ // curl_easy_setopt(myHandle.get(), CURLOPT_VERBOSE, 1);
+ // curl_easy_setopt(myHandle.get(), CURLOPT_DEBUGFUNCTION , ???);
+
+ curl_slist* chunk = nullptr;
+ chunk = curl_slist_append(chunk, "Content-Type: application/octet-stream");
+ chunk = curl_slist_append(chunk, "Accept: application/octet-stream");
+
+ // Send the empty expect because we do not need the server to respond with 100-Contine
+ chunk = curl_slist_append(chunk, "Expect:");
+
+ std::unique_ptr<curl_slist, void (*)(curl_slist*)> chunkHolder(chunk, curl_slist_free_all);
+
+ curl_easy_setopt(myHandle.get(), CURLOPT_HTTPHEADER, chunk);
+
+ CURLcode result = curl_easy_perform(myHandle.get());
+ if (result != CURLE_OK) {
+ shared_promise.setError({ErrorCodes::OperationFailed,
+ str::stream() << "Bad HTTP response from API server: "
+ << curl_easy_strerror(result)});
+ return;
+ }
+
+ auto d = dataBuilder.getCursor();
+ shared_promise.emplaceValue(std::vector<uint8_t>(d.data(), d.data() + d.length()));
+ }
+
+private:
+ std::unique_ptr<executor::ThreadPoolTaskExecutor> _executor{};
+};
+
+} // namespace
+
+std::unique_ptr<FreeMonHttpClientInterface> createFreeMonHttpClient(
+ std::unique_ptr<executor::ThreadPoolTaskExecutor> executor) {
+
+ if (!curlLibraryManager.initialize()) {
+ return nullptr;
+ }
+
+ return std::make_unique<FreeMonCurlHttpClient>(std::move(executor));
+}
+
+} // namespace mongo