1 #include "connection.h"
2 
3 #include "error.h"
4 #include "logger.h"
5 
6 namespace couchnode
7 {
8 
Connection(Instance *instance)9 Connection::Connection(Instance *instance)
10     : _instance(instance)
11 {
12 }
13 
~Connection()14 Connection::~Connection()
15 {
16     if (_instance) {
17         _instance->shutdown();
18         _instance = nullptr;
19     }
20 }
21 
NAN_MODULE_INIT(Connection::Init)22 NAN_MODULE_INIT(Connection::Init)
23 {
24     Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(fnNew);
25     tpl->SetClassName(Nan::New<String>("CbConnection").ToLocalChecked());
26     tpl->InstanceTemplate()->SetInternalFieldCount(1);
27 
28     Nan::SetPrototypeMethod(tpl, "connect", fnConnect);
29     Nan::SetPrototypeMethod(tpl, "selectBucket", fnSelectBucket);
30     Nan::SetPrototypeMethod(tpl, "shutdown", fnShutdown);
31     Nan::SetPrototypeMethod(tpl, "cntl", fnCntl);
32     Nan::SetPrototypeMethod(tpl, "get", fnGet);
33     Nan::SetPrototypeMethod(tpl, "exists", fnExists);
34     Nan::SetPrototypeMethod(tpl, "getReplica", fnGetReplica);
35     Nan::SetPrototypeMethod(tpl, "store", fnStore);
36     Nan::SetPrototypeMethod(tpl, "remove", fnRemove);
37     Nan::SetPrototypeMethod(tpl, "touch", fnTouch);
38     Nan::SetPrototypeMethod(tpl, "unlock", fnUnlock);
39     Nan::SetPrototypeMethod(tpl, "counter", fnCounter);
40     Nan::SetPrototypeMethod(tpl, "lookupIn", fnLookupIn);
41     Nan::SetPrototypeMethod(tpl, "mutateIn", fnMutateIn);
42     Nan::SetPrototypeMethod(tpl, "viewQuery", fnViewQuery);
43     Nan::SetPrototypeMethod(tpl, "query", fnQuery);
44     Nan::SetPrototypeMethod(tpl, "analyticsQuery", fnAnalyticsQuery);
45     Nan::SetPrototypeMethod(tpl, "searchQuery", fnSearchQuery);
46     Nan::SetPrototypeMethod(tpl, "httpRequest", fnHttpRequest);
47     Nan::SetPrototypeMethod(tpl, "ping", fnPing);
48     Nan::SetPrototypeMethod(tpl, "diag", fnDiag);
49 
50     constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
51 
52     Nan::Set(target, Nan::New("Connection").ToLocalChecked(),
53              Nan::GetFunction(tpl).ToLocalChecked());
54 }
55 
NAN_METHOD(Connection::fnNew)56 NAN_METHOD(Connection::fnNew)
57 {
58     Nan::HandleScope scope;
59 
60     if (info.Length() != 7) {
61         return Nan::ThrowError(Error::create("expected 7 parameters"));
62     }
63 
64     lcb_STATUS err;
65 
66     lcb_io_opt_st *iops;
67     lcbuv_options_t iopsOptions;
68 
69     iopsOptions.version = 0;
70     iopsOptions.v.v0.loop = Nan::GetCurrentEventLoop();
71     iopsOptions.v.v0.startsop_noop = 1;
72 
73     err = lcb_create_libuv_io_opts(0, &iops, &iopsOptions);
74     if (err != LCB_SUCCESS) {
75         return Nan::ThrowError(Error::create(err));
76     }
77 
78     lcb_INSTANCE_TYPE connType = LCB_TYPE_BUCKET;
79     if (!info[0]->IsUndefined() && !info[0]->IsNull()) {
80         if (!info[0]->IsNumber()) {
81             return Nan::ThrowError(
82                 Error::create("must pass enum integer for connType"));
83         }
84 
85         connType = static_cast<lcb_INSTANCE_TYPE>(
86             Nan::To<uint32_t>(info[0]).ToChecked());
87     }
88 
89     lcb_CREATEOPTS *createOpts = nullptr;
90     lcb_createopts_create(&createOpts, connType);
91 
92     Nan::Utf8String *utfConnStr = NULL;
93     if (!info[1]->IsUndefined() && !info[1]->IsNull()) {
94         if (!info[1]->IsString()) {
95             return Nan::ThrowError(
96                 Error::create("must pass string for connStr"));
97         }
98         utfConnStr = new Nan::Utf8String(info[1]);
99 
100         lcb_createopts_connstr(createOpts, **utfConnStr, utfConnStr->length());
101     }
102 
103     Nan::Utf8String *utfUsername = NULL;
104     if (!info[2]->IsUndefined() && !info[2]->IsNull()) {
105         if (!info[2]->IsString()) {
106             return Nan::ThrowError(
107                 Error::create("must pass string for username"));
108         }
109         utfUsername = new Nan::Utf8String(info[2]);
110     }
111 
112     Nan::Utf8String *utfPassword = NULL;
113     if (!info[3]->IsUndefined() && !info[3]->IsNull()) {
114         if (!info[3]->IsString()) {
115             return Nan::ThrowError(
116                 Error::create("must pass string for password"));
117         }
118         utfPassword = new Nan::Utf8String(info[3]);
119     }
120 
121     if (utfUsername && utfPassword) {
122         lcb_createopts_credentials(createOpts, **utfUsername,
123                                    utfUsername->length(), **utfPassword,
124                                    utfPassword->length());
125     } else if (utfUsername) {
126         lcb_createopts_credentials(createOpts, **utfUsername,
127                                    utfUsername->length(), nullptr, 0);
128     } else if (utfPassword) {
129         lcb_createopts_credentials(createOpts, nullptr, 0, **utfPassword,
130                                    utfPassword->length());
131     }
132 
133     Logger *logger = nullptr;
134     if (!info[4]->IsUndefined() && !info[4]->IsNull()) {
135         if (!info[4]->IsFunction()) {
136             return Nan::ThrowError(
137                 Error::create("must pass function for logger"));
138         }
139 
140         Local<Function> logFn = info[4].As<Function>();
141         if (!logFn.IsEmpty()) {
142             logger = new Logger(logFn);
143             lcb_createopts_logger(createOpts, logger->lcbProcs());
144         }
145     }
146 
147     RequestTracer *tracer = nullptr;
148     if (!info[5]->IsUndefined() && !info[5]->IsNull()) {
149         if (!info[5]->IsObject()) {
150             return Nan::ThrowError(
151                 Error::create("must pass object for tracer"));
152         }
153 
154         Local<Object> tracerVal = info[5].As<Object>();
155         if (!tracerVal.IsEmpty()) {
156             tracer = new RequestTracer(tracerVal);
157             lcb_createopts_tracer(createOpts, tracer->lcbProcs());
158         }
159     }
160 
161     Meter *meter = nullptr;
162     if (!info[6]->IsUndefined() && !info[6]->IsNull()) {
163         if (!info[6]->IsObject()) {
164             return Nan::ThrowError(Error::create("must pass object for meter"));
165         }
166 
167         Local<Object> meterVal = info[6].As<Object>();
168         if (!meterVal.IsEmpty()) {
169             meter = new Meter(meterVal);
170             lcb_createopts_meter(createOpts, meter->lcbProcs());
171         }
172     }
173 
174     lcb_createopts_io(createOpts, iops);
175 
176     lcb_INSTANCE *instance;
177     err = lcb_create(&instance, createOpts);
178 
179     lcb_createopts_destroy(createOpts);
180 
181     if (utfConnStr) {
182         delete utfConnStr;
183     }
184     if (utfUsername) {
185         delete utfUsername;
186     }
187     if (utfPassword) {
188         delete utfPassword;
189     }
190 
191     if (err != LCB_SUCCESS) {
192         if (logger) {
193             delete logger;
194         }
195         if (tracer) {
196             delete tracer;
197         }
198         if (meter) {
199             delete meter;
200         }
201 
202         return Nan::ThrowError(Error::create(err));
203     }
204 
205     Instance *inst = new Instance(instance, logger, tracer, meter);
206 
207     Connection *obj = new Connection(inst);
208     obj->Wrap(info.This());
209 
210     inst->_connection = obj;
211 
212     info.GetReturnValue().Set(info.This());
213 }
214 
NAN_METHOD(Connection::fnConnect)215 NAN_METHOD(Connection::fnConnect)
216 {
217     Connection *me = ObjectWrap::Unwrap<Connection>(info.This());
218     Instance *inst = me->_instance;
219     Nan::HandleScope scope;
220 
221     if (info.Length() != 1) {
222         return Nan::ThrowError(Error::create("expected 1 parameter"));
223     }
224 
225     if (inst->_bootstrapCookie) {
226         delete inst->_bootstrapCookie;
227         inst->_bootstrapCookie = nullptr;
228     }
229     inst->_bootstrapCookie = new Cookie("connect", info[0].As<Function>());
230 
231     lcb_STATUS ec = lcb_connect(inst->_instance);
232     if (ec != LCB_SUCCESS) {
233         return Nan::ThrowError(Error::create(ec));
234     }
235 
236     info.GetReturnValue().Set(true);
237 }
238 
NAN_METHOD(Connection::fnSelectBucket)239 NAN_METHOD(Connection::fnSelectBucket)
240 {
241     Connection *me = ObjectWrap::Unwrap<Connection>(info.This());
242     Instance *inst = me->_instance;
243     Nan::HandleScope scope;
244 
245     if (info.Length() != 2) {
246         return Nan::ThrowError(Error::create("expected 2 parameters"));
247     }
248 
249     if (!info[0]->IsString()) {
250         return Nan::ThrowError(
251             Error::create("must pass string for bucket name"));
252     }
253 
254     Nan::Utf8String bucketName(info[0]);
255 
256     if (inst->_openCookie) {
257         delete inst->_openCookie;
258         inst->_openCookie = nullptr;
259     }
260     inst->_openCookie = new Cookie("open", info[1].As<Function>());
261 
262     lcb_STATUS ec = lcb_open(inst->_instance, *bucketName, bucketName.length());
263     if (ec != LCB_SUCCESS) {
264         return Nan::ThrowError(Error::create(ec));
265     }
266 
267     info.GetReturnValue().Set(true);
268 }
269 
NAN_METHOD(Connection::fnShutdown)270 NAN_METHOD(Connection::fnShutdown)
271 {
272     Connection *me = ObjectWrap::Unwrap<Connection>(info.This());
273     Nan::HandleScope scope;
274 
275     if (me->_instance) {
276         me->_instance->shutdown();
277         me->_instance = nullptr;
278     }
279 
280     info.GetReturnValue().Set(true);
281 }
282 
283 enum CntlFormat {
284     CntlInvalid = 0,
285     CntlTimeValue = 1,
286 };
287 
getCntlFormat(int option)288 CntlFormat getCntlFormat(int option)
289 {
290     switch (option) {
291     case LCB_CNTL_CONFIGURATION_TIMEOUT:
292     case LCB_CNTL_VIEW_TIMEOUT:
293     case LCB_CNTL_QUERY_TIMEOUT:
294     case LCB_CNTL_HTTP_TIMEOUT:
295     case LCB_CNTL_DURABILITY_INTERVAL:
296     case LCB_CNTL_DURABILITY_TIMEOUT:
297     case LCB_CNTL_OP_TIMEOUT:
298     case LCB_CNTL_CONFDELAY_THRESH:
299         return CntlTimeValue;
300     }
301 
302     return CntlInvalid;
303 }
304 
NAN_METHOD(Connection::fnCntl)305 NAN_METHOD(Connection::fnCntl)
306 {
307     Connection *me = ObjectWrap::Unwrap<Connection>(info.This());
308     Instance *inst = me->_instance;
309     Nan::HandleScope scope;
310 
311     int mode = Nan::To<int>(info[0]).FromJust();
312     int option = Nan::To<int>(info[1]).FromJust();
313 
314     CntlFormat fmt = getCntlFormat(option);
315     if (fmt == CntlTimeValue) {
316         if (mode == LCB_CNTL_GET) {
317             int val;
318             lcb_STATUS err = lcb_cntl(inst->_instance, mode, option, &val);
319             if (err != LCB_SUCCESS) {
320                 Nan::ThrowError(Error::create(err));
321                 return;
322             }
323 
324             info.GetReturnValue().Set(val);
325             return;
326         } else {
327             int val = Nan::To<int>(info[2]).FromJust();
328             lcb_STATUS err = lcb_cntl(inst->_instance, mode, option, &val);
329             if (err != LCB_SUCCESS) {
330                 Nan::ThrowError(Error::create(err));
331                 return;
332             }
333 
334             // No return value during a SET
335             return;
336         }
337     }
338 
339     Nan::ThrowError(Error::create("unexpected cntl cmd"));
340 }
341 
342 } // namespace couchnode
343