couchbase 4.2.5-dev.3 → 4.2.6-dev

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 (122) hide show
  1. package/README.md +81 -9
  2. package/deps/couchbase-cxx-client/CMakeLists.txt +9 -1
  3. package/deps/couchbase-cxx-client/bin/api.rb +234 -0
  4. package/deps/couchbase-cxx-client/bin/create-search-index +18 -135
  5. package/deps/couchbase-cxx-client/bin/init-cluster +17 -139
  6. package/deps/couchbase-cxx-client/bin/load-sample-buckets +54 -0
  7. package/deps/couchbase-cxx-client/core/cluster.hxx +33 -12
  8. package/deps/couchbase-cxx-client/core/cluster_options.hxx +3 -0
  9. package/deps/couchbase-cxx-client/core/crud_component.cxx +51 -22
  10. package/deps/couchbase-cxx-client/core/impl/build_deferred_query_indexes.cxx +115 -50
  11. package/deps/couchbase-cxx-client/core/impl/cluster.cxx +6 -0
  12. package/deps/couchbase-cxx-client/core/impl/create_bucket.cxx +155 -0
  13. package/deps/couchbase-cxx-client/core/impl/create_query_index.cxx +172 -59
  14. package/deps/couchbase-cxx-client/core/impl/dns_srv_tracker.cxx +2 -1
  15. package/deps/couchbase-cxx-client/core/impl/drop_bucket.cxx +66 -0
  16. package/deps/couchbase-cxx-client/core/impl/drop_query_index.cxx +138 -59
  17. package/deps/couchbase-cxx-client/core/impl/flush_bucket.cxx +66 -0
  18. package/deps/couchbase-cxx-client/core/impl/get_all_buckets.cxx +163 -0
  19. package/deps/couchbase-cxx-client/core/impl/get_all_query_indexes.cxx +67 -37
  20. package/deps/couchbase-cxx-client/core/impl/get_bucket.cxx +153 -0
  21. package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.cxx +113 -0
  22. package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.hxx +60 -0
  23. package/deps/couchbase-cxx-client/core/impl/key_value_error_category.cxx +2 -4
  24. package/deps/couchbase-cxx-client/core/impl/manager_error_context.cxx +100 -0
  25. package/deps/couchbase-cxx-client/core/impl/query.cxx +1 -0
  26. package/deps/couchbase-cxx-client/core/impl/update_bucket.cxx +130 -0
  27. package/deps/couchbase-cxx-client/core/impl/watch_query_indexes.cxx +53 -29
  28. package/deps/couchbase-cxx-client/core/io/dns_client.cxx +71 -38
  29. package/deps/couchbase-cxx-client/core/io/dns_config.cxx +5 -4
  30. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +5 -6
  31. package/deps/couchbase-cxx-client/core/meta/features.hxx +6 -0
  32. package/deps/couchbase-cxx-client/core/operations/document_query.cxx +11 -0
  33. package/deps/couchbase-cxx-client/core/operations/document_query.hxx +1 -0
  34. package/deps/couchbase-cxx-client/core/origin.cxx +270 -0
  35. package/deps/couchbase-cxx-client/core/origin.hxx +2 -0
  36. package/deps/couchbase-cxx-client/core/protocol/status.cxx +2 -2
  37. package/deps/couchbase-cxx-client/core/range_scan_options.cxx +3 -27
  38. package/deps/couchbase-cxx-client/core/range_scan_options.hxx +13 -17
  39. package/deps/couchbase-cxx-client/core/range_scan_orchestrator.cxx +367 -170
  40. package/deps/couchbase-cxx-client/core/range_scan_orchestrator.hxx +13 -2
  41. package/deps/couchbase-cxx-client/core/range_scan_orchestrator_options.hxx +5 -3
  42. package/deps/couchbase-cxx-client/core/scan_options.hxx +0 -19
  43. package/deps/couchbase-cxx-client/core/scan_result.cxx +19 -5
  44. package/deps/couchbase-cxx-client/core/scan_result.hxx +5 -2
  45. package/deps/couchbase-cxx-client/core/timeout_defaults.hxx +2 -3
  46. package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +1 -0
  47. package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +2 -0
  48. package/deps/couchbase-cxx-client/core/topology/collections_manifest_fmt.hxx +1 -1
  49. package/deps/couchbase-cxx-client/core/topology/configuration.hxx +5 -0
  50. package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +2 -0
  51. package/deps/couchbase-cxx-client/core/utils/connection_string.cxx +4 -0
  52. package/deps/couchbase-cxx-client/couchbase/behavior_options.hxx +19 -2
  53. package/deps/couchbase-cxx-client/couchbase/bucket_manager.hxx +135 -0
  54. package/deps/couchbase-cxx-client/couchbase/build_query_index_options.hxx +0 -30
  55. package/deps/couchbase-cxx-client/couchbase/cluster.hxx +14 -0
  56. package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +7 -48
  57. package/deps/couchbase-cxx-client/couchbase/create_bucket_options.hxx +41 -0
  58. package/deps/couchbase-cxx-client/couchbase/create_primary_query_index_options.hxx +0 -29
  59. package/deps/couchbase-cxx-client/couchbase/create_query_index_options.hxx +0 -33
  60. package/deps/couchbase-cxx-client/couchbase/drop_bucket_options.hxx +41 -0
  61. package/deps/couchbase-cxx-client/couchbase/drop_primary_query_index_options.hxx +0 -30
  62. package/deps/couchbase-cxx-client/couchbase/drop_query_index_options.hxx +0 -31
  63. package/deps/couchbase-cxx-client/couchbase/error_codes.hxx +1 -2
  64. package/deps/couchbase-cxx-client/couchbase/flush_bucket_options.hxx +41 -0
  65. package/deps/couchbase-cxx-client/couchbase/get_all_buckets_options.hxx +44 -0
  66. package/deps/couchbase-cxx-client/couchbase/get_all_query_indexes_options.hxx +0 -30
  67. package/deps/couchbase-cxx-client/couchbase/get_bucket_options.hxx +43 -0
  68. package/deps/couchbase-cxx-client/couchbase/management/bucket_settings.hxx +116 -0
  69. package/deps/couchbase-cxx-client/couchbase/manager_error_context.hxx +29 -53
  70. package/deps/couchbase-cxx-client/couchbase/query_index_manager.hxx +16 -83
  71. package/deps/couchbase-cxx-client/couchbase/query_options.hxx +18 -0
  72. package/deps/couchbase-cxx-client/couchbase/security_options.hxx +15 -0
  73. package/deps/couchbase-cxx-client/couchbase/update_bucket_options.hxx +41 -0
  74. package/deps/couchbase-cxx-client/couchbase/watch_query_indexes_options.hxx +0 -31
  75. package/deps/couchbase-cxx-client/docs/cbc-analytics.md +1 -0
  76. package/deps/couchbase-cxx-client/docs/cbc-get.md +1 -0
  77. package/deps/couchbase-cxx-client/docs/cbc-pillowfight.md +1 -0
  78. package/deps/couchbase-cxx-client/docs/cbc-query.md +1 -0
  79. package/deps/couchbase-cxx-client/docs/cbc.md +10 -0
  80. package/deps/couchbase-cxx-client/test/CMakeLists.txt +1 -0
  81. package/deps/couchbase-cxx-client/test/test_integration_collections.cxx +6 -0
  82. package/deps/couchbase-cxx-client/test/test_integration_crud.cxx +5 -0
  83. package/deps/couchbase-cxx-client/test/test_integration_examples.cxx +137 -1
  84. package/deps/couchbase-cxx-client/test/test_integration_management.cxx +709 -266
  85. package/deps/couchbase-cxx-client/test/test_integration_query.cxx +19 -7
  86. package/deps/couchbase-cxx-client/test/test_integration_range_scan.cxx +351 -112
  87. package/deps/couchbase-cxx-client/test/test_integration_search.cxx +10 -1
  88. package/deps/couchbase-cxx-client/test/test_transaction_public_async_api.cxx +13 -12
  89. package/deps/couchbase-cxx-client/test/test_transaction_public_blocking_api.cxx +27 -21
  90. package/deps/couchbase-cxx-client/test/test_unit_query.cxx +75 -0
  91. package/deps/couchbase-cxx-client/test/utils/server_version.hxx +5 -0
  92. package/deps/couchbase-cxx-client/test/utils/wait_until.cxx +29 -10
  93. package/deps/couchbase-cxx-client/test/utils/wait_until.hxx +3 -1
  94. package/deps/couchbase-cxx-client/tools/utils.cxx +4 -1
  95. package/dist/binding.d.ts +21 -16
  96. package/dist/binding.js +1 -4
  97. package/dist/bindingutilities.d.ts +6 -1
  98. package/dist/bindingutilities.js +36 -1
  99. package/dist/collection.d.ts +65 -3
  100. package/dist/collection.js +107 -0
  101. package/dist/crudoptypes.d.ts +34 -0
  102. package/dist/crudoptypes.js +18 -1
  103. package/dist/queryexecutor.js +1 -0
  104. package/dist/querytypes.d.ts +7 -0
  105. package/dist/rangeScan.d.ts +107 -0
  106. package/dist/rangeScan.js +91 -0
  107. package/dist/streamablepromises.d.ts +6 -0
  108. package/dist/streamablepromises.js +25 -1
  109. package/package.json +13 -14
  110. package/scripts/createPlatformPackages.js +1 -4
  111. package/src/addondata.hpp +1 -0
  112. package/src/binding.cpp +5 -2
  113. package/src/connection.cpp +108 -2
  114. package/src/connection.hpp +1 -0
  115. package/src/constants.cpp +2 -12
  116. package/src/jstocbpp_autogen.hpp +49 -22
  117. package/src/jstocbpp_basic.hpp +2 -8
  118. package/src/mutationtoken.cpp +13 -0
  119. package/src/scan_iterator.cpp +90 -0
  120. package/src/scan_iterator.hpp +30 -0
  121. package/tools/gen-bindings-json.py +9 -8
  122. package/deps/couchbase-cxx-client/core/impl/collection_query_index_manager.cxx +0 -93
@@ -17,13 +17,8 @@
17
17
  require "set"
18
18
  require "timeout"
19
19
  require "optparse"
20
- require "openssl"
21
20
 
22
- class Object
23
- def to_b
24
- ![nil, false, 0, "", "0", "f", "F", "false", "FALSE", "off", "OFF", "no", "NO"].include?(self)
25
- end
26
- end
21
+ require_relative "api"
27
22
 
28
23
  options = {
29
24
  verbose: ENV.fetch("CB_VERBOSE", true).to_b,
@@ -49,106 +44,6 @@ options[:port] = ENV.fetch("CB_PORT", default_port).to_i
49
44
  options[:sample_buckets] << "beer-sample" if ENV.fetch("CB_BEER_SAMPLE", false).to_b
50
45
  options[:sample_buckets] << "travel-sample" if ENV.fetch("CB_TRAVEL_SAMPLE", false).to_b
51
46
 
52
- require "net/http"
53
- require "json"
54
-
55
- class API
56
- def initialize(options)
57
- @options = options
58
- connect
59
- end
60
-
61
- def connect
62
- @client = Net::HTTP.start(@options[:host], @options[:port],
63
- use_ssl: @options[:strict_encryption],
64
- verify_mode: OpenSSL::SSL::VERIFY_NONE)
65
- end
66
-
67
- def url(path)
68
- "#{@client.use_ssl? ? 'https' : 'http'}://#{@options[:username]}@#{@options[:host]}:#{@options[:port]}#{path}"
69
- end
70
-
71
- def decode_response(response)
72
- payload =
73
- if /application\/json/.match?(response['content-type'])
74
- JSON.parse(response.body)
75
- else
76
- response.body
77
- end
78
- p payload if @options[:verbose]
79
- payload
80
- end
81
-
82
- def setup_request(request)
83
- request.basic_auth(@options[:username], @options[:password])
84
- request['accept'] = "application/json"
85
- end
86
-
87
- def get(path)
88
- puts "# GET #{url(path)}"
89
- req = Net::HTTP::Get.new(path)
90
- setup_request(req)
91
- res = @client.request(req)
92
- decode_response(res)
93
- rescue EOFError
94
- connect
95
- retry
96
- rescue => ex
97
- puts "#{__method__}: #{ex}, sleep for 1 second and retry"
98
- sleep(1)
99
- retry
100
- end
101
-
102
- def post_form(path, fields = {})
103
- puts "# POST #{url(path)} #{fields}"
104
- req = Net::HTTP::Post.new(path)
105
- setup_request(req)
106
- req.form_data = fields
107
- res = @client.request(req)
108
- decode_response(res)
109
- rescue EOFError
110
- connect
111
- retry
112
- rescue => ex
113
- puts "#{__method__}: #{ex}, sleep for 1 second and retry"
114
- sleep(1)
115
- retry
116
- end
117
-
118
- def post_json(path, object)
119
- data = JSON.generate(object)
120
- puts "# POST #{url(path)} #{data}"
121
- req = Net::HTTP::Post.new(path)
122
- req['content-type'] = "application/json"
123
- setup_request(req)
124
- res = @client.request(req, data)
125
- decode_response(res)
126
- rescue EOFError
127
- connect
128
- retry
129
- rescue => ex
130
- puts "#{__method__}: #{ex}, sleep for 1 second and retry"
131
- sleep(1)
132
- retry
133
- end
134
-
135
- def put_json(path, object)
136
- data = JSON.generate(object)
137
- puts "# PUT #{url(path)} #{data}"
138
- req = Net::HTTP::Put.new(path)
139
- req['content-type'] = "application/json"
140
- setup_request(req)
141
- res = @client.request(req, data)
142
- decode_response(res)
143
- rescue EOFError
144
- connect
145
- retry
146
- rescue => ex
147
- puts "#{__method__}: #{ex}, sleep for 1 second and retry"
148
- sleep(1)
149
- retry
150
- end
151
- end
152
47
 
153
48
  api =
154
49
  begin
@@ -180,7 +75,10 @@ api.post_form("/settings/web",
180
75
  password: options[:password],
181
76
  username: options[:username],
182
77
  port: "SAME")
183
- api.post_form("/settings/indexes", storageMode: "plasma")
78
+ res = api.post_form("/settings/indexes", storageMode: "plasma")
79
+ if res["errors"]
80
+ res = api.post_form("/settings/indexes", storageMode: "forestdb")
81
+ end
184
82
 
185
83
  if options[:cluster_run_nodes] > 1
186
84
  known_nodes = []
@@ -235,50 +133,30 @@ if options[:sec_bucket].to_b
235
133
  name: options[:sec_bucket])
236
134
  end
237
135
 
238
- api.post_json("/sampleBuckets/install", options[:sample_buckets].to_a) if options[:sample_buckets].any?
239
-
240
136
  api.post_form("/settings/developerPreview", enabled: true) if options[:enable_developer_preview]
241
137
 
242
- def service_port_for_bucket(api, service, bucket, options)
243
- port_key = options[:strict_encryption] ? "#{service}SSL" : service
244
- port = 0
245
- while port.zero?
246
- config = api.get("/pools/default/b/#{bucket}")
247
- port = config["nodesExt"].find{|n| n["services"].key?(port_key)}["services"][port_key].to_i rescue 0
248
- sleep 1
249
- end
250
- port
251
- end
138
+ service_address = service_address_for_bucket(api, "n1ql", options[:bucket], options)
139
+ puts query_service: service_address
252
140
 
253
- query_api = API.new(options.merge(port: service_port_for_bucket(api, "n1ql", options[:bucket], options)))
141
+ query_api = API.new(options.merge(service_address))
254
142
  query_api.post_form("/query/service", statement: "CREATE PRIMARY INDEX ON `#{options[:bucket]}` USING GSI")
143
+
144
+ expected_counts = {
145
+ "travel-sample" => 30_000,
146
+ "beer-sample" => 7_000,
147
+ }
255
148
  options[:sample_buckets].each do |bucket|
256
- service_port_for_bucket(api, "n1ql", bucket, options)
257
- query_api.post_form("/query/service", statement: "CREATE PRIMARY INDEX ON `#{bucket}` USING GSI")
149
+ ensure_sample_bucket_loaded(api, expected_counts[bucket], options.merge(bucket: bucket))
258
150
  end
259
151
 
260
- puts service_port_for_bucket(api, "fts", options[:bucket], options)
261
-
262
152
  if options[:sample_buckets].include?("travel-sample") && services.include?("fts")
263
- search_api = API.new(options.merge(port: service_port_for_bucket(api, "fts", options[:bucket], options)))
153
+ service_address = service_address_for_bucket(api, "fts", options[:bucket], options)
154
+ puts search_service: service_address
264
155
 
265
156
  has_v6_nodes =
266
157
  api.get("/pools/default")["nodes"]
267
158
  .any? { |node| node["version"] =~ /^6\./ && node["services"].include?("fts") }
268
-
269
159
  index_definition_path = File.join(__dir__, "travel-sample-index#{"-v6" if has_v6_nodes}.json")
270
- puts "using index definition: #{File.basename(index_definition_path)}"
271
- index_definition = JSON.load(File.read(index_definition_path))
272
- search_api.put_json("/api/index/travel-sample-index", index_definition)
273
-
274
- expected_number_of_the_documents = 1000
275
- indexed_documents = 0
276
- Timeout.timeout(10 * 60) do # give it 10 minutes to index
277
- while indexed_documents < expected_number_of_the_documents
278
- resp = search_api.get("/api/index/travel-sample-index/count")
279
- indexed_documents = resp['count'].to_i
280
- sleep 1
281
- end
282
- end
160
+ ensure_search_index_created(index_definition_path, options.merge(service_address))
283
161
  end
284
162
 
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright 2020-2021 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
+ require "timeout"
18
+ require "set"
19
+
20
+ require_relative "api"
21
+
22
+ options = {
23
+ host: ENV.fetch("CB_HOST", ARGV[0] || "127.0.0.1"),
24
+ strict_encryption: ENV.fetch("CB_STRICT_ENCRYPTION", ARGV[1]).to_b,
25
+ username: ENV.fetch("CB_USERNAME", ARGV[2] || "Administrator"),
26
+ password: ENV.fetch("CB_PASSWORD", ARGV[3] || "password"),
27
+ verbose: ENV.fetch("CB_VERBOSE", true).to_b,
28
+ bucket: "travel-sample",
29
+ sample_buckets: Set.new,
30
+ }
31
+ options[:sample_buckets] << "beer-sample" if ENV.fetch("CB_BEER_SAMPLE", false).to_b
32
+ options[:sample_buckets] << "travel-sample" if ENV.fetch("CB_TRAVEL_SAMPLE", false).to_b
33
+ options[:sample_buckets] |= ARGV[4..-1]
34
+ return if options[:sample_buckets].empty?
35
+
36
+ p options: options
37
+
38
+ management_api =
39
+ begin
40
+ API.new(options.merge(port: options[:strict_encryption] ? 18091 : 8091))
41
+ rescue => ex
42
+ puts "#{ex}, sleep for 1 second and retry"
43
+ sleep(1)
44
+ retry
45
+ end
46
+
47
+ expected_counts = {
48
+ "travel-sample" => 30_000,
49
+ "beer-sample" => 7_000,
50
+ }
51
+
52
+ options[:sample_buckets].each do |bucket|
53
+ ensure_sample_bucket_loaded(management_api, expected_counts[bucket], options.merge(bucket: bucket))
54
+ end
@@ -79,6 +79,7 @@ class cluster : public std::enable_shared_from_this<cluster>
79
79
  }
80
80
 
81
81
  origin_ = std::move(origin);
82
+ CB_LOG_DEBUG(R"(open cluster, id: "{}", core version: "{}", {})", id_, couchbase::core::meta::sdk_semver(), origin_.to_json());
82
83
  // ignore the enable_tracing flag if a tracer was passed in
83
84
  if (nullptr != origin_.options().tracer) {
84
85
  tracer_ = origin_.options().tracer;
@@ -111,10 +112,7 @@ class cluster : public std::enable_shared_from_this<cluster>
111
112
  return self->dns_srv_tracker_->get_srv_nodes(
112
113
  [self, hostname = std::move(hostname), handler = std::forward<Handler>(handler)](origin::node_list nodes,
113
114
  std::error_code ec) mutable {
114
- if (ec) {
115
- return self->close([ec, handler = std::forward<Handler>(handler)]() mutable { handler(ec); });
116
- }
117
- if (!nodes.empty()) {
115
+ if (!ec && !nodes.empty()) {
118
116
  self->origin_.set_nodes(std::move(nodes));
119
117
  CB_LOG_INFO("replace list of bootstrap nodes with addresses from DNS SRV of \"{}\": [{}]",
120
118
  hostname,
@@ -339,8 +337,8 @@ class cluster : public std::enable_shared_from_this<cluster>
339
337
  void do_open(Handler&& handler)
340
338
  {
341
339
  // Warn users if they attempt to use Capella without TLS being enabled.
340
+ bool has_capella_host = false;
342
341
  {
343
- bool has_capella_host = false;
344
342
  bool has_non_capella_host = false;
345
343
  static std::string suffix = "cloud.couchbase.com";
346
344
  for (const auto& node : origin_.get_hostnames()) {
@@ -359,6 +357,7 @@ class cluster : public std::enable_shared_from_this<cluster>
359
357
 
360
358
  if (origin_.options().enable_tls /* TLS is enabled */
361
359
  && origin_.options().trust_certificate.empty() /* No CA certificate (or other SDK-specific trust source) is specified */
360
+ && origin_.options().trust_certificate_value.empty() /* and certificate value has not been specified */
362
361
  && origin_.options().tls_verify != tls_verify_mode::none /* The user did not disable all TLS verification */
363
362
  && has_non_capella_host /* The connection string has a hostname that does NOT end in ".cloud.couchbase.com" */) {
364
363
  CB_LOG_WARNING("[{}] When TLS is enabled, the cluster options must specify certificate(s) to trust or ensure that they are "
@@ -368,7 +367,17 @@ class cluster : public std::enable_shared_from_this<cluster>
368
367
  }
369
368
 
370
369
  if (origin_.options().enable_tls) {
371
- tls_.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
370
+ long tls_options = asio::ssl::context::default_workarounds | // various bug workarounds that should be rather harmless
371
+ asio::ssl::context::no_sslv2 | // published: 1995, deprecated: 2011
372
+ asio::ssl::context::no_sslv3; // published: 1996, deprecated: 2015
373
+ if (origin_.options().tls_disable_deprecated_protocols) {
374
+ tls_options |= asio::ssl::context::no_tlsv1 | // published: 1999, deprecated: 2021
375
+ asio::ssl::context::no_tlsv1_1; // published: 2006, deprecated: 2021
376
+ }
377
+ if (origin_.options().tls_disable_v1_2 || has_capella_host) {
378
+ tls_options |= asio::ssl::context::no_tlsv1_2; // published: 2008, still in use
379
+ }
380
+ tls_.set_options(tls_options);
372
381
  switch (origin_.options().tls_verify) {
373
382
  case tls_verify_mode::none:
374
383
  tls_.set_verify_mode(asio::ssl::verify_none);
@@ -378,7 +387,8 @@ class cluster : public std::enable_shared_from_this<cluster>
378
387
  tls_.set_verify_mode(asio::ssl::verify_peer);
379
388
  break;
380
389
  }
381
- if (origin_.options().trust_certificate.empty()) { // trust certificate is not explicitly specified
390
+ if (origin_.options().trust_certificate.empty() &&
391
+ origin_.options().trust_certificate_value.empty()) { // trust certificate is not explicitly specified
382
392
  CB_LOG_DEBUG(R"([{}]: use default CA for TLS verify)", id_);
383
393
  std::error_code ec{};
384
394
 
@@ -414,11 +424,22 @@ class cluster : public std::enable_shared_from_this<cluster>
414
424
  std::error_code ec{};
415
425
  // load only the explicit certificate
416
426
  // system and default capella certificates are not loaded
417
- CB_LOG_DEBUG(R"([{}]: use TLS verify file: "{}")", id_, origin_.options().trust_certificate);
418
- tls_.load_verify_file(origin_.options().trust_certificate, ec);
419
- if (ec) {
420
- CB_LOG_ERROR("[{}]: unable to load verify file \"{}\": {}", id_, origin_.options().trust_certificate, ec.message());
421
- return close([ec, handler = std::forward<Handler>(handler)]() mutable { return handler(ec); });
427
+ if (!origin_.options().trust_certificate_value.empty()) {
428
+ CB_LOG_DEBUG(R"([{}]: use TLS certificate passed through via options object)", id_);
429
+ tls_.add_certificate_authority(asio::const_buffer(origin_.options().trust_certificate_value.data(),
430
+ origin_.options().trust_certificate_value.size()),
431
+ ec);
432
+ if (ec) {
433
+ CB_LOG_WARNING("[{}]: unable to load CA passed via options object: {}", id_, ec.message());
434
+ }
435
+ }
436
+ if (!origin_.options().trust_certificate.empty()) {
437
+ CB_LOG_DEBUG(R"([{}]: use TLS verify file: "{}")", id_, origin_.options().trust_certificate);
438
+ tls_.load_verify_file(origin_.options().trust_certificate, ec);
439
+ if (ec) {
440
+ CB_LOG_ERROR("[{}]: unable to load verify file \"{}\": {}", id_, origin_.options().trust_certificate, ec.message());
441
+ return close([ec, handler = std::forward<Handler>(handler)]() mutable { return handler(ec); });
442
+ }
422
443
  }
423
444
  }
424
445
  #ifdef COUCHBASE_CXX_CLIENT_TLS_KEY_LOG_FILE
@@ -50,7 +50,10 @@ struct cluster_options {
50
50
  std::chrono::milliseconds management_timeout = timeout_defaults::management_timeout;
51
51
 
52
52
  bool enable_tls{ false };
53
+ bool tls_disable_deprecated_protocols{ true };
54
+ bool tls_disable_v1_2{ false };
53
55
  std::string trust_certificate{};
56
+ std::string trust_certificate_value{};
54
57
  bool enable_mutation_tokens{ true };
55
58
  bool enable_tcp_keep_alive{ true };
56
59
  io::ip_protocol use_ip_protocol{ io::ip_protocol::any };
@@ -1,6 +1,6 @@
1
1
  /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
2
  /*
3
- * Copyright 2020-2021 Couchbase, Inc.
3
+ * Copyright 2020-2023 Couchbase, Inc.
4
4
  *
5
5
  * Licensed under the Apache License, Version 2.0 (the "License");
6
6
  * you may not use this file except in compliance with the License.
@@ -39,6 +39,8 @@
39
39
 
40
40
  #include <tl/expected.hpp>
41
41
 
42
+ #include <random>
43
+
42
44
  namespace couchbase::core
43
45
  {
44
46
  static std::pair<std::vector<std::byte>, std::error_code>
@@ -53,17 +55,40 @@ serialize_range_scan_create_options(const range_scan_create_options& options)
53
55
  body["collection"] = fmt::format("{:x}", options.collection_id);
54
56
  }
55
57
 
56
- if (std::holds_alternative<range_scan>(options.scan_type)) {
57
- const auto& range = std::get<range_scan>(options.scan_type);
58
+ if (std::holds_alternative<range_scan>(options.scan_type) || std::holds_alternative<prefix_scan>(options.scan_type)) {
59
+ const auto& range = (std::holds_alternative<range_scan>(options.scan_type))
60
+ ? std::get<range_scan>(options.scan_type)
61
+ : std::get<prefix_scan>(options.scan_type).to_range_scan();
62
+
63
+ const auto& from = range.from.value_or(scan_term{ "" });
64
+ const auto& to = range.to.value_or(scan_term{ "\xf4\x8f\xfb\xfb" });
65
+
58
66
  body["range"] = {
59
- { range.start_.exclusive ? "excl_start" : "start", base64::encode(range.start_.id) },
60
- { range.end_.exclusive ? "excl_end" : "end", base64::encode(range.end_.id) },
67
+ { from.exclusive ? "excl_start" : "start", base64::encode(from.term) },
68
+ { to.exclusive ? "excl_end" : "end", base64::encode(to.term) },
61
69
  };
62
70
  } else if (std::holds_alternative<sampling_scan>(options.scan_type)) {
63
71
  const auto& sampling = std::get<sampling_scan>(options.scan_type);
72
+
73
+ // The limit in sampling scan is required to be greater than 0
74
+ if (sampling.limit <= 0) {
75
+ return { {}, errc::common::invalid_argument };
76
+ }
77
+
78
+ std::uint64_t seed{};
79
+ if (sampling.seed.has_value()) {
80
+ seed = sampling.seed.value();
81
+ } else {
82
+ // Generate random uint64 as seed
83
+ std::random_device rd;
84
+ std::mt19937_64 gen(rd());
85
+ std::uniform_int_distribution<std::uint64_t> dis;
86
+ seed = dis(gen);
87
+ }
88
+
64
89
  body["sampling"] = {
65
90
  { "samples", sampling.limit },
66
- { "seed", sampling.seed.value_or(0) },
91
+ { "seed", seed },
67
92
  };
68
93
  } else {
69
94
  return { {}, errc::common::invalid_argument };
@@ -75,7 +100,7 @@ serialize_range_scan_create_options(const range_scan_create_options& options)
75
100
  { "vb_uuid", std::to_string(snapshot.vbucket_uuid) },
76
101
  { "seqno", snapshot.sequence_number },
77
102
  { "timeout_ms",
78
- (options.timeout == std::chrono::milliseconds::zero()) ? timeout_defaults::range_scan_timeout.count()
103
+ (options.timeout == std::chrono::milliseconds::zero()) ? timeout_defaults::key_value_scan_timeout.count()
79
104
  : options.timeout.count() },
80
105
  };
81
106
  if (snapshot.sequence_number_exists) {
@@ -98,7 +123,7 @@ parse_range_scan_keys(gsl::span<std::byte> data, range_scan_item_callback&& item
98
123
  if (remaining.size() < key_length) {
99
124
  return errc::network::protocol_error;
100
125
  }
101
- item_callback(range_scan_item{ { remaining.begin(), remaining.begin() + static_cast<std::ptrdiff_t>(key_length) } });
126
+ item_callback(range_scan_item{ { reinterpret_cast<const char*>(remaining.data()), key_length } });
102
127
  if (remaining.size() == key_length) {
103
128
  return {};
104
129
  }
@@ -130,13 +155,13 @@ parse_range_scan_documents(gsl::span<std::byte> data, range_scan_item_callback&&
130
155
  body.datatype = data[24];
131
156
  data = gsl::make_span(data.data() + header_offset, data.size() - header_offset);
132
157
 
133
- std::vector<std::byte> key{};
158
+ std::string key{};
134
159
  {
135
160
  auto [key_length, remaining] = utils::decode_unsigned_leb128<std::size_t>(data, core::utils::leb_128_no_throw{});
136
161
  if (remaining.size() < key_length) {
137
162
  return errc::network::protocol_error;
138
163
  }
139
- key = { remaining.begin(), remaining.begin() + static_cast<std::ptrdiff_t>(key_length) };
164
+ key = { reinterpret_cast<const char*>(remaining.data()), key_length };
140
165
  data = gsl::make_span(remaining.data() + key_length, remaining.size() - key_length);
141
166
  }
142
167
 
@@ -242,19 +267,10 @@ class crud_component_impl
242
267
  if (error) {
243
268
  return cb({}, error);
244
269
  }
245
- bool ids_only;
246
- switch (response->extras_.size()) {
247
- case 4:
248
- ids_only = mcbp::big_endian::read_uint32(response->extras_, 0) == 0;
249
- break;
250
-
251
- case 0:
252
- ids_only = options.ids_only; // support servers before MB-54267. TODO: remove after server GA
253
- break;
254
-
255
- default:
256
- return cb({}, errc::network::protocol_error);
270
+ if (response->extras_.size() != 4) {
271
+ return cb({}, errc::network::protocol_error);
257
272
  }
273
+ bool ids_only = mcbp::big_endian::read_uint32(response->extras_, 0) == 0;
258
274
 
259
275
  if (auto ec = parse_range_scan_data(response->value_, std::move(item_cb), ids_only); ec) {
260
276
  return cb({}, ec);
@@ -276,6 +292,19 @@ class crud_component_impl
276
292
 
277
293
  req->persistent_ = true;
278
294
  req->vbucket_ = vbucket_id;
295
+
296
+ if (options.timeout != std::chrono::milliseconds::zero()) {
297
+ auto timer = std::make_shared<asio::steady_timer>(io_);
298
+ timer->expires_after(options.timeout);
299
+ timer->async_wait([req](auto error) {
300
+ if (error == asio::error::operation_aborted) {
301
+ return;
302
+ }
303
+ req->cancel(couchbase::errc::common::unambiguous_timeout);
304
+ });
305
+ req->set_deadline(timer);
306
+ }
307
+
279
308
  mcbp::buffer_writer buf{ scan_uuid.size() + sizeof(std::uint32_t) * 3 };
280
309
  buf.write(scan_uuid);
281
310
  buf.write_uint32(options.batch_item_limit);