Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/util/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as url from 'url';
import * as _ from 'lodash';

import { nthIndexOf } from './util';
import { isIPv6Address } from './ip-utils';
import { Destination } from '../types';

// Is this URL fully qualified?
Expand Down Expand Up @@ -92,11 +93,14 @@ export const getDestination = (protocol: string, host: string): Destination => {

export const normalizeHost = (protocol: string, host: string) => {
const { hostname, port } = getDestination(protocol, host);
const normalizedHostname = isIPv6Address(hostname)
? `[${hostname}]`
: hostname;

if (port === getDefaultPort(protocol)) {
return hostname;
return normalizedHostname;
} else {
return `${hostname}:${port}`;
return `${normalizedHostname}:${port}`;
}
}

Expand Down
36 changes: 34 additions & 2 deletions test/request-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Buffer } from 'buffer';
import * as zlib from 'zlib';
import * as stream from 'stream';

import { expect, nodeOnly } from './test-utils';
import { buildBodyReader } from '../src/util/request-utils';
import { buildBodyReader, preprocessRequest } from '../src/util/request-utils';
import { LastHopEncrypted } from '../src/util/socket-extensions';

nodeOnly(() => {
describe("buildBodyReader", () => {
Expand Down Expand Up @@ -88,4 +90,34 @@ nodeOnly(() => {
});

});
});

describe("preprocessRequest", () => {
it('reconstructs valid absolute URLs from bracketed IPv6 host headers', () => {
const req = Object.assign(new stream.PassThrough(), {
method: 'GET',
url: '/api',
headers: {
host: '[::1]:8000'
},
rawHeaders: ['Host', '[::1]:8000'],
httpVersion: '1.1',
socket: {
[LastHopEncrypted]: false
}
}) as any;

const result = preprocessRequest(req, {
type: 'request',
serverPort: 45454,
maxBodySize: 1024
});

expect(result).to.not.equal(null);
expect(req.url).to.equal('http://[::1]:8000/api');
expect(req.destination).to.deep.equal({
hostname: '::1',
port: 8000
});
});
});
});
36 changes: 35 additions & 1 deletion test/url-normalization.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,42 @@
import { normalizeUrl } from '../src/util/url';
import { normalizeHost, normalizeUrl } from '../src/util/url';

import { expect } from "./test-utils";

describe("URL normalization for matching", () => {
describe("host normalization", () => {
it("should bracket IPv6 hosts with non-default HTTP ports", () => {
expect(
normalizeHost('http', '[::1]:8000')
).to.equal('[::1]:8000');
});

it("should bracket IPv6 hosts with non-default HTTPS ports", () => {
expect(
normalizeHost('https', '[::1]:8443')
).to.equal('[::1]:8443');
});

it("should bracket IPv6 hosts when normalizing away the default port", () => {
expect(
normalizeHost('http', '[::1]:80')
).to.equal('[::1]');

expect(
normalizeHost('https', '[::1]:443')
).to.equal('[::1]');
});

it("should preserve existing formatting for IPv4 and domain hosts", () => {
expect(
normalizeHost('http', '127.0.0.1:8000')
).to.equal('127.0.0.1:8000');

expect(
normalizeHost('https', 'example.com:443')
).to.equal('example.com');
});
});

it("should do nothing to fully specified URLs", () => {
expect(
normalizeUrl('https://example.com/abc')
Expand Down
Loading