1'use strict'; 2 3const { 4 mustCall, 5 mustNotCall, 6 hasCrypto, 7 platformTimeout, 8 skip 9} = require('../common'); 10if (!hasCrypto) 11 skip('missing crypto'); 12const { strictEqual } = require('assert'); 13const { 14 createServer, 15 connect, 16 constants: { 17 HTTP2_HEADER_STATUS, 18 HTTP_STATUS_OK 19 } 20} = require('http2'); 21 22{ 23 // Http2ServerResponse.end accepts chunk, encoding, cb as args 24 // It may be invoked repeatedly without throwing errors 25 // but callback will only be called once 26 const server = createServer(mustCall((request, response) => { 27 response.end('end', 'utf8', mustCall(() => { 28 response.end(mustCall()); 29 process.nextTick(() => { 30 response.end(mustCall()); 31 server.close(); 32 }); 33 })); 34 response.on('finish', mustCall(() => { 35 response.end(mustCall()); 36 })); 37 response.end(mustCall()); 38 })); 39 server.listen(0, mustCall(() => { 40 let data = ''; 41 const { port } = server.address(); 42 const url = `http://localhost:${port}`; 43 const client = connect(url, mustCall(() => { 44 const headers = { 45 ':path': '/', 46 ':method': 'GET', 47 ':scheme': 'http', 48 ':authority': `localhost:${port}` 49 }; 50 const request = client.request(headers); 51 request.setEncoding('utf8'); 52 request.on('data', (chunk) => (data += chunk)); 53 request.on('end', mustCall(() => { 54 strictEqual(data, 'end'); 55 client.close(); 56 })); 57 request.end(); 58 request.resume(); 59 })); 60 })); 61} 62 63{ 64 // Http2ServerResponse.end should return self after end 65 const server = createServer(mustCall((request, response) => { 66 strictEqual(response, response.end()); 67 strictEqual(response, response.end()); 68 server.close(); 69 })); 70 server.listen(0, mustCall(() => { 71 const { port } = server.address(); 72 const url = `http://localhost:${port}`; 73 const client = connect(url, mustCall(() => { 74 const headers = { 75 ':path': '/', 76 ':method': 'GET', 77 ':scheme': 'http', 78 ':authority': `localhost:${port}` 79 }; 80 const request = client.request(headers); 81 request.setEncoding('utf8'); 82 request.on('end', mustCall(() => { 83 client.close(); 84 })); 85 request.end(); 86 request.resume(); 87 })); 88 })); 89} 90 91{ 92 // Http2ServerResponse.end can omit encoding arg, sets it to utf-8 93 const server = createServer(mustCall((request, response) => { 94 response.end('test\uD83D\uDE00', mustCall(() => { 95 server.close(); 96 })); 97 })); 98 server.listen(0, mustCall(() => { 99 let data = ''; 100 const { port } = server.address(); 101 const url = `http://localhost:${port}`; 102 const client = connect(url, mustCall(() => { 103 const headers = { 104 ':path': '/', 105 ':method': 'GET', 106 ':scheme': 'http', 107 ':authority': `localhost:${port}` 108 }; 109 const request = client.request(headers); 110 request.setEncoding('utf8'); 111 request.on('data', (chunk) => (data += chunk)); 112 request.on('end', mustCall(() => { 113 strictEqual(data, 'test\uD83D\uDE00'); 114 client.close(); 115 })); 116 request.end(); 117 request.resume(); 118 })); 119 })); 120} 121 122{ 123 // Http2ServerResponse.end can omit chunk & encoding args 124 const server = createServer(mustCall((request, response) => { 125 response.end(mustCall(() => { 126 server.close(); 127 })); 128 })); 129 server.listen(0, mustCall(() => { 130 const { port } = server.address(); 131 const url = `http://localhost:${port}`; 132 const client = connect(url, mustCall(() => { 133 const headers = { 134 ':path': '/', 135 ':method': 'GET', 136 ':scheme': 'http', 137 ':authority': `localhost:${port}` 138 }; 139 const request = client.request(headers); 140 request.on('data', mustNotCall()); 141 request.on('end', mustCall(() => client.close())); 142 request.end(); 143 request.resume(); 144 })); 145 })); 146} 147 148{ 149 // Http2ServerResponse.end is necessary on HEAD requests in compat 150 // for http1 compatibility 151 const server = createServer(mustCall((request, response) => { 152 strictEqual(response.writableEnded, false); 153 strictEqual(response.finished, false); 154 response.writeHead(HTTP_STATUS_OK, { foo: 'bar' }); 155 strictEqual(response.finished, false); 156 response.end('data', mustCall()); 157 strictEqual(response.writableEnded, true); 158 strictEqual(response.finished, true); 159 })); 160 server.listen(0, mustCall(() => { 161 const { port } = server.address(); 162 const url = `http://localhost:${port}`; 163 const client = connect(url, mustCall(() => { 164 const headers = { 165 ':path': '/', 166 ':method': 'HEAD', 167 ':scheme': 'http', 168 ':authority': `localhost:${port}` 169 }; 170 const request = client.request(headers); 171 request.on('response', mustCall((headers, flags) => { 172 strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK); 173 strictEqual(flags, 5); // The end of stream flag is set 174 strictEqual(headers.foo, 'bar'); 175 })); 176 request.on('data', mustNotCall()); 177 request.on('end', mustCall(() => { 178 client.close(); 179 server.close(); 180 })); 181 request.end(); 182 request.resume(); 183 })); 184 })); 185} 186 187{ 188 // .end should trigger 'end' event on request if user did not attempt 189 // to read from the request 190 const server = createServer(mustCall((request, response) => { 191 request.on('end', mustCall()); 192 response.end(); 193 })); 194 server.listen(0, mustCall(() => { 195 const { port } = server.address(); 196 const url = `http://localhost:${port}`; 197 const client = connect(url, mustCall(() => { 198 const headers = { 199 ':path': '/', 200 ':method': 'HEAD', 201 ':scheme': 'http', 202 ':authority': `localhost:${port}` 203 }; 204 const request = client.request(headers); 205 request.on('data', mustNotCall()); 206 request.on('end', mustCall(() => { 207 client.close(); 208 server.close(); 209 })); 210 request.end(); 211 request.resume(); 212 })); 213 })); 214} 215 216 217{ 218 // Should be able to call .end with cb from stream 'close' 219 const server = createServer(mustCall((request, response) => { 220 response.writeHead(HTTP_STATUS_OK, { foo: 'bar' }); 221 response.stream.on('close', mustCall(() => { 222 response.end(mustCall()); 223 })); 224 })); 225 server.listen(0, mustCall(() => { 226 const { port } = server.address(); 227 const url = `http://localhost:${port}`; 228 const client = connect(url, mustCall(() => { 229 const headers = { 230 ':path': '/', 231 ':method': 'HEAD', 232 ':scheme': 'http', 233 ':authority': `localhost:${port}` 234 }; 235 const request = client.request(headers); 236 request.on('response', mustCall((headers, flags) => { 237 strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK); 238 strictEqual(flags, 5); // The end of stream flag is set 239 strictEqual(headers.foo, 'bar'); 240 })); 241 request.on('data', mustNotCall()); 242 request.on('end', mustCall(() => { 243 client.close(); 244 server.close(); 245 })); 246 request.end(); 247 request.resume(); 248 })); 249 })); 250} 251 252{ 253 // Should be able to respond to HEAD request after timeout 254 const server = createServer(mustCall((request, response) => { 255 setTimeout(mustCall(() => { 256 response.writeHead(HTTP_STATUS_OK, { foo: 'bar' }); 257 response.end('data', mustCall()); 258 }), platformTimeout(10)); 259 })); 260 server.listen(0, mustCall(() => { 261 const { port } = server.address(); 262 const url = `http://localhost:${port}`; 263 const client = connect(url, mustCall(() => { 264 const headers = { 265 ':path': '/', 266 ':method': 'HEAD', 267 ':scheme': 'http', 268 ':authority': `localhost:${port}` 269 }; 270 const request = client.request(headers); 271 request.on('response', mustCall((headers, flags) => { 272 strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK); 273 strictEqual(flags, 5); // The end of stream flag is set 274 strictEqual(headers.foo, 'bar'); 275 })); 276 request.on('data', mustNotCall()); 277 request.on('end', mustCall(() => { 278 client.close(); 279 server.close(); 280 })); 281 request.end(); 282 request.resume(); 283 })); 284 })); 285} 286 287{ 288 // Finish should only trigger after 'end' is called 289 const server = createServer(mustCall((request, response) => { 290 let finished = false; 291 response.writeHead(HTTP_STATUS_OK, { foo: 'bar' }); 292 response.on('finish', mustCall(() => { 293 finished = false; 294 })); 295 response.end('data', mustCall(() => { 296 strictEqual(finished, false); 297 response.end('data', mustCall()); 298 })); 299 })); 300 server.listen(0, mustCall(() => { 301 const { port } = server.address(); 302 const url = `http://localhost:${port}`; 303 const client = connect(url, mustCall(() => { 304 const headers = { 305 ':path': '/', 306 ':method': 'HEAD', 307 ':scheme': 'http', 308 ':authority': `localhost:${port}` 309 }; 310 const request = client.request(headers); 311 request.on('response', mustCall((headers, flags) => { 312 strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK); 313 strictEqual(flags, 5); // The end of stream flag is set 314 strictEqual(headers.foo, 'bar'); 315 })); 316 request.on('data', mustNotCall()); 317 request.on('end', mustCall(() => { 318 client.close(); 319 server.close(); 320 })); 321 request.end(); 322 request.resume(); 323 })); 324 })); 325} 326 327{ 328 // Should be able to respond to HEAD with just .end 329 const server = createServer(mustCall((request, response) => { 330 response.end('data', mustCall()); 331 response.end(mustCall()); 332 })); 333 server.listen(0, mustCall(() => { 334 const { port } = server.address(); 335 const url = `http://localhost:${port}`; 336 const client = connect(url, mustCall(() => { 337 const headers = { 338 ':path': '/', 339 ':method': 'HEAD', 340 ':scheme': 'http', 341 ':authority': `localhost:${port}` 342 }; 343 const request = client.request(headers); 344 request.on('response', mustCall((headers, flags) => { 345 strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK); 346 strictEqual(flags, 5); // The end of stream flag is set 347 })); 348 request.on('data', mustNotCall()); 349 request.on('end', mustCall(() => { 350 client.close(); 351 server.close(); 352 })); 353 request.end(); 354 request.resume(); 355 })); 356 })); 357} 358