import { URL } from 'url' ; import uuid from 'uuid' ; import { Request , RouteOptionsApp , RequestApplicationState } from '@hapi/hapi' ; import { Observable , fromEvent , merge } from 'rxjs' ; import { shareReplay , first , takeUntil } from 'rxjs/operators' ; import { RecursiveReadonly } from '@osd/utility-types' ; import { deepFreeze } from '@osd/std' ; import { Headers } from './headers' ; import { RouteMethod , RouteConfigOptions , validBodyOutput , isSafeMethod } from './route' ; import { OpenSearchDashboardsSocket , IOpenSearchDashboardsSocket } from './socket' ; import { RouteValidator , RouteValidatorFullConfig } from './validator' ; const requestSymbol = Symbol ( 'request' ) ; internal export interface OpenSearchDashboardsRouteOptions extends RouteOptionsApp { xsrfRequired : boolean ; } internal export interface OpenSearchDashboardsRequestState extends RequestApplicationState { requestId : string ; requestUuid : string ; } public export type OpenSearchDashboardsRequestRouteOptions < Method extends RouteMethod > = Method extends 'get' | 'options' ? Required < Omit < RouteConfigOptions < Method > , 'body' > > : Required < RouteConfigOptions < Method > > ; public export interface OpenSearchDashboardsRequestRoute < Method extends RouteMethod > { path : string ; method : Method ; options : OpenSearchDashboardsRequestRouteOptions < Method > ; } public export interface OpenSearchDashboardsRequestEvents { /**
     * Observable that emits once if and when the request has been aborted.
     */ aborted$ : Observable < void > ; remarks completed$ : Observable < void > ; } deprecated public export interface LegacyRequest extends Request { } public export class OpenSearchDashboardsRequest < Params = unknown , Query = unknown , Body = unknown , Method extends RouteMethod = any > { internal public static from < P , Q , B > ( req , routeSchemas = { } , withoutSecretHeaders = true ) { const routeValidator = RouteValidator . from < P , Q , B > ( routeSchemas ) ; const requestParts = OpenSearchDashboardsRequest . validate ( req , routeValidator ) ; return new OpenSearchDashboardsRequest ( req , requestParts . params , requestParts . query , requestParts . body , withoutSecretHeaders ) ; } internal private static validate < P , Q , B > ( req , routeValidator ) { const params = routeValidator . getParams ( req . params , 'request params' ) ; const query = routeValidator . getQuery ( req . query , 'request query' ) ; const body = routeValidator . getBody ( req . payload , 'request body' ) ; return { query , params , body } ; } remarks public readonly id : string ; remarks public readonly uuid : string ; /** a WHATWG URL standard object. */ public readonly url : URL ; /** matched route details */ public readonly route : RecursiveReadonly < OpenSearchDashboardsRequestRoute < Method > > ; remarks public readonly headers : Headers ; /**
     * Whether or not the request is a "system request" rather than an application-level request.
     * Can be set on the client using the `HttpFetchOptions#asSystemRequest` option.
     */ public readonly isSystemRequest : boolean ; /**  IOpenSearchDashboardsSocket public readonly socket : IOpenSearchDashboardsSocket ; /** Request events  OpenSearchDashboardsRequestEvents public readonly events : OpenSearchDashboardsRequestEvents ; public readonly auth : { isAuthenticated : boolean ; } ; internal protected readonly [ requestSymbol ] : Request ; constructor ( request , public readonly params , public readonly query , public readonly body , private readonly withoutSecretHeaders ) { this . id = ( request . app as OpenSearchDashboardsRequestState | undefined ) ?. requestId ?? uuid . v4 ( ) ; this . uuid = ( request . app as OpenSearchDashboardsRequestState | undefined ) ?. requestUuid ?? uuid . v4 ( ) ; this . url = request . url ; this . headers = deepFreeze ( { ... request . headers } ) ; this . isSystemRequest = request . headers [ 'osd-system-request' ] === 'true' || request . headers [ 'osd-system-api' ] === 'true' ; Object . defineProperty ( this , requestSymbol , { value : request , enumerable : false , } ) ; this . route = deepFreeze ( this . getRouteInfo ( request ) ) ; this . socket = new OpenSearchDashboardsSocket ( request . raw . req . socket ) ; this . events = this . getEvents ( request ) ; this . auth = { isAuthenticated : Boolean ( request . auth ?. isAuthenticated ) , } ; } private getEvents ( request ) { const finish$ = merge ( fromEvent ( request . raw . res , 'finish' ) , fromEvent ( request . raw . req , 'close' ) ) . pipe ( shareReplay ( 1 ) , first ( ) ) ; const aborted$ = fromEvent < void > ( request . raw . req , 'aborted' ) . pipe ( first ( ) , takeUntil ( finish$ ) ) ; const completed$ = merge < void , void > ( finish$ , aborted$ ) . pipe ( shareReplay ( 1 ) , first ( ) ) ; return { aborted$ , completed$ , } as const ; } private getRouteInfo ( request ) { const method = request . method as Method ; const { parse , maxBytes , allow , output , timeout : payloadTimeout } = request . route . settings . payload || { } ; const socketTimeout = ( request . raw . req . socket as any ) ?. timeout ; const options = ( { authRequired : this . getAuthRequired ( request ) , xsrfRequired : ( request . route . settings . app as OpenSearchDashboardsRouteOptions ) ?. xsrfRequired ?? true , tags : request . route . settings . tags || [ ] , timeout : { payload : payloadTimeout , idleSocket : socketTimeout === 0 ? undefined : socketTimeout , } , body : isSafeMethod ( method ) ? undefined : { parse , maxBytes , accepts : allow , output : output as typeof validBodyOutput [ number ] , } , } as unknown ) as OpenSearchDashboardsRequestRouteOptions < Method > ; return { path : request . path , method , options , } ; } private getAuthRequired ( request ) { const authOptions = request . route . settings . auth ; if ( typeof authOptions === 'object' ) { if ( authOptions . mode === 'optional' || authOptions . mode === 'try' ) { return 'optional' ; } if ( authOptions . mode === 'required' ) { return true ; } } if ( authOptions === undefined ) { return true ; } if ( authOptions === false ) return false ; throw new Error ( `unexpected authentication options: ${ JSON . stringify ( authOptions ) } for route: ${ this . url . pathname }${ this . url . search }` ) ; } } internal export const ensureRawRequest = ( request ) => isOpenSearchDashboardsRequest ( request ) ? request [ requestSymbol ] : request ; /**
 * Checks if an incoming request is a  OpenSearchDashboardsRequest internal export function isOpenSearchDashboardsRequest ( request ) { return request instanceof OpenSearchDashboardsRequest ; } function isRequest ( request ) { try { return request . raw . req && typeof request . raw . req === 'object' ; } catch { return false ; } } internal export function isRealRequest ( request ) { return isOpenSearchDashboardsRequest ( request ) || isRequest ( request ) ; }