CommunitySolidServer/src/ldp/http/OriginalUrlExtractor.ts
2021-01-29 09:12:49 +01:00

57 lines
1.8 KiB
TypeScript

import type { TLSSocket } from 'tls';
import type { HttpRequest } from '../../server/HttpRequest';
import { parseForwarded } from '../../util/HeaderUtil';
import { toCanonicalUriPath } from '../../util/PathUtil';
import type { ResourceIdentifier } from '../representation/ResourceIdentifier';
import { TargetExtractor } from './TargetExtractor';
/**
* Reconstructs the original URL of an incoming {@link HttpRequest}.
*/
export class OriginalUrlExtractor extends TargetExtractor {
private readonly includeQueryString: boolean;
public constructor(options: { includeQueryString?: boolean } = {}) {
super();
this.includeQueryString = options.includeQueryString ?? true;
}
public async handle({ request: { url, connection, headers }}: { request: HttpRequest }): Promise<ResourceIdentifier> {
if (!url) {
throw new Error('Missing URL');
}
// Extract host and protocol (possibly overridden by the Forwarded header)
let { host } = headers;
let protocol = (connection as TLSSocket)?.encrypted ? 'https' : 'http';
if (headers.forwarded) {
const forwarded = parseForwarded(headers.forwarded);
if (forwarded.host) {
({ host } = forwarded);
}
if (forwarded.proto) {
({ proto: protocol } = forwarded);
}
}
// Perform a sanity check on the host
if (!host) {
throw new Error('Missing Host header');
}
if (/[/\\*]/u.test(host)) {
throw new Error(`The request has an invalid Host header: ${host}`);
}
// URL object applies punycode encoding to domain
const base = `${protocol}://${host}`;
const originalUrl = new URL(toCanonicalUriPath(url), base);
// Drop the query string if requested
if (!this.includeQueryString) {
originalUrl.search = '';
}
return { path: originalUrl.href };
}
}