couchbase 3.0.0.alpha.5 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -3
  3. data/README.md +4 -2
  4. data/Rakefile +1 -1
  5. data/couchbase.gemspec +17 -12
  6. data/ext/.idea/misc.xml +12 -0
  7. data/ext/CMakeLists.txt +10 -1
  8. data/ext/build_config.hxx.in +20 -0
  9. data/ext/build_version.hxx.in +1 -1
  10. data/ext/couchbase/bucket.hxx +90 -24
  11. data/ext/couchbase/cluster.hxx +125 -84
  12. data/ext/couchbase/cluster_options.hxx +53 -0
  13. data/ext/couchbase/configuration.hxx +220 -2
  14. data/ext/couchbase/couchbase.cxx +134 -127
  15. data/ext/couchbase/io/dns_client.hxx +3 -1
  16. data/ext/couchbase/io/http_command.hxx +91 -0
  17. data/ext/couchbase/io/http_session.hxx +58 -19
  18. data/ext/couchbase/io/http_session_manager.hxx +26 -31
  19. data/ext/couchbase/io/mcbp_command.hxx +180 -0
  20. data/ext/couchbase/io/mcbp_message.hxx +5 -0
  21. data/ext/couchbase/io/mcbp_session.hxx +213 -98
  22. data/ext/couchbase/io/streams.hxx +165 -0
  23. data/ext/couchbase/operations.hxx +1 -1
  24. data/ext/couchbase/operations/analytics_dataset_create.hxx +1 -1
  25. data/ext/couchbase/operations/bucket_create.hxx +4 -2
  26. data/ext/couchbase/operations/bucket_drop.hxx +4 -2
  27. data/ext/couchbase/operations/bucket_flush.hxx +4 -2
  28. data/ext/couchbase/operations/bucket_get.hxx +4 -2
  29. data/ext/couchbase/operations/bucket_get_all.hxx +4 -2
  30. data/ext/couchbase/operations/bucket_update.hxx +4 -2
  31. data/ext/couchbase/operations/cluster_developer_preview_enable.hxx +4 -2
  32. data/ext/couchbase/operations/collection_create.hxx +4 -2
  33. data/ext/couchbase/operations/collection_drop.hxx +4 -2
  34. data/ext/couchbase/operations/document_analytics.hxx +0 -4
  35. data/ext/couchbase/operations/document_decrement.hxx +6 -3
  36. data/ext/couchbase/operations/document_get.hxx +3 -0
  37. data/ext/couchbase/operations/document_get_and_lock.hxx +3 -0
  38. data/ext/couchbase/operations/document_get_and_touch.hxx +5 -2
  39. data/ext/couchbase/operations/document_get_projected.hxx +12 -9
  40. data/ext/couchbase/operations/document_increment.hxx +6 -3
  41. data/ext/couchbase/operations/document_insert.hxx +5 -2
  42. data/ext/couchbase/operations/document_lookup_in.hxx +3 -0
  43. data/ext/couchbase/operations/document_mutate_in.hxx +6 -3
  44. data/ext/couchbase/operations/document_remove.hxx +3 -0
  45. data/ext/couchbase/operations/document_replace.hxx +5 -2
  46. data/ext/couchbase/operations/document_search.hxx +6 -7
  47. data/ext/couchbase/operations/document_touch.hxx +5 -2
  48. data/ext/couchbase/operations/document_unlock.hxx +3 -0
  49. data/ext/couchbase/operations/document_upsert.hxx +5 -2
  50. data/ext/couchbase/operations/query_index_build_deferred.hxx +3 -3
  51. data/ext/couchbase/operations/query_index_create.hxx +3 -3
  52. data/ext/couchbase/operations/query_index_drop.hxx +3 -3
  53. data/ext/couchbase/operations/query_index_get_all.hxx +3 -3
  54. data/ext/couchbase/operations/scope_create.hxx +4 -2
  55. data/ext/couchbase/operations/scope_drop.hxx +4 -2
  56. data/ext/couchbase/operations/scope_get_all.hxx +4 -2
  57. data/ext/couchbase/operations/search_index_analyze_document.hxx +2 -2
  58. data/ext/couchbase/operations/search_index_control_ingest.hxx +2 -2
  59. data/ext/couchbase/operations/search_index_control_plan_freeze.hxx +2 -2
  60. data/ext/couchbase/operations/search_index_control_query.hxx +2 -2
  61. data/ext/couchbase/operations/search_index_drop.hxx +2 -2
  62. data/ext/couchbase/operations/search_index_get.hxx +2 -2
  63. data/ext/couchbase/operations/search_index_get_all.hxx +2 -2
  64. data/ext/couchbase/operations/search_index_get_documents_count.hxx +2 -2
  65. data/ext/couchbase/operations/search_index_upsert.hxx +2 -2
  66. data/ext/couchbase/origin.hxx +148 -0
  67. data/ext/couchbase/protocol/cmd_cluster_map_change_notification.hxx +1 -6
  68. data/ext/couchbase/protocol/cmd_decrement.hxx +5 -5
  69. data/ext/couchbase/protocol/cmd_get_and_touch.hxx +5 -5
  70. data/ext/couchbase/protocol/cmd_get_cluster_config.hxx +1 -6
  71. data/ext/couchbase/protocol/cmd_increment.hxx +5 -5
  72. data/ext/couchbase/protocol/cmd_info.hxx +0 -11
  73. data/ext/couchbase/protocol/cmd_insert.hxx +5 -5
  74. data/ext/couchbase/protocol/cmd_mutate_in.hxx +6 -6
  75. data/ext/couchbase/protocol/cmd_replace.hxx +5 -5
  76. data/ext/couchbase/protocol/cmd_touch.hxx +1 -1
  77. data/ext/couchbase/protocol/cmd_upsert.hxx +5 -5
  78. data/ext/couchbase/timeout_defaults.hxx +7 -0
  79. data/ext/couchbase/utils/connection_string.hxx +139 -0
  80. data/ext/extconf.rb +44 -11
  81. data/ext/test/main.cxx +93 -15
  82. data/ext/third_party/http_parser/Makefile +160 -0
  83. data/ext/third_party/json/Makefile +77 -0
  84. data/lib/couchbase/analytics_options.rb +18 -4
  85. data/lib/couchbase/binary_collection.rb +2 -2
  86. data/lib/couchbase/binary_collection_options.rb +2 -2
  87. data/lib/couchbase/bucket.rb +4 -4
  88. data/lib/couchbase/cluster.rb +60 -46
  89. data/lib/couchbase/collection.rb +13 -13
  90. data/lib/couchbase/collection_options.rb +15 -9
  91. data/{bin/console → lib/couchbase/datastructures.rb} +4 -7
  92. data/lib/couchbase/datastructures/couchbase_list.rb +171 -0
  93. data/lib/couchbase/datastructures/couchbase_map.rb +205 -0
  94. data/lib/couchbase/datastructures/couchbase_queue.rb +145 -0
  95. data/lib/couchbase/datastructures/couchbase_set.rb +138 -0
  96. data/lib/couchbase/errors.rb +66 -63
  97. data/lib/couchbase/management/user_manager.rb +1 -1
  98. data/lib/couchbase/mutation_state.rb +1 -0
  99. data/lib/couchbase/query_options.rb +25 -2
  100. data/lib/couchbase/scope.rb +0 -7
  101. data/lib/couchbase/search_options.rb +7 -0
  102. data/lib/couchbase/version.rb +1 -1
  103. data/lib/couchbase/view_options.rb +4 -3
  104. metadata +20 -82
  105. data/.github/workflows/tests-6.0.3.yml +0 -52
  106. data/.github/workflows/tests-dev-preview.yml +0 -55
  107. data/.github/workflows/tests.yml +0 -50
  108. data/.gitignore +0 -20
  109. data/.gitmodules +0 -21
  110. data/.idea/.gitignore +0 -5
  111. data/.idea/dictionaries/gem_terms.xml +0 -18
  112. data/.idea/inspectionProfiles/Project_Default.xml +0 -8
  113. data/.idea/vcs.xml +0 -13
  114. data/bin/check-cluster +0 -31
  115. data/bin/fetch-stats +0 -19
  116. data/bin/init-cluster +0 -82
  117. data/bin/jenkins/build-extension +0 -35
  118. data/bin/jenkins/install-dependencies +0 -47
  119. data/bin/jenkins/test-with-cbdyncluster +0 -58
  120. data/bin/setup +0 -24
  121. data/ext/couchbase/configuration_monitor.hxx +0 -93
  122. data/ext/couchbase/operations/command.hxx +0 -163
  123. data/rbi/couchbase.rbi +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8161b1f71ed9c5bf76741005dc12a1d2dde2c0583d4bf160cf5553285b061ee5
4
- data.tar.gz: 964cfd24dc5e5b92dabc5e9e527ad864e5b7115c53302d6c279315a04c7a3b5e
3
+ metadata.gz: 1e71b3d69cab6a09f02460f5b983a908c256a76e12dd68f57fab9173c6a50d0e
4
+ data.tar.gz: b58766149d745a869828fe7f2f04f56182560de48a3365b95b253313f7f428c4
5
5
  SHA512:
6
- metadata.gz: a812521c76a7a8aaa4ef364d344f507bb750e54214aa33ca4dbcb0ed88ec5cf1330545ffe9a22543f89b5ee82bf264aa8c152d5140d374534b62715152280112
7
- data.tar.gz: 9d113e948491fdf943c38d50718e11222c30413a17bff1b7fb32775e2872ee0255cea4f9f4fea44cd2e0cf7d1e75f37939f1396a6472953a9410c6bb40566ec9
6
+ metadata.gz: 12b4ab101a664897933c2ca98841c1d7cd27c6b5a15653eb2b48e59eba69f76f431ce25b316eba392ad8f104cdcde0fa44b38004a341e316141d821a7256c852
7
+ data.tar.gz: 4a1723022980425bc7b0a907d5487ae72c654d29709f1137a32349a98eab7e11f23d75a95414df60a35097e9eae4cd9ec2414d184877b95c06f62cb97825770a
data/Gemfile CHANGED
@@ -17,8 +17,17 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
17
17
 
18
18
  gemspec
19
19
 
20
+ gem "rake"
21
+
20
22
  group :development do
21
- gem "byebug"
22
- gem "rake"
23
- # gem "sorbet"
23
+ gem "yard"
24
+ platforms :mri do
25
+ gem "byebug"
26
+ end
27
+ end
28
+
29
+ group :test do
30
+ gem "minitest"
31
+ gem "minitest-reporters"
32
+ gem "simplecov-cobertura"
24
33
  end
data/README.md CHANGED
@@ -4,7 +4,7 @@ This repository contains the third generation of the official Couchbase SDK for
4
4
 
5
5
  ## Support and Feedback
6
6
 
7
- If you find an issue, please file it in [our JIRA issue tracker](http://couchbase.com/issues/browse/RCBC).
7
+ If you find an issue, please file it in [our JIRA issue tracker](https://couchbase.com/issues/browse/RCBC).
8
8
  Also you are always welcome on [our forum](https://forums.couchbase.com/c/ruby-sdk).
9
9
 
10
10
  Please attach version information to ticket/post. To obtain this information use the following command:
@@ -13,10 +13,12 @@ Please attach version information to ticket/post. To obtain this information use
13
13
 
14
14
  ## Installation
15
15
 
16
+ The library tested with the MRI 2.5, 2.6 and 2.7. Supported platforms are Linux and MacOS.
17
+
16
18
  Add this line to your application's Gemfile:
17
19
 
18
20
  ```ruby
19
- gem "couchbase", "3.0.0.alpha.5"
21
+ gem "couchbase", "3.0.0.beta.1"
20
22
  ```
21
23
 
22
24
  And then execute:
data/Rakefile CHANGED
@@ -33,7 +33,7 @@ task :doc do
33
33
  input_dir = File.join(__dir__, "lib")
34
34
  output_dir = File.join(__dir__, "doc", "couchbase-ruby-client-#{Couchbase::VERSION[:sdk]}")
35
35
  rm_rf output_dir
36
- sh "yard doc --output-dir #{output_dir} #{input_dir} - README.md"
36
+ sh "yard doc --hide-api private --output-dir #{output_dir} #{input_dir} --main README.md"
37
37
  puts "#{File.realpath(output_dir)}/index.html"
38
38
  end
39
39
 
@@ -26,13 +26,23 @@ Gem::Specification.new do |spec|
26
26
  spec.homepage = "https://www.couchbase.com"
27
27
  spec.license = "Apache-2.0"
28
28
 
29
- spec.metadata["homepage_uri"] = spec.homepage
30
- spec.metadata["source_code_uri"] = "https://github.com/couchbase/couchbase-ruby-client"
31
- spec.metadata["changelog_uri"] = "#{spec.metadata["source_code_uri"]}/releases"
32
- spec.metadata["github_repo"] = "ssh://github.com/couchbase/couchbase-ruby-client"
29
+ spec.metadata = {
30
+ "homepage_uri" => "https://docs.couchbase.com/ruby-sdk/3.0/hello-world/start-using-sdk.html",
31
+ "bug_tracker_uri" => "https://couchbase.com/issues/browse/RCBC",
32
+ "mailing_list_uri" => "https://forums.couchbase.com/c/ruby-sdk",
33
+ "source_code_uri" => "https://github.com/couchbasse/couchbase-ruby-client/tree/#{spec.version}",
34
+ "changelog_uri" => "https://github.com/couchbase/couchbase-ruby-client/releases/tag/#{spec.version}",
35
+ "documentation_uri" => "https://docs.couchbase.com/sdk-api/couchbase-ruby-client-#{spec.version}/index.html",
36
+ "github_repo" => "ssh://github.com/couchbase/couchbase-ruby-client",
37
+ }
33
38
 
34
39
  spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
35
40
  exclude_paths = %w[
41
+ .idea
42
+ .github
43
+ .gitignore
44
+ .gitmodules
45
+ bin
36
46
  test
37
47
  spec
38
48
  features
@@ -57,17 +67,12 @@ Gem::Specification.new do |spec|
57
67
  ext/third_party/spdlog/tests
58
68
  ]
59
69
  `git ls-files --recurse-submodules -z`
60
- .split("\x0")
61
- .reject { |f| f.match(%r{^(#{Regexp.union(exclude_paths)})/}) }
70
+ .split("\x0")
71
+ .reject { |f| f.match(%r{^(#{Regexp.union(exclude_paths)})}) }
62
72
  end
63
73
  spec.bindir = "exe"
64
74
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
65
75
  spec.require_paths = ["lib"]
66
76
  spec.extensions = ["ext/extconf.rb"]
67
- spec.rdoc_options.append('--exclude', 'ext/')
68
-
69
- spec.add_development_dependency "bundler", "~> 2.1"
70
- spec.add_development_dependency "rake", "~> 13.0"
71
- spec.add_development_dependency "minitest", "~> 5.14"
72
- spec.add_development_dependency "minitest-reporters", "~> 1.4"
77
+ spec.rdoc_options << "--exclude" << "ext/"
73
78
  end
@@ -1,4 +1,16 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <project version="4">
3
3
  <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
4
+ <component name="CidrRootsConfiguration">
5
+ <sourceRoots>
6
+ <file path="$PROJECT_DIR$/couchbase" />
7
+ </sourceRoots>
8
+ <libraryRoots>
9
+ <file path="$PROJECT_DIR$/third_party" />
10
+ </libraryRoots>
11
+ <excludeRoots>
12
+ <file path="$PROJECT_DIR$/cmake-build-debug" />
13
+ <file path="$PROJECT_DIR$/cmake-build-debug-system-gcc" />
14
+ </excludeRoots>
15
+ </component>
4
16
  </project>
@@ -29,7 +29,7 @@ if(MSVC)
29
29
  target_compile_options(project_warnings INTERFACE /W4 /WX "/permissive-")
30
30
  else()
31
31
  option(ONLY_COVERAGE "Build only tests necessary for coverage" FALSE)
32
- option(LIBCPP "Build with libc++" FALSE)
32
+ option(STATIC_STDLIB "Statically link C++ standard library" FALSE)
33
33
  option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE)
34
34
  option(ENABLE_FUZZERS "Enable fuzz testing tools" FALSE)
35
35
 
@@ -52,6 +52,11 @@ else()
52
52
  target_link_libraries(project_options INTERFACE -fsanitize=thread)
53
53
  endif()
54
54
 
55
+ if(STATIC_STDLIB)
56
+ target_compile_options(project_options INTERFACE -static-libgcc -static-libstdc++)
57
+ target_link_options(project_options INTERFACE -static-libgcc -static-libstdc++)
58
+ endif()
59
+
55
60
  target_compile_options(
56
61
  project_warnings
57
62
  INTERFACE -Wall
@@ -87,6 +92,9 @@ else()
87
92
  endif()
88
93
  endif()
89
94
 
95
+ # Read more at https://wiki.wireshark.org/TLS
96
+ option(TLS_KEY_LOG_FILE "Path to file to write per-session secrets (Useful for Wireshark SSL/TLS dissection)")
97
+
90
98
  include(FindOpenSSL)
91
99
  message(STATUS "OPENSSL_VERSION: ${OPENSSL_VERSION}")
92
100
  message(STATUS "OPENSSL_INCLUDEDIRS: ${OPENSSL_INCLUDE_DIR}")
@@ -129,6 +137,7 @@ endif()
129
137
 
130
138
  string(TIMESTAMP BACKEND_BUILD_TIMESTAMP "%Y-%m-%d %H:%M:%S" UTC)
131
139
  configure_file(${PROJECT_SOURCE_DIR}/build_version.hxx.in ${PROJECT_BINARY_DIR}/generated/build_version.hxx @ONLY)
140
+ configure_file(${PROJECT_SOURCE_DIR}/build_config.hxx.in ${PROJECT_BINARY_DIR}/generated/build_config.hxx @ONLY)
132
141
  add_library(couchbase SHARED couchbase/couchbase.cxx)
133
142
  target_include_directories(couchbase PRIVATE ${PROJECT_BINARY_DIR}/generated)
134
143
  target_link_libraries(
@@ -0,0 +1,20 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #cmakedefine TLS_KEY_LOG_FILE "@TLS_KEY_LOG_FILE@"
@@ -22,5 +22,5 @@
22
22
  #define BACKEND_C_COMPILER "@CMAKE_C_COMPILER_ID@ @CMAKE_C_COMPILER_VERSION@"
23
23
  #define BACKEND_SYSTEM "@CMAKE_SYSTEM@"
24
24
  #define BACKEND_SYSTEM_PROCESSOR "@CMAKE_SYSTEM_PROCESSOR@"
25
- #define BACKEND_GIT_REVISION "96d4d0b033b3d0ab2c198d755fa0180a54e9211f"
25
+ #define BACKEND_GIT_REVISION "f01d0c237750cc67ee75325ed160bc0da1d2bf7a"
26
26
 
@@ -18,19 +18,29 @@
18
18
  #pragma once
19
19
 
20
20
  #include <utility>
21
+ #include <queue>
21
22
 
22
- #include <configuration_monitor.hxx>
23
23
  #include <operations.hxx>
24
+ #include <origin.hxx>
24
25
 
25
26
  namespace couchbase
26
27
  {
27
- class bucket
28
+ class bucket : public std::enable_shared_from_this<bucket>
28
29
  {
29
30
  public:
30
- explicit bucket(asio::io_context& ctx, std::string name, configuration config)
31
- : ctx_(ctx)
31
+ explicit bucket(const std::string& client_id,
32
+ asio::io_context& ctx,
33
+ asio::ssl::context& tls,
34
+ std::string name,
35
+ couchbase::origin origin,
36
+ const std::vector<protocol::hello_feature>& known_features)
37
+
38
+ : client_id_(client_id)
39
+ , ctx_(ctx)
40
+ , tls_(tls)
32
41
  , name_(std::move(name))
33
- , config_(std::move(config))
42
+ , origin_(std::move(origin))
43
+ , known_features_(known_features)
34
44
  {
35
45
  }
36
46
 
@@ -44,21 +54,55 @@ class bucket
44
54
  return name_;
45
55
  }
46
56
 
47
- void set_node(size_t index, std::shared_ptr<io::mcbp_session> session)
57
+ template<typename Handler>
58
+ void bootstrap(Handler&& handler)
48
59
  {
49
- if (closed_) {
50
- return;
60
+ std::shared_ptr<io::mcbp_session> new_session;
61
+ if (origin_.options().enable_tls) {
62
+ new_session = std::make_shared<io::mcbp_session>(client_id_, ctx_, tls_, origin_, name_, known_features_);
63
+ } else {
64
+ new_session = std::make_shared<io::mcbp_session>(client_id_, ctx_, origin_, name_, known_features_);
51
65
  }
52
-
53
- sessions_.emplace(index, std::move(session));
54
- }
55
-
56
- void remove_node(size_t index)
57
- {
58
- if (closed_) {
59
- return;
60
- }
61
- sessions_.erase(index);
66
+ new_session->bootstrap([self = shared_from_this(), new_session, h = std::forward<Handler>(handler)](
67
+ std::error_code ec, const configuration& cfg) mutable {
68
+ if (!ec) {
69
+ self->config_ = cfg;
70
+ size_t this_index = new_session->index();
71
+ self->sessions_.emplace(this_index, std::move(new_session));
72
+ if (cfg.nodes.size() > 1) {
73
+ for (const auto& n : cfg.nodes) {
74
+ if (n.index != this_index) {
75
+ couchbase::origin origin(
76
+ self->origin_.get_username(),
77
+ self->origin_.get_password(),
78
+ n.hostname_for(self->origin_.options().network),
79
+ n.port_or(self->origin_.options().network, service_type::kv, self->origin_.options().enable_tls, 0),
80
+ self->origin_.options());
81
+ std::shared_ptr<io::mcbp_session> s;
82
+ if (self->origin_.options().enable_tls) {
83
+ s = std::make_shared<io::mcbp_session>(
84
+ self->client_id_, self->ctx_, self->tls_, origin, self->name_, self->known_features_);
85
+ } else {
86
+ s = std::make_shared<io::mcbp_session>(
87
+ self->client_id_, self->ctx_, origin, self->name_, self->known_features_);
88
+ }
89
+ s->bootstrap([host = n.hostname, bucket = self->name_](std::error_code err, const configuration& /*config*/) {
90
+ // TODO: retry, we know that auth is correct
91
+ if (err) {
92
+ spdlog::warn("unable to bootstrap node {} ({}): {}", host, bucket, err.message());
93
+ }
94
+ });
95
+ self->sessions_.emplace(n.index, std::move(s));
96
+ }
97
+ }
98
+ }
99
+ while (!self->deferred_commands_.empty()) {
100
+ self->deferred_commands_.front()();
101
+ self->deferred_commands_.pop();
102
+ }
103
+ }
104
+ h(ec, cfg);
105
+ });
62
106
  }
63
107
 
64
108
  template<typename Request, typename Handler>
@@ -67,11 +111,16 @@ class bucket
67
111
  if (closed_) {
68
112
  return;
69
113
  }
70
- size_t index = 0;
71
- std::tie(request.partition, index) = config_.map_key(request.id.key);
72
- auto session = sessions_.at(index);
73
- auto cmd = std::make_shared<operations::command<Request>>(ctx_, std::move(request));
74
- cmd->send_to(session, std::forward<Handler>(handler));
114
+ auto cmd = std::make_shared<operations::mcbp_command<Request>>(ctx_, request);
115
+ cmd->start([cmd, handler = std::forward<Handler>(handler)](std::error_code ec, std::optional<io::mcbp_message> msg) mutable {
116
+ using encoded_response_type = typename Request::encoded_response_type;
117
+ handler(make_response(ec, cmd->request, msg ? encoded_response_type(*msg) : encoded_response_type{}));
118
+ });
119
+ if (config_) {
120
+ map_and_send(cmd);
121
+ } else {
122
+ deferred_commands_.emplace([self = shared_from_this(), cmd]() { self->map_and_send(cmd); });
123
+ }
75
124
  }
76
125
 
77
126
  void close()
@@ -85,10 +134,27 @@ class bucket
85
134
  }
86
135
  }
87
136
 
137
+ template<typename Request>
138
+ void map_and_send(std::shared_ptr<operations::mcbp_command<Request>> cmd)
139
+ {
140
+ size_t index = 0;
141
+ std::tie(cmd->request.partition, index) = config_->map_key(cmd->request.id.key);
142
+ auto session = sessions_.at(index);
143
+ cmd->send_to(session);
144
+ }
145
+
88
146
  private:
147
+ std::string client_id_;
89
148
  asio::io_context& ctx_;
149
+ asio::ssl::context& tls_;
90
150
  std::string name_;
91
- configuration config_;
151
+ origin origin_;
152
+
153
+ std::optional<configuration> config_{};
154
+ std::vector<protocol::hello_feature> known_features_;
155
+
156
+ std::queue<std::function<void()>> deferred_commands_{};
157
+
92
158
  bool closed_{ false };
93
159
  std::map<size_t, std::shared_ptr<io::mcbp_session>> sessions_{};
94
160
  };
@@ -19,36 +19,21 @@
19
19
 
20
20
  #include <utility>
21
21
  #include <thread>
22
+ #include <fstream>
23
+
24
+ #include <asio/ssl.hpp>
22
25
 
23
26
  #include <io/mcbp_session.hxx>
24
27
  #include <io/http_session_manager.hxx>
28
+ #include <io/http_command.hxx>
29
+ #include <io/dns_client.hxx>
30
+ #include <origin.hxx>
25
31
  #include <bucket.hxx>
26
32
  #include <operations.hxx>
27
33
  #include <operations/document_query.hxx>
28
34
 
29
35
  namespace couchbase
30
36
  {
31
- struct origin {
32
- std::string username;
33
- std::string password;
34
- std::string hostname;
35
-
36
- [[nodiscard]] const std::string& get_username() const
37
- {
38
- return username;
39
- }
40
-
41
- [[nodiscard]] const std::string& get_password() const
42
- {
43
- return password;
44
- }
45
-
46
- [[nodiscard]] std::pair<std::string, std::string> get_address() const
47
- {
48
- return { hostname, "11210" };
49
- }
50
- };
51
-
52
37
  class cluster
53
38
  {
54
39
  public:
@@ -56,88 +41,53 @@ class cluster
56
41
  : id_(uuid::to_string(uuid::random()))
57
42
  , ctx_(ctx)
58
43
  , work_(asio::make_work_guard(ctx_))
59
- , session_(std::make_shared<io::mcbp_session>(id_, ctx_))
60
- , session_manager_(std::make_shared<io::http_session_manager>(id_, ctx_))
44
+ , tls_(asio::ssl::context::tls_client)
45
+ , session_manager_(std::make_shared<io::http_session_manager>(id_, ctx_, tls_))
46
+ , dns_config_(io::dns::dns_config::get())
47
+ , dns_client_(ctx_)
61
48
  {
62
49
  }
63
50
 
64
- ~cluster()
65
- {
66
- work_.reset();
67
- }
68
-
69
51
  template<typename Handler>
70
52
  void open(const couchbase::origin& origin, Handler&& handler)
71
53
  {
72
- auto address = origin.get_address();
73
54
  origin_ = origin;
74
- session_->bootstrap(address.first,
75
- address.second,
76
- origin.get_username(),
77
- origin.get_password(),
78
- [this, handler = std::forward<Handler>(handler)](std::error_code ec, configuration config) mutable {
79
- session_manager_->set_configuration(config);
80
- handler(ec);
81
- });
55
+ if (origin_.options().enable_dns_srv) {
56
+ return do_dns_srv(std::forward<Handler>(handler));
57
+ }
58
+ do_open(std::forward<Handler>(handler));
82
59
  }
83
60
 
84
61
  template<typename Handler>
85
62
  void close(Handler&& handler)
86
63
  {
87
64
  asio::post(asio::bind_executor(ctx_, [this, handler = std::forward<Handler>(handler)]() {
88
- session_->stop();
65
+ if (session_) {
66
+ session_->stop();
67
+ }
89
68
  for (auto& bucket : buckets_) {
90
69
  bucket.second->close();
91
70
  }
92
71
  handler();
72
+ work_.reset();
93
73
  }));
94
74
  }
95
75
 
96
76
  template<typename Handler>
97
77
  void open_bucket(const std::string& bucket_name, Handler&& handler)
98
78
  {
99
- if (!session_->has_config()) {
100
- return handler(std::make_error_code(error::common_errc::invalid_argument));
79
+ std::vector<protocol::hello_feature> known_features;
80
+ if (session_ && session_->has_config()) {
81
+ known_features = session_->supported_features();
101
82
  }
102
- configuration config = session_->config();
103
- auto known_features = session_->supported_features();
104
- auto& node = config.nodes.front();
105
- auto new_session = std::make_shared<io::mcbp_session>(id_, ctx_, bucket_name, known_features);
106
- new_session->bootstrap(node.hostname,
107
- std::to_string(*node.services_plain.key_value),
108
- origin_.get_username(),
109
- origin_.get_password(),
110
- [this, name = bucket_name, new_session, known_features, h = std::forward<Handler>(handler)](
111
- std::error_code ec, configuration cfg) mutable {
112
- if (!ec) {
113
- if (!session_->supports_gcccp()) {
114
- session_manager_->set_configuration(cfg);
115
- }
116
- auto b = std::make_shared<bucket>(ctx_, name, cfg);
117
- size_t this_index = new_session->index();
118
- b->set_node(this_index, new_session);
119
- if (cfg.nodes.size() > 1) {
120
- for (const auto& n : cfg.nodes) {
121
- if (n.index != this_index) {
122
- auto s = std::make_shared<io::mcbp_session>(id_, ctx_, name, known_features);
123
- b->set_node(n.index, s);
124
- s->bootstrap(n.hostname,
125
- std::to_string(*n.services_plain.key_value),
126
- origin_.get_username(),
127
- origin_.get_password(),
128
- [s, i = n.index, b](std::error_code err, configuration /*config*/) {
129
- if (err) {
130
- spdlog::warn("unable to bootstrap node: {}", err.message());
131
- b->remove_node(i);
132
- }
133
- });
134
- }
135
- }
136
- }
137
- buckets_.emplace(name, std::move(b));
138
- }
139
- h(ec);
140
- });
83
+ auto b = std::make_shared<bucket>(id_, ctx_, tls_, bucket_name, origin_, known_features);
84
+ b->bootstrap([this, handler = std::forward<Handler>(handler)](std::error_code ec, const configuration& config) mutable {
85
+ if (!ec && !session_->supports_gcccp()) {
86
+ session_manager_->set_configuration(config, origin_.options());
87
+ }
88
+ handler(ec);
89
+ });
90
+ buckets_.emplace(bucket_name, b);
141
91
  }
142
92
 
143
93
  template<class Request, class Handler>
@@ -153,11 +103,11 @@ class cluster
153
103
  template<class Request, class Handler>
154
104
  void execute_http(Request request, Handler&& handler)
155
105
  {
156
- auto session = session_manager_->check_out(Request::type, origin_.username, origin_.password);
106
+ auto session = session_manager_->check_out(Request::type, origin_.get_username(), origin_.get_password());
157
107
  if (!session) {
158
108
  return handler(operations::make_response(std::make_error_code(error::common_errc::service_not_available), request, {}));
159
109
  }
160
- auto cmd = std::make_shared<operations::command<Request>>(ctx_, std::move(request));
110
+ auto cmd = std::make_shared<operations::http_command<Request>>(ctx_, request);
161
111
  cmd->send_to(session, [this, session, handler = std::forward<Handler>(handler)](typename Request::response_type resp) mutable {
162
112
  handler(std::move(resp));
163
113
  session_manager_->check_in(Request::type, session);
@@ -165,12 +115,103 @@ class cluster
165
115
  }
166
116
 
167
117
  private:
118
+ template<typename Handler>
119
+ void do_dns_srv(Handler&& handler)
120
+ {
121
+ std::string hostname;
122
+ std::string service;
123
+ std::tie(hostname, service) = origin_.next_address();
124
+ service = origin_.options().enable_tls ? "_couchbases" : "_couchbase";
125
+ dns_client_.query_srv(
126
+ hostname,
127
+ service,
128
+ [hostname, this, handler = std::forward<Handler>(handler)](couchbase::io::dns::dns_client::dns_srv_response resp) mutable {
129
+ if (resp.ec) {
130
+ spdlog::warn("failed to fetch DNS SRV records for \"{}\" ({}), assuming that cluster is listening this address",
131
+ hostname,
132
+ resp.ec.message());
133
+ } else if (resp.targets.empty()) {
134
+ spdlog::warn("DNS SRV query returned 0 records for \"{}\", assuming that cluster is listening this address", hostname);
135
+ } else {
136
+ origin::node_list nodes;
137
+ nodes.reserve(resp.targets.size());
138
+ for (const auto& address : resp.targets) {
139
+ origin::node_entry node;
140
+ node.first = address.hostname;
141
+ node.second = std::to_string(address.port);
142
+ nodes.emplace_back(node);
143
+ }
144
+ origin_.set_nodes(nodes);
145
+ spdlog::info("replace list of bootstrap nodes with addresses from DNS SRV of \"{}\": [{}]",
146
+ hostname,
147
+ fmt::join(origin_.get_nodes(), ", "));
148
+ }
149
+ return do_open(std::forward<Handler>(handler));
150
+ });
151
+ }
152
+
153
+ template<typename Handler>
154
+ void do_open(Handler&& handler)
155
+ {
156
+ if (origin_.options().enable_tls) {
157
+ tls_.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
158
+ if (!origin_.options().trust_certificate.empty()) {
159
+ std::error_code ec{};
160
+ tls_.use_certificate_chain_file(origin_.options().trust_certificate, ec);
161
+ if (ec) {
162
+ spdlog::error("unable to load certificate chain \"{}\": {}", origin_.options().trust_certificate, ec.message());
163
+ return handler(ec);
164
+ }
165
+ }
166
+ #ifdef TLS_KEY_LOG_FILE
167
+ SSL_CTX_set_keylog_callback(tls_.native_handle(), [](const SSL* /* ssl */, const char* line) {
168
+ std::ofstream keylog(TLS_KEY_LOG_FILE, std::ios::out | std::ios::app | std::ios::binary);
169
+ keylog << std::string_view(line) << std::endl;
170
+ });
171
+ spdlog::critical("TLS_KEY_LOG_FILE was set to \"{}\" during build, all TLS keys will be logged for network analysis "
172
+ "(https://wiki.wireshark.org/TLS). DO NOT USE THIS BUILD IN PRODUCTION",
173
+ TLS_KEY_LOG_FILE);
174
+ #endif
175
+ session_ = std::make_shared<io::mcbp_session>(id_, ctx_, tls_, origin_);
176
+ } else {
177
+ session_ = std::make_shared<io::mcbp_session>(id_, ctx_, origin_);
178
+ }
179
+ session_->bootstrap([this, handler = std::forward<Handler>(handler)](std::error_code ec, const configuration& config) mutable {
180
+ if (!ec) {
181
+ if (origin_.options().network == "auto") {
182
+ origin_.options().network = config.select_network(session_->bootstrap_hostname());
183
+ spdlog::info(R"({} detected network is "{}")", session_->log_prefix(), origin_.options().network);
184
+ }
185
+ if (origin_.options().network != "default") {
186
+ origin::node_list nodes;
187
+ nodes.reserve(config.nodes.size());
188
+ for (const auto& address : config.nodes) {
189
+ origin::node_entry node;
190
+ node.first = address.hostname_for(origin_.options().network);
191
+ node.second =
192
+ std::to_string(address.port_or(origin_.options().network, service_type::kv, origin_.options().enable_tls, 0));
193
+ nodes.emplace_back(node);
194
+ }
195
+ origin_.set_nodes(nodes);
196
+ spdlog::info("replace list of bootstrap nodes with addresses of alternative network \"{}\": [{}]",
197
+ origin_.options().network,
198
+ fmt::join(origin_.get_nodes(), ","));
199
+ }
200
+ session_manager_->set_configuration(config, origin_.options());
201
+ }
202
+ handler(ec);
203
+ });
204
+ }
205
+
168
206
  std::string id_;
169
207
  asio::io_context& ctx_;
170
208
  asio::executor_work_guard<asio::io_context::executor_type> work_;
171
- std::shared_ptr<io::mcbp_session> session_;
209
+ asio::ssl::context tls_;
172
210
  std::shared_ptr<io::http_session_manager> session_manager_;
173
- std::map<std::string, std::shared_ptr<bucket>> buckets_;
174
- origin origin_;
211
+ io::dns::dns_config& dns_config_;
212
+ couchbase::io::dns::dns_client dns_client_;
213
+ std::shared_ptr<io::mcbp_session> session_{};
214
+ std::map<std::string, std::shared_ptr<bucket>> buckets_{};
215
+ couchbase::origin origin_{};
175
216
  };
176
217
  } // namespace couchbase