@@ -18,6 +18,7 @@ import * as os from "os";
18
18
import * as path from "path" ;
19
19
import * as pem from "pem" ;
20
20
import * as util from "util" ;
21
+ import * as url from "url" ;
21
22
import * as ws from "ws" ;
22
23
import { buildDir } from "./constants" ;
23
24
import { createPortScanner } from "./portScanner" ;
@@ -140,13 +141,13 @@ export const createApp = async (options: CreateAppOptions): Promise<{
140
141
} ;
141
142
142
143
const portScanner = createPortScanner ( ) ;
143
- wss . on ( "connection" , ( ws , req ) => {
144
+ wss . on ( "connection" , async ( ws , req ) => {
144
145
if ( req . url && req . url . startsWith ( "/tunnel" ) ) {
145
146
try {
146
147
const rawPort = req . url . split ( "/" ) . pop ( ) ;
147
148
const port = Number . parseInt ( rawPort ! , 10 ) ;
148
149
149
- handleTunnel ( ws , port ) ;
150
+ await handleTunnel ( ws , port ) ;
150
151
} catch ( ex ) {
151
152
ws . close ( TunnelCloseCode . Error , ex . toString ( ) ) ;
152
153
}
@@ -189,31 +190,70 @@ export const createApp = async (options: CreateAppOptions): Promise<{
189
190
new Server ( connection , options . serverOptions ) ;
190
191
} ) ;
191
192
193
+ const redirect = (
194
+ req : express . Request , res : express . Response ,
195
+ to : string = "" , from : string = "" ,
196
+ code : number = 302 , protocol : string = req . protocol ,
197
+ ) : void => {
198
+ const currentUrl = `${ protocol } ://${ req . headers . host } ${ req . originalUrl } ` ;
199
+ const newUrl = url . parse ( currentUrl ) ;
200
+ if ( from && newUrl . pathname ) {
201
+ newUrl . pathname = newUrl . pathname . replace ( new RegExp ( `\/${ from } \/?$` ) , "/" ) ;
202
+ }
203
+ if ( to ) {
204
+ newUrl . pathname = ( newUrl . pathname || "" ) . replace ( / \/ $ / , "" ) + `/${ to } ` ;
205
+ }
206
+ newUrl . path = undefined ; // Path is not necessary for format().
207
+ const newUrlString = url . format ( newUrl ) ;
208
+ logger . trace ( `Redirecting from ${ currentUrl } to ${ newUrlString } ` ) ;
209
+
210
+ return res . redirect ( code , newUrlString ) ;
211
+ } ;
212
+
192
213
const baseDir = buildDir || path . join ( __dirname , ".." ) ;
193
- const authStaticFunc = expressStaticGzip ( path . join ( baseDir , "build/web/auth " ) ) ;
194
- const unauthStaticFunc = expressStaticGzip ( path . join ( baseDir , "build/web/unauth" ) ) ;
214
+ const staticGzip = expressStaticGzip ( path . join ( baseDir , "build/web" ) ) ;
215
+
195
216
app . use ( ( req , res , next ) => {
217
+ logger . trace ( `\u001B[1m${ req . method } ${ res . statusCode } \u001B[0m${ req . originalUrl } ` , field ( "host" , req . hostname ) , field ( "ip" , req . ip ) ) ;
218
+
219
+ // Force HTTPS unless allowing HTTP.
196
220
if ( ! isEncrypted ( req . socket ) && ! options . allowHttp ) {
197
- return res . redirect ( 301 , ` https:// ${ req . headers . host ! } ${ req . path } ` ) ;
221
+ return redirect ( req , res , "" , "" , 301 , " https" ) ;
198
222
}
199
223
200
- if ( isAuthed ( req ) ) {
201
- // We can serve the actual VSCode bin
202
- authStaticFunc ( req , res , next ) ;
203
- } else {
204
- // Serve only the unauthed version
205
- unauthStaticFunc ( req , res , next ) ;
206
- }
224
+ next ( ) ;
207
225
} ) ;
226
+
208
227
// @ts -ignore
209
- app . use ( ( err , req , res , next ) => {
228
+ app . use ( ( err , _req , _res , next ) => {
229
+ logger . error ( err . message ) ;
210
230
next ( ) ;
211
231
} ) ;
212
- app . get ( "/ping" , ( req , res ) => {
232
+
233
+ // If not authenticated, redirect to the login page.
234
+ app . get ( "/" , ( req , res , next ) => {
235
+ if ( ! isAuthed ( req ) ) {
236
+ return redirect ( req , res , "login" ) ;
237
+ }
238
+ next ( ) ;
239
+ } ) ;
240
+
241
+ // If already authenticated, redirect back to the root.
242
+ app . get ( "/login" , ( req , res , next ) => {
243
+ if ( isAuthed ( req ) ) {
244
+ return redirect ( req , res , "" , "login" ) ;
245
+ }
246
+ next ( ) ;
247
+ } ) ;
248
+
249
+ // For getting general server data.
250
+ app . get ( "/ping" , ( _req , res ) => {
213
251
res . json ( {
214
252
hostname : os . hostname ( ) ,
215
253
} ) ;
216
254
} ) ;
255
+
256
+ // For getting a resource on disk.
217
257
app . get ( "/resource/:url(*)" , async ( req , res ) => {
218
258
if ( ! ensureAuthed ( req , res ) ) {
219
259
return ;
@@ -254,6 +294,8 @@ export const createApp = async (options: CreateAppOptions): Promise<{
254
294
res . end ( ) ;
255
295
}
256
296
} ) ;
297
+
298
+ // For writing a resource to disk.
257
299
app . post ( "/resource/:url(*)" , async ( req , res ) => {
258
300
if ( ! ensureAuthed ( req , res ) ) {
259
301
return ;
@@ -282,6 +324,9 @@ export const createApp = async (options: CreateAppOptions): Promise<{
282
324
}
283
325
} ) ;
284
326
327
+ // Everything else just pulls from the static build directory.
328
+ app . use ( staticGzip ) ;
329
+
285
330
return {
286
331
express : app ,
287
332
server,
0 commit comments