Fix base path

Now it should work whether you have a trailing slash or not.
This commit is contained in:
Asher 2020-02-13 12:40:36 -06:00
parent cc79edb312
commit bf1be16d11
No known key found for this signature in database
GPG Key ID: D63C1EF81242354A
5 changed files with 50 additions and 36 deletions

View File

@ -1,3 +1,4 @@
import { field, logger } from "@coder/logger"
import { getBasepath, navigate, setBasepath } from "hookrouter"
import * as React from "react"
import { Application, isExecutableApplication } from "../common/api"
@ -14,16 +15,25 @@ interface RedirectedApplication extends Application {
redirected?: boolean
}
const origin = typeof window !== "undefined" ? window.location.origin + window.location.pathname : undefined
let resolved = false
const App: React.FunctionComponent<AppProps> = (props) => {
const [authed, setAuthed] = React.useState<boolean>(props.options.authed)
const [app, setApp] = React.useState<RedirectedApplication | undefined>(props.options.app)
const [error, setError] = React.useState<HttpError | Error | string>()
if (typeof window !== "undefined") {
const url = new URL(origin + props.options.basePath)
if (!resolved && typeof document !== "undefined") {
// Get the base path. We need the full URL for connecting the web socket.
// Use the path name plus the provided base path. For example:
// foo.com/base + ./ => foo.com/base
// foo.com/base/ + ./ => foo.com/base
// foo.com/base/bar + ./ => foo.com/base
// foo.com/base/bar/ + ./../ => foo.com/base
const parts = window.location.pathname.replace(/^\//g, "").split("/")
parts[parts.length - 1] = props.options.basePath
const url = new URL(window.location.origin + "/" + parts.join("/"))
setBasepath(normalize(url.pathname))
logger.debug("resolved base path", field("base", getBasepath()))
resolved = true
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(window as any).setAuthed = (a: boolean): void => {

View File

@ -117,8 +117,9 @@ interface ProviderRoute extends Route {
export interface HttpProviderOptions {
readonly auth: AuthType
readonly password?: string
readonly base: string
readonly commit: string
readonly password?: string
}
/**
@ -150,10 +151,16 @@ export abstract class HttpProvider {
public abstract handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse | undefined>
/**
* Get the base relative to the provided route.
* Get the base relative to the provided route. For each slash we need to go
* up a directory. For example:
* / => ./
* /foo => ./
* /foo/ => ./../
* /foo/bar => ./../
* /foo/bar/ => ./../../
*/
public base(route: Route): string {
const depth = ((route.fullPath + "/").match(/\//g) || []).length
const depth = (route.originalPath.match(/\//g) || []).length
return normalize("./" + (depth > 1 ? "../".repeat(depth - 1) : ""))
}
@ -403,6 +410,7 @@ export class HttpServer {
new provider(
{
auth: this.options.auth || AuthType.None,
base: `/${endpoint}`,
commit: this.options.commit,
password: this.options.password,
},
@ -510,11 +518,6 @@ export class HttpServer {
return { redirect: redirect(route.fullPath) }
}
// Redirect our indexes to a trailing slash so relative paths in the served
// HTML will operate against the base path properly.
if (route.requestPath === "/index.html" && !route.originalPath.endsWith("/") && this.providers.has(route.base)) {
return { redirect: redirect(route.fullPath + "/") }
}
return undefined
}
@ -572,7 +575,7 @@ export class HttpServer {
}
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}, pathname: "" }
const originalPath = parsedUrl.pathname || ""
const originalPath = parsedUrl.pathname || "/"
const fullPath = normalize(originalPath)
const { base, requestPath } = parse(fullPath)

View File

@ -199,6 +199,8 @@ export class VscodeHttpProvider extends HttpProvider {
...response,
content: response.content
.replace(/{{COMMIT}}/g, options.commit)
.replace(/{{BASE}}/g, this.base(route))
.replace(/{{VS_BASE}}/g, this.base(route) + this.options.base)
.replace(`"{{REMOTE_USER_DATA_URI}}"`, `'${JSON.stringify(options.remoteUserDataUri)}'`)
.replace(`"{{PRODUCT_CONFIGURATION}}"`, `'${JSON.stringify(options.productConfiguration)}'`)
.replace(`"{{WORKBENCH_WEB_CONFIGURATION}}"`, `'${JSON.stringify(options.workbenchWebConfiguration)}'`)

View File

@ -19,28 +19,27 @@
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="../static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="../static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css">
<link rel="apple-touch-icon" href="../static-{{COMMIT}}/src/browser/media/code-server.png" />
<link rel="icon" href="{{BASE}}/static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="{{BASE}}/static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css">
<link rel="apple-touch-icon" href="{{BASE}}/static-{{COMMIT}}/src/browser/media/code-server.png" />
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- Prefetch to avoid waterfall -->
<link rel="prefetch" href="./static-{{COMMIT}}/node_modules/semver-umd/lib/semver-umd.js">
<link rel="prefetch" href="{{BASE}}/static-{{COMMIT}}/node_modules/semver-umd/lib/semver-umd.js">
</head>
<body aria-label="">
</body>
<!-- Startup (do not modify order of script tags!) -->
<!-- NOTE:coder: Modified to work against the current path and use the commit for caching. -->
<script>
// NOTE: Changes to inline scripts require update of content security policy
const basePath = window.location.pathname.replace(/\/+$/, '');
const base = window.location.origin + basePath;
const parts = window.location.pathname.replace(/^\//g, "").split("/")
parts[parts.length - 1] = "{{VS_BASE}}"
const url = new URL(window.location.origin + "/" + parts.join("/"))
const el = document.getElementById('vscode-remote-commit');
const commit = el ? el.getAttribute('data-settings') : "";
const staticBase = base + '/static-' + commit;
const staticBase = url.href + '/static-' + commit;
let nlsConfig;
try {
nlsConfig = JSON.parse(document.getElementById('vscode-remote-nls-configuration').getAttribute('data-settings'));
@ -54,7 +53,7 @@
// FIXME: Only works if path separators are /.
const path = nlsConfig._resolvedLanguagePackCoreLocation
+ '/' + bundle.replace(/\//g, '!') + '.nls.json';
fetch(`${base}/resource/?path=${encodeURIComponent(path)}`)
fetch(`${url.href}/resource/?path=${encodeURIComponent(path)}`)
.then((response) => response.json())
.then((json) => {
bundles[bundle] = json;
@ -78,15 +77,14 @@
'vs/nls': nlsConfig,
};
</script>
<script src="./static-{{COMMIT}}/out/vs/loader.js"></script>
<script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.nls.js"></script>
<script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.js"></script>
<script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/loader.js"></script>
<script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.web.api.nls.js"></script>
<script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.web.api.js"></script>
<!-- TODO@coder: This errors with multiple anonymous define calls (one is
workbench.js and one is semver-umd.js). For now use the same method found in
workbench-dev.html. Appears related to the timing of the script load events. -->
<!-- <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.js"></script> -->
<!-- <script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.js"></script> -->
<script>
// NOTE: Changes to inline scripts require update of content security policy
require(['vs/code/browser/workbench/workbench'], function() {});
</script>
</html>

View File

@ -19,9 +19,9 @@
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="../static/src/browser/media/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="../static/src/browser/media/manifest.json" crossorigin="use-credentials">
<link rel="apple-touch-icon" href="../static/src/browser/media/code-server.png" />
<link rel="icon" href="{{BASE}}/static/src/browser/media/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="{{BASE}}/static/src/browser/media/manifest.json" crossorigin="use-credentials">
<link rel="apple-touch-icon" href="{{BASE}}/static/src/browser/media/code-server.png" />
<meta name="apple-mobile-web-app-capable" content="yes">
</head>
@ -30,11 +30,12 @@
<!-- Startup (do not modify order of script tags!) -->
<script>
const basePath = window.location.pathname.replace(/\/+$/, '');
const base = window.location.origin + basePath;
const parts = window.location.pathname.replace(/^\//g, "").split("/")
parts[parts.length - 1] = "{{VS_BASE}}"
const url = new URL(window.location.origin + "/" + parts.join("/"))
const el = document.getElementById('vscode-remote-commit');
const commit = el ? el.getAttribute('data-settings') : "";
const staticBase = base + '/static-' + commit;
const staticBase = url.href + '/static-' + commit;
self.require = {
baseUrl: `${staticBase}/out`,
paths: {
@ -48,7 +49,7 @@
},
};
</script>
<script src="./static/out/vs/loader.js"></script>
<script src="{{VS_BASE}}/static/out/vs/loader.js"></script>
<script>
require(['vs/code/browser/workbench/workbench'], function() {});
</script>