@@ -18,7 +18,7 @@ import { isPromiseCanceledError, onUnexpectedError, setUnexpectedErrorHandler }
18
18
import { Emitter , Event } from 'vs/base/common/event' ;
19
19
import { IDisposable } from 'vs/base/common/lifecycle' ;
20
20
import { FileAccess , Schemas } from 'vs/base/common/network' ;
21
- import { join } from 'vs/base/common/path' ;
21
+ import { dirname , join } from 'vs/base/common/path' ;
22
22
import * as platform from 'vs/base/common/platform' ;
23
23
import Severity from 'vs/base/common/severity' ;
24
24
import { ReadableStreamEventPayload } from 'vs/base/common/stream' ;
@@ -73,7 +73,7 @@ import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/
73
73
export type IRawURITransformerFactory = ( remoteAuthority : string ) => IRawURITransformer ;
74
74
export const IRawURITransformerFactory = createDecorator < IRawURITransformerFactory > ( 'rawURITransformerFactory' ) ;
75
75
76
- const APP_ROOT = path . join ( __dirname , '.. ', '..' , '..' , '..' ) ;
76
+ const APP_ROOT = dirname ( FileAccess . asFileUri ( ' ', require ) . fsPath ) ;
77
77
const uriTransformerPath = path . join ( APP_ROOT , 'out/serverUriTransformer' ) ;
78
78
const rawURITransformerFactory : IRawURITransformerFactory = < any > require . __$__nodeRequire ( uriTransformerPath ) ;
79
79
@@ -174,6 +174,28 @@ function serveError(req: http.IncomingMessage, res: http.ServerResponse, errorCo
174
174
res . end ( errorMessage ) ;
175
175
}
176
176
177
+ function getFirstQueryValue ( parsedUrl : url . UrlWithParsedQuery , key : string ) {
178
+ const result = parsedUrl . query [ key ] ;
179
+ return Array . isArray ( result ) ? result [ 0 ] : result ;
180
+ }
181
+
182
+ function getFirstQueryValues ( parsedUrl : url . UrlWithParsedQuery , ignoreKeys ?: string [ ] ) {
183
+ const queryValues = new Map ( ) ;
184
+
185
+ for ( const key in parsedUrl . query ) {
186
+ if ( ignoreKeys && ignoreKeys . indexOf ( key ) >= 0 ) {
187
+ continue ;
188
+ }
189
+
190
+ const value = getFirstQueryValue ( parsedUrl , key ) ;
191
+ if ( typeof value === 'string' ) {
192
+ queryValues . set ( key , value ) ;
193
+ }
194
+ }
195
+
196
+ return queryValues ;
197
+ }
198
+
177
199
async function serveFile ( logService : ILogService , req : http . IncomingMessage , res : http . ServerResponse , filePath : string , responseHeaders : http . OutgoingHttpHeaders = { } ) {
178
200
try {
179
201
@@ -198,7 +220,7 @@ async function serveFile(logService: ILogService, req: http.IncomingMessage, res
198
220
// Data
199
221
fs . createReadStream ( filePath ) . pipe ( res ) ;
200
222
} catch ( error ) {
201
- logService . error ( error . toString ( ) ) ;
223
+ logService . error ( error ) ;
202
224
res . writeHead ( 404 , { 'Content-Type' : 'text/plain' } ) ;
203
225
return res . end ( 'Not found' ) ;
204
226
}
@@ -226,6 +248,57 @@ async function handleRoot(req: http.IncomingMessage, resp: http.ServerResponse,
226
248
return resp . end ( entryPointContent ) ;
227
249
}
228
250
251
+ const mapCallbackUriToRequestId = new Map < string , string > ( ) ;
252
+ async function handleCallback ( logService : ILogService , req : http . IncomingMessage , res : http . ServerResponse , parsedUrl : url . UrlWithParsedQuery ) {
253
+ const wellKnownKeys = [ 'vscode-requestId' , 'vscode-scheme' , 'vscode-authority' , 'vscode-path' , 'vscode-query' , 'vscode-fragment' ] ;
254
+ const [ requestId , vscodeScheme , vscodeAuthority , vscodePath , vscodeQuery , vscodeFragment ] = wellKnownKeys . map ( key => {
255
+ const value = getFirstQueryValue ( parsedUrl , key ) ;
256
+ if ( value ) {
257
+ return decodeURIComponent ( value ) ;
258
+ }
259
+
260
+ return value ;
261
+ } ) ;
262
+
263
+ if ( ! requestId ) {
264
+ res . writeHead ( 400 , { 'Content-Type' : 'text/plain' } ) ;
265
+ return res . end ( `Bad request.` ) ;
266
+ }
267
+
268
+ // merge over additional query values that we got
269
+ let query = vscodeQuery ;
270
+ let index = 0 ;
271
+ getFirstQueryValues ( parsedUrl , wellKnownKeys ) . forEach ( ( value , key ) => {
272
+ if ( ! query ) {
273
+ query = '' ;
274
+ }
275
+
276
+ const prefix = ( index ++ === 0 ) ? '' : '&' ;
277
+ query += `${ prefix } ${ key } =${ value } ` ;
278
+ } ) ;
279
+
280
+ // add to map of known callbacks
281
+ mapCallbackUriToRequestId . set ( requestId , JSON . stringify ( { scheme : vscodeScheme || product . urlProtocol , authority : vscodeAuthority , path : vscodePath , query, fragment : vscodeFragment } ) ) ;
282
+ return serveFile ( logService , req , res , FileAccess . asFileUri ( 'vs/code/browser/workbench/callback.html' , require ) . fsPath , { 'Content-Type' : 'text/html' } ) ;
283
+ }
284
+
285
+ async function handleFetchCallback ( req : http . IncomingMessage , res : http . ServerResponse , parsedUrl : url . UrlWithParsedQuery ) {
286
+ const requestId = getFirstQueryValue ( parsedUrl , 'vscode-requestId' ) ;
287
+ if ( ! requestId ) {
288
+ res . writeHead ( 400 , { 'Content-Type' : 'text/plain' } ) ;
289
+ return res . end ( `Bad request.` ) ;
290
+ }
291
+
292
+ const knownCallbackUri = mapCallbackUriToRequestId . get ( requestId ) ;
293
+ if ( knownCallbackUri ) {
294
+ mapCallbackUriToRequestId . delete ( requestId ) ;
295
+ }
296
+
297
+ res . writeHead ( 200 , { 'Content-Type' : 'text/json' } ) ;
298
+ return res . end ( knownCallbackUri ) ;
299
+ }
300
+
301
+
229
302
interface ServerParsedArgs extends NativeParsedArgs {
230
303
port ?: string
231
304
}
@@ -253,14 +326,15 @@ export interface IServerOptions {
253
326
}
254
327
255
328
export async function main ( options : IServerOptions ) : Promise < void > {
256
- const devMode = ! ! process . env [ 'VSCODE_DEV' ] ;
257
329
const connectionToken = generateUuid ( ) ;
258
330
259
331
const parsedArgs = parseArgs ( process . argv , SERVER_OPTIONS ) ;
260
332
parsedArgs [ 'user-data-dir' ] = URI . file ( path . join ( os . homedir ( ) , product . dataFolderName ) ) . fsPath ;
261
333
const productService = { _serviceBrand : undefined , ...product } ;
262
334
const environmentService = new NativeEnvironmentService ( parsedArgs , productService ) ;
263
335
336
+ const devMode = ! environmentService . isBuilt ;
337
+
264
338
// see src/vs/code/electron-main/main.ts#142
265
339
const bufferLogService = new BufferLogService ( ) ;
266
340
const logService = new MultiplexLogService ( [ new ConsoleMainLogger ( getLogLevel ( environmentService ) ) , bufferLogService ] ) ;
@@ -613,6 +687,15 @@ export async function main(options: IServerOptions): Promise<void> {
613
687
if ( pathname === '/' ) {
614
688
return handleRoot ( req , res , devMode ? options . mainDev || WEB_MAIN_DEV : options . main || WEB_MAIN , environmentService ) ;
615
689
}
690
+
691
+ if ( pathname === '/callback' ) {
692
+ return handleCallback ( logService , req , res , parsedUrl ) ;
693
+ }
694
+
695
+ if ( pathname === '/fetch-callback' ) {
696
+ return handleFetchCallback ( req , res , parsedUrl ) ;
697
+ }
698
+
616
699
if ( pathname === '/manifest.json' ) {
617
700
res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
618
701
return res . end ( JSON . stringify ( {
@@ -634,7 +717,6 @@ export async function main(options: IServerOptions): Promise<void> {
634
717
}
635
718
//#region static end
636
719
637
- // TODO uri callbacks ?
638
720
logService . error ( `${ req . method } ${ req . url } not found` ) ;
639
721
return serveError ( req , res , 404 , 'Not found.' ) ;
640
722
} catch ( error ) {
0 commit comments