libcouchbase 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/ext/libcouchbase/.gitignore +2 -0
  3. data/ext/libcouchbase/CMakeLists.txt +5 -7
  4. data/ext/libcouchbase/README.markdown +2 -2
  5. data/ext/libcouchbase/RELEASE_NOTES.markdown +49 -0
  6. data/ext/libcouchbase/cmake/Modules/ConfigureDtrace.cmake +11 -0
  7. data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -0
  8. data/ext/libcouchbase/cmake/Modules/GetLibcouchbaseFlags.cmake +2 -1
  9. data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
  10. data/ext/libcouchbase/cmake/config-cmake.h.in +2 -0
  11. data/ext/libcouchbase/cmake/defs.mk.in +0 -2
  12. data/ext/libcouchbase/cmake/source_files.cmake +34 -14
  13. data/ext/libcouchbase/configure.pl +1 -1
  14. data/ext/libcouchbase/contrib/genhash/genhash.h +6 -0
  15. data/ext/libcouchbase/include/libcouchbase/auth.h +10 -0
  16. data/ext/libcouchbase/include/libcouchbase/couchbase.h +10 -0
  17. data/ext/libcouchbase/include/libcouchbase/error.h +7 -0
  18. data/ext/libcouchbase/include/libcouchbase/n1ql.h +13 -1
  19. data/ext/libcouchbase/include/libcouchbase/plugins/io/bsdio-inl.c +1 -1
  20. data/ext/libcouchbase/include/libcouchbase/subdoc.h +9 -0
  21. data/ext/libcouchbase/include/libcouchbase/views.h +7 -1
  22. data/ext/libcouchbase/include/libcouchbase/visibility.h +1 -0
  23. data/ext/libcouchbase/include/memcached/protocol_binary.h +21 -1132
  24. data/ext/libcouchbase/packaging/parse-git-describe.pl +1 -1
  25. data/ext/libcouchbase/plugins/io/libev/libev_io_opts.h +3 -2
  26. data/ext/libcouchbase/src/README.md +0 -2
  27. data/ext/libcouchbase/src/auth-priv.h +1 -0
  28. data/ext/libcouchbase/src/auth.cc +10 -0
  29. data/ext/libcouchbase/src/bootstrap.cc +216 -0
  30. data/ext/libcouchbase/src/bootstrap.h +50 -39
  31. data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +455 -0
  32. data/ext/libcouchbase/src/bucketconfig/bc_file.cc +281 -0
  33. data/ext/libcouchbase/src/bucketconfig/bc_http.cc +528 -0
  34. data/ext/libcouchbase/src/bucketconfig/bc_http.h +50 -25
  35. data/ext/libcouchbase/src/bucketconfig/bc_mcraw.cc +115 -0
  36. data/ext/libcouchbase/src/bucketconfig/clconfig.h +407 -386
  37. data/ext/libcouchbase/src/bucketconfig/confmon.cc +378 -0
  38. data/ext/libcouchbase/src/cbft.cc +22 -27
  39. data/ext/libcouchbase/src/cntl.cc +24 -24
  40. data/ext/libcouchbase/src/connspec.cc +30 -1
  41. data/ext/libcouchbase/src/connspec.h +17 -0
  42. data/ext/libcouchbase/src/dns-srv.cc +143 -0
  43. data/ext/libcouchbase/src/{dump.c → dump.cc} +8 -11
  44. data/ext/libcouchbase/src/getconfig.cc +73 -0
  45. data/ext/libcouchbase/src/handler.cc +84 -85
  46. data/ext/libcouchbase/src/hostlist.cc +0 -1
  47. data/ext/libcouchbase/src/hostlist.h +6 -1
  48. data/ext/libcouchbase/src/http/http-priv.h +125 -112
  49. data/ext/libcouchbase/src/http/http.cc +9 -29
  50. data/ext/libcouchbase/src/http/http.h +1 -34
  51. data/ext/libcouchbase/src/http/http_io.cc +22 -26
  52. data/ext/libcouchbase/src/instance.cc +102 -28
  53. data/ext/libcouchbase/src/internal.h +47 -29
  54. data/ext/libcouchbase/src/jsparse/parser.cc +146 -202
  55. data/ext/libcouchbase/src/jsparse/parser.h +91 -98
  56. data/ext/libcouchbase/src/lcbht/lcbht.cc +177 -0
  57. data/ext/libcouchbase/src/lcbht/lcbht.h +174 -163
  58. data/ext/libcouchbase/src/lcbio/connect.cc +562 -0
  59. data/ext/libcouchbase/src/lcbio/connect.h +9 -2
  60. data/ext/libcouchbase/src/lcbio/ctx.c +1 -1
  61. data/ext/libcouchbase/src/lcbio/iotable.h +61 -16
  62. data/ext/libcouchbase/src/lcbio/ioutils.h +1 -1
  63. data/ext/libcouchbase/src/lcbio/manager.c +2 -2
  64. data/ext/libcouchbase/src/lcbio/timer-cxx.h +87 -0
  65. data/ext/libcouchbase/src/mc/mcreq.h +9 -2
  66. data/ext/libcouchbase/src/mcserver/mcserver.cc +723 -0
  67. data/ext/libcouchbase/src/mcserver/mcserver.h +160 -70
  68. data/ext/libcouchbase/src/mcserver/negotiate.cc +118 -152
  69. data/ext/libcouchbase/src/mcserver/negotiate.h +85 -74
  70. data/ext/libcouchbase/src/mctx-helper.h +51 -0
  71. data/ext/libcouchbase/src/n1ql/ixmgmt.cc +1 -2
  72. data/ext/libcouchbase/src/n1ql/n1ql.cc +56 -32
  73. data/ext/libcouchbase/src/{newconfig.c → newconfig.cc} +42 -70
  74. data/ext/libcouchbase/src/nodeinfo.cc +4 -8
  75. data/ext/libcouchbase/src/operations/{cbflush.c → cbflush.cc} +7 -15
  76. data/ext/libcouchbase/src/operations/{counter.c → counter.cc} +0 -0
  77. data/ext/libcouchbase/src/operations/{durability-cas.c → durability-cas.cc} +92 -76
  78. data/ext/libcouchbase/src/operations/{durability-seqno.c → durability-seqno.cc} +55 -49
  79. data/ext/libcouchbase/src/operations/durability.cc +643 -0
  80. data/ext/libcouchbase/src/operations/durability_internal.h +212 -124
  81. data/ext/libcouchbase/src/operations/{get.c → get.cc} +24 -26
  82. data/ext/libcouchbase/src/operations/{observe-seqno.c → observe-seqno.cc} +5 -8
  83. data/ext/libcouchbase/src/operations/{observe.c → observe.cc} +69 -94
  84. data/ext/libcouchbase/src/operations/{pktfwd.c → pktfwd.cc} +0 -0
  85. data/ext/libcouchbase/src/operations/{remove.c → remove.cc} +0 -0
  86. data/ext/libcouchbase/src/operations/{stats.c → stats.cc} +66 -78
  87. data/ext/libcouchbase/src/operations/{store.c → store.cc} +27 -32
  88. data/ext/libcouchbase/src/operations/subdoc.cc +38 -18
  89. data/ext/libcouchbase/src/operations/{touch.c → touch.cc} +0 -0
  90. data/ext/libcouchbase/src/packetutils.h +200 -137
  91. data/ext/libcouchbase/src/probes.d +1 -1
  92. data/ext/libcouchbase/src/{retrychk.c → retrychk.cc} +3 -4
  93. data/ext/libcouchbase/src/retryq.cc +394 -0
  94. data/ext/libcouchbase/src/retryq.h +116 -104
  95. data/ext/libcouchbase/src/settings.h +2 -1
  96. data/ext/libcouchbase/src/ssl/ssl_c.c +1 -0
  97. data/ext/libcouchbase/src/ssl/ssl_e.c +0 -1
  98. data/ext/libcouchbase/src/trace.h +8 -8
  99. data/ext/libcouchbase/src/vbucket/vbucket.c +0 -1
  100. data/ext/libcouchbase/src/views/{docreq.c → docreq.cc} +48 -54
  101. data/ext/libcouchbase/src/views/docreq.h +24 -30
  102. data/ext/libcouchbase/src/views/viewreq.cc +318 -0
  103. data/ext/libcouchbase/src/views/viewreq.h +43 -13
  104. data/ext/libcouchbase/src/{wait.c → wait.cc} +12 -17
  105. data/ext/libcouchbase/tests/basic/t_connstr.cc +89 -50
  106. data/ext/libcouchbase/tests/basic/t_jsparse.cc +27 -78
  107. data/ext/libcouchbase/tests/basic/t_packet.cc +35 -42
  108. data/ext/libcouchbase/tests/htparse/t_basic.cc +58 -78
  109. data/ext/libcouchbase/tests/iotests/t_confmon.cc +94 -111
  110. data/ext/libcouchbase/tests/iotests/t_sched.cc +1 -2
  111. data/ext/libcouchbase/tests/mc/t_alloc.cc +9 -9
  112. data/ext/libcouchbase/tools/cbc-pillowfight.cc +1 -1
  113. data/lib/libcouchbase/version.rb +1 -1
  114. metadata +36 -39
  115. data/ext/libcouchbase/include/memcached/vbucket.h +0 -42
  116. data/ext/libcouchbase/src/bootstrap.c +0 -269
  117. data/ext/libcouchbase/src/bucketconfig/bc_cccp.c +0 -495
  118. data/ext/libcouchbase/src/bucketconfig/bc_file.c +0 -347
  119. data/ext/libcouchbase/src/bucketconfig/bc_http.c +0 -630
  120. data/ext/libcouchbase/src/bucketconfig/bc_mcraw.c +0 -150
  121. data/ext/libcouchbase/src/bucketconfig/confmon.c +0 -474
  122. data/ext/libcouchbase/src/getconfig.c +0 -100
  123. data/ext/libcouchbase/src/lcbht/lcbht.c +0 -282
  124. data/ext/libcouchbase/src/lcbio/connect.c +0 -557
  125. data/ext/libcouchbase/src/mcserver/mcserver.c +0 -784
  126. data/ext/libcouchbase/src/operations/durability.c +0 -668
  127. data/ext/libcouchbase/src/packetutils.c +0 -60
  128. data/ext/libcouchbase/src/retryq.c +0 -424
  129. data/ext/libcouchbase/src/simplestring.c +0 -211
  130. data/ext/libcouchbase/src/simplestring.h +0 -228
  131. data/ext/libcouchbase/src/ssobuf.h +0 -82
  132. data/ext/libcouchbase/src/views/viewreq.c +0 -358
  133. data/ext/libcouchbase/tests/basic/t_string.cc +0 -112
@@ -0,0 +1,281 @@
1
+ /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2013 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
+ #include "internal.h"
19
+ #include "clconfig.h"
20
+ #include <lcbio/lcbio.h>
21
+ #include <lcbio/timer-ng.h>
22
+ #include <lcbio/timer-cxx.h>
23
+ #include <fstream>
24
+ #include <iostream>
25
+ #include <istream>
26
+
27
+ #define CONFIG_CACHE_MAGIC "{{{fb85b563d0a8f65fa8d3d58f1b3a0708}}}"
28
+
29
+ #define LOGARGS(pb, lvl) static_cast<Provider*>(pb)->parent->settings, "bc_file", LCB_LOG_##lvl, __FILE__, __LINE__
30
+ #define LOGFMT "(cache=%s) "
31
+ #define LOGID(fb) fb->filename.c_str()
32
+
33
+ using namespace lcb::clconfig;
34
+
35
+ struct FileProvider : Provider, Listener {
36
+ FileProvider(Confmon* confmon);
37
+ ~FileProvider();
38
+
39
+ enum Status { CACHE_ERROR, NO_CHANGES, UPDATED };
40
+ Status load_cache();
41
+ void reload_cache();
42
+ void maybe_remove_file() {
43
+ if (!is_readonly && !filename.empty()) {
44
+ remove(filename.c_str());
45
+ }
46
+ }
47
+ void write_cache(lcbvb_CONFIG *vbc);
48
+
49
+ /* Overrides */
50
+ ConfigInfo *get_cached();
51
+ lcb_error_t refresh();
52
+ void dump(FILE *) const;
53
+ void clconfig_lsn(EventType, ConfigInfo*);
54
+
55
+ std::string filename;
56
+ ConfigInfo *config;
57
+ time_t last_mtime;
58
+ int last_errno;
59
+ bool is_readonly; /* Whether the config cache should _not_ overwrite the file */
60
+ lcb::io::Timer<FileProvider, &FileProvider::reload_cache> timer;
61
+ };
62
+
63
+ FileProvider::Status FileProvider::load_cache()
64
+ {
65
+ if (filename.empty()) {
66
+ return CACHE_ERROR;
67
+ }
68
+
69
+ std::ifstream ifs(filename.c_str(),
70
+ std::ios::in | std::ios::binary | std::ios::ate);
71
+
72
+ if (!ifs.is_open() || !ifs.good()) {
73
+ int save_errno = last_errno = errno;
74
+ lcb_log(LOGARGS(this, ERROR), LOGFMT "Couldn't open for reading: %s", LOGID(this), strerror(save_errno));
75
+ return CACHE_ERROR;
76
+ }
77
+
78
+ struct stat st;
79
+ if (stat(filename.c_str(), &st)) {
80
+ last_errno = errno;
81
+ return CACHE_ERROR;
82
+ }
83
+
84
+ if (last_mtime == st.st_mtime) {
85
+ lcb_log(LOGARGS(this, WARN), LOGFMT "Modification time too old", LOGID(this));
86
+ return NO_CHANGES;
87
+ }
88
+
89
+ size_t fsize = ifs.tellg();
90
+ if (!fsize) {
91
+ lcb_log(LOGARGS(this, WARN), LOGFMT "File '%s' is empty", LOGID(this), filename.c_str());
92
+ return CACHE_ERROR;
93
+ }
94
+ ifs.seekg(0, std::ios::beg);
95
+ std::vector<char> buf(fsize);
96
+ ifs.read(&buf[0], fsize);
97
+ buf.push_back(0); // NUL termination
98
+
99
+ char *end = std::strstr(&buf[0], CONFIG_CACHE_MAGIC);
100
+ if (end == NULL) {
101
+ lcb_log(LOGARGS(this, ERROR), LOGFMT "Couldn't find magic", LOGID(this));
102
+ maybe_remove_file();
103
+ return CACHE_ERROR;
104
+ }
105
+ *end = '\0'; // Stop parsing at MAGIC
106
+
107
+ lcbvb_CONFIG *vbc = lcbvb_create();
108
+ if (vbc == NULL) {
109
+ return CACHE_ERROR;
110
+ }
111
+
112
+ Status status = CACHE_ERROR;
113
+
114
+ if (lcbvb_load_json(vbc, &buf[0]) != 0) {
115
+ lcb_log(LOGARGS(this, ERROR), LOGFMT "Couldn't parse configuration", LOGID(this));
116
+ lcb_log_badconfig(LOGARGS(this, ERROR), vbc, &buf[0]);
117
+ maybe_remove_file();
118
+ goto GT_DONE;
119
+ }
120
+
121
+ if (lcbvb_get_distmode(vbc) != LCBVB_DIST_VBUCKET) {
122
+ lcb_log(LOGARGS(this, ERROR), LOGFMT "Not applying cached memcached config", LOGID(this));
123
+ goto GT_DONE;
124
+ }
125
+
126
+ if (strcmp(vbc->bname, settings().bucket) != 0) {
127
+ lcb_log(LOGARGS(this, ERROR), LOGFMT "Bucket name in file is different from the one requested", LOGID(this));
128
+ goto GT_DONE;
129
+ }
130
+
131
+ if (config) {
132
+ config->decref();
133
+ }
134
+
135
+ config = ConfigInfo::create(vbc, CLCONFIG_FILE);
136
+ last_mtime = st.st_mtime;
137
+
138
+ status = UPDATED;
139
+ vbc = NULL;
140
+
141
+ GT_DONE:
142
+ if (vbc != NULL) {
143
+ lcbvb_destroy(vbc);
144
+ }
145
+ return status;
146
+ }
147
+
148
+ void FileProvider::write_cache(lcbvb_CONFIG *cfg)
149
+ {
150
+ if (filename.empty() || is_readonly) {
151
+ return;
152
+ }
153
+
154
+ std::ofstream ofs(filename.c_str(), std::ios::trunc);
155
+ if (ofs.good()) {
156
+ lcb_log(LOGARGS(this, INFO), LOGFMT "Writing configuration to file", LOGID(this));
157
+ char *json = lcbvb_save_json(cfg);
158
+ ofs << json;
159
+ ofs << CONFIG_CACHE_MAGIC;
160
+ free(json);
161
+ } else {
162
+ int save_errno = errno;
163
+ lcb_log(LOGARGS(this, ERROR), LOGFMT "Couldn't open file for writing: %s", LOGID(this), strerror(save_errno));
164
+ }
165
+ }
166
+
167
+ ConfigInfo* FileProvider::get_cached() {
168
+ return filename.empty() ? NULL : config;
169
+ }
170
+
171
+ void FileProvider::reload_cache() {
172
+ if (load_cache() == UPDATED) {
173
+ parent->provider_got_config(this, config);
174
+ } else {
175
+ parent->provider_failed(this, LCB_ERROR);
176
+ }
177
+ }
178
+
179
+ lcb_error_t FileProvider::refresh() {
180
+ if (!timer.is_armed()) {
181
+ timer.signal();
182
+ }
183
+ return LCB_SUCCESS;
184
+ }
185
+
186
+ FileProvider::~FileProvider() {
187
+ timer.release();
188
+ if (config) {
189
+ config->decref();
190
+ }
191
+ }
192
+
193
+ void FileProvider::clconfig_lsn(EventType event, ConfigInfo *info) {
194
+ if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) {
195
+ return;
196
+ }
197
+ if (!enabled) {
198
+ return;
199
+ }
200
+
201
+ if (info->get_origin() == CLCONFIG_PHONY ||
202
+ info->get_origin() == CLCONFIG_FILE) {
203
+ lcb_log(LOGARGS(this, TRACE), "Not writing configuration originating from PHONY or FILE to cache");
204
+ return;
205
+ }
206
+
207
+ write_cache(info->vbc);
208
+ }
209
+
210
+ void FileProvider::dump(FILE *fp) const {
211
+ fprintf(fp, "## BEGIN FILE PROVIEDER DUMP ##\n");
212
+ if (!filename.empty()) {
213
+ fprintf(fp, "FILENAME: %s\n", filename.c_str());
214
+ }
215
+ fprintf(fp, "LAST SYSTEM ERRNO: %d\n", last_errno);
216
+ fprintf(fp, "LAST MTIME: %lu\n", (unsigned long)last_mtime);
217
+ fprintf(fp, "## END FILE PROVIDER DUMP ##\n");
218
+
219
+ }
220
+
221
+ FileProvider::FileProvider(Confmon *parent_)
222
+ : Provider(parent_, CLCONFIG_FILE),
223
+ config(NULL), last_mtime(0), last_errno(0), is_readonly(false),
224
+ timer(parent_->iot, this) {
225
+ parent->add_listener(this);
226
+ }
227
+
228
+ static std::string mkcachefile(const char *name, const char *bucket)
229
+ {
230
+ if (name != NULL) {
231
+ return std::string(name);
232
+ } else {
233
+ std::string buffer(lcb_get_tmpdir());
234
+ if (buffer.empty()) {
235
+ buffer += ".";
236
+ }
237
+ buffer += "/";
238
+ buffer += bucket;
239
+ return buffer;
240
+ }
241
+ }
242
+
243
+ bool lcb::clconfig::file_set_filename(Provider *p, const char *f, bool ro)
244
+ {
245
+ FileProvider *provider = static_cast<FileProvider*>(p);
246
+ provider->enabled = 1;
247
+ provider->filename = mkcachefile(f, p->parent->settings->bucket);
248
+ provider->is_readonly = bool(ro);
249
+
250
+ if (ro) {
251
+ FILE *fp_tmp = fopen(provider->filename.c_str(), "r");
252
+ if (!fp_tmp) {
253
+ lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't open for reading: %s", LOGID(provider), strerror(errno));
254
+ return false;
255
+ } else {
256
+ fclose(fp_tmp);
257
+ }
258
+ }
259
+ return true;
260
+ }
261
+
262
+ const char *
263
+ lcb::clconfig::file_get_filename(Provider *p)
264
+ {
265
+ FileProvider *fp = static_cast<FileProvider*>(p);
266
+ if (fp->filename.empty()) {
267
+ return NULL;
268
+ } else {
269
+ return fp->filename.c_str();
270
+ }
271
+ }
272
+
273
+ void
274
+ lcb::clconfig::file_set_readonly(Provider *p, bool val)
275
+ {
276
+ static_cast<FileProvider*>(p)->is_readonly = val;
277
+ }
278
+
279
+ Provider* lcb::clconfig::new_file_provider(Confmon *mon) {
280
+ return new FileProvider(mon);
281
+ }
@@ -0,0 +1,528 @@
1
+ /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2013 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
+ #include "internal.h"
19
+ #include "clconfig.h"
20
+ #include "bc_http.h"
21
+ #include <lcbio/ssl.h>
22
+ #include "ctx-log-inl.h"
23
+ #define LOGARGS(ht, lvlbase) ht->parent->settings, "htconfig", LCB_LOG_##lvlbase, __FILE__, __LINE__
24
+ #define LOGFMT "<%s:%s> "
25
+ #define LOGID(h) get_ctx_host(h->ioctx), get_ctx_port(h->ioctx)
26
+
27
+ using namespace lcb::clconfig;
28
+
29
+ static void io_error_handler(lcbio_CTX *, lcb_error_t);
30
+ static void on_connected(lcbio_SOCKET *, void *, lcb_error_t, lcbio_OSERR);
31
+ static void read_common(lcbio_CTX *, unsigned);
32
+
33
+ /**
34
+ * Determine if we're in compatibility mode with the previous versions of the
35
+ * library - where the idle timeout is disabled and a perpetual streaming
36
+ * connection will always remain open (regardless of whether it was triggered
37
+ * by start_refresh/get_refresh).
38
+ */
39
+ bool HttpProvider::is_v220_compat() const {
40
+ lcb_uint32_t setting = parent->settings->bc_http_stream_time;
41
+ return setting == (lcb_uint32_t)-1;
42
+ }
43
+
44
+ void HttpProvider::close_current()
45
+ {
46
+ disconn_timer.cancel();
47
+ if (ioctx) {
48
+ lcbio_ctx_close(ioctx, NULL, NULL);
49
+ } else if (creq){
50
+ lcbio_connect_cancel(creq);
51
+ }
52
+ creq = NULL;
53
+ ioctx = NULL;
54
+ }
55
+
56
+ /**
57
+ * Call when there is an error in I/O. This includes read, write, connect
58
+ * and timeouts.
59
+ */
60
+ lcb_error_t HttpProvider::on_io_error(lcb_error_t origerr)
61
+ {
62
+ close_current();
63
+
64
+ creq = lcbio_connect_hl(
65
+ parent->iot, &settings(), nodes, 0, settings().config_node_timeout,
66
+ on_connected, this);
67
+ if (creq) {
68
+ return LCB_SUCCESS;
69
+ }
70
+ parent->provider_failed(this, origerr);
71
+ io_timer.cancel();
72
+ if (is_v220_compat() && parent->config != NULL) {
73
+ lcb_log(LOGARGS(this, INFO), "HTTP node list finished. Trying to obtain connection from first node in list");
74
+ as_reconnect.arm_if_disarmed(settings().grace_next_cycle);
75
+ }
76
+ return origerr;
77
+ }
78
+
79
+ void set_new_config(HttpProvider *http)
80
+ {
81
+ const lcb_host_t *curhost;
82
+ if (http->current_config) {
83
+ http->current_config->decref();
84
+ }
85
+
86
+ curhost = lcbio_get_host(lcbio_ctx_sock(http->ioctx));
87
+ http->current_config = http->last_parsed;
88
+ http->current_config->incref();
89
+ lcbvb_replace_host(http->current_config->vbc, curhost->host);
90
+ http->parent->provider_got_config(http, http->current_config);
91
+ }
92
+
93
+ static lcb_error_t
94
+ process_chunk(HttpProvider *http, const void *buf, unsigned nbuf)
95
+ {
96
+ namespace htp = lcb::htparse;
97
+ lcb_error_t err = LCB_SUCCESS;
98
+ int rv;
99
+ lcbvb_CONFIG *cfgh;
100
+ unsigned state, oldstate, diff;
101
+ htp::Response& resp = http->htp->get_cur_response();
102
+
103
+ oldstate = resp.state;
104
+ state = http->htp->parse(buf, nbuf);
105
+ diff = state ^ oldstate;
106
+
107
+ if (state & htp::Parser::S_ERROR) {
108
+ return LCB_PROTOCOL_ERROR;
109
+ }
110
+
111
+ if (diff & htp::Parser::S_HEADER) {
112
+ /* see that we got a success? */
113
+ if (resp.status == 200) {
114
+ /* nothing */
115
+ } else if (resp.status == 404) {
116
+ const int urlmode = http->settings().bc_http_urltype;
117
+ err = LCB_BUCKET_ENOENT;
118
+
119
+ if (++http->uritype > LCB_HTCONFIG_URLTYPE_COMPAT) {
120
+ lcb_log(LOGARGS(http, ERR), LOGFMT "Got 404 on config stream. Assuming bucket does not exist as we've tried both URL types", LOGID(http));
121
+ goto GT_HT_ERROR;
122
+
123
+ } else if ((urlmode & LCB_HTCONFIG_URLTYPE_COMPAT) == 0) {
124
+ lcb_log(LOGARGS(http, ERR), LOGFMT "Got 404 on config stream for terse URI. Compat URI disabled, so not trying", LOGID(http));
125
+
126
+ } else {
127
+ /* reissue the request; but wait for it to drain */
128
+ lcb_log(LOGARGS(http, WARN), LOGFMT "Got 404 on config stream. Assuming terse URI not supported on cluster", LOGID(http));
129
+ http->try_nexturi = 1;
130
+ err = LCB_SUCCESS;
131
+ goto GT_CHECKDONE;
132
+ }
133
+ } else if (resp.status == 401) {
134
+ err = LCB_AUTH_ERROR;
135
+ } else {
136
+ err = LCB_ERROR;
137
+ }
138
+
139
+ GT_HT_ERROR:
140
+ if (err != LCB_SUCCESS) {
141
+ lcb_log(LOGARGS(http, ERR), LOGFMT "Got non-success HTTP status code %d", LOGID(http), resp.status);
142
+ return err;
143
+ }
144
+ }
145
+
146
+ GT_CHECKDONE:
147
+ if (http->try_nexturi) {
148
+ lcb_host_t *host;
149
+ if (!(state & htp::Parser::S_DONE)) {
150
+ return LCB_SUCCESS;
151
+ }
152
+ host = lcbio_get_host(lcbio_ctx_sock(http->ioctx));
153
+ http->try_nexturi = 0;
154
+ if ((err = http->setup_request_header(*host)) != LCB_SUCCESS) {
155
+ return err;
156
+ }
157
+
158
+ /* reset the state? */
159
+ http->htp->reset();
160
+ lcbio_ctx_put(http->ioctx, http->request_buf.c_str(), http->request_buf.size());
161
+ return LCB_SUCCESS;
162
+ }
163
+
164
+ if (http->settings().conntype == LCB_TYPE_CLUSTER) {
165
+ /* don't bother with parsing the actual config */
166
+ resp.body.clear();
167
+ return LCB_SUCCESS;
168
+ }
169
+ if (!(state & htp::Parser::S_BODY)) {
170
+ /* nothing to parse yet */
171
+ return LCB_SUCCESS;
172
+ }
173
+
174
+ /* seek ahead for strstr */
175
+ size_t termpos = resp.body.find(CONFIG_DELIMITER);
176
+ if (termpos == std::string::npos) {
177
+ return LCB_SUCCESS;
178
+ }
179
+ resp.body[termpos] = '\0';
180
+ cfgh = lcbvb_create();
181
+ if (!cfgh) {
182
+ return LCB_CLIENT_ENOMEM;
183
+ }
184
+ rv = lcbvb_load_json(cfgh, resp.body.c_str());
185
+ if (rv != 0) {
186
+ lcb_log(LOGARGS(http, ERR), LOGFMT "Failed to parse a valid config from HTTP stream", LOGID(http));
187
+ lcb_log_badconfig(LOGARGS(http, ERR), cfgh, resp.body.c_str());
188
+ lcbvb_destroy(cfgh);
189
+ return LCB_PROTOCOL_ERROR;
190
+ }
191
+ if (http->last_parsed) {
192
+ http->last_parsed->decref();
193
+ }
194
+ http->last_parsed = ConfigInfo::create(cfgh, CLCONFIG_HTTP);
195
+ http->generation++;
196
+
197
+ /** Relocate the stream */
198
+ resp.body.erase(0, termpos + sizeof(CONFIG_DELIMITER)-1);
199
+ return LCB_SUCCESS;
200
+ }
201
+
202
+ /**
203
+ * Common function to handle parsing the HTTP stream for both v0 and v1 io
204
+ * implementations.
205
+ */
206
+ static void
207
+ read_common(lcbio_CTX *ctx, unsigned nr)
208
+ {
209
+ lcbio_CTXRDITER riter;
210
+ HttpProvider *http = reinterpret_cast<HttpProvider*>(lcbio_ctx_data(ctx));
211
+ int old_generation = http->generation;
212
+
213
+ lcb_log(LOGARGS(http, TRACE), LOGFMT "Received %d bytes on HTTP stream", LOGID(http), nr);
214
+ http->io_timer.rearm(http->settings().config_node_timeout);
215
+
216
+ LCBIO_CTX_ITERFOR(ctx, &riter, nr) {
217
+ unsigned nbuf = lcbio_ctx_risize(&riter);
218
+ void *buf = lcbio_ctx_ribuf(&riter);
219
+ lcb_error_t err = process_chunk(http, buf, nbuf);
220
+
221
+ if (err != LCB_SUCCESS) {
222
+ http->on_io_error(err);
223
+ return;
224
+ }
225
+ }
226
+
227
+ if (http->generation != old_generation) {
228
+ lcb_log(LOGARGS(http, DEBUG), LOGFMT "Generation %d -> %d", LOGID(http), old_generation, http->generation);
229
+ http->io_timer.cancel();
230
+ set_new_config(http);
231
+ }
232
+
233
+ lcbio_ctx_rwant(ctx, 1);
234
+ lcbio_ctx_schedule(ctx);
235
+ }
236
+
237
+ lcb_error_t HttpProvider::setup_request_header(const lcb_host_t &host) {
238
+ request_buf.assign("GET ");
239
+ if (settings().conntype == LCB_TYPE_BUCKET) {
240
+ if (uritype == LCB_HTCONFIG_URLTYPE_25PLUS) {
241
+ request_buf.append(REQBUCKET_TERSE_PREFIX);
242
+ } else {
243
+ request_buf.append(REQBUCKET_COMPAT_PREFIX);
244
+ }
245
+ request_buf.append(settings().bucket);
246
+ } else if (settings().conntype == LCB_TYPE_CLUSTER) {
247
+ request_buf.append(REQPOOLS_URI);
248
+ } else {
249
+ return LCB_EINVAL;
250
+ }
251
+
252
+ request_buf.append(" HTTP/1.1\r\n");
253
+ const char *username = NULL, *password = NULL;
254
+ lcbauth_get_upass(settings().auth, &username, &password);
255
+
256
+ if (password) {
257
+ char cred[256], b64[256];
258
+ snprintf(cred, sizeof(cred), "%s:%s", username, password);
259
+
260
+ if (lcb_base64_encode(cred, b64, sizeof(b64)) == -1) {
261
+ return LCB_EINTERNAL;
262
+ }
263
+ request_buf.append("Authorization: Basic ").append(b64).append("\r\n");
264
+ }
265
+ request_buf.append("Host: ").append(host.host).append(":").append(host.port).append("\r\n");
266
+ request_buf.append("User-Agent: libcouchbase/").append(LCB_VERSION_STRING).append("\r\n");
267
+ request_buf.append("\r\n");
268
+ return LCB_SUCCESS;
269
+ }
270
+
271
+ void HttpProvider::reset_stream_state() {
272
+ const int urlmode = settings().bc_http_urltype;
273
+ if (last_parsed) {
274
+ last_parsed->decref();
275
+ last_parsed = NULL;
276
+ }
277
+ if (urlmode & LCB_HTCONFIG_URLTYPE_25PLUS) {
278
+ uritype = LCB_HTCONFIG_URLTYPE_25PLUS;
279
+ } else {
280
+ uritype = LCB_HTCONFIG_URLTYPE_COMPAT;
281
+ }
282
+ try_nexturi = false;
283
+ htp->reset();
284
+ }
285
+
286
+ static void
287
+ on_connected(lcbio_SOCKET *sock, void *arg, lcb_error_t err, lcbio_OSERR syserr)
288
+ {
289
+ HttpProvider *http = reinterpret_cast<HttpProvider*>(arg);
290
+ lcb_host_t *host;
291
+ lcbio_CTXPROCS procs;
292
+ http->creq = NULL;
293
+
294
+ if (err != LCB_SUCCESS) {
295
+ lcb_log(LOGARGS(http, ERR), "Connection to REST API failed with %s (os errno = %d)", lcb_strerror_short(err), syserr);
296
+ http->on_io_error(err);
297
+ return;
298
+ }
299
+ host = lcbio_get_host(sock);
300
+ lcb_log(LOGARGS(http, DEBUG), "Successfuly connected to REST API %s:%s", host->host, host->port);
301
+
302
+ lcbio_sslify_if_needed(sock, http->parent->settings);
303
+ http->reset_stream_state();
304
+
305
+ if ((err = http->setup_request_header(*host)) != LCB_SUCCESS) {
306
+ lcb_log(LOGARGS(http, ERR), "Couldn't setup request header");
307
+ http->on_io_error(err);
308
+ return;
309
+ }
310
+
311
+ memset(&procs, 0, sizeof(procs));
312
+ procs.cb_err = io_error_handler;
313
+ procs.cb_read = read_common;
314
+ http->ioctx = lcbio_ctx_new(sock, http, &procs);
315
+ http->ioctx->subsys = "bc_http";
316
+
317
+ lcbio_ctx_put(http->ioctx, http->request_buf.c_str(), http->request_buf.size());
318
+ lcbio_ctx_rwant(http->ioctx, 1);
319
+ lcbio_ctx_schedule(http->ioctx);
320
+ http->io_timer.rearm(http->settings().config_node_timeout);
321
+ }
322
+
323
+ void HttpProvider::on_timeout() {
324
+ lcb_log(LOGARGS(this, ERR), LOGFMT "HTTP Provider timed out waiting for I/O", LOGID(this));
325
+
326
+ /**
327
+ * If we're not the current provider then ignore the timeout until we're
328
+ * actively requested to do so
329
+ */
330
+ if (this != parent->cur_provider || !parent->is_refreshing()) {
331
+ lcb_log(LOGARGS(this, DEBUG), LOGFMT "Ignoring timeout because we're either not in a refresh or not the current provider", LOGID(this));
332
+ return;
333
+ }
334
+
335
+ on_io_error(LCB_ETIMEDOUT);
336
+ }
337
+
338
+
339
+ lcb_error_t HttpProvider::connect_next() {
340
+ lcb_log(LOGARGS(this, TRACE), "Starting HTTP Configuration Provider %p", (void*)this);
341
+ close_current();
342
+ as_reconnect.cancel();
343
+
344
+ if (nodes->empty()) {
345
+ lcb_log(LOGARGS(this, ERROR), "Not scheduling HTTP provider since no nodes have been configured for HTTP bootstrap");
346
+ return LCB_CONNECT_ERROR;
347
+ }
348
+
349
+ creq = lcbio_connect_hl(parent->iot, &settings(), nodes, 1,
350
+ settings().config_node_timeout, on_connected, this);
351
+ if (creq) {
352
+ return LCB_SUCCESS;
353
+ }
354
+ lcb_log(LOGARGS(this, ERROR), "%p: Couldn't schedule connection", (void*)this);
355
+ return LCB_CONNECT_ERROR;
356
+ }
357
+
358
+ void HttpProvider::delayed_disconn() {
359
+ lcb_log(LOGARGS(this, DEBUG), "Stopping HTTP provider %p", (void*)this);
360
+
361
+ /** closes the connection and cleans up the timer */
362
+ close_current();
363
+ io_timer.cancel();
364
+ }
365
+
366
+ void HttpProvider::delayed_reconnect() {
367
+ if (ioctx) {
368
+ /* have a context already */
369
+ return;
370
+ }
371
+ lcb_error_t err = connect_next();
372
+ if (err != LCB_SUCCESS) {
373
+ on_io_error(err);
374
+ }
375
+ }
376
+
377
+ bool HttpProvider::pause() {
378
+ if (is_v220_compat()) {
379
+ return LCB_SUCCESS;
380
+ }
381
+ disconn_timer.arm_if_disarmed(settings().bc_http_stream_time);
382
+ return LCB_SUCCESS;
383
+ }
384
+
385
+ lcb_error_t HttpProvider::refresh() {
386
+ /**
387
+ * We want a grace interval here because we might already be fetching a
388
+ * connection. HOWEVER we don't want to indefinitely wait on a socket
389
+ * so we issue a timer indicating how long we expect to wait for a
390
+ * streaming update until we get something.
391
+ */
392
+
393
+ /** If we need a new socket, we do connect_next. */
394
+ if (ioctx == NULL && creq == NULL) {
395
+ as_reconnect.signal();
396
+ }
397
+ disconn_timer.cancel();
398
+ if (ioctx) {
399
+ io_timer.rearm(settings().config_node_timeout);
400
+ }
401
+ return LCB_SUCCESS;
402
+ }
403
+
404
+ ConfigInfo* HttpProvider::get_cached() {
405
+ return current_config;
406
+ }
407
+
408
+ void HttpProvider::config_updated(lcbvb_CONFIG *newconfig)
409
+ {
410
+ unsigned sopts;
411
+ lcbvb_SVCMODE mode;
412
+ nodes->clear();
413
+
414
+ sopts = settings().sslopts;
415
+ if (sopts & LCB_SSL_ENABLED) {
416
+ mode = LCBVB_SVCMODE_SSL;
417
+ } else {
418
+ mode = LCBVB_SVCMODE_PLAIN;
419
+ }
420
+
421
+ for (size_t ii = 0; ii < newconfig->nsrv; ++ii) {
422
+ const char *ss;
423
+ lcb_error_t status;
424
+ ss = lcbvb_get_hostport(newconfig, ii, LCBVB_SVCTYPE_MGMT, mode);
425
+ if (!ss) {
426
+ /* not supported? */
427
+ continue;
428
+ }
429
+ status = nodes->add(ss, LCB_CONFIG_HTTP_PORT);
430
+ lcb_assert(status == LCB_SUCCESS);
431
+ }
432
+ if (nodes->empty()) {
433
+ lcb_log(LOGARGS(this, FATAL), "New nodes do not contain management ports");
434
+ }
435
+
436
+ if (settings().randomize_bootstrap_nodes) {
437
+ nodes->randomize();
438
+ }
439
+ }
440
+
441
+ void HttpProvider::configure_nodes(const lcb::Hostlist& newnodes) {
442
+ nodes->assign(newnodes);
443
+ if (settings().randomize_bootstrap_nodes) {
444
+ nodes->randomize();
445
+ }
446
+ }
447
+
448
+ const lcb::Hostlist* HttpProvider::get_nodes() const {
449
+ return nodes;
450
+ }
451
+
452
+ HttpProvider::~HttpProvider() {
453
+ reset_stream_state();
454
+ close_current();
455
+ delete htp;
456
+ disconn_timer.release();
457
+ io_timer.release();
458
+ as_reconnect.release();
459
+
460
+ if (current_config) {
461
+ current_config->decref();
462
+ }
463
+ if (nodes) {
464
+ delete nodes;
465
+ }
466
+ }
467
+
468
+ void HttpProvider::dump(FILE *fp) const {
469
+ fprintf(fp, "## BEGIN HTTP PROVIDER DUMP\n");
470
+ fprintf(fp, "NUMBER OF CONFIGS RECEIVED: %u\n", generation);
471
+ fprintf(fp, "DUMPING I/O TIMER\n");
472
+ io_timer.dump(fp);
473
+ if (ioctx) {
474
+ fprintf(fp, "DUMPING CURRENT CONNECTION:\n");
475
+ lcbio_ctx_dump(ioctx, fp);
476
+ } else if (creq) {
477
+ fprintf(fp, "CURRENTLY CONNECTING..\n");
478
+ } else {
479
+ fprintf(fp, "NO CONNECTION ACTIVE\n");
480
+ }
481
+ }
482
+
483
+
484
+ HttpProvider::HttpProvider(Confmon *parent_)
485
+ : Provider(parent_, CLCONFIG_HTTP),
486
+ ioctx(NULL),
487
+ htp(new lcb::htparse::Parser(parent->settings)),
488
+ disconn_timer(parent->iot, this),
489
+ io_timer(parent->iot, this),
490
+ as_reconnect(parent->iot, this),
491
+ nodes(new Hostlist()),
492
+ current_config(NULL),
493
+ last_parsed(NULL),
494
+ generation(0),
495
+ try_nexturi(false),
496
+ uritype(0) {
497
+
498
+ memset(&creq, 0, sizeof creq);
499
+ }
500
+
501
+ static void
502
+ io_error_handler(lcbio_CTX *ctx, lcb_error_t err)
503
+ {
504
+ reinterpret_cast<HttpProvider *>(lcbio_ctx_data(ctx))->on_io_error(err);
505
+ }
506
+
507
+
508
+ const lcbio_SOCKET* lcb::clconfig::http_get_conn(const Provider *p) {
509
+ const HttpProvider *http = static_cast<const HttpProvider *>(p);
510
+ if (!http->ioctx) {
511
+ return NULL;
512
+ }
513
+ return lcbio_ctx_sock(http->ioctx);
514
+
515
+ }
516
+
517
+ const lcb_host_t* lcb::clconfig::http_get_host(const Provider *p)
518
+ {
519
+ const lcbio_SOCKET *sock = http_get_conn(p);
520
+ if (sock) {
521
+ return lcbio_get_host(sock);
522
+ }
523
+ return NULL;
524
+ }
525
+
526
+ Provider* lcb::clconfig::new_http_provider(Confmon* mon) {
527
+ return new HttpProvider(mon);
528
+ }