X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/c37030c2566a511787933f801a89430f26e5b18a..dda7233c5d3879f2580543ead0ad7cd76196a160:/methods/http.cc?ds=sidebyside diff --git a/methods/http.cc b/methods/http.cc index d9487be88..df62034e3 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 Aquire Method - This is the HTTP aquire method for APT. + HTTP Acquire Method - This is the HTTP aquire 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. @@ -39,6 +39,7 @@ #include #include #include +#include #include // Internet stuff @@ -57,6 +58,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; @@ -368,8 +370,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 +547,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 +555,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 +630,12 @@ bool ServerState::HeaderLine(string Line) return true; } + if (stringcasecmp(Tag,"Location:") == 0) + { + Location = Val; + return true; + } + return true; } /*}}}*/ @@ -900,7 +908,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 +922,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 +972,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 @@ -996,7 +1028,6 @@ bool HttpMethod::Fetch(FetchItem *) // Queue the requests int Depth = -1; - bool Tail = false; for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; I = I->Next, Depth++) { @@ -1008,8 +1039,6 @@ bool HttpMethod::Fetch(FetchItem *) if (Server->Comp(I->Uri) == false) break; if (QueueBack == I) - Tail = true; - if (Tail == true) { QueueBack = I->Next; SendReq(I,Server->Out); @@ -1028,6 +1057,7 @@ 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); @@ -1041,6 +1071,10 @@ bool HttpMethod::Configuration(string Message) /* */ int HttpMethod::Loop() { + typedef vector StringVector; + typedef vector::iterator StringVectorIterator; + map Redirected; + signal(SIGTERM,SigTerm); signal(SIGINT,SigTerm); @@ -1071,7 +1105,6 @@ int HttpMethod::Loop() delete Server; Server = new ServerState(Queue->Uri,this); } - /* If the server has explicitly said this is the last connection then we pre-emptively shut down the pipeline and tear down the connection. This will speed up HTTP/1.0 servers a tad @@ -1168,8 +1201,24 @@ int HttpMethod::Loop() URIDone(Res); } else - Fail(true); - + { + if (Server->ServerFd == -1) + { + FailCounter++; + _error->Discard(); + Server->Close(); + + if (FailCounter >= 2) + { + Fail(_("Connection failed"),true); + FailCounter = 0; + } + + QueueBack = Queue; + } + else + Fail(true); + } break; } @@ -1212,6 +1261,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; @@ -1227,9 +1316,11 @@ int HttpMethod::Loop() 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(); }