Skip to content

Commit 4fb6e1f

Browse files
Prefer chunked Transfer-Encoding over Content-Length when parsing requests
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent d8e7ba9 commit 4fb6e1f

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

src/tink/http/Request.hx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,17 @@ class IncomingRequest extends Message<IncomingRequestHeader, IncomingRequestBody
163163
parts.a,
164164
Plain(switch parts.a.getContentLength() {
165165
case Success(len):
166-
parts.b.limit(len);
166+
// RFC 7230 §3.3.3: a chunked Transfer-Encoding takes precedence over Content-Length
167+
switch parts.a.byName(TRANSFER_ENCODING) {
168+
case Success((_:String).toLowerCase().split(',').map(StringTools.trim) => encodings) if(encodings.indexOf('chunked') != -1):
169+
Chunked.decode(parts.b);
170+
case _:
171+
parts.b.limit(len);
172+
}
167173
case Failure(_):
168174
switch [parts.a.method, parts.a.byName(TRANSFER_ENCODING)] {
169175
case [GET | OPTIONS, _]: Source.EMPTY;
170-
case [_, Success((_:String).split(',').map(StringTools.trim) => encodings)] if(encodings.indexOf('chunked') != -1): Chunked.decode(parts.b);
176+
case [_, Success((_:String).toLowerCase().split(',').map(StringTools.trim) => encodings)] if(encodings.indexOf('chunked') != -1): Chunked.decode(parts.b);
171177
case _: return new Error(411, 'Content-Length header missing');
172178
}
173179
})

tests/TestHeader.hx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,18 @@ class TestHeader {
174174
@:variant('foo', 'bar', 'Basic Zm9vOmJhcg==')
175175
public function basicAuth(username:String, password:String, output:String)
176176
return assert(HeaderValue.basicAuth(username, password) == output);
177+
178+
// a chunked Transfer-Encoding must be used even when Content-Length is present (RFC 7230 §3.3.3),
179+
// and the token match must be case-insensitive
180+
public function parseBodyPrefersTransferEncoding() {
181+
var raw:IdealSource = 'POST / HTTP/1.1\r\nContent-Length: 999\r\nTransfer-Encoding: Chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n';
182+
IncomingRequest.parse('127.0.0.1', raw)
183+
.next(function(req) return switch req.body {
184+
case Plain(source): source.all();
185+
case Parsed(_): new Error('unexpected parsed body');
186+
})
187+
.next(function(chunk) return asserts.assert(chunk.toString() == '123'))
188+
.handle(asserts.handle);
189+
return asserts;
190+
}
177191
}

0 commit comments

Comments
 (0)