Skip to content

Commit 8b2bdd6

Browse files
committed
http: export connections lists
1 parent 503f147 commit 8b2bdd6

19 files changed

+543
-36
lines changed

doc/api/http.md

+51
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,31 @@ This event is guaranteed to be passed an instance of the {net.Socket} class,
14431443
a subclass of {stream.Duplex}, unless the user specifies a socket
14441444
type other than {net.Socket}.
14451445

1446+
### `server.activeConnections()`
1447+
1448+
<!-- YAML
1449+
added: REPLACEME
1450+
-->
1451+
1452+
* Returns: {net.Socket\[]}
1453+
1454+
Returns an array of all the sockets connected to the server that have an
1455+
active request.
1456+
1457+
A request is considered active if it has not fully received by the server and
1458+
it has not expired yet according to `server.headersTimeout` (for the headers) or
1459+
`server.requestTimeout` (for the entire request).
1460+
1461+
### `server.connections()`
1462+
1463+
<!-- YAML
1464+
added: REPLACEME
1465+
-->
1466+
1467+
* Returns: {net.Socket\[]}
1468+
1469+
Returns an array of all the sockets connected to the server.
1470+
14461471
### `server.close([callback])`
14471472

14481473
<!-- YAML
@@ -1453,6 +1478,18 @@ added: v0.1.90
14531478

14541479
Stops the server from accepting new connections. See [`net.Server.close()`][].
14551480

1481+
### `server.expiredConnections()`
1482+
1483+
<!-- YAML
1484+
added: REPLACEME
1485+
-->
1486+
1487+
* Returns: {net.Socket\[]}
1488+
1489+
Returns an array of all the sockets connected to the server that have an
1490+
expired request according to `server.headersTimeout` (for the headers) or
1491+
`server.requestTimeout` (for the entire request).
1492+
14561493
### `server.headersTimeout`
14571494

14581495
<!-- YAML
@@ -1473,6 +1510,20 @@ It must be set to a non-zero value (e.g. 120 seconds) to protect against
14731510
potential Denial-of-Service attacks in case the server is deployed without a
14741511
reverse proxy in front.
14751512

1513+
### `server.idleConnections()`
1514+
1515+
<!-- YAML
1516+
added: REPLACEME
1517+
-->
1518+
1519+
* Returns: {net.Socket\[]}
1520+
1521+
Returns an array of all the sockets connected to the server that have no
1522+
active requests.
1523+
1524+
The list of sockets returned in this list are usually still connected because
1525+
they are using HTTP Keep-Alive.
1526+
14761527
### `server.listen()`
14771528

14781529
Starts the HTTP server listening for connections.

doc/api/https.md

+58-14
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,26 @@ added: v0.3.4
124124

125125
See [`http.Server`][] for more information.
126126

127+
### `server.activeConnections()`
128+
129+
<!-- YAML
130+
added: REPLACEME
131+
-->
132+
133+
* Returns: {net.Socket\[]}
134+
135+
See [`http.Server.activeConnections()`][].
136+
137+
### `server.connections()`
138+
139+
<!-- YAML
140+
added: REPLACEME
141+
-->
142+
143+
* Returns: {net.Socket\[]}
144+
145+
See [`http.Server.connections()`][].
146+
127147
### `server.close([callback])`
128148

129149
<!-- YAML
@@ -133,7 +153,17 @@ added: v0.1.90
133153
* `callback` {Function}
134154
* Returns: {https.Server}
135155

136-
See [`server.close()`][`http.close()`] from the HTTP module for details.
156+
See [`http.Server.close()`][].
157+
158+
### `server.expiredConnections()`
159+
160+
<!-- YAML
161+
added: REPLACEME
162+
-->
163+
164+
* Returns: {net.Socket\[]}
165+
166+
See [`http.Server.expiredConnections()`][].
137167

138168
### `server.headersTimeout`
139169

@@ -143,7 +173,17 @@ added: v11.3.0
143173

144174
* {number} **Default:** `60000`
145175

146-
See [`http.Server#headersTimeout`][].
176+
See [`http.Server.headersTimeout`][].
177+
178+
### `server.idleConnections()`
179+
180+
<!-- YAML
181+
added: REPLACEME
182+
-->
183+
184+
* Returns: {net.Socket\[]}
185+
186+
See [`http.Server.idleConnections()`][].
147187

148188
### `server.listen()`
149189

@@ -154,7 +194,7 @@ This method is identical to [`server.listen()`][] from [`net.Server`][].
154194

155195
* {number} **Default:** `2000`
156196

157-
See [`http.Server#maxHeadersCount`][].
197+
See [`http.Server.maxHeadersCount`][].
158198

159199
### `server.requestTimeout`
160200

@@ -164,7 +204,7 @@ added: v14.11.0
164204

165205
* {number} **Default:** `0`
166206

167-
See [`http.Server#requestTimeout`][].
207+
See [`http.Server.requestTimeout`][].
168208

169209
### `server.setTimeout([msecs][, callback])`
170210

@@ -176,7 +216,7 @@ added: v0.11.2
176216
* `callback` {Function}
177217
* Returns: {https.Server}
178218

179-
See [`http.Server#setTimeout()`][].
219+
See [`http.Server.setTimeout()`][].
180220

181221
### `server.timeout`
182222

@@ -190,7 +230,7 @@ changes:
190230

191231
* {number} **Default:** 0 (no timeout)
192232

193-
See [`http.Server#timeout`][].
233+
See [`http.Server.timeout`][].
194234

195235
### `server.keepAliveTimeout`
196236

@@ -200,7 +240,7 @@ added: v8.0.0
200240

201241
* {number} **Default:** `5000` (5 seconds)
202242

203-
See [`http.Server#keepAliveTimeout`][].
243+
See [`http.Server.keepAliveTimeout`][].
204244

205245
## `https.createServer([options][, requestListener])`
206246

@@ -523,14 +563,18 @@ headers: max-age=0; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="; p
523563
[`http.Agent(options)`]: http.md#new-agentoptions
524564
[`http.Agent`]: http.md#class-httpagent
525565
[`http.ClientRequest`]: http.md#class-httpclientrequest
526-
[`http.Server#headersTimeout`]: http.md#serverheaderstimeout
527-
[`http.Server#keepAliveTimeout`]: http.md#serverkeepalivetimeout
528-
[`http.Server#maxHeadersCount`]: http.md#servermaxheaderscount
529-
[`http.Server#requestTimeout`]: http.md#serverrequesttimeout
530-
[`http.Server#setTimeout()`]: http.md#serversettimeoutmsecs-callback
531-
[`http.Server#timeout`]: http.md#servertimeout
566+
[`http.Server.activeConnections()`]: http.md#serveractiveconnections
567+
[`http.Server.close()`]: http.md#serverclosecallback
568+
[`http.Server.connections()`]: http.md#serverconnections
569+
[`http.Server.expiredconnections()`]: http.md#serverexpiredconnections
570+
[`http.Server.headersTimeout`]: http.md#serverheaderstimeout
571+
[`http.Server.idleconnections()`]: http.md#serveridleconnections
572+
[`http.Server.keepAliveTimeout`]: http.md#serverkeepalivetimeout
573+
[`http.Server.maxHeadersCount`]: http.md#servermaxheaderscount
574+
[`http.Server.requestTimeout`]: http.md#serverrequesttimeout
575+
[`http.Server.setTimeout()`]: http.md#serversettimeoutmsecs-callback
576+
[`http.Server.timeout`]: http.md#servertimeout
532577
[`http.Server`]: http.md#class-httpserver
533-
[`http.close()`]: http.md#serverclosecallback
534578
[`http.createServer()`]: http.md#httpcreateserveroptions-requestlistener
535579
[`http.get()`]: http.md#httpgetoptions-callback
536580
[`http.request()`]: http.md#httprequestoptions-callback

lib/_http_server.js

+27
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323

2424
const {
25+
Array,
2526
ArrayIsArray,
2627
Error,
2728
ObjectKeys,
@@ -465,6 +466,22 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) {
465466
return this;
466467
};
467468

469+
Server.prototype.connections = function connections() {
470+
return connectionsToResource(this[kConnections].all());
471+
};
472+
473+
Server.prototype.activeConnections = function activeConnections() {
474+
return connectionsToResource(this[kConnections].active());
475+
};
476+
477+
Server.prototype.idleConnections = function idleConnections() {
478+
return connectionsToResource(this[kConnections].idle());
479+
};
480+
481+
Server.prototype.expiredConnections = function expiredConnections() {
482+
return connectionsToResource(this[kConnections].expired(this.headersTimeout, this.requestTimeout));
483+
};
484+
468485
Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) {
469486
switch (event) {
470487
case 'request': {
@@ -488,6 +505,16 @@ Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) {
488505
}
489506
};
490507

508+
function connectionsToResource(resources) {
509+
const connections = Array(resources.length);
510+
511+
for (let i = 0, l = resources.length; i < l; i++) {
512+
connections[i] = resources[i].socket;
513+
}
514+
515+
return connections;
516+
}
517+
491518
function checkConnections() {
492519
const expired = this[kConnections].expired(this.headersTimeout, this.requestTimeout);
493520

lib/https.js

+8
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ function Server(opts, requestListener) {
8787
ObjectSetPrototypeOf(Server.prototype, tls.Server.prototype);
8888
ObjectSetPrototypeOf(Server, tls.Server);
8989

90+
Server.prototype.activeConnections = HttpServer.prototype.activeConnections;
91+
92+
Server.prototype.connections = HttpServer.prototype.connections;
93+
94+
Server.prototype.expiredConnections = HttpServer.prototype.expiredConnections;
95+
96+
Server.prototype.idleConnections = HttpServer.prototype.idleConnections;
97+
9098
Server.prototype.setTimeout = HttpServer.prototype.setTimeout;
9199

92100
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
5+
const { createServer } = require('http');
6+
const { connect } = require('net');
7+
8+
const server = createServer(common.mustCall(function(req, res) {
9+
res.writeHead(200, { 'Connection': 'keep-alive' });
10+
res.end();
11+
}), { requestTimeout: common.platformTimeout(60000), headersTimeout: 0 });
12+
13+
server.listen(0, function() {
14+
const port = server.address().port;
15+
16+
// Create a new request, let it finish but leave the connection opened using HTTP keep-alive
17+
const client1 = connect(port);
18+
let response1 = '';
19+
20+
client1.on('data', common.mustCall((chunk) => {
21+
response1 += chunk.toString('utf-8');
22+
}));
23+
24+
client1.on('end', common.mustCall(function() {
25+
assert(response1.startsWith('HTTP/1.1 200 OK\r\nConnection: keep-alive'));
26+
}));
27+
client1.on('close', common.mustCall());
28+
29+
client1.resume();
30+
client1.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n');
31+
32+
// Create a second request but never finish it
33+
const client2 = connect(port);
34+
client2.on('close', common.mustCall());
35+
36+
client2.resume();
37+
client2.write('GET / HTTP/1.1\r\n');
38+
39+
// Now, after a while, close the server
40+
setTimeout(common.mustCall(() => {
41+
for (const connection of server.connections()) {
42+
connection.destroy();
43+
}
44+
45+
server.close(common.mustCall());
46+
}), common.platformTimeout(1000));
47+
48+
// This timer should never go off as the server.close should shut everything down
49+
setTimeout(common.mustNotCall(), common.platformTimeout(1500)).unref();
50+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
5+
const { createServer } = require('http');
6+
const { connect } = require('net');
7+
8+
const server = createServer(common.mustCall(function(req, res) {
9+
res.writeHead(200, { 'Connection': 'keep-alive' });
10+
res.end();
11+
}), { requestTimeout: common.platformTimeout(60000), headersTimeout: 0 });
12+
13+
server.listen(0, function() {
14+
const port = server.address().port;
15+
16+
// Create a new request, let it finish but leave the connection opened using HTTP keep-alive
17+
const client1 = connect(port);
18+
let response1 = '';
19+
let client1Closed = false;
20+
21+
client1.on('data', common.mustCall((chunk) => {
22+
response1 += chunk.toString('utf-8');
23+
}));
24+
25+
client1.on('end', common.mustCall(function() {
26+
assert(response1.startsWith('HTTP/1.1 200 OK\r\nConnection: keep-alive'));
27+
}));
28+
client1.on('close', common.mustCall(() => {
29+
client1Closed = true;
30+
}));
31+
32+
client1.resume();
33+
client1.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n');
34+
35+
// Create a second request but never finish it
36+
const client2 = connect(port);
37+
let client2Closed = false;
38+
client2.on('close', common.mustCall(() => {
39+
client2Closed = true;
40+
}));
41+
42+
client2.resume();
43+
client2.write('GET / HTTP/1.1\r\n');
44+
45+
// Now, after a while, close the server
46+
setTimeout(common.mustCall(() => {
47+
for (const connection of server.idleConnections()) {
48+
connection.destroy();
49+
}
50+
51+
server.close(common.mustCall());
52+
}), common.platformTimeout(1000));
53+
54+
// Check that only the idle connection got closed
55+
setTimeout(common.mustCall(() => {
56+
assert(client1Closed);
57+
assert(!client2Closed);
58+
59+
for (const connection of server.connections()) {
60+
connection.destroy();
61+
}
62+
63+
server.close();
64+
}), common.platformTimeout(1500)).unref();
65+
});

0 commit comments

Comments
 (0)