roster-server 3.0.0 → 3.0.2

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/index.js CHANGED
@@ -985,7 +985,7 @@ class Roster {
985
985
  socket.setTimeout(0);
986
986
  const site = servername && this._isolatedSiteFor(routes, servername);
987
987
  if (site) {
988
- site.handoff(port, head, socket);
988
+ site.handoff(port, head, socket, () => this._sockets.delete(socket));
989
989
  return;
990
990
  }
991
991
  socket.unshift(head);
@@ -108,8 +108,10 @@ class IsolatedSite {
108
108
 
109
109
  /**
110
110
  * Pass an accepted client socket, with the bytes already read from it, to the site process.
111
+ * `onHandedOff` runs once the site process owns the connection; the main process never
112
+ * sees a 'close' event for it.
111
113
  */
112
- handoff(port, head, socket) {
114
+ handoff(port, head, socket, onHandedOff) {
113
115
  const child = this.child;
114
116
  if (!this.ready || this.pending >= this.isolation.backlog) {
115
117
  socket.destroy();
@@ -119,6 +121,7 @@ class IsolatedSite {
119
121
  child.send({ type: 'connection', port, head: head.toString('base64') }, socket, error => {
120
122
  if (this.child === child) this.pending--;
121
123
  if (error) socket.destroy();
124
+ else onHandedOff();
122
125
  });
123
126
  }
124
127
 
@@ -138,6 +138,8 @@ process.on('disconnect', shutdown);
138
138
  process.on('message', (message, handle) => {
139
139
  if (message.type === 'start') start(message);
140
140
  else if (message.type === 'connection') {
141
+ // A connection can close while its socket is being transferred over IPC.
142
+ if (!handle) return;
141
143
  if (roster) roster.accept(message.port, Buffer.from(message.head, 'base64'), handle);
142
144
  else handle.destroy();
143
145
  } else if (message.type === 'certificate') roster.settleCertificate(message);
package/package.json CHANGED
@@ -1,54 +1,54 @@
1
1
  {
2
- "name": "roster-server",
3
- "version": "3.0.0",
4
- "description": "👾 RosterServer - A domain host router to host multiple HTTPS.",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "node --test 'test/**/*.test.js'"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/clasen/RosterServer.git"
12
- },
13
- "keywords": [
14
- "greenlock",
15
- "https",
16
- "domain",
17
- "multi-domain",
18
- "letsencrypt",
19
- "hosting",
20
- "ssl",
21
- "host",
22
- "vhost",
23
- "virtual-host",
24
- "socket.io",
25
- "websocket",
26
- "express",
27
- "greenlock-express",
28
- "shotx",
29
- "bun",
30
- "clasen"
31
- ],
32
- "author": "Martin Clasen",
33
- "license": "MIT",
34
- "bugs": {
35
- "url": "https://github.com/clasen/RosterServer/issues"
36
- },
37
- "homepage": "https://github.com/clasen/RosterServer#readme",
38
- "dependencies": {
39
- "@greenlock/manager": "^3.1.0",
40
- "@root/acme": "^3.1.0",
41
- "@root/csr": "^0.8.1",
42
- "@root/encoding": "^1.0.1",
43
- "@root/keypairs": "^0.10.0",
44
- "@root/mkdirp": "^1.0.0",
45
- "@root/request": "^1.6.1",
46
- "acme-dns-01-cli": "^3.0.7",
47
- "acme-http-01-standalone": "^3.0.5",
48
- "cert-info": "^1.5.1",
49
- "greenlock-store-fs": "^3.2.2",
50
- "lemonlog": "^1.2.0",
51
- "redirect-https": "^1.3.1",
52
- "safe-replace": "^1.1.0"
53
- }
54
- }
2
+ "name": "roster-server",
3
+ "version": "3.0.2",
4
+ "description": "👾 RosterServer - A domain host router to host multiple HTTPS.",
5
+ "main": "index.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/clasen/RosterServer.git"
9
+ },
10
+ "keywords": [
11
+ "greenlock",
12
+ "https",
13
+ "domain",
14
+ "multi-domain",
15
+ "letsencrypt",
16
+ "hosting",
17
+ "ssl",
18
+ "host",
19
+ "vhost",
20
+ "virtual-host",
21
+ "socket.io",
22
+ "websocket",
23
+ "express",
24
+ "greenlock-express",
25
+ "shotx",
26
+ "bun",
27
+ "clasen"
28
+ ],
29
+ "author": "Martin Clasen",
30
+ "license": "MIT",
31
+ "bugs": {
32
+ "url": "https://github.com/clasen/RosterServer/issues"
33
+ },
34
+ "homepage": "https://github.com/clasen/RosterServer#readme",
35
+ "dependencies": {
36
+ "@greenlock/manager": "^3.1.0",
37
+ "@root/acme": "^3.1.0",
38
+ "@root/csr": "^0.8.1",
39
+ "@root/encoding": "^1.0.1",
40
+ "@root/keypairs": "^0.10.0",
41
+ "@root/mkdirp": "^1.0.0",
42
+ "@root/request": "^1.6.1",
43
+ "acme-dns-01-cli": "^3.0.7",
44
+ "acme-http-01-standalone": "^3.0.5",
45
+ "cert-info": "^1.5.1",
46
+ "greenlock-store-fs": "^3.2.2",
47
+ "lemonlog": "^1.2.0",
48
+ "redirect-https": "^1.3.1",
49
+ "safe-replace": "^1.1.0"
50
+ },
51
+ "scripts": {
52
+ "test": "node --test 'test/**/*.test.js'"
53
+ }
54
+ }
@@ -131,6 +131,29 @@ it('routes each SNI host to its own process with the client address and headers
131
131
  assert.equal(redirect.headers.location, 'https://a.example.com/path');
132
132
  });
133
133
 
134
+ it('keeps serving in the same worker when a connection arrives without a socket', { timeout: 20000 }, async t => {
135
+ const { roster, port } = await startProduction(t, {
136
+ 'disconnected.example.com': `${REPORT_SITE}
137
+ process.on('message', message => {
138
+ if (message.type === 'connection' && message.head === '') process.send({ type: 'observed' });
139
+ });`
140
+ }, { certificates: ['disconnected.example.com'] });
141
+ const first = JSON.parse((await get(port, 'disconnected.example.com')).body);
142
+ const child = roster._isolatedSites[0].child;
143
+ const observed = Promise.race([
144
+ once(child, 'message').then(([message]) => message.type),
145
+ once(child, 'exit').then(() => 'exited')
146
+ ]);
147
+ await new Promise((resolve, reject) => {
148
+ child.send({ type: 'connection', port: 443, head: '' }, error => error ? reject(error) : resolve());
149
+ });
150
+ assert.equal(await observed, 'observed', 'worker exited on a connection without a socket');
151
+ const response = await get(port, 'disconnected.example.com');
152
+ assert.equal(response.status, 200);
153
+ assert.equal(JSON.parse(response.body).pid, first.pid);
154
+ assert.equal(child.exitCode, null);
155
+ });
156
+
134
157
  it('keeps serving other sites while one blocks its event loop', { timeout: 20000 }, async t => {
135
158
  const { port } = await startProduction(t, {
136
159
  'busy.example.com': `module.exports = () => (req, res) => {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes