Skip to content

Commit

Permalink
expand polyfill of network providers, #28
Browse files Browse the repository at this point in the history
  • Loading branch information
or-else committed Jan 19, 2020
1 parent 6763f43 commit 18ac7a7
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 39 deletions.
15 changes: 11 additions & 4 deletions README.md
Expand Up @@ -7,13 +7,13 @@ Regularly released NPM packages are at https://www.npmjs.com/package/tinode-sdk
You may include the latest standalone minified SDK into your html file as
```html
<script crossorigin="anonymous"
src="https://unpkg.com/tinode-sdk/umd/tinode.prod.js">
src="https://cdn.jsdelivr.net/npm/tinode-sdk/umd/tinode.prod.js">
</script>
```
or while developing as
```html
<script crossorigin="anonymous"
src="https://unpkg.com/tinode-sdk/umd/tinode.dev.js">
src="https://cdn.jsdelivr.net/npm/tinode-sdk/umd/tinode.dev.js">
</script>
```

Expand All @@ -25,8 +25,15 @@ or while developing as

## Node JS compatibility

To use tinode-sdk as a Node JS dependency, you have to provide a WebSocket provider:
To use tinode-sdk as a Node JS dependency, you have to polyfill network providers, for example with https://www.npmjs.com/package/ws and https://www.npmjs.com/package/xmlhttprequest (or https://www.npmjs.com/package/xhr).
```js
Tinode.setWebSocketProvider(require('ws'));
Tinode.setNetworkProviders(require('ws'), require('xmlhttprequest'));
this.tinode = new Tinode(...);
```
or (before instantiating Tinode):
```js
window.WebSocket = require('ws');
window.XMLHttpRequest = require('xmlhttprequest');
```

Keep in mind that the SDK also references `URL.createObjectURL()` which is not currently polyfilled. It will throw an exception when the user attempts to download a file attachment.
84 changes: 49 additions & 35 deletions src/tinode.js
Expand Up @@ -56,8 +56,12 @@ let WebSocketProvider;
if (typeof WebSocket != 'undefined') {
WebSocketProvider = WebSocket;
}
initForNonBrowserApp();

let XHRProvider;
if (typeof XMLHttpRequest != 'undefined') {
XHRProvider = XMLHttpRequest;
}
initForNonBrowserApp();

// Global constants
const PROTOCOL_VERSION = '0';
Expand Down Expand Up @@ -146,9 +150,10 @@ function initForNonBrowserApp() {
if (typeof window == 'undefined') {
global.window = {
WebSocket: WebSocketProvider,
XMLHttpRequest: XHRProvider,
URL: {
createObjectURL: function() {
throw new Error("Unable to use window.URL in a non browser application");
throw new Error("Unable to use URL.createObjectURL in a non-browser application");
}
}
}
Expand Down Expand Up @@ -237,25 +242,6 @@ function mergeToCache(cache, key, newval, ignore) {
return cache[key];
}

// Basic cross-domain requester. Supports normal browsers and IE8+
function xdreq() {
let xdreq = null;

// Detect browser support for CORS
if ('withCredentials' in new XMLHttpRequest()) {
// Support for standard cross-domain requests
xdreq = new XMLHttpRequest();
} else if (typeof XDomainRequest != 'undefined') {
// IE-specific "CORS" with XDR
xdreq = new XDomainRequest();
} else {
// Browser without CORS support, don't know how to handle
throw new Error("Browser not supported");
}

return xdreq;
};

// JSON stringify helper - pre-processor for JSON.stringify
function jsonBuildHelper(key, val) {
if (val instanceof Date) {
Expand Down Expand Up @@ -869,7 +855,7 @@ var Connection = function(host_, apiKey_, transport_, secure_, autoreconnect_) {
let _sender = null;

function lp_sender(url_) {
let sender = xdreq();
let sender = XMLHttpRequest();
sender.onreadystatechange = function(evt) {
if (sender.readyState == XDR_DONE && sender.status >= 400) {
// Some sort of error response
Expand All @@ -882,7 +868,7 @@ var Connection = function(host_, apiKey_, transport_, secure_, autoreconnect_) {
}

function lp_poller(url_, resolve, reject) {
let poller = xdreq();
let poller = XMLHttpRequest();
let promiseCompleted = false;

poller.onreadystatechange = function(evt) {
Expand Down Expand Up @@ -1013,21 +999,47 @@ var Connection = function(host_, apiKey_, transport_, secure_, autoreconnect_) {
}
}

let initialized = false;
if (transport_ === 'lp') {
// explicit request to use long polling
init_lp(this);
} else if (transport_ === 'ws') {
// explicit request to use web socket
// if websockets are not available, horrible things will happen
init_ws(this);
} else {

// Default transport selection
if (typeof window != 'object' || !window['WebSocket']) {
// The browser has no websockets
init_lp(this);
} else {
} else if (typeof window == 'object') {
if (window['WebSocket']) {
// Using web sockets -- default.
init_ws(this);
} else if (window['XMLHttpRequest']) {
// The browser has no websockets, using long polling.
init_lp(this);
}
}

if (!initialized) {
// No transport is avaiilable.
console.log("No network transport is available. Running Node? Call 'Tinode.setNetworkProviders()'.");
throw new Error("No network transport is available. Running Node? Call 'Tinode.setNetworkProviders()'.");
}

/**
* Check if the given network transport is available.
* @memberof Tinode.Connection#
* @param {String} trans - either 'ws' (websocket) or 'lp' (long polling).
* @returns true if given transport is available, false otherwise.
*/
this.transportAvailable = function(transp) {
switch (transp) {
case 'ws':
return typeof window == 'object' && window['WebSocket'];
case 'lp':
return typeof window == 'object' && window['XMLHttpRequest'];
default:
console.log("Request for unknown transport", transp);
return false;
}
}

Expand Down Expand Up @@ -1691,14 +1703,15 @@ Tinode.getVersion = function() {
};

/**
* To use for non browser app, allow to specify WebSocket provider
* @param provider webSocket provider ex: for nodeJS require('ws')
* @memberof Tinode
* To use Tinode in a non browser context, supply WebSocket and XMLHttpRequest providers.
* @static
*
* @memberof Tinode
* @param wsProvider WebSocket provider, e.g. for nodeJS require('ws').
* @param xhrProvider XMLHttpRequest provider, e.g. for node require('xhr').
*/
Tinode.setWebSocketProvider = function(provider) {
WebSocketProvider = provider;
Tinode.setNetworkProviders = function(wsProvider, xhrProvider) {
WebSocketProvider = wsprovider;
XHRProvider = xhrprovider;
};

/**
Expand Down Expand Up @@ -5364,7 +5377,7 @@ var LargeFileHelper = function(tinode) {
this._apiKey = tinode._apiKey;
this._authToken = tinode.getAuthToken();
this._msgId = tinode.getNextUniqueId();
this.xhr = xdreq();
this.xhr = XMLHttpRequest();

// Promise
this.toResolve = null;
Expand Down Expand Up @@ -5556,6 +5569,7 @@ LargeFileHelper.prototype = {
this.xhr.onload = function() {
if (this.status == 200) {
const link = document.createElement('a');
// URL.createObjectURL is not available in non-browser environment. This call will fail.
link.href = window.URL.createObjectURL(new Blob([this.response], {
type: mimetype
}));
Expand Down

0 comments on commit 18ac7a7

Please sign in to comment.