UNPKG

33.2 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.10.0
2var Connection, HANDSHAKE_AUTHFAIL, HANDSHAKE_SUCCESS, HttpConnection, Promise, TcpConnection, ar, aropt, crypto, cursors, err, events, mkAtom, mkErr, net, pbkdf2_cache, protoProtocol, protoQueryType, protoResponseType, protoVersion, protoVersionNumber, protodef, r, tls, util, varar,
3 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
4 hasProp = {}.hasOwnProperty;
5
6net = require('net');
7
8tls = require('tls');
9
10events = require('events');
11
12util = require('./util');
13
14err = require('./errors');
15
16cursors = require('./cursor');
17
18protodef = require('./proto-def');
19
20crypto = require("crypto");
21
22protoVersion = protodef.VersionDummy.Version.V1_0;
23
24protoVersionNumber = 0;
25
26protoProtocol = protodef.VersionDummy.Protocol.JSON;
27
28protoQueryType = protodef.Query.QueryType;
29
30protoResponseType = protodef.Response.ResponseType;
31
32r = require('./ast');
33
34Promise = require('bluebird');
35
36ar = util.ar;
37
38varar = util.varar;
39
40aropt = util.aropt;
41
42mkAtom = util.mkAtom;
43
44mkErr = util.mkErr;
45
46HANDSHAKE_SUCCESS = "SUCCESS";
47
48HANDSHAKE_AUTHFAIL = "ERROR: Incorrect authorization key.\n";
49
50Connection = (function(superClass) {
51 extend(Connection, superClass);
52
53 Connection.prototype.DEFAULT_HOST = 'localhost';
54
55 Connection.prototype.DEFAULT_PORT = 28015;
56
57 Connection.prototype.DEFAULT_AUTH_KEY = '';
58
59 Connection.prototype.DEFAULT_TIMEOUT = 20;
60
61 function Connection(host, callback) {
62 var conCallback, errCallback;
63 if (typeof host === 'undefined') {
64 host = {};
65 } else if (typeof host === 'string') {
66 host = {
67 host: host
68 };
69 }
70 this.host = host.host || this.DEFAULT_HOST;
71 this.port = host.port || this.DEFAULT_PORT;
72 this.db = host.db;
73 this.authKey = host.authKey || this.DEFAULT_AUTH_KEY;
74 this.timeout = host.timeout || this.DEFAULT_TIMEOUT;
75 if (typeof host.ssl === 'boolean' && host.ssl) {
76 this.ssl = {};
77 } else if (typeof host.ssl === 'object') {
78 this.ssl = host.ssl;
79 } else {
80 this.ssl = false;
81 }
82 this.outstandingCallbacks = {};
83 this.nextToken = 1;
84 this.open = false;
85 this.closing = false;
86 this.buffer = new Buffer(0);
87 this._events = this._events || {};
88 errCallback = (function(_this) {
89 return function(e) {
90 _this.removeListener('connect', conCallback);
91 if (e instanceof err.ReqlError) {
92 return callback(e);
93 } else {
94 return callback(new err.ReqlDriverError("Could not connect to " + _this.host + ":" + _this.port + ".\n" + e.message));
95 }
96 };
97 })(this);
98 this.once('error', errCallback);
99 conCallback = (function(_this) {
100 return function() {
101 _this.removeListener('error', errCallback);
102 _this.open = true;
103 return callback(null, _this);
104 };
105 })(this);
106 this.once('connect', conCallback);
107 this._closePromise = null;
108 }
109
110 Connection.prototype._data = function(buf) {
111 var response, responseBuffer, responseLength, results, token;
112 this.buffer = Buffer.concat([this.buffer, buf]);
113 results = [];
114 while (this.buffer.length >= 12) {
115 token = this.buffer.readUInt32LE(0) + 0x100000000 * this.buffer.readUInt32LE(4);
116 responseLength = this.buffer.readUInt32LE(8);
117 if (!(this.buffer.length >= (12 + responseLength))) {
118 break;
119 }
120 responseBuffer = this.buffer.slice(12, responseLength + 12);
121 response = JSON.parse(responseBuffer);
122 this._processResponse(response, token);
123 results.push(this.buffer = this.buffer.slice(12 + responseLength));
124 }
125 return results;
126 };
127
128 Connection.prototype._delQuery = function(token) {
129 return delete this.outstandingCallbacks[token];
130 };
131
132 Connection.prototype._processResponse = function(response, token) {
133 var cb, cursor, errType, feed, k, len1, note, opts, profile, ref, ref1, root;
134 profile = response.p;
135 if (this.outstandingCallbacks[token] != null) {
136 ref = this.outstandingCallbacks[token], cb = ref.cb, root = ref.root, cursor = ref.cursor, opts = ref.opts, feed = ref.feed;
137 if (cursor != null) {
138 cursor._addResponse(response);
139 if (cursor._endFlag && cursor._outstandingRequests === 0) {
140 return this._delQuery(token);
141 }
142 } else if (cb != null) {
143 switch (response.t) {
144 case protoResponseType.COMPILE_ERROR:
145 cb(mkErr(err.ReqlServerCompileError, response, root));
146 return this._delQuery(token);
147 case protoResponseType.CLIENT_ERROR:
148 cb(mkErr(err.ReqlDriverError, response, root));
149 return this._delQuery(token);
150 case protoResponseType.RUNTIME_ERROR:
151 errType = util.errorClass(response.e);
152 cb(mkErr(errType, response, root));
153 return this._delQuery(token);
154 case protoResponseType.SUCCESS_ATOM:
155 response = mkAtom(response, opts);
156 if (Array.isArray(response)) {
157 response = cursors.makeIterable(response);
158 }
159 if (profile != null) {
160 response = {
161 profile: profile,
162 value: response
163 };
164 }
165 cb(null, response);
166 return this._delQuery(token);
167 case protoResponseType.SUCCESS_PARTIAL:
168 cursor = null;
169 ref1 = response.n;
170 for (k = 0, len1 = ref1.length; k < len1; k++) {
171 note = ref1[k];
172 switch (note) {
173 case protodef.Response.ResponseNote.SEQUENCE_FEED:
174 if (cursor == null) {
175 cursor = new cursors.Feed(this, token, opts, root);
176 }
177 break;
178 case protodef.Response.ResponseNote.UNIONED_FEED:
179 if (cursor == null) {
180 cursor = new cursors.UnionedFeed(this, token, opts, root);
181 }
182 break;
183 case protodef.Response.ResponseNote.ATOM_FEED:
184 if (cursor == null) {
185 cursor = new cursors.AtomFeed(this, token, opts, root);
186 }
187 break;
188 case protodef.Response.ResponseNote.ORDER_BY_LIMIT_FEED:
189 if (cursor == null) {
190 cursor = new cursors.OrderByLimitFeed(this, token, opts, root);
191 }
192 }
193 }
194 if (cursor == null) {
195 cursor = new cursors.Cursor(this, token, opts, root);
196 }
197 this.outstandingCallbacks[token].cursor = cursor;
198 if (profile != null) {
199 return cb(null, {
200 profile: profile,
201 value: cursor._addResponse(response)
202 });
203 } else {
204 return cb(null, cursor._addResponse(response));
205 }
206 break;
207 case protoResponseType.SUCCESS_SEQUENCE:
208 cursor = new cursors.Cursor(this, token, opts, root);
209 this._delQuery(token);
210 if (profile != null) {
211 return cb(null, {
212 profile: profile,
213 value: cursor._addResponse(response)
214 });
215 } else {
216 return cb(null, cursor._addResponse(response));
217 }
218 break;
219 case protoResponseType.WAIT_COMPLETE:
220 this._delQuery(token);
221 return cb(null, null);
222 case protoResponseType.SERVER_INFO:
223 this._delQuery(token);
224 response = mkAtom(response, opts);
225 return cb(null, response);
226 default:
227 return cb(new err.ReqlDriverError("Unknown response type"));
228 }
229 }
230 } else {
231
232 }
233 };
234
235 Connection.prototype.close = varar(0, 2, function(optsOrCallback, callback) {
236 var cb, key, noreplyWait, opts;
237 if (callback != null) {
238 opts = optsOrCallback;
239 if (Object.prototype.toString.call(opts) !== '[object Object]') {
240 throw new err.ReqlDriverError("First argument to two-argument `close` must be an object.");
241 }
242 cb = callback;
243 } else if (Object.prototype.toString.call(optsOrCallback) === '[object Object]') {
244 opts = optsOrCallback;
245 cb = null;
246 } else if (typeof optsOrCallback === 'function') {
247 opts = {};
248 cb = optsOrCallback;
249 } else {
250 opts = optsOrCallback;
251 cb = null;
252 }
253 for (key in opts) {
254 if (!hasProp.call(opts, key)) continue;
255 if (key !== 'noreplyWait') {
256 throw new err.ReqlDriverError("First argument to two-argument `close` must be { noreplyWait: <bool> }.");
257 }
258 }
259 if (this._closePromise != null) {
260 return this._closePromise.nodeify(cb);
261 }
262 this.closing = true;
263 noreplyWait = ((opts.noreplyWait == null) || opts.noreplyWait) && this.open;
264 return this._closePromise = new Promise((function(_this) {
265 return function(resolve, reject) {
266 var wrappedCb;
267 wrappedCb = function(err, result) {
268 _this.open = false;
269 _this.closing = false;
270 _this.cancel();
271 if (err != null) {
272 return reject(err);
273 } else {
274 return resolve(result);
275 }
276 };
277 if (noreplyWait) {
278 return _this.noreplyWait(wrappedCb);
279 } else {
280 return wrappedCb();
281 }
282 };
283 })(this)).nodeify(cb);
284 });
285
286 Connection.prototype.noreplyWait = varar(0, 1, function(callback) {
287 var query, token;
288 if (!this.open) {
289 return new Promise(function(resolve, reject) {
290 return reject(new err.ReqlDriverError("Connection is closed."));
291 }).nodeify(callback);
292 }
293 token = this.nextToken++;
294 query = {};
295 query.type = protoQueryType.NOREPLY_WAIT;
296 query.token = token;
297 return new Promise((function(_this) {
298 return function(resolve, reject) {
299 var wrappedCb;
300 wrappedCb = function(err, result) {
301 if (err) {
302 return reject(err);
303 } else {
304 return resolve(result);
305 }
306 };
307 _this.outstandingCallbacks[token] = {
308 cb: wrappedCb,
309 root: null,
310 opts: null
311 };
312 return _this._sendQuery(query);
313 };
314 })(this)).nodeify(callback);
315 });
316
317 Connection.prototype.server = varar(0, 1, function(callback) {
318 var query, token;
319 if (!this.open) {
320 return new Promise(function(resolve, reject) {
321 return reject(new err.ReqlDriverError("Connection is closed."));
322 }).nodeify(callback);
323 }
324 token = this.nextToken++;
325 query = {};
326 query.type = protoQueryType.SERVER_INFO;
327 query.token = token;
328 return new Promise((function(_this) {
329 return function(resolve, reject) {
330 var wrappedCb;
331 wrappedCb = function(err, result) {
332 if (err) {
333 return reject(err);
334 } else {
335 return resolve(result);
336 }
337 };
338 _this.outstandingCallbacks[token] = {
339 cb: wrappedCb,
340 root: null,
341 opts: null
342 };
343 return _this._sendQuery(query);
344 };
345 })(this)).nodeify(callback);
346 });
347
348 Connection.prototype.cancel = ar(function() {
349 var key, ref, response, value;
350 response = {
351 t: protoResponseType.RUNTIME_ERROR,
352 r: ["Connection is closed."],
353 b: []
354 };
355 ref = this.outstandingCallbacks;
356 for (key in ref) {
357 if (!hasProp.call(ref, key)) continue;
358 value = ref[key];
359 if (value.cursor != null) {
360 value.cursor._addResponse(response);
361 } else if (value.cb != null) {
362 value.cb(mkErr(util.errorClass(response.e), response, value.root));
363 }
364 }
365 return this.outstandingCallbacks = {};
366 });
367
368 Connection.prototype.reconnect = varar(0, 2, function(optsOrCallback, callback) {
369 var cb, opts;
370 if (callback != null) {
371 opts = optsOrCallback;
372 cb = callback;
373 } else if (typeof optsOrCallback === "function") {
374 opts = {};
375 cb = optsOrCallback;
376 } else {
377 if (optsOrCallback != null) {
378 opts = optsOrCallback;
379 } else {
380 opts = {};
381 }
382 cb = callback;
383 }
384 return new Promise((function(_this) {
385 return function(resolve, reject) {
386 var closeCb;
387 closeCb = function(err) {
388 return _this.constructor.call(_this, {
389 host: _this.host,
390 port: _this.port,
391 timeout: _this.timeout,
392 authKey: _this.authKey
393 }, function(err, conn) {
394 if (err != null) {
395 return reject(err);
396 } else {
397 return resolve(conn);
398 }
399 });
400 };
401 return _this.close(opts, closeCb);
402 };
403 })(this)).nodeify(cb);
404 });
405
406 Connection.prototype.use = ar(function(db) {
407 return this.db = db;
408 });
409
410 Connection.prototype.isOpen = function() {
411 return this.open && !this.closing;
412 };
413
414 Connection.prototype._start = function(term, cb, opts) {
415 var key, query, token, value;
416 if (!this.open) {
417 throw new err.ReqlDriverError("Connection is closed.");
418 }
419 token = this.nextToken++;
420 query = {};
421 query.global_optargs = {};
422 query.type = protoQueryType.START;
423 query.query = term.build();
424 query.token = token;
425 for (key in opts) {
426 if (!hasProp.call(opts, key)) continue;
427 value = opts[key];
428 query.global_optargs[util.fromCamelCase(key)] = r.expr(value).build();
429 }
430 if ((opts.db != null) || (this.db != null)) {
431 query.global_optargs.db = r.db(opts.db || this.db).build();
432 }
433 if (opts.noreply != null) {
434 query.global_optargs['noreply'] = r.expr(!!opts.noreply).build();
435 }
436 if (opts.profile != null) {
437 query.global_optargs['profile'] = r.expr(!!opts.profile).build();
438 }
439 if ((opts.noreply == null) || !opts.noreply) {
440 this.outstandingCallbacks[token] = {
441 cb: cb,
442 root: term,
443 opts: opts
444 };
445 }
446 this._sendQuery(query);
447 if ((opts.noreply != null) && opts.noreply && typeof cb === 'function') {
448 return cb(null);
449 }
450 };
451
452 Connection.prototype._continueQuery = function(token) {
453 var query;
454 if (!this.open) {
455 throw new err.ReqlDriverError("Connection is closed.");
456 }
457 query = {
458 type: protoQueryType.CONTINUE,
459 token: token
460 };
461 return this._sendQuery(query);
462 };
463
464 Connection.prototype._endQuery = function(token) {
465 var query;
466 if (!this.open) {
467 throw new err.ReqlDriverError("Connection is closed.");
468 }
469 query = {
470 type: protoQueryType.STOP,
471 token: token
472 };
473 return this._sendQuery(query);
474 };
475
476 Connection.prototype._sendQuery = function(query) {
477 var data;
478 data = [query.type];
479 if (!(query.query === void 0)) {
480 data.push(query.query);
481 if ((query.global_optargs != null) && Object.keys(query.global_optargs).length > 0) {
482 data.push(query.global_optargs);
483 }
484 }
485 return this._writeQuery(query.token, JSON.stringify(data));
486 };
487
488 return Connection;
489
490})(events.EventEmitter);
491
492pbkdf2_cache = {};
493
494TcpConnection = (function(superClass) {
495 extend(TcpConnection, superClass);
496
497 TcpConnection.isAvailable = function() {
498 return !process.browser;
499 };
500
501 function TcpConnection(host, callback) {
502 var timeout;
503 if (!TcpConnection.isAvailable()) {
504 throw new err.ReqlDriverError("TCP sockets are not available in this environment");
505 }
506 TcpConnection.__super__.constructor.call(this, host, callback);
507 if (this.ssl) {
508 this.ssl.host = this.host;
509 this.ssl.port = this.port;
510 this.rawSocket = tls.connect(this.ssl);
511 } else {
512 this.rawSocket = net.connect(this.port, this.host);
513 }
514 this.rawSocket.setNoDelay();
515 this.rawSocket.setKeepAlive(true);
516 timeout = setTimeout(((function(_this) {
517 return function() {
518 _this.rawSocket.destroy();
519 return _this.emit('error', new err.ReqlTimeoutError("Could not connect to " + _this.host + ":" + _this.port + ", operation timed out."));
520 };
521 })(this)), this.timeout * 1000);
522 this.rawSocket.once('error', (function(_this) {
523 return function() {
524 return clearTimeout(timeout);
525 };
526 })(this));
527 this.rawSocket.once('connect', (function(_this) {
528 return function() {
529 var auth_i, auth_r, auth_salt, client_first_message_bare, compare_digest, handshake_callback, handshake_error, max, message, min, nullbyte, pbkdf2_hmac, protocol, r_string, server_first_message, server_signature, state, version, xor_bytes;
530 version = new Buffer(4);
531 version.writeUInt32LE(protoVersion, 0);
532 protocol = new Buffer(4);
533 protocol.writeUInt32LE(protoProtocol, 0);
534 r_string = new Buffer(crypto.randomBytes(18)).toString('base64');
535 _this.rawSocket.user = host["user"];
536 _this.rawSocket.password = host["password"];
537 if (_this.rawSocket.user === void 0) {
538 _this.rawSocket.user = "admin";
539 }
540 if (_this.rawSocket.password === void 0) {
541 _this.rawSocket.password = "";
542 }
543 client_first_message_bare = "n=" + _this.rawSocket.user + ",r=" + r_string;
544 message = JSON.stringify({
545 protocol_version: protoVersionNumber,
546 authentication_method: "SCRAM-SHA-256",
547 authentication: "n,," + client_first_message_bare
548 });
549 nullbyte = new Buffer('\0', "binary");
550 _this.rawSocket.write(Buffer.concat([version, Buffer(message.toString()), nullbyte]));
551 state = 1;
552 min = 0;
553 max = 0;
554 server_first_message = "";
555 server_signature = "";
556 auth_r = "";
557 auth_salt = "";
558 auth_i = 0;
559 xor_bytes = function(a, b) {
560 var i, k, len, ref, res;
561 res = [];
562 len = Math.min(a.length, b.length);
563 for (i = k = 0, ref = len; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
564 res.push(a[i] ^ b[i]);
565 }
566 return new Buffer(res);
567 };
568 pbkdf2_hmac = function(password, salt, iterations) {
569 var c, cache_string, k, mac, ref, t, u;
570 cache_string = password.toString("base64") + "," + salt.toString("base64") + "," + iterations.toString();
571 if (pbkdf2_cache[cache_string]) {
572 return pbkdf2_cache[cache_string];
573 }
574 mac = crypto.createHmac("sha256", password);
575 mac.update(salt);
576 mac.update("\x00\x00\x00\x01");
577 u = mac.digest();
578 t = u;
579 for (c = k = 0, ref = iterations - 1; 0 <= ref ? k < ref : k > ref; c = 0 <= ref ? ++k : --k) {
580 mac = crypto.createHmac("sha256", password);
581 mac.update(t);
582 t = mac.digest();
583 u = xor_bytes(u, t);
584 }
585 pbkdf2_cache[cache_string] = u;
586 return u;
587 };
588 compare_digest = function(a, b) {
589 var i, k, left, len, ref, result, right;
590 left = void 0;
591 right = b;
592 result = void 0;
593 if (a.length === b.length) {
594 left = a;
595 result = 0;
596 } else {
597 left = b;
598 result = 1;
599 }
600 len = Math.min(a.length, b.length);
601 for (i = k = 0, ref = len; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
602 result |= xor_bytes(a[i], b[i]);
603 }
604 return result === 0;
605 };
606 handshake_error = function(code, message) {
607 if ((10 <= code && code <= 20)) {
608 return _this.emit('error', new err.ReqlAuthError(message));
609 } else {
610 return _this.emit('error', new err.ReqlDriverError(message));
611 }
612 };
613 handshake_callback = function(buf) {
614 var auth_message, authentication, b, client_final_message_without_proof, client_key, client_proof, client_signature, first_equals, i, item, j, k, l, len1, len2, ref, ref1, salted_password, server_key, server_reply, status_buf, status_str, stored_key, v;
615 _this.buffer = Buffer.concat([_this.buffer, buf]);
616 j = 0;
617 ref = _this.buffer;
618 for (i = k = 0, len1 = ref.length; k < len1; i = ++k) {
619 b = ref[i];
620 if (b === 0) {
621 status_buf = _this.buffer.slice(j, i);
622 j = i + 1;
623 status_str = status_buf.toString();
624 server_reply = JSON.parse(status_str);
625 if (state === 1) {
626 if (!server_reply.success) {
627 handshake_error(server_reply.error_code, server_reply.error);
628 return;
629 }
630 min = server_reply.min_protocol_version;
631 max = server_reply.max_protocol_version;
632 if (min > protoVersionNumber || max < protoVersionNumber) {
633 throw new err.ReqlDriverError("Unsupported protocol version " + protoVersionNumber + ", expected between " + min + " and " + max + ".");
634 }
635 state = 2;
636 } else if (state === 2) {
637 if (!server_reply.success) {
638 handshake_error(server_reply.error_code, server_reply.error);
639 return;
640 }
641 authentication = {};
642 server_first_message = server_reply.authentication;
643 ref1 = server_first_message.split(",");
644 for (l = 0, len2 = ref1.length; l < len2; l++) {
645 item = ref1[l];
646 i = item.indexOf("=");
647 authentication[item.slice(0, i)] = item.slice(i + 1);
648 }
649 auth_r = authentication.r;
650 auth_salt = new Buffer(authentication.s, 'base64');
651 auth_i = parseInt(authentication.i);
652 if (!auth_r.substr(0, r_string) === r_string) {
653 throw new err.ReqlAuthError("Invalid nonce from server");
654 }
655 client_final_message_without_proof = "c=biws,r=" + auth_r;
656 salted_password = pbkdf2_hmac(_this.rawSocket.password, auth_salt, auth_i);
657 client_key = crypto.createHmac("sha256", salted_password).update("Client Key").digest();
658 stored_key = crypto.createHash("sha256").update(client_key).digest();
659 auth_message = client_first_message_bare + "," + server_first_message + "," + client_final_message_without_proof;
660 client_signature = crypto.createHmac("sha256", stored_key).update(auth_message).digest();
661 client_proof = xor_bytes(client_key, client_signature);
662 server_key = crypto.createHmac("sha256", salted_password).update("Server Key").digest();
663 server_signature = crypto.createHmac("sha256", server_key).update(auth_message).digest();
664 state = 3;
665 message = JSON.stringify({
666 authentication: client_final_message_without_proof + ",p=" + client_proof.toString("base64")
667 });
668 nullbyte = new Buffer('\0', "binary");
669 _this.rawSocket.write(Buffer.concat([Buffer(message.toString()), nullbyte]));
670 } else if (state === 3) {
671 if (!server_reply.success) {
672 handshake_error(server_reply.error_code, server_reply.error);
673 return;
674 }
675 first_equals = server_reply.authentication.indexOf('=');
676 v = server_reply.authentication.slice(first_equals + 1);
677 if (!compare_digest(v, server_signature.toString("base64"))) {
678 throw new err.ReqlAuthError("Invalid server signature");
679 }
680 state = 4;
681 _this.rawSocket.removeListener('data', handshake_callback);
682 _this.rawSocket.on('data', function(buf) {
683 return _this._data(buf);
684 });
685 clearTimeout(timeout);
686 _this.emit('connect');
687 } else {
688 throw new err.ReqlDriverError("Unexpected handshake state");
689 }
690 }
691 }
692 return _this.buffer = _this.buffer.slice(j + 1);
693 };
694 return _this.rawSocket.on('data', handshake_callback);
695 };
696 })(this));
697 this.rawSocket.on('error', (function(_this) {
698 return function(err) {
699 return _this.emit('error', err);
700 };
701 })(this));
702 this.rawSocket.on('close', (function(_this) {
703 return function() {
704 if (_this.isOpen()) {
705 _this.close({
706 noreplyWait: false
707 });
708 }
709 return _this.emit('close');
710 };
711 })(this));
712 this.rawSocket.on('timeout', (function(_this) {
713 return function() {
714 _this.open = false;
715 return _this.emit('timeout');
716 };
717 })(this));
718 }
719
720 TcpConnection.prototype.clientPort = function() {
721 return this.rawSocket.localPort;
722 };
723
724 TcpConnection.prototype.clientAddress = function() {
725 return this.rawSocket.localAddress;
726 };
727
728 TcpConnection.prototype.close = varar(0, 2, function(optsOrCallback, callback) {
729 var cb, opts;
730 if (callback != null) {
731 opts = optsOrCallback;
732 cb = callback;
733 } else if (Object.prototype.toString.call(optsOrCallback) === '[object Object]') {
734 opts = optsOrCallback;
735 cb = null;
736 } else if (typeof optsOrCallback === "function") {
737 opts = {};
738 cb = optsOrCallback;
739 } else {
740 opts = {};
741 }
742 return new Promise((function(_this) {
743 return function(resolve, reject) {
744 var wrappedCb;
745 wrappedCb = function(error, result) {
746 var cleanupSocket, closeCb, ref;
747 closeCb = function() {
748 if (error != null) {
749 return reject(error);
750 } else {
751 return resolve(result);
752 }
753 };
754 cleanupSocket = function() {
755 var ref;
756 closeCb();
757 if ((ref = _this.rawSocket) != null) {
758 ref.removeAllListeners();
759 }
760 _this.rawSocket = null;
761 return _this.emit("close");
762 };
763 if (_this.rawSocket != null) {
764 if (_this.rawSocket.readyState === 'closed') {
765 return cleanupSocket();
766 } else {
767 if ((ref = _this.rawSocket) != null) {
768 ref.once("close", cleanupSocket);
769 }
770 return _this.rawSocket.end();
771 }
772 } else {
773 return process.nextTick(closeCb);
774 }
775 };
776 return TcpConnection.__super__.close.call(_this, opts, wrappedCb);
777 };
778 })(this)).nodeify(cb);
779 });
780
781 TcpConnection.prototype.cancel = function() {
782 this.rawSocket.destroy();
783 return TcpConnection.__super__.cancel.call(this);
784 };
785
786 TcpConnection.prototype._writeQuery = function(token, data) {
787 var tokenBuf;
788 tokenBuf = new Buffer(8);
789 tokenBuf.writeUInt32LE(token & 0xFFFFFFFF, 0);
790 tokenBuf.writeUInt32LE(Math.floor(token / 0xFFFFFFFF), 4);
791 this.rawSocket.write(tokenBuf);
792 return this.write(new Buffer(data));
793 };
794
795 TcpConnection.prototype.write = function(chunk) {
796 var lengthBuffer;
797 lengthBuffer = new Buffer(4);
798 lengthBuffer.writeUInt32LE(chunk.length, 0);
799 this.rawSocket.write(lengthBuffer);
800 return this.rawSocket.write(chunk);
801 };
802
803 return TcpConnection;
804
805})(Connection);
806
807HttpConnection = (function(superClass) {
808 extend(HttpConnection, superClass);
809
810 HttpConnection.prototype.DEFAULT_PROTOCOL = 'http';
811
812 HttpConnection.isAvailable = function() {
813 return typeof XMLHttpRequest !== "undefined";
814 };
815
816 function HttpConnection(host, callback) {
817 var protocol, url, xhr;
818 if (!HttpConnection.isAvailable()) {
819 throw new err.ReqlDriverError("XMLHttpRequest is not available in this environment");
820 }
821 HttpConnection.__super__.constructor.call(this, host, callback);
822 protocol = host.protocol === 'https' ? 'https' : this.DEFAULT_PROTOCOL;
823 url = protocol + "://" + this.host + ":" + this.port + host.pathname + "ajax/reql/";
824 xhr = new XMLHttpRequest;
825 xhr.open("POST", url + ("open-new-connection?cacheBuster=" + (Math.random())), true);
826 xhr.responseType = "text";
827 xhr.onreadystatechange = (function(_this) {
828 return function(e) {
829 if (xhr.readyState === 4) {
830 if (xhr.status === 200) {
831 _this._url = url;
832 _this._connId = xhr.response;
833 return _this.emit('connect');
834 } else {
835 return _this.emit('error', new err.ReqlDriverError("XHR error, http status " + xhr.status + "."));
836 }
837 }
838 };
839 })(this);
840 xhr.send();
841 this.xhr = xhr;
842 }
843
844 HttpConnection.prototype.cancel = function() {
845 var xhr;
846 if (this._connId != null) {
847 this.xhr.abort();
848 xhr = new XMLHttpRequest;
849 xhr.open("POST", this._url + "close-connection?conn_id=" + this._connId, true);
850 xhr.responseType = "arraybuffer";
851 xhr.send();
852 this._url = null;
853 this._connId = null;
854 return HttpConnection.__super__.cancel.call(this);
855 }
856 };
857
858 HttpConnection.prototype.close = varar(0, 2, function(optsOrCallback, callback) {
859 var cb, opts;
860 if (callback != null) {
861 opts = optsOrCallback;
862 cb = callback;
863 } else if (Object.prototype.toString.call(optsOrCallback) === '[object Object]') {
864 opts = optsOrCallback;
865 cb = null;
866 } else {
867 opts = {};
868 cb = optsOrCallback;
869 }
870 if (!((cb == null) || typeof cb === 'function')) {
871 throw new err.ReqlDriverError("Final argument to `close` must be a callback function or object.");
872 }
873 return HttpConnection.__super__.close.call(this, opts, cb);
874 });
875
876 HttpConnection.prototype._writeQuery = function(token, data) {
877 var buf;
878 buf = new Buffer(encodeURI(data).split(/%..|./).length - 1 + 8);
879 buf.writeUInt32LE(token & 0xFFFFFFFF, 0);
880 buf.writeUInt32LE(Math.floor(token / 0xFFFFFFFF), 4);
881 buf.write(data, 8);
882 return this.write(buf, token);
883 };
884
885 HttpConnection.prototype.write = function(chunk, token) {
886 var i, view, xhr;
887 xhr = new XMLHttpRequest;
888 xhr.open("POST", this._url + "?conn_id=" + this._connId, true);
889 xhr.responseType = "arraybuffer";
890 xhr.onreadystatechange = (function(_this) {
891 return function(e) {
892 var b, buf;
893 if (xhr.readyState === 4 && xhr.status === 200) {
894 buf = new Buffer((function() {
895 var k, len1, ref, results;
896 ref = new Uint8Array(xhr.response);
897 results = [];
898 for (k = 0, len1 = ref.length; k < len1; k++) {
899 b = ref[k];
900 results.push(b);
901 }
902 return results;
903 })());
904 return _this._data(buf);
905 }
906 };
907 })(this);
908 xhr.onerror = (function(_this) {
909 return function(e) {
910 return _this.outstandingCallbacks[token].cb(new Error("This HTTP connection is not open"));
911 };
912 })(this);
913 view = new Uint8Array(chunk.length);
914 i = 0;
915 while (i < chunk.length) {
916 view[i] = chunk[i];
917 i++;
918 }
919 xhr.send(view);
920 return this.xhr = xhr;
921 };
922
923 return HttpConnection;
924
925})(Connection);
926
927module.exports.isConnection = function(connection) {
928 return connection instanceof Connection;
929};
930
931module.exports.connect = varar(0, 2, function(hostOrCallback, callback) {
932 var host;
933 if (typeof hostOrCallback === 'function') {
934 host = {};
935 callback = hostOrCallback;
936 } else {
937 host = hostOrCallback;
938 }
939 return new Promise(function(resolve, reject) {
940 var create_connection, wrappedCb;
941 if ((host.authKey != null) && ((host.password != null) || (host.user != null) || (host.username != null))) {
942 throw new err.ReqlDriverError("Cannot use both authKey and password");
943 }
944 if (host.user && host.username) {
945 throw new err.ReqlDriverError("Cannot use both user and username");
946 } else if (host.authKey) {
947 host.user = "admin";
948 host.password = host.authKey;
949 } else {
950 if (host.username != null) {
951 host.user = host.username;
952 }
953 }
954 create_connection = (function(_this) {
955 return function(host, callback) {
956 if (TcpConnection.isAvailable()) {
957 return new TcpConnection(host, callback);
958 } else if (HttpConnection.isAvailable()) {
959 return new HttpConnection(host, callback);
960 } else {
961 throw new err.ReqlDriverError("Neither TCP nor HTTP avaiable in this environment");
962 }
963 };
964 })(this);
965 wrappedCb = function(err, result) {
966 if (err) {
967 return reject(err);
968 } else {
969 return resolve(result);
970 }
971 };
972 return create_connection(host, wrappedCb);
973 }).nodeify(callback);
974});
975
976module.exports.Connection = Connection;
977
978module.exports.HttpConnection = HttpConnection;
979
980module.exports.TcpConnection = TcpConnection;