@@ -23,12 +23,33 @@ import type {
2323 CreateGatewayInput ,
2424 CreateGatewayRuleInput ,
2525 CreateGatewayTargetInput ,
26+ GatewayInvokeRequest ,
27+ GatewayInvokeResponse ,
2628} from "../handlers/gateway/types" ;
27- import type { AwsClients , CoreOptions } from "./types" ;
29+ import type { Logger } from "../logging" ;
30+ import { abortable } from "./abortable" ;
31+ import type { AwsClients , CoreFetch , CoreOptions } from "./types" ;
2832import { toClientConfig } from "./utils" ;
2933
34+ async function * emptyBody ( ) : AsyncGenerator < Uint8Array > { }
35+
36+ function toQuery ( url : URL ) : Record < string , string | string [ ] > {
37+ const query : Record < string , string | string [ ] > = { } ;
38+ for ( const [ name , value ] of url . searchParams ) {
39+ const previous = query [ name ] ;
40+ if ( previous === undefined ) query [ name ] = value ;
41+ else if ( Array . isArray ( previous ) ) previous . push ( value ) ;
42+ else query [ name ] = [ previous , value ] ;
43+ }
44+ return query ;
45+ }
46+
3047export class GatewayClient implements CoreGatewayClient {
31- constructor ( private readonly clients : AwsClients ) { }
48+ constructor (
49+ private readonly clients : AwsClients ,
50+ private readonly fetch : CoreFetch ,
51+ private readonly logger : Logger ,
52+ ) { }
3253
3354 async createGateway (
3455 input : CreateGatewayInput ,
@@ -45,10 +66,128 @@ export class GatewayClient implements CoreGatewayClient {
4566 ) ;
4667 }
4768
48- async getGateway ( id : string , options : CoreOptions ) : Promise < GetGatewayResponse > {
69+ async invokeGateway (
70+ request : GatewayInvokeRequest ,
71+ options : CoreOptions ,
72+ signal ?: AbortSignal ,
73+ ) : Promise < GatewayInvokeResponse > {
74+ const logger = this . logger . child ( {
75+ operation : "invokeGateway" ,
76+ authMode : request . authorizerType ,
77+ gatewayId : request . gatewayId ,
78+ method : request . method ,
79+ region : options . region ,
80+ } ) ;
81+ const url = new URL ( request . url ) ;
82+ if ( url . protocol !== "https:" ) {
83+ throw new TypeError ( "Gateway invocation requires an HTTPS URL" ) ;
84+ }
85+
86+ const headers = new Headers ( request . applicationHeaders ) ;
87+ try {
88+ if ( request . contentType !== undefined ) headers . set ( "Content-Type" , request . contentType ) ;
89+ if ( request . accept !== undefined ) headers . set ( "Accept" , request . accept ) ;
90+ if ( request . runtimeSessionId !== undefined ) {
91+ headers . set ( "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id" , request . runtimeSessionId ) ;
92+ }
93+ if ( request . mcpSessionId !== undefined ) {
94+ headers . set ( "Mcp-Session-Id" , request . mcpSessionId ) ;
95+ }
96+ if ( request . mcpProtocolVersion !== undefined ) {
97+ headers . set ( "Mcp-Protocol-Version" , request . mcpProtocolVersion ) ;
98+ }
99+ if (
100+ request . authorizerType === "CUSTOM_JWT" ||
101+ request . authorizerType === "AUTHENTICATE_ONLY"
102+ ) {
103+ headers . set ( "Authorization" , `Bearer ${ request . bearerToken } ` ) ;
104+ }
105+ } catch {
106+ throw new TypeError ( "Invalid Gateway request header" ) ;
107+ }
108+
109+ let fetchHeaders : RequestInit [ "headers" ] = headers ;
110+ try {
111+ if ( request . authorizerType === "AWS_IAM" ) {
112+ const client = this . clients . data ( toClientConfig ( options ) ) ;
113+ const signer = await client . config . signer ( {
114+ name : "sigv4" ,
115+ signingName : "bedrock-agentcore" ,
116+ signingRegion : options . region ,
117+ properties : { } ,
118+ } ) ;
119+ const signed = await signer . sign ( {
120+ method : request . method ,
121+ protocol : url . protocol ,
122+ hostname : url . hostname ,
123+ ...( url . port && { port : Number ( url . port ) } ) ,
124+ path : url . pathname ,
125+ query : toQuery ( url ) ,
126+ headers : {
127+ ...Object . fromEntries ( headers . entries ( ) ) ,
128+ host : url . host ,
129+ } ,
130+ ...( request . payload !== undefined && { body : request . payload } ) ,
131+ } ) ;
132+ fetchHeaders = signed . headers ;
133+ }
134+
135+ const response = await this . fetch ( url , {
136+ method : request . method ,
137+ redirect : "error" ,
138+ headers : fetchHeaders ,
139+ ...( request . payload !== undefined && {
140+ body : request . payload as RequestInit [ "body" ] ,
141+ } ) ,
142+ signal,
143+ } ) ;
144+ if ( ! response . ok ) {
145+ logger
146+ . child ( { httpStatusCode : response . status } )
147+ . debug ( "Gateway invocation returned a non-success response" ) ;
148+ await response . body ?. cancel ( ) . catch ( ( ) => undefined ) ;
149+ throw new Error ( `HTTP ${ response . status } ` ) ;
150+ }
151+
152+ const body = ( response . body as AsyncIterable < Uint8Array > | null ) ?? emptyBody ( ) ;
153+ return {
154+ statusCode : response . status ,
155+ contentType : response . headers . get ( "content-type" ) ?? "" ,
156+ runtimeSessionId :
157+ response . headers . get ( "x-amzn-bedrock-agentcore-runtime-session-id" ) ?? undefined ,
158+ mcpSessionId : response . headers . get ( "mcp-session-id" ) ?? undefined ,
159+ mcpProtocolVersion : response . headers . get ( "mcp-protocol-version" ) ?? undefined ,
160+ requestId :
161+ response . headers . get ( "x-amzn-requestid" ) ??
162+ response . headers . get ( "x-amz-request-id" ) ??
163+ undefined ,
164+ body : signal ? abortable ( body , signal ) : body ,
165+ } ;
166+ } catch ( error ) {
167+ if ( signal ?. aborted ) throw signal . reason ?? error ;
168+ if ( ( error as Error ) ?. message ?. startsWith ( "HTTP " ) ) throw error ;
169+ logger
170+ . child ( {
171+ errorName :
172+ error instanceof TypeError
173+ ? "TypeError"
174+ : error instanceof Error
175+ ? "Error"
176+ : typeof error ,
177+ } )
178+ . debug ( "Gateway invocation transport failed" ) ;
179+ throw new Error ( "Gateway invocation failed" ) ;
180+ }
181+ }
182+
183+ async getGateway (
184+ id : string ,
185+ options : CoreOptions ,
186+ signal ?: AbortSignal ,
187+ ) : Promise < GetGatewayResponse > {
49188 return this . clients
50189 . control ( toClientConfig ( options ) )
51- . send ( new GetGatewayCommand ( { gatewayIdentifier : id } ) ) ;
190+ . send ( new GetGatewayCommand ( { gatewayIdentifier : id } ) , { abortSignal : signal } ) ;
52191 }
53192
54193 async listGateways (
0 commit comments