roster-server 3.4.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,7 +28,7 @@ await roster.start();
28
28
  console.log(roster.getUrl('example.com'));
29
29
  ```
30
30
 
31
- Local mode binds to `localhost`, skips certificates, and assigns domain ports within `minLocalPort`–`maxLocalPort` (default `4000`–`9999`). Assignments use a domain hash and resolve collisions within the instance; they do not probe for available OS ports. A bind failure rejects startup. Read the assigned URL with `getUrl()` instead of hardcoding a sample port.
31
+ Local mode binds to `hostname` (default `::`, IPv4 and IPv6 on all interfaces), skips certificates, and assigns domain ports within `minLocalPort`–`maxLocalPort` (default `4000`–`9999`). Assignments use a domain hash and resolve collisions within the instance; they do not probe for available OS ports. A bind failure rejects startup. Read the assigned URL with `getUrl()` instead of hardcoding a sample port.
32
32
 
33
33
  For production HTTPS, use a real contact email, public DNS pointing to the host, and explicit storage paths:
34
34
 
@@ -313,7 +313,7 @@ Pass operational settings through the constructor, using the consuming applicati
313
313
  | `filename` | `index` | Script entry basename, without extension. |
314
314
  | `local` | `false` | Local HTTP mode with assigned ports and no certificates. |
315
315
  | `port` | `443` | Default port for manual registrations and routing helpers. |
316
- | `hostname` | `::` | Production bind address; local mode uses `localhost`. |
316
+ | `hostname` | `::` | Bind address in production and local mode. |
317
317
  | `minLocalPort`, `maxLocalPort` | `4000`, `9999` | Inclusive local assignment range. |
318
318
  | `staging` | `false` | ACME staging environment. |
319
319
  | `autoCertificates` | `true` | Certificate runtime during `init()`; disable for serving-only workers. |
package/index.js CHANGED
@@ -950,6 +950,7 @@ class Roster {
950
950
  const plugins = this.plugins.map(isolatedPluginSource);
951
951
  const options = {
952
952
  local: this.local,
953
+ hostname: this.hostname,
953
954
  port: this.defaultPort,
954
955
  filename: this.filename,
955
956
  greenlockStorePath: this.greenlockStorePath,
@@ -1630,7 +1631,7 @@ class Roster {
1630
1631
  virtualServer.processUpgrade(req, socket, head);
1631
1632
  });
1632
1633
 
1633
- await this._listen(httpServer, port, 'localhost');
1634
+ await this._listen(httpServer, port, this.hostname);
1634
1635
  const cleanDomain = normalizeDomainForLocalHost(domain);
1635
1636
  log.info(`🌐 ${domain} → http://${localHostForDomain(cleanDomain)}:${port}`);
1636
1637
  }
@@ -57,7 +57,7 @@ class SiteWorkerRoster extends Roster {
57
57
  if (key.startsWith('www.')) continue;
58
58
  const { domain, port } = this.parseDomainWithPort(key);
59
59
  const server = net.createServer({ pauseOnConnect: true });
60
- const listening = this._listen(server, this._site.localPorts[`${domain}:${port}`], 'localhost');
60
+ const listening = this._listen(server, this._site.localPorts[`${domain}:${port}`], this.hostname);
61
61
  server.on('connection', socket => {
62
62
  // The site owns the connection from here; it may pass it to another process.
63
63
  this._sockets.delete(socket);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roster-server",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "👾 RosterServer - A domain host router to host multiple HTTPS.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@ Use this skill to configure a consuming application. Keep its existing configura
9
9
 
10
10
  ## Choose the lifecycle owner
11
11
 
12
- - **Local development:** `new Roster({ local: true, wwwPath: absolutePath })`, register sites, then `await roster.start()`. Local mode binds to `localhost` and assigns ports; read URLs with `roster.getUrl(domain)` after startup.
12
+ - **Local development:** `new Roster({ local: true, wwwPath: absolutePath })`, register sites, then `await roster.start()`. Local mode binds to `hostname` (default `::`, IPv4 and IPv6) and assigns ports; read URLs with `roster.getUrl(domain)` after startup.
13
13
  - **Standalone HTTPS:** set the real `email`, absolute `wwwPath` and `greenlockStorePath`; leave `local` false. `await roster.start()` owns ACME HTTP on port 80 and the configured HTTPS listeners. A different HTTPS port does not eliminate the need for port 80.
14
14
  - **External server/worker:** `await roster.init()`, then `roster.attach(server, { port })` or an HTTPS helper. The caller owns listening and closing that server. `port` on `attach()` selects the routing table, not the TCP port.
15
15
  - **Serving-only worker:** use `autoCertificates: false` with `init()` and `createServingHttpsServer({ servername })`. A separate manager must provide certificates first. Calling `start()` still enables the standalone ACME lifecycle.
@@ -91,9 +91,9 @@ function get(port, servername, { pathname = '/', headers = {} } = {}) {
91
91
  });
92
92
  }
93
93
 
94
- function localGet(port, pathname = '/') {
94
+ function localGet(port, pathname = '/', hostname = 'localhost') {
95
95
  return new Promise((resolve, reject) => {
96
- http.get({ hostname: 'localhost', port, path: pathname, agent: false }, res => {
96
+ http.get({ hostname, port, path: pathname, agent: false }, res => {
97
97
  let body = '';
98
98
  res.on('data', chunk => { body += chunk; });
99
99
  res.on('end', () => resolve({ status: res.statusCode, body }));
@@ -315,6 +315,9 @@ it('isolates directory sites in local mode, including aliases and request plugin
315
315
  assert.equal(alias.pid, site.pid);
316
316
  assert.notEqual(other.pid, site.pid);
317
317
  assert.deepEqual(await localGet(port('other.example'), '/wp-login.php'), { status: 404, body: 'Not Found' });
318
+ for (const address of ['127.0.0.1', '::1']) {
319
+ assert.equal(JSON.parse((await localGet(port('site.example'), '/', address)).body).pid, site.pid);
320
+ }
318
321
  });
319
322
 
320
323
  it('serves a site module already loaded by the main process from that process', { timeout: 20000 }, async t => {
@@ -479,6 +482,7 @@ it('gives a socket site plain connections on its local port', { timeout: 20000 }
479
482
  await roster.start();
480
483
  const { port } = new URL(roster.getUrl('raw.example'));
481
484
  assert.notEqual(JSON.parse((await localGet(port)).body).pid, process.pid);
485
+ for (const address of ['127.0.0.1', '::1']) assert.equal((await localGet(port, '/', address)).status, 200);
482
486
  assert.equal(fs.readFileSync(`${marker}.head`, 'utf8'), '0');
483
487
  await roster.close();
484
488
  assert.equal(fs.readFileSync(marker, 'utf8'), 'closed');
@@ -94,7 +94,7 @@ it('waits for listening, shares start, and closes owned servers once', async t =
94
94
 
95
95
  it('rejects bind failures and rolls back already opened ports', async t => {
96
96
  const blocker = http.createServer();
97
- await new Promise(resolve => blocker.listen(0, 'localhost', resolve));
97
+ await new Promise(resolve => blocker.listen(0, '::', resolve));
98
98
  t.after(() => new Promise(resolve => blocker.close(resolve)));
99
99
  const roster = createRoster(t);
100
100
  const ports = [0, blocker.address().port];