UNPKG

14.5 kBSource Map (JSON)View Raw
1{"version":3,"sources":["../src/socket.js"],"names":[],"mappings":"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","file":"socket.js","sourcesContent":["import { AsyncSubject } from 'rxjs/AsyncSubject'\nimport { BehaviorSubject } from 'rxjs/BehaviorSubject'\nimport { Subject } from 'rxjs/Subject'\nimport { Observable } from 'rxjs/Observable'\nimport { merge } from 'rxjs/observable/merge'\nimport { filter } from 'rxjs/operator/filter'\nimport { share } from 'rxjs/operator/share'\n\nimport { WebSocket } from './shim.js'\nimport { serialize, deserialize } from './serialization.js'\nimport { log } from './logging.js'\n\nconst PROTOCOL_VERSION = 'rethinkdb-horizon-v0'\n\n// Before connecting the first time\nconst STATUS_UNCONNECTED = { type: 'unconnected' }\n// After the websocket is opened, but before handshake\nconst STATUS_CONNECTED = { type: 'connected' }\n// After the websocket is opened and handshake is completed\nconst STATUS_READY = { type: 'ready' }\n// After unconnected, maybe before or after connected. Any socket level error\nconst STATUS_ERROR = { type: 'error' }\n// Occurs when the socket closes\nconst STATUS_DISCONNECTED = { type: 'disconnected' }\n\nclass ProtocolError extends Error {\n constructor(msg, errorCode) {\n super(msg)\n this.errorCode = errorCode\n }\n toString() {\n return `${this.message} (Code: ${this.errorCode})`\n }\n}\n\n// Wraps native websockets with a Subject, which is both an Subscriber\n// and an Observable (it is bi-directional after all!). This\n// implementation is adapted from Rx.DOM.fromWebSocket and\n// RxSocketSubject by Ben Lesh, but it also deals with some simple\n// protocol level things like serializing from/to JSON, routing\n// request_ids, looking at the `state` field to decide when an\n// observable is closed.\nclass HorizonSocket extends Subject {\n constructor(host, secure, path, handshaker) {\n const hostString = `ws${secure ? 's' : ''}://${host}/${path}`\n const msgBuffer = []\n let ws, handshakeDisp\n // Handshake is an asyncsubject because we want it to always cache\n // the last value it received, like a promise\n const handshake = new AsyncSubject()\n const statusSubject = new BehaviorSubject(STATUS_UNCONNECTED)\n\n const isOpen = () => Boolean(ws) && ws.readyState === WebSocket.OPEN\n\n // Serializes to a string before sending\n function wsSend(msg) {\n const stringMsg = JSON.stringify(serialize(msg))\n ws.send(stringMsg)\n }\n\n // This is the observable part of the Subject. It forwards events\n // from the underlying websocket\n const socketObservable = Observable.create(subscriber => {\n ws = new WebSocket(hostString, PROTOCOL_VERSION)\n ws.onerror = () => {\n // If the websocket experiences the error, we forward it through\n // to the observable. Unfortunately, the event we receive in\n // this callback doesn't tell us much of anything, so there's no\n // reason to forward it on and we just send a generic error.\n statusSubject.next(STATUS_ERROR)\n const errMsg = `Websocket ${hostString} experienced an error`\n subscriber.error(new Error(errMsg))\n }\n ws.onopen = () => {\n // Send the handshake\n statusSubject.next(STATUS_CONNECTED)\n handshakeDisp = this.makeRequest(handshaker()).subscribe(\n x => {\n handshake.next(x)\n handshake.complete()\n\n handshake.next(STATUS_READY)\n },\n err => handshake.error(err),\n () => handshake.complete()\n )\n // Send any messages that have been buffered\n while (msgBuffer.length > 0) {\n const msg = msgBuffer.shift()\n log('Sending buffered:', msg)\n wsSend(msg)\n }\n }\n ws.onmessage = event => {\n const deserialized = deserialize(JSON.parse(event.data))\n log('Received', deserialized)\n subscriber.next(deserialized)\n }\n ws.onclose = e => {\n // This will happen if the socket is closed by the server If\n // .close is called from the client (see closeSocket), this\n // listener will be removed\n statusSubject.next(STATUS_DISCONNECTED)\n if (e.code !== 1000 || !e.wasClean) {\n subscriber.error(\n new Error(`Socket closed unexpectedly with code: ${e.code}`))\n } else {\n subscriber.complete()\n }\n }\n return () => {\n if (handshakeDisp) {\n handshakeDisp.unsubscribe()\n }\n // This is the \"unsubscribe\" method on the final Subject\n closeSocket(1000, '')\n }\n })::share() // This makes it a \"hot\" observable, and refCounts it\n // Note possible edge cases: the `share` operator is equivalent to\n // .multicast(() => new Subject()).refCount() // RxJS 5\n // .multicast(new Subject()).refCount() // RxJS 4\n\n // This is the Subscriber part of the Subject. How we can send stuff\n // over the websocket\n const socketSubscriber = {\n next(messageToSend) {\n // When next is called on this subscriber\n // Note: If we aren't ready, the message is silently dropped\n if (isOpen()) {\n log('Sending', messageToSend)\n wsSend(messageToSend) // wsSend serializes to a string\n } else {\n log('Buffering', messageToSend)\n msgBuffer.push(messageToSend)\n }\n },\n error(error) {\n // The subscriber is receiving an error. Better close the\n // websocket with an error\n if (!error.code) {\n throw new Error('no code specified. Be sure to pass ' +\n '{ code: ###, reason: \"\" } to error()')\n }\n closeSocket(error.code, error.reason)\n },\n complete() {\n // complete for the subscriber here is equivalent to \"close\n // this socket successfully (which is what code 1000 is)\"\n closeSocket(1000, '')\n },\n }\n\n function closeSocket(code, reason) {\n statusSubject.next(STATUS_DISCONNECTED)\n if (!code) {\n ws.close() // successful close\n } else {\n ws.close(code, reason)\n }\n ws.onopen = undefined\n ws.onclose = undefined\n ws.onmessage = undefined\n }\n\n super(socketSubscriber, socketObservable)\n\n // Subscriptions will be the observable containing all\n // queries/writes/changefeed requests. Specifically, the documents\n // that initiate them, each one with a different request_id\n const subscriptions = new Subject()\n // Unsubscriptions is similar, only it holds only requests to\n // close a particular request_id on the server. Currently we only\n // need these for changefeeds.\n const unsubscriptions = new Subject()\n const outgoing = Observable::merge(subscriptions, unsubscriptions)\n // How many requests are outstanding\n let activeRequests = 0\n // Monotonically increasing counter for request_ids\n let requestCounter = 0\n // Unsubscriber for subscriptions/unsubscriptions\n let subDisp = null\n // Now that super has been called, we can add attributes to this\n this.handshake = handshake\n // Lets external users keep track of the current websocket status\n // without causing it to connect\n this.status = statusSubject\n\n const incrementActive = () => {\n if (++activeRequests === 1) {\n // We subscribe the socket itself to the subscription and\n // unsubscription requests. Since the socket is both an\n // observable and an subscriber. Here it's acting as an subscriber,\n // watching our requests.\n subDisp = outgoing.subscribe(this)\n }\n }\n\n // Decrement the number of active requests on the socket, and\n // close the socket if we're the last request\n const decrementActive = () => {\n if (--activeRequests === 0) {\n subDisp.unsubscribe()\n }\n }\n\n // This is used externally to send requests to the server\n this.makeRequest = rawRequest => Observable.create(reqSubscriber => {\n // Get a new request id\n const request_id = requestCounter++\n // Add the request id to the request and the unsubscribe request\n // if there is one\n rawRequest.request_id = request_id\n let unsubscribeRequest\n if (rawRequest.type === 'subscribe') {\n unsubscribeRequest = { request_id, type: 'end_subscription' }\n }\n // First, increment activeRequests and decide if we need to\n // connect to the socket\n incrementActive()\n\n // Now send the request to the server\n subscriptions.next(rawRequest)\n\n // Create an observable from the socket that filters by request_id\n const unsubscribeFilter = this\n ::filter(x => x.request_id === request_id)\n .subscribe(\n resp => {\n // Need to faithfully end the stream if there is an error\n if (resp.error !== undefined) {\n reqSubscriber.error(\n new ProtocolError(resp.error, resp.error_code))\n } else if (resp.data !== undefined ||\n resp.token !== undefined) {\n try {\n reqSubscriber.next(resp)\n } catch (e) { }\n }\n if (resp.state === 'synced') {\n // Create a little dummy object for sync notifications\n reqSubscriber.next({\n type: 'state',\n state: 'synced',\n })\n } else if (resp.state === 'complete') {\n reqSubscriber.complete()\n }\n },\n err => reqSubscriber.error(err),\n () => reqSubscriber.complete()\n )\n return () => {\n // Unsubscribe if necessary\n if (unsubscribeRequest) {\n unsubscriptions.next(unsubscribeRequest)\n }\n decrementActive()\n unsubscribeFilter.unsubscribe()\n }\n })\n }\n}\n\nmodule.exports = HorizonSocket\n"]}
\No newline at end of file