diff options
-rw-r--r-- | methods/http.cc | 99 | ||||
-rw-r--r-- | methods/http.h | 14 | ||||
-rw-r--r-- | methods/https.cc | 90 | ||||
-rw-r--r-- | methods/https.h | 10 | ||||
-rw-r--r-- | methods/server.cc | 151 | ||||
-rw-r--r-- | methods/server.h | 73 |
6 files changed, 211 insertions, 226 deletions
diff --git a/methods/http.cc b/methods/http.cc index 8d3c569c1..b460644dd 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -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; } /*}}}*/ diff --git a/methods/http.h b/methods/http.h index b7341f5f8..4b0e77524 100644 --- a/methods/http.h +++ b/methods/http.h @@ -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; diff --git a/methods/https.cc b/methods/https.cc index 1bdd394ad..c473e474d 100644 --- a/methods/https.cc +++ b/methods/https.cc @@ -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; } /*}}}*/ diff --git a/methods/https.h b/methods/https.h index 04e72e815..3b99b3abe 100644 --- a/methods/https.h +++ b/methods/https.h @@ -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();}; diff --git a/methods/server.cc b/methods/server.cc index 0408dddfd..29419e5cf 100644 --- a/methods/server.cc +++ b/methods/server.cc @@ -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) { } diff --git a/methods/server.h b/methods/server.h index c3adba87a..6b12c7c7a 100644 --- a/methods/server.h +++ b/methods/server.h @@ -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; |