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.
- package/README.md +81 -9
- package/deps/couchbase-cxx-client/CMakeLists.txt +9 -1
- package/deps/couchbase-cxx-client/bin/api.rb +234 -0
- package/deps/couchbase-cxx-client/bin/create-search-index +18 -135
- package/deps/couchbase-cxx-client/bin/init-cluster +17 -139
- package/deps/couchbase-cxx-client/bin/load-sample-buckets +54 -0
- package/deps/couchbase-cxx-client/core/cluster.hxx +33 -12
- package/deps/couchbase-cxx-client/core/cluster_options.hxx +3 -0
- package/deps/couchbase-cxx-client/core/crud_component.cxx +51 -22
- package/deps/couchbase-cxx-client/core/impl/build_deferred_query_indexes.cxx +115 -50
- package/deps/couchbase-cxx-client/core/impl/cluster.cxx +6 -0
- package/deps/couchbase-cxx-client/core/impl/create_bucket.cxx +155 -0
- package/deps/couchbase-cxx-client/core/impl/create_query_index.cxx +172 -59
- package/deps/couchbase-cxx-client/core/impl/dns_srv_tracker.cxx +2 -1
- package/deps/couchbase-cxx-client/core/impl/drop_bucket.cxx +66 -0
- package/deps/couchbase-cxx-client/core/impl/drop_query_index.cxx +138 -59
- package/deps/couchbase-cxx-client/core/impl/flush_bucket.cxx +66 -0
- package/deps/couchbase-cxx-client/core/impl/get_all_buckets.cxx +163 -0
- package/deps/couchbase-cxx-client/core/impl/get_all_query_indexes.cxx +67 -37
- package/deps/couchbase-cxx-client/core/impl/get_bucket.cxx +153 -0
- package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.cxx +113 -0
- package/deps/couchbase-cxx-client/core/impl/internal_manager_error_context.hxx +60 -0
- package/deps/couchbase-cxx-client/core/impl/key_value_error_category.cxx +2 -4
- package/deps/couchbase-cxx-client/core/impl/manager_error_context.cxx +100 -0
- package/deps/couchbase-cxx-client/core/impl/query.cxx +1 -0
- package/deps/couchbase-cxx-client/core/impl/update_bucket.cxx +130 -0
- package/deps/couchbase-cxx-client/core/impl/watch_query_indexes.cxx +53 -29
- package/deps/couchbase-cxx-client/core/io/dns_client.cxx +71 -38
- package/deps/couchbase-cxx-client/core/io/dns_config.cxx +5 -4
- package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +5 -6
- package/deps/couchbase-cxx-client/core/meta/features.hxx +6 -0
- package/deps/couchbase-cxx-client/core/operations/document_query.cxx +11 -0
- package/deps/couchbase-cxx-client/core/operations/document_query.hxx +1 -0
- package/deps/couchbase-cxx-client/core/origin.cxx +270 -0
- package/deps/couchbase-cxx-client/core/origin.hxx +2 -0
- package/deps/couchbase-cxx-client/core/protocol/status.cxx +2 -2
- package/deps/couchbase-cxx-client/core/range_scan_options.cxx +3 -27
- package/deps/couchbase-cxx-client/core/range_scan_options.hxx +13 -17
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator.cxx +367 -170
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator.hxx +13 -2
- package/deps/couchbase-cxx-client/core/range_scan_orchestrator_options.hxx +5 -3
- package/deps/couchbase-cxx-client/core/scan_options.hxx +0 -19
- package/deps/couchbase-cxx-client/core/scan_result.cxx +19 -5
- package/deps/couchbase-cxx-client/core/scan_result.hxx +5 -2
- package/deps/couchbase-cxx-client/core/timeout_defaults.hxx +2 -3
- package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +1 -0
- package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +2 -0
- package/deps/couchbase-cxx-client/core/topology/collections_manifest_fmt.hxx +1 -1
- package/deps/couchbase-cxx-client/core/topology/configuration.hxx +5 -0
- package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +2 -0
- package/deps/couchbase-cxx-client/core/utils/connection_string.cxx +4 -0
- package/deps/couchbase-cxx-client/couchbase/behavior_options.hxx +19 -2
- package/deps/couchbase-cxx-client/couchbase/bucket_manager.hxx +135 -0
- package/deps/couchbase-cxx-client/couchbase/build_query_index_options.hxx +0 -30
- package/deps/couchbase-cxx-client/couchbase/cluster.hxx +14 -0
- package/deps/couchbase-cxx-client/couchbase/collection_query_index_manager.hxx +7 -48
- package/deps/couchbase-cxx-client/couchbase/create_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/create_primary_query_index_options.hxx +0 -29
- package/deps/couchbase-cxx-client/couchbase/create_query_index_options.hxx +0 -33
- package/deps/couchbase-cxx-client/couchbase/drop_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/drop_primary_query_index_options.hxx +0 -30
- package/deps/couchbase-cxx-client/couchbase/drop_query_index_options.hxx +0 -31
- package/deps/couchbase-cxx-client/couchbase/error_codes.hxx +1 -2
- package/deps/couchbase-cxx-client/couchbase/flush_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/get_all_buckets_options.hxx +44 -0
- package/deps/couchbase-cxx-client/couchbase/get_all_query_indexes_options.hxx +0 -30
- package/deps/couchbase-cxx-client/couchbase/get_bucket_options.hxx +43 -0
- package/deps/couchbase-cxx-client/couchbase/management/bucket_settings.hxx +116 -0
- package/deps/couchbase-cxx-client/couchbase/manager_error_context.hxx +29 -53
- package/deps/couchbase-cxx-client/couchbase/query_index_manager.hxx +16 -83
- package/deps/couchbase-cxx-client/couchbase/query_options.hxx +18 -0
- package/deps/couchbase-cxx-client/couchbase/security_options.hxx +15 -0
- package/deps/couchbase-cxx-client/couchbase/update_bucket_options.hxx +41 -0
- package/deps/couchbase-cxx-client/couchbase/watch_query_indexes_options.hxx +0 -31
- package/deps/couchbase-cxx-client/docs/cbc-analytics.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc-get.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc-pillowfight.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc-query.md +1 -0
- package/deps/couchbase-cxx-client/docs/cbc.md +10 -0
- package/deps/couchbase-cxx-client/test/CMakeLists.txt +1 -0
- package/deps/couchbase-cxx-client/test/test_integration_collections.cxx +6 -0
- package/deps/couchbase-cxx-client/test/test_integration_crud.cxx +5 -0
- package/deps/couchbase-cxx-client/test/test_integration_examples.cxx +137 -1
- package/deps/couchbase-cxx-client/test/test_integration_management.cxx +709 -266
- package/deps/couchbase-cxx-client/test/test_integration_query.cxx +19 -7
- package/deps/couchbase-cxx-client/test/test_integration_range_scan.cxx +351 -112
- package/deps/couchbase-cxx-client/test/test_integration_search.cxx +10 -1
- package/deps/couchbase-cxx-client/test/test_transaction_public_async_api.cxx +13 -12
- package/deps/couchbase-cxx-client/test/test_transaction_public_blocking_api.cxx +27 -21
- package/deps/couchbase-cxx-client/test/test_unit_query.cxx +75 -0
- package/deps/couchbase-cxx-client/test/utils/server_version.hxx +5 -0
- package/deps/couchbase-cxx-client/test/utils/wait_until.cxx +29 -10
- package/deps/couchbase-cxx-client/test/utils/wait_until.hxx +3 -1
- package/deps/couchbase-cxx-client/tools/utils.cxx +4 -1
- package/dist/binding.d.ts +21 -16
- package/dist/binding.js +1 -4
- package/dist/bindingutilities.d.ts +6 -1
- package/dist/bindingutilities.js +36 -1
- package/dist/collection.d.ts +65 -3
- package/dist/collection.js +107 -0
- package/dist/crudoptypes.d.ts +34 -0
- package/dist/crudoptypes.js +18 -1
- package/dist/queryexecutor.js +1 -0
- package/dist/querytypes.d.ts +7 -0
- package/dist/rangeScan.d.ts +107 -0
- package/dist/rangeScan.js +91 -0
- package/dist/streamablepromises.d.ts +6 -0
- package/dist/streamablepromises.js +25 -1
- package/package.json +13 -14
- package/scripts/createPlatformPackages.js +1 -4
- package/src/addondata.hpp +1 -0
- package/src/binding.cpp +5 -2
- package/src/connection.cpp +108 -2
- package/src/connection.hpp +1 -0
- package/src/constants.cpp +2 -12
- package/src/jstocbpp_autogen.hpp +49 -22
- package/src/jstocbpp_basic.hpp +2 -8
- package/src/mutationtoken.cpp +13 -0
- package/src/scan_iterator.cpp +90 -0
- package/src/scan_iterator.hpp +30 -0
- package/tools/gen-bindings-json.py +9 -8
- 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
|
-
|
|
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
|
-
|
|
243
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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()
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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-
|
|
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::
|
|
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
|
-
{
|
|
60
|
-
{
|
|
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",
|
|
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::
|
|
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.
|
|
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::
|
|
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.
|
|
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
|
-
|
|
246
|
-
|
|
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);
|