Skip to content

Commit

Permalink
feat: add forceNativeWebSocket client option (#1910)
Browse files Browse the repository at this point in the history
* Revert "fix(browser): add `navigator` polifilly for wechat mini (#1796)"

This reverts commit c26908a.
since the polyfill hardcode navigator, it is impossible to determine the userAgent at runtime

* doc(README): update WeChat instructions

* feat: add forceNativeWebSocket client option

* Revert "feat: add compatibility with txiki.js (#1895)"

This reverts commit 37b08c9.
Not special support for txiki.js is required thanks to forceNativeWebSocket client option

* style: unify import name IS_BROWSER -> isBrowser

* fixup! feat: add forceNativeWebSocket client option

style: fix lint

* fixup! fixup! feat: add forceNativeWebSocket client option

chore: remove test_store folder pushed
refactor: load protocols only once
refactor: use forceNativeWebSocket only for ws choice
doc(README): typo + update forceNativeWebSocket behaviour description
  • Loading branch information
EmixamPP authored Jul 26, 2024
1 parent 6b278dc commit 103d172
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 45 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -467,13 +467,13 @@ The arguments are:
- `log`: custom log function. Default uses [debug](https://www.npmjs.com/package/debug) package.
- `manualConnect`: prevents the constructor to call `connect`. In this case after the `mqtt.connect` is called you should call `client.connect` manually.
- `timerVariant`: defaults to `auto`, which tries to determine which timer is most appropriate for you environment, if you're having detection issues, you can set it to `worker` or `native`. If none suits you, you can pass a timer object with set and clear properties:
```js
timerVariant: {
set: (func, timer) => setInterval(func, timer),
clear: (id) => clearInterval(id)
}
```

```js
timerVariant: {
set: (func, timer) => setInterval(func, timer),
clear: (id) => clearInterval(id)
}
```
- `forceNativeWebSocket`: set to true if you're having detection issues (i.e. the `ws does not work in the browser` exception) to force the use of native WebSocket. It is important to note that if set to true for the first client created, then all the clients will use native WebSocket. And conversely, if not set or set to false, all will use the detection result.
- `unixSocket`: if you want to connect to a unix socket, set this to true
In case mqtts (mqtt over tls) is required, the `options` object is passed through to [`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback). If using a **self-signed certificate**, set `rejectUnauthorized: false`. However, be cautious as this exposes you to potential man in the middle attacks and isn't recommended for production.
Expand Down Expand Up @@ -905,6 +905,7 @@ Supports [WeChat Mini Program](https://mp.weixin.qq.com/). Use the `wxs` protoco
```js
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only' // import before mqtt.
import 'esbuild-plugin-polyfill-node/polyfills/navigator'
const mqtt = require("mqtt");
const client = mqtt.connect("wxs://test.mosquitto.org", {
timerVariant: 'native' // more info ref issue: #1797
Expand Down
10 changes: 1 addition & 9 deletions esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,7 @@ const options = {
polyfillNode({
polyfills: [
'readable-stream'
],
globals: {
global: false,
__dirname: false,
__filename: false,
buffer: true,
process: true,
navigator: true, // Needed for WeChat, ref #1789
}
]
}),
{
name: 'resolve-package-json',
Expand Down
4 changes: 4 additions & 0 deletions src/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ export interface IClientOptions extends ISecureClientOptions {
* or pass a custom timer object
*/
timerVariant?: TimerVariant | Timer
/**
* false, set to true to force the use of native WebSocket if you're having issues with the detection
*/
forceNativeWebSocket?: boolean
}

export interface IClientPublishOptions {
Expand Down
45 changes: 26 additions & 19 deletions src/lib/connect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import MqttClient, {
MqttClientEventCallbacks,
MqttProtocol,
} from '../client'
import IS_BROWSER from '../is-browser'
import isBrowser from '../is-browser'
import { StreamBuilder } from '../shared'

// Handling the process.nextTick is not a function error in react-native applications.
Expand All @@ -16,24 +16,7 @@ if (typeof process?.nextTick !== 'function') {

const debug = _debug('mqttjs')

const protocols: Record<string, StreamBuilder> = {}

if (!IS_BROWSER) {
protocols.mqtt = require('./tcp').default
protocols.tcp = require('./tcp').default
protocols.ssl = require('./tls').default
protocols.tls = protocols.ssl
protocols.mqtts = require('./tls').default
} else {
protocols.wx = require('./wx').default
protocols.wxs = require('./wx').default

protocols.ali = require('./ali').default
protocols.alis = require('./ali').default
}

protocols.ws = require('./ws').default
protocols.wss = require('./ws').default
let protocols: Record<string, StreamBuilder> = null

/**
* Parse the auth attribute and merge username and password in the options object.
Expand Down Expand Up @@ -152,6 +135,30 @@ function connect(
}
}

// only loads the protocols once
if (!protocols) {
protocols = {}
if (!isBrowser && !opts.forceNativeWebSocket) {
protocols.ws = require('./ws').streamBuilder
protocols.wss = require('./ws').streamBuilder

protocols.mqtt = require('./tcp').default
protocols.tcp = require('./tcp').default
protocols.ssl = require('./tls').default
protocols.tls = protocols.ssl
protocols.mqtts = require('./tls').default
} else {
protocols.ws = require('./ws').browserStreamBuilder
protocols.wss = require('./ws').browserStreamBuilder

protocols.wx = require('./wx').default
protocols.wxs = require('./wx').default

protocols.ali = require('./ali').default
protocols.alis = require('./ali').default
}
}

if (!protocols[opts.protocol]) {
const isSecure = ['mqtts', 'wss'].indexOf(opts.protocol) !== -1
// returns the first available protocol based on available protocols (that depends on environment)
Expand Down
6 changes: 3 additions & 3 deletions src/lib/connect/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Buffer } from 'buffer'
import Ws, { ClientOptions } from 'ws'
import _debug from 'debug'
import { DuplexOptions, Transform } from 'readable-stream'
import IS_BROWSER from '../is-browser'
import isBrowser from '../is-browser'
import MqttClient, { IClientOptions } from '../client'
import { BufferedDuplex, writev } from '../BufferedDuplex'

Expand Down Expand Up @@ -44,7 +44,7 @@ function setDefaultOpts(opts: IClientOptions) {
if (!opts.wsOptions) {
options.wsOptions = {}
}
if (!IS_BROWSER && opts.protocol === 'wss') {
if (!isBrowser && !opts.forceNativeWebSocket && opts.protocol === 'wss') {
// Add cert/key/ca etc options
WSS_OPTIONS.forEach((prop) => {
if (
Expand Down Expand Up @@ -298,4 +298,4 @@ const browserStreamBuilder: StreamBuilder = (client, opts) => {
return stream
}

export default IS_BROWSER ? browserStreamBuilder : streamBuilder
export { browserStreamBuilder, streamBuilder }
8 changes: 1 addition & 7 deletions src/lib/is-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ const isStandardBrowserEnv = () => {
return false
}

const isTxikijsEnv = () =>
typeof navigator !== 'undefined' && navigator.userAgent === 'txiki.js'

const isWebWorkerEnv = () =>
Boolean(
// eslint-disable-next-line no-restricted-globals
Expand All @@ -40,10 +37,7 @@ const isReactNativeEnv = () =>
typeof navigator !== 'undefined' && navigator.product === 'ReactNative'

const isBrowser =
isStandardBrowserEnv() ||
isWebWorkerEnv() ||
isReactNativeEnv() ||
isTxikijsEnv()
isStandardBrowserEnv() || isWebWorkerEnv() || isReactNativeEnv()

export const isWebWorker = isWebWorkerEnv()

Expand Down

0 comments on commit 103d172

Please sign in to comment.