Skip to content

Commit 64c69e4

Browse files
committed
Initial commit
0 parents  commit 64c69e4

File tree

9 files changed

+223
-0
lines changed

9 files changed

+223
-0
lines changed

.editorconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
root = true
2+
3+
[*]
4+
indent_style = tab
5+
end_of_line = lf
6+
charset = utf-8
7+
trim_trailing_whitespace = true
8+
insert_final_newline = true
9+
10+
[*.yml]
11+
indent_style = space
12+
indent_size = 2
13+

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.nyc_output
2+
node_modules
3+
package-lock.json
4+

.npmrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package-lock=false
2+

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: node_js
2+
node_js:
3+
- '11'
4+
- '10'
5+
after_success: npm run coveralls
6+

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2018 Szymon Marczak
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# `resolve-alpn`
2+
3+
[![Build Status](https://travis-ci.org/szmarczak/resolve-alpn.svg?branch=master)](https://travis-ci.org/szmarczak/resolve-alpn) [![Coverage Status](https://coveralls.io/repos/github/szmarczak/resolve-alpn/badge.svg?branch=master)](https://coveralls.io/github/szmarczak/resolve-alpn?branch=master)
4+
5+
## API
6+
7+
### resolveALPN(options)
8+
9+
Returns an object with an `alpnProtocol` property. The `socket` property may be also present.
10+
11+
```js
12+
const result = await resolveALPN({
13+
host: 'nghttp2.org',
14+
ALPNProtocols: ['h2', 'http/1.1']
15+
});
16+
17+
console.log(result); // {alpnProtocol: 'h2'}
18+
```
19+
20+
#### options
21+
22+
Same as [TLS options](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
23+
24+
##### options.resolveSocket
25+
26+
By default, the socket gets destroyed and the promise resolves.<br>
27+
If you set this to true, it will return the socket in a `socket` property.
28+
29+
```js
30+
const result = await resolveALPN({
31+
host: 'nghttp2.org',
32+
ALPNProtocols: ['h2', 'http/1.1'],
33+
resolveSocket: true
34+
});
35+
36+
console.log(result); // {alpnProtocol: 'h2', socket: tls.TLSSocket}
37+
38+
// Remember to close the socket!
39+
result.socket.end();
40+
```
41+
42+
## License
43+
44+
MIT

index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
const tls = require('tls');
3+
4+
module.exports = (options = {}) => new Promise((resolve, reject) => {
5+
const socket = tls.connect(options, () => {
6+
if (options.resolveSocket) {
7+
socket.off('error', reject);
8+
resolve({alpnProtocol: socket.alpnProtocol, socket});
9+
} else {
10+
socket.destroy();
11+
resolve({alpnProtocol: socket.alpnProtocol});
12+
}
13+
});
14+
15+
socket.on('error', reject);
16+
});

package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "resolve-alpn",
3+
"version": "1.0.0",
4+
"description": "Detects the ALPN protocol",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "xo && nyc ava",
8+
"coveralls": "nyc report --reporter=text-lcov | coveralls"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/szmarczak/resolve-alpn.git"
13+
},
14+
"keywords": [
15+
"alpn",
16+
"http2"
17+
],
18+
"author": "Szymon Marczak",
19+
"license": "MIT",
20+
"bugs": {
21+
"url": "https://github.com/szmarczak/resolve-alpn/issues"
22+
},
23+
"homepage": "https://github.com/szmarczak/resolve-alpn#readme",
24+
"devDependencies": {
25+
"ava": "^1.0.1",
26+
"coveralls": "^3.0.2",
27+
"nyc": "^13.1.0",
28+
"pem": "^1.13.2",
29+
"xo": "^0.23.0"
30+
}
31+
}

test.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import http2 from 'http2';
2+
import tls from 'tls';
3+
import util from 'util';
4+
import test from 'ava';
5+
import pem from 'pem';
6+
import resolveALPN from '.';
7+
8+
const createCertificate = util.promisify(pem.createCertificate);
9+
10+
const createServer = async () => {
11+
const caKeys = await createCertificate({
12+
days: 1,
13+
selfSigned: true
14+
});
15+
16+
const caRootKey = caKeys.serviceKey;
17+
const caRootCert = caKeys.certificate;
18+
19+
const keys = await createCertificate({
20+
serviceCertificate: caRootCert,
21+
serviceKey: caRootKey,
22+
serial: Date.now(),
23+
days: 500,
24+
country: '',
25+
state: '',
26+
locality: '',
27+
organization: '',
28+
organizationUnit: '',
29+
commonName: 'localhost'
30+
});
31+
32+
const key = keys.clientKey;
33+
const cert = keys.certificate;
34+
35+
const s = http2.createSecureServer({cert, key, allowHTTP1: true});
36+
37+
s.listen = util.promisify(s.listen);
38+
s.close = util.promisify(s.close);
39+
40+
s.options = {
41+
host: 'localhost',
42+
rejectUnauthorized: false,
43+
ALPNProtocols: ['h2']
44+
};
45+
46+
s.on('listening', () => {
47+
s.options.port = s.address().port;
48+
});
49+
50+
return s;
51+
};
52+
53+
let s;
54+
55+
test.before('setup', async () => {
56+
s = await createServer();
57+
await s.listen();
58+
});
59+
60+
test.after('cleanup', async () => {
61+
await s.close();
62+
});
63+
64+
test('works', async t => {
65+
const result = await resolveALPN(s.options);
66+
t.deepEqual(result, {
67+
alpnProtocol: 'h2'
68+
});
69+
});
70+
71+
test('`resolveSocket` option', async t => {
72+
const result = await resolveALPN({
73+
...s.options,
74+
resolveSocket: true
75+
});
76+
77+
t.is(result.alpnProtocol, 'h2');
78+
t.true(result.socket instanceof tls.TLSSocket);
79+
80+
result.socket.destroy();
81+
});
82+
83+
test('empty options', async t => {
84+
await t.throwsAsync(() => resolveALPN(), {code: 'ECONNREFUSED'});
85+
});

0 commit comments

Comments
 (0)