Skip to content

host vs hostname inconsistency #16712

Closed
Closed
@sholladay

Description

@sholladay

Version: Node 8 and others

Problem

The semantics of the host option for server.listen() are incompatible with the conventions of other APIs, both in Node itself and in browsers, etc. This is a footgun that could be fixed by making the options for .listen() be consistent with other APIs.

Context

Most systems I interact with distinguish between a hostname and a host, where the latter includes a port number and the former does not.

In general, Node behaves this way, too (see os.hostname(), WHATWG URL, and the legacy url module APIs). However, the option object passed to server.listen(option) is weird. It only understands host (not hostname), but a port number is disallowed. This is confusing and leads to very surprising results in some cases.

Prior art

In browsers

location.href = 'https://localhost:3000';
console.log(location.host !== location.hostname);  // true
const whatwgParsed = new URL('https://localhost:3000');
console.log(whatwgParsed.host !== whatwgParsed.hostname);  // true

In Node.js

const url = require('url');
const target = 'https://localhost:3000';
const legacyParsed = url.parse(target);
console.log(legacyParsed.host !== legacyParsed.hostname);  // true
const whatwgParsed = new url.URL(target);
console.log(whatwgParsed.host !== whatwgParsed.hostname);  // true

Current behavior

The server.listen() API understands a host option, but it is incompatible with host from other APIs, including WHATWG URL and others. This is because .listen() refuses perfectly valid hosts such as localhost:3000. A reasonable expectation would be to try hostname instead, which is the more common name for the current behavior of the host option. Unfortunately, that doesn't work either, as .listen() does not understand hostname, and it will be silently ignored.

// Reports an error correctly, since the API lacks a default port, but the error message is somewhat vague
server.listen({ host : 'localhost' }, () => {});
Error: Invalid listen argument: { host: 'localhost' }

// Listens on 0.0.0.0 rather than localhost (works as documented, but very confusing and as a footgun is arguably unsafe)
server.listen({ hostname : 'localhost', port : 3000}, () => {});

// Reports an error even though the host is valid (works as documented, but confusing and incompatible with other specs and APIs)
server.listen({ host : 'localhost:3000' }, () => {});
Error: Invalid listen argument: { host: 'localhost:3000' }

// Succeeds, even though host usually takes precedence over separate hostname and port options and the host lacks a port (works as documented, but a bit strange)
server.listen({ host : 'localhost', port : 3000 }, () => {});

Expected behavior

APIs that accept a host should respect their port number to avoid confusion and inconsistency. Having separate options for hostname and port is also awesome, but in that case it should be named hostname instead of host. I would argue that Node should only support hostname instead of host (leaving parsing hosts to userland) but that would be a breaking change, so I'm not sure how feasible that is. At the very least, I think a new hostname option could be introduced with the correct behavior, and then host could be extended to respect port numbers, and the fact that host continues to be in the API would purely be for backwards compatibility reasons.

// Should report an intuitive error. The host is perfectly valid, but the API lacks a default port.
server.listen({ host : 'localhost' }, () => {});
Error: No port specified

// Should listen on localhost, whereas it currently listens on 0.0.0.0 instead because hostname is not understood.
server.listen({ hostname : 'localhost', port : 3000}, () => {});

// If host needs to be supported, then this should listen on localhost and port 3000, whereas it currently throws an error.
server.listen({ host : 'localhost:3000' }, () => {});

// Should probably throw an error, whereas it currently does not.
server.listen({ host : 'localhost', port : 3000 }, () => {});

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues that request new features to be added to Node.js.help wantedIssues that need assistance from volunteers or PRs that need help to proceed.netIssues and PRs related to the net subsystem.stalewhatwg-urlIssues and PRs related to the WHATWG URL implementation.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions