// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: http.cc,v 1.50 2001/03/06 07:15:29 jgg Exp $
+// $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.
#include <signal.h>
#include <stdio.h>
#include <errno.h>
+#include <string.h>
+#include <apti18n.h>
// Internet stuff
#include <netdb.h>
+#include "config.h"
#include "connect.h"
#include "rfc2553emu.h"
#include "http.h"
/*}}}*/
+using namespace std;
string HttpMethod::FailFile;
int HttpMethod::FailFd = -1;
unsigned long PipelineDepth = 10;
unsigned long TimeOut = 120;
bool Debug = false;
+URI Proxy;
+unsigned long CircleBuf::BwReadLimit=0;
+unsigned long CircleBuf::BwTickReadData=0;
+struct timeval CircleBuf::BwReadTick={0,0};
+const unsigned int CircleBuf::BW_HZ=10;
+
// CircleBuf::CircleBuf - Circular input buffer /*{{{*/
// ---------------------------------------------------------------------
/* */
{
Buf = new unsigned char[Size];
Reset();
+
+ CircleBuf::BwReadLimit = _config->FindI("Acquire::http::Dl-Limit",0)*1024;
}
/*}}}*/
// CircleBuf::Reset - Reset to the default state /*{{{*/
is non-blocking.. */
bool CircleBuf::Read(int Fd)
{
+ unsigned long BwReadMax;
+
while (1)
{
// Woops, buffer is full
if (InP - OutP == Size)
return true;
-
+
+ // what's left to read in this tick
+ BwReadMax = CircleBuf::BwReadLimit/BW_HZ;
+
+ if(CircleBuf::BwReadLimit) {
+ struct timeval now;
+ gettimeofday(&now,0);
+
+ unsigned long d = (now.tv_sec-CircleBuf::BwReadTick.tv_sec)*1000000 +
+ now.tv_usec-CircleBuf::BwReadTick.tv_usec;
+ if(d > 1000000/BW_HZ) {
+ CircleBuf::BwReadTick = now;
+ CircleBuf::BwTickReadData = 0;
+ }
+
+ if(CircleBuf::BwTickReadData >= BwReadMax) {
+ usleep(1000000/BW_HZ);
+ return true;
+ }
+ }
+
// Write the buffer segment
int Res;
- Res = read(Fd,Buf + (InP%Size),LeftRead());
+ if(CircleBuf::BwReadLimit) {
+ Res = read(Fd,Buf + (InP%Size),
+ BwReadMax > LeftRead() ? LeftRead() : BwReadMax);
+ } else
+ Res = read(Fd,Buf + (InP%Size),LeftRead());
+ if(Res > 0 && BwReadLimit > 0)
+ CircleBuf::BwTickReadData += Res;
+
if (Res == 0)
return false;
if (Res < 0)
unsigned long Sz = LeftRead();
if (OutQueue.length() - StrPos < Sz)
Sz = OutQueue.length() - StrPos;
- memcpy(Buf + (InP%Size),OutQueue.begin() + StrPos,Sz);
+ memcpy(Buf + (InP%Size),OutQueue.c_str() + StrPos,Sz);
// Advance
StrPos += Sz;
{
if (Buf[I%Size] != '\n')
continue;
- for (I++; I < InP && Buf[I%Size] == '\r'; I++);
+ ++I;
if (Single == false)
{
- if (Buf[I%Size] != '\n')
- continue;
- for (I++; I < InP && Buf[I%Size] == '\r'; I++);
+ if (I < InP && Buf[I%Size] == '\r')
+ ++I;
+ if (I >= InP || Buf[I%Size] != '\n')
+ continue;
+ ++I;
}
- if (I > InP)
- I = InP;
-
Data = "";
while (OutP < I)
{
unsigned long Sz = LeftWrite();
if (Sz == 0)
return false;
- if (I - OutP < LeftWrite())
+ if (I - OutP < Sz)
Sz = I - OutP;
Data += string((char *)(Buf + (OutP%Size)),Sz);
OutP += Sz;
{
State = Header;
- Owner->Status("Waiting for file");
+ Owner->Status(_("Waiting for headers"));
Major = 0;
Minor = 0;
{
string::const_iterator J = I;
for (; J != Data.end() && *J != '\n' && *J != '\r';J++);
- if (HeaderLine(string(I,J-I)) == false)
+ if (HeaderLine(string(I,J)) == false)
return 2;
I = J;
}
// The http server might be trying to do something evil.
if (Line.length() >= MAXLEN)
- return _error->Error("Got a single header line over %u chars",MAXLEN);
+ return _error->Error(_("Got a single header line over %u chars"),MAXLEN);
string::size_type Pos = Line.find(' ');
if (Pos == string::npos || Pos+1 > Line.length())
// Blah, some servers use "connection:closes", evil.
Pos = Line.find(':');
if (Pos == string::npos || Pos + 2 > Line.length())
- return _error->Error("Bad header line");
+ return _error->Error(_("Bad header line"));
Pos++;
}
string Tag = string(Line,0,Pos);
string Val = string(Line,Pos2);
- if (stringcasecmp(Tag.begin(),Tag.begin()+4,"HTTP") == 0)
+ if (stringcasecmp(Tag.c_str(),Tag.c_str()+4,"HTTP") == 0)
{
// Evil servers return no version
if (Line[4] == '/')
{
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");
+ return _error->Error(_("The HTTP server sent an invalid reply header"));
}
else
{
Major = 0;
Minor = 9;
if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
- return _error->Error("The http server sent an invalid reply header");
+ return _error->Error(_("The HTTP server sent an invalid reply header"));
}
/* Check the HTTP response header to get the default persistance
return true;
if (sscanf(Val.c_str(),"%lu",&Size) != 1)
- return _error->Error("The http server sent an invalid Content-Length header");
+ return _error->Error(_("The HTTP server sent an invalid Content-Length header"));
return true;
}
HaveContent = true;
if (sscanf(Val.c_str(),"bytes %lu-%*u/%lu",&StartPos,&Size) != 2)
- return _error->Error("The http server sent an invalid Content-Range header");
+ return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
if ((unsigned)StartPos > Size)
- return _error->Error("This http server has broken range support");
+ return _error->Error(_("This HTTP server has broken range support"));
return true;
}
if (stringcasecmp(Tag,"Last-Modified:") == 0)
{
if (StrToTime(Val,Date) == false)
- return _error->Error("Unknown date format");
+ return _error->Error(_("Unknown date format"));
return true;
}
will glitch HTTP/1.0 proxies because they do not filter it out and
pass it on, HTTP/1.1 says the connection should default to keep alive
and we expect the proxy to do this */
- if (Proxy.empty() == true)
+ if (Proxy.empty() == true || Proxy.Host.empty())
sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
QuoteString(Uri.Path,"~").c_str(),ProperHost.c_str());
else
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());
- if (_config->FindB("Acquire::http::No-Cache",false) == true)
- strcat(Buf,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
- else
+ // only generate a cache control header if we actually want to
+ // use a cache
+ if (_config->FindB("Acquire::http::No-Cache",false) == false)
{
if (Itm->IndexFile == true)
sprintf(Buf+strlen(Buf),"Cache-Control: max-age=%u\r\n",
- _config->FindI("Acquire::http::Max-Age",60*60*24));
+ _config->FindI("Acquire::http::Max-Age",0));
else
{
if (_config->FindB("Acquire::http::No-Store",false) == true)
}
}
}
+ // 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;
Req += string("Authorization: Basic ") +
Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n";
- Req += "User-Agent: Debian APT-HTTP/1.3\r\n\r\n";
+ Req += "User-Agent: Ubuntu APT-HTTP/1.3 ("VERSION")\r\n\r\n";
if (Debug == true)
cerr << Req << endl;
tv.tv_usec = 0;
int Res = 0;
if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0)
- return _error->Errno("select","Select failed");
+ {
+ if (errno == EINTR)
+ return true;
+ return _error->Errno("select",_("Select failed"));
+ }
if (Res == 0)
{
- _error->Error("Connection timed out");
+ _error->Error(_("Connection timed out"));
return ServerDie(Srv);
}
if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
{
if (Srv->In.Write(FileFD) == false)
- return _error->Errno("write","Error writing to output file");
+ return _error->Errno("write",_("Error writing to output file"));
}
// Handle commands from APT
{
if (File != 0)
{
- SetNonBlock(File->Fd(),false);
+ // 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 (Srv->In.WriteSpace() == false)
return true;
while (Srv->In.WriteSpace() == true)
{
if (Srv->In.Write(File->Fd()) == false)
- return _error->Errno("write","Error writing to file");
+ return _error->Errno("write",_("Error writing to file"));
if (Srv->In.IsLimit() == true)
return true;
}
// Dump the buffer to the file
if (Srv->State == ServerState::Data)
{
- SetNonBlock(File->Fd(),false);
+ // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking
+ // can't be set
+ if (File->Name() != "/dev/null")
+ SetNonBlock(File->Fd(),false);
while (Srv->In.WriteSpace() == true)
{
if (Srv->In.Write(File->Fd()) == false)
- return _error->Errno("write","Error writing to the file");
+ return _error->Errno("write",_("Error writing to the file"));
// Done
if (Srv->In.IsLimit() == true)
{
Srv->Close();
if (LErrno == 0)
- return _error->Error("Error reading from server Remote end closed connection");
+ return _error->Error(_("Error reading from server. Remote end closed connection"));
errno = LErrno;
- return _error->Errno("read","Error reading from server");
+ return _error->Errno("read",_("Error reading from server"));
}
else
{
failure */
if (Srv->Result < 200 || Srv->Result >= 300)
{
+ char err[255];
+ snprintf(err,sizeof(err)-1,"HttpError%i",Srv->Result);
+ SetFailReason(err);
_error->Error("%u %s",Srv->Result,Srv->Code);
if (Srv->HaveContent == true)
return 4;
lseek(File->Fd(),0,SEEK_SET);
if (Srv->In.Hash->AddFD(File->Fd(),Srv->StartPos) == false)
{
- _error->Errno("read","Problem hashing file");
+ _error->Errno("read",_("Problem hashing file"));
return 5;
}
lseek(File->Fd(),0,SEEK_END);
depth. */
bool HttpMethod::Fetch(FetchItem *)
{
- if (Server == 0)
+ if (Server == 0)
return true;
// Queue the requests
// The header data is bad
case 2:
{
- _error->Error("Bad header Data");
+ _error->Error(_("Bad header data"));
Fail(true);
RotateDNS();
continue;
if (FailCounter >= 2)
{
- Fail("Connection failed",true);
+ Fail(_("Connection failed"),true);
FailCounter = 0;
}
// Send status to APT
if (Result == true)
{
- Res.MD5Sum = Server->In.Hash->MD5.Result();
+ Res.TakeHashes(*Server->In.Hash);
URIDone(Res);
}
else
// Hard internal error, kill the connection and fail
case 5:
{
+ delete File;
+ File = 0;
+
Fail();
RotateDNS();
Server->Close();
}
default:
- Fail("Internal error");
+ Fail(_("Internal error"));
break;
}
}
/*}}}*/
-int main()
-{
- HttpMethod Mth;
-
- return Mth.Loop();
-}
+
+