@@ -85,53 +85,62 @@ export class WebClientServer {
85
85
private readonly _productService : IProductService
86
86
) { }
87
87
88
- /**
89
- * @coder Patched to handle an arbitrary path depth, such as in Coder Enterprise.
90
- */
91
88
async handle ( req : http . IncomingMessage , res : http . ServerResponse , parsedUrl : url . UrlWithParsedQuery ) : Promise < void > {
92
89
try {
93
90
const pathname = parsedUrl . pathname ! ;
94
- const parsedPath = path . parse ( pathname ) ;
95
- const pathPrefix = getPathPrefix ( pathname ) ;
96
91
97
- if ( [ 'manifest.json' , 'webmanifest.json' ] . includes ( parsedPath . base ) ) {
92
+ /**
93
+ * Add a custom manifest.
94
+ *
95
+ * @author coder
96
+ */
97
+ if ( pathname === '/manifest.json' || pathname === '/webmanifest.json' ) {
98
98
return this . _handleManifest ( req , res , parsedUrl ) ;
99
99
}
100
-
101
- if ( [ 'favicon.ico' , 'code-192.png' , 'code-512.png' ] . includes ( parsedPath . base ) ) {
100
+ if ( pathname === '/favicon.ico' || pathname === '/manifest.json' || pathname === '/code-192.png' || pathname === '/code-512.png' ) {
102
101
// always serve icons/manifest, even without a token
103
- return serveFile ( this . _logService , req , res , join ( APP_ROOT , 'resources' , 'server' , parsedPath . base ) ) ;
102
+ return serveFile ( this . _logService , req , res , join ( APP_ROOT , 'resources' , 'server' , pathname . substr ( 1 ) ) ) ;
104
103
}
105
-
106
- if ( parsedPath . base === this . _environmentService . serviceWorkerFileName ) {
107
- return serveFile ( this . _logService , req , res , this . _environmentService . serviceWorkerPath , {
108
- 'Service-Worker-Allowed' : pathPrefix ,
109
- } ) ;
104
+ /**
105
+ * Add an endpoint for self-hosting webviews. This must be unique per
106
+ * webview as the code relies on unique service workers. In our case we
107
+ * use /webview/{{uuid}}.
108
+ *
109
+ * @author coder
110
+ */
111
+ if ( / ^ \/ w e b v i e w \/ / . test ( pathname ) ) {
112
+ // always serve webview requests, even without a token
113
+ return this . _handleWebview ( req , res , parsedUrl ) ;
110
114
}
111
-
112
- if ( parsedPath . dir . includes ( '/static/' ) && parsedPath . ext ) {
113
- parsedUrl . pathname = pathname . substring ( pathname . indexOf ( '/static/' ) ) ;
115
+ if ( / ^ \/ s t a t i c \/ / . test ( pathname ) ) {
114
116
// always serve static requests, even without a token
115
117
return this . _handleStatic ( req , res , parsedUrl ) ;
116
118
}
117
-
118
- if ( parsedPath . base === 'callback' ) {
119
+ /**
120
+ * Move the service worker to the root. This makes the scope the root
121
+ * (otherwise we would need to include Service-Worker-Allowed).
122
+ *
123
+ * @author coder
124
+ */
125
+ if ( pathname === '/' + this . _environmentService . serviceWorkerFileName ) {
126
+ return serveFile ( this . _logService , req , res , this . _environmentService . serviceWorkerPath ) ;
127
+ }
128
+ if ( pathname === '/' ) {
129
+ // the token handling is done inside the handler
130
+ return this . _handleRoot ( req , res , parsedUrl ) ;
131
+ }
132
+ if ( pathname === '/callback' ) {
119
133
// callback support
120
134
return this . _handleCallback ( req , res , parsedUrl ) ;
121
135
}
122
-
123
- if ( parsedPath . base === 'fetch-callback' ) {
136
+ if ( pathname === '/fetch-callback' ) {
124
137
// callback fetch support
125
138
return this . _handleFetchCallback ( req , res , parsedUrl ) ;
126
139
}
127
140
128
- if ( pathname . endsWith ( '/' ) ) {
129
- // the token handling is done inside the handler
130
- return this . _handleRoot ( req , res , parsedUrl ) ;
131
- }
132
-
133
141
const error = new HTTPNotFoundError ( `"${ parsedUrl . pathname } " not found.` ) ;
134
142
req . emit ( 'error' , error ) ;
143
+
135
144
return ;
136
145
} catch ( error ) {
137
146
console . log ( 'VS CODE ERRORED' ) ;
@@ -239,6 +248,28 @@ export class WebClientServer {
239
248
return serveFile ( this . _logService , req , res , filePath , headers ) ;
240
249
}
241
250
251
+ /**
252
+ * Handle HTTP requests for /webview/*
253
+ *
254
+ * A unique path is required for every webview service worker.
255
+ */
256
+ private async _handleWebview ( req : http . IncomingMessage , res : http . ServerResponse , parsedUrl : url . UrlWithParsedQuery ) : Promise < void > {
257
+ const headers : Record < string , string > = Object . create ( null ) ;
258
+
259
+ // support paths that are uri-encoded (e.g. spaces => %20)
260
+ const normalizedPathname = decodeURIComponent ( parsedUrl . pathname ! ) ;
261
+
262
+ // Strip `/webview/{uuid}` from the path.
263
+ const relativeFilePath = normalize ( normalizedPathname . split ( '/' ) . splice ( 3 ) . join ( '/' ) ) ;
264
+
265
+ const filePath = join ( APP_ROOT , 'out/vs/workbench/contrib/webview/browser/pre' , relativeFilePath ) ;
266
+ if ( ! isEqualOrParent ( filePath , APP_ROOT , ! isLinux ) ) {
267
+ return this . serveError ( req , res , 400 , `Bad request.` , parsedUrl ) ;
268
+ }
269
+
270
+ return serveFile ( this . _logService , req , res , filePath , headers ) ;
271
+ }
272
+
242
273
/**
243
274
* Handle HTTP requests for /
244
275
*/
@@ -317,8 +348,9 @@ export class WebClientServer {
317
348
logoutEndpointUrl : './logout' ,
318
349
webEndpointUrl : this . createRequestUrl ( req , parsedUrl , '/static' ) . toString ( ) ,
319
350
webEndpointUrlTemplate : this . createRequestUrl ( req , parsedUrl , '/static' ) . toString ( ) ,
351
+ webviewContentExternalBaseUrlTemplate : './webview/{{uuid}}/' ,
320
352
321
- updateUrl : this . createRequestUrl ( req , parsedUrl , ' /update/check') . toString ( ) ,
353
+ updateUrl : '. /update/check'
322
354
} ,
323
355
folderUri : ( workspacePath && isFolder ) ? transformer . transformOutgoing ( URI . file ( workspacePath ) ) : undefined ,
324
356
workspaceUri : ( workspacePath && ! isFolder ) ? transformer . transformOutgoing ( URI . file ( workspacePath ) ) : undefined ,
@@ -343,7 +375,7 @@ export class WebClientServer {
343
375
// the sha is the same as in src/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html
344
376
`script-src 'self' 'unsafe-eval' ${ this . _getScriptCspHashes ( data ) . join ( ' ' ) } 'sha256-cb2sg39EJV8ABaSNFfWu/ou8o1xVXYK7jp90oZ9vpcg=';` ,
345
377
'child-src \'self\';' ,
346
- `frame-src 'self' https://*.vscode-webview.net ${ this . _productService . webEndpointUrl || '' } data:;` ,
378
+ `frame-src 'self' ${ this . _productService . webEndpointUrl || '' } data:;` ,
347
379
'worker-src \'self\' data:;' ,
348
380
'style-src \'self\' \'unsafe-inline\';' ,
349
381
'connect-src \'self\' ws: wss: https:;' ,
0 commit comments