summaryrefslogtreecommitdiff
path: root/test/parallel/test-http2-altsvc.js
blob: 3a1a1cf62991b7f9f6ff6990682660567089a38a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
'use strict';

const common = require('../common');
if (!common.hasCrypto)
  common.skip('missing crypto');

const assert = require('assert');
const http2 = require('http2');
const { URL } = require('url');
const Countdown = require('../common/countdown');

const server = http2.createServer();
server.on('stream', common.mustCall((stream) => {
  stream.session.altsvc('h2=":8000"', stream.id);
  stream.respond();
  stream.end('ok');
}));
server.on('session', common.mustCall((session) => {
  // Origin may be specified by string, URL object, or object with an
  // origin property. For string and URL object, origin is guaranteed
  // to be an ASCII serialized origin. For object with an origin
  // property, it is up to the user to ensure proper serialization.
  session.altsvc('h2=":8000"', 'https://example.org:8111/this');
  session.altsvc('h2=":8000"', new URL('https://example.org:8111/this'));
  session.altsvc('h2=":8000"', { origin: 'https://example.org:8111' });

  // Won't error, but won't send anything because the stream does not exist
  session.altsvc('h2=":8000"', 3);

  // Will error because the numeric stream id is out of valid range
  [0, -1, 1.1, 0xFFFFFFFF + 1, Infinity, -Infinity].forEach((input) => {
    assert.throws(
      () => session.altsvc('h2=":8000"', input),
      {
        code: 'ERR_OUT_OF_RANGE',
        name: 'RangeError',
        message: 'The value of "originOrStream" is out of ' +
                 `range. It must be > 0 && < 4294967296. Received ${input}`
      }
    );
  });

  // First argument must be a string
  [0, {}, [], null, Infinity].forEach((input) => {
    assert.throws(
      () => session.altsvc(input),
      {
        code: 'ERR_INVALID_ARG_TYPE',
        name: 'TypeError'
      }
    );
  });

  ['\u0001', 'h2="\uff20"', '👀'].forEach((input) => {
    assert.throws(
      () => session.altsvc(input),
      {
        code: 'ERR_INVALID_CHAR',
        name: 'TypeError',
        message: 'Invalid character in alt'
      }
    );
  });

  [{}, [], true].forEach((input) => {
    assert.throws(
      () => session.altsvc('clear', input),
      {
        code: 'ERR_INVALID_ARG_TYPE',
        name: 'TypeError'
      }
    );
  });

  [
    'abc:',
    new URL('abc:'),
    { origin: 'null' },
    { origin: '' }
  ].forEach((input) => {
    assert.throws(
      () => session.altsvc('h2=":8000', input),
      {
        code: 'ERR_HTTP2_ALTSVC_INVALID_ORIGIN',
        name: 'TypeError',
        message: 'HTTP/2 ALTSVC frames require a valid origin'
      }
    );
  });

  // Arguments + origin are too long for an ALTSVC frame
  assert.throws(
    () => {
      session.altsvc('h2=":8000"',
                     `http://example.${'a'.repeat(17000)}.org:8000`);
    },
    {
      code: 'ERR_HTTP2_ALTSVC_LENGTH',
      name: 'TypeError',
      message: 'HTTP/2 ALTSVC frames are limited to 16382 bytes'
    }
  );
}));

server.listen(0, common.mustCall(() => {
  const client = http2.connect(`http://localhost:${server.address().port}`);

  const countdown = new Countdown(4, () => {
    client.close();
    server.close();
  });

  client.on('altsvc', common.mustCall((alt, origin, stream) => {
    assert.strictEqual(alt, 'h2=":8000"');
    switch (stream) {
      case 0:
        assert.strictEqual(origin, 'https://example.org:8111');
        break;
      case 1:
        assert.strictEqual(origin, '');
        break;
      default:
        assert.fail('should not happen');
    }
    countdown.dec();
  }, 4));

  const req = client.request();
  req.resume();
  req.on('close', common.mustCall());
}));