Implement the actual proxy
This commit is contained in:
@ -43,7 +43,8 @@ export class ApiHttpProvider extends HttpProvider {
|
||||
|
||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
||||
this.ensureAuthenticated(request)
|
||||
if (route.requestPath !== "/index.html") {
|
||||
// Only serve root pages.
|
||||
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||
throw new HttpError("Not found", HttpCode.NotFound)
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,8 @@ export class DashboardHttpProvider extends HttpProvider {
|
||||
}
|
||||
|
||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
||||
if (route.requestPath !== "/index.html") {
|
||||
// Only serve root pages.
|
||||
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||
throw new HttpError("Not found", HttpCode.NotFound)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,8 @@ interface LoginPayload {
|
||||
*/
|
||||
export class LoginHttpProvider extends HttpProvider {
|
||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
||||
if (this.options.auth !== AuthType.Password || route.requestPath !== "/index.html") {
|
||||
// Only serve root pages and only if password authentication is enabled.
|
||||
if (this.options.auth !== AuthType.Password || (route.requestPath && route.requestPath !== "/index.html")) {
|
||||
throw new HttpError("Not found", HttpCode.NotFound)
|
||||
}
|
||||
switch (route.base) {
|
||||
|
@ -1,4 +1,6 @@
|
||||
import * as http from "http"
|
||||
import proxy from "http-proxy"
|
||||
import * as net from "net"
|
||||
import { HttpCode, HttpError } from "../../common/http"
|
||||
import { HttpProvider, HttpProviderOptions, HttpProxyProvider, HttpResponse, Route } from "../http"
|
||||
|
||||
@ -10,6 +12,7 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
||||
* Proxy domains are stored here without the leading `*.`
|
||||
*/
|
||||
public readonly proxyDomains: string[]
|
||||
private readonly proxy = proxy.createProxyServer({})
|
||||
|
||||
/**
|
||||
* Domains can be provided in the form `coder.com` or `*.coder.com`. Either
|
||||
@ -20,15 +23,20 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
||||
this.proxyDomains = proxyDomains.map((d) => d.replace(/^\*\./, "")).filter((d, i, arr) => arr.indexOf(d) === i)
|
||||
}
|
||||
|
||||
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
|
||||
public async handleRequest(
|
||||
route: Route,
|
||||
request: http.IncomingMessage,
|
||||
response: http.ServerResponse,
|
||||
): Promise<HttpResponse> {
|
||||
if (!this.authenticated(request)) {
|
||||
if (route.requestPath === "/index.html") {
|
||||
return { redirect: "/login", query: { to: route.fullPath } }
|
||||
// Only redirect from the root. Other requests get an unauthorized error.
|
||||
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
|
||||
}
|
||||
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
|
||||
return { redirect: "/login", query: { to: route.fullPath } }
|
||||
}
|
||||
|
||||
const payload = this.proxy(route.base.replace(/^\//, ""))
|
||||
const payload = this.doProxy(route.requestPath, request, response, route.base.replace(/^\//, ""))
|
||||
if (payload) {
|
||||
return payload
|
||||
}
|
||||
@ -36,6 +44,16 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
||||
throw new HttpError("Not found", HttpCode.NotFound)
|
||||
}
|
||||
|
||||
public async handleWebSocket(
|
||||
route: Route,
|
||||
request: http.IncomingMessage,
|
||||
socket: net.Socket,
|
||||
head: Buffer,
|
||||
): Promise<void> {
|
||||
this.ensureAuthenticated(request)
|
||||
this.doProxy(route.requestPath, request, socket, head, route.base.replace(/^\//, ""))
|
||||
}
|
||||
|
||||
public getCookieDomain(host: string): string {
|
||||
let current: string | undefined
|
||||
this.proxyDomains.forEach((domain) => {
|
||||
@ -46,7 +64,26 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
||||
return current || host
|
||||
}
|
||||
|
||||
public maybeProxy(request: http.IncomingMessage): HttpResponse | undefined {
|
||||
public maybeProxyRequest(
|
||||
route: Route,
|
||||
request: http.IncomingMessage,
|
||||
response: http.ServerResponse,
|
||||
): HttpResponse | undefined {
|
||||
const port = this.getPort(request)
|
||||
return port ? this.doProxy(route.fullPath, request, response, port) : undefined
|
||||
}
|
||||
|
||||
public maybeProxyWebSocket(
|
||||
route: Route,
|
||||
request: http.IncomingMessage,
|
||||
socket: net.Socket,
|
||||
head: Buffer,
|
||||
): HttpResponse | undefined {
|
||||
const port = this.getPort(request)
|
||||
return port ? this.doProxy(route.fullPath, request, socket, head, port) : undefined
|
||||
}
|
||||
|
||||
private getPort(request: http.IncomingMessage): string | undefined {
|
||||
// No proxy until we're authenticated. This will cause the login page to
|
||||
// show as well as let our assets keep loading normally.
|
||||
if (!this.authenticated(request)) {
|
||||
@ -67,26 +104,58 @@ export class ProxyHttpProvider extends HttpProvider implements HttpProxyProvider
|
||||
return undefined
|
||||
}
|
||||
|
||||
return this.proxy(port)
|
||||
return port
|
||||
}
|
||||
|
||||
private proxy(portStr: string): HttpResponse {
|
||||
if (!portStr) {
|
||||
private doProxy(
|
||||
path: string,
|
||||
request: http.IncomingMessage,
|
||||
response: http.ServerResponse,
|
||||
portStr: string,
|
||||
): HttpResponse
|
||||
private doProxy(
|
||||
path: string,
|
||||
request: http.IncomingMessage,
|
||||
socket: net.Socket,
|
||||
head: Buffer,
|
||||
portStr: string,
|
||||
): HttpResponse
|
||||
private doProxy(
|
||||
path: string,
|
||||
request: http.IncomingMessage,
|
||||
responseOrSocket: http.ServerResponse | net.Socket,
|
||||
headOrPortStr: Buffer | string,
|
||||
portStr?: string,
|
||||
): HttpResponse {
|
||||
const _portStr = typeof headOrPortStr === "string" ? headOrPortStr : portStr
|
||||
if (!_portStr) {
|
||||
return {
|
||||
code: HttpCode.BadRequest,
|
||||
content: "Port must be provided",
|
||||
}
|
||||
}
|
||||
const port = parseInt(portStr, 10)
|
||||
|
||||
const port = parseInt(_portStr, 10)
|
||||
if (isNaN(port)) {
|
||||
return {
|
||||
code: HttpCode.BadRequest,
|
||||
content: `"${portStr}" is not a valid number`,
|
||||
content: `"${_portStr}" is not a valid number`,
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: HttpCode.Ok,
|
||||
content: `will proxy this to ${port}`,
|
||||
|
||||
const options: proxy.ServerOptions = {
|
||||
autoRewrite: true,
|
||||
changeOrigin: true,
|
||||
ignorePath: true,
|
||||
target: `http://127.0.0.1:${port}${path}`,
|
||||
}
|
||||
|
||||
if (responseOrSocket instanceof net.Socket) {
|
||||
this.proxy.ws(request, responseOrSocket, headOrPortStr, options)
|
||||
} else {
|
||||
this.proxy.web(request, responseOrSocket, options)
|
||||
}
|
||||
|
||||
return { handled: true }
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ export class UpdateHttpProvider extends HttpProvider {
|
||||
this.ensureAuthenticated(request)
|
||||
this.ensureMethod(request)
|
||||
|
||||
if (route.requestPath !== "/index.html") {
|
||||
// Only serve root pages.
|
||||
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||
throw new HttpError("Not found", HttpCode.NotFound)
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,8 @@ export class VscodeHttpProvider extends HttpProvider {
|
||||
|
||||
switch (route.base) {
|
||||
case "/":
|
||||
if (route.requestPath !== "/index.html") {
|
||||
// Only serve this at the root.
|
||||
if (route.requestPath && route.requestPath !== "/index.html") {
|
||||
throw new HttpError("Not found", HttpCode.NotFound)
|
||||
} else if (!this.authenticated(request)) {
|
||||
return { redirect: "/login", query: { to: this.options.base } }
|
||||
|
Reference in New Issue
Block a user