Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add forceNativeWebSocket client option #1910

Merged
merged 7 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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