libcouchbase 0.3.3 → 1.0.0

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