Skip to content

Commit

Permalink
http: export connections lists
Browse files Browse the repository at this point in the history
  • Loading branch information
ShogunPanda committed Apr 25, 2022
1 parent 503f147 commit 8b2bdd6
Show file tree
Hide file tree
Showing 19 changed files with 543 additions and 36 deletions.
51 changes: 51 additions & 0 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,31 @@ This event is guaranteed to be passed an instance of the {net.Socket} class,
a subclass of {stream.Duplex}, unless the user specifies a socket
type other than {net.Socket}.

### `server.activeConnections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

Returns an array of all the sockets connected to the server that have an
active request.

A request is considered active if it has not fully received by the server and
it has not expired yet according to `server.headersTimeout` (for the headers) or
`server.requestTimeout` (for the entire request).

### `server.connections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

Returns an array of all the sockets connected to the server.

### `server.close([callback])`

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

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

### `server.expiredConnections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

Returns an array of all the sockets connected to the server that have an
expired request according to `server.headersTimeout` (for the headers) or
`server.requestTimeout` (for the entire request).

### `server.headersTimeout`

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

### `server.idleConnections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

Returns an array of all the sockets connected to the server that have no
active requests.

The list of sockets returned in this list are usually still connected because
they are using HTTP Keep-Alive.

### `server.listen()`

Starts the HTTP server listening for connections.
Expand Down
72 changes: 58 additions & 14 deletions doc/api/https.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ added: v0.3.4

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

### `server.activeConnections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

See [`http.Server.activeConnections()`][].

### `server.connections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

See [`http.Server.connections()`][].

### `server.close([callback])`

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

See [`server.close()`][`http.close()`] from the HTTP module for details.
See [`http.Server.close()`][].

### `server.expiredConnections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

See [`http.Server.expiredConnections()`][].

### `server.headersTimeout`

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

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

See [`http.Server#headersTimeout`][].
See [`http.Server.headersTimeout`][].

### `server.idleConnections()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}

See [`http.Server.idleConnections()`][].

### `server.listen()`

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

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

See [`http.Server#maxHeadersCount`][].
See [`http.Server.maxHeadersCount`][].

### `server.requestTimeout`

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

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

See [`http.Server#requestTimeout`][].
See [`http.Server.requestTimeout`][].

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

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

See [`http.Server#setTimeout()`][].
See [`http.Server.setTimeout()`][].

### `server.timeout`

Expand All @@ -190,7 +230,7 @@ changes:

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

See [`http.Server#timeout`][].
See [`http.Server.timeout`][].

### `server.keepAliveTimeout`

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

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

See [`http.Server#keepAliveTimeout`][].
See [`http.Server.keepAliveTimeout`][].

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

Expand Down Expand Up @@ -523,14 +563,18 @@ headers: max-age=0; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="; p
[`http.Agent(options)`]: http.md#new-agentoptions
[`http.Agent`]: http.md#class-httpagent
[`http.ClientRequest`]: http.md#class-httpclientrequest
[`http.Server#headersTimeout`]: http.md#serverheaderstimeout
[`http.Server#keepAliveTimeout`]: http.md#serverkeepalivetimeout
[`http.Server#maxHeadersCount`]: http.md#servermaxheaderscount
[`http.Server#requestTimeout`]: http.md#serverrequesttimeout
[`http.Server#setTimeout()`]: http.md#serversettimeoutmsecs-callback
[`http.Server#timeout`]: http.md#servertimeout
[`http.Server.activeConnections()`]: http.md#serveractiveconnections
[`http.Server.close()`]: http.md#serverclosecallback
[`http.Server.connections()`]: http.md#serverconnections
[`http.Server.expiredconnections()`]: http.md#serverexpiredconnections
[`http.Server.headersTimeout`]: http.md#serverheaderstimeout
[`http.Server.idleconnections()`]: http.md#serveridleconnections
[`http.Server.keepAliveTimeout`]: http.md#serverkeepalivetimeout
[`http.Server.maxHeadersCount`]: http.md#servermaxheaderscount
[`http.Server.requestTimeout`]: http.md#serverrequesttimeout
[`http.Server.setTimeout()`]: http.md#serversettimeoutmsecs-callback
[`http.Server.timeout`]: http.md#servertimeout
[`http.Server`]: http.md#class-httpserver
[`http.close()`]: http.md#serverclosecallback
[`http.createServer()`]: http.md#httpcreateserveroptions-requestlistener
[`http.get()`]: http.md#httpgetoptions-callback
[`http.request()`]: http.md#httprequestoptions-callback
Expand Down
27 changes: 27 additions & 0 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'use strict';

const {
Array,
ArrayIsArray,
Error,
ObjectKeys,
Expand Down Expand Up @@ -465,6 +466,22 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) {
return this;
};

Server.prototype.connections = function connections() {
return connectionsToResource(this[kConnections].all());
};

Server.prototype.activeConnections = function activeConnections() {
return connectionsToResource(this[kConnections].active());
};

Server.prototype.idleConnections = function idleConnections() {
return connectionsToResource(this[kConnections].idle());
};

Server.prototype.expiredConnections = function expiredConnections() {
return connectionsToResource(this[kConnections].expired(this.headersTimeout, this.requestTimeout));
};

Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) {
switch (event) {
case 'request': {
Expand All @@ -488,6 +505,16 @@ Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) {
}
};

function connectionsToResource(resources) {
const connections = Array(resources.length);

for (let i = 0, l = resources.length; i < l; i++) {
connections[i] = resources[i].socket;
}

return connections;
}

function checkConnections() {
const expired = this[kConnections].expired(this.headersTimeout, this.requestTimeout);

Expand Down
8 changes: 8 additions & 0 deletions lib/https.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ function Server(opts, requestListener) {
ObjectSetPrototypeOf(Server.prototype, tls.Server.prototype);
ObjectSetPrototypeOf(Server, tls.Server);

Server.prototype.activeConnections = HttpServer.prototype.activeConnections;

Server.prototype.connections = HttpServer.prototype.connections;

Server.prototype.expiredConnections = HttpServer.prototype.expiredConnections;

Server.prototype.idleConnections = HttpServer.prototype.idleConnections;

Server.prototype.setTimeout = HttpServer.prototype.setTimeout;

/**
Expand Down
50 changes: 50 additions & 0 deletions test/parallel/test-http-server-close-all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';
const common = require('../common');
const assert = require('assert');

const { createServer } = require('http');
const { connect } = require('net');

const server = createServer(common.mustCall(function(req, res) {
res.writeHead(200, { 'Connection': 'keep-alive' });
res.end();
}), { requestTimeout: common.platformTimeout(60000), headersTimeout: 0 });

server.listen(0, function() {
const port = server.address().port;

// Create a new request, let it finish but leave the connection opened using HTTP keep-alive
const client1 = connect(port);
let response1 = '';

client1.on('data', common.mustCall((chunk) => {
response1 += chunk.toString('utf-8');
}));

client1.on('end', common.mustCall(function() {
assert(response1.startsWith('HTTP/1.1 200 OK\r\nConnection: keep-alive'));
}));
client1.on('close', common.mustCall());

client1.resume();
client1.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n');

// Create a second request but never finish it
const client2 = connect(port);
client2.on('close', common.mustCall());

client2.resume();
client2.write('GET / HTTP/1.1\r\n');

// Now, after a while, close the server
setTimeout(common.mustCall(() => {
for (const connection of server.connections()) {
connection.destroy();
}

server.close(common.mustCall());
}), common.platformTimeout(1000));

// This timer should never go off as the server.close should shut everything down
setTimeout(common.mustNotCall(), common.platformTimeout(1500)).unref();
});
65 changes: 65 additions & 0 deletions test/parallel/test-http-server-close-idle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict';
const common = require('../common');
const assert = require('assert');

const { createServer } = require('http');
const { connect } = require('net');

const server = createServer(common.mustCall(function(req, res) {
res.writeHead(200, { 'Connection': 'keep-alive' });
res.end();
}), { requestTimeout: common.platformTimeout(60000), headersTimeout: 0 });

server.listen(0, function() {
const port = server.address().port;

// Create a new request, let it finish but leave the connection opened using HTTP keep-alive
const client1 = connect(port);
let response1 = '';
let client1Closed = false;

client1.on('data', common.mustCall((chunk) => {
response1 += chunk.toString('utf-8');
}));

client1.on('end', common.mustCall(function() {
assert(response1.startsWith('HTTP/1.1 200 OK\r\nConnection: keep-alive'));
}));
client1.on('close', common.mustCall(() => {
client1Closed = true;
}));

client1.resume();
client1.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n');

// Create a second request but never finish it
const client2 = connect(port);
let client2Closed = false;
client2.on('close', common.mustCall(() => {
client2Closed = true;
}));

client2.resume();
client2.write('GET / HTTP/1.1\r\n');

// Now, after a while, close the server
setTimeout(common.mustCall(() => {
for (const connection of server.idleConnections()) {
connection.destroy();
}

server.close(common.mustCall());
}), common.platformTimeout(1000));

// Check that only the idle connection got closed
setTimeout(common.mustCall(() => {
assert(client1Closed);
assert(!client2Closed);

for (const connection of server.connections()) {
connection.destroy();
}

server.close();
}), common.platformTimeout(1500)).unref();
});
Loading

0 comments on commit 8b2bdd6

Please sign in to comment.