]>
git.saurik.com Git - apt.git/blob - methods/http.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: http.cc,v 1.24 1999/01/30 08:23:49 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>
43 #include <netinet/in.h>
44 #include <sys/socket.h>
45 #include <arpa/inet.h>
51 string
HttpMethod::FailFile
;
52 int HttpMethod::FailFd
= -1;
53 time_t HttpMethod::FailTime
= 0;
54 unsigned long PipelineDepth
= 10;
55 unsigned long TimeOut
= 120;
57 // CircleBuf::CircleBuf - Circular input buffer /*{{{*/
58 // ---------------------------------------------------------------------
60 CircleBuf::CircleBuf(unsigned long Size
) : Size(Size
), MD5(0)
62 Buf
= new unsigned char[Size
];
66 // CircleBuf::Reset - Reset to the default state /*{{{*/
67 // ---------------------------------------------------------------------
69 void CircleBuf::Reset()
74 MaxGet
= (unsigned int)-1;
79 MD5
= new MD5Summation
;
83 // CircleBuf::Read - Read from a FD into the circular buffer /*{{{*/
84 // ---------------------------------------------------------------------
85 /* This fills up the buffer with as much data as is in the FD, assuming it
87 bool CircleBuf::Read(int Fd
)
91 // Woops, buffer is full
92 if (InP
- OutP
== Size
)
95 // Write the buffer segment
97 Res
= read(Fd
,Buf
+ (InP%Size
),LeftRead());
109 gettimeofday(&Start
,0);
114 // CircleBuf::Read - Put the string into the buffer /*{{{*/
115 // ---------------------------------------------------------------------
116 /* This will hold the string in and fill the buffer with it as it empties */
117 bool CircleBuf::Read(string Data
)
124 // CircleBuf::FillOut - Fill the buffer from the output queue /*{{{*/
125 // ---------------------------------------------------------------------
127 void CircleBuf::FillOut()
129 if (OutQueue
.empty() == true)
133 // Woops, buffer is full
134 if (InP
- OutP
== Size
)
137 // Write the buffer segment
138 unsigned long Sz
= LeftRead();
139 if (OutQueue
.length() - StrPos
< Sz
)
140 Sz
= OutQueue
.length() - StrPos
;
141 memcpy(Buf
+ (InP%Size
),OutQueue
.begin() + StrPos
,Sz
);
146 if (OutQueue
.length() == StrPos
)
155 // CircleBuf::Write - Write from the buffer into a FD /*{{{*/
156 // ---------------------------------------------------------------------
157 /* This empties the buffer into the FD. */
158 bool CircleBuf::Write(int Fd
)
164 // Woops, buffer is empty
171 // Write the buffer segment
173 Res
= write(Fd
,Buf
+ (OutP%Size
),LeftWrite());
186 MD5
->Add(Buf
+ (OutP%Size
),Res
);
192 // CircleBuf::WriteTillEl - Write from the buffer to a string /*{{{*/
193 // ---------------------------------------------------------------------
194 /* This copies till the first empty line */
195 bool CircleBuf::WriteTillEl(string
&Data
,bool Single
)
197 // We cheat and assume it is unneeded to have more than one buffer load
198 for (unsigned long I
= OutP
; I
< InP
; I
++)
200 if (Buf
[I%Size
] != '\n')
202 for (I
++; I
< InP
&& Buf
[I%Size
] == '\r'; I
++);
206 if (Buf
[I%Size
] != '\n')
208 for (I
++; I
< InP
&& Buf
[I%Size
] == '\r'; I
++);
217 unsigned long Sz
= LeftWrite();
220 if (I
- OutP
< LeftWrite())
222 Data
+= string((char *)(Buf
+ (OutP%Size
)),Sz
);
230 // CircleBuf::Stats - Print out stats information /*{{{*/
231 // ---------------------------------------------------------------------
233 void CircleBuf::Stats()
239 gettimeofday(&Stop
,0);
240 /* float Diff = Stop.tv_sec - Start.tv_sec +
241 (float)(Stop.tv_usec - Start.tv_usec)/1000000;
242 clog << "Got " << InP << " in " << Diff << " at " << InP/Diff << endl;*/
246 // ServerState::ServerState - Constructor /*{{{*/
247 // ---------------------------------------------------------------------
249 ServerState::ServerState(URI Srv
,HttpMethod
*Owner
) : Owner(Owner
),
250 In(64*1024), Out(4*1024),
256 // ServerState::Open - Open a connection to the server /*{{{*/
257 // ---------------------------------------------------------------------
258 /* This opens a connection to the server. */
261 bool ServerState::Open()
263 // Use the already open connection if possible.
271 // Determine the proxy setting
272 if (getenv("http_proxy") == 0)
274 string DefProxy
= _config
->Find("Acquire::http::Proxy");
275 string SpecificProxy
= _config
->Find("Acquire::http::Proxy::" + ServerName
.Host
);
276 if (SpecificProxy
.empty() == false)
278 if (SpecificProxy
== "DIRECT")
281 Proxy
= SpecificProxy
;
287 Proxy
= getenv("http_proxy");
289 // Determine what host and port to use based on the proxy settings
292 if (Proxy
.empty() == true)
294 if (ServerName
.Port
!= 0)
295 Port
= ServerName
.Port
;
296 Host
= ServerName
.Host
;
305 /* We used a cached address record.. Yes this is against the spec but
306 the way we have setup our rotating dns suggests that this is more
308 if (LastHost
!= Host
)
310 Owner
->Status("Connecting to %s",Host
.c_str());
313 hostent
*Addr
= gethostbyname(Host
.c_str());
314 if (Addr
== 0 || Addr
->h_addr_list
[0] == 0)
315 return _error
->Error("Could not resolve '%s'",Host
.c_str());
317 LastHostA
= *(in_addr
*)(Addr
->h_addr_list
[0]);
320 Owner
->Status("Connecting to %s (%s)",Host
.c_str(),inet_ntoa(LastHostA
));
323 if ((ServerFd
= socket(AF_INET
,SOCK_STREAM
,0)) < 0)
324 return _error
->Errno("socket","Could not create a socket");
326 // Connect to the server
327 struct sockaddr_in server
;
328 server
.sin_family
= AF_INET
;
329 server
.sin_port
= htons(Port
);
330 server
.sin_addr
= LastHostA
;
331 SetNonBlock(ServerFd
,true);
332 if (connect(ServerFd
,(sockaddr
*)&server
,sizeof(server
)) < 0 &&
333 errno
!= EINPROGRESS
)
334 return _error
->Errno("socket","Could not create a socket");
336 /* This implements a timeout for connect by opening the connection
340 FD_SET(ServerFd
,&wfds
);
345 if ((Res
= select(ServerFd
+1,0,&wfds
,0,&tv
)) < 0)
346 return _error
->Errno("select","Select failed");
348 return _error
->Error("Could not connect, connection timed out");
349 unsigned int Err
,Len
=sizeof(Err
);
350 if (getsockopt(ServerFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
351 return _error
->Errno("getsockopt","Failed");
353 return _error
->Error("Could not connect.");
358 // ServerState::Close - Close a connection to the server /*{{{*/
359 // ---------------------------------------------------------------------
361 bool ServerState::Close()
368 // ServerState::RunHeaders - Get the headers before the data /*{{{*/
369 // ---------------------------------------------------------------------
370 /* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
371 parse error occured */
372 int ServerState::RunHeaders()
376 Owner
->Status("Waiting for file");
390 if (In
.WriteTillEl(Data
) == false)
393 for (string::const_iterator I
= Data
.begin(); I
< Data
.end(); I
++)
395 string::const_iterator J
= I
;
396 for (; J
!= Data
.end() && *J
!= '\n' && *J
!= '\r';J
++);
397 if (HeaderLine(string(I
,J
-I
)) == false)
403 while (Owner
->Go(false,this) == true);
408 // ServerState::RunData - Transfer the data from the socket /*{{{*/
409 // ---------------------------------------------------------------------
411 bool ServerState::RunData()
415 // Chunked transfer encoding is fun..
416 if (Encoding
== Chunked
)
420 // Grab the block size
426 if (In
.WriteTillEl(Data
,true) == true)
429 while ((Last
= Owner
->Go(false,this)) == true);
434 // See if we are done
435 unsigned long Len
= strtol(Data
.c_str(),0,16);
440 // We have to remove the entity trailer
444 if (In
.WriteTillEl(Data
,true) == true && Data
.length() <= 2)
447 while ((Last
= Owner
->Go(false,this)) == true);
453 // Transfer the block
455 while (Owner
->Go(true,this) == true)
456 if (In
.IsLimit() == true)
460 if (In
.IsLimit() == false)
463 // The server sends an extra new line before the next block specifier..
468 if (In
.WriteTillEl(Data
,true) == true)
471 while ((Last
= Owner
->Go(false,this)) == true);
478 /* Closes encoding is used when the server did not specify a size, the
479 loss of the connection means we are done */
480 if (Encoding
== Closes
)
483 In
.Limit(Size
- StartPos
);
485 // Just transfer the whole block.
488 if (In
.IsLimit() == false)
494 while (Owner
->Go(true,this) == true);
497 return Owner
->Flush(this);
500 // ServerState::HeaderLine - Process a header line /*{{{*/
501 // ---------------------------------------------------------------------
503 bool ServerState::HeaderLine(string Line
)
505 if (Line
.empty() == true)
508 // The http server might be trying to do something evil.
509 if (Line
.length() >= MAXLEN
)
510 return _error
->Error("Got a single header line over %u chars",MAXLEN
);
512 string::size_type Pos
= Line
.find(' ');
513 if (Pos
== string::npos
|| Pos
+1 > Line
.length())
514 return _error
->Error("Bad header line");
516 string Tag
= string(Line
,0,Pos
);
517 string Val
= string(Line
,Pos
+1);
519 if (stringcasecmp(Tag
.begin(),Tag
.begin()+4,"HTTP") == 0)
521 // Evil servers return no version
524 if (sscanf(Line
.c_str(),"HTTP/%u.%u %u %[^\n]",&Major
,&Minor
,
526 return _error
->Error("The http server sent an invalid reply header");
532 if (sscanf(Line
.c_str(),"HTTP %u %[^\n]",&Result
,Code
) != 2)
533 return _error
->Error("The http server sent an invalid reply header");
539 if (stringcasecmp(Tag
,"Content-Length:") == 0)
541 if (Encoding
== Closes
)
545 // The length is already set from the Content-Range header
549 if (sscanf(Val
.c_str(),"%lu",&Size
) != 1)
550 return _error
->Error("The http server sent an invalid Content-Length header");
554 if (stringcasecmp(Tag
,"Content-Type:") == 0)
560 if (stringcasecmp(Tag
,"Content-Range:") == 0)
564 if (sscanf(Val
.c_str(),"bytes %lu-%*u/%lu",&StartPos
,&Size
) != 2)
565 return _error
->Error("The http server sent an invalid Content-Range header");
566 if ((unsigned)StartPos
> Size
)
567 return _error
->Error("This http server has broken range support");
571 if (stringcasecmp(Tag
,"Transfer-Encoding:") == 0)
574 if (stringcasecmp(Val
,"chunked") == 0)
580 if (stringcasecmp(Tag
,"Last-Modified:") == 0)
582 if (StrToTime(Val
,Date
) == false)
583 return _error
->Error("Unknown date format");
591 // HttpMethod::SendReq - Send the HTTP request /*{{{*/
592 // ---------------------------------------------------------------------
593 /* This places the http request in the outbound buffer */
594 void HttpMethod::SendReq(FetchItem
*Itm
,CircleBuf
&Out
)
598 // The HTTP server expects a hostname with a trailing :port
600 string ProperHost
= Uri
.Host
;
603 sprintf(Buf
,":%u",Uri
.Port
);
608 if (Itm
->Uri
.length() >= sizeof(Buf
))
611 /* Build the request. We include a keep-alive header only for non-proxy
612 requests. This is to tweak old http/1.0 servers that do support keep-alive
613 but not HTTP/1.1 automatic keep-alive. Doing this with a proxy server
614 will glitch HTTP/1.0 proxies because they do not filter it out and
615 pass it on, HTTP/1.1 says the connection should default to keep alive
616 and we expect the proxy to do this */
617 if (Proxy
.empty() == true)
618 sprintf(Buf
,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
619 QuoteString(Uri
.Path
,"~").c_str(),ProperHost
.c_str());
622 /* Generate a cache control header if necessary. We place a max
623 cache age on index files, optionally set a no-cache directive
624 and a no-store directive for archives. */
625 sprintf(Buf
,"GET %s HTTP/1.1\r\nHost: %s\r\n",
626 Itm
->Uri
.c_str(),ProperHost
.c_str());
627 if (_config
->FindB("Acquire::http::No-Cache",false) == true)
628 strcat(Buf
,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
631 if (Itm
->IndexFile
== true)
632 sprintf(Buf
+strlen(Buf
),"Cache-Control: max-age=%u\r\n",
633 _config
->FindI("Acquire::http::Max-Age",60*60*24));
636 if (_config
->FindB("Acquire::http::No-Store",false) == true)
637 strcat(Buf
,"Cache-Control: no-store\r\n");
644 // Check for a partial file
646 if (stat(Itm
->DestFile
.c_str(),&SBuf
) >= 0 && SBuf
.st_size
> 0)
648 // In this case we send an if-range query with a range header
649 sprintf(Buf
,"Range: bytes=%li-\r\nIf-Range: %s\r\n",SBuf
.st_size
- 1,
650 TimeRFC1123(SBuf
.st_mtime
).c_str());
655 if (Itm
->LastModified
!= 0)
657 sprintf(Buf
,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm
->LastModified
).c_str());
662 if (Proxy
.User
.empty() == false || Proxy
.Password
.empty() == false)
663 Req
+= string("Proxy-Authorization: Basic ") +
664 Base64Encode(Proxy
.User
+ ":" + Proxy
.Password
) + "\r\n";
666 Req
+= "User-Agent: Debian APT-HTTP/1.2\r\n\r\n";
667 // cerr << Req << endl;
672 // HttpMethod::Go - Run a single loop /*{{{*/
673 // ---------------------------------------------------------------------
674 /* This runs the select loop over the server FDs, Output file FDs and
676 bool HttpMethod::Go(bool ToFile
,ServerState
*Srv
)
678 // Server has closed the connection
679 if (Srv
->ServerFd
== -1 && Srv
->In
.WriteSpace() == false)
682 fd_set rfds
,wfds
,efds
;
688 if (Srv
->Out
.WriteSpace() == true && Srv
->ServerFd
!= -1)
689 FD_SET(Srv
->ServerFd
,&wfds
);
690 if (Srv
->In
.ReadSpace() == true && Srv
->ServerFd
!= -1)
691 FD_SET(Srv
->ServerFd
,&rfds
);
698 if (Srv
->In
.WriteSpace() == true && ToFile
== true && FileFD
!= -1)
699 FD_SET(FileFD
,&wfds
);
702 FD_SET(STDIN_FILENO
,&rfds
);
706 FD_SET(FileFD
,&efds
);
707 if (Srv
->ServerFd
!= -1)
708 FD_SET(Srv
->ServerFd
,&efds
);
710 // Figure out the max fd
712 if (MaxFd
< Srv
->ServerFd
)
713 MaxFd
= Srv
->ServerFd
;
720 if ((Res
= select(MaxFd
+1,&rfds
,&wfds
,&efds
,&tv
)) < 0)
721 return _error
->Errno("select","Select failed");
725 _error
->Error("Connection timed out");
726 return ServerDie(Srv
);
729 // Some kind of exception (error) on the sockets, die
730 if ((FileFD
!= -1 && FD_ISSET(FileFD
,&efds
)) ||
731 (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&efds
)))
732 return _error
->Error("Socket Exception");
735 if (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&rfds
))
738 if (Srv
->In
.Read(Srv
->ServerFd
) == false)
739 return ServerDie(Srv
);
742 if (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&wfds
))
745 if (Srv
->Out
.Write(Srv
->ServerFd
) == false)
746 return ServerDie(Srv
);
749 // Send data to the file
750 if (FileFD
!= -1 && FD_ISSET(FileFD
,&wfds
))
752 if (Srv
->In
.Write(FileFD
) == false)
753 return _error
->Errno("write","Error writing to output file");
756 // Handle commands from APT
757 if (FD_ISSET(STDIN_FILENO
,&rfds
))
766 // HttpMethod::Flush - Dump the buffer into the file /*{{{*/
767 // ---------------------------------------------------------------------
768 /* This takes the current input buffer from the Server FD and writes it
770 bool HttpMethod::Flush(ServerState
*Srv
)
774 SetNonBlock(File
->Fd(),false);
775 if (Srv
->In
.WriteSpace() == false)
778 while (Srv
->In
.WriteSpace() == true)
780 if (Srv
->In
.Write(File
->Fd()) == false)
781 return _error
->Errno("write","Error writing to file");
782 if (Srv
->In
.IsLimit() == true)
786 if (Srv
->In
.IsLimit() == true || Srv
->Encoding
== ServerState::Closes
)
792 // HttpMethod::ServerDie - The server has closed the connection. /*{{{*/
793 // ---------------------------------------------------------------------
795 bool HttpMethod::ServerDie(ServerState
*Srv
)
797 // Dump the buffer to the file
798 if (Srv
->State
== ServerState::Data
)
800 SetNonBlock(File
->Fd(),false);
801 while (Srv
->In
.WriteSpace() == true)
803 if (Srv
->In
.Write(File
->Fd()) == false)
804 return _error
->Errno("write","Error writing to the file");
807 if (Srv
->In
.IsLimit() == true)
812 // See if this is because the server finished the data stream
813 if (Srv
->In
.IsLimit() == false && Srv
->State
!= ServerState::Header
&&
814 Srv
->Encoding
!= ServerState::Closes
)
818 return _error
->Error("Error reading from server Remote end closed connection");
819 return _error
->Errno("read","Error reading from server");
825 // Nothing left in the buffer
826 if (Srv
->In
.WriteSpace() == false)
829 // We may have got multiple responses back in one packet..
837 // HttpMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
838 // ---------------------------------------------------------------------
839 /* We look at the header data we got back from the server and decide what
843 3 - Unrecoverable error
844 4 - Error with error content page
845 5 - Unrecoverable non-server error (close the connection) */
846 int HttpMethod::DealWithHeaders(FetchResult
&Res
,ServerState
*Srv
)
849 if (Srv
->Result
== 304)
851 unlink(Queue
->DestFile
.c_str());
853 Res
.LastModified
= Queue
->LastModified
;
857 /* We have a reply we dont handle. This should indicate a perm server
859 if (Srv
->Result
< 200 || Srv
->Result
>= 300)
861 _error
->Error("%u %s",Srv
->Result
,Srv
->Code
);
862 if (Srv
->HaveContent
== true)
867 // This is some sort of 2xx 'data follows' reply
868 Res
.LastModified
= Srv
->Date
;
869 Res
.Size
= Srv
->Size
;
873 File
= new FileFd(Queue
->DestFile
,FileFd::WriteAny
);
874 if (_error
->PendingError() == true)
877 FailFile
= Queue
->DestFile
;
878 FailFile
.c_str(); // Make sure we don't do a malloc in the signal handler
880 FailTime
= Srv
->Date
;
882 // Set the expected size
883 if (Srv
->StartPos
>= 0)
885 Res
.ResumePoint
= Srv
->StartPos
;
886 ftruncate(File
->Fd(),Srv
->StartPos
);
889 // Set the start point
890 lseek(File
->Fd(),0,SEEK_END
);
893 Srv
->In
.MD5
= new MD5Summation
;
895 // Fill the MD5 Hash if the file is non-empty (resume)
896 if (Srv
->StartPos
> 0)
898 lseek(File
->Fd(),0,SEEK_SET
);
899 if (Srv
->In
.MD5
->AddFD(File
->Fd(),Srv
->StartPos
) == false)
901 _error
->Errno("read","Problem hashing file");
904 lseek(File
->Fd(),0,SEEK_END
);
907 SetNonBlock(File
->Fd(),true);
911 // HttpMethod::SigTerm - Handle a fatal signal /*{{{*/
912 // ---------------------------------------------------------------------
913 /* This closes and timestamps the open file. This is neccessary to get
914 resume behavoir on user abort */
915 void HttpMethod::SigTerm(int)
924 UBuf
.actime
= FailTime
;
925 UBuf
.modtime
= FailTime
;
926 utime(FailFile
.c_str(),&UBuf
);
931 // HttpMethod::Fetch - Fetch an item /*{{{*/
932 // ---------------------------------------------------------------------
933 /* This adds an item to the pipeline. We keep the pipeline at a fixed
935 bool HttpMethod::Fetch(FetchItem
*)
940 // Queue the requests
943 for (FetchItem
*I
= Queue
; I
!= 0 && Depth
< (signed)PipelineDepth
; I
= I
->Next
, Depth
++)
945 // Make sure we stick with the same server
946 if (Server
->Comp(I
->Uri
) == false)
953 SendReq(I
,Server
->Out
);
961 // HttpMethod::Configuration - Handle a configuration message /*{{{*/
962 // ---------------------------------------------------------------------
963 /* We stash the desired pipeline depth */
964 bool HttpMethod::Configuration(string Message
)
966 if (pkgAcqMethod::Configuration(Message
) == false)
969 TimeOut
= _config
->FindI("Acquire::http::Timeout",TimeOut
);
970 PipelineDepth
= _config
->FindI("Acquire::http::Pipeline-Depth",
976 // HttpMethod::Loop - Main loop /*{{{*/
977 // ---------------------------------------------------------------------
979 int HttpMethod::Loop()
981 signal(SIGTERM
,SigTerm
);
982 signal(SIGINT
,SigTerm
);
989 if (FailCounter
>= 2)
991 Fail("Massive Server Brain Damage",true);
995 // We have no commands, wait for some to arrive
998 if (WaitFd(STDIN_FILENO
) == false)
1009 // Connect to the server
1010 if (Server
== 0 || Server
->Comp(Queue
->Uri
) == false)
1013 Server
= new ServerState(Queue
->Uri
,this);
1016 // Reset the pipeline
1017 if (Server
->ServerFd
== -1)
1020 // Connnect to the host
1021 if (Server
->Open() == false)
1029 // Fill the pipeline.
1032 // Fetch the next URL header data from the server.
1033 switch (Server
->RunHeaders())
1038 // The header data is bad
1041 _error
->Error("Bad header Data");
1046 // The server closed a connection during the header get..
1057 // Decide what to do.
1059 Res
.Filename
= Queue
->DestFile
;
1060 switch (DealWithHeaders(Res
,Server
))
1062 // Ok, the file is Open
1068 bool Result
= Server
->RunData();
1070 // Close the file, destroy the FD object and timestamp it
1076 struct utimbuf UBuf
;
1078 UBuf
.actime
= Server
->Date
;
1079 UBuf
.modtime
= Server
->Date
;
1080 utime(Queue
->DestFile
.c_str(),&UBuf
);
1082 // Send status to APT
1085 Res
.MD5Sum
= Server
->In
.MD5
->Result();
1101 // Hard server error, not found or something
1108 // Hard internal error, kill the connection and fail
1116 // We need to flush the data, the header is like a 404 w/ error text
1121 // Send to content to dev/null
1122 File
= new FileFd("/dev/null",FileFd::WriteExists
);
1130 Fail("Internal error");