@@ -16,6 +16,7 @@ import {
1616 resolveAllowedDsns ,
1717 resolveServerTunnelOption ,
1818 resolveTunnelPaths ,
19+ MAX_TUNNEL_BODY_SIZE ,
1920} from '../../../src/utils/tunnel' ;
2021import {
2122 _INTERNAL_resetManagedTunnelDefaults ,
@@ -39,11 +40,15 @@ function getTunnelIgnoreSpanFilter(
3940 return getTunnelIgnoreSpanFilters ( tunnelOptions , paths ) [ 0 ] ;
4041}
4142
42- function makeEnvelopeRequest ( envelopeHeader : Record < string , unknown > , url = 'http://localhost/tunnel' ) : Request {
43- const envelope = createEnvelope ( envelopeHeader , [ ] ) ;
44- const body = serializeEnvelope ( envelope ) ;
43+ function makeEnvelopeRequest (
44+ envelopeHeader : Record < string , unknown > ,
45+ url = 'http://localhost/tunnel' ,
46+ contentLength ?: number ,
47+ ) : Request {
48+ const body = serializeEnvelope ( createEnvelope ( envelopeHeader , [ ] ) ) ;
49+ const length = contentLength ?? new TextEncoder ( ) . encode ( body as string ) . length ;
4550
46- return new Request ( url , { method : 'POST' , body } ) ;
51+ return new Request ( url , { method : 'POST' , body, headers : { 'content-length' : String ( length ) } } ) ;
4752}
4853
4954/**
@@ -56,6 +61,7 @@ function makeEndlessRequest(head: string, onCancel?: () => void): Request {
5661 return new Request ( 'http://localhost/tunnel' , {
5762 method : 'POST' ,
5863 duplex : 'half' ,
64+ headers : { 'content-length' : String ( MAX_TUNNEL_BODY_SIZE ) } ,
5965 body : new ReadableStream < Uint8Array > ( {
6066 pull ( controller ) {
6167 controller . enqueue ( sentHead ? filler : new TextEncoder ( ) . encode ( head ) ) ;
@@ -74,6 +80,40 @@ describe('handleTunnelRequest', () => {
7480 vi . stubGlobal ( 'fetch' , fetchMock ) ;
7581 } ) ;
7682
83+ it ( 'forwards the envelope when Content-Length equals the limit' , async ( ) => {
84+ fetchMock . mockResolvedValueOnce ( new Response ( 'ok' ) ) ;
85+
86+ const result = await handleTunnelRequest ( {
87+ request : makeEnvelopeRequest ( { dsn : TEST_DSN } , undefined , MAX_TUNNEL_BODY_SIZE ) ,
88+ allowedDsns : [ TEST_DSN ] ,
89+ } ) ;
90+
91+ expect ( result . status ) . toBe ( 200 ) ;
92+ } ) ;
93+
94+ it ( 'returns 413 when Content-Length is above the limit, without reading the body' , async ( ) => {
95+ const request = makeEnvelopeRequest ( { dsn : TEST_DSN } , undefined , MAX_TUNNEL_BODY_SIZE + 1 ) ;
96+
97+ const result = await handleTunnelRequest ( { request, allowedDsns : [ TEST_DSN ] } ) ;
98+
99+ expect ( result . status ) . toBe ( 413 ) ;
100+ expect ( request . bodyUsed ) . toBe ( false ) ;
101+ expect ( fetchMock ) . not . toHaveBeenCalled ( ) ;
102+ } ) ;
103+
104+ it ( 'returns 411 when the request has no Content-Length' , async ( ) => {
105+ const result = await handleTunnelRequest ( {
106+ request : new Request ( 'http://localhost/tunnel' , {
107+ method : 'POST' ,
108+ body : `${ JSON . stringify ( { dsn : TEST_DSN } ) } \n` ,
109+ } ) ,
110+ allowedDsns : [ TEST_DSN ] ,
111+ } ) ;
112+
113+ expect ( result . status ) . toBe ( 411 ) ;
114+ expect ( fetchMock ) . not . toHaveBeenCalled ( ) ;
115+ } ) ;
116+
77117 afterEach ( ( ) => {
78118 vi . restoreAllMocks ( ) ;
79119 getCurrentScope ( ) . setClient ( undefined ) ;
@@ -132,7 +172,7 @@ describe('handleTunnelRequest', () => {
132172 request : new Request ( 'http://localhost/tunnel' , {
133173 method : 'POST' ,
134174 body : `${ JSON . stringify ( { dsn : TEST_DSN } ) } \n` ,
135- headers : incomingHeaders ,
175+ headers : { ... incomingHeaders , 'content-length' : '1' } ,
136176 } ) ,
137177 allowedDsns : [ TEST_DSN ] ,
138178 } ) ;
0 commit comments