|
1 | 1 | import * as http from "http" |
2 | 2 | import { HttpCode, HttpError } from "../../common/http" |
3 | | -import { AuthType, HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http" |
| 3 | +import { HttpProvider, HttpProviderOptions, HttpProxyProvider, HttpResponse, Route } from "../http" |
4 | 4 |
|
5 | 5 | /** |
6 | 6 | * Proxy HTTP provider. |
7 | 7 | */ |
8 | | -export class ProxyHttpProvider extends HttpProvider { |
9 | | - public constructor(options: HttpProviderOptions, private readonly proxyDomains: string[]) { |
| 8 | +export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider { |
| 9 | + public readonly proxyDomains: string[] |
| 10 | + |
| 11 | + public constructor(options: HttpProviderOptions, proxyDomains: string[] = []) { |
10 | 12 | super(options) |
| 13 | + this.proxyDomains = proxyDomains.map((d) => d.replace(/^\*\./, "")).filter((d, i, arr) => arr.indexOf(d) === i) |
11 | 14 | } |
12 | 15 |
|
13 | | - public async handleRequest(route: Route): Promise<HttpResponse> { |
14 | | - if (this.options.auth !== AuthType.Password || route.requestPath !== "/index.html") { |
15 | | - throw new HttpError("Not found", HttpCode.NotFound) |
| 16 | + public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> { |
| 17 | + if (!this.authenticated(request)) { |
| 18 | + if (route.requestPath === "/index.html") { |
| 19 | + return { redirect: "/login", query: { to: route.fullPath } } |
| 20 | + } |
| 21 | + throw new HttpError("Unauthorized", HttpCode.Unauthorized) |
16 | 22 | } |
| 23 | + |
17 | 24 | const payload = this.proxy(route.base.replace(/^\//, "")) |
18 | | - if (!payload) { |
19 | | - throw new HttpError("Not found", HttpCode.NotFound) |
| 25 | + if (payload) { |
| 26 | + return payload |
20 | 27 | } |
21 | | - return payload |
| 28 | + |
| 29 | + throw new HttpError("Not found", HttpCode.NotFound) |
22 | 30 | } |
23 | 31 |
|
24 | | - public async getRoot(route: Route, error?: Error): Promise<HttpResponse> { |
25 | | - const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/login.html") |
26 | | - response.content = response.content.replace(/{{ERROR}}/, error ? `<div class="error">${error.message}</div>` : "") |
27 | | - return this.replaceTemplates(route, response) |
| 32 | + public getProxyDomain(host?: string): string | undefined { |
| 33 | + if (!host || !this.proxyDomains) { |
| 34 | + return undefined |
| 35 | + } |
| 36 | + |
| 37 | + return this.proxyDomains.find((d) => host.endsWith(d)) |
28 | 38 | } |
29 | 39 |
|
30 | | - /** |
31 | | - * Return a response if the request should be proxied. Anything that ends in a |
32 | | - * proxy domain and has a subdomain should be proxied. The port is found in |
33 | | - * the top-most subdomain. |
34 | | - * |
35 | | - * For example, if the proxy domain is `coder.com` then `8080.coder.com` and |
36 | | - * `test.8080.coder.com` will both proxy to `8080` but `8080.test.coder.com` |
37 | | - * will have an error because `test` isn't a port. If the proxy domain was |
38 | | - * `test.coder.com` then it would work. |
39 | | - */ |
40 | 40 | public maybeProxy(request: http.IncomingMessage): HttpResponse | undefined { |
41 | | - const host = request.headers.host |
42 | | - if (!host || !this.proxyDomains) { |
| 41 | + // No proxy until we're authenticated. This will cause the login page to |
| 42 | + // show as well as let our assets keep loading normally. |
| 43 | + if (!this.authenticated(request)) { |
43 | 44 | return undefined |
44 | 45 | } |
45 | 46 |
|
46 | | - const proxyDomain = this.proxyDomains.find((d) => host.endsWith(d)) |
47 | | - if (!proxyDomain) { |
| 47 | + const host = request.headers.host |
| 48 | + const proxyDomain = this.getProxyDomain(host) |
| 49 | + if (!host || !proxyDomain) { |
48 | 50 | return undefined |
49 | 51 | } |
50 | 52 |
|
|
0 commit comments