]> git.saurik.com Git - apt.git/commitdiff
separating state variables regarding server/request
authorDavid Kalnischkies <david@kalnischkies.de>
Wed, 9 Nov 2016 11:25:44 +0000 (12:25 +0100)
committerDavid Kalnischkies <david@kalnischkies.de>
Sat, 31 Dec 2016 01:29:21 +0000 (02:29 +0100)
Having a Reset(bool) method to partially reset certain variables like
the download size always were strange, so this commit splits the
ServerState into an additional RequestState living on the stack for as
long as we deal with this request causing an automatic "reset".

There is much to do still to make this code look better, but this is a
good first step which compiles cleanly and passes all tests, so keeping
it as history might be beneficial and due to avoiding explicit memory
allocations it ends up fixing a small memory leak in https, too.

Closes: #440057
methods/http.cc
methods/http.h
methods/https.cc
methods/https.h
methods/server.cc
methods/server.h

index 8d3c569c1750946b0a42d358f3825b06436d9b17..b460644dd5590cf3b3abb39fd0957cb37b3a8eef 100644 (file)
@@ -555,12 +555,12 @@ bool HttpServerState::Close()
 }
                                                                        /*}}}*/
 // HttpServerState::RunData - Transfer the data from the socket                /*{{{*/
-bool HttpServerState::RunData(FileFd * const File)
+bool HttpServerState::RunData(RequestState &Req)
 {
-   State = Data;
+   Req.State = RequestState::Data;
    
    // Chunked transfer encoding is fun..
-   if (Encoding == Chunked)
+   if (Req.Encoding == RequestState::Chunked)
    {
       while (1)
       {
@@ -573,7 +573,7 @@ bool HttpServerState::RunData(FileFd * const File)
            if (In.WriteTillEl(Data,true) == true)
               break;
         }
-        while ((Last = Go(false, File)) == true);
+        while ((Last = Go(false, Req)) == true);
 
         if (Last == false)
            return false;
@@ -591,7 +591,7 @@ bool HttpServerState::RunData(FileFd * const File)
               if (In.WriteTillEl(Data,true) == true && Data.length() <= 2)
                  break;
            }
-           while ((Last = Go(false, File)) == true);
+           while ((Last = Go(false, Req)) == true);
            if (Last == false)
               return false;
            return !_error->PendingError();
@@ -599,7 +599,7 @@ bool HttpServerState::RunData(FileFd * const File)
         
         // Transfer the block
         In.Limit(Len);
-        while (Go(true, File) == true)
+        while (Go(true, Req) == true)
            if (In.IsLimit() == true)
               break;
         
@@ -615,7 +615,7 @@ bool HttpServerState::RunData(FileFd * const File)
            if (In.WriteTillEl(Data,true) == true)
               break;
         }
-        while ((Last = Go(false, File)) == true);
+        while ((Last = Go(false, Req)) == true);
         if (Last == false)
            return false;
       }
@@ -624,10 +624,10 @@ bool HttpServerState::RunData(FileFd * const File)
    {
       /* Closes encoding is used when the server did not specify a size, the
          loss of the connection means we are done */
-      if (JunkSize != 0)
-        In.Limit(JunkSize);
-      else if (DownloadSize != 0)
-        In.Limit(DownloadSize);
+      if (Req.JunkSize != 0)
+        In.Limit(Req.JunkSize);
+      else if (Req.DownloadSize != 0)
+        In.Limit(Req.DownloadSize);
       else if (Persistent == false)
         In.Limit(-1);
       
@@ -640,19 +640,19 @@ bool HttpServerState::RunData(FileFd * const File)
         In.Limit(-1);
         return !_error->PendingError();
       }
-      while (Go(true, File) == true);
+      while (Go(true, Req) == true);
    }
 
-   return Owner->Flush() && !_error->PendingError();
+   return Flush(&Req.File) && !_error->PendingError();
 }
                                                                        /*}}}*/
-bool HttpServerState::RunDataToDevNull()                               /*{{{*/
+bool HttpServerState::RunDataToDevNull(RequestState &Req)              /*{{{*/
 {
    // no need to clean up if we discard the connection anyhow
    if (Persistent == false)
       return true;
-   FileFd DevNull("/dev/null", FileFd::WriteOnly);
-   return RunData(&DevNull);
+   Req.File.Open("/dev/null", FileFd::WriteOnly);
+   return RunData(Req);
 }
                                                                        /*}}}*/
 bool HttpServerState::ReadHeaderLines(std::string &Data)               /*{{{*/
@@ -660,9 +660,9 @@ bool HttpServerState::ReadHeaderLines(std::string &Data)            /*{{{*/
    return In.WriteTillEl(Data);
 }
                                                                        /*}}}*/
-bool HttpServerState::LoadNextResponse(bool const ToFile, FileFd * const File)/*{{{*/
+bool HttpServerState::LoadNextResponse(bool const ToFile, RequestState &Req)/*{{{*/
 {
-   return Go(ToFile, File);
+   return Go(ToFile, Req);
 }
                                                                        /*}}}*/
 bool HttpServerState::WriteResponse(const std::string &Data)           /*{{{*/
@@ -682,11 +682,10 @@ bool HttpServerState::InitHashes(HashStringList const &ExpectedHashes)    /*{{{*/
    return true;
 }
                                                                        /*}}}*/
-void HttpServerState::Reset(bool const Everything)                     /*{{{*/
+void HttpServerState::Reset()                                          /*{{{*/
 {
-   ServerState::Reset(Everything);
-   if (Everything)
-      ServerFd = -1;
+   ServerState::Reset();
+   ServerFd = -1;
 }
                                                                        /*}}}*/
 
@@ -696,22 +695,22 @@ APT_PURE Hashes * HttpServerState::GetHashes()                            /*{{{*/
 }
                                                                        /*}}}*/
 // HttpServerState::Die - The server has closed the connection.                /*{{{*/
-bool HttpServerState::Die(FileFd * const File)
+bool HttpServerState::Die(RequestState &Req)
 {
    unsigned int LErrno = errno;
 
    // Dump the buffer to the file
-   if (State == ServerState::Data)
+   if (Req.State == RequestState::Data)
    {
-      if (File == nullptr)
+      if (Req.File.IsOpen() == false)
         return true;
       // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking
       // can't be set
-      if (File->Name() != "/dev/null")
-        SetNonBlock(File->Fd(),false);
+      if (Req.File.Name() != "/dev/null")
+        SetNonBlock(Req.File.Fd(),false);
       while (In.WriteSpace() == true)
       {
-        if (In.Write(File->Fd()) == false)
+        if (In.Write(Req.File.Fd()) == false)
            return _error->Errno("write",_("Error writing to the file"));
 
         // Done
@@ -721,7 +720,7 @@ bool HttpServerState::Die(FileFd * const File)
    }
 
    // See if this is because the server finished the data stream
-   if (In.IsLimit() == false && State != HttpServerState::Header &&
+   if (In.IsLimit() == false && Req.State != RequestState::Header &&
        Persistent == true)
    {
       Close();
@@ -752,7 +751,7 @@ bool HttpServerState::Die(FileFd * const File)
    into the file */
 bool HttpServerState::Flush(FileFd * const File)
 {
-   if (File != NULL)
+   if (File != nullptr)
    {
       // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking
       // can't be set
@@ -779,7 +778,7 @@ bool HttpServerState::Flush(FileFd * const File)
 // ---------------------------------------------------------------------
 /* This runs the select loop over the server FDs, Output file FDs and
    stdin. */
-bool HttpServerState::Go(bool ToFile, FileFd * const File)
+bool HttpServerState::Go(bool ToFile, RequestState &Req)
 {
    // Server has closed the connection
    if (ServerFd == -1 && (In.WriteSpace() == false || 
@@ -800,8 +799,8 @@ bool HttpServerState::Go(bool ToFile, FileFd * const File)
    
    // Add the file
    int FileFD = -1;
-   if (File != NULL)
-      FileFD = File->Fd();
+   if (Req.File.IsOpen())
+      FileFD = Req.File.Fd();
    
    if (In.WriteSpace() == true && ToFile == true && FileFD != -1)
       FD_SET(FileFD,&wfds);
@@ -830,7 +829,7 @@ bool HttpServerState::Go(bool ToFile, FileFd * const File)
    if (Res == 0)
    {
       _error->Error(_("Connection timed out"));
-      return Die(File);
+      return Die(Req);
    }
    
    // Handle server IO
@@ -838,14 +837,14 @@ bool HttpServerState::Go(bool ToFile, FileFd * const File)
    {
       errno = 0;
       if (In.Read(ServerFd) == false)
-        return Die(File);
+        return Die(Req);
    }
         
    if (ServerFd != -1 && FD_ISSET(ServerFd,&wfds))
    {
       errno = 0;
       if (Out.Write(ServerFd) == false)
-        return Die(File);
+        return Die(Req);
    }
 
    // Send data to the file
@@ -855,11 +854,11 @@ bool HttpServerState::Go(bool ToFile, FileFd * const File)
         return _error->Errno("write",_("Error writing to output file"));
    }
 
-   if (MaximumSize > 0 && File && File->Tell() > MaximumSize)
+   if (Req.MaximumSize > 0 && Req.File.IsOpen() && Req.File.Failed() == false && Req.File.Tell() > Req.MaximumSize)
    {
       Owner->SetFailReason("MaximumSizeExceeded");
       return _error->Error("Writing more data than expected (%llu > %llu)",
-                           File->Tell(), MaximumSize);
+                           Req.File.Tell(), Req.MaximumSize);
    }
 
    // Handle commands from APT
@@ -978,32 +977,28 @@ void HttpMethod::RotateDNS()                                              /*{{{*/
    ::RotateDNS();
 }
                                                                        /*}}}*/
-ServerMethod::DealWithHeadersResult HttpMethod::DealWithHeaders(FetchResult &Res)/*{{{*/
+ServerMethod::DealWithHeadersResult HttpMethod::DealWithHeaders(FetchResult &Res, RequestState &Req)/*{{{*/
 {
-   auto ret = ServerMethod::DealWithHeaders(Res);
+   auto ret = ServerMethod::DealWithHeaders(Res, Req);
    if (ret != ServerMethod::FILE_IS_OPEN)
       return ret;
-
-   // Open the file
-   delete File;
-   File = new FileFd(Queue->DestFile,FileFd::WriteAny);
-   if (_error->PendingError() == true)
+   if (Req.File.Open(Queue->DestFile, FileFd::WriteAny) == false)
       return ERROR_NOT_FROM_SERVER;
 
    FailFile = Queue->DestFile;
    FailFile.c_str();   // Make sure we don't do a malloc in the signal handler
-   FailFd = File->Fd();
-   FailTime = Server->Date;
+   FailFd = Req.File.Fd();
+   FailTime = Req.Date;
 
-   if (Server->InitHashes(Queue->ExpectedHashes) == false || Server->AddPartialFileToHashes(*File) == false)
+   if (Server->InitHashes(Queue->ExpectedHashes) == false || Req.AddPartialFileToHashes(Req.File) == false)
    {
       _error->Errno("read",_("Problem hashing file"));
       return ERROR_NOT_FROM_SERVER;
    }
-   if (Server->StartPos > 0)
-      Res.ResumePoint = Server->StartPos;
+   if (Req.StartPos > 0)
+      Res.ResumePoint = Req.StartPos;
 
-   SetNonBlock(File->Fd(),true);
+   SetNonBlock(Req.File.Fd(),true);
    return FILE_IS_OPEN;
 }
                                                                        /*}}}*/
@@ -1015,7 +1010,5 @@ HttpMethod::HttpMethod(std::string &&pProg) : ServerMethod(pProg.c_str(), "1.2",
    auto const plus = Binary.find('+');
    if (plus != std::string::npos)
       addName = Binary.substr(0, plus);
-   File = 0;
-   Server = 0;
 }
                                                                        /*}}}*/
index b7341f5f8671c0f817a4a9db05b12ce949bc4be9..4b0e7752409e521b2b58eb8f2ad88bc6b7b15f58 100644 (file)
@@ -99,23 +99,23 @@ struct HttpServerState: public ServerState
 
    protected:
    virtual bool ReadHeaderLines(std::string &Data) APT_OVERRIDE;
-   virtual bool LoadNextResponse(bool const ToFile, FileFd * const File) APT_OVERRIDE;
+   virtual bool LoadNextResponse(bool const ToFile, RequestState &Req) APT_OVERRIDE;
    virtual bool WriteResponse(std::string const &Data) APT_OVERRIDE;
 
    public:
-   virtual void Reset(bool const Everything = true) APT_OVERRIDE;
+   virtual void Reset() APT_OVERRIDE;
 
-   virtual bool RunData(FileFd * const File) APT_OVERRIDE;
-   virtual bool RunDataToDevNull() APT_OVERRIDE;
+   virtual bool RunData(RequestState &Req) APT_OVERRIDE;
+   virtual bool RunDataToDevNull(RequestState &Req) APT_OVERRIDE;
 
    virtual bool Open() APT_OVERRIDE;
    virtual bool IsOpen() APT_OVERRIDE;
    virtual bool Close() APT_OVERRIDE;
    virtual bool InitHashes(HashStringList const &ExpectedHashes) APT_OVERRIDE;
    virtual Hashes * GetHashes() APT_OVERRIDE;
-   virtual bool Die(FileFd * const File) APT_OVERRIDE;
+   virtual bool Die(RequestState &Req) APT_OVERRIDE;
    virtual bool Flush(FileFd * const File) APT_OVERRIDE;
-   virtual bool Go(bool ToFile, FileFd * const File) APT_OVERRIDE;
+   virtual bool Go(bool ToFile, RequestState &Req) APT_OVERRIDE;
 
    HttpServerState(URI Srv, HttpMethod *Owner);
    virtual ~HttpServerState() {Close();};
@@ -128,7 +128,7 @@ class HttpMethod : public ServerMethod
 
    virtual std::unique_ptr<ServerState> CreateServerState(URI const &uri) APT_OVERRIDE;
    virtual void RotateDNS() APT_OVERRIDE;
-   virtual DealWithHeadersResult DealWithHeaders(FetchResult &Res) APT_OVERRIDE;
+   virtual DealWithHeadersResult DealWithHeaders(FetchResult &Res, RequestState &Req) APT_OVERRIDE;
 
    protected:
    std::string AutoDetectProxyCmd;
index 1bdd394adfd62d5e1ac23eb0e9f87bff4f16919f..c473e474d0e2f1961e496a0dcd72d20ffe62f3a9 100644 (file)
@@ -43,8 +43,9 @@ struct APT_HIDDEN CURLUserPointer {
    HttpsMethod * const https;
    HttpsMethod::FetchResult * const Res;
    HttpsMethod::FetchItem const * const Itm;
+   RequestState * const Req;
    CURLUserPointer(HttpsMethod * const https, HttpsMethod::FetchResult * const Res,
-        HttpsMethod::FetchItem const * const Itm) : https(https), Res(Res), Itm(Itm) {}
+        HttpsMethod::FetchItem const * const Itm, RequestState * const Req) : https(https), Res(Res), Itm(Itm), Req(Req) {}
 };
 
 size_t
@@ -63,55 +64,58 @@ HttpsMethod::parse_header(void *buffer, size_t size, size_t nmemb, void *userp)
 
    if (line.empty() == true)
    {
-      me->https->Server->JunkSize = 0;
-      if (me->https->Server->Result != 416 && me->https->Server->StartPos != 0)
+      if (me->Req->File.Open(me->Itm->DestFile, FileFd::WriteAny) == false)
+        return ERROR_NOT_FROM_SERVER;
+
+      me->Req->JunkSize = 0;
+      if (me->Req->Result != 416 && me->Req->StartPos != 0)
         ;
-      else if (me->https->Server->Result == 416)
+      else if (me->Req->Result == 416)
       {
         bool partialHit = false;
         if (me->Itm->ExpectedHashes.usable() == true)
         {
            Hashes resultHashes(me->Itm->ExpectedHashes);
            FileFd file(me->Itm->DestFile, FileFd::ReadOnly);
-           me->https->Server->TotalFileSize = file.FileSize();
-           me->https->Server->Date = file.ModificationTime();
+           me->Req->TotalFileSize = file.FileSize();
+           me->Req->Date = file.ModificationTime();
            resultHashes.AddFD(file);
            HashStringList const hashList = resultHashes.GetHashStringList();
            partialHit = (me->Itm->ExpectedHashes == hashList);
         }
-        else if (me->https->Server->Result == 416 && me->https->Server->TotalFileSize == me->https->File->FileSize())
+        else if (me->Req->Result == 416 && me->Req->TotalFileSize == me->Req->File.FileSize())
            partialHit = true;
 
         if (partialHit == true)
         {
-           me->https->Server->Result = 200;
-           me->https->Server->StartPos = me->https->Server->TotalFileSize;
+           me->Req->Result = 200;
+           me->Req->StartPos = me->Req->TotalFileSize;
            // the actual size is not important for https as curl will deal with it
            // by itself and e.g. doesn't bother us with transport-encoding…
-           me->https->Server->JunkSize = std::numeric_limits<unsigned long long>::max();
+           me->Req->JunkSize = std::numeric_limits<unsigned long long>::max();
         }
         else
-           me->https->Server->StartPos = 0;
+           me->Req->StartPos = 0;
       }
       else
-        me->https->Server->StartPos = 0;
+        me->Req->StartPos = 0;
 
-      me->Res->LastModified = me->https->Server->Date;
-      me->Res->Size = me->https->Server->TotalFileSize;
-      me->Res->ResumePoint = me->https->Server->StartPos;
+      me->Res->LastModified = me->Req->Date;
+      me->Res->Size = me->Req->TotalFileSize;
+      me->Res->ResumePoint = me->Req->StartPos;
 
       // we expect valid data, so tell our caller we get the file now
-      if (me->https->Server->Result >= 200 && me->https->Server->Result < 300)
+      if (me->Req->Result >= 200 && me->Req->Result < 300)
       {
         if (me->Res->Size != 0 && me->Res->Size > me->Res->ResumePoint)
            me->https->URIStart(*me->Res);
-        if (me->https->Server->AddPartialFileToHashes(*(me->https->File)) == false)
+        if (me->Req->AddPartialFileToHashes(me->Req->File) == false)
            return 0;
       }
       else
-        me->https->Server->JunkSize = std::numeric_limits<decltype(me->https->Server->JunkSize)>::max();
+        me->Req->JunkSize = std::numeric_limits<decltype(me->Req->JunkSize)>::max();
    }
-   else if (me->https->Server->HeaderLine(line) == false)
+   else if (me->Req->HeaderLine(line) == false)
       return 0;
 
    return size*nmemb;
@@ -120,29 +124,29 @@ HttpsMethod::parse_header(void *buffer, size_t size, size_t nmemb, void *userp)
 size_t 
 HttpsMethod::write_data(void *buffer, size_t size, size_t nmemb, void *userp)
 {
-   HttpsMethod *me = static_cast<HttpsMethod *>(userp);
+   CURLUserPointer *me = static_cast<CURLUserPointer *>(userp);
    size_t buffer_size = size * nmemb;
    // we don't need to count the junk here, just drop anything we get as
    // we don't always know how long it would be, e.g. in chunked encoding.
-   if (me->Server->JunkSize != 0)
+   if (me->Req->JunkSize != 0)
       return buffer_size;
 
-   if(me->File->Write(buffer, buffer_size) != true)
+   if(me->Req->File.Write(buffer, buffer_size) != true)
       return 0;
 
-   if(me->Queue->MaximumSize > 0)
+   if(me->https->Queue->MaximumSize > 0)
    {
-      unsigned long long const TotalWritten = me->File->Tell();
-      if (TotalWritten > me->Queue->MaximumSize)
+      unsigned long long const TotalWritten = me->Req->File.Tell();
+      if (TotalWritten > me->https->Queue->MaximumSize)
       {
-        me->SetFailReason("MaximumSizeExceeded");
+        me->https->SetFailReason("MaximumSizeExceeded");
         _error->Error("Writing more data than expected (%llu > %llu)",
-              TotalWritten, me->Queue->MaximumSize);
+              TotalWritten, me->https->Queue->MaximumSize);
         return 0;
       }
    }
 
-   if (me->Server->GetHashes()->Add((unsigned char const * const)buffer, buffer_size) == false)
+   if (me->https->Server->GetHashes()->Add((unsigned char const * const)buffer, buffer_size) == false)
       return 0;
 
    return buffer_size;
@@ -268,15 +272,18 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
       return _error->Error("Unsupported proxy configured: %s", URI::SiteOnly(Proxy).c_str());
 
    maybe_add_auth (Uri, _config->FindFile("Dir::Etc::netrc"));
+   if (Server == nullptr || Server->Comp(Itm->Uri) == false)
+      Server = CreateServerState(Itm->Uri);
 
    FetchResult Res;
-   CURLUserPointer userp(this, &Res, Itm);
+   RequestState Req(this, Server.get());
+   CURLUserPointer userp(this, &Res, Itm, &Req);
    // callbacks
    curl_easy_setopt(curl, CURLOPT_URL, static_cast<string>(Uri).c_str());
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parse_header);
    curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &userp);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
-   curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
+   curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userp);
    // options
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true);
    curl_easy_setopt(curl, CURLOPT_FILETIME, true);
@@ -387,13 +394,6 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
         headers = curl_slist_append(headers, "Accept: text/*");
    }
 
-   // go for it - if the file exists, append on it
-   File = new FileFd(Itm->DestFile, FileFd::WriteAny);
-   if (Server == nullptr || Server->Comp(Itm->Uri) == false)
-      Server = CreateServerState(Itm->Uri);
-   else
-      Server->Reset(false);
-
    // if we have the file send an if-range query with a range header
    if (Server->RangesAllowed && stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
    {
@@ -423,9 +423,9 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
    long curl_condition_unmet = 0;
    curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &curl_condition_unmet);
    if (curl_condition_unmet == 1)
-      Server->Result = 304;
+      Req.Result = 304;
 
-   File->Close();
+   Req.File.Close();
    curl_slist_free_all(headers);
 
    // cleanup
@@ -456,7 +456,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
       return false;
    }
 
-   switch (DealWithHeaders(Res))
+   switch (DealWithHeaders(Res, Req))
    {
       case ServerMethod::IMS_HIT:
         URIDone(Res);
@@ -464,7 +464,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
 
       case ServerMethod::ERROR_WITH_CONTENT_PAGE:
         // unlink, no need keep 401/404 page content in partial/
-        RemoveFile(Binary.c_str(), File->Name());
+        RemoveFile(Binary.c_str(), Req.File.Name());
       case ServerMethod::ERROR_UNRECOVERABLE:
       case ServerMethod::ERROR_NOT_FROM_SERVER:
         return false;
@@ -475,9 +475,9 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
 
       case ServerMethod::FILE_IS_OPEN:
         struct stat resultStat;
-        if (unlikely(stat(File->Name().c_str(), &resultStat) != 0))
+        if (unlikely(stat(Req.File.Name().c_str(), &resultStat) != 0))
         {
-           _error->Errno("stat", "Unable to access file %s", File->Name().c_str());
+           _error->Errno("stat", "Unable to access file %s", Req.File.Name().c_str());
            return false;
         }
         Res.Size = resultStat.st_size;
@@ -490,7 +490,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
            times[0].tv_sec = Res.LastModified;
            times[1].tv_sec = Res.LastModified;
            times[0].tv_usec = times[1].tv_usec = 0;
-           utimes(File->Name().c_str(), times);
+           utimes(Req.File.Name().c_str(), times);
         }
         else
            Res.LastModified = resultStat.st_mtime;
@@ -502,8 +502,6 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
         URIDone(Res);
         break;
    }
-
-   delete File;
    return true;
 }
                                                                        /*}}}*/
index 04e72e8154e68147472edf325a9dfc00a59a5e27..3b99b3abef526b3442ee990e51e00125dc4419af 100644 (file)
@@ -32,23 +32,23 @@ class HttpsServerState : public ServerState
 
    protected:
    virtual bool ReadHeaderLines(std::string &/*Data*/) APT_OVERRIDE { return false; }
-   virtual bool LoadNextResponse(bool const /*ToFile*/, FileFd * const /*File*/) APT_OVERRIDE { return false; }
+   virtual bool LoadNextResponse(bool const /*ToFile*/, RequestState &/*Req*/) APT_OVERRIDE { return false; }
 
    public:
    virtual bool WriteResponse(std::string const &/*Data*/) APT_OVERRIDE { return false; }
 
    /** \brief Transfer the data from the socket */
-   virtual bool RunData(FileFd * const /*File*/) APT_OVERRIDE { return false; }
-   virtual bool RunDataToDevNull() APT_OVERRIDE { return false; }
+   virtual bool RunData(RequestState &) APT_OVERRIDE { return false; }
+   virtual bool RunDataToDevNull(RequestState &) APT_OVERRIDE { return false; }
 
    virtual bool Open() APT_OVERRIDE { return false; }
    virtual bool IsOpen() APT_OVERRIDE { return false; }
    virtual bool Close() APT_OVERRIDE { return false; }
    virtual bool InitHashes(HashStringList const &ExpectedHashes) APT_OVERRIDE;
    virtual Hashes * GetHashes() APT_OVERRIDE;
-   virtual bool Die(FileFd * const /*File*/) APT_OVERRIDE { return false; }
+   virtual bool Die(RequestState &/*Req*/) APT_OVERRIDE { return false; }
    virtual bool Flush(FileFd * const /*File*/) APT_OVERRIDE { return false; }
-   virtual bool Go(bool /*ToFile*/, FileFd * const /*File*/) APT_OVERRIDE { return false; }
+   virtual bool Go(bool /*ToFile*/, RequestState &/*Req*/) APT_OVERRIDE { return false; }
 
    HttpsServerState(URI Srv, HttpsMethod *Owner);
    virtual ~HttpsServerState() {Close();};
index 0408dddfd3f8dfbb8ef395f1b3d8935e8cac712e..29419e5cf982a6e9d8d6a0d6804ce8d43eaa1ef0 100644 (file)
@@ -43,12 +43,10 @@ time_t ServerMethod::FailTime = 0;
 // ---------------------------------------------------------------------
 /* Returns 0 if things are OK, 1 if an IO error occurred and 2 if a header
    parse error occurred */
-ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File,
+ServerState::RunHeadersResult ServerState::RunHeaders(RequestState &Req,
                                                       const std::string &Uri)
 {
-   Reset(false);
    Owner->Status(_("Waiting for headers"));
-
    do
    {
       string Data;
@@ -57,35 +55,32 @@ ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File,
 
       if (Owner->Debug == true)
         clog << "Answer for: " << Uri << endl << Data;
-      
+
       for (string::const_iterator I = Data.begin(); I < Data.end(); ++I)
       {
         string::const_iterator J = I;
         for (; J != Data.end() && *J != '\n' && *J != '\r'; ++J);
-        if (HeaderLine(string(I,J)) == false)
+        if (Req.HeaderLine(string(I,J)) == false)
            return RUN_HEADERS_PARSE_ERROR;
         I = J;
       }
 
       // 100 Continue is a Nop...
-      if (Result == 100)
+      if (Req.Result == 100)
         continue;
       
       // Tidy up the connection persistence state.
-      if (Encoding == Closes && HaveContent == true)
+      if (Req.Encoding == RequestState::Closes && Req.HaveContent == true)
         Persistent = false;
       
       return RUN_HEADERS_OK;
    }
-   while (LoadNextResponse(false, File) == true);
+   while (LoadNextResponse(false, Req) == true);
    
    return RUN_HEADERS_IO_ERROR;
 }
                                                                        /*}}}*/
-// ServerState::HeaderLine - Process a header line                     /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool ServerState::HeaderLine(string Line)
+bool RequestState::HeaderLine(string const &Line)                      /*{{{*/
 {
    if (Line.empty() == true)
       return true;
@@ -116,18 +111,18 @@ bool ServerState::HeaderLine(string Line)
       /* Check the HTTP response header to get the default persistence
          state. */
       if (Major < 1)
-        Persistent = false;
+        Server->Persistent = false;
       else
       {
         if (Major == 1 && Minor == 0)
         {
-           Persistent = false;
+           Server->Persistent = false;
         }
         else
         {
-           Persistent = true;
-           if (PipelineAllowed)
-              Pipeline = true;
+           Server->Persistent = true;
+           if (Server->PipelineAllowed)
+              Server->Pipeline = true;
         }
       }
 
@@ -209,16 +204,16 @@ bool ServerState::HeaderLine(string Line)
    {
       if (stringcasecmp(Val,"close") == 0)
       {
-        Persistent = false;
-        Pipeline = false;
+        Server->Persistent = false;
+        Server->Pipeline = false;
         /* Some servers send error pages (as they are dynamically generated)
            for simplicity via a connection close instead of e.g. chunked,
            so assuming an always closing server only if we get a file + close */
         if (Result >= 200 && Result < 300)
-           PipelineAllowed = false;
+           Server->PipelineAllowed = false;
       }
       else if (stringcasecmp(Val,"keep-alive") == 0)
-        Persistent = true;
+        Server->Persistent = true;
       return true;
    }
 
@@ -240,7 +235,7 @@ bool ServerState::HeaderLine(string Line)
       std::string ranges = ',' + Val + ',';
       ranges.erase(std::remove(ranges.begin(), ranges.end(), ' '), ranges.end());
       if (ranges.find(",bytes,") == std::string::npos)
-        RangesAllowed = false;
+        Server->RangesAllowed = false;
       return true;
    }
 
@@ -249,28 +244,23 @@ bool ServerState::HeaderLine(string Line)
                                                                        /*}}}*/
 // ServerState::ServerState - Constructor                              /*{{{*/
 ServerState::ServerState(URI Srv, ServerMethod *Owner) :
-   DownloadSize(0), ServerName(Srv), TimeOut(120), Owner(Owner)
+   ServerName(Srv), TimeOut(120), Owner(Owner)
 {
    Reset();
 }
                                                                        /*}}}*/
-bool ServerState::AddPartialFileToHashes(FileFd &File)                 /*{{{*/
+bool RequestState::AddPartialFileToHashes(FileFd &File)                        /*{{{*/
 {
    File.Truncate(StartPos);
-   return GetHashes()->AddFD(File, StartPos);
+   return Server->GetHashes()->AddFD(File, StartPos);
 }
                                                                        /*}}}*/
-void ServerState::Reset(bool const Everything)                         /*{{{*/
+void ServerState::Reset()                                              /*{{{*/
 {
-   Major = 0; Minor = 0; Result = 0; Code[0] = '\0';
-   TotalFileSize = 0; JunkSize = 0; StartPos = 0;
-   Encoding = Closes; time(&Date); HaveContent = false;
-   State = Header; MaximumSize = 0;
-   if (Everything)
-   {
-      Persistent = false; Pipeline = false; PipelineAllowed = true;
-      RangesAllowed = true;
-   }
+   Persistent = false;
+   Pipeline = false;
+   PipelineAllowed = true;
+   RangesAllowed = true;
 }
                                                                        /*}}}*/
 
@@ -280,10 +270,10 @@ void ServerState::Reset(bool const Everything)                            /*{{{*/
    to do. Returns DealWithHeadersResult (see http.h for details).
  */
 ServerMethod::DealWithHeadersResult
-ServerMethod::DealWithHeaders(FetchResult &Res)
+ServerMethod::DealWithHeaders(FetchResult &Res, RequestState &Req)
 {
    // Not Modified
-   if (Server->Result == 304)
+   if (Req.Result == 304)
    {
       RemoveFile("server", Queue->DestFile);
       Res.IMSHit = true;
@@ -300,26 +290,26 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
     * redirect.  Pass on those codes so the error handling kicks in.
     */
    if (AllowRedirect
-       && (Server->Result > 300 && Server->Result < 400)
-       && (Server->Result != 300       // Multiple Choices
-           && Server->Result != 304    // Not Modified
-           && Server->Result != 306))  // (Not part of HTTP/1.1, reserved)
+       && (Req.Result > 300 && Req.Result < 400)
+       && (Req.Result != 300       // Multiple Choices
+           && Req.Result != 304    // Not Modified
+           && Req.Result != 306))  // (Not part of HTTP/1.1, reserved)
    {
-      if (Server->Location.empty() == true)
+      if (Req.Location.empty() == true)
         ;
-      else if (Server->Location[0] == '/' && Queue->Uri.empty() == false)
+      else if (Req.Location[0] == '/' && Queue->Uri.empty() == false)
       {
         URI Uri = Queue->Uri;
         if (Uri.Host.empty() == false)
             NextURI = URI::SiteOnly(Uri);
         else
            NextURI.clear();
-        NextURI.append(DeQuoteString(Server->Location));
+        NextURI.append(DeQuoteString(Req.Location));
         if (Queue->Uri == NextURI)
         {
            SetFailReason("RedirectionLoop");
            _error->Error("Redirection loop encountered");
-           if (Server->HaveContent == true)
+           if (Req.HaveContent == true)
               return ERROR_WITH_CONTENT_PAGE;
            return ERROR_UNRECOVERABLE;
         }
@@ -327,12 +317,12 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
       }
       else
       {
-        NextURI = DeQuoteString(Server->Location);
+        NextURI = DeQuoteString(Req.Location);
         URI tmpURI = NextURI;
         if (tmpURI.Access.find('+') != std::string::npos)
         {
            _error->Error("Server tried to trick us into using a specific implementation: %s", tmpURI.Access.c_str());
-           if (Server->HaveContent == true)
+           if (Req.HaveContent == true)
               return ERROR_WITH_CONTENT_PAGE;
            return ERROR_UNRECOVERABLE;
         }
@@ -358,7 +348,7 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
         {
            SetFailReason("RedirectionLoop");
            _error->Error("Redirection loop encountered");
-           if (Server->HaveContent == true)
+           if (Req.HaveContent == true)
               return ERROR_WITH_CONTENT_PAGE;
            return ERROR_UNRECOVERABLE;
         }
@@ -390,7 +380,7 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
       /* else pass through for error message */
    }
    // retry after an invalid range response without partial data
-   else if (Server->Result == 416)
+   else if (Req.Result == 416)
    {
       struct stat SBuf;
       if (stat(Queue->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
@@ -400,25 +390,25 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
         {
            Hashes resultHashes(Queue->ExpectedHashes);
            FileFd file(Queue->DestFile, FileFd::ReadOnly);
-           Server->TotalFileSize = file.FileSize();
-           Server->Date = file.ModificationTime();
+           Req.TotalFileSize = file.FileSize();
+           Req.Date = file.ModificationTime();
            resultHashes.AddFD(file);
            HashStringList const hashList = resultHashes.GetHashStringList();
            partialHit = (Queue->ExpectedHashes == hashList);
         }
-        else if ((unsigned long long)SBuf.st_size == Server->TotalFileSize)
+        else if ((unsigned long long)SBuf.st_size == Req.TotalFileSize)
            partialHit = true;
         if (partialHit == true)
         {
            // the file is completely downloaded, but was not moved
-           if (Server->HaveContent == true)
+           if (Req.HaveContent == true)
            {
               // nuke the sent error page
-              Server->RunDataToDevNull();
-              Server->HaveContent = false;
+              Server->RunDataToDevNull(Req);
+              Req.HaveContent = false;
            }
-           Server->StartPos = Server->TotalFileSize;
-           Server->Result = 200;
+           Req.StartPos = Req.TotalFileSize;
+           Req.Result = 200;
         }
         else if (RemoveFile("server", Queue->DestFile))
         {
@@ -430,23 +420,23 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
 
    /* We have a reply we don't handle. This should indicate a perm server
       failure */
-   if (Server->Result < 200 || Server->Result >= 300)
+   if (Req.Result < 200 || Req.Result >= 300)
    {
       if (_error->PendingError() == false)
       {
         std::string err;
-        strprintf(err, "HttpError%u", Server->Result);
+        strprintf(err, "HttpError%u", Req.Result);
         SetFailReason(err);
-        _error->Error("%u %s", Server->Result, Server->Code);
+        _error->Error("%u %s", Req.Result, Req.Code);
       }
-      if (Server->HaveContent == true)
+      if (Req.HaveContent == true)
         return ERROR_WITH_CONTENT_PAGE;
       return ERROR_UNRECOVERABLE;
    }
 
    // This is some sort of 2xx 'data follows' reply
-   Res.LastModified = Server->Date;
-   Res.Size = Server->TotalFileSize;
+   Res.LastModified = Req.Date;
+   Res.Size = Req.TotalFileSize;
    return FILE_IS_OPEN;
 }
                                                                        /*}}}*/
@@ -605,9 +595,10 @@ int ServerMethod::Loop()
 
       // Fill the pipeline.
       Fetch(0);
-      
+
+      RequestState Req(this, Server.get());
       // Fetch the next URL header data from the server.
-      switch (Server->RunHeaders(File, Queue->Uri))
+      switch (Server->RunHeaders(Req, Queue->Uri))
       {
         case ServerState::RUN_HEADERS_OK:
         break;
@@ -646,7 +637,7 @@ int ServerMethod::Loop()
       // Decide what to do.
       FetchResult Res;
       Res.Filename = Queue->DestFile;
-      switch (DealWithHeaders(Res))
+      switch (DealWithHeaders(Res, Req))
       {
         // Ok, the file is Open
         case FILE_IS_OPEN:
@@ -660,24 +651,23 @@ int ServerMethod::Loop()
             // we could do "Server->MaximumSize = Queue->MaximumSize" here
             // but that would break the clever pipeline messup detection
             // so instead we use the size of the biggest item in the queue
-            Server->MaximumSize = FindMaximumObjectSizeInQueue();
+            Req.MaximumSize = FindMaximumObjectSizeInQueue();
 
-            if (Server->HaveContent)
-              Result = Server->RunData(File);
+            if (Req.HaveContent)
+              Result = Server->RunData(Req);
 
            /* If the server is sending back sizeless responses then fill in
               the size now */
            if (Res.Size == 0)
-              Res.Size = File->Size();
-           
+              Res.Size = Req.File.Size();
+
            // Close the file, destroy the FD object and timestamp it
            FailFd = -1;
-           delete File;
-           File = 0;
-           
+           Req.File.Close();
+
            // Timestamp
            struct timeval times[2];
-           times[0].tv_sec = times[1].tv_sec = Server->Date;
+           times[0].tv_sec = times[1].tv_sec = Req.Date;
            times[0].tv_usec = times[1].tv_usec = 0;
            utimes(Queue->DestFile.c_str(), times);
 
@@ -758,9 +748,6 @@ int ServerMethod::Loop()
         // Hard internal error, kill the connection and fail
         case ERROR_NOT_FROM_SERVER:
         {
-           delete File;
-           File = 0;
-
            Fail();
            RotateDNS();
            Server->Close();
@@ -770,7 +757,7 @@ int ServerMethod::Loop()
         // We need to flush the data, the header is like a 404 w/ error text
         case ERROR_WITH_CONTENT_PAGE:
         {
-           Server->RunDataToDevNull();
+           Server->RunDataToDevNull(Req);
            Fail();
            break;
         }
@@ -779,8 +766,8 @@ int ServerMethod::Loop()
         case TRY_AGAIN_OR_REDIRECT:
         {
            // Clear rest of response if there is content
-           if (Server->HaveContent)
-              Server->RunDataToDevNull();
+           if (Req.HaveContent)
+              Server->RunDataToDevNull(Req);
            Redirect(NextURI);
            break;
         }
@@ -805,7 +792,7 @@ unsigned long long ServerMethod::FindMaximumObjectSizeInQueue() const       /*{{{*/
 }
                                                                        /*}}}*/
 ServerMethod::ServerMethod(std::string &&Binary, char const * const Ver,unsigned long const Flags) :/*{{{*/
-   aptMethod(std::move(Binary), Ver, Flags), Server(nullptr), File(NULL), PipelineDepth(10),
+   aptMethod(std::move(Binary), Ver, Flags), Server(nullptr), PipelineDepth(10),
    AllowRedirect(false), Debug(false)
 {
 }
index c3adba87a48034409e3d6cede3dded4d651d5a1f..6b12c7c7a4b3d68ee942f9ab60bfcd5028eb8ac6 100644 (file)
@@ -12,6 +12,7 @@
 #define APT_SERVER_H
 
 #include <apt-pkg/strutl.h>
+#include <apt-pkg/fileutl.h>
 #include "aptmethod.h"
 
 #include <time.h>
@@ -24,52 +25,62 @@ using std::endl;
 
 class Hashes;
 class ServerMethod;
-class FileFd;
+struct ServerState;
 
-struct ServerState
+struct RequestState
 {
-   // This is the last parsed Header Line
-   unsigned int Major;
-   unsigned int Minor;
-   unsigned int Result;
+   unsigned int Major = 0;
+   unsigned int Minor = 0;
+   unsigned int Result = 0;
    char Code[360];
 
-   // These are some statistics from the last parsed header lines
-
    // total size of the usable content (aka: the file)
-   unsigned long long TotalFileSize;
+   unsigned long long TotalFileSize = 0;
    // size we actually download (can be smaller than Size if we have partial content)
-   unsigned long long DownloadSize;
+   unsigned long long DownloadSize = 0;
    // size of junk content (aka: server error pages)
-   unsigned long long JunkSize;
+   unsigned long long JunkSize = 0;
    // The start of the data (for partial content)
-   unsigned long long StartPos;
+   unsigned long long StartPos = 0;
+
+   unsigned long long MaximumSize = 0;
 
    time_t Date;
-   bool HaveContent;
-   enum {Chunked,Stream,Closes} Encoding;
-   enum {Header, Data} State;
+   bool HaveContent = false;
+   enum {Chunked,Stream,Closes} Encoding = Closes;
+   enum {Header, Data} State = Header;
+   std::string Location;
+
+   FileFd File;
+
+   ServerMethod * const Owner;
+   ServerState * const Server;
+
+   bool HeaderLine(std::string const &Line);
+   bool AddPartialFileToHashes(FileFd &File);
+
+   RequestState(ServerMethod * const Owner, ServerState * const Server) :
+      Owner(Owner), Server(Server) { time(&Date); }
+};
+
+struct ServerState
+{
    bool Persistent;
    bool PipelineAllowed;
    bool RangesAllowed;
-   std::string Location;
 
-   // This is a Persistent attribute of the server itself.
    bool Pipeline;
    URI ServerName;
    URI Proxy;
    unsigned long TimeOut;
 
-   unsigned long long MaximumSize;
-
    protected:
    ServerMethod *Owner;
 
    virtual bool ReadHeaderLines(std::string &Data) = 0;
-   virtual bool LoadNextResponse(bool const ToFile, FileFd * const File) = 0;
+   virtual bool LoadNextResponse(bool const ToFile, RequestState &Req) = 0;
 
    public:
-   bool HeaderLine(std::string Line);
 
    /** \brief Result of the header acquire */
    enum RunHeadersResult {
@@ -81,25 +92,24 @@ struct ServerState
       RUN_HEADERS_PARSE_ERROR
    };
    /** \brief Get the headers before the data */
-   RunHeadersResult RunHeaders(FileFd * const File, const std::string &Uri);
-   bool AddPartialFileToHashes(FileFd &File);
+   RunHeadersResult RunHeaders(RequestState &Req, const std::string &Uri);
 
    bool Comp(URI Other) const {return Other.Host == ServerName.Host && Other.Port == ServerName.Port;};
-   virtual void Reset(bool const Everything = true);
+   virtual void Reset();
    virtual bool WriteResponse(std::string const &Data) = 0;
 
    /** \brief Transfer the data from the socket */
-   virtual bool RunData(FileFd * const File) = 0;
-   virtual bool RunDataToDevNull() = 0;
+   virtual bool RunData(RequestState &Req) = 0;
+   virtual bool RunDataToDevNull(RequestState &Req) = 0;
 
    virtual bool Open() = 0;
    virtual bool IsOpen() = 0;
    virtual bool Close() = 0;
    virtual bool InitHashes(HashStringList const &ExpectedHashes) = 0;
-   virtual Hashes * GetHashes() = 0;
-   virtual bool Die(FileFd * const File) = 0;
+   virtual bool Die(RequestState &Req) = 0;
    virtual bool Flush(FileFd * const File) = 0;
-   virtual bool Go(bool ToFile, FileFd * const File) = 0;
+   virtual bool Go(bool ToFile, RequestState &Req) = 0;
+   virtual Hashes * GetHashes() = 0;
 
    ServerState(URI Srv, ServerMethod *Owner);
    virtual ~ServerState() {};
@@ -112,7 +122,6 @@ class ServerMethod : public aptMethod
 
    std::unique_ptr<ServerState> Server;
    std::string NextURI;
-   FileFd *File;
 
    unsigned long PipelineDepth;
    bool AllowRedirect;
@@ -140,7 +149,7 @@ class ServerMethod : public aptMethod
       TRY_AGAIN_OR_REDIRECT
    };
    /** \brief Handle the retrieved header data */
-   virtual DealWithHeadersResult DealWithHeaders(FetchResult &Res);
+   virtual DealWithHeadersResult DealWithHeaders(FetchResult &Res, RequestState &Req);
 
    // In the event of a fatal signal this file will be closed and timestamped.
    static std::string FailFile;
@@ -148,8 +157,6 @@ class ServerMethod : public aptMethod
    static time_t FailTime;
    static APT_NORETURN void SigTerm(int);
 
-   virtual bool Flush() { return Server->Flush(File); };
-
    int Loop();
 
    virtual void SendReq(FetchItem *Itm) = 0;