couchbase 3.0.0.alpha.5-universal-darwin-19 → 3.0.0.beta.1-universal-darwin-19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) 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/libcouchbase.bundle +0 -0
  98. data/lib/couchbase/management/user_manager.rb +1 -1
  99. data/lib/couchbase/mutation_state.rb +1 -0
  100. data/lib/couchbase/query_options.rb +25 -2
  101. data/lib/couchbase/scope.rb +0 -7
  102. data/lib/couchbase/search_options.rb +7 -0
  103. data/lib/couchbase/version.rb +1 -1
  104. data/lib/couchbase/view_options.rb +4 -3
  105. metadata +20 -82
  106. data/.github/workflows/tests-6.0.3.yml +0 -52
  107. data/.github/workflows/tests-dev-preview.yml +0 -55
  108. data/.github/workflows/tests.yml +0 -50
  109. data/.gitignore +0 -20
  110. data/.gitmodules +0 -21
  111. data/.idea/.gitignore +0 -5
  112. data/.idea/dictionaries/gem_terms.xml +0 -18
  113. data/.idea/inspectionProfiles/Project_Default.xml +0 -8
  114. data/.idea/vcs.xml +0 -13
  115. data/bin/check-cluster +0 -31
  116. data/bin/fetch-stats +0 -19
  117. data/bin/init-cluster +0 -82
  118. data/bin/jenkins/build-extension +0 -35
  119. data/bin/jenkins/install-dependencies +0 -47
  120. data/bin/jenkins/test-with-cbdyncluster +0 -58
  121. data/bin/setup +0 -24
  122. data/ext/couchbase/configuration_monitor.hxx +0 -93
  123. data/ext/couchbase/operations/command.hxx +0 -163
  124. data/rbi/couchbase.rbi +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87dcd3df3d46f4af2d6602541c0daf3e2a0f9f36f9ca1601fdc713358ac2adc3
4
- data.tar.gz: 6252f8320fe2bbeb9c8c0da1cfceb1b58649cb464cf34a310e3093cb2260f97f
3
+ metadata.gz: 0662d4e64ea7bc1e7b6cc7975cb9029a03ab89d70f279a1eef8d84cb9804510c
4
+ data.tar.gz: 80317092f682ecca19c7fc0410b1b1f7d44207a8efab95c4cf166140916dd9cc
5
5
  SHA512:
6
- metadata.gz: b95dced52e79cd8515da3efadd65f836ab23f2e9aed6b1d8df34d539c908068fb9afe3ebff726236616d43258d860156fcc1dbd79348ec989aee9d7308043840
7
- data.tar.gz: '01928bf4b1661f9cc6c4cc6351ee41fdf3b8b30425d32bc4e7316e99c130ad37eb0c61f497b4eb678b7e086d713be8459a84b5bbae14a9ff4866327bea65e28a'
6
+ metadata.gz: 641149befde0a43934f1afddaf10128b689bbc3b9f826d9da69bca82bed1b654eb9f667fc34e0c0cb9ace5d1d80eff6992f9c1b25f78e3811bfc1a909b48ba7b
7
+ data.tar.gz: 65f86e6f65023c3ac3e6d1f84373775f13cb2c03c67980f43c2b86ad54300e11f3ee04865068e5c8e727bfa3d44cf195fbdffd5477211740c7bd7583ceda784d
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