Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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