couchbase 3.0.0.alpha.3-universal-darwin-19 → 3.0.0.alpha.4-universal-darwin-19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests-6.0.3.yml +4 -1
  3. data/.github/workflows/tests-dev-preview.yml +4 -1
  4. data/.github/workflows/tests.yml +4 -1
  5. data/README.md +1 -1
  6. data/bin/check-cluster +31 -0
  7. data/bin/init-cluster +16 -4
  8. data/examples/analytics.rb +221 -0
  9. data/examples/managing_analytics_indexes.rb +72 -0
  10. data/examples/managing_view_indexes.rb +54 -0
  11. data/examples/search_with_consistency.rb +84 -0
  12. data/examples/view.rb +50 -0
  13. data/ext/.clang-tidy +1 -0
  14. data/ext/build_version.hxx.in +1 -1
  15. data/ext/couchbase/bucket.hxx +0 -1
  16. data/ext/couchbase/couchbase.cxx +1421 -55
  17. data/ext/couchbase/io/dns_client.hxx +215 -0
  18. data/ext/couchbase/io/dns_codec.hxx +207 -0
  19. data/ext/couchbase/io/dns_config.hxx +116 -0
  20. data/ext/couchbase/io/dns_message.hxx +558 -0
  21. data/ext/couchbase/io/http_session.hxx +16 -4
  22. data/ext/couchbase/io/mcbp_session.hxx +2 -1
  23. data/ext/couchbase/mutation_token.hxx +1 -1
  24. data/ext/couchbase/operations.hxx +19 -0
  25. data/ext/couchbase/operations/analytics_dataset_create.hxx +117 -0
  26. data/ext/couchbase/operations/analytics_dataset_drop.hxx +103 -0
  27. data/ext/couchbase/operations/analytics_dataset_get_all.hxx +107 -0
  28. data/ext/couchbase/operations/analytics_dataverse_create.hxx +104 -0
  29. data/ext/couchbase/operations/analytics_dataverse_drop.hxx +104 -0
  30. data/ext/couchbase/operations/analytics_get_pending_mutations.hxx +91 -0
  31. data/ext/couchbase/operations/analytics_index_create.hxx +128 -0
  32. data/ext/couchbase/operations/analytics_index_drop.hxx +110 -0
  33. data/ext/couchbase/operations/analytics_index_get_all.hxx +106 -0
  34. data/ext/couchbase/operations/analytics_link_connect.hxx +102 -0
  35. data/ext/couchbase/operations/analytics_link_disconnect.hxx +101 -0
  36. data/ext/couchbase/operations/design_document.hxx +59 -0
  37. data/ext/couchbase/operations/document_analytics.hxx +293 -0
  38. data/ext/couchbase/operations/document_query.hxx +2 -2
  39. data/ext/couchbase/operations/document_search.hxx +19 -1
  40. data/ext/couchbase/operations/document_view.hxx +227 -0
  41. data/ext/couchbase/operations/search_index.hxx +17 -0
  42. data/ext/couchbase/operations/search_index_control_ingest.hxx +3 -1
  43. data/ext/couchbase/operations/view_index_drop.hxx +67 -0
  44. data/ext/couchbase/operations/view_index_get.hxx +90 -0
  45. data/ext/couchbase/operations/view_index_get_all.hxx +125 -0
  46. data/ext/couchbase/operations/view_index_upsert.hxx +87 -0
  47. data/ext/couchbase/service_type.hxx +38 -1
  48. data/ext/couchbase/timeout_defaults.hxx +3 -1
  49. data/ext/couchbase/utils/connection_string.hxx +231 -0
  50. data/ext/couchbase/version.hxx +1 -1
  51. data/ext/test/main.cxx +3 -12
  52. data/lib/couchbase/analytics_options.rb +165 -0
  53. data/lib/couchbase/bucket.rb +49 -0
  54. data/lib/couchbase/cluster.rb +46 -207
  55. data/lib/couchbase/management/analytics_index_manager.rb +138 -24
  56. data/lib/couchbase/management/view_index_manager.rb +63 -10
  57. data/lib/couchbase/query_options.rb +219 -0
  58. data/lib/couchbase/search_options.rb +6 -6
  59. data/lib/couchbase/version.rb +1 -1
  60. data/lib/couchbase/view_options.rb +155 -0
  61. metadata +34 -2
@@ -0,0 +1,215 @@
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
+ #include <memory>
21
+ #include <sstream>
22
+
23
+ #include <io/dns_codec.hxx>
24
+ #include <io/dns_config.hxx>
25
+
26
+ #include <asio/read.hpp>
27
+
28
+ namespace couchbase::io::dns
29
+ {
30
+ class dns_client
31
+ {
32
+ public:
33
+ struct dns_srv_response {
34
+ struct address {
35
+ std::string hostname;
36
+ std::uint16_t port;
37
+ };
38
+ std::error_code ec;
39
+ std::vector<address> targets{};
40
+ };
41
+
42
+ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
43
+ {
44
+ public:
45
+ dns_srv_command(asio::io_context& ctx,
46
+ const std::string& name,
47
+ const std::string& service,
48
+ const asio::ip::address& address,
49
+ std::uint16_t port)
50
+ : deadline_(ctx)
51
+ , udp_(ctx)
52
+ , tcp_(ctx)
53
+ , address_(address)
54
+ , port_(port)
55
+ {
56
+ static std::string protocol{ "_tcp" };
57
+ dns_message request{};
58
+ question_record qr;
59
+ qr.klass = resource_class::in;
60
+ qr.type = resource_type::srv;
61
+ qr.name.labels.push_back(service);
62
+ qr.name.labels.push_back(protocol);
63
+ std::string label;
64
+ std::istringstream name_stream(name);
65
+ while (std::getline(name_stream, label, '.')) {
66
+ qr.name.labels.push_back(label);
67
+ }
68
+ request.questions.emplace_back(qr);
69
+ send_buf_ = dns_codec::encode(request);
70
+ }
71
+
72
+ template<class Handler>
73
+ void execute(std::chrono::milliseconds timeout, Handler&& handler)
74
+ {
75
+ asio::ip::udp::endpoint endpoint(address_, port_);
76
+ udp_.open(endpoint.protocol());
77
+ udp_.async_send_to(
78
+ asio::buffer(send_buf_),
79
+ endpoint,
80
+ [self = shared_from_this(), handler = std::forward<Handler>(handler)](std::error_code ec1,
81
+ std::size_t /* bytes_transferred */) mutable {
82
+ if (ec1 == asio::error::operation_aborted) {
83
+ self->deadline_.cancel();
84
+ return handler({ std::make_error_code(error::common_errc::ambiguous_timeout) });
85
+ }
86
+ if (ec1) {
87
+ self->deadline_.cancel();
88
+ return handler({ ec1 });
89
+ }
90
+
91
+ asio::ip::udp::endpoint sender_endpoint;
92
+ self->recv_buf_.resize(512);
93
+ self->udp_.async_receive_from(
94
+ asio::buffer(self->recv_buf_),
95
+ sender_endpoint,
96
+ [self, handler = std::forward<Handler>(handler)](std::error_code ec2, std::size_t bytes_transferred) mutable {
97
+ self->deadline_.cancel();
98
+ if (ec2) {
99
+ return handler({ ec2 });
100
+ }
101
+ self->recv_buf_.resize(bytes_transferred);
102
+ dns_message message = dns_codec::decode(self->recv_buf_);
103
+ if (message.header.flags.tc == truncation::yes) {
104
+ self->udp_.close();
105
+ return self->retry_with_tcp(std::forward<Handler>(handler));
106
+ }
107
+ dns_srv_response resp{ ec2 };
108
+ resp.targets.reserve(message.answers.size());
109
+ for (const auto& answer : message.answers) {
110
+ resp.targets.emplace_back(
111
+ dns_srv_response::address{ fmt::format("{}", fmt::join(answer.target.labels, ".")), answer.port });
112
+ }
113
+ return handler(resp);
114
+ });
115
+ });
116
+ deadline_.expires_after(timeout);
117
+ deadline_.async_wait([self = shared_from_this()](std::error_code ec) {
118
+ if (ec == asio::error::operation_aborted) {
119
+ return;
120
+ }
121
+ self->udp_.cancel();
122
+ self->tcp_.cancel();
123
+ });
124
+ }
125
+
126
+ private:
127
+ template<class Handler>
128
+ void retry_with_tcp(Handler&& handler)
129
+ {
130
+ asio::ip::tcp::no_delay no_delay(true);
131
+ std::error_code ignore_ec;
132
+ tcp_.set_option(no_delay, ignore_ec);
133
+ asio::ip::tcp::endpoint endpoint(address_, port_);
134
+ tcp_.async_connect(
135
+ endpoint, [self = shared_from_this(), handler = std::forward<Handler>(handler)](std::error_code ec1) mutable {
136
+ if (ec1) {
137
+ self->deadline_.cancel();
138
+ return handler({ ec1 });
139
+ }
140
+ auto send_size = static_cast<uint16_t>(self->send_buf_.size());
141
+ self->send_buf_.insert(self->send_buf_.begin(), std::uint8_t(send_size & 0xffU));
142
+ self->send_buf_.insert(self->send_buf_.begin(), std::uint8_t(send_size >> 8U));
143
+ asio::async_write(
144
+ self->tcp_,
145
+ asio::buffer(self->send_buf_),
146
+ [self, handler = std::forward<Handler>(handler)](std::error_code ec2, std::size_t /* bytes_transferred */) mutable {
147
+ if (ec2) {
148
+ self->deadline_.cancel();
149
+ if (ec2 == asio::error::operation_aborted) {
150
+ ec2 = std::make_error_code(error::common_errc::ambiguous_timeout);
151
+ }
152
+ return handler({ ec2 });
153
+ }
154
+ asio::async_read(self->tcp_,
155
+ asio::buffer(&self->recv_buf_size_, sizeof(self->recv_buf_size_)),
156
+ [self, handler = std::forward<Handler>(handler)](std::error_code ec3,
157
+ std::size_t /* bytes_transferred */) mutable {
158
+ if (ec3) {
159
+ self->deadline_.cancel();
160
+ return handler({ ec3 });
161
+ }
162
+ self->recv_buf_size_ = ntohs(self->recv_buf_size_);
163
+ self->recv_buf_.resize(self->recv_buf_size_);
164
+ asio::async_read(
165
+ self->tcp_,
166
+ asio::buffer(self->recv_buf_),
167
+ [self, handler = std::forward<Handler>(handler)](std::error_code ec4,
168
+ std::size_t bytes_transferred) mutable {
169
+ self->deadline_.cancel();
170
+ if (ec4) {
171
+ return handler({ ec4 });
172
+ }
173
+ self->recv_buf_.resize(bytes_transferred);
174
+ dns_message message = dns_codec::decode(self->recv_buf_);
175
+ dns_srv_response resp{ ec4 };
176
+ resp.targets.reserve(message.answers.size());
177
+ for (const auto& answer : message.answers) {
178
+ resp.targets.emplace_back(dns_srv_response::address{
179
+ fmt::format("{}", fmt::join(answer.target.labels, ".")), answer.port });
180
+ }
181
+ return handler(resp);
182
+ });
183
+ });
184
+ });
185
+ });
186
+ }
187
+
188
+ asio::steady_timer deadline_;
189
+ asio::ip::udp::socket udp_;
190
+ asio::ip::tcp::socket tcp_;
191
+
192
+ asio::ip::address address_;
193
+ std::uint16_t port_;
194
+
195
+ std::vector<uint8_t> send_buf_;
196
+ std::uint16_t recv_buf_size_{ 0 };
197
+ std::vector<uint8_t> recv_buf_;
198
+ };
199
+
200
+ explicit dns_client(asio::io_context& ctx)
201
+ : ctx_(ctx)
202
+ {
203
+ }
204
+
205
+ template<class Handler>
206
+ void query_srv(const std::string& name, const std::string& service, Handler&& handler)
207
+ {
208
+ dns_config& config = dns_config::get();
209
+ auto cmd = std::make_shared<dns_srv_command>(ctx_, name, service, config.address(), config.port());
210
+ cmd->execute(config.timeout(), std::forward<Handler>(handler));
211
+ }
212
+
213
+ asio::io_context& ctx_;
214
+ };
215
+ } // namespace couchbase::io::dns
@@ -0,0 +1,207 @@
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
+ #include <cstring>
21
+
22
+ #include <arpa/inet.h>
23
+
24
+ #include <io/dns_message.hxx>
25
+
26
+ namespace couchbase::io::dns
27
+ {
28
+ class dns_codec
29
+ {
30
+ public:
31
+ static dns_message decode(const std::vector<uint8_t>& payload)
32
+ {
33
+ dns_message message{};
34
+ std::size_t offset = 0;
35
+
36
+ std::memcpy(&message.header.id, payload.data() + offset, sizeof(std::uint16_t));
37
+ offset += sizeof(std::uint16_t);
38
+ message.header.id = ntohs(message.header.id);
39
+
40
+ uint16_t flags = 0;
41
+ std::memcpy(&flags, payload.data() + offset, sizeof(std::uint16_t));
42
+ offset += sizeof(std::uint16_t);
43
+ message.header.flags.decode(ntohs(flags));
44
+
45
+ std::memcpy(&message.header.question_records, payload.data() + offset, sizeof(std::uint16_t));
46
+ offset += sizeof(std::uint16_t);
47
+ message.header.question_records = ntohs(message.header.question_records);
48
+
49
+ std::memcpy(&message.header.answer_records, payload.data() + offset, sizeof(std::uint16_t));
50
+ offset += sizeof(std::uint16_t);
51
+ message.header.answer_records = ntohs(message.header.answer_records);
52
+
53
+ std::memcpy(&message.header.authority_records, payload.data() + offset, sizeof(std::uint16_t));
54
+ offset += sizeof(std::uint16_t);
55
+ message.header.authority_records = ntohs(message.header.authority_records);
56
+
57
+ std::memcpy(&message.header.additional_records, payload.data() + offset, sizeof(std::uint16_t));
58
+ offset += sizeof(std::uint16_t);
59
+ message.header.additional_records = ntohs(message.header.additional_records);
60
+
61
+ for (std::uint16_t idx = 0; idx < message.header.question_records; ++idx) {
62
+ question_record qr;
63
+ qr.name = get_name(payload, offset);
64
+
65
+ std::uint16_t val = 0;
66
+ std::memcpy(&val, payload.data() + offset, sizeof(std::uint16_t));
67
+ offset += sizeof(std::uint16_t);
68
+ val = ntohs(val);
69
+ qr.type = static_cast<resource_type>(val);
70
+
71
+ std::memcpy(&val, payload.data() + offset, sizeof(std::uint16_t));
72
+ offset += sizeof(std::uint16_t);
73
+ val = ntohs(val);
74
+ qr.klass = static_cast<resource_class>(val);
75
+
76
+ message.questions.emplace_back(qr);
77
+ }
78
+
79
+ message.answers.reserve(message.header.answer_records);
80
+ for (std::uint16_t idx = 0; idx < message.header.answer_records; ++idx) {
81
+ srv_record ar;
82
+ ar.name = get_name(payload, offset);
83
+
84
+ std::uint16_t val = 0;
85
+ std::memcpy(&val, payload.data() + offset, sizeof(std::uint16_t));
86
+ offset += sizeof(std::uint16_t);
87
+ val = ntohs(val);
88
+ ar.type = static_cast<resource_type>(val);
89
+
90
+ std::memcpy(&val, payload.data() + offset, sizeof(std::uint16_t));
91
+ offset += sizeof(std::uint16_t);
92
+ val = ntohs(val);
93
+ ar.klass = static_cast<resource_class>(val);
94
+
95
+ std::memcpy(&ar.ttl, payload.data() + offset, sizeof(std::uint32_t));
96
+ offset += static_cast<std::uint16_t>(4U);
97
+ ar.ttl = ntohl(ar.ttl);
98
+
99
+ std::uint16_t size = 0;
100
+ std::memcpy(&size, payload.data() + offset, sizeof(std::uint16_t));
101
+ offset += sizeof(std::uint16_t);
102
+ size = ntohs(size);
103
+
104
+ if (ar.klass != resource_class::in || ar.type != resource_type::srv) {
105
+ // ignore everything except SRV answers
106
+ offset += size;
107
+ continue;
108
+ }
109
+
110
+ std::memcpy(&val, payload.data() + offset, sizeof(std::uint16_t));
111
+ offset += sizeof(std::uint16_t);
112
+ ar.priority = ntohs(val);
113
+
114
+ std::memcpy(&val, payload.data() + offset, sizeof(std::uint16_t));
115
+ offset += sizeof(std::uint16_t);
116
+ ar.weight = ntohs(val);
117
+
118
+ std::memcpy(&val, payload.data() + offset, sizeof(std::uint16_t));
119
+ offset += sizeof(std::uint16_t);
120
+ ar.port = ntohs(val);
121
+
122
+ ar.target = get_name(payload, offset);
123
+
124
+ message.answers.emplace_back(ar);
125
+ }
126
+ return message;
127
+ }
128
+
129
+ static std::vector<uint8_t> encode(const dns_message& message)
130
+ {
131
+ std::vector<std::uint8_t> payload;
132
+ payload.resize(message.request_size(), 0);
133
+ std::size_t offset = 0;
134
+
135
+ // write header
136
+ {
137
+ uint16_t val;
138
+
139
+ val = htons(message.header.id);
140
+ std::memcpy(payload.data() + offset, &val, sizeof(std::uint16_t));
141
+ offset += sizeof(std::uint16_t);
142
+
143
+ val = htons(message.header.flags.encode());
144
+ std::memcpy(payload.data() + offset, &val, sizeof(std::uint16_t));
145
+ offset += sizeof(std::uint16_t);
146
+
147
+ val = htons(static_cast<std::uint16_t>(message.questions.size()));
148
+ std::memcpy(payload.data() + offset, &val, sizeof(std::uint16_t));
149
+ offset += sizeof(std::uint16_t) + 3 * sizeof(std::uint16_t); // answer, authority, additional are all zeros
150
+ }
151
+
152
+ // write body
153
+ for (const auto& question : message.questions) {
154
+ for (const auto& label : question.name.labels) {
155
+ payload[offset] = static_cast<std::uint8_t>(label.size());
156
+ ++offset;
157
+ std::memcpy(payload.data() + offset, label.data(), label.size());
158
+ offset += label.size();
159
+ }
160
+ payload[offset] = '\0';
161
+ ++offset;
162
+
163
+ uint16_t val;
164
+
165
+ val = htons(static_cast<std::uint16_t>(question.type));
166
+ std::memcpy(payload.data() + offset, &val, sizeof(std::uint16_t));
167
+ offset += sizeof(std::uint16_t);
168
+
169
+ val = htons(static_cast<std::uint16_t>(question.klass));
170
+ std::memcpy(payload.data() + offset, &val, sizeof(std::uint16_t));
171
+ offset += sizeof(std::uint16_t);
172
+ }
173
+ return payload;
174
+ }
175
+
176
+ private:
177
+ static resource_name get_name(const std::vector<std::uint8_t>& payload, std::size_t& offset)
178
+ {
179
+ resource_name name{};
180
+ std::optional<std::size_t> save_offset{};
181
+ while (true) {
182
+ std::uint8_t len = payload[offset];
183
+ if (len == 0) {
184
+ offset += 1;
185
+ if (save_offset) {
186
+ // restore offset after pointer jump
187
+ offset = *save_offset;
188
+ }
189
+ return name;
190
+ }
191
+ if ((len & 0b1100'0000U) != 0) {
192
+ std::uint16_t ptr = 0;
193
+ std::memcpy(&ptr, payload.data() + offset, sizeof(std::uint16_t));
194
+ ptr = ntohs(ptr);
195
+ ptr &= 0b0011'1111'1111'1111U;
196
+ // store old offset and jump to pointer
197
+ save_offset = offset + sizeof(std::uint16_t);
198
+ offset = ptr;
199
+ } else {
200
+ std::string label(payload.data() + offset + 1, payload.data() + offset + 1 + len);
201
+ name.labels.emplace_back(label);
202
+ offset += static_cast<std::uint16_t>(1U + len);
203
+ }
204
+ }
205
+ }
206
+ };
207
+ } // namespace couchbase::io::dns
@@ -0,0 +1,116 @@
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
+ #include <unistd.h>
21
+
22
+ #include <string>
23
+ #include <fstream>
24
+
25
+ #include <asio/ip/address.hpp>
26
+
27
+ #include <timeout_defaults.hxx>
28
+
29
+ namespace couchbase::io::dns
30
+ {
31
+ class dns_config
32
+ {
33
+ public:
34
+ static inline constexpr auto default_resolv_conf_path = "/etc/resolv.conf";
35
+ static inline constexpr auto default_host = "8.8.8.8";
36
+ static inline constexpr std::uint16_t default_port = 53;
37
+
38
+ [[nodiscard]] const asio::ip::address& address() const
39
+ {
40
+ return address_;
41
+ }
42
+
43
+ [[nodiscard]] std::uint16_t port() const
44
+ {
45
+ return port_;
46
+ }
47
+
48
+ [[nodiscard]] std::chrono::milliseconds timeout() const
49
+ {
50
+ return timeout_;
51
+ }
52
+
53
+ static dns_config& get()
54
+ {
55
+ static dns_config instance{};
56
+
57
+ instance.initialize();
58
+
59
+ return instance;
60
+ }
61
+
62
+ private:
63
+ void initialize()
64
+ {
65
+ if (!initialized_) {
66
+ load_resolv_conf(default_resolv_conf_path);
67
+ std::error_code ec;
68
+ address_ = asio::ip::address::from_string(host_, ec);
69
+ if (ec) {
70
+ host_ = default_host;
71
+ address_ = asio::ip::address::from_string(host_, ec);
72
+ }
73
+ initialized_ = true;
74
+ }
75
+ }
76
+
77
+ void load_resolv_conf(const char* conf_path)
78
+ {
79
+ if (access(conf_path, R_OK) == 0) {
80
+ std::ifstream conf(conf_path);
81
+ while (conf.good()) {
82
+ std::string line;
83
+ std::getline(conf, line);
84
+ if (line.empty()) {
85
+ continue;
86
+ }
87
+ std::size_t offset = 0;
88
+ while (line[offset] == ' ') {
89
+ ++offset;
90
+ }
91
+ if (line[offset] == '#') {
92
+ continue;
93
+ }
94
+ std::size_t space = line.find(' ', offset);
95
+ if (space == std::string::npos || space == offset || line.size() < space + 2) {
96
+ continue;
97
+ }
98
+ std::string keyword = line.substr(offset, space);
99
+ if (keyword != "nameserver") {
100
+ continue;
101
+ }
102
+ offset = space + 1;
103
+ space = line.find(' ', offset);
104
+ host_ = line.substr(offset, space);
105
+ break;
106
+ }
107
+ }
108
+ }
109
+
110
+ std::atomic_bool initialized_{ false };
111
+ std::string host_{ default_host };
112
+ asio::ip::address address_{};
113
+ std::uint16_t port_{ default_port };
114
+ std::chrono::milliseconds timeout_{ timeout_defaults::dns_srv_timeout };
115
+ };
116
+ } // namespace couchbase::io::dns