libcouchbase 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }