@@ -18,6 +18,7 @@ import * as os from "os";
1818import * as path from "path" ;
1919import * as pem from "pem" ;
2020import * as util from "util" ;
21+ import * as url from "url" ;
2122import * as ws from "ws" ;
2223import { buildDir } from "./constants" ;
2324import { createPortScanner } from "./portScanner" ;
@@ -140,13 +141,13 @@ export const createApp = async (options: CreateAppOptions): Promise<{
140141 } ;
141142
142143 const portScanner = createPortScanner ( ) ;
143- wss . on ( "connection" , ( ws , req ) => {
144+ wss . on ( "connection" , async ( ws , req ) => {
144145 if ( req . url && req . url . startsWith ( "/tunnel" ) ) {
145146 try {
146147 const rawPort = req . url . split ( "/" ) . pop ( ) ;
147148 const port = Number . parseInt ( rawPort ! , 10 ) ;
148149
149- handleTunnel ( ws , port ) ;
150+ await handleTunnel ( ws , port ) ;
150151 } catch ( ex ) {
151152 ws . close ( TunnelCloseCode . Error , ex . toString ( ) ) ;
152153 }
@@ -189,31 +190,70 @@ export const createApp = async (options: CreateAppOptions): Promise<{
189190 new Server ( connection , options . serverOptions ) ;
190191 } ) ;
191192
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+
192213 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+
195216 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.
196220 if ( ! isEncrypted ( req . socket ) && ! options . allowHttp ) {
197- return res . redirect ( 301 , ` https:// ${ req . headers . host ! } ${ req . path } ` ) ;
221+ return redirect ( req , res , "" , "" , 301 , " https" ) ;
198222 }
199223
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 ( ) ;
207225 } ) ;
226+
208227 // @ts -ignore
209- app . use ( ( err , req , res , next ) => {
228+ app . use ( ( err , _req , _res , next ) => {
229+ logger . error ( err . message ) ;
210230 next ( ) ;
211231 } ) ;
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 ) => {
213251 res . json ( {
214252 hostname : os . hostname ( ) ,
215253 } ) ;
216254 } ) ;
255+
256+ // For getting a resource on disk.
217257 app . get ( "/resource/:url(*)" , async ( req , res ) => {
218258 if ( ! ensureAuthed ( req , res ) ) {
219259 return ;
@@ -254,6 +294,8 @@ export const createApp = async (options: CreateAppOptions): Promise<{
254294 res . end ( ) ;
255295 }
256296 } ) ;
297+
298+ // For writing a resource to disk.
257299 app . post ( "/resource/:url(*)" , async ( req , res ) => {
258300 if ( ! ensureAuthed ( req , res ) ) {
259301 return ;
@@ -282,6 +324,9 @@ export const createApp = async (options: CreateAppOptions): Promise<{
282324 }
283325 } ) ;
284326
327+ // Everything else just pulls from the static build directory.
328+ app . use ( staticGzip ) ;
329+
285330 return {
286331 express : app ,
287332 server,
0 commit comments