Skip to content

Commit 65d2263

Browse files
committed
fix(core): implement two-level Map for O(1) protocol+subprotocol lookup
Signed-off-by: Thembo Jonathan <thembojonathan@gmail.com>
1 parent 8fbc11d commit 65d2263

22 files changed

Lines changed: 688 additions & 65 deletions

packages/binding-coap/src/coap-client-factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export default class CoapClientFactory implements ProtocolClientFactory {
3232
this.server = server;
3333
}
3434

35+
public getSupportedProtocols(): Array<[string, string?]> {
36+
return [["coap"]];
37+
}
38+
3539
public getClient(): ProtocolClient {
3640
debug(`CoapClientFactory creating client for '${this.scheme}'`);
3741
return new CoapClient(this.server);

packages/binding-coap/src/coaps-client-factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ const { debug } = createLoggers("binding-coap", "coaps-client-factory");
2525
export default class CoapsClientFactory implements ProtocolClientFactory {
2626
public readonly scheme: string = "coaps";
2727

28+
public getSupportedProtocols(): Array<[string, string?]> {
29+
return [["coaps"]];
30+
}
31+
2832
public getClient(): ProtocolClient {
2933
debug(`CoapsClientFactory creating client for '${this.scheme}'`);
3034
return new CoapsClient();

packages/binding-file/src/file-client-factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ const { debug } = createLoggers("binding-file", "file-client-factory");
2424
export default class FileClientFactory implements ProtocolClientFactory {
2525
public readonly scheme: string = "file";
2626

27+
public getSupportedProtocols(): Array<[string, string?]> {
28+
return [["file"]];
29+
}
30+
2731
public getClient(): ProtocolClient {
2832
debug(`FileClientFactory creating client for '${this.scheme}'`);
2933
return new FileClient();

packages/binding-http/src/http-client-factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export default class HttpClientFactory implements ProtocolClientFactory {
3333
this.config = config;
3434
}
3535

36+
public getSupportedProtocols(): Array<[string, string?]> {
37+
return [["http"]];
38+
}
39+
3640
public getClient(): ProtocolClient {
3741
// HTTP over HTTPS proxy requires HttpsClient
3842
if (this.config && this.config.proxy && this.config.proxy.href && this.config.proxy.href.startsWith("https:")) {

packages/binding-http/src/https-client-factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export default class HttpsClientFactory implements ProtocolClientFactory {
3131
this.config = config;
3232
}
3333

34+
public getSupportedProtocols(): Array<[string, string?]> {
35+
return [["https"]];
36+
}
37+
3438
public getClient(): ProtocolClient {
3539
// HTTPS over HTTP proxy requires HttpClient
3640
if (this.config && this.config.proxy && this.config.proxy.href && this.config.proxy.href.startsWith("http:")) {

packages/binding-mbus/src/mbus-client-factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const info = createInfoLogger("binding-mbus", "mbus-client-factory");
2121
export default class MBusClientFactory implements ProtocolClientFactory {
2222
public readonly scheme: string = "mbus+tcp";
2323

24+
public getSupportedProtocols(): Array<[string, string?]> {
25+
return [["mbus+tcp"]];
26+
}
27+
2428
public getClient(): ProtocolClient {
2529
info(`MBusClientFactory creating client for '${this.scheme}'`);
2630
return new MBusClient();

packages/binding-modbus/src/modbus-client-factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export default class ModbusClientFactory implements ProtocolClientFactory {
2222
public readonly scheme: string = "modbus+tcp";
2323
private singleton?: ModbusClient;
2424

25+
public getSupportedProtocols(): Array<[string, string?]> {
26+
return [["modbus+tcp"]];
27+
}
28+
2529
public getClient(): ProtocolClient {
2630
debug(`Get client for '${this.scheme}'`);
2731
this.init();

packages/binding-mqtt/src/mqtt-client-factory.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919

2020
import { ProtocolClientFactory, ProtocolClient, createDebugLogger } from "@node-wot/core";
21+
import { MqttClientConfig } from "./mqtt";
2122
import MqttClient from "./mqtt-client";
2223

2324
const debug = createDebugLogger("binding-mqtt", "mqtt-client-factory");
@@ -26,8 +27,19 @@ export default class MqttClientFactory implements ProtocolClientFactory {
2627
public readonly scheme: string = "mqtt";
2728
private readonly clients: Array<ProtocolClient> = [];
2829

30+
constructor(private readonly config: MqttClientConfig = {}) {}
31+
32+
getSupportedProtocols(): Array<[string, string?]> {
33+
return [
34+
["mqtt"], // mqtt://
35+
["mqtts"], // mqtts://
36+
["ws", "mqtt"], // ws:// + subprotocol:mqtt
37+
["wss", "mqtt"], // wss:// + subprotocol:mqtt
38+
];
39+
}
40+
2941
getClient(): ProtocolClient {
30-
const client = new MqttClient();
42+
const client = new MqttClient(this.config);
3143
this.clients.push(client);
3244
return client;
3345
}

packages/binding-mqtt/src/mqtt-client.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ export default class MqttClient implements ProtocolClient {
5454

5555
private client?: mqtt.MqttClient;
5656

57+
private getBrokerUri(href: string): string {
58+
const requestUri = new URL(href);
59+
60+
if (href.startsWith("ws://") || href.startsWith("wss://")) {
61+
return `${requestUri.protocol}//${requestUri.host}`;
62+
}
63+
64+
const compositeMatch = href.match(/^([a-z]+)\+([a-z]+):\/\//i);
65+
if (compositeMatch) {
66+
const transportScheme = compositeMatch[2];
67+
return `${transportScheme}://${requestUri.host}`;
68+
}
69+
70+
return `${this.scheme}://${requestUri.host}`;
71+
}
72+
5773
public async subscribeResource(
5874
form: MqttForm,
5975
next: (value: Content) => void,
@@ -62,7 +78,7 @@ export default class MqttClient implements ProtocolClient {
6278
): Promise<Subscription> {
6379
const contentType = form.contentType ?? ContentSerdes.DEFAULT;
6480
const requestUri = new url.URL(form.href);
65-
const brokerUri: string = `${this.scheme}://` + requestUri.host;
81+
const brokerUri: string = this.getBrokerUri(form.href);
6682
// Keeping the path as the topic for compatibility reasons.
6783
// Current specification allows only form["mqv:filter"]
6884
const filter = requestUri.pathname.slice(1) ?? form["mqv:filter"];
@@ -92,7 +108,7 @@ export default class MqttClient implements ProtocolClient {
92108
public async readResource(form: MqttForm): Promise<Content> {
93109
const contentType = form.contentType ?? ContentSerdes.DEFAULT;
94110
const requestUri = new url.URL(form.href);
95-
const brokerUri: string = `${this.scheme}://` + requestUri.host;
111+
const brokerUri: string = this.getBrokerUri(form.href);
96112
// Keeping the path as the topic for compatibility reasons.
97113
// Current specification allows only form["mqv:filter"]
98114
const filter = requestUri.pathname.slice(1) ?? form["mqv:filter"];
@@ -124,7 +140,7 @@ export default class MqttClient implements ProtocolClient {
124140

125141
public async writeResource(form: MqttForm, content: Content): Promise<void> {
126142
const requestUri = new url.URL(form.href);
127-
const brokerUri = `${this.scheme}://${requestUri.host}`;
143+
const brokerUri = this.getBrokerUri(form.href);
128144
const topic = requestUri.pathname.slice(1) ?? form["mqv:topic"];
129145

130146
let pool = this.pools.get(brokerUri);
@@ -147,7 +163,7 @@ export default class MqttClient implements ProtocolClient {
147163
public async invokeResource(form: MqttForm, content: Content): Promise<Content> {
148164
const requestUri = new url.URL(form.href);
149165
const topic = requestUri.pathname.slice(1);
150-
const brokerUri = `${this.scheme}://${requestUri.host}`;
166+
const brokerUri = this.getBrokerUri(form.href);
151167

152168
let pool = this.pools.get(brokerUri);
153169

@@ -170,7 +186,7 @@ export default class MqttClient implements ProtocolClient {
170186

171187
public async unlinkResource(form: Form): Promise<void> {
172188
const requestUri = new url.URL(form.href);
173-
const brokerUri: string = `${this.scheme}://` + requestUri.host;
189+
const brokerUri: string = this.getBrokerUri(form.href);
174190
const topic = requestUri.pathname.slice(1);
175191

176192
const pool = this.pools.get(brokerUri);

packages/binding-mqtt/src/mqtts-client-factory.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,15 @@
1717
* Protocol test suite to test protocol implementations
1818
*/
1919

20-
import { ProtocolClientFactory, ProtocolClient, createDebugLogger } from "@node-wot/core";
2120
import { MqttClientConfig } from "./mqtt";
22-
import MqttClient from "./mqtt-client";
21+
import MqttClientFactory from "./mqtt-client-factory";
2322

24-
const debug = createDebugLogger("binding-mqtt", "mqtts-client-factory");
25-
26-
export default class MqttsClientFactory implements ProtocolClientFactory {
27-
public readonly scheme: string = "mqtts";
28-
private readonly clients: Array<ProtocolClient> = [];
29-
30-
constructor(private readonly config: MqttClientConfig) {}
31-
getClient(): ProtocolClient {
32-
const client = new MqttClient(this.config, true);
33-
this.clients.push(client);
34-
return client;
35-
}
36-
37-
init(): boolean {
38-
return true;
39-
}
40-
41-
destroy(): boolean {
42-
debug(`MqttClientFactory stopping all clients for '${this.scheme}'`);
43-
this.clients.forEach((client) => client.stop());
44-
return true;
23+
/**
24+
* @deprecated Use MqttClientFactory instead. MqttClientFactory now handles both secure and non-secure MQTT protocols.
25+
* This class is kept for backward compatibility and simply wraps MqttClientFactory.
26+
*/
27+
export default class MqttsClientFactory extends MqttClientFactory {
28+
constructor(config: MqttClientConfig) {
29+
super(config);
4530
}
4631
}

0 commit comments

Comments
 (0)