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 const internalMiddleware = async ( 36 req: ServerRequest, 37 res: ServerResponse, 38 next: ServerNext 39 ) => { 40 try { 41 return await this.handleRequestAsync(req, res, next); 42 } catch (error: any) { 43 Log.exception(error); 44 // 5xx = Server Error HTTP code 45 res.statusCode = 500; 46 if (typeof error === 'object' && error !== null) { 47 res.end( 48 JSON.stringify({ 49 error: error.toString(), 50 }) 51 ); 52 } else { 53 res.end(`Unexpected error: ${error}`); 54 } 55 } 56 }; 57 const middleware = async (req: ServerRequest, res: ServerResponse, next: ServerNext) => { 58 if (!this._shouldHandleRequest(req)) { 59 return next(); 60 } 61 return internalMiddleware(req, res, next); 62 }; 63 64 middleware.internal = internalMiddleware; 65 66 return middleware; 67 } 68} 69 70export function disableResponseCache(res: ServerResponse): ServerResponse { 71 res.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate'); 72 res.setHeader('Expires', '-1'); 73 res.setHeader('Pragma', 'no-cache'); 74 return res; 75} 76