@@ -10,6 +10,7 @@ var path = require('path'),
10
10
url = require ( 'url' ) ,
11
11
http = require ( 'http' ) ,
12
12
https = require ( 'https' ) ,
13
+ http2 = require ( 'http2' ) ,
13
14
{ promisify } = require ( 'util' ) ,
14
15
test = require ( 'tape' ) ,
15
16
sinon = require ( 'sinon' ) ,
@@ -18,6 +19,8 @@ var path = require('path'),
18
19
19
20
const createServersAsync = promisify ( createServers ) ;
20
21
22
+ const { HTTP2_HEADER_PATH } = http2 . constants ;
23
+
21
24
const ca = fs . readFileSync ( path . join ( __dirname , './fixtures/example-ca-cert.pem' ) ) ;
22
25
23
26
//
@@ -50,6 +53,74 @@ async function download(httpsURL) {
50
53
} ) ;
51
54
}
52
55
56
+ //
57
+ // Request and download response from a URL using HTTP/2
58
+ //
59
+ async function download2 ( httpsURL ) {
60
+ return new Promise ( ( resolve , reject ) => {
61
+ const clientSession = http2 . connect ( httpsURL ) ;
62
+ const fail = results => {
63
+ clientSession . close ( ) ;
64
+ reject ( results ) ;
65
+ } ;
66
+
67
+ const req = clientSession . request ( { [ HTTP2_HEADER_PATH ] : '/' } ) ;
68
+ req . on ( 'response' , ( ) => {
69
+ const chunks = [ ] ;
70
+ req
71
+ . on ( 'data' , chunk => chunks . push ( chunk ) )
72
+ . once ( 'end' , ( ) => {
73
+ resolve ( chunks . map ( chunk => chunk . toString ( 'utf8' ) ) . join ( '' ) ) ;
74
+ clientSession . close ( ) ;
75
+ } ) ;
76
+ } )
77
+ . once ( 'aborted' , fail )
78
+ . once ( 'close' , fail )
79
+ . once ( 'error' , fail ) ;
80
+ } ) ;
81
+ }
82
+
83
+ /**
84
+ * Helper to start a server with HTTP/2 support.
85
+ * Returns stop function to handle server and dns cleanup after tests.
86
+ *
87
+ * Tests requests can be made to foo.example.org:3456
88
+ *
89
+ * @param {object } options - Additional http2 server options.
90
+ * @returns {Promise<(function(): void)|*> } callback
91
+ */
92
+ async function startHttp2Server ( options = { } ) {
93
+ // disabling to avoid UNABLE_TO_VERIFY_LEAF_SIGNATURE for tests
94
+ process . env . NODE_TLS_REJECT_UNAUTHORIZED = '0' ;
95
+
96
+ const servers = await createServersAsync ( {
97
+ http2 : {
98
+ port : 3456 ,
99
+ root : path . join ( __dirname , 'fixtures' ) ,
100
+ key : 'example-org-key.pem' ,
101
+ cert : 'example-org-cert.pem' ,
102
+ ...options
103
+ } ,
104
+ handler : ( req , res ) => {
105
+ const { httpVersion } = req ;
106
+ const { socket : { alpnProtocol } } = httpVersion === '2.0' ? req . stream . session : req ;
107
+ res . writeHead ( 200 , { 'content-type' : 'application/json' } ) ;
108
+ res . end ( JSON . stringify ( {
109
+ alpnProtocol,
110
+ httpVersion
111
+ } ) ) ;
112
+ }
113
+ } ) ;
114
+
115
+ evilDNS . add ( 'foo.example.org' , '0.0.0.0' ) ;
116
+
117
+ return function stop ( ) {
118
+ servers && servers . http2 && servers . http2 . close ( ) ;
119
+ evilDNS . clear ( ) ;
120
+ delete process . env . NODE_TLS_REJECT_UNAUTHORIZED ;
121
+ } ;
122
+ }
123
+
53
124
test ( 'only http' , function ( t ) {
54
125
t . plan ( 5 ) ;
55
126
createServers ( {
@@ -145,6 +216,28 @@ test('only https', function (t) {
145
216
} ) ;
146
217
} ) ;
147
218
219
+ test ( 'only http2' , function ( t ) {
220
+ t . plan ( 4 ) ;
221
+ var time = 4000000 ;
222
+ createServers ( {
223
+ log : console . log ,
224
+ http2 : {
225
+ timeout : time ,
226
+ port : 3456 ,
227
+ root : path . join ( __dirname , 'fixtures' ) ,
228
+ key : 'example-org-key.pem' ,
229
+ cert : 'example-org-cert.pem'
230
+ } ,
231
+ handler : fend
232
+ } , function ( err , servers ) {
233
+ t . error ( err ) ;
234
+ t . equals ( typeof servers , 'object' ) ;
235
+ t . equals ( typeof servers . http2 , 'object' ) ;
236
+ t . equals ( servers . http2 . timeout , time ) ;
237
+ servers . http2 . close ( ) ;
238
+ } ) ;
239
+ } ) ;
240
+
148
241
test ( 'absolute cert path resolution' , function ( t ) {
149
242
t . plan ( 3 ) ;
150
243
createServers ( {
@@ -445,6 +538,50 @@ test('multiple https servers', async function (t) {
445
538
}
446
539
} ) ;
447
540
541
+ test ( 'supports http2-only requests' , async function ( t ) {
542
+ t . plan ( 2 ) ;
543
+ const url = 'https://foo.example.org:3456/' ;
544
+
545
+ let stopServer ;
546
+ try {
547
+ stopServer = await startHttp2Server ( { } ) ;
548
+
549
+ const httpsResponse = await download ( url ) ;
550
+ t . ok ( httpsResponse . includes ( 'Unknown ALPN Protocol' ) ) ;
551
+
552
+ const response = JSON . parse ( await download2 ( url ) ) ;
553
+ t . equals ( response . httpVersion , '2.0' ) ;
554
+
555
+ } catch ( err ) {
556
+ t . error ( err ) ;
557
+ } finally {
558
+ stopServer && stopServer ( ) ;
559
+ }
560
+ } ) ;
561
+
562
+ test ( 'supports http2 and https requests' , async function ( t ) {
563
+ t . plan ( 2 ) ;
564
+ const url = 'https://foo.example.org:3456/' ;
565
+
566
+ let stopServer ;
567
+ try {
568
+ stopServer = await startHttp2Server ( {
569
+ allowHTTP1 : true
570
+ } ) ;
571
+
572
+ const httpsResponse = JSON . parse ( await download ( url ) ) ;
573
+ t . equals ( httpsResponse . httpVersion , '1.1' ) ;
574
+
575
+ const response = JSON . parse ( await download2 ( url ) ) ;
576
+ t . equals ( response . httpVersion , '2.0' ) ;
577
+
578
+ } catch ( err ) {
579
+ t . error ( err ) ;
580
+ } finally {
581
+ stopServer && stopServer ( ) ;
582
+ }
583
+ } ) ;
584
+
448
585
async function testSni ( t , sniConfig , hostNames ) {
449
586
t . plan ( 1 ) ;
450
587
0 commit comments