@@ -15,13 +15,6 @@ type BodyLimitOptions = {
1515 onError ?: OnError
1616}
1717
18- class BodyLimitError extends Error {
19- constructor ( message : string ) {
20- super ( message )
21- this . name = 'BodyLimitError'
22- }
23- }
24-
2518/**
2619 * Body Limit Middleware for Hono.
2720 *
@@ -74,52 +67,45 @@ export const bodyLimit = (options: BodyLimitOptions): MiddlewareHandler => {
7467 const hasTransferEncoding = c . req . raw . headers . has ( 'transfer-encoding' )
7568 const hasContentLength = c . req . raw . headers . has ( 'content-length' )
7669
77- // RFC 7230: If both Transfer-Encoding and Content-Length are present,
78- // Transfer-Encoding takes precedence and Content-Length should be ignored
79- if ( hasTransferEncoding && hasContentLength ) {
80- // Both headers present - follow RFC 7230 and ignore Content-Length
81- // This might indicate request smuggling attempt
82- }
83-
8470 if ( hasContentLength && ! hasTransferEncoding ) {
8571 // Only Content-Length present - we can trust it
8672 const contentLength = parseInt ( c . req . raw . headers . get ( 'content-length' ) || '0' , 10 )
8773 return contentLength > maxSize ? onError ( c ) : next ( )
8874 }
8975
90- // Transfer-Encoding present (chunked) or no length headers
91-
76+ // Transfer-Encoding present (chunked) or no length headers.
77+ // Per RFC 7230, when both are present Transfer-Encoding takes precedence
78+ // and Content-Length is ignored. Read the body up-front so the size check
79+ // is final before the handler runs, regardless of how (or whether) the
80+ // handler reads the body.
9281 let size = 0
82+ const chunks : Uint8Array [ ] = [ ]
9383 const rawReader = c . req . raw . body . getReader ( )
94- const reader = new ReadableStream ( {
95- async start ( controller ) {
96- try {
97- for ( ; ; ) {
98- const { done, value } = await rawReader . read ( )
99- if ( done ) {
100- break
101- }
102- size += value . length
103- if ( size > maxSize ) {
104- controller . error ( new BodyLimitError ( ERROR_MESSAGE ) )
105- break
106- }
84+ for ( ; ; ) {
85+ const { done, value } = await rawReader . read ( )
86+ if ( done ) {
87+ break
88+ }
89+ size += value . length
90+ if ( size > maxSize ) {
91+ return onError ( c )
92+ }
93+ chunks . push ( value )
94+ }
10795
108- controller . enqueue ( value )
96+ const requestInit : RequestInit & { duplex : 'half' } = {
97+ body : new ReadableStream ( {
98+ start ( controller ) {
99+ for ( const chunk of chunks ) {
100+ controller . enqueue ( chunk )
109101 }
110- } finally {
111102 controller . close ( )
112- }
113- } ,
114- } )
115-
116- const requestInit : RequestInit & { duplex : 'half' } = { body : reader , duplex : 'half' }
103+ } ,
104+ } ) ,
105+ duplex : 'half' ,
106+ }
117107 c . req . raw = new Request ( c . req . raw , requestInit as RequestInit )
118108
119- await next ( )
120-
121- if ( c . error instanceof BodyLimitError ) {
122- c . res = await onError ( c )
123- }
109+ return next ( )
124110 }
125111}
0 commit comments