X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/893d3e85b98124fc28002be5584b685324646037..ff371080d8672b94ef289f1162704e74959b4ddf:/methods/http.cc diff --git a/methods/http.cc b/methods/http.cc index d4e231fbe..b05444691 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -39,8 +40,10 @@ #include #include #include +#include #include + // Internet stuff #include @@ -48,7 +51,6 @@ #include "connect.h" #include "rfc2553emu.h" #include "http.h" - /*}}}*/ using namespace std; @@ -57,6 +59,7 @@ int HttpMethod::FailFd = -1; time_t HttpMethod::FailTime = 0; unsigned long PipelineDepth = 10; unsigned long TimeOut = 120; +bool AllowRedirect = false; bool Debug = false; URI Proxy; @@ -309,22 +312,27 @@ bool ServerState::Open() Persistent = true; // Determine the proxy setting - if (getenv("http_proxy") == 0) + string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host); + if (!SpecificProxy.empty()) { - string DefProxy = _config->Find("Acquire::http::Proxy"); - string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host); - if (SpecificProxy.empty() == false) - { - if (SpecificProxy == "DIRECT") - Proxy = ""; - else - Proxy = SpecificProxy; - } - else - Proxy = DefProxy; + if (SpecificProxy == "DIRECT") + Proxy = ""; + else + Proxy = SpecificProxy; } else - Proxy = getenv("http_proxy"); + { + string DefProxy = _config->Find("Acquire::http::Proxy"); + if (!DefProxy.empty()) + { + Proxy = DefProxy; + } + else + { + char* result = getenv("http_proxy"); + Proxy = result ? result : ""; + } + } // Parse no_proxy, a , separated list of domains if (getenv("no_proxy") != 0) @@ -368,8 +376,8 @@ bool ServerState::Close() /*}}}*/ // ServerState::RunHeaders - Get the headers before the data /*{{{*/ // --------------------------------------------------------------------- -/* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header - parse error occured */ +/* Returns 0 if things are OK, 1 if an IO error occurred and 2 if a header + parse error occurred */ int ServerState::RunHeaders() { State = Header; @@ -545,7 +553,7 @@ bool ServerState::HeaderLine(string Line) // Evil servers return no version if (Line[4] == '/') { - if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor, + if (sscanf(Line.c_str(),"HTTP/%u.%u %u%[^\n]",&Major,&Minor, &Result,Code) != 4) return _error->Error(_("The HTTP server sent an invalid reply header")); } @@ -553,7 +561,7 @@ bool ServerState::HeaderLine(string Line) { Major = 0; Minor = 9; - if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2) + if (sscanf(Line.c_str(),"HTTP %u%[^\n]",&Result,Code) != 2) return _error->Error(_("The HTTP server sent an invalid reply header")); } @@ -628,6 +636,12 @@ bool ServerState::HeaderLine(string Line) return true; } + if (stringcasecmp(Tag,"Location:") == 0) + { + Location = Val; + return true; + } + return true; } /*}}}*/ @@ -668,23 +682,25 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out) and a no-store directive for archives. */ sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\n", Itm->Uri.c_str(),ProperHost.c_str()); - // only generate a cache control header if we actually want to - // use a cache - if (_config->FindB("Acquire::http::No-Cache",false) == false) + } + // 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) { - 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"); - } + 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"); } } - // generate a no-cache header if needed - if (_config->FindB("Acquire::http::No-Cache",false) == true) - strcat(Buf,"Cache-Control: no-cache\r\nPragma: no-cache\r\n"); string Req = Buf; @@ -711,11 +727,14 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out) Req += string("Proxy-Authorization: Basic ") + Base64Encode(Proxy.User + ":" + 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: Debian APT-HTTP/1.3 ("VERSION")\r\n\r\n"; + } + Req += "User-Agent: " + _config->Find("Acquire::http::User-Agent", + "Debian APT-HTTP/1.3 ("VERSION")") + "\r\n\r\n"; if (Debug == true) cerr << Req << endl; @@ -900,7 +919,9 @@ bool HttpMethod::ServerDie(ServerState *Srv) 1 - IMS hit 3 - Unrecoverable error 4 - Error with error content page - 5 - Unrecoverable non-server error (close the connection) */ + 5 - Unrecoverable non-server error (close the connection) + 6 - Try again with a new or changed URI + */ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) { // Not Modified @@ -912,6 +933,27 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) return 1; } + /* Redirect + * + * Note that it is only OK for us to treat all redirection the same + * because we *always* use GET, not other HTTP methods. There are + * three redirection codes for which it is not appropriate that we + * redirect. Pass on those codes so the error handling kicks in. + */ + if (AllowRedirect + && (Srv->Result > 300 && Srv->Result < 400) + && (Srv->Result != 300 // Multiple Choices + && Srv->Result != 304 // Not Modified + && Srv->Result != 306)) // (Not part of HTTP/1.1, reserved) + { + if (!Srv->Location.empty()) + { + NextURI = Srv->Location; + return 6; + } + /* else pass through for error message */ + } + /* We have a reply we dont handle. This should indicate a perm server failure */ if (Srv->Result < 200 || Srv->Result >= 300) @@ -941,7 +983,8 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) if (Srv->StartPos >= 0) { Res.ResumePoint = Srv->StartPos; - ftruncate(File->Fd(),Srv->StartPos); + if (ftruncate(File->Fd(),Srv->StartPos) < 0) + _error->Errno("ftruncate", _("Failed to truncate file")); } // Set the start point @@ -1025,11 +1068,16 @@ bool HttpMethod::Configuration(string Message) if (pkgAcqMethod::Configuration(Message) == false) return false; + AllowRedirect = _config->FindB("Acquire::http::AllowRedirect",true); TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut); 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; } /*}}}*/ @@ -1038,6 +1086,10 @@ bool HttpMethod::Configuration(string Message) /* */ int HttpMethod::Loop() { + typedef vector StringVector; + typedef vector::iterator StringVectorIterator; + map Redirected; + signal(SIGTERM,SigTerm); signal(SIGINT,SigTerm); @@ -1224,6 +1276,46 @@ int HttpMethod::Loop() break; } + // Try again with a new URL + case 6: + { + // Clear rest of response if there is content + if (Server->HaveContent) + { + File = new FileFd("/dev/null",FileFd::WriteExists); + Server->RunData(); + delete File; + File = 0; + } + + /* Detect redirect loops. No more redirects are allowed + after the same URI is seen twice in a queue item. */ + StringVector &R = Redirected[Queue->DestFile]; + bool StopRedirects = false; + if (R.size() == 0) + R.push_back(Queue->Uri); + else if (R[0] == "STOP" || R.size() > 10) + StopRedirects = true; + else + { + for (StringVectorIterator I = R.begin(); I != R.end(); I++) + if (Queue->Uri == *I) + { + R[0] = "STOP"; + break; + } + + R.push_back(Queue->Uri); + } + + if (StopRedirects == false) + Redirect(NextURI); + else + Fail(); + + break; + } + default: Fail(_("Internal error")); break; @@ -1235,13 +1327,58 @@ int HttpMethod::Loop() return 0; } /*}}}*/ +// 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) + { + 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]; + if (read(InFd, buf, sizeof(buf)) < 0) + return _error->Errno("read", "Failed to read"); + ExecWait(Process, "ProxyAutoDetect"); + + if (Debug) + clog << "auto detect command returned: '" << buf << "'" << endl; + + if (strstr(buf, "http://") == buf) + _config->Set("Acquire::http::proxy", _strstrip(buf)); + + return true; +} + /*}}}*/ int main() { setlocale(LC_ALL, ""); + // ignore SIGPIPE, this can happen on write() if the socket + // closes the connection (this is dealt with via ServerDie()) + signal(SIGPIPE, SIG_IGN); HttpMethod Mth; - return Mth.Loop(); }