• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24const assert = require('assert');
25const http = require('http');
26
27// Simple test of Node's HTTP Client mutable headers
28// OutgoingMessage.prototype.setHeader(name, value)
29// OutgoingMessage.prototype.getHeader(name)
30// OutgoingMessage.prototype.removeHeader(name, value)
31// ServerResponse.prototype.statusCode
32// <ClientRequest>.method
33// <ClientRequest>.path
34
35let test = 'headers';
36const content = 'hello world\n';
37const cookies = [
38  'session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT',
39  'prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT'
40];
41
42const s = http.createServer(common.mustCall((req, res) => {
43  switch (test) {
44    case 'headers':
45      // Check that header-related functions work before setting any headers
46      const headers = res.getHeaders();
47      const exoticObj = Object.create(null);
48      assert.deepStrictEqual(headers, exoticObj);
49      assert.deepStrictEqual(res.getHeaderNames(), []);
50      assert.deepStrictEqual(res.hasHeader('Connection'), false);
51      assert.deepStrictEqual(res.getHeader('Connection'), undefined);
52
53      assert.throws(
54        () => res.setHeader(),
55        {
56          code: 'ERR_INVALID_HTTP_TOKEN',
57          name: 'TypeError',
58          message: 'Header name must be a valid HTTP token ["undefined"]'
59        }
60      );
61      assert.throws(
62        () => res.setHeader('someHeader'),
63        {
64          code: 'ERR_HTTP_INVALID_HEADER_VALUE',
65          name: 'TypeError',
66          message: 'Invalid value "undefined" for header "someHeader"'
67        }
68      );
69      assert.throws(
70        () => res.getHeader(),
71        {
72          code: 'ERR_INVALID_ARG_TYPE',
73          name: 'TypeError',
74          message: 'The "name" argument must be of type string. ' +
75                   'Received undefined'
76        }
77      );
78      assert.throws(
79        () => res.removeHeader(),
80        {
81          code: 'ERR_INVALID_ARG_TYPE',
82          name: 'TypeError',
83          message: 'The "name" argument must be of type string. ' +
84                   'Received undefined'
85        }
86      );
87
88      const arrayValues = [1, 2, 3];
89      res.setHeader('x-test-header', 'testing');
90      res.setHeader('X-TEST-HEADER2', 'testing');
91      res.setHeader('set-cookie', cookies);
92      res.setHeader('x-test-array-header', arrayValues);
93
94      assert.strictEqual(res.getHeader('x-test-header'), 'testing');
95      assert.strictEqual(res.getHeader('x-test-header2'), 'testing');
96
97      const headersCopy = res.getHeaders();
98      const expected = {
99        'x-test-header': 'testing',
100        'x-test-header2': 'testing',
101        'set-cookie': cookies,
102        'x-test-array-header': arrayValues
103      };
104      Object.setPrototypeOf(expected, null);
105      assert.deepStrictEqual(headersCopy, expected);
106
107      assert.deepStrictEqual(res.getHeaderNames(),
108                             ['x-test-header', 'x-test-header2',
109                              'set-cookie', 'x-test-array-header']);
110
111      assert.strictEqual(res.hasHeader('x-test-header2'), true);
112      assert.strictEqual(res.hasHeader('X-TEST-HEADER2'), true);
113      assert.strictEqual(res.hasHeader('X-Test-Header2'), true);
114      [
115        undefined,
116        null,
117        true,
118        {},
119        { toString: () => 'X-TEST-HEADER2' },
120        () => { }
121      ].forEach((val) => {
122        assert.throws(
123          () => res.hasHeader(val),
124          {
125            code: 'ERR_INVALID_ARG_TYPE',
126            name: 'TypeError',
127            message: 'The "name" argument must be of type string.' +
128                     common.invalidArgTypeHelper(val)
129          }
130        );
131      });
132
133      res.removeHeader('x-test-header2');
134
135      assert.strictEqual(res.hasHeader('x-test-header2'), false);
136      assert.strictEqual(res.hasHeader('X-TEST-HEADER2'), false);
137      assert.strictEqual(res.hasHeader('X-Test-Header2'), false);
138      break;
139
140    case 'contentLength':
141      res.setHeader('content-length', content.length);
142      assert.strictEqual(res.getHeader('Content-Length'), content.length);
143      break;
144
145    case 'transferEncoding':
146      res.setHeader('transfer-encoding', 'chunked');
147      assert.strictEqual(res.getHeader('Transfer-Encoding'), 'chunked');
148      break;
149
150    case 'writeHead':
151      res.statusCode = 404;
152      res.setHeader('x-foo', 'keyboard cat');
153      res.writeHead(200, { 'x-foo': 'bar', 'x-bar': 'baz' });
154      break;
155
156    default:
157      assert.fail('Unknown test');
158  }
159
160  res.statusCode = 201;
161  res.end(content);
162}, 4));
163
164s.listen(0, nextTest);
165
166
167function nextTest() {
168  if (test === 'end') {
169    return s.close();
170  }
171
172  let bufferedResponse = '';
173
174  http.get({ port: s.address().port }, common.mustCall((response) => {
175    switch (test) {
176      case 'headers':
177        assert.strictEqual(response.statusCode, 201);
178        assert.strictEqual(response.headers['x-test-header'], 'testing');
179        assert.strictEqual(response.headers['x-test-array-header'],
180                           [1, 2, 3].join(', '));
181        assert.deepStrictEqual(cookies, response.headers['set-cookie']);
182        assert.strictEqual(response.headers['x-test-header2'], undefined);
183        test = 'contentLength';
184        break;
185
186      case 'contentLength':
187        assert.strictEqual(+response.headers['content-length'], content.length);
188        test = 'transferEncoding';
189        break;
190
191      case 'transferEncoding':
192        assert.strictEqual(response.headers['transfer-encoding'], 'chunked');
193        test = 'writeHead';
194        break;
195
196      case 'writeHead':
197        assert.strictEqual(response.headers['x-foo'], 'bar');
198        assert.strictEqual(response.headers['x-bar'], 'baz');
199        assert.strictEqual(response.statusCode, 200);
200        test = 'end';
201        break;
202
203      default:
204        assert.fail('Unknown test');
205    }
206
207    response.setEncoding('utf8');
208    response.on('data', (s) => {
209      bufferedResponse += s;
210    });
211
212    response.on('end', common.mustCall(() => {
213      assert.strictEqual(bufferedResponse, content);
214      common.mustCall(nextTest)();
215    }));
216  }));
217}
218