Skip to content

Commit

Permalink
client: Add support for unejected React Native apps
Browse files Browse the repository at this point in the history
  • Loading branch information
sonnyp committed Oct 20, 2018
1 parent 91bb2f4 commit 5b3c7fb
Show file tree
Hide file tree
Showing 16 changed files with 176 additions and 38 deletions.
47 changes: 44 additions & 3 deletions packages/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,33 @@

An XMPP client is an entity that connects to an XMPP server.

`@xmpp/client` package includes a minimal set of features to connect /authenticate securely and reliably.
`@xmpp/client` package includes a minimal set of features to connect and authenticate securely and reliably.

It supports Node.js, browser and React Native. See [Connection Method](#connection-methods) for differences.

## Install

`npm install @xmpp/client` or `yarn add @xmpp/client`

## Example
## Setup

```js
const {client, xml, jid} = require('@xmpp/client')
```

or

```html
<script src="https://unpkg.com/@xmpp/client/dist/xmpp.min.js" crossorigin></script>
```

```js
const {client, xml, jid} = window.XMPP
```

## Example

```js
const xmpp = client({
service: 'ws://localhost:5280/xmpp-websocket',
domain: 'localhost',
Expand Down Expand Up @@ -63,7 +79,7 @@ See [jid package](/packages/jid)

- `service` `<string>` The service to connect to, accepts an URI or a domain.
- `domain` lookup and connect to the most secure endpoint using [@xmpp/resolve](/packages/resolve)
- `xmpp://hostname:port` plain TCP, can be upgraded to TLS using [@xmpp/starttls](/packages/starttls)
- `xmpp://hostname:port` plain TCP, may be upgraded to TLS by [@xmpp/starttls](/packages/starttls)
- `xmpps://hostname:port` direct TLS
- `ws://hostname:port/path` plain WebSocket
- `wss://hostname:port/path` secure WebSocket
Expand Down Expand Up @@ -163,3 +179,28 @@ xmpp.send(xml('presence'))
### xmpp.reconnect

See [@xmpp/reconnect](/packages/reconnect).

## Connection methods

XMPP supports multiple transports, this table list `@xmpp/client` supported and unsupported transport for each environment.

| transport | protocols | Node.js | Browser | React Native |
| :------------------------------: | :-----------: | :-----: | :-----: | :----------: |
| [WebSocket](/packages/websocket) | ws://, wss:// ||||
| [TCP](/packages/tcp) | xmpp:// ||||
| [TLS](/packages/tls) | xmpps:// ||||

## Authentication methods

Multiple authentication mechanisms are supported.
PLAIN should only be used over secure WebSocket (`wss://)`, direct TLS (`xmpps:`) or a TCP (`xmpp:`) connection upgraded to TLS via [STARTTLS](/starttls)

| SASL | Node.js | Browser | React Native |
| :---------------------------------------: | :-----: | :-----: | :----------: |
| [ANONYMOUS](/packages/sasl-anonymous) ||||
| [PLAIN](/packages/sasl-plain) ||||
| [SCRAM-SHA-1](/packages/sasl-scram-sha-1) ||||

- ☐ : Optional
- ✗ : Unavailable
- ✔ : Included
67 changes: 67 additions & 0 deletions packages/client/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict'

const {xml, jid, Client} = require('@xmpp/client-core')
const getDomain = require('./lib/getDomain')

const _reconnect = require('@xmpp/reconnect')
const _websocket = require('@xmpp/websocket')
const _middleware = require('@xmpp/middleware')
const _streamFeatures = require('@xmpp/stream-features')
const _iqCaller = require('@xmpp/iq/caller')
const _iqCallee = require('@xmpp/iq/callee')
const _resolve = require('@xmpp/resolve')

// Stream features - order matters and define priority
const _sasl = require('@xmpp/sasl')
const _resourceBinding = require('@xmpp/resource-binding')
const _sessionEstablishment = require('@xmpp/session-establishment')

// SASL mechanisms - order matters and define priority
const anonymous = require('@xmpp/sasl-anonymous')
const plain = require('@xmpp/sasl-plain')

function client(options = {}) {
const {resource, credentials, username, password, ...params} = options

const {domain, service} = params
if (!domain && service) {
params.domain = getDomain(service)
}

const entity = new Client(params)

const reconnect = _reconnect({entity})
const websocket = _websocket({entity})

const middleware = _middleware({entity})
const streamFeatures = _streamFeatures({middleware})
const iqCaller = _iqCaller({middleware, entity})
const iqCallee = _iqCallee({middleware, entity})
const resolve = _resolve({entity})
// Stream features - order matters and define priority
const sasl = _sasl({streamFeatures}, credentials || {username, password})
const resourceBinding = _resourceBinding({iqCaller, streamFeatures}, resource)
const sessionEstablishment = _sessionEstablishment({iqCaller, streamFeatures})
// SASL mechanisms - order matters and define priority
const mechanisms = Object.entries({anonymous, plain})
.map(([k, v]) => ({[k]: v(sasl)}))

return Object.assign(entity, {
entity,
reconnect,
websocket,
middleware,
streamFeatures,
iqCaller,
iqCallee,
resolve,
sasl,
resourceBinding,
sessionEstablishment,
mechanisms,
})
}

module.exports.xml = xml
module.exports.jid = jid
module.exports.client = client
24 changes: 4 additions & 20 deletions packages/client/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const {xml, jid, Client} = require('@xmpp/client-core')
const getDomain = require('./lib/getDomain')

const _reconnect = require('@xmpp/reconnect')
const _websocket = require('@xmpp/websocket')
Expand All @@ -23,19 +24,6 @@ const anonymous = require('@xmpp/sasl-anonymous')
const scramsha1 = require('@xmpp/sasl-scram-sha-1')
const plain = require('@xmpp/sasl-plain')

const URL = global.URL || require('url').URL // eslint-disable-line node/no-unsupported-features/node-builtins

function getDomain(service) {
// WHATWG URL parser requires a protocol
if (!service.includes('://')) {
service = 'http://' + service
}
const url = new URL(service)
// WHATWG URL parser doesn't support non Web protocols in browser
url.protocol = 'http:'
return url.hostname
}

function client(options = {}) {
const {resource, credentials, username, password, ...params} = options

Expand All @@ -48,24 +36,21 @@ function client(options = {}) {

const reconnect = _reconnect({entity})
const websocket = _websocket({entity})
const tcp = typeof _tcp === 'function' ? _tcp({entity}) : undefined
const tls = typeof _tls === 'function' ? _tls({entity}) : undefined
const tcp = _tcp({entity})
const tls = _tls({entity})

const middleware = _middleware({entity})
const streamFeatures = _streamFeatures({middleware})
const iqCaller = _iqCaller({middleware, entity})
const iqCallee = _iqCallee({middleware, entity})
const resolve = _resolve({entity})
// Stream features - order matters and define priority
const starttls =
typeof _starttls === 'function' ? _starttls({streamFeatures}) : undefined
const starttls = _starttls({streamFeatures})
const sasl = _sasl({streamFeatures}, credentials || {username, password})
const resourceBinding = _resourceBinding({iqCaller, streamFeatures}, resource)
const sessionEstablishment = _sessionEstablishment({iqCaller, streamFeatures})
// SASL mechanisms - order matters and define priority
const mechanisms = Object.entries({anonymous, scramsha1, plain})
// Ignore browserify stubs
.filter(([, v]) => typeof v === 'function')
.map(([k, v]) => ({[k]: v(sasl)}))

return Object.assign(entity, {
Expand All @@ -90,4 +75,3 @@ function client(options = {}) {
module.exports.xml = xml
module.exports.jid = jid
module.exports.client = client
module.exports.getDomain = getDomain
6 changes: 6 additions & 0 deletions packages/client/lib/getDomain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict'

module.exports = function getDomain(service) {
const domain = service.split('://')[1] || service
return domain.split(':')[0].split('/')[0]
}
9 changes: 2 additions & 7 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,8 @@
"@xmpp/tls": "^0.5.0",
"@xmpp/websocket": "^0.5.0"
},
"browser": {
"url": false,
"@xmpp/tcp": false,
"@xmpp/tls": false,
"@xmpp/starttls": false,
"@xmpp/sasl-scram-sha-1": false
},
"browser": "./browser.js",
"react-native": "./browser.js",
"engines": {
"node": ">= 10.0.0",
"yarn": ">= 1.0.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/client/test/getDomain.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const test = require('ava')
const {getDomain} = require('..')
const getDomain = require('../lib/getDomain')

test('getDomain', t => {
t.is(getDomain('ws://foo:123/foobar'), 'foo')
Expand Down
3 changes: 3 additions & 0 deletions packages/events/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"events": "^3.0.0"
}
}
2 changes: 1 addition & 1 deletion packages/reconnect/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const EventEmitter = require('events')
const {EventEmitter} = require('@xmpp/events')

class Reconnect extends EventEmitter {
constructor(entity) {
Expand Down
3 changes: 3 additions & 0 deletions packages/reconnect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"XMPP",
"reconnect"
],
"dependencies": {
"@xmpp/events": "^0.5.0"
},
"engines": {
"node": ">= 10.0.0",
"yarn": ">= 1.0.0"
Expand Down
20 changes: 16 additions & 4 deletions packages/sasl/lib/b64.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
'use strict'

const {Base64} = require('js-base64')

module.exports.encode = function encode(string) {
if (!global.Buffer) {
if (global.btoa) {
return global.btoa(string)
}
return Buffer.from(string, 'utf8').toString('base64')

if (global.Buffer) {
return Buffer.from(string, 'utf8').toString('base64')
}

return Base64.btoa(string)
}

module.exports.decode = function decode(string) {
if (!global.Buffer) {
if (global.atob) {
return global.atob(string)
}
return Buffer.from(string, 'base64').toString('utf8')

if (global.Buffer) {
return Buffer.from(string, 'base64').toString('utf8')
}

return Base64.btoa(string)
}
4 changes: 3 additions & 1 deletion packages/sasl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
"dependencies": {
"@xmpp/error": "^0.5.0",
"@xmpp/xml": "^0.5.0",
"js-base64": "^2.4.9",
"saslmechanisms": "^0.1.1"
},
"browser": {
"buffer": false
"buffer": false,
"js-base64": false
},
"engines": {
"node": ">= 10.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/starttls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ STARTTLS negotiation for `@xmpp/client`.

Included and enabled in `@xmpp/client` for Node.js

STARTTLS will automatically be negotiated upon TCP connection.
STARTTLS will automatically upgrade the TCP connection to TLS upon connecton if the server supports it.

## References

Expand Down
5 changes: 5 additions & 0 deletions packages/tcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# TCP

TCP transport for `@xmpp/client`.

Included and enabled in `@xmpp/client` for Node.js.
5 changes: 5 additions & 0 deletions packages/tls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# TLS

TLS transport for `@xmpp/client`.

Included and enabled in `@xmpp/client` for Node.js.
5 changes: 5 additions & 0 deletions packages/websocket/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# WebSocket

WebSocket transport for `@xmpp/client`.

Included and enabled in `@xmpp/client`.
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3675,6 +3675,11 @@ events@^2.0.0:
resolved "https://registry.yarnpkg.com/events/-/events-2.1.0.tgz#2a9a1e18e6106e0e812aa9ebd4a819b3c29c0ba5"
integrity sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==

events@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==

evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
Expand Down Expand Up @@ -5006,6 +5011,11 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=

js-base64@^2.4.9:
version "2.4.9"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03"
integrity sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==

js-levenshtein@^1.1.3:
version "1.1.4"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.4.tgz#3a56e3cbf589ca0081eb22cd9ba0b1290a16d26e"
Expand Down

0 comments on commit 5b3c7fb

Please sign in to comment.