Skip to content

Commit 2c6eb79

Browse files
committed
fix: return 409 error when subdomain is in use instead of silent fallback
Previously, requesting an already-in-use subdomain would silently fall back to a random subdomain. Now returns a clear 409 Conflict error with a helpful message telling users to wait or use a different subdomain.
1 parent 2d34a8c commit 2c6eb79

File tree

4 files changed

+27
-10
lines changed

4 files changed

+27
-10
lines changed

lib/ClientManager.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ class ClientManager {
3232
const clients = this.clients;
3333
const stats = this.stats;
3434

35-
// can't ask for id already is use
35+
// can't ask for id already in use
3636
if (clients[id]) {
37-
id = hri.random();
37+
const err = new Error(`Subdomain '${id}' is already in use. Try again in a few seconds or use a different subdomain.`);
38+
err.code = 'SUBDOMAIN_IN_USE';
39+
throw err;
3840
}
3941

4042
const maxSockets = this.opt.max_tcp_sockets;

lib/ClientManager.test.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ describe('ClientManager', () => {
2323
manager.removeClient('foobar');
2424
});
2525

26-
it('should create a new client with random id if previous exists', async () => {
26+
it('should throw SUBDOMAIN_IN_USE error if previous exists', async () => {
2727
const manager = new ClientManager();
2828
const clientA = await manager.newClient('foobar');
29-
const clientB = await manager.newClient('foobar');
30-
assert(clientA.id, 'foobar');
31-
assert(manager.hasClient(clientB.id));
32-
assert(clientB.id != clientA.id);
33-
manager.removeClient(clientB.id);
29+
assert.equal(clientA.id, 'foobar');
30+
31+
try {
32+
await manager.newClient('foobar');
33+
assert.fail('Should have thrown SUBDOMAIN_IN_USE error');
34+
} catch (err) {
35+
assert.equal(err.code, 'SUBDOMAIN_IN_USE');
36+
assert(err.message.includes('foobar'));
37+
}
38+
3439
manager.removeClient('foobar');
3540
});
3641

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "@desplega.ai/localtunnel-server",
44
"type": "module",
55
"description": "expose localhost to the world (desplega.ai fork)",
6-
"version": "0.1.2",
6+
"version": "0.1.3",
77
"license": "MIT",
88
"repository": {
99
"type": "git",

server.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,17 @@ export default function (opt) {
334334
authOptions.password = ctx.query.password || generatePassword();
335335
}
336336

337-
const info = await manager.newClient(reqId, ctx, authOptions);
337+
let info;
338+
try {
339+
info = await manager.newClient(reqId, ctx, authOptions);
340+
} catch (err) {
341+
if (err.code === 'SUBDOMAIN_IN_USE') {
342+
ctx.status = 409;
343+
ctx.body = { message: err.message };
344+
return;
345+
}
346+
throw err;
347+
}
338348

339349
let url = schema + '://' + info.id + '.' + opt.domain || ctx.request.host;
340350

0 commit comments

Comments
 (0)