From b9c96776cc512b561cbadb561acf515af62e861d Mon Sep 17 00:00:00 2001 From: Clinton Thomas <1033162+KernelClint@users.noreply.github.com> Date: Tue, 1 Sep 2026 14:06:32 -0400 Subject: [PATCH] http: return one Content-Length message per packet AI-Assisted: yes (GPT-5.6-Cyber) --- scapy/layers/http.py | 9 ++++++++- test/scapy/layers/http.uts | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/scapy/layers/http.py b/scapy/layers/http.py index ac3496fcb24..85f2dee0efb 100644 --- a/scapy/layers/http.py +++ b/scapy/layers/http.py @@ -694,7 +694,8 @@ def tcp_reassemble(cls, data, metadata, session): # Subtract the length of the "HTTP*" layer elif http_packet.payload.payload or length == 0: http_length = len(data) - http_packet.payload._original_len - detect_end = lambda dat: len(dat) - http_length >= length + metadata["http_end"] = http_end = http_length + length + detect_end = lambda dat: len(dat) >= http_end else: # The HTTP layer isn't fully received. if metadata.get("tcp_end", False): @@ -738,9 +739,15 @@ def tcp_reassemble(cls, data, metadata, session): metadata["detect_unknown"] = True metadata["detect_end"] = detect_end if detect_end(data): + http_end = metadata.get("http_end") + if http_end is not None and len(data) > http_end: + return cls(data[:http_end]) / conf.padding_layer(data[http_end:]) return http_packet else: if detect_end(data): + http_end = metadata.get("http_end") + if http_end is not None and len(data) > http_end: + return cls(data[:http_end]) / conf.padding_layer(data[http_end:]) http_packet = cls(data) return http_packet diff --git a/test/scapy/layers/http.uts b/test/scapy/layers/http.uts index 1b132a5a614..08fb04ce2aa 100644 --- a/test/scapy/layers/http.uts +++ b/test/scapy/layers/http.uts @@ -272,6 +272,21 @@ pkt.clear_cache() bytes(pkt) assert bytes(pkt) == b'HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=ISO-8859-1\r\nDate: Tue, 16 Jun 2026 17:47:46 GMT\r\nExpires: -1\r\nTransfer-Encoding: chunked\r\nVary: Accept-Encoding\r\n\r\n14\r\n00000000000000000000\r\n0\r\n\r\n' += TCPSession separates coalesced HTTP messages with Content-Length +~ http + +from scapy.layers.http import HTTP, HTTPRequest +from scapy.sessions import TCPSession + +first_bytes = b"GET /first HTTP/1.1\r\nHost: example\r\nContent-Length: 0\r\n\r\n" +second_bytes = b"POST /second HTTP/1.1\r\nHost: example\r\nContent-Length: 0\r\n\r\n" +session = TCPSession(app=True) +first = session.process(first_bytes + second_bytes, cls=HTTP) +second = session.process(b"", cls=HTTP) + +assert first[HTTPRequest].Path == b"/first" +assert second[HTTPRequest].Path == b"/second" + = Test chunked with gzip conf.contribs["http"]["auto_compression"] = False