1import { parse } from 'url'; 2 3import * as Log from '../../../log'; 4import { ServerNext, ServerRequest, ServerResponse } from './server.types'; 5 6/** Base middleware creator for Expo dev servers. */ 7export abstract class ExpoMiddleware { 8 constructor(protected projectRoot: string, protected supportedPaths: string[]) {} 9 10 /** 11 * Returns true when the middleware should handle the incoming server request. 12 * Exposed for testing. 13 */ 14 _shouldHandleRequest(req: ServerRequest): boolean { 15 if (!req.url) { 16 return false; 17 } 18 const parsed = parse(req.url); 19 // Strip the query params 20 if (!parsed.pathname) { 21 return false; 22 } 23 24 return this.supportedPaths.includes(parsed.pathname); 25 } 26 27 abstract handleRequestAsync( 28 req: ServerRequest, 29 res: ServerResponse, 30 next: ServerNext 31 ): Promise<void>; 32 33 /** Create a server middleware handler. */ 34 public getHandler(): ( 35 req: ServerRequest, 36 res: ServerResponse, 37 next: ServerNext 38 ) => Promise<void> { 39 return async (req: ServerRequest, res: ServerResponse, next: ServerNext) => { 40 if (!this._shouldHandleRequest(req)) { 41 return next(); 42 } 43 44 try { 45 return await this.handleRequestAsync(req, res, next); 46 } catch (error: any) { 47 Log.exception(error); 48 // 5xx = Server Error HTTP code 49 res.statusCode = 500; 50 if (typeof error === 'object' && error !== null) { 51 res.end( 52 JSON.stringify({ 53 error: error.toString(), 54 }) 55 ); 56 } else { 57 res.end(`Unexpected error: ${error}`); 58 } 59 } 60 }; 61 } 62} 63 64export function disableResponseCache(res: ServerResponse): ServerResponse { 65 res.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate'); 66 res.setHeader('Expires', '-1'); 67 res.setHeader('Pragma', 'no-cache'); 68 return res; 69} 70