|
1 var expect = require('chai').expect; |
|
2 var util = require('./util'); |
|
3 var fs = require('fs'); |
|
4 var path = require('path'); |
|
5 |
|
6 var http2 = require('../lib/http'); |
|
7 var https = require('https'); |
|
8 |
|
9 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; |
|
10 |
|
11 var options = { |
|
12 key: fs.readFileSync(path.join(__dirname, '../example/localhost.key')), |
|
13 cert: fs.readFileSync(path.join(__dirname, '../example/localhost.crt')), |
|
14 log: util.log |
|
15 }; |
|
16 |
|
17 http2.globalAgent = new http2.Agent({ log: util.log }); |
|
18 |
|
19 describe('http.js', function() { |
|
20 describe('Server', function() { |
|
21 describe('new Server(options)', function() { |
|
22 it('should throw if called without \'plain\' or TLS options', function() { |
|
23 expect(function() { |
|
24 new http2.Server(); |
|
25 }).to.throw(Error); |
|
26 expect(function() { |
|
27 http2.createServer(util.noop); |
|
28 }).to.throw(Error); |
|
29 }); |
|
30 }); |
|
31 describe('property `timeout`', function() { |
|
32 it('should be a proxy for the backing HTTPS server\'s `timeout` property', function() { |
|
33 var server = new http2.Server(options); |
|
34 var backingServer = server._server; |
|
35 var newTimeout = 10; |
|
36 server.timeout = newTimeout; |
|
37 expect(server.timeout).to.be.equal(newTimeout); |
|
38 expect(backingServer.timeout).to.be.equal(newTimeout); |
|
39 }); |
|
40 }); |
|
41 describe('method `setTimeout(timeout, [callback])`', function() { |
|
42 it('should be a proxy for the backing HTTPS server\'s `setTimeout` method', function() { |
|
43 var server = new http2.Server(options); |
|
44 var backingServer = server._server; |
|
45 var newTimeout = 10; |
|
46 var newCallback = util.noop; |
|
47 backingServer.setTimeout = function(timeout, callback) { |
|
48 expect(timeout).to.be.equal(newTimeout); |
|
49 expect(callback).to.be.equal(newCallback); |
|
50 }; |
|
51 server.setTimeout(newTimeout, newCallback); |
|
52 }); |
|
53 }); |
|
54 }); |
|
55 describe('Agent', function() { |
|
56 describe('property `maxSockets`', function() { |
|
57 it('should be a proxy for the backing HTTPS agent\'s `maxSockets` property', function() { |
|
58 var agent = new http2.Agent({ log: util.log }); |
|
59 var backingAgent = agent._httpsAgent; |
|
60 var newMaxSockets = backingAgent.maxSockets + 1; |
|
61 agent.maxSockets = newMaxSockets; |
|
62 expect(agent.maxSockets).to.be.equal(newMaxSockets); |
|
63 expect(backingAgent.maxSockets).to.be.equal(newMaxSockets); |
|
64 }); |
|
65 }); |
|
66 describe('method `request(options, [callback])`', function() { |
|
67 it('should throw when trying to use with \'http\' scheme', function() { |
|
68 expect(function() { |
|
69 var agent = new http2.Agent({ log: util.log }); |
|
70 agent.request({ protocol: 'http:' }); |
|
71 }).to.throw(Error); |
|
72 }); |
|
73 }); |
|
74 }); |
|
75 describe('OutgoingRequest', function() { |
|
76 function testFallbackProxyMethod(name, originalArguments, done) { |
|
77 var request = new http2.OutgoingRequest(); |
|
78 |
|
79 // When in HTTP/2 mode, this call should be ignored |
|
80 request.stream = { reset: util.noop }; |
|
81 request[name].apply(request, originalArguments); |
|
82 delete request.stream; |
|
83 |
|
84 // When in fallback mode, this call should be forwarded |
|
85 request[name].apply(request, originalArguments); |
|
86 var mockFallbackRequest = { on: util.noop }; |
|
87 mockFallbackRequest[name] = function() { |
|
88 expect(arguments).to.deep.equal(originalArguments); |
|
89 done(); |
|
90 }; |
|
91 request._fallback(mockFallbackRequest); |
|
92 } |
|
93 describe('method `setNoDelay(noDelay)`', function() { |
|
94 it('should act as a proxy for the backing HTTPS agent\'s `setNoDelay` method', function(done) { |
|
95 testFallbackProxyMethod('setNoDelay', [true], done); |
|
96 }); |
|
97 }); |
|
98 describe('method `setSocketKeepAlive(enable, initialDelay)`', function() { |
|
99 it('should act as a proxy for the backing HTTPS agent\'s `setSocketKeepAlive` method', function(done) { |
|
100 testFallbackProxyMethod('setSocketKeepAlive', [true, util.random(10, 100)], done); |
|
101 }); |
|
102 }); |
|
103 describe('method `setTimeout(timeout, [callback])`', function() { |
|
104 it('should act as a proxy for the backing HTTPS agent\'s `setTimeout` method', function(done) { |
|
105 testFallbackProxyMethod('setTimeout', [util.random(10, 100), util.noop], done); |
|
106 }); |
|
107 }); |
|
108 describe('method `abort()`', function() { |
|
109 it('should act as a proxy for the backing HTTPS agent\'s `abort` method', function(done) { |
|
110 testFallbackProxyMethod('abort', [], done); |
|
111 }); |
|
112 }); |
|
113 }); |
|
114 describe('test scenario', function() { |
|
115 describe('simple request', function() { |
|
116 it('should work as expected', function(done) { |
|
117 var path = '/x'; |
|
118 var message = 'Hello world'; |
|
119 |
|
120 var server = http2.createServer(options, function(request, response) { |
|
121 expect(request.url).to.equal(path); |
|
122 response.end(message); |
|
123 }); |
|
124 |
|
125 server.listen(1234, function() { |
|
126 http2.get('https://localhost:1234' + path, function(response) { |
|
127 response.on('readable', function() { |
|
128 expect(response.read().toString()).to.equal(message); |
|
129 server.close(); |
|
130 done(); |
|
131 }); |
|
132 }); |
|
133 }); |
|
134 }); |
|
135 }); |
|
136 describe('request with payload', function() { |
|
137 it('should work as expected', function(done) { |
|
138 var path = '/x'; |
|
139 var message = 'Hello world'; |
|
140 |
|
141 var server = http2.createServer(options, function(request, response) { |
|
142 expect(request.url).to.equal(path); |
|
143 request.once('readable', function() { |
|
144 expect(request.read().toString()).to.equal(message); |
|
145 response.end(); |
|
146 }); |
|
147 }); |
|
148 |
|
149 server.listen(1240, function() { |
|
150 var request = http2.request({ |
|
151 host: 'localhost', |
|
152 port: 1240, |
|
153 path: path |
|
154 }); |
|
155 request.write(message); |
|
156 request.end(); |
|
157 request.on('response', function() { |
|
158 server.close(); |
|
159 done(); |
|
160 }); |
|
161 }); |
|
162 }); |
|
163 }); |
|
164 describe('request with custom status code and headers', function() { |
|
165 it('should work as expected', function(done) { |
|
166 var path = '/x'; |
|
167 var message = 'Hello world'; |
|
168 var headerName = 'name'; |
|
169 var headerValue = 'value'; |
|
170 |
|
171 var server = http2.createServer(options, function(request, response) { |
|
172 // Request URL and headers |
|
173 expect(request.url).to.equal(path); |
|
174 expect(request.headers[headerName]).to.equal(headerValue); |
|
175 |
|
176 // A header to be overwritten later |
|
177 response.setHeader(headerName, 'to be overwritten'); |
|
178 expect(response.getHeader(headerName)).to.equal('to be overwritten'); |
|
179 |
|
180 // A header to be deleted |
|
181 response.setHeader('nonexistent', 'x'); |
|
182 response.removeHeader('nonexistent'); |
|
183 expect(response.getHeader('nonexistent')).to.equal(undefined); |
|
184 |
|
185 // Don't send date |
|
186 response.sendDate = false; |
|
187 |
|
188 // Specifying more headers, the status code and a reason phrase with `writeHead` |
|
189 var moreHeaders = {}; |
|
190 moreHeaders[headerName] = headerValue; |
|
191 response.writeHead(600, 'to be discarded', moreHeaders); |
|
192 expect(response.getHeader(headerName)).to.equal(headerValue); |
|
193 |
|
194 // Empty response body |
|
195 response.end(message); |
|
196 }); |
|
197 |
|
198 server.listen(1239, function() { |
|
199 var headers = {}; |
|
200 headers[headerName] = headerValue; |
|
201 var request = http2.request({ |
|
202 host: 'localhost', |
|
203 port: 1239, |
|
204 path: path, |
|
205 headers: headers |
|
206 }); |
|
207 request.end(); |
|
208 request.on('response', function(response) { |
|
209 expect(response.headers[headerName]).to.equal(headerValue); |
|
210 expect(response.headers['nonexistent']).to.equal(undefined); |
|
211 expect(response.headers['date']).to.equal(undefined); |
|
212 response.on('readable', function() { |
|
213 expect(response.read().toString()).to.equal(message); |
|
214 server.close(); |
|
215 done(); |
|
216 }); |
|
217 }); |
|
218 }); |
|
219 }); |
|
220 }); |
|
221 describe('request over plain TCP', function() { |
|
222 it('should work as expected', function(done) { |
|
223 var path = '/x'; |
|
224 var message = 'Hello world'; |
|
225 |
|
226 var server = http2.createServer({ |
|
227 plain: true, |
|
228 log: util.log |
|
229 }, function(request, response) { |
|
230 expect(request.url).to.equal(path); |
|
231 response.end(message); |
|
232 }); |
|
233 |
|
234 server.listen(1237, function() { |
|
235 var request = http2.request({ |
|
236 plain: true, |
|
237 host: 'localhost', |
|
238 port: 1237, |
|
239 path: path |
|
240 }, function(response) { |
|
241 response.on('readable', function() { |
|
242 expect(response.read().toString()).to.equal(message); |
|
243 server.close(); |
|
244 done(); |
|
245 }); |
|
246 }); |
|
247 request.end(); |
|
248 }); |
|
249 }); |
|
250 }); |
|
251 describe('request to an HTTPS/1 server', function() { |
|
252 it('should fall back to HTTPS/1 successfully', function(done) { |
|
253 var path = '/x'; |
|
254 var message = 'Hello world'; |
|
255 |
|
256 var server = https.createServer(options, function(request, response) { |
|
257 expect(request.url).to.equal(path); |
|
258 response.end(message); |
|
259 }); |
|
260 |
|
261 server.listen(5678, function() { |
|
262 http2.get('https://localhost:5678' + path, function(response) { |
|
263 response.on('readable', function() { |
|
264 expect(response.read().toString()).to.equal(message); |
|
265 done(); |
|
266 }); |
|
267 }); |
|
268 }); |
|
269 }); |
|
270 }); |
|
271 describe('HTTPS/1 request to a HTTP/2 server', function() { |
|
272 it('should fall back to HTTPS/1 successfully', function(done) { |
|
273 var path = '/x'; |
|
274 var message = 'Hello world'; |
|
275 |
|
276 var server = http2.createServer(options, function(request, response) { |
|
277 expect(request.url).to.equal(path); |
|
278 response.end(message); |
|
279 }); |
|
280 |
|
281 server.listen(1236, function() { |
|
282 https.get('https://localhost:1236' + path, function(response) { |
|
283 response.on('readable', function() { |
|
284 expect(response.read().toString()).to.equal(message); |
|
285 done(); |
|
286 }); |
|
287 }); |
|
288 }); |
|
289 }); |
|
290 }); |
|
291 describe('two parallel request', function() { |
|
292 it('should work as expected', function(done) { |
|
293 var path = '/x'; |
|
294 var message = 'Hello world'; |
|
295 |
|
296 var server = http2.createServer(options, function(request, response) { |
|
297 expect(request.url).to.equal(path); |
|
298 response.end(message); |
|
299 }); |
|
300 |
|
301 server.listen(1237, function() { |
|
302 done = util.callNTimes(2, done); |
|
303 // 1. request |
|
304 http2.get('https://localhost:1237' + path, function(response) { |
|
305 response.on('readable', function() { |
|
306 expect(response.read().toString()).to.equal(message); |
|
307 done(); |
|
308 }); |
|
309 }); |
|
310 // 2. request |
|
311 http2.get('https://localhost:1237' + path, function(response) { |
|
312 response.on('readable', function() { |
|
313 expect(response.read().toString()).to.equal(message); |
|
314 done(); |
|
315 }); |
|
316 }); |
|
317 }); |
|
318 }); |
|
319 }); |
|
320 describe('two subsequent request', function() { |
|
321 it('should use the same HTTP/2 connection', function(done) { |
|
322 var path = '/x'; |
|
323 var message = 'Hello world'; |
|
324 |
|
325 var server = http2.createServer(options, function(request, response) { |
|
326 expect(request.url).to.equal(path); |
|
327 response.end(message); |
|
328 }); |
|
329 |
|
330 server.listen(1238, function() { |
|
331 // 1. request |
|
332 http2.get('https://localhost:1238' + path, function(response) { |
|
333 response.on('readable', function() { |
|
334 expect(response.read().toString()).to.equal(message); |
|
335 |
|
336 // 2. request |
|
337 http2.get('https://localhost:1238' + path, function(response) { |
|
338 response.on('readable', function() { |
|
339 expect(response.read().toString()).to.equal(message); |
|
340 done(); |
|
341 }); |
|
342 }); |
|
343 }); |
|
344 }); |
|
345 }); |
|
346 }); |
|
347 }); |
|
348 describe('request and response with trailers', function() { |
|
349 it('should work as expected', function(done) { |
|
350 var path = '/x'; |
|
351 var message = 'Hello world'; |
|
352 var requestTrailers = { 'content-md5': 'x' }; |
|
353 var responseTrailers = { 'content-md5': 'y' }; |
|
354 |
|
355 var server = http2.createServer(options, function(request, response) { |
|
356 expect(request.url).to.equal(path); |
|
357 request.on('data', util.noop); |
|
358 request.once('end', function() { |
|
359 expect(request.trailers).to.deep.equal(requestTrailers); |
|
360 response.write(message); |
|
361 response.addTrailers(responseTrailers); |
|
362 response.end(); |
|
363 }); |
|
364 }); |
|
365 |
|
366 server.listen(1241, function() { |
|
367 var request = http2.request('https://localhost:1241' + path); |
|
368 request.addTrailers(requestTrailers); |
|
369 request.end(); |
|
370 request.on('response', function(response) { |
|
371 response.on('data', util.noop); |
|
372 response.once('end', function() { |
|
373 expect(response.trailers).to.deep.equal(responseTrailers); |
|
374 done(); |
|
375 }); |
|
376 }); |
|
377 }); |
|
378 }); |
|
379 }); |
|
380 describe('server push', function() { |
|
381 it('should work as expected', function(done) { |
|
382 var path = '/x'; |
|
383 var message = 'Hello world'; |
|
384 var pushedPath = '/y'; |
|
385 var pushedMessage = 'Hello world 2'; |
|
386 |
|
387 var server = http2.createServer(options, function(request, response) { |
|
388 expect(request.url).to.equal(path); |
|
389 var push1 = response.push('/y'); |
|
390 push1.end(pushedMessage); |
|
391 var push2 = response.push({ path: '/y', protocol: 'https:' }); |
|
392 push2.end(pushedMessage); |
|
393 response.end(message); |
|
394 }); |
|
395 |
|
396 server.listen(1235, function() { |
|
397 var request = http2.get('https://localhost:1235' + path); |
|
398 done = util.callNTimes(5, done); |
|
399 |
|
400 request.on('response', function(response) { |
|
401 response.on('readable', function() { |
|
402 expect(response.read().toString()).to.equal(message); |
|
403 done(); |
|
404 }); |
|
405 response.on('end', done); |
|
406 }); |
|
407 |
|
408 request.on('push', function(promise) { |
|
409 expect(promise.url).to.be.equal(pushedPath); |
|
410 promise.on('response', function(pushStream) { |
|
411 pushStream.on('readable', function() { |
|
412 expect(pushStream.read().toString()).to.equal(pushedMessage); |
|
413 done(); |
|
414 }); |
|
415 pushStream.on('end', done); |
|
416 }); |
|
417 }); |
|
418 }); |
|
419 }); |
|
420 }); |
|
421 }); |
|
422 }); |