X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/7f9b781e30dbf73725a30df7e4608d58fe6e2386..10639577016904cccab77c311e7c0b4363997504:/methods/http.cc diff --git a/methods/http.cc b/methods/http.cc index 0c7445d76..c1c5d8a4a 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -1,20 +1,18 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: http.cc,v 1.20 1999/01/04 00:51:23 jgg Exp $ +// $Id: http.cc,v 1.49 2001/02/23 07:19:49 jgg Exp $ /* ###################################################################### HTTP Aquire 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. It accepts on the command line - a list of url destination pairs and writes to stdout the status of the - operation as defined in the APT method spec. - - It is based on a doubly buffered select loop. All the requests are + pipelining, range, if-range and so on. + + It is based on a doubly buffered select loop. A groupe of requests are fed into a single output buffer that is constantly fed out the socket. This provides ideal pipelining as in many cases all of the requests will fit into a single packet. The input socket is buffered - the same way and fed into the fd for the file. + the same way and fed into the fd for the file (may be a pipe in future). This double buffering provides fairly substantial transfer rates, compared to wget the http method is about 4% faster. Most importantly, @@ -38,14 +36,15 @@ #include #include #include +#include // Internet stuff -#include -#include -#include #include +#include "connect.h" +#include "rfc2553emu.h" #include "http.h" + /*}}}*/ string HttpMethod::FailFile; @@ -53,6 +52,7 @@ int HttpMethod::FailFd = -1; time_t HttpMethod::FailTime = 0; unsigned long PipelineDepth = 10; unsigned long TimeOut = 120; +bool Debug = false; // CircleBuf::CircleBuf - Circular input buffer /*{{{*/ // --------------------------------------------------------------------- @@ -256,8 +256,6 @@ ServerState::ServerState(URI Srv,HttpMethod *Owner) : Owner(Owner), // ServerState::Open - Open a connection to the server /*{{{*/ // --------------------------------------------------------------------- /* This opens a connection to the server. */ -string LastHost; -in_addr LastHostA; bool ServerState::Open() { // Use the already open connection if possible. @@ -267,7 +265,8 @@ bool ServerState::Open() Close(); In.Reset(); Out.Reset(); - + Persistent = true; + // Determine the proxy setting if (getenv("http_proxy") == 0) { @@ -286,10 +285,17 @@ bool ServerState::Open() else Proxy = getenv("http_proxy"); + // Parse no_proxy, a , separated list of domains + if (getenv("no_proxy") != 0) + { + if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true) + Proxy = ""; + } + // Determine what host and port to use based on the proxy settings - int Port = 80; + int Port = 0; string Host; - if (Proxy.empty() == true) + if (Proxy.empty() == true || Proxy.Host.empty() == true) { if (ServerName.Port != 0) Port = ServerName.Port; @@ -302,55 +308,9 @@ bool ServerState::Open() Host = Proxy.Host; } - /* We used a cached address record.. Yes this is against the spec but - the way we have setup our rotating dns suggests that this is more - sensible */ - if (LastHost != Host) - { - Owner->Status("Connecting to %s",Host.c_str()); - - // Lookup the host - hostent *Addr = gethostbyname(Host.c_str()); - if (Addr == 0 || Addr->h_addr_list[0] == 0) - return _error->Error("Could not resolve '%s'",Host.c_str()); - LastHost = Host; - LastHostA = *(in_addr *)(Addr->h_addr_list[0]); - } - - Owner->Status("Connecting to %s (%s)",Host.c_str(),inet_ntoa(LastHostA)); - - // Get a socket - if ((ServerFd = socket(AF_INET,SOCK_STREAM,0)) < 0) - return _error->Errno("socket","Could not create a socket"); - - // Connect to the server - struct sockaddr_in server; - server.sin_family = AF_INET; - server.sin_port = htons(Port); - server.sin_addr = LastHostA; - SetNonBlock(ServerFd,true); - if (connect(ServerFd,(sockaddr *)&server,sizeof(server)) < 0 && - errno != EINPROGRESS) - return _error->Errno("socket","Could not create a socket"); - - /* This implements a timeout for connect by opening the connection - nonblocking */ - fd_set wfds; - FD_ZERO(&wfds); - FD_SET(ServerFd,&wfds); - struct timeval tv; - tv.tv_sec = TimeOut; - tv.tv_usec = 0; - int Res = 0; - if ((Res = select(ServerFd+1,0,&wfds,0,&tv)) < 0) - return _error->Errno("select","Select failed"); - if (Res == 0) - return _error->Error("Could not connect, connection timed out"); - unsigned int Err,Len=sizeof(Err); - if (getsockopt(ServerFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0) - return _error->Errno("getsockopt","Failed"); - if (Err != 0) - return _error->Error("Could not connect."); + // Connect to the remote server + if (Connect(Host,Port,"http",80,ServerFd,TimeOut,Owner) == false) + return false; return true; } @@ -389,6 +349,9 @@ int ServerState::RunHeaders() string Data; if (In.WriteTillEl(Data) == false) continue; + + if (Debug == true) + clog << Data; for (string::const_iterator I = Data.begin(); I < Data.end(); I++) { @@ -398,10 +361,19 @@ int ServerState::RunHeaders() return 2; I = J; } + + // 100 Continue is a Nop... + if (Result == 100) + continue; + + // Tidy up the connection persistance state. + if (Encoding == Closes && HaveContent == true) + Persistent = false; + return 0; } while (Owner->Go(false,this) == true); - + return 1; } /*}}}*/ @@ -447,7 +419,7 @@ bool ServerState::RunData() while ((Last = Owner->Go(false,this)) == true); if (Last == false) return false; - return true; + return !_error->PendingError(); } // Transfer the block @@ -489,12 +461,12 @@ bool ServerState::RunData() continue; In.Limit(-1); - return true; + return !_error->PendingError(); } while (Owner->Go(true,this) == true); } - return Owner->Flush(this); + return Owner->Flush(this) && !_error->PendingError(); } /*}}}*/ // ServerState::HeaderLine - Process a header line /*{{{*/ @@ -511,11 +483,22 @@ bool ServerState::HeaderLine(string Line) string::size_type Pos = Line.find(' '); if (Pos == string::npos || Pos+1 > Line.length()) - return _error->Error("Bad header line"); - - string Tag = string(Line,0,Pos); - string Val = string(Line,Pos+1); + { + // Blah, some servers use "connection:closes", evil. + Pos = Line.find(':'); + if (Pos == string::npos || Pos + 2 > Line.length()) + return _error->Error("Bad header line"); + Pos++; + } + // Parse off any trailing spaces between the : and the next word. + string::size_type Pos2 = Pos; + while (Pos2 < Line.length() && isspace(Line[Pos2]) != 0) + Pos2++; + + string Tag = string(Line,0,Pos); + string Val = string(Line,Pos2); + if (stringcasecmp(Tag.begin(),Tag.begin()+4,"HTTP") == 0) { // Evil servers return no version @@ -532,7 +515,19 @@ bool ServerState::HeaderLine(string Line) if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2) return _error->Error("The http server sent an invalid reply header"); } - + + /* Check the HTTP response header to get the default persistance + state. */ + if (Major < 1) + Persistent = false; + else + { + if (Major == 1 && Minor <= 0) + Persistent = false; + else + Persistent = true; + } + return true; } @@ -572,11 +567,19 @@ bool ServerState::HeaderLine(string Line) { HaveContent = true; if (stringcasecmp(Val,"chunked") == 0) - Encoding = Chunked; - + Encoding = Chunked; return true; } + if (stringcasecmp(Tag,"Connection:") == 0) + { + if (stringcasecmp(Val,"close") == 0) + Persistent = false; + if (stringcasecmp(Val,"keep-alive") == 0) + Persistent = true; + return true; + } + if (stringcasecmp(Tag,"Last-Modified:") == 0) { if (StrToTime(Val,Date) == false) @@ -646,7 +649,7 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out) 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=%li-\r\nIf-Range: %s\r\n",SBuf.st_size - 1, + sprintf(Buf,"Range: bytes=%li-\r\nIf-Range: %s\r\n",(long)SBuf.st_size - 1, TimeRFC1123(SBuf.st_mtime).c_str()); Req += Buf; } @@ -663,8 +666,14 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out) Req += string("Proxy-Authorization: Basic ") + Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n"; - Req += "User-Agent: Debian APT-HTTP/1.2\r\n\r\n"; - cerr << Req << endl; + 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\r\n\r\n"; + + if (Debug == true) + cerr << Req << endl; Out.Read(Req); } @@ -676,18 +685,20 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out) bool HttpMethod::Go(bool ToFile,ServerState *Srv) { // Server has closed the connection - if (Srv->ServerFd == -1 && Srv->In.WriteSpace() == false) + if (Srv->ServerFd == -1 && (Srv->In.WriteSpace() == false || + ToFile == false)) return false; - fd_set rfds,wfds,efds; + fd_set rfds,wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); - FD_ZERO(&efds); - // Add the server - if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1) + /* Add the server. We only send more requests if the connection will + be persisting */ + if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1 + && Srv->Persistent == true) FD_SET(Srv->ServerFd,&wfds); - if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1) + if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1) FD_SET(Srv->ServerFd,&rfds); // Add the file @@ -701,23 +712,17 @@ bool HttpMethod::Go(bool ToFile,ServerState *Srv) // Add stdin FD_SET(STDIN_FILENO,&rfds); - // Error Set - if (FileFD != -1) - FD_SET(FileFD,&efds); - if (Srv->ServerFd != -1) - FD_SET(Srv->ServerFd,&efds); - // Figure out the max fd int MaxFd = FileFD; if (MaxFd < Srv->ServerFd) MaxFd = Srv->ServerFd; - + // Select struct timeval tv; tv.tv_sec = TimeOut; tv.tv_usec = 0; int Res = 0; - if ((Res = select(MaxFd+1,&rfds,&wfds,&efds,&tv)) < 0) + if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0) return _error->Errno("select","Select failed"); if (Res == 0) @@ -726,11 +731,6 @@ bool HttpMethod::Go(bool ToFile,ServerState *Srv) return ServerDie(Srv); } - // Some kind of exception (error) on the sockets, die - if ((FileFD != -1 && FD_ISSET(FileFD,&efds)) || - (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&efds))) - return _error->Error("Socket Exception"); - // Handle server IO if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds)) { @@ -756,7 +756,7 @@ bool HttpMethod::Go(bool ToFile,ServerState *Srv) // Handle commands from APT if (FD_ISSET(STDIN_FILENO,&rfds)) { - if (Run(true) != 0) + if (Run(true) != -1) exit(100); } @@ -794,6 +794,8 @@ bool HttpMethod::Flush(ServerState *Srv) /* */ bool HttpMethod::ServerDie(ServerState *Srv) { + unsigned int LErrno = errno; + // Dump the buffer to the file if (Srv->State == ServerState::Data) { @@ -814,8 +816,9 @@ bool HttpMethod::ServerDie(ServerState *Srv) Srv->Encoding != ServerState::Closes) { Srv->Close(); - if (errno == 0) + if (LErrno == 0) return _error->Error("Error reading from server Remote end closed connection"); + errno = LErrno; return _error->Errno("read","Error reading from server"); } else @@ -875,6 +878,7 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) return 5; FailFile = Queue->DestFile; + FailFile.c_str(); // Make sure we dont do a malloc in the signal handler FailFd = File->Fd(); FailTime = Srv->Date; @@ -914,17 +918,16 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) void HttpMethod::SigTerm(int) { if (FailFd == -1) - exit(100); + _exit(100); close(FailFd); // Timestamp struct utimbuf UBuf; - time(&UBuf.actime); UBuf.actime = FailTime; UBuf.modtime = FailTime; utime(FailFile.c_str(),&UBuf); - exit(100); + _exit(100); } /*}}}*/ // HttpMethod::Fetch - Fetch an item /*{{{*/ @@ -939,8 +942,13 @@ 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++) + for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; + I = I->Next, Depth++) { + // If pipelining is disabled, we only queue 1 request + if (Server->Pipeline == false && Depth >= 0) + break; + // Make sure we stick with the same server if (Server->Comp(I->Uri) == false) break; @@ -951,7 +959,7 @@ bool HttpMethod::Fetch(FetchItem *) QueueBack = I->Next; SendReq(I,Server->Out); continue; - } + } } return true; @@ -968,6 +976,7 @@ bool HttpMethod::Configuration(string Message) TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut); PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth", PipelineDepth); + Debug = _config->FindB("Debug::Acquire::http",false); return true; } @@ -984,13 +993,7 @@ int HttpMethod::Loop() int FailCounter = 0; while (1) - { - if (FailCounter >= 2) - { - Fail("Massive Server Brain Damage"); - FailCounter = 0; - } - + { // We have no commands, wait for some to arrive if (Queue == 0) { @@ -998,8 +1001,10 @@ int HttpMethod::Loop() return 0; } - // Run messages - if (Run(true) != 0) + /* Run messages, we can accept 0 (no message) if we didn't + do a WaitFd above.. Otherwise the FD is closed. */ + int Result = Run(true); + if (Result != -1 && (Result != 0 || Queue == 0)) return 100; if (Queue == 0) @@ -1011,7 +1016,15 @@ 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 + since we don't have to wait for the close sequence to + complete */ + if (Server->Persistent == false) + Server->Close(); + // Reset the pipeline if (Server->ServerFd == -1) QueueBack = Queue; @@ -1019,7 +1032,9 @@ int HttpMethod::Loop() // Connnect to the host if (Server->Open() == false) { - Fail(); + Fail(true); + delete Server; + Server = 0; continue; } @@ -1036,7 +1051,8 @@ int HttpMethod::Loop() case 2: { _error->Error("Bad header Data"); - Fail(); + Fail(true); + RotateDNS(); continue; } @@ -1047,6 +1063,15 @@ int HttpMethod::Loop() FailCounter++; _error->Discard(); Server->Close(); + Server->Pipeline = false; + + if (FailCounter >= 2) + { + Fail("Connection failed",true); + FailCounter = 0; + } + + RotateDNS(); continue; } }; @@ -1064,6 +1089,11 @@ int HttpMethod::Loop() // Run the data bool Result = Server->RunData(); + /* If the server is sending back sizeless responses then fill in + the size now */ + if (Res.Size == 0) + Res.Size = File->Size(); + // Close the file, destroy the FD object and timestamp it FailFd = -1; delete File; @@ -1079,12 +1109,12 @@ int HttpMethod::Loop() // Send status to APT if (Result == true) { - Res.MD5Sum = Server->In.MD5->Result(); + Res.MD5Sum = Server->In.MD5->Result(); URIDone(Res); } else - Fail(); - + Fail(true); + break; } @@ -1106,6 +1136,7 @@ int HttpMethod::Loop() case 5: { Fail(); + RotateDNS(); Server->Close(); break; }