You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
H1: valid request rejected with 414 when its data end exactly at the end of a full input buffer #3540
A valid request on a keep-alive connection is sometimes rejected with 414 URI Too Long. show errors shows the complete, well-formed request head at position 0, with len equal to wraps at and 56 bytes free:
buffer starts at 0 (including 0 out), 56 free,
len 33736, wraps at 33736, error at position 0
H1 connection flags 0x00000020, H1 stream flags 0x00000890
H1 msg state MSG_RQBEFORE(0), H1 msg flags 0x00001400
00000 PUT /x HTTP/1.1\r\n
We see this in production on large PUTs from clients with sub-ms RTT.
From 1 ms of RTT upward it no longer reproduces.
Expected Behavior
The request is parsed and forwarded.
Steps to Reproduce the Behavior
Config:
global
tune.bufsize 33792
defaults
mode http
timeout client 10s
timeout server 10s
timeout connect 5s
frontend fe
bind 127.0.0.1:8000
default_backend be
backend be
server s1 127.0.0.1:8001
Script: it starts a backend on :8001, sends PUT #1 except its last 16
body bytes, waits, then sends those 16 bytes and PUT ##2 in one write:
importhttp.server, socket, threading, timeclassH(http.server.BaseHTTPRequestHandler):
protocol_version="HTTP/1.1"defdo_PUT(self):
self.rfile.read(int(self.headers["Content-Length"]))
self.send_response(200)
self.send_header("Content-Length", "0")
self.end_headers()
deflog_message(self, *a): passsrv=http.server.ThreadingHTTPServer(("127.0.0.1", 8001), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
defput(n, extra=""):
return (f"PUT /x HTTP/1.1\r\nHost: a\r\nContent-Length: {n}\r\n"f"{extra}\r\n").encode() +b"x"*nr1=put(1000)
r2=put(450000, "Connection: close\r\n")
s=socket.create_connection(("127.0.0.1", 8000))
s.sendall(r1[:-16])
time.sleep(0.2)
try:
s.sendall(r1[-16:] +r2)
exceptOSError:
pass# HAProxy may close after its 414 while we are still sendingresp=b""whiled:=s.recv(65536):
resp+=dprint(*[l.split(b"\r\n")[0].decode() forlinresp.split(b"HTTP/1.1 ")[1:]])
Output: 200 OK 414 URI Too Long on every run. With a 15- or 17-byte
tail it prints 200 OK 200 OK.
Do you have any idea what may have caused this?
h1_parse_msg_hdrs() realigns the input buffer only when head + data > size. Here the H1 mux reads into an empty ibuf, so head = sizeof(struct htx) = 40. It consumes the 16 tail bytes, so head = 56. Then it tops the buffer up to size - HTX_BUF_OVERHEAD while request #1 waits for its response. That leaves head + data == size exactly, so there is no realign. b_tail() wraps to the buffer origin, h1_headers_to_hdr_list() gets stop < start and returns 0, and since the buffer is full this is reported as headers-too-large (414 in RQBEFORE).
Do you have an idea how to solve the issue?
The data are contiguous after the realign check, so use the non-wrapping accessors:
--- a/src/h1_htx.c+++ b/src/h1_htx.c
@@ int h1_parse_msg_hdrs(...)
- ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),+ ret = h1_headers_to_hdr_list(__b_peek(srcbuf, ofs), __b_tail(srcbuf),
hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, h1sl);
With this change the reproducer gives 200 OK 200 OK on 3.4.6 and master. I'll send a patch to the ML.
What is your configuration?
See above (minimal reproducer).
Output of haproxy -vv
HAProxy version 3.4.6-0+ha34+ubuntu26.04u1 2026/09/28 - https://haproxy.org/
Running on: Linux 7.0.0-31-generic x86_64
Also reproduced on master (3.5-dev9, 069081792). The code involved is the same since at least 2.2.
@capflam indeed, I can confirm this fixes our case! With the repro used in the issue body, it works with your patch. So you may want to add the 414 failure case in the commit message 🙂
Activity
capflam commentedon Oct 15, 2026
Thanks for the report. I'm on a series of patches about H1 that includes this one I guess:
0001-BUG-MEDIUM-h1-htx-don-t-stall-on-messages-ending-exa.patch.txt
In my case, I was able to hit a timeout but it seems to be the same issue. It will be merged soon.
manu-ns commentedon Oct 15, 2026
@capflam indeed, I can confirm this fixes our case! With the repro used in the issue body, it works with your patch. So you may want to add the 414 failure case in the commit message 🙂
Thanks for the speedy response!
capflam commentedon Oct 15, 2026
Cool, thanks. I will keep the issue open till the fix is merged. I will reference it in the commit message. Thanks !
BUG/MEDIUM: h1-htx: don't stall on messages ending exactly at the buf…
capflam commentedon Oct 16, 2026
The fix was merged in DEV. Thanks !
BUG/MEDIUM: h1-htx: don't stall on messages ending exactly at the buf…