X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/649d3c5b7df830a67ad946921233da349c13a826..124e6916b7b02984803ff8217e8163947aae2882:/methods/http.cc diff --git a/methods/http.cc b/methods/http.cc index 71a02e53a..78b20e66d 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -3,7 +3,7 @@ // $Id: http.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $ /* ###################################################################### - HTTP Acquire Method - This is the HTTP aquire method for APT. + HTTP Acquire Method - This is the HTTP acquire method for APT. It uses HTTP/1.1 and many of the fancy options there-in, such as pipelining, range, if-range and so on. @@ -33,25 +33,23 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include -#include #include -#include #include #include -#include -#include #include -#include - -// Internet stuff -#include +#include #include "config.h" #include "connect.h" -#include "rfc2553emu.h" #include "http.h" #include @@ -62,11 +60,12 @@ unsigned long long CircleBuf::BwReadLimit=0; unsigned long long CircleBuf::BwTickReadData=0; struct timeval CircleBuf::BwReadTick={0,0}; const unsigned int CircleBuf::BW_HZ=10; - + // CircleBuf::CircleBuf - Circular input buffer /*{{{*/ // --------------------------------------------------------------------- /* */ -CircleBuf::CircleBuf(unsigned long long Size) : Size(Size), Hash(0) +CircleBuf::CircleBuf(unsigned long long Size) + : Size(Size), Hash(NULL), TotalWriten(0) { Buf = new unsigned char[Size]; Reset(); @@ -82,14 +81,15 @@ void CircleBuf::Reset() InP = 0; OutP = 0; StrPos = 0; + TotalWriten = 0; MaxGet = (unsigned long long)-1; OutQueue = string(); - if (Hash != 0) + if (Hash != NULL) { delete Hash; - Hash = new Hashes; - } -}; + Hash = NULL; + } +} /*}}}*/ // CircleBuf::Read - Read from a FD into the circular buffer /*{{{*/ // --------------------------------------------------------------------- @@ -97,8 +97,6 @@ void CircleBuf::Reset() is non-blocking.. */ bool CircleBuf::Read(int Fd) { - unsigned long long BwReadMax; - while (1) { // Woops, buffer is full @@ -106,7 +104,7 @@ bool CircleBuf::Read(int Fd) return true; // what's left to read in this tick - BwReadMax = CircleBuf::BwReadLimit/BW_HZ; + unsigned long long const BwReadMax = CircleBuf::BwReadLimit/BW_HZ; if(CircleBuf::BwReadLimit) { struct timeval now; @@ -221,8 +219,10 @@ bool CircleBuf::Write(int Fd) return false; } + + TotalWriten += Res; - if (Hash != 0) + if (Hash != NULL) Hash->Add(Buf + (OutP%Size),Res); OutP += Res; @@ -309,6 +309,7 @@ bool HttpServerState::Open() Persistent = true; // Determine the proxy setting + AutoDetectProxy(ServerName); string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host); if (!SpecificProxy.empty()) { @@ -441,10 +442,12 @@ 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 (Encoding == Closes) + if (Persistent == false) In.Limit(-1); + else if (JunkSize != 0) + In.Limit(JunkSize); else - In.Limit(Size - StartPos); + In.Limit(DownloadSize); // Just transfer the whole block. do @@ -476,27 +479,20 @@ bool HttpServerState::WriteResponse(const std::string &Data) /*{{{*/ return Out.Read(Data); } /*}}}*/ -bool HttpServerState::IsOpen() /*{{{*/ +APT_PURE bool HttpServerState::IsOpen() /*{{{*/ { return (ServerFd != -1); } /*}}}*/ -bool HttpServerState::InitHashes(FileFd &File) /*{{{*/ +bool HttpServerState::InitHashes(HashStringList const &ExpectedHashes) /*{{{*/ { delete In.Hash; - In.Hash = new Hashes; - - // Set the expected size and read file for the hashes - if (StartPos >= 0) - { - File.Truncate(StartPos); - - return In.Hash->AddFD(File, StartPos); - } + In.Hash = new Hashes(ExpectedHashes); return true; } /*}}}*/ -Hashes * HttpServerState::GetHashes() /*{{{*/ + +APT_PURE Hashes * HttpServerState::GetHashes() /*{{{*/ { return In.Hash; } @@ -526,7 +522,7 @@ bool HttpServerState::Die(FileFd &File) // See if this is because the server finished the data stream if (In.IsLimit() == false && State != HttpServerState::Header && - Encoding != HttpServerState::Closes) + Persistent == true) { Close(); if (LErrno == 0) @@ -573,7 +569,7 @@ bool HttpServerState::Flush(FileFd * const File) return true; } - if (In.IsLimit() == true || Encoding == ServerState::Closes) + if (In.IsLimit() == true || Persistent == false) return true; } return false; @@ -659,6 +655,13 @@ bool HttpServerState::Go(bool ToFile, FileFd * const File) return _error->Errno("write",_("Error writing to output file")); } + if (MaximumSize > 0 && File && File->Tell() > MaximumSize) + { + Owner->SetFailReason("MaximumSizeExceeded"); + return _error->Error("Writing more data than expected (%llu > %llu)", + File->Tell(), MaximumSize); + } + // Handle commands from APT if (FD_ISSET(STDIN_FILENO,&rfds)) { @@ -678,22 +681,13 @@ void HttpMethod::SendReq(FetchItem *Itm) URI Uri = Itm->Uri; // The HTTP server expects a hostname with a trailing :port - char Buf[1000]; + std::stringstream Req; string ProperHost; if (Uri.Host.find(':') != string::npos) ProperHost = '[' + Uri.Host + ']'; else ProperHost = Uri.Host; - if (Uri.Port != 0) - { - sprintf(Buf,":%u",Uri.Port); - ProperHost += Buf; - } - - // Just in case. - if (Itm->Uri.length() >= sizeof(Buf)) - abort(); /* RFC 2616 §5.1.2 requires absolute URIs for requests to proxies, but while its a must for all servers to accept absolute URIs, @@ -712,30 +706,23 @@ void HttpMethod::SendReq(FetchItem *Itm) in 1.1, can cause problems with proxies, and we are an HTTP/1.1 client anyway. C.f. https://tools.ietf.org/wg/httpbis/trac/ticket/158 */ - sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\n", - requesturi.c_str(),ProperHost.c_str()); + Req << "GET " << requesturi << " HTTP/1.1\r\n"; + if (Uri.Port != 0) + Req << "Host: " << ProperHost << ":" << Uri.Port << "\r\n"; + else + Req << "Host: " << ProperHost << "\r\n"; // generate a cache control header (if needed) - if (_config->FindB("Acquire::http::No-Cache",false) == true) - { - strcat(Buf,"Cache-Control: no-cache\r\nPragma: no-cache\r\n"); - } - else - { - if (Itm->IndexFile == true) - { - sprintf(Buf+strlen(Buf),"Cache-Control: max-age=%u\r\n", - _config->FindI("Acquire::http::Max-Age",0)); - } - else - { - if (_config->FindB("Acquire::http::No-Store",false) == true) - strcat(Buf,"Cache-Control: no-store\r\n"); - } - } + if (_config->FindB("Acquire::http::No-Cache",false) == true) + Req << "Cache-Control: no-cache\r\n" + << "Pragma: no-cache\r\n"; + else if (Itm->IndexFile == true) + Req << "Cache-Control: max-age=" << _config->FindI("Acquire::http::Max-Age",0) << "\r\n"; + else if (_config->FindB("Acquire::http::No-Store",false) == true) + Req << "Cache-Control: no-store\r\n"; // If we ask for uncompressed files servers might respond with content- - // negotation which lets us end up with compressed files we do not support, + // negotiation which lets us end up with compressed files we do not support, // see 657029, 657560 and co, so if we have no extension on the request // ask for text only. As a sidenote: If there is nothing to negotate servers // seem to be nice and ignore it. @@ -744,46 +731,35 @@ void HttpMethod::SendReq(FetchItem *Itm) size_t const filepos = Itm->Uri.find_last_of('/'); string const file = Itm->Uri.substr(filepos + 1); if (flExtension(file) == file) - strcat(Buf,"Accept: text/*\r\n"); + Req << "Accept: text/*\r\n"; } - string Req = Buf; - - // Check for a partial file + // Check for a partial file and send if-queries accordingly struct stat SBuf; if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0) - { - // In this case we send an if-range query with a range header - sprintf(Buf,"Range: bytes=%lli-\r\nIf-Range: %s\r\n",(long long)SBuf.st_size, - TimeRFC1123(SBuf.st_mtime).c_str()); - Req += Buf; - } - else - { - if (Itm->LastModified != 0) - { - sprintf(Buf,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm->LastModified).c_str()); - Req += Buf; - } - } + Req << "Range: bytes=" << SBuf.st_size << "-\r\n" + << "If-Range: " << TimeRFC1123(SBuf.st_mtime) << "\r\n"; + else if (Itm->LastModified != 0) + Req << "If-Modified-Since: " << TimeRFC1123(Itm->LastModified).c_str() << "\r\n"; if (Server->Proxy.User.empty() == false || Server->Proxy.Password.empty() == false) - Req += string("Proxy-Authorization: Basic ") + - Base64Encode(Server->Proxy.User + ":" + Server->Proxy.Password) + "\r\n"; + Req << "Proxy-Authorization: Basic " + << Base64Encode(Server->Proxy.User + ":" + Server->Proxy.Password) << "\r\n"; maybe_add_auth (Uri, _config->FindFile("Dir::Etc::netrc")); if (Uri.User.empty() == false || Uri.Password.empty() == false) - { - Req += string("Authorization: Basic ") + - Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n"; - } - Req += "User-Agent: " + _config->Find("Acquire::http::User-Agent", - "Debian APT-HTTP/1.3 (" PACKAGE_VERSION ")") + "\r\n\r\n"; - + Req << "Authorization: Basic " + << Base64Encode(Uri.User + ":" + Uri.Password) << "\r\n"; + + Req << "User-Agent: " << _config->Find("Acquire::http::User-Agent", + "Debian APT-HTTP/1.3 (" PACKAGE_VERSION ")") << "\r\n"; + + Req << "\r\n"; + if (Debug == true) - cerr << Req << endl; + cerr << Req.str() << endl; - Server->WriteResponse(Req); + Server->WriteResponse(Req.str()); } /*}}}*/ // HttpMethod::Configuration - Handle a configuration message /*{{{*/ @@ -798,69 +774,13 @@ bool HttpMethod::Configuration(string Message) PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth", PipelineDepth); Debug = _config->FindB("Debug::Acquire::http",false); - AutoDetectProxyCmd = _config->Find("Acquire::http::ProxyAutoDetect"); - - // Get the proxy to use - AutoDetectProxy(); - - return true; -} - /*}}}*/ -// HttpMethod::AutoDetectProxy - auto detect proxy /*{{{*/ -// --------------------------------------------------------------------- -/* */ -bool HttpMethod::AutoDetectProxy() -{ - if (AutoDetectProxyCmd.empty()) - return true; - - if (Debug) - clog << "Using auto proxy detect command: " << AutoDetectProxyCmd << endl; - - int Pipes[2] = {-1,-1}; - if (pipe(Pipes) != 0) - return _error->Errno("pipe", "Failed to create Pipe"); - - pid_t Process = ExecFork(); - if (Process == 0) - { - close(Pipes[0]); - dup2(Pipes[1],STDOUT_FILENO); - SetCloseExec(STDOUT_FILENO,false); - - const char *Args[2]; - Args[0] = AutoDetectProxyCmd.c_str(); - Args[1] = 0; - execv(Args[0],(char **)Args); - cerr << "Failed to exec method " << Args[0] << endl; - _exit(100); - } - char buf[512]; - int InFd = Pipes[0]; - close(Pipes[1]); - int res = read(InFd, buf, sizeof(buf)-1); - ExecWait(Process, "ProxyAutoDetect", true); - - if (res < 0) - return _error->Errno("read", "Failed to read"); - if (res == 0) - return _error->Warning("ProxyAutoDetect returned no data"); - - // add trailing \0 - buf[res] = 0; - - if (Debug) - clog << "auto detect command returned: '" << buf << "'" << endl; - - if (strstr(buf, "http://") == buf) - _config->Set("Acquire::http::proxy", _strstrip(buf)); return true; } /*}}}*/ -ServerState * HttpMethod::CreateServerState(URI uri) /*{{{*/ +std::unique_ptr HttpMethod::CreateServerState(URI const &uri)/*{{{*/ { - return new HttpServerState(uri, this); + return std::unique_ptr(new HttpServerState(uri, this)); } /*}}}*/ void HttpMethod::RotateDNS() /*{{{*/