1'use strict'; 2 3const common = require('../common'); 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6const assert = require('assert'); 7const h2 = require('http2'); 8 9// Http2ServerResponse should support checking and reading custom headers 10 11const server = h2.createServer(); 12server.listen(0, common.mustCall(function() { 13 const port = server.address().port; 14 server.once('request', common.mustCall(function(request, response) { 15 const real = 'foo-bar'; 16 const fake = 'bar-foo'; 17 const denormalised = ` ${real.toUpperCase()}\n\t`; 18 const expectedValue = 'abc123'; 19 20 response.setHeader(real, expectedValue); 21 22 assert.strictEqual(response.hasHeader(real), true); 23 assert.strictEqual(response.hasHeader(fake), false); 24 assert.strictEqual(response.hasHeader(denormalised), true); 25 assert.strictEqual(response.getHeader(real), expectedValue); 26 assert.strictEqual(response.getHeader(denormalised), expectedValue); 27 assert.strictEqual(response.getHeader(fake), undefined); 28 29 response.removeHeader(fake); 30 assert.strictEqual(response.hasHeader(fake), false); 31 32 response.setHeader(real, expectedValue); 33 assert.strictEqual(response.getHeader(real), expectedValue); 34 assert.strictEqual(response.hasHeader(real), true); 35 response.removeHeader(real); 36 assert.strictEqual(response.hasHeader(real), false); 37 38 response.setHeader(denormalised, expectedValue); 39 assert.strictEqual(response.getHeader(denormalised), expectedValue); 40 assert.strictEqual(response.hasHeader(denormalised), true); 41 response.removeHeader(denormalised); 42 assert.strictEqual(response.hasHeader(denormalised), false); 43 44 ['hasHeader', 'getHeader', 'removeHeader'].forEach((fnName) => { 45 assert.throws( 46 () => response[fnName](), 47 { 48 code: 'ERR_INVALID_ARG_TYPE', 49 name: 'TypeError', 50 message: 'The "name" argument must be of type string. Received ' + 51 'undefined' 52 } 53 ); 54 }); 55 56 [ 57 ':status', 58 ':method', 59 ':path', 60 ':authority', 61 ':scheme', 62 ].forEach((header) => assert.throws( 63 () => response.setHeader(header, 'foobar'), 64 { 65 code: 'ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', 66 name: 'TypeError', 67 message: 'Cannot set HTTP/2 pseudo-headers' 68 }) 69 ); 70 assert.throws(() => { 71 response.setHeader(real, null); 72 }, { 73 code: 'ERR_HTTP2_INVALID_HEADER_VALUE', 74 name: 'TypeError', 75 message: 'Invalid value "null" for header "foo-bar"' 76 }); 77 assert.throws(() => { 78 response.setHeader(real, undefined); 79 }, { 80 code: 'ERR_HTTP2_INVALID_HEADER_VALUE', 81 name: 'TypeError', 82 message: 'Invalid value "undefined" for header "foo-bar"' 83 }); 84 assert.throws( 85 () => response.setHeader(), // Header name undefined 86 { 87 code: 'ERR_INVALID_ARG_TYPE', 88 name: 'TypeError', 89 message: 'The "name" argument must be of type string. Received ' + 90 'undefined' 91 } 92 ); 93 assert.throws( 94 () => response.setHeader(''), 95 { 96 code: 'ERR_INVALID_HTTP_TOKEN', 97 name: 'TypeError', 98 message: 'Header name must be a valid HTTP token [""]' 99 } 100 ); 101 102 response.setHeader(real, expectedValue); 103 const expectedHeaderNames = [real]; 104 assert.deepStrictEqual(response.getHeaderNames(), expectedHeaderNames); 105 const expectedHeaders = Object.create(null); 106 expectedHeaders[real] = expectedValue; 107 assert.deepStrictEqual(response.getHeaders(), expectedHeaders); 108 109 response.getHeaders()[fake] = fake; 110 assert.strictEqual(response.hasHeader(fake), false); 111 assert.strictEqual(Object.getPrototypeOf(response.getHeaders()), null); 112 113 assert.strictEqual(response.sendDate, true); 114 response.sendDate = false; 115 assert.strictEqual(response.sendDate, false); 116 117 response.sendDate = true; 118 assert.strictEqual(response.sendDate, true); 119 response.removeHeader('Date'); 120 assert.strictEqual(response.sendDate, false); 121 122 response.on('finish', common.mustCall(function() { 123 assert.strictEqual(response.headersSent, true); 124 125 assert.throws( 126 () => response.setHeader(real, expectedValue), 127 { 128 code: 'ERR_HTTP2_HEADERS_SENT', 129 name: 'Error', 130 message: 'Response has already been initiated.' 131 } 132 ); 133 assert.throws( 134 () => response.removeHeader(real, expectedValue), 135 { 136 code: 'ERR_HTTP2_HEADERS_SENT', 137 name: 'Error', 138 message: 'Response has already been initiated.' 139 } 140 ); 141 142 process.nextTick(() => { 143 assert.throws( 144 () => response.setHeader(real, expectedValue), 145 { 146 code: 'ERR_HTTP2_HEADERS_SENT', 147 name: 'Error', 148 message: 'Response has already been initiated.' 149 } 150 ); 151 assert.throws( 152 () => response.removeHeader(real, expectedValue), 153 { 154 code: 'ERR_HTTP2_HEADERS_SENT', 155 name: 'Error', 156 message: 'Response has already been initiated.' 157 } 158 ); 159 160 assert.strictEqual(response.headersSent, true); 161 server.close(); 162 }); 163 })); 164 response.end(); 165 })); 166 167 const url = `http://localhost:${port}`; 168 const client = h2.connect(url, common.mustCall(function() { 169 const headers = { 170 ':path': '/', 171 ':method': 'GET', 172 ':scheme': 'http', 173 ':authority': `localhost:${port}` 174 }; 175 const request = client.request(headers); 176 request.on('end', common.mustCall(function() { 177 client.close(); 178 })); 179 request.end(); 180 request.resume(); 181 })); 182})); 183