Skip to content

Commit 927a38f

Browse files
committed
feat: add initial plot/base/server implementation
--- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: passed - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed ---
1 parent b20d000 commit 927a38f

File tree

7 files changed

+452
-0
lines changed

7 files changed

+452
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2018 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
/**
22+
* Returns a new connections store.
23+
*
24+
* @private
25+
* @returns {Object} store
26+
*/
27+
function create() {
28+
return {};
29+
}
30+
31+
32+
// EXPORTS //
33+
34+
module.exports = create;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
/**
22+
* Plot server.
23+
*
24+
* @module @stdlib/plot/base/server
25+
*
26+
* @example
27+
* var createServer = require( '@stdlib/plot/base/server' );
28+
*
29+
* function router( req, res ) {
30+
* res.end();
31+
* }
32+
*
33+
* function onReady( error, server ) {
34+
* if ( error ) {
35+
* throw error
36+
* }
37+
* server.close();
38+
* }
39+
*
40+
* createServer( router, onReady );
41+
*/
42+
43+
// MODULES //
44+
45+
var main = require( './main.js' );
46+
47+
48+
// EXPORTS //
49+
50+
module.exports = main;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDGjCCAgKgAwIBAgIUY77Qg5UIMpE7/e3T9lfhA51jfdcwDQYJKoZIhvcNAQEL
3+
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDgxNjA4NTU1MVoXDTI1MDkx
4+
NTA4NTU1MVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
5+
AAOCAQ8AMIIBCgKCAQEAolomYjGUgoRTbfkVZqcifglvr9d5K7mc280pbM5Zhbob
6+
SVCc/BU52V8W7z18qq1jfaMwBbLKiIV5M8w50uGeE/mc51D2QohFQqWVby0fhJ57
7+
WW7w+Bn/bex1IG9mqtwfUxwgMCnyncWf8/0N9TY51EApOnmjZUil4u56dDUxb903
8+
UYt2evse7DmzHJBsNVdo14cf0ea91w5KMligts+UkyseBVeodKmWV5tea5tAif+Q
9+
q3E2CFK+QkYhBm0JowdsbJfCNQv5dKrZwjtHR/YbFfWsuG8YJR36bEZcPEAqgcBu
10+
RIlBBHSJlCwnWxhU8/U1A4yAhy7R4E4uabH2hCz5nQIDAQABo2QwYjAdBgNVHQ4E
11+
FgQUfFkcV4D8H6oBGZgBED/1Oe9cOm0wHwYDVR0jBBgwFoAUfFkcV4D8H6oBGZgB
12+
ED/1Oe9cOm0wDwYDVR0TAQH/BAUwAwEB/zAPBgNVHREECDAGhwR/AAABMA0GCSqG
13+
SIb3DQEBCwUAA4IBAQByJA+NEziDO1JGSyzfzuog51khl0YGeA9i1wWuoiYe6VbJ
14+
12BbFhb01bw+8kFQznWoJyNXh+yMP1/5FkbYjhWLEXoQsmetH2kxmug9kOZR/59C
15+
EA7sNYNdLb8t6w7VfqfkMuC0dYskB0nRfawCIr+F9x3mM4A7z8O0DlvvbJtn7bq2
16+
gKANwfZOSnv5bJeNwd35LYXMyAHZey0HZA3Q/bblt4kbjBB5PRoTHorX5FGzub8k
17+
ZM9XTixGfueivEc8tahTI+s8ncBBhMir+c8tCxfb/KEahextPkO6Y6pPJjZuqFGz
18+
4vprylLQvT3zqX5VFQ/D+LWm0ySgprWxVbk8WnHy
19+
-----END CERTIFICATE-----
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCiWiZiMZSChFNt
3+
+RVmpyJ+CW+v13kruZzbzSlszlmFuhtJUJz8FTnZXxbvPXyqrWN9ozAFssqIhXkz
4+
zDnS4Z4T+ZznUPZCiEVCpZVvLR+EnntZbvD4Gf9t7HUgb2aq3B9THCAwKfKdxZ/z
5+
/Q31NjnUQCk6eaNlSKXi7np0NTFv3TdRi3Z6+x7sObMckGw1V2jXhx/R5r3XDkoy
6+
WKC2z5STKx4FV6h0qZZXm15rm0CJ/5CrcTYIUr5CRiEGbQmjB2xsl8I1C/l0qtnC
7+
O0dH9hsV9ay4bxglHfpsRlw8QCqBwG5EiUEEdImULCdbGFTz9TUDjICHLtHgTi5p
8+
sfaELPmdAgMBAAECggEAMMYFfv1nILixOboZWFAAuD2MfJauk9i8y3m8pq/tTBPn
9+
i6R4rPCRgwCiqrfdoobZe06RqDyYxN/YVBvYCUb3jwevv/xeJwm6SPXmyVIUhGp6
10+
IxmFsftZdcQSQe7FuLHNhVHPR7jqZsSOs6WD0nV6dQ3bdPJDCLQFBgJf7lZmpjSk
11+
B1RO5SITejFYZzhA88btRhN/Mo1Aq9tNRX5wSbgdsp9vGpnMhLSnh49lzNgMrlCt
12+
5cRbFIp8nGNhEtdL602Iu3l/cnROsDkUpZ67Y4hCaD+ZLZzZiY6XcwLBkK/66yFJ
13+
+Av3nMKNImFhmaBmFF9PP3uIThLCDU6uLB2nbOTS7wKBgQDUdnq1QIvP2brhzqZl
14+
Miw/Uhn3vAI5afTj/t2L5fq8PlXqc0eEPePO/gDdzJz4sivn8nvOseATEy1rvVeb
15+
bkBxgznIHVJz4BPJ1usALSUNXDoYo8g5kjLym0xRbFfji9+y4oBOFN2dqeuePHSn
16+
zTihYvb7YnXKGDqlbD+1j6x+6wKBgQDDnu5SO6n2N1B2XtQSBm1jzGWoNi9er1jt
17+
hwTq1B2lrviVuDa0kwXuGD9RTutUQyHBgzJIjjpTqmmY55YHSjtlzh4Jv5IipNb4
18+
orWI4zWdqCCTkHfJDlLCDMxyq6Lf+P1Abu2C9+INK3xJ92QrXqRy7bzCCGizARox
19+
Ur675lUXlwKBgE7k8Av+O+yi3VKceg155BRfWGU1212Wiule6sXYUJM2UpEM3bGt
20+
ibqDd67lHCT9hHBNCsxmIN70cBlh5fZBqox8PiihOszsmGyK8hjOqsObS7d6mg9G
21+
ysRsBpr1sG0+s4KpuRtFhEXXozbPHEJzYmQLa8tNdI/nD1/+Imo5P8ShAoGAMZT/
22+
7OHog1qjz0zbzbHJWik9fvQ073fcP2Agk/CtjwgZQ8GMRBqbvyxwKAirxxC45pZ9
23+
UTyTdg9UcogIU9Kx2Wzz2h6vSI6lWiHLh2Wnek1Z7G0Kn3A886hQzaPJRaGTdxhm
24+
pPJ1XkcuWoUU45U6c1Lkq/17pBtcazIA3BlWagMCgYBNaRXg01jVdAOM2Lfd2dgr
25+
UKuucsQ2ZcfNqCPLoN0uq3W7zPrAl0dCLLssoMzXfvCTL9DDuurU9EHwzBm/84BG
26+
cXKodXA+rC5axoj/BC4R0MkerjGuFxr0rh2o8yc6aTEer5Cvs4xkP2dAMgfXuc8B
27+
3anarHhn0p3D/1YJppG3XQ==
28+
-----END PRIVATE KEY-----
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var resolve = require( 'path' ).resolve;
24+
var logger = require( 'debug' );
25+
var isFunction = require( '@stdlib/assert/is-function' );
26+
var objectKeys = require( '@stdlib/utils/keys' );
27+
var readFileSync = require( '@stdlib/fs/read-file' ).sync;
28+
var serverFactory = require( '@stdlib/net/http2-secure-server' );
29+
var format = require( '@stdlib/string/format' );
30+
var createStore = require( './connections_store.js' );
31+
var serverOpts = require( './opts.js' );
32+
33+
34+
// VARIABLES //
35+
36+
var debug = logger( 'plot-server' );
37+
var KEYS_DIR = resolve( __dirname, 'keys' );
38+
39+
40+
// MAIN //
41+
42+
/**
43+
* Creates a plot server.
44+
*
45+
* @param {Function} router - request handler
46+
* @param {Callback} clbk - callback to invoke upon creating a server
47+
* @throws {TypeError} first argument must be a function
48+
* @throws {TypeError} second argument must be a function
49+
* @throws {Error} error encountered when starting server
50+
*
51+
* @example
52+
* function router( req, res ) {
53+
* res.end();
54+
* }
55+
*
56+
* function onReady( error, server ) {
57+
* if ( error ) {
58+
* throw error
59+
* }
60+
* server.close();
61+
* }
62+
*
63+
* createServer( router, onReady );
64+
*/
65+
function createServer( router, clbk ) {
66+
var connections;
67+
var server;
68+
var sopts;
69+
var boot;
70+
71+
if ( !isFunction( router ) ) {
72+
throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', router ) );
73+
}
74+
if ( !isFunction( clbk ) ) {
75+
throw new TypeError( format( 'invalid argument. Second argument must be a function. Value: `%s`.', clbk ) );
76+
}
77+
// Extract server options:
78+
sopts = serverOpts( {} );
79+
80+
// Set require server options for SSL:
81+
sopts = {
82+
'cert': readFileSync( resolve( KEYS_DIR, 'localhost_cert.pem' ) ),
83+
'key': readFileSync( resolve( KEYS_DIR, 'localhost_privkey.pem' ) ),
84+
'allowHTTP1': true
85+
};
86+
87+
// Create a function to boot a server...
88+
boot = serverFactory( sopts, requestListener );
89+
90+
debug( 'Starting server...' );
91+
boot( onServer );
92+
93+
// Initialize a connections store:
94+
connections = createStore();
95+
96+
/**
97+
* Callback invoked upon creating a server.
98+
*
99+
* @private
100+
* @param {(Error|null)} error - error object
101+
* @param {Server} _server - server instance
102+
* @throws {Error} error encountered when starting server
103+
*/
104+
function onServer( error, _server ) {
105+
if ( error ) {
106+
throw error;
107+
}
108+
debug( 'Server started.' );
109+
server = _server;
110+
111+
// Track connections so that we can perform clean-up upon closing the server of any persistent TCP connections which are still hanging around:
112+
server.on( 'connection', onConnection );
113+
114+
server.once( 'close', onClose );
115+
clbk( null, server );
116+
}
117+
118+
/**
119+
* Callback invoked upon receiving a socket connection.
120+
*
121+
* @private
122+
* @param {Socket} socket - socket connection
123+
*/
124+
function onConnection( socket ) {
125+
var key = socket.remoteAddress + ':' + socket.remotePort;
126+
127+
debug( 'Received a socket connection: %s.', key );
128+
connections[ key ] = socket;
129+
socket.on( 'close', onClose );
130+
131+
/**
132+
* Callback invoked once a socket connection closes.
133+
*
134+
* @private
135+
*/
136+
function onClose() {
137+
debug( 'Socket connection closed: %s.', key );
138+
delete connections[ key ];
139+
}
140+
}
141+
142+
/**
143+
* Callback invoked upon receiving an HTTP request.
144+
*
145+
* @private
146+
* @param {IncomingMessage} request - HTTP request object
147+
* @param {ServerResponse} response - HTTP response object
148+
* @returns {void}
149+
*/
150+
function requestListener( request, response ) {
151+
debug( 'Received a request for %s', request.url );
152+
router( request, response );
153+
}
154+
155+
/**
156+
* Callback invoked once a server closes.
157+
*
158+
* @private
159+
*/
160+
function onClose() {
161+
debug( 'Server closed.' );
162+
setTimeout( destroyConnections, 5000 );
163+
}
164+
165+
/**
166+
* Destroys all connections.
167+
*
168+
* @private
169+
*/
170+
function destroyConnections() {
171+
var keys;
172+
var i;
173+
174+
debug( 'Destroying all connections...' );
175+
keys = objectKeys( connections );
176+
for ( i = 0; i < keys.length; i++ ) {
177+
debug( 'Destroying connection %s...', keys[i] );
178+
connections[ keys[i] ].destroy();
179+
}
180+
}
181+
}
182+
183+
184+
// EXPORTS //
185+
186+
module.exports = createServer;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var hasOwnProp = require( '@stdlib/assert/has-own-property' );
24+
25+
26+
// MAIN //
27+
28+
/**
29+
* Extracts server options from input function options.
30+
*
31+
* @private
32+
* @param {Options} options - function options
33+
* @param {NonNegativeInteger} [options.port] - server port
34+
* @param {NonNegativeInteger} [options.maxport] - max server port
35+
* @param {string} [options.hostname] - server hostname
36+
* @param {string} [options.address] - server address
37+
* @returns {Options} server options
38+
*
39+
* @example
40+
* var options = {
41+
* 'port': 7331,
42+
* 'address': '127.0.0.1'
43+
* };
44+
* var out = opts( options );
45+
* // returns {'port': 7331, 'address': '127.0.0.1'}
46+
*/
47+
function opts( options ) {
48+
var out = {};
49+
if ( hasOwnProp( options, 'port' ) ) {
50+
out.port = options.port;
51+
}
52+
if ( hasOwnProp( options, 'maxport' ) ) {
53+
out.maxport = options.maxport;
54+
}
55+
if ( hasOwnProp( options, 'hostname' ) ) {
56+
out.hostname = options.hostname;
57+
}
58+
if ( hasOwnProp( options, 'address' ) ) {
59+
out.address = options.address;
60+
}
61+
return out;
62+
}
63+
64+
65+
// EXPORTS //
66+
67+
module.exports = opts;

0 commit comments

Comments
 (0)