Skip to content

Hapi request fails when using HTTP2 and IPv6 #4560

@gsoldevila

Description

@gsoldevila

Runtime

node.js

Runtime version

22.17.1

Module version

21.4.3

Last module version without issue

No response

Used with

standalone

Any other relevant information

In HTTP2 the Host: header is no longer present. According to the spec:

In HTTP/2, the functionality of the HTTP/1.1 Host header is replaced by the :authority pseudo-header, which is a mandatory, colon-prefixed header that specifies the authority (host and port) of the target server. This change is part of HTTP/2's binary framing and header compression (HPACK), and any HTTP/2 request converted to HTTP/1.1 must create a Host header from the :authority pseudo-header.

  • When building the request object and extracting its information (here), we rely solely on Host: header.
  • Then, when starting Hapi in HTTP2 mode and performing a request, the Host: header is missing, so the request.url getter) uses the host:port from the _core.info.
  • Now, if the server is configured with an IPv6 host (such as ::1), the getter ends up building an invalid URL, as the host name is not surrounded by square brackets. An IPv6 address like 2001:db8::1:8080 would be ambiguous, as 8080 could be interpreted as the last segment of the IP address rather than the port.

What are you trying to achieve or the steps to reproduce?

Here are the steps to reproduce:

const fs = require('fs');
const { Server } = require('@hapi/hapi');
const http2 = require('http2');

// must use a valid certificate (self-signed ones don't work with HTTP2)
const key = fs.readFileSync('path/to/keyFile.key');
const cert = fs.readFileSync('path/to/certFile.crt');
const ca = fs.readFileSync('path/to/ca.crt');

const listener = http2.createSecureServer({
  key,
  cert,
  ca,
});

const server = new Server({
  host: '::1',
  port: 10001,
  listener,
  tls: true,
});

server.route({
  method: 'GET',
  path: '/foo',
  // Note that the handler is trying to print 'req.url' which triggers the getter logic
  handler: (req, h) => h.response(`Hello world!: ${req.url}`),
});

server.start();

// Open your favorite browser and head over to: https://[::1]:10001/foo

What was the result you got?

Debug: internal, implementation, error 
    TypeError: Invalid URL
    at new URL (node:internal/url:818:25)
    at Request._parseUrl (/home/gsoldevila/test/node_modules/@hapi/hapi/lib/request.js:187:25)
    at get url (/home/gsoldevila/test/node_modules/@hapi/hapi/lib/request.js:121:21)
    at handler (/home/gsoldevila/test/hapi.js:34:56)
    at exports.Manager.execute (/home/gsoldevila/test/node_modules/@hapi/hapi/lib/toolkit.js:57:29)
    at internals.handler (/home/gsoldevila/test/node_modules/@hapi/hapi/lib/handler.js:46:48)
    at exports.execute (/home/gsoldevila/test/node_modules/@hapi/hapi/lib/handler.js:31:36)
    at Request._lifecycle (/home/gsoldevila/test/node_modules/@hapi/hapi/lib/request.js:370:68)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async Request._execute (/home/gsoldevila/test/node_modules/@hapi/hapi/lib/request.js:280:9)

What result did you expect?

I would expect a 200 OK and seeing the following in my browser:

Hello world!: https://[::1]:10001/foo

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug or defect

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions