]>
git.saurik.com Git - apt.git/blob - methods/http.cc
0c0acf60ff1e35c3f34c4d828bd9c2b0ffcf634d
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: http.cc,v 1.31 1999/05/25 05:56:24 jgg Exp $
4 /* ######################################################################
6 HTTP Aquire Method - This is the HTTP aquire method for APT.
8 It uses HTTP/1.1 and many of the fancy options there-in, such as
9 pipelining, range, if-range and so on. It accepts on the command line
10 a list of url destination pairs and writes to stdout the status of the
11 operation as defined in the APT method spec.
13 It is based on a doubly buffered select loop. All the requests are
14 fed into a single output buffer that is constantly fed out the
15 socket. This provides ideal pipelining as in many cases all of the
16 requests will fit into a single packet. The input socket is buffered
17 the same way and fed into the fd for the file.
19 This double buffering provides fairly substantial transfer rates,
20 compared to wget the http method is about 4% faster. Most importantly,
21 when HTTP is compared with FTP as a protocol the speed difference is
22 huge. In tests over the internet from two sites to llug (via ATM) this
23 program got 230k/s sustained http transfer rates. FTP on the other
24 hand topped out at 170k/s. That combined with the time to setup the
25 FTP connection makes HTTP a vastly superior protocol.
27 ##################################################################### */
29 // Include Files /*{{{*/
30 #include <apt-pkg/fileutl.h>
31 #include <apt-pkg/acquire-method.h>
32 #include <apt-pkg/error.h>
33 #include <apt-pkg/md5.h>
44 /*#include <netinet/in.h>
45 #include <sys/socket.h>
46 #include <arpa/inet.h>
49 #include "rfc2553emu.h"
54 string
HttpMethod::FailFile
;
55 int HttpMethod::FailFd
= -1;
56 time_t HttpMethod::FailTime
= 0;
57 unsigned long PipelineDepth
= 10;
58 unsigned long TimeOut
= 120;
60 // CircleBuf::CircleBuf - Circular input buffer /*{{{*/
61 // ---------------------------------------------------------------------
63 CircleBuf::CircleBuf(unsigned long Size
) : Size(Size
), MD5(0)
65 Buf
= new unsigned char[Size
];
69 // CircleBuf::Reset - Reset to the default state /*{{{*/
70 // ---------------------------------------------------------------------
72 void CircleBuf::Reset()
77 MaxGet
= (unsigned int)-1;
82 MD5
= new MD5Summation
;
86 // CircleBuf::Read - Read from a FD into the circular buffer /*{{{*/
87 // ---------------------------------------------------------------------
88 /* This fills up the buffer with as much data as is in the FD, assuming it
90 bool CircleBuf::Read(int Fd
)
94 // Woops, buffer is full
95 if (InP
- OutP
== Size
)
98 // Write the buffer segment
100 Res
= read(Fd
,Buf
+ (InP%Size
),LeftRead());
112 gettimeofday(&Start
,0);
117 // CircleBuf::Read - Put the string into the buffer /*{{{*/
118 // ---------------------------------------------------------------------
119 /* This will hold the string in and fill the buffer with it as it empties */
120 bool CircleBuf::Read(string Data
)
127 // CircleBuf::FillOut - Fill the buffer from the output queue /*{{{*/
128 // ---------------------------------------------------------------------
130 void CircleBuf::FillOut()
132 if (OutQueue
.empty() == true)
136 // Woops, buffer is full
137 if (InP
- OutP
== Size
)
140 // Write the buffer segment
141 unsigned long Sz
= LeftRead();
142 if (OutQueue
.length() - StrPos
< Sz
)
143 Sz
= OutQueue
.length() - StrPos
;
144 memcpy(Buf
+ (InP%Size
),OutQueue
.begin() + StrPos
,Sz
);
149 if (OutQueue
.length() == StrPos
)
158 // CircleBuf::Write - Write from the buffer into a FD /*{{{*/
159 // ---------------------------------------------------------------------
160 /* This empties the buffer into the FD. */
161 bool CircleBuf::Write(int Fd
)
167 // Woops, buffer is empty
174 // Write the buffer segment
176 Res
= write(Fd
,Buf
+ (OutP%Size
),LeftWrite());
189 MD5
->Add(Buf
+ (OutP%Size
),Res
);
195 // CircleBuf::WriteTillEl - Write from the buffer to a string /*{{{*/
196 // ---------------------------------------------------------------------
197 /* This copies till the first empty line */
198 bool CircleBuf::WriteTillEl(string
&Data
,bool Single
)
200 // We cheat and assume it is unneeded to have more than one buffer load
201 for (unsigned long I
= OutP
; I
< InP
; I
++)
203 if (Buf
[I%Size
] != '\n')
205 for (I
++; I
< InP
&& Buf
[I%Size
] == '\r'; I
++);
209 if (Buf
[I%Size
] != '\n')
211 for (I
++; I
< InP
&& Buf
[I%Size
] == '\r'; I
++);
220 unsigned long Sz
= LeftWrite();
223 if (I
- OutP
< LeftWrite())
225 Data
+= string((char *)(Buf
+ (OutP%Size
)),Sz
);
233 // CircleBuf::Stats - Print out stats information /*{{{*/
234 // ---------------------------------------------------------------------
236 void CircleBuf::Stats()
242 gettimeofday(&Stop
,0);
243 /* float Diff = Stop.tv_sec - Start.tv_sec +
244 (float)(Stop.tv_usec - Start.tv_usec)/1000000;
245 clog << "Got " << InP << " in " << Diff << " at " << InP/Diff << endl;*/
249 // ServerState::ServerState - Constructor /*{{{*/
250 // ---------------------------------------------------------------------
252 ServerState::ServerState(URI Srv
,HttpMethod
*Owner
) : Owner(Owner
),
253 In(64*1024), Out(4*1024),
259 // ServerState::Open - Open a connection to the server /*{{{*/
260 // ---------------------------------------------------------------------
261 /* This opens a connection to the server. */
264 struct addrinfo
*LastHostAddr
= 0;
265 bool ServerState::Open()
267 // Use the already open connection if possible.
275 // Determine the proxy setting
276 if (getenv("http_proxy") == 0)
278 string DefProxy
= _config
->Find("Acquire::http::Proxy");
279 string SpecificProxy
= _config
->Find("Acquire::http::Proxy::" + ServerName
.Host
);
280 if (SpecificProxy
.empty() == false)
282 if (SpecificProxy
== "DIRECT")
285 Proxy
= SpecificProxy
;
291 Proxy
= getenv("http_proxy");
293 // Determine what host and port to use based on the proxy settings
296 if (Proxy
.empty() == true)
298 if (ServerName
.Port
!= 0)
299 Port
= ServerName
.Port
;
300 Host
= ServerName
.Host
;
309 /* We used a cached address record.. Yes this is against the spec but
310 the way we have setup our rotating dns suggests that this is more
312 if (LastHost
!= Host
|| LastPort
!= Port
)
314 Owner
->Status("Connecting to %s",Host
.c_str());
319 snprintf(S
,sizeof(S
),"%u",Port
);
321 // Free the old address structure
322 if (LastHostAddr
!= 0)
324 freeaddrinfo(LastHostAddr
);
328 // We only understand SOCK_STREAM sockets.
329 struct addrinfo Hints
;
330 memset(&Hints
,0,sizeof(Hints
));
331 Hints
.ai_socktype
= SOCK_STREAM
;
333 // Resolve both the host and service simultaneously
334 if (getaddrinfo(Host
.c_str(),S
,&Hints
,&LastHostAddr
) != 0 ||
336 return _error
->Error("Could not resolve '%s'",Host
.c_str());
342 // Connect to the server
344 // Owner->Status("Connecting to %s (%s)",Host.c_str(),inet_ntoa(LastHostA));
345 if ((ServerFd
= socket(LastHostAddr
->ai_family
,LastHostAddr
->ai_socktype
,
346 LastHostAddr
->ai_protocol
)) < 0)
347 return _error
->Errno("socket","Could not create a socket");
348 SetNonBlock(ServerFd
,true);
349 if (connect(ServerFd
,LastHostAddr
->ai_addr
,LastHostAddr
->ai_addrlen
) < 0 &&
350 errno
!= EINPROGRESS
)
351 return _error
->Errno("connect","Connect initiate the connection");
353 /* This implements a timeout for connect by opening the connection
355 if (WaitFd(ServerFd
,true,TimeOut
) == false)
356 return _error
->Error("Could not connect, connection timed out");
358 unsigned int Len
= sizeof(Err
);
359 if (getsockopt(ServerFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
360 return _error
->Errno("getsockopt","Failed");
362 return _error
->Error("Could not connect.");
367 // ServerState::Close - Close a connection to the server /*{{{*/
368 // ---------------------------------------------------------------------
370 bool ServerState::Close()
377 // ServerState::RunHeaders - Get the headers before the data /*{{{*/
378 // ---------------------------------------------------------------------
379 /* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
380 parse error occured */
381 int ServerState::RunHeaders()
385 Owner
->Status("Waiting for file");
399 if (In
.WriteTillEl(Data
) == false)
402 for (string::const_iterator I
= Data
.begin(); I
< Data
.end(); I
++)
404 string::const_iterator J
= I
;
405 for (; J
!= Data
.end() && *J
!= '\n' && *J
!= '\r';J
++);
406 if (HeaderLine(string(I
,J
-I
)) == false)
412 while (Owner
->Go(false,this) == true);
417 // ServerState::RunData - Transfer the data from the socket /*{{{*/
418 // ---------------------------------------------------------------------
420 bool ServerState::RunData()
424 // Chunked transfer encoding is fun..
425 if (Encoding
== Chunked
)
429 // Grab the block size
435 if (In
.WriteTillEl(Data
,true) == true)
438 while ((Last
= Owner
->Go(false,this)) == true);
443 // See if we are done
444 unsigned long Len
= strtol(Data
.c_str(),0,16);
449 // We have to remove the entity trailer
453 if (In
.WriteTillEl(Data
,true) == true && Data
.length() <= 2)
456 while ((Last
= Owner
->Go(false,this)) == true);
459 return !_error
->PendingError();
462 // Transfer the block
464 while (Owner
->Go(true,this) == true)
465 if (In
.IsLimit() == true)
469 if (In
.IsLimit() == false)
472 // The server sends an extra new line before the next block specifier..
477 if (In
.WriteTillEl(Data
,true) == true)
480 while ((Last
= Owner
->Go(false,this)) == true);
487 /* Closes encoding is used when the server did not specify a size, the
488 loss of the connection means we are done */
489 if (Encoding
== Closes
)
492 In
.Limit(Size
- StartPos
);
494 // Just transfer the whole block.
497 if (In
.IsLimit() == false)
501 return !_error
->PendingError();
503 while (Owner
->Go(true,this) == true);
506 return Owner
->Flush(this) && !_error
->PendingError();
509 // ServerState::HeaderLine - Process a header line /*{{{*/
510 // ---------------------------------------------------------------------
512 bool ServerState::HeaderLine(string Line
)
514 if (Line
.empty() == true)
517 // The http server might be trying to do something evil.
518 if (Line
.length() >= MAXLEN
)
519 return _error
->Error("Got a single header line over %u chars",MAXLEN
);
521 string::size_type Pos
= Line
.find(' ');
522 if (Pos
== string::npos
|| Pos
+1 > Line
.length())
524 // Blah, some servers use "connection:closes", evil.
525 Pos
= Line
.find(':');
526 if (Pos
== string::npos
|| Pos
+ 2 > Line
.length())
527 return _error
->Error("Bad header line");
531 // Parse off any trailing spaces between the : and the next word.
532 string::size_type Pos2
= Pos
;
533 while (Pos2
< Line
.length() && isspace(Line
[Pos2
]) != 0)
536 string Tag
= string(Line
,0,Pos
);
537 string Val
= string(Line
,Pos2
);
539 if (stringcasecmp(Tag
.begin(),Tag
.begin()+4,"HTTP") == 0)
541 // Evil servers return no version
544 if (sscanf(Line
.c_str(),"HTTP/%u.%u %u %[^\n]",&Major
,&Minor
,
546 return _error
->Error("The http server sent an invalid reply header");
552 if (sscanf(Line
.c_str(),"HTTP %u %[^\n]",&Result
,Code
) != 2)
553 return _error
->Error("The http server sent an invalid reply header");
559 if (stringcasecmp(Tag
,"Content-Length:") == 0)
561 if (Encoding
== Closes
)
565 // The length is already set from the Content-Range header
569 if (sscanf(Val
.c_str(),"%lu",&Size
) != 1)
570 return _error
->Error("The http server sent an invalid Content-Length header");
574 if (stringcasecmp(Tag
,"Content-Type:") == 0)
580 if (stringcasecmp(Tag
,"Content-Range:") == 0)
584 if (sscanf(Val
.c_str(),"bytes %lu-%*u/%lu",&StartPos
,&Size
) != 2)
585 return _error
->Error("The http server sent an invalid Content-Range header");
586 if ((unsigned)StartPos
> Size
)
587 return _error
->Error("This http server has broken range support");
591 if (stringcasecmp(Tag
,"Transfer-Encoding:") == 0)
594 if (stringcasecmp(Val
,"chunked") == 0)
600 if (stringcasecmp(Tag
,"Last-Modified:") == 0)
602 if (StrToTime(Val
,Date
) == false)
603 return _error
->Error("Unknown date format");
611 // HttpMethod::SendReq - Send the HTTP request /*{{{*/
612 // ---------------------------------------------------------------------
613 /* This places the http request in the outbound buffer */
614 void HttpMethod::SendReq(FetchItem
*Itm
,CircleBuf
&Out
)
618 // The HTTP server expects a hostname with a trailing :port
620 string ProperHost
= Uri
.Host
;
623 sprintf(Buf
,":%u",Uri
.Port
);
628 if (Itm
->Uri
.length() >= sizeof(Buf
))
631 /* Build the request. We include a keep-alive header only for non-proxy
632 requests. This is to tweak old http/1.0 servers that do support keep-alive
633 but not HTTP/1.1 automatic keep-alive. Doing this with a proxy server
634 will glitch HTTP/1.0 proxies because they do not filter it out and
635 pass it on, HTTP/1.1 says the connection should default to keep alive
636 and we expect the proxy to do this */
637 if (Proxy
.empty() == true)
638 sprintf(Buf
,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
639 QuoteString(Uri
.Path
,"~").c_str(),ProperHost
.c_str());
642 /* Generate a cache control header if necessary. We place a max
643 cache age on index files, optionally set a no-cache directive
644 and a no-store directive for archives. */
645 sprintf(Buf
,"GET %s HTTP/1.1\r\nHost: %s\r\n",
646 Itm
->Uri
.c_str(),ProperHost
.c_str());
647 if (_config
->FindB("Acquire::http::No-Cache",false) == true)
648 strcat(Buf
,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
651 if (Itm
->IndexFile
== true)
652 sprintf(Buf
+strlen(Buf
),"Cache-Control: max-age=%u\r\n",
653 _config
->FindI("Acquire::http::Max-Age",60*60*24));
656 if (_config
->FindB("Acquire::http::No-Store",false) == true)
657 strcat(Buf
,"Cache-Control: no-store\r\n");
664 // Check for a partial file
666 if (stat(Itm
->DestFile
.c_str(),&SBuf
) >= 0 && SBuf
.st_size
> 0)
668 // In this case we send an if-range query with a range header
669 sprintf(Buf
,"Range: bytes=%li-\r\nIf-Range: %s\r\n",SBuf
.st_size
- 1,
670 TimeRFC1123(SBuf
.st_mtime
).c_str());
675 if (Itm
->LastModified
!= 0)
677 sprintf(Buf
,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm
->LastModified
).c_str());
682 if (Proxy
.User
.empty() == false || Proxy
.Password
.empty() == false)
683 Req
+= string("Proxy-Authorization: Basic ") +
684 Base64Encode(Proxy
.User
+ ":" + Proxy
.Password
) + "\r\n";
686 Req
+= "User-Agent: Debian APT-HTTP/1.2\r\n\r\n";
687 // cerr << Req << endl;
692 // HttpMethod::Go - Run a single loop /*{{{*/
693 // ---------------------------------------------------------------------
694 /* This runs the select loop over the server FDs, Output file FDs and
696 bool HttpMethod::Go(bool ToFile
,ServerState
*Srv
)
698 // Server has closed the connection
699 if (Srv
->ServerFd
== -1 && Srv
->In
.WriteSpace() == false)
702 fd_set rfds
,wfds
,efds
;
708 if (Srv
->Out
.WriteSpace() == true && Srv
->ServerFd
!= -1)
709 FD_SET(Srv
->ServerFd
,&wfds
);
710 if (Srv
->In
.ReadSpace() == true && Srv
->ServerFd
!= -1)
711 FD_SET(Srv
->ServerFd
,&rfds
);
718 if (Srv
->In
.WriteSpace() == true && ToFile
== true && FileFD
!= -1)
719 FD_SET(FileFD
,&wfds
);
722 FD_SET(STDIN_FILENO
,&rfds
);
726 FD_SET(FileFD
,&efds
);
727 if (Srv
->ServerFd
!= -1)
728 FD_SET(Srv
->ServerFd
,&efds
);
730 // Figure out the max fd
732 if (MaxFd
< Srv
->ServerFd
)
733 MaxFd
= Srv
->ServerFd
;
740 if ((Res
= select(MaxFd
+1,&rfds
,&wfds
,&efds
,&tv
)) < 0)
741 return _error
->Errno("select","Select failed");
745 _error
->Error("Connection timed out");
746 return ServerDie(Srv
);
749 // Some kind of exception (error) on the sockets, die
750 if ((FileFD
!= -1 && FD_ISSET(FileFD
,&efds
)) ||
751 (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&efds
)))
752 return _error
->Error("Socket Exception");
755 if (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&rfds
))
758 if (Srv
->In
.Read(Srv
->ServerFd
) == false)
759 return ServerDie(Srv
);
762 if (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&wfds
))
765 if (Srv
->Out
.Write(Srv
->ServerFd
) == false)
766 return ServerDie(Srv
);
769 // Send data to the file
770 if (FileFD
!= -1 && FD_ISSET(FileFD
,&wfds
))
772 if (Srv
->In
.Write(FileFD
) == false)
773 return _error
->Errno("write","Error writing to output file");
776 // Handle commands from APT
777 if (FD_ISSET(STDIN_FILENO
,&rfds
))
786 // HttpMethod::Flush - Dump the buffer into the file /*{{{*/
787 // ---------------------------------------------------------------------
788 /* This takes the current input buffer from the Server FD and writes it
790 bool HttpMethod::Flush(ServerState
*Srv
)
794 SetNonBlock(File
->Fd(),false);
795 if (Srv
->In
.WriteSpace() == false)
798 while (Srv
->In
.WriteSpace() == true)
800 if (Srv
->In
.Write(File
->Fd()) == false)
801 return _error
->Errno("write","Error writing to file");
802 if (Srv
->In
.IsLimit() == true)
806 if (Srv
->In
.IsLimit() == true || Srv
->Encoding
== ServerState::Closes
)
812 // HttpMethod::ServerDie - The server has closed the connection. /*{{{*/
813 // ---------------------------------------------------------------------
815 bool HttpMethod::ServerDie(ServerState
*Srv
)
817 unsigned int LErrno
= errno
;
819 // Dump the buffer to the file
820 if (Srv
->State
== ServerState::Data
)
822 SetNonBlock(File
->Fd(),false);
823 while (Srv
->In
.WriteSpace() == true)
825 if (Srv
->In
.Write(File
->Fd()) == false)
826 return _error
->Errno("write","Error writing to the file");
829 if (Srv
->In
.IsLimit() == true)
834 // See if this is because the server finished the data stream
835 if (Srv
->In
.IsLimit() == false && Srv
->State
!= ServerState::Header
&&
836 Srv
->Encoding
!= ServerState::Closes
)
840 return _error
->Error("Error reading from server Remote end closed connection");
842 return _error
->Errno("read","Error reading from server");
848 // Nothing left in the buffer
849 if (Srv
->In
.WriteSpace() == false)
852 // We may have got multiple responses back in one packet..
860 // HttpMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
861 // ---------------------------------------------------------------------
862 /* We look at the header data we got back from the server and decide what
866 3 - Unrecoverable error
867 4 - Error with error content page
868 5 - Unrecoverable non-server error (close the connection) */
869 int HttpMethod::DealWithHeaders(FetchResult
&Res
,ServerState
*Srv
)
872 if (Srv
->Result
== 304)
874 unlink(Queue
->DestFile
.c_str());
876 Res
.LastModified
= Queue
->LastModified
;
880 /* We have a reply we dont handle. This should indicate a perm server
882 if (Srv
->Result
< 200 || Srv
->Result
>= 300)
884 _error
->Error("%u %s",Srv
->Result
,Srv
->Code
);
885 if (Srv
->HaveContent
== true)
890 // This is some sort of 2xx 'data follows' reply
891 Res
.LastModified
= Srv
->Date
;
892 Res
.Size
= Srv
->Size
;
896 File
= new FileFd(Queue
->DestFile
,FileFd::WriteAny
);
897 if (_error
->PendingError() == true)
900 FailFile
= Queue
->DestFile
;
901 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
903 FailTime
= Srv
->Date
;
905 // Set the expected size
906 if (Srv
->StartPos
>= 0)
908 Res
.ResumePoint
= Srv
->StartPos
;
909 ftruncate(File
->Fd(),Srv
->StartPos
);
912 // Set the start point
913 lseek(File
->Fd(),0,SEEK_END
);
916 Srv
->In
.MD5
= new MD5Summation
;
918 // Fill the MD5 Hash if the file is non-empty (resume)
919 if (Srv
->StartPos
> 0)
921 lseek(File
->Fd(),0,SEEK_SET
);
922 if (Srv
->In
.MD5
->AddFD(File
->Fd(),Srv
->StartPos
) == false)
924 _error
->Errno("read","Problem hashing file");
927 lseek(File
->Fd(),0,SEEK_END
);
930 SetNonBlock(File
->Fd(),true);
934 // HttpMethod::SigTerm - Handle a fatal signal /*{{{*/
935 // ---------------------------------------------------------------------
936 /* This closes and timestamps the open file. This is neccessary to get
937 resume behavoir on user abort */
938 void HttpMethod::SigTerm(int)
946 UBuf
.actime
= FailTime
;
947 UBuf
.modtime
= FailTime
;
948 utime(FailFile
.c_str(),&UBuf
);
953 // HttpMethod::Fetch - Fetch an item /*{{{*/
954 // ---------------------------------------------------------------------
955 /* This adds an item to the pipeline. We keep the pipeline at a fixed
957 bool HttpMethod::Fetch(FetchItem
*)
962 // Queue the requests
965 for (FetchItem
*I
= Queue
; I
!= 0 && Depth
< (signed)PipelineDepth
; I
= I
->Next
, Depth
++)
967 // Make sure we stick with the same server
968 if (Server
->Comp(I
->Uri
) == false)
975 SendReq(I
,Server
->Out
);
983 // HttpMethod::Configuration - Handle a configuration message /*{{{*/
984 // ---------------------------------------------------------------------
985 /* We stash the desired pipeline depth */
986 bool HttpMethod::Configuration(string Message
)
988 if (pkgAcqMethod::Configuration(Message
) == false)
991 TimeOut
= _config
->FindI("Acquire::http::Timeout",TimeOut
);
992 PipelineDepth
= _config
->FindI("Acquire::http::Pipeline-Depth",
998 // HttpMethod::Loop - Main loop /*{{{*/
999 // ---------------------------------------------------------------------
1001 int HttpMethod::Loop()
1003 signal(SIGTERM
,SigTerm
);
1004 signal(SIGINT
,SigTerm
);
1008 int FailCounter
= 0;
1011 // We have no commands, wait for some to arrive
1014 if (WaitFd(STDIN_FILENO
) == false)
1025 // Connect to the server
1026 if (Server
== 0 || Server
->Comp(Queue
->Uri
) == false)
1029 Server
= new ServerState(Queue
->Uri
,this);
1032 // Reset the pipeline
1033 if (Server
->ServerFd
== -1)
1036 // Connnect to the host
1037 if (Server
->Open() == false)
1045 // Fill the pipeline.
1048 // Fetch the next URL header data from the server.
1049 switch (Server
->RunHeaders())
1054 // The header data is bad
1057 _error
->Error("Bad header Data");
1062 // The server closed a connection during the header get..
1070 if (FailCounter
>= 2)
1072 Fail("Connection timed out",true);
1080 // Decide what to do.
1082 Res
.Filename
= Queue
->DestFile
;
1083 switch (DealWithHeaders(Res
,Server
))
1085 // Ok, the file is Open
1091 bool Result
= Server
->RunData();
1093 // Close the file, destroy the FD object and timestamp it
1099 struct utimbuf UBuf
;
1101 UBuf
.actime
= Server
->Date
;
1102 UBuf
.modtime
= Server
->Date
;
1103 utime(Queue
->DestFile
.c_str(),&UBuf
);
1105 // Send status to APT
1108 Res
.MD5Sum
= Server
->In
.MD5
->Result();
1124 // Hard server error, not found or something
1131 // Hard internal error, kill the connection and fail
1139 // We need to flush the data, the header is like a 404 w/ error text
1144 // Send to content to dev/null
1145 File
= new FileFd("/dev/null",FileFd::WriteExists
);
1153 Fail("Internal error");