Fix HTTPS redirects and TLS sockets

It still won't work behind a base path, but if you're using a reverse
proxy you can just redirect to HTTPS yourself. And should probably
handle TLS termination there too.

For sockets I just needed to add back the proxy call.
This commit is contained in:
Asher 2020-02-19 17:00:58 -06:00
parent e5b68a8f4c
commit c567a06ff5
No known key found for this signature in database
GPG Key ID: D63C1EF81242354A

View File

@ -1,4 +1,4 @@
import { logger } from "@coder/logger" import { field, logger } from "@coder/logger"
import * as fs from "fs-extra" import * as fs from "fs-extra"
import * as http from "http" import * as http from "http"
import * as httpolyglot from "httpolyglot" import * as httpolyglot from "httpolyglot"
@ -13,6 +13,7 @@ import * as tls from "tls"
import * as url from "url" import * as url from "url"
import { HttpCode, HttpError } from "../common/http" import { HttpCode, HttpError } from "../common/http"
import { normalize, plural, split } from "../common/util" import { normalize, plural, split } from "../common/util"
import { SocketProxyProvider } from "./socket"
import { getMediaMime, xdgLocalDir } from "./util" import { getMediaMime, xdgLocalDir } from "./util"
export type Cookies = { [key: string]: string[] | undefined } export type Cookies = { [key: string]: string[] | undefined }
@ -370,6 +371,7 @@ export class HttpServer {
public readonly protocol: "http" | "https" public readonly protocol: "http" | "https"
private readonly providers = new Map<string, HttpProvider>() private readonly providers = new Map<string, HttpProvider>()
private readonly heart: Heart private readonly heart: Heart
private readonly socketProvider = new SocketProxyProvider()
public constructor(private readonly options: HttpServerOptions) { public constructor(private readonly options: HttpServerOptions) {
this.heart = new Heart(path.join(xdgLocalDir, "heartbeat"), async () => { this.heart = new Heart(path.join(xdgLocalDir, "heartbeat"), async () => {
@ -392,6 +394,7 @@ export class HttpServer {
} }
public dispose(): void { public dispose(): void {
this.socketProvider.stop()
this.providers.forEach((p) => p.dispose()) this.providers.forEach((p) => p.dispose())
} }
@ -510,6 +513,7 @@ export class HttpServer {
if (error.code === "ENOENT" || error.code === "EISDIR") { if (error.code === "ENOENT" || error.code === "EISDIR") {
e = new HttpError("Not found", HttpCode.NotFound) e = new HttpError("Not found", HttpCode.NotFound)
} }
logger.debug("Request error", field("url", request.url))
logger.debug(error.stack) logger.debug(error.stack)
const code = typeof e.code === "number" ? e.code : HttpCode.ServerError const code = typeof e.code === "number" ? e.code : HttpCode.ServerError
const content = (await route.provider.getErrorRoot(route, code, code, e.message)).content const content = (await route.provider.getErrorRoot(route, code, code, e.message)).content
@ -547,11 +551,13 @@ export class HttpServer {
} }
}) })
return ( const secure = (request.connection as tls.TLSSocket).encrypted
(this.options.cert ? `${this.protocol}://${request.headers.host}` : "") + const redirect =
(this.options.cert && !secure ? `${this.protocol}://${request.headers.host}/` : "") +
normalize(`${route.provider.base(route)}/${payload.redirect}`, true) + normalize(`${route.provider.base(route)}/${payload.redirect}`, true) +
(Object.keys(query).length > 0 ? `?${querystring.stringify(query)}` : "") (Object.keys(query).length > 0 ? `?${querystring.stringify(query)}` : "")
) logger.debug("Redirecting", field("secure", !!secure), field("from", request.url), field("to", redirect))
return redirect
} }
private onUpgrade = async (request: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise<void> => { private onUpgrade = async (request: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise<void> => {
@ -572,7 +578,9 @@ export class HttpServer {
throw new HttpError("Not found", HttpCode.NotFound) throw new HttpError("Not found", HttpCode.NotFound)
} }
if (!(await route.provider.handleWebSocket(route, request, socket, head))) { if (
!(await route.provider.handleWebSocket(route, request, await this.socketProvider.createProxy(socket), head))
) {
throw new HttpError("Not found", HttpCode.NotFound) throw new HttpError("Not found", HttpCode.NotFound)
} }
} catch (error) { } catch (error) {
@ -609,7 +617,7 @@ export class HttpServer {
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}, pathname: "" } const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}, pathname: "" }
const originalPath = parsedUrl.pathname || "/" const originalPath = parsedUrl.pathname || "/"
const fullPath = normalize(originalPath) const fullPath = normalize(originalPath, true)
const { base, requestPath } = parse(fullPath) const { base, requestPath } = parse(fullPath)
// Providers match on the path after their base so we need to account for // Providers match on the path after their base so we need to account for