@@ -6,6 +6,43 @@ import { detectRequestLocation } from "../server-helper";
66import { generateAgentInvocationToken } from "./agents/me/me.server" ;
77import { handleSlackWebhook , isSlackRequest } from "./agents/slack-webhook" ;
88
9+ const HOP_BY_HOP_HEADERS = new Set ( [
10+ "connection" ,
11+ "keep-alive" ,
12+ "proxy-authenticate" ,
13+ "proxy-authorization" ,
14+ "proxy-connection" ,
15+ "te" ,
16+ "trailer" ,
17+ "transfer-encoding" ,
18+ "upgrade" ,
19+ ] ) ;
20+
21+ const stripHopByHopHeaders = ( headers : Headers ) : Headers => {
22+ const connectionTokens = new Set < string > ( ) ;
23+ headers . forEach ( ( value , key ) => {
24+ if ( key . toLowerCase ( ) !== "connection" ) {
25+ return ;
26+ }
27+ for ( const token of value . split ( "," ) ) {
28+ const trimmed = token . trim ( ) . toLowerCase ( ) ;
29+ if ( trimmed ) {
30+ connectionTokens . add ( trimmed ) ;
31+ }
32+ }
33+ } ) ;
34+
35+ const sanitized = new Headers ( ) ;
36+ headers . forEach ( ( value , key ) => {
37+ const lowerKey = key . toLowerCase ( ) ;
38+ if ( HOP_BY_HOP_HEADERS . has ( lowerKey ) || connectionTokens . has ( lowerKey ) ) {
39+ return ;
40+ }
41+ sanitized . set ( key , value ) ;
42+ } ) ;
43+ return sanitized ;
44+ } ;
45+
946export type AgentRequestRouting =
1047 | { mode : "webhook" ; subpath ?: string }
1148 | { mode : "subdomain" } ;
@@ -67,6 +104,10 @@ export default async function handleAgentRequest(
67104 }
68105 // Ensure we preserve the search params.
69106 url . search = incomingUrl . search ;
107+ // Ensure protocol is http/https for fetch.
108+ if ( url . protocol !== "http:" && url . protocol !== "https:" ) {
109+ url . protocol = "http:" ;
110+ }
70111
71112 let contentLength : number | undefined ;
72113 const contentLengthRaw = c . req . raw . headers . get ( "content-length" ) ;
@@ -176,19 +217,26 @@ export default async function handleAgentRequest(
176217 agent_deployment_target_id : query . agent_deployment . target_id ,
177218 } )
178219 ) ;
220+ const sanitizedHeaders = stripHopByHopHeaders ( headers ) ;
179221
180222 let response : Response | undefined ;
181223 let error : string | undefined ;
182224 try {
183225 // Use the body we already read if it's a Slack request, otherwise use the stream
226+ const hasBody =
227+ c . req . raw . method !== "GET" &&
228+ c . req . raw . method !== "HEAD" &&
229+ c . req . raw . method !== "OPTIONS" ;
184230 const bodyToSend =
185231 requestBodyText !== undefined ? requestBodyText : c . req . raw . body ;
186- response = await fetch ( url , {
187- body : bodyToSend ,
232+ const request = new Request ( url . toString ( ) , {
188233 method : c . req . raw . method ,
189- signal,
190- headers,
234+ headers : sanitizedHeaders ,
235+ body : hasBody ? bodyToSend : undefined ,
236+ // @ts -expect-error - Required for Node.js streaming.
237+ duplex : hasBody ? "half" : undefined ,
191238 } ) ;
239+ response = await fetch ( request , { signal, redirect : "manual" } ) ;
192240 } catch ( err ) {
193241 error = err instanceof Error ? err . message : JSON . stringify ( err ) ;
194242 }
0 commit comments