diff --git a/packages/ddp-client/.npm/package/npm-shrinkwrap.json b/packages/ddp-client/.npm/package/npm-shrinkwrap.json index 46fc9fb495..56bebb3f83 100644 --- a/packages/ddp-client/.npm/package/npm-shrinkwrap.json +++ b/packages/ddp-client/.npm/package/npm-shrinkwrap.json @@ -1,35 +1,10 @@ { "lockfileVersion": 1, "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=" - }, - "http-parser-js": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.6.tgz", - "integrity": "sha1-GVJz9YcExFLWcQdr4gEyndNB3FU=" - }, "lolex": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.4.0.tgz", "integrity": "sha1-LycSsbwYDendzF06epbvPAuxYq0=" - }, - "permessage-deflate": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/permessage-deflate/-/permessage-deflate-0.1.6.tgz", - "integrity": "sha1-WB8c7fvUQPrEfQd3vohjM4a5kt4=" - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=" - }, - "websocket-extensions": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.2.tgz", - "integrity": "sha1-Dhh4HeYpoYMIzhSBZQ9n/6JpOl0=" } } } diff --git a/packages/ddp-client/client/client.js b/packages/ddp-client/client/client.js index f2cbc06654..fd9c746bbc 100644 --- a/packages/ddp-client/client/client.js +++ b/packages/ddp-client/client/client.js @@ -1,11 +1,5 @@ export { DDP } from '../common/namespace.js'; -if (false) { - // This is used inside livedata_connection, but this is what gets - // it included in the client bundle - import './stream_client_sockjs'; -} - import '../common/livedata_connection'; // Initialize the default server connection and put it on Meteor.connection diff --git a/packages/ddp-client/common/getClientStreamClass.js b/packages/ddp-client/common/getClientStreamClass.js deleted file mode 100644 index 06c0bcfe63..0000000000 --- a/packages/ddp-client/common/getClientStreamClass.js +++ /dev/null @@ -1,18 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -// In the client and server entry points, we make sure the -// bundler loads the correct thing. Here, we just need to -// make sure that we require the right one. -export default function getClientStreamClass() { - // The static analyzer of the bundler specifically looks - // for static calls to 'require', so it won't treat the - // below calls as a request to include that module. - // - // That means stream_client_nodejs won't be included on - // the client, as desired. - const modulePath = Meteor.isClient - ? '../client/stream_client_sockjs' - : '../server/stream_client_nodejs'; - - return require(modulePath).default; -} diff --git a/packages/ddp-client/common/livedata_connection.js b/packages/ddp-client/common/livedata_connection.js index 3e34493ec9..aedfa26596 100644 --- a/packages/ddp-client/common/livedata_connection.js +++ b/packages/ddp-client/common/livedata_connection.js @@ -6,7 +6,6 @@ import { Random } from 'meteor/random'; import { Hook } from 'meteor/callback-hook'; import { MongoID } from 'meteor/mongo-id'; import { DDP } from './namespace.js'; -import getClientStreamClass from './getClientStreamClass.js'; import MethodInvoker from './MethodInvoker.js'; import { hasOwn, @@ -83,8 +82,10 @@ export class Connection { if (typeof url === 'object') { self._stream = url; } else { - self._stream = new (getClientStreamClass())(url, { + const { ClientStream } = require("meteor/socket-stream-client"); + self._stream = new ClientStream(url, { retry: options.retry, + ConnectionError: DDP.ConnectionError, headers: options.headers, _sockjsOptions: options._sockjsOptions, // Used to keep some tests quiet, or for other cases in which diff --git a/packages/ddp-client/package.js b/packages/ddp-client/package.js index 5b3b2069b5..7a80a406b2 100644 --- a/packages/ddp-client/package.js +++ b/packages/ddp-client/package.js @@ -5,9 +5,7 @@ Package.describe({ }); Npm.depends({ - 'faye-websocket': '0.11.1', - lolex: '1.4.0', - 'permessage-deflate': '0.1.6' + lolex: '1.4.0' }); Package.onUse((api) => { @@ -22,6 +20,7 @@ Package.onUse((api) => { 'callback-hook', 'ddp-common', 'reload', + 'socket-stream-client', // we depend on _diffObjects, _applyChanges, 'diff-sequence', @@ -73,6 +72,4 @@ Package.onTest((api) => { api.addFiles('test/livedata_tests.js'); api.addFiles('test/livedata_test_service.js'); api.addFiles('test/random_stream_tests.js'); - api.addFiles('test/stream_tests.js', 'client'); - api.addFiles('test/stream_client_tests.js', 'server'); }); diff --git a/packages/ddp-client/server/server.js b/packages/ddp-client/server/server.js index 6ca9ff0513..8566aba9c2 100644 --- a/packages/ddp-client/server/server.js +++ b/packages/ddp-client/server/server.js @@ -1,7 +1 @@ export { DDP } from '../common/namespace.js'; - -if (false) { - // This is used inside livedata_connection, but this is what gets - // it included in the server bundle - import './stream_client_nodejs'; -} diff --git a/packages/socket-stream-client/.npm/package/.gitignore b/packages/socket-stream-client/.npm/package/.gitignore new file mode 100644 index 0000000000..3c3629e647 --- /dev/null +++ b/packages/socket-stream-client/.npm/package/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/socket-stream-client/.npm/package/README b/packages/socket-stream-client/.npm/package/README new file mode 100644 index 0000000000..3d492553a4 --- /dev/null +++ b/packages/socket-stream-client/.npm/package/README @@ -0,0 +1,7 @@ +This directory and the files immediately inside it are automatically generated +when you change this package's NPM dependencies. Commit the files in this +directory (npm-shrinkwrap.json, .gitignore, and this README) to source control +so that others run the same versions of sub-dependencies. + +You should NOT check in the node_modules directory that Meteor automatically +creates; if you are using git, the .gitignore file tells git to ignore it. diff --git a/packages/socket-stream-client/.npm/package/npm-shrinkwrap.json b/packages/socket-stream-client/.npm/package/npm-shrinkwrap.json new file mode 100644 index 0000000000..a1878c0efe --- /dev/null +++ b/packages/socket-stream-client/.npm/package/npm-shrinkwrap.json @@ -0,0 +1,30 @@ +{ + "lockfileVersion": 1, + "dependencies": { + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=" + }, + "http-parser-js": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", + "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=" + }, + "permessage-deflate": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/permessage-deflate/-/permessage-deflate-0.1.6.tgz", + "integrity": "sha1-WB8c7fvUQPrEfQd3vohjM4a5kt4=" + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=" + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + } + } +} diff --git a/packages/socket-stream-client/README.md b/packages/socket-stream-client/README.md new file mode 100644 index 0000000000..3cc33d0f8c --- /dev/null +++ b/packages/socket-stream-client/README.md @@ -0,0 +1,8 @@ +# socket-stream-client +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/socket-stream-client) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/socket-stream-client) +*** + +This package provides the `ClientStream` abstraction used by the +[`ddp-client`](https://github.com/meteor/meteor/tree/devel/packages/ddp-client) +package. + diff --git a/packages/ddp-client/client/stream_client_sockjs.js b/packages/socket-stream-client/browser.js similarity index 89% rename from packages/ddp-client/client/stream_client_sockjs.js rename to packages/socket-stream-client/browser.js index f266bea1dd..6c74fc4352 100644 --- a/packages/ddp-client/client/stream_client_sockjs.js +++ b/packages/socket-stream-client/browser.js @@ -1,22 +1,16 @@ -import { Meteor } from 'meteor/meteor'; -import { DDP } from '../common/namespace.js'; import { toSockjsUrl, toWebsocketUrl, -} from '../common/urlHelpers.js'; -import StreamClientCommon from '../common/stream_client_common.js'; +} from "./urls.js"; -export default class ClientStream extends StreamClientCommon { +import { StreamClientCommon } from "./common.js"; + +export class ClientStream extends StreamClientCommon { // @param url {String} URL to Meteor app // "http://subdomain.meteor.com/" or "/" or // "ddp+sockjs://foo-**.meteor.com/sockjs" constructor(url, options) { - super(); - - this.options = { - retry: true, - ...options - }; + super(options); this._initCommon(this.options); @@ -114,8 +108,8 @@ export default class ClientStream extends StreamClientCommon { } _heartbeat_timeout() { - Meteor._debug('Connection timeout. No sockjs heartbeat received.'); - this._lostConnection(new DDP.ConnectionError('Heartbeat timed out')); + console.log('Connection timeout. No sockjs heartbeat received.'); + this._lostConnection(new this.ConnectionError("Heartbeat timed out")); } _heartbeat_received() { @@ -186,11 +180,11 @@ export default class ClientStream extends StreamClientCommon { this.socket.onclose = () => { this._lostConnection(); }; - this.socket.onerror = () => { + this.socket.onerror = (...args) => { // XXX is this ever called? - Meteor._debug( + console.log( 'stream error', - Array.from(arguments), + args, new Date().toDateString() ); }; @@ -201,7 +195,9 @@ export default class ClientStream extends StreamClientCommon { if (this.connectionTimer) clearTimeout(this.connectionTimer); this.connectionTimer = setTimeout(() => { - this._lostConnection(new DDP.ConnectionError('DDP connection timed out')); + this._lostConnection( + new this.ConnectionError("DDP connection timed out") + ); }, this.CONNECT_TIMEOUT); } } diff --git a/packages/ddp-client/test/stream_tests.js b/packages/socket-stream-client/client-tests.js similarity index 97% rename from packages/ddp-client/test/stream_tests.js rename to packages/socket-stream-client/client-tests.js index 20e67bb6d5..3d207cd007 100644 --- a/packages/ddp-client/test/stream_tests.js +++ b/packages/socket-stream-client/client-tests.js @@ -1,7 +1,5 @@ -import { toSockjsUrl } from '../common/urlHelpers.js'; -import getClientStreamClass from '../common/getClientStreamClass.js'; - -const ClientStream = getClientStreamClass(); +import { toSockjsUrl } from "./urls.js"; +import { ClientStream } from "meteor/socket-stream-client"; Tinytest.add('stream - status', function(test) { // Very basic test. Just see that it runs and returns something. Not a diff --git a/packages/ddp-client/common/stream_client_common.js b/packages/socket-stream-client/common.js similarity index 85% rename from packages/ddp-client/common/stream_client_common.js rename to packages/socket-stream-client/common.js index 8d3ac32f44..8883cacdf4 100644 --- a/packages/ddp-client/common/stream_client_common.js +++ b/packages/socket-stream-client/common.js @@ -1,11 +1,18 @@ -import { Random } from 'meteor/random'; -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; import { Retry } from 'meteor/retry'; -import { DDP } from './namespace.js'; +const forcedReconnectError = new Error("forced reconnect"); + +export class StreamClientCommon { + constructor(options) { + this.options = { + retry: true, + ...(options || null), + }; + + this.ConnectionError = + options && options.ConnectionError || Error; + } -export default class StreamClientCommon { // Register for callbacks. on(name, callback) { if (name !== 'message' && name !== 'reset' && name !== 'disconnect') @@ -43,11 +50,14 @@ export default class StreamClientCommon { retryCount: 0 }; - this.statusListeners = - typeof Tracker !== 'undefined' && new Tracker.Dependency(); + if (Package.tracker) { + this.statusListeners = new Package.tracker.Tracker.Dependency(); + } this.statusChanged = () => { - if (this.statusListeners) this.statusListeners.changed(); + if (this.statusListeners) { + this.statusListeners.changed(); + } }; //// Retry logic @@ -69,9 +79,8 @@ export default class StreamClientCommon { if (this.currentStatus.connected) { if (options._force || options.url) { - // force reconnect. - this._lostConnection(new DDP.ForcedReconnectError()); - } // else, noop. + this._lostConnection(forcedReconnectError); + } return; } @@ -131,10 +140,8 @@ export default class StreamClientCommon { _retryLater(maybeError) { var timeout = 0; - if ( - this.options.retry || - (maybeError && maybeError.errorType === 'DDP.ForcedReconnectError') - ) { + if (this.options.retry || + maybeError === forcedReconnectError) { timeout = this._retry.retryLater( this.currentStatus.retryCount, this._retryNow.bind(this) @@ -164,7 +171,9 @@ export default class StreamClientCommon { // Get current status. Reactive. status() { - if (this.statusListeners) this.statusListeners.depend(); + if (this.statusListeners) { + this.statusListeners.depend(); + } return this.currentStatus; } } diff --git a/packages/ddp-client/server/stream_client_nodejs.js b/packages/socket-stream-client/node.js similarity index 92% rename from packages/ddp-client/server/stream_client_nodejs.js rename to packages/socket-stream-client/node.js index 271789dfea..a66b45b798 100644 --- a/packages/ddp-client/server/stream_client_nodejs.js +++ b/packages/socket-stream-client/node.js @@ -1,7 +1,6 @@ -import { Meteor } from 'meteor/meteor'; -import { DDP } from '../common/namespace.js'; -import { toWebsocketUrl } from '../common/urlHelpers.js'; -import StreamClientCommon from '../common/stream_client_common.js'; +import { Meteor } from "meteor/meteor"; +import { toWebsocketUrl } from "./urls.js"; +import { StreamClientCommon } from "./common.js"; // @param endpoint {String} URL to Meteor app // "http://subdomain.meteor.com/" or "/" or @@ -14,14 +13,9 @@ import StreamClientCommon from '../common/stream_client_common.js'; // We don't do any heartbeating. (The logic that did this in sockjs was removed, // because it used a built-in sockjs mechanism. We could do it with WebSocket // ping frames or with DDP-level messages.) -export default class ClientStream extends StreamClientCommon { +export class ClientStream extends StreamClientCommon { constructor(endpoint, options) { - super(); - - this.options = { - retry: true, - ...(options || null), - }; + super(options); this.client = null; // created in _launchConnection this.endpoint = endpoint; @@ -155,7 +149,7 @@ export default class ClientStream extends StreamClientCommon { this._clearConnectionTimer(); this.connectionTimer = Meteor.setTimeout(() => { - this._lostConnection(new DDP.ConnectionError('DDP connection timed out')); + this._lostConnection(new this.ConnectionError('DDP connection timed out')); }, this.CONNECT_TIMEOUT); this.client.on( @@ -182,7 +176,7 @@ export default class ClientStream extends StreamClientCommon { // Faye's 'error' object is not a JS error (and among other things, // doesn't stringify well). Convert it to one. - this._lostConnection(new DDP.ConnectionError(error.message)); + this._lostConnection(new this.ConnectionError(error.message)); }); clientOnIfCurrent('close', 'stream close callback', () => { diff --git a/packages/socket-stream-client/package.js b/packages/socket-stream-client/package.js new file mode 100644 index 0000000000..543a7950a0 --- /dev/null +++ b/packages/socket-stream-client/package.js @@ -0,0 +1,30 @@ +Package.describe({ + name: "socket-stream-client", + version: "0.1.0", + summary: "Provides the ClientStream abstraction used by ddp-client", + documentation: "README.md" +}); + +Npm.depends({ + "faye-websocket": "0.11.1", + "permessage-deflate": "0.1.6" +}); + +Package.onUse(function(api) { + api.use("ecmascript"); + api.use("retry"); // TODO Try to remove this. + api.mainModule("browser.js", "client", { lazy: true }); + api.mainModule("node.js", "server", { lazy: true }); +}); + +Package.onTest(function(api) { + api.use("underscore"); + api.use("ecmascript"); + api.use("tinytest"); + api.use("test-helpers"); + api.use("tracker"); + api.use("http"); + api.use("socket-stream-client"); + api.mainModule("client-tests.js", "client"); + api.mainModule("server-tests.js", "server"); +}); diff --git a/packages/ddp-client/test/stream_client_tests.js b/packages/socket-stream-client/server-tests.js similarity index 85% rename from packages/ddp-client/test/stream_client_tests.js rename to packages/socket-stream-client/server-tests.js index 23a8fd1ef8..aa1ad157b6 100644 --- a/packages/ddp-client/test/stream_client_tests.js +++ b/packages/socket-stream-client/server-tests.js @@ -1,6 +1,5 @@ -import ClientStream from '../server/stream_client_nodejs.js'; - -var Fiber = Npm.require('fibers'); +import { ClientStream } from "meteor/socket-stream-client"; +import Fiber from "fibers"; testAsyncMulti('stream client - callbacks run in a fiber', [ function(test, expect) { diff --git a/packages/ddp-client/common/urlHelpers.js b/packages/socket-stream-client/urls.js similarity index 93% rename from packages/ddp-client/common/urlHelpers.js rename to packages/socket-stream-client/urls.js index 438101fa59..5f4c04f282 100644 --- a/packages/ddp-client/common/urlHelpers.js +++ b/packages/socket-stream-client/urls.js @@ -1,5 +1,3 @@ -import { Random } from 'meteor/random'; - // @param url {String} URL to Meteor app, eg: // "/" or "madewith.meteor.com" or "https://foo.meteor.com" // or "ddp+sockjs://ddp--****-foo.meteor.com/sockjs" @@ -30,7 +28,7 @@ function translateUrl(url, newSchemeBase, subPath) { // In the host (ONLY!), change '*' characters into random digits. This // allows different stream connections to connect to different hostnames // and avoid browser per-hostname connection limits. - host = host.replace(/\*/g, () => Math.floor(Random.fraction() * 10)); + host = host.replace(/\*/g, () => Math.floor(Math.random() * 10)); return newScheme + '://' + host + rest; } else if (httpUrlMatch) { @@ -65,6 +63,5 @@ export function toSockjsUrl(url) { } export function toWebsocketUrl(url) { - var ret = translateUrl(url, 'ws', 'websocket'); - return ret; + return translateUrl(url, 'ws', 'websocket'); }