From c0d56134320e507c82952f3d2a03f76b701945cb Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 18 Nov 2015 11:59:55 +0100 Subject: [PATCH 04/10] BUG/MEDIUM: http: switch the request channel to no-delay once done. There's an issue when sending POST data that came in a second packet, the CF_NEVER_WAIT flag is not always set on the request channel, while the server is waiting for the request. We must always set this flag in this case since we're not going to shut down after sending, contrary to the response side. Note that option http-no-delay works around this issue. Reproducer : listen px mode http timeout client 10s timeout server 5s timeout connect 3s option http-server-close #option http-no-delay bind :8001 server s1 127.0.0.1:8003 $ (printf "POST / HTTP/1.1\r\nTransfer-encoding: chunked\r\n\r\n"; sleep 0.01; printf "10\r\nAZERTYUIOPQSDFGH\r\n0\r\n\r\n") | nc6 0 8001 Before this fix : 12:03:31.946763 epoll_wait(3, {{EPOLLIN, {u32=5, u64=5}}}, 200, 1000) = 1 12:03:32.634175 accept4(5, {sa_family=AF_INET, sin_port=htons(53849), sin_addr=inet_addr("127.0.0.1")}, [16], SOCK_NONBLOCK) = 6 12:03:32.634318 setsockopt(6, SOL_TCP, TCP_NODELAY, [1], 4) = 0 12:03:32.634434 accept4(5, 0x7ffccfbb2cf0, [128], SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable) 12:03:32.634574 recvfrom(6, "POST / HTTP/1.1\r\nTransfer-encodi"..., 8192, 0, NULL, NULL) = 47 12:03:32.634809 setsockopt(6, SOL_TCP, TCP_QUICKACK, [1], 4) = 0 12:03:32.634952 socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 7 12:03:32.635031 fcntl(7, F_SETFL, O_RDONLY|O_NONBLOCK) = 0 12:03:32.635089 setsockopt(7, SOL_TCP, TCP_NODELAY, [1], 4) = 0 12:03:32.635153 connect(7, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) 12:03:32.635315 epoll_wait(3, {}, 200, 0) = 0 12:03:32.635394 sendto(7, "POST / HTTP/1.1\r\nTransfer-encodi"..., 66, MSG_DONTWAIT|MSG_NOSIGNAL, NULL, 0) = 66 12:03:32.635527 recvfrom(6, 0x7f0224e66024, 8192, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable) 12:03:32.635651 epoll_ctl(3, EPOLL_CTL_ADD, 6, {EPOLLIN|0x2000, {u32=6, u64=6}}) = 0 12:03:32.635782 epoll_wait(3, {}, 200, 0) = 0 12:03:32.635842 recvfrom(7, 0x7f0224e66024, 8192, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable) 12:03:32.635924 epoll_ctl(3, EPOLL_CTL_ADD, 7, {EPOLLIN|0x2000, {u32=7, u64=7}}) = 0 12:03:32.636027 epoll_wait(3, {{EPOLLIN, {u32=6, u64=6}}}, 200, 1000) = 1 12:03:32.644892 recvfrom(6, "10\r\nAZERTYUIOPQSDFGH\r\n0\r\n\r\n", 8192, 0, NULL, NULL) = 27 12:03:32.645016 epoll_wait(3, {}, 200, 0) = 0 12:03:32.645105 sendto(7, "10\r\nAZERTYUIOPQSDFGH\r\n0\r\n\r\n", 27, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE, NULL, 0) = 27 After the fix : 11:59:12.538617 connect(7, {sa_family=AF_INET, sin_port=htons(8003), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress) 11:59:12.538787 epoll_wait(3, {}, 200, 0) = 0 11:59:12.538867 sendto(7, "POST / HTTP/1.1\r\nTransfer-encodi"..., 66, MSG_DONTWAIT|MSG_NOSIGNAL, NULL, 0) = 66 11:59:12.539031 recvfrom(6, 0x7f832ce45024, 8192, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable) 11:59:12.539161 epoll_ctl(3, EPOLL_CTL_ADD, 6, {EPOLLIN|0x2000, {u32=6, u64=6}}) = 0 11:59:12.539259 epoll_wait(3, {}, 200, 0) = 0 11:59:12.539337 recvfrom(7, 0x7f832ce45024, 8192, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable) 11:59:12.539421 epoll_ctl(3, EPOLL_CTL_ADD, 7, {EPOLLIN|0x2000, {u32=7, u64=7}}) = 0 11:59:12.539499 epoll_wait(3, {{EPOLLIN, {u32=6, u64=6}}}, 200, 1000) = 1 11:59:12.548519 recvfrom(6, "10\r\nAZERTYUIOPQSDFGH\r\n0\r\n\r\n", 8192, 0, NULL, NULL) = 27 11:59:12.548844 epoll_wait(3, {}, 200, 0) = 0 11:59:12.549012 sendto(7, "10\r\nAZERTYUIOPQSDFGH\r\n0\r\n\r\n", 27, MSG_DONTWAIT|MSG_NOSIGNAL, NULL, 0) = 27 11:59:12.549454 epoll_wait(3, {}, 200, 1000) = 0 This fix must be backported to 1.6, 1.5 and 1.4. (cherry picked from commit 7f876a1eeb14ffae708327aad8a0b4b029da5e26) (cherry picked from commit 712a5339f384db62796aa4d4901e091dd7fd24dd) --- src/proto_http.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/proto_http.c b/src/proto_http.c index e7e1785..b32e778 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -5001,6 +5001,13 @@ int http_sync_req_state(struct session *s) */ chn->cons->flags |= SI_FL_NOHALF; + /* In any case we've finished parsing the request so we must + * disable Nagle when sending data because 1) we're not going + * to shut this side, and 2) the server is waiting for us to + * send pending data. + */ + chn->flags |= CF_NEVER_WAIT; + if (txn->rsp.msg_state == HTTP_MSG_ERROR) goto wait_other_side; @@ -5015,7 +5022,6 @@ int http_sync_req_state(struct session *s) /* if any side switches to tunnel mode, the other one does too */ channel_auto_read(chn); txn->req.msg_state = HTTP_MSG_TUNNEL; - chn->flags |= CF_NEVER_WAIT; goto wait_other_side; } @@ -5048,7 +5054,6 @@ int http_sync_req_state(struct session *s) if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN) { channel_auto_read(chn); txn->req.msg_state = HTTP_MSG_TUNNEL; - chn->flags |= CF_NEVER_WAIT; } } -- 2.4.10