From 82453a91a42f7c792408ac02b2b044ef328849b7 Mon Sep 17 00:00:00 2001
From: Julian Andres Klode <julian.klode@canonical.com>
Date: Mon, 29 Jun 2020 14:03:21 +0200
Subject: [PATCH] http: Redesign reading of pending data

Instead of reading the data early, disable the timeout for the
select() call and read the data later. Also, change Read() to
call only once to drain the buffer in such instances.

We could optimize this to call read() multiple times if there
is also pending stuff on the socket, but that it slightly more
complex and should not provide any benefits.
---
 methods/http.cc | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/methods/http.cc b/methods/http.cc
index 95f24e9..db64421 100644
--- a/methods/http.cc
+++ b/methods/http.cc
@@ -778,13 +778,11 @@ ResultState HttpServerState::Go(bool ToFile, RequestState &Req)
 				ToFile == false))
       return ResultState::TRANSIENT_ERROR;
 
-   // Handle server IO
-   if (ServerFd->HasPending() && In.ReadSpace() == true)
-   {
-      errno = 0;
-      if (In.Read(ServerFd) == false)
-	 return Die(Req);
-   }
+   // Record if we have data pending to read in the server, so that we can
+   // skip the wait in select(). This can happen if data has already been
+   // read into a methodfd's buffer - the TCP queue might be empty at that
+   // point.
+   bool ServerPending = ServerFd->HasPending();
 
    fd_set rfds,wfds;
    FD_ZERO(&rfds);
@@ -816,7 +814,7 @@ ResultState HttpServerState::Go(bool ToFile, RequestState &Req)
 
    // Select
    struct timeval tv;
-   tv.tv_sec = TimeOut;
+   tv.tv_sec = ServerPending ? 0 : TimeOut;
    tv.tv_usec = 0;
    int Res = 0;
    if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0)
@@ -827,14 +825,14 @@ ResultState HttpServerState::Go(bool ToFile, RequestState &Req)
       return ResultState::TRANSIENT_ERROR;
    }
    
-   if (Res == 0)
+   if (Res == 0 && not ServerPending)
    {
       _error->Error(_("Connection timed out"));
       return ResultState::TRANSIENT_ERROR;
    }
    
    // Handle server IO
-   if (ServerFd->Fd() != -1 && FD_ISSET(ServerFd->Fd(), &rfds))
+   if (ServerPending || (ServerFd->Fd() != -1 && FD_ISSET(ServerFd->Fd(), &rfds)))
    {
       errno = 0;
       if (In.Read(ServerFd) == false)
-- 
GitLab