]>
git.saurik.com Git - apt.git/blob - methods/http.cc
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: http.cc,v 1.30 1999/04/04 02:02:04 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>
52 string
HttpMethod::FailFile
;
53 int HttpMethod::FailFd
= -1;
54 time_t HttpMethod::FailTime
= 0;
55 unsigned long PipelineDepth
= 10;
56 unsigned long TimeOut
= 120;
58 // CircleBuf::CircleBuf - Circular input buffer /*{{{*/
59 // ---------------------------------------------------------------------
61 CircleBuf::CircleBuf(unsigned long Size
) : Size(Size
), MD5(0)
63 Buf
= new unsigned char[Size
];
67 // CircleBuf::Reset - Reset to the default state /*{{{*/
68 // ---------------------------------------------------------------------
70 void CircleBuf::Reset()
75 MaxGet
= (unsigned int)-1;
80 MD5
= new MD5Summation
;
84 // CircleBuf::Read - Read from a FD into the circular buffer /*{{{*/
85 // ---------------------------------------------------------------------
86 /* This fills up the buffer with as much data as is in the FD, assuming it
88 bool CircleBuf::Read(int Fd
)
92 // Woops, buffer is full
93 if (InP
- OutP
== Size
)
96 // Write the buffer segment
98 Res
= read(Fd
,Buf
+ (InP%Size
),LeftRead());
110 gettimeofday(&Start
,0);
115 // CircleBuf::Read - Put the string into the buffer /*{{{*/
116 // ---------------------------------------------------------------------
117 /* This will hold the string in and fill the buffer with it as it empties */
118 bool CircleBuf::Read(string Data
)
125 // CircleBuf::FillOut - Fill the buffer from the output queue /*{{{*/
126 // ---------------------------------------------------------------------
128 void CircleBuf::FillOut()
130 if (OutQueue
.empty() == true)
134 // Woops, buffer is full
135 if (InP
- OutP
== Size
)
138 // Write the buffer segment
139 unsigned long Sz
= LeftRead();
140 if (OutQueue
.length() - StrPos
< Sz
)
141 Sz
= OutQueue
.length() - StrPos
;
142 memcpy(Buf
+ (InP%Size
),OutQueue
.begin() + StrPos
,Sz
);
147 if (OutQueue
.length() == StrPos
)
156 // CircleBuf::Write - Write from the buffer into a FD /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This empties the buffer into the FD. */
159 bool CircleBuf::Write(int Fd
)
165 // Woops, buffer is empty
172 // Write the buffer segment
174 Res
= write(Fd
,Buf
+ (OutP%Size
),LeftWrite());
187 MD5
->Add(Buf
+ (OutP%Size
),Res
);
193 // CircleBuf::WriteTillEl - Write from the buffer to a string /*{{{*/
194 // ---------------------------------------------------------------------
195 /* This copies till the first empty line */
196 bool CircleBuf::WriteTillEl(string
&Data
,bool Single
)
198 // We cheat and assume it is unneeded to have more than one buffer load
199 for (unsigned long I
= OutP
; I
< InP
; I
++)
201 if (Buf
[I%Size
] != '\n')
203 for (I
++; I
< InP
&& Buf
[I%Size
] == '\r'; I
++);
207 if (Buf
[I%Size
] != '\n')
209 for (I
++; I
< InP
&& Buf
[I%Size
] == '\r'; I
++);
218 unsigned long Sz
= LeftWrite();
221 if (I
- OutP
< LeftWrite())
223 Data
+= string((char *)(Buf
+ (OutP%Size
)),Sz
);
231 // CircleBuf::Stats - Print out stats information /*{{{*/
232 // ---------------------------------------------------------------------
234 void CircleBuf::Stats()
240 gettimeofday(&Stop
,0);
241 /* float Diff = Stop.tv_sec - Start.tv_sec +
242 (float)(Stop.tv_usec - Start.tv_usec)/1000000;
243 clog << "Got " << InP << " in " << Diff << " at " << InP/Diff << endl;*/
247 // ServerState::ServerState - Constructor /*{{{*/
248 // ---------------------------------------------------------------------
250 ServerState::ServerState(URI Srv
,HttpMethod
*Owner
) : Owner(Owner
),
251 In(64*1024), Out(4*1024),
257 // ServerState::Open - Open a connection to the server /*{{{*/
258 // ---------------------------------------------------------------------
259 /* This opens a connection to the server. */
262 bool ServerState::Open()
264 // Use the already open connection if possible.
272 // Determine the proxy setting
273 if (getenv("http_proxy") == 0)
275 string DefProxy
= _config
->Find("Acquire::http::Proxy");
276 string SpecificProxy
= _config
->Find("Acquire::http::Proxy::" + ServerName
.Host
);
277 if (SpecificProxy
.empty() == false)
279 if (SpecificProxy
== "DIRECT")
282 Proxy
= SpecificProxy
;
288 Proxy
= getenv("http_proxy");
290 // Determine what host and port to use based on the proxy settings
293 if (Proxy
.empty() == true)
295 if (ServerName
.Port
!= 0)
296 Port
= ServerName
.Port
;
297 Host
= ServerName
.Host
;
306 /* We used a cached address record.. Yes this is against the spec but
307 the way we have setup our rotating dns suggests that this is more
309 if (LastHost
!= Host
)
311 Owner
->Status("Connecting to %s",Host
.c_str());
314 hostent
*Addr
= gethostbyname(Host
.c_str());
315 if (Addr
== 0 || Addr
->h_addr_list
[0] == 0)
316 return _error
->Error("Could not resolve '%s'",Host
.c_str());
318 LastHostA
= *(in_addr
*)(Addr
->h_addr_list
[0]);
321 Owner
->Status("Connecting to %s (%s)",Host
.c_str(),inet_ntoa(LastHostA
));
324 if ((ServerFd
= socket(AF_INET
,SOCK_STREAM
,0)) < 0)
325 return _error
->Errno("socket","Could not create a socket");
327 // Connect to the server
328 struct sockaddr_in server
;
329 server
.sin_family
= AF_INET
;
330 server
.sin_port
= htons(Port
);
331 server
.sin_addr
= LastHostA
;
332 SetNonBlock(ServerFd
,true);
333 if (connect(ServerFd
,(sockaddr
*)&server
,sizeof(server
)) < 0 &&
334 errno
!= EINPROGRESS
)
335 return _error
->Errno("socket","Could not create a socket");
337 /* This implements a timeout for connect by opening the connection
339 if (WaitFd(ServerFd
,true,TimeOut
) == false)
340 return _error
->Error("Could not connect, connection timed out");
342 unsigned int Len
= sizeof(Err
);
343 if (getsockopt(ServerFd
,SOL_SOCKET
,SO_ERROR
,&Err
,&Len
) != 0)
344 return _error
->Errno("getsockopt","Failed");
346 return _error
->Error("Could not connect.");
351 // ServerState::Close - Close a connection to the server /*{{{*/
352 // ---------------------------------------------------------------------
354 bool ServerState::Close()
361 // ServerState::RunHeaders - Get the headers before the data /*{{{*/
362 // ---------------------------------------------------------------------
363 /* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
364 parse error occured */
365 int ServerState::RunHeaders()
369 Owner
->Status("Waiting for file");
383 if (In
.WriteTillEl(Data
) == false)
386 for (string::const_iterator I
= Data
.begin(); I
< Data
.end(); I
++)
388 string::const_iterator J
= I
;
389 for (; J
!= Data
.end() && *J
!= '\n' && *J
!= '\r';J
++);
390 if (HeaderLine(string(I
,J
-I
)) == false)
396 while (Owner
->Go(false,this) == true);
401 // ServerState::RunData - Transfer the data from the socket /*{{{*/
402 // ---------------------------------------------------------------------
404 bool ServerState::RunData()
408 // Chunked transfer encoding is fun..
409 if (Encoding
== Chunked
)
413 // Grab the block size
419 if (In
.WriteTillEl(Data
,true) == true)
422 while ((Last
= Owner
->Go(false,this)) == true);
427 // See if we are done
428 unsigned long Len
= strtol(Data
.c_str(),0,16);
433 // We have to remove the entity trailer
437 if (In
.WriteTillEl(Data
,true) == true && Data
.length() <= 2)
440 while ((Last
= Owner
->Go(false,this)) == true);
443 return !_error
->PendingError();
446 // Transfer the block
448 while (Owner
->Go(true,this) == true)
449 if (In
.IsLimit() == true)
453 if (In
.IsLimit() == false)
456 // The server sends an extra new line before the next block specifier..
461 if (In
.WriteTillEl(Data
,true) == true)
464 while ((Last
= Owner
->Go(false,this)) == true);
471 /* Closes encoding is used when the server did not specify a size, the
472 loss of the connection means we are done */
473 if (Encoding
== Closes
)
476 In
.Limit(Size
- StartPos
);
478 // Just transfer the whole block.
481 if (In
.IsLimit() == false)
485 return !_error
->PendingError();
487 while (Owner
->Go(true,this) == true);
490 return Owner
->Flush(this) && !_error
->PendingError();
493 // ServerState::HeaderLine - Process a header line /*{{{*/
494 // ---------------------------------------------------------------------
496 bool ServerState::HeaderLine(string Line
)
498 if (Line
.empty() == true)
501 // The http server might be trying to do something evil.
502 if (Line
.length() >= MAXLEN
)
503 return _error
->Error("Got a single header line over %u chars",MAXLEN
);
505 string::size_type Pos
= Line
.find(' ');
506 if (Pos
== string::npos
|| Pos
+1 > Line
.length())
508 // Blah, some servers use "connection:closes", evil.
509 Pos
= Line
.find(':');
510 if (Pos
== string::npos
|| Pos
+ 2 > Line
.length())
511 return _error
->Error("Bad header line");
515 // Parse off any trailing spaces between the : and the next word.
516 string::size_type Pos2
= Pos
;
517 while (Pos2
< Line
.length() && isspace(Line
[Pos2
]) != 0)
520 string Tag
= string(Line
,0,Pos
);
521 string Val
= string(Line
,Pos2
);
523 if (stringcasecmp(Tag
.begin(),Tag
.begin()+4,"HTTP") == 0)
525 // Evil servers return no version
528 if (sscanf(Line
.c_str(),"HTTP/%u.%u %u %[^\n]",&Major
,&Minor
,
530 return _error
->Error("The http server sent an invalid reply header");
536 if (sscanf(Line
.c_str(),"HTTP %u %[^\n]",&Result
,Code
) != 2)
537 return _error
->Error("The http server sent an invalid reply header");
543 if (stringcasecmp(Tag
,"Content-Length:") == 0)
545 if (Encoding
== Closes
)
549 // The length is already set from the Content-Range header
553 if (sscanf(Val
.c_str(),"%lu",&Size
) != 1)
554 return _error
->Error("The http server sent an invalid Content-Length header");
558 if (stringcasecmp(Tag
,"Content-Type:") == 0)
564 if (stringcasecmp(Tag
,"Content-Range:") == 0)
568 if (sscanf(Val
.c_str(),"bytes %lu-%*u/%lu",&StartPos
,&Size
) != 2)
569 return _error
->Error("The http server sent an invalid Content-Range header");
570 if ((unsigned)StartPos
> Size
)
571 return _error
->Error("This http server has broken range support");
575 if (stringcasecmp(Tag
,"Transfer-Encoding:") == 0)
578 if (stringcasecmp(Val
,"chunked") == 0)
584 if (stringcasecmp(Tag
,"Last-Modified:") == 0)
586 if (StrToTime(Val
,Date
) == false)
587 return _error
->Error("Unknown date format");
595 // HttpMethod::SendReq - Send the HTTP request /*{{{*/
596 // ---------------------------------------------------------------------
597 /* This places the http request in the outbound buffer */
598 void HttpMethod::SendReq(FetchItem
*Itm
,CircleBuf
&Out
)
602 // The HTTP server expects a hostname with a trailing :port
604 string ProperHost
= Uri
.Host
;
607 sprintf(Buf
,":%u",Uri
.Port
);
612 if (Itm
->Uri
.length() >= sizeof(Buf
))
615 /* Build the request. We include a keep-alive header only for non-proxy
616 requests. This is to tweak old http/1.0 servers that do support keep-alive
617 but not HTTP/1.1 automatic keep-alive. Doing this with a proxy server
618 will glitch HTTP/1.0 proxies because they do not filter it out and
619 pass it on, HTTP/1.1 says the connection should default to keep alive
620 and we expect the proxy to do this */
621 if (Proxy
.empty() == true)
622 sprintf(Buf
,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
623 QuoteString(Uri
.Path
,"~").c_str(),ProperHost
.c_str());
626 /* Generate a cache control header if necessary. We place a max
627 cache age on index files, optionally set a no-cache directive
628 and a no-store directive for archives. */
629 sprintf(Buf
,"GET %s HTTP/1.1\r\nHost: %s\r\n",
630 Itm
->Uri
.c_str(),ProperHost
.c_str());
631 if (_config
->FindB("Acquire::http::No-Cache",false) == true)
632 strcat(Buf
,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
635 if (Itm
->IndexFile
== true)
636 sprintf(Buf
+strlen(Buf
),"Cache-Control: max-age=%u\r\n",
637 _config
->FindI("Acquire::http::Max-Age",60*60*24));
640 if (_config
->FindB("Acquire::http::No-Store",false) == true)
641 strcat(Buf
,"Cache-Control: no-store\r\n");
648 // Check for a partial file
650 if (stat(Itm
->DestFile
.c_str(),&SBuf
) >= 0 && SBuf
.st_size
> 0)
652 // In this case we send an if-range query with a range header
653 sprintf(Buf
,"Range: bytes=%li-\r\nIf-Range: %s\r\n",SBuf
.st_size
- 1,
654 TimeRFC1123(SBuf
.st_mtime
).c_str());
659 if (Itm
->LastModified
!= 0)
661 sprintf(Buf
,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm
->LastModified
).c_str());
666 if (Proxy
.User
.empty() == false || Proxy
.Password
.empty() == false)
667 Req
+= string("Proxy-Authorization: Basic ") +
668 Base64Encode(Proxy
.User
+ ":" + Proxy
.Password
) + "\r\n";
670 Req
+= "User-Agent: Debian APT-HTTP/1.2\r\n\r\n";
671 // cerr << Req << endl;
676 // HttpMethod::Go - Run a single loop /*{{{*/
677 // ---------------------------------------------------------------------
678 /* This runs the select loop over the server FDs, Output file FDs and
680 bool HttpMethod::Go(bool ToFile
,ServerState
*Srv
)
682 // Server has closed the connection
683 if (Srv
->ServerFd
== -1 && Srv
->In
.WriteSpace() == false)
686 fd_set rfds
,wfds
,efds
;
692 if (Srv
->Out
.WriteSpace() == true && Srv
->ServerFd
!= -1)
693 FD_SET(Srv
->ServerFd
,&wfds
);
694 if (Srv
->In
.ReadSpace() == true && Srv
->ServerFd
!= -1)
695 FD_SET(Srv
->ServerFd
,&rfds
);
702 if (Srv
->In
.WriteSpace() == true && ToFile
== true && FileFD
!= -1)
703 FD_SET(FileFD
,&wfds
);
706 FD_SET(STDIN_FILENO
,&rfds
);
710 FD_SET(FileFD
,&efds
);
711 if (Srv
->ServerFd
!= -1)
712 FD_SET(Srv
->ServerFd
,&efds
);
714 // Figure out the max fd
716 if (MaxFd
< Srv
->ServerFd
)
717 MaxFd
= Srv
->ServerFd
;
724 if ((Res
= select(MaxFd
+1,&rfds
,&wfds
,&efds
,&tv
)) < 0)
725 return _error
->Errno("select","Select failed");
729 _error
->Error("Connection timed out");
730 return ServerDie(Srv
);
733 // Some kind of exception (error) on the sockets, die
734 if ((FileFD
!= -1 && FD_ISSET(FileFD
,&efds
)) ||
735 (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&efds
)))
736 return _error
->Error("Socket Exception");
739 if (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&rfds
))
742 if (Srv
->In
.Read(Srv
->ServerFd
) == false)
743 return ServerDie(Srv
);
746 if (Srv
->ServerFd
!= -1 && FD_ISSET(Srv
->ServerFd
,&wfds
))
749 if (Srv
->Out
.Write(Srv
->ServerFd
) == false)
750 return ServerDie(Srv
);
753 // Send data to the file
754 if (FileFD
!= -1 && FD_ISSET(FileFD
,&wfds
))
756 if (Srv
->In
.Write(FileFD
) == false)
757 return _error
->Errno("write","Error writing to output file");
760 // Handle commands from APT
761 if (FD_ISSET(STDIN_FILENO
,&rfds
))
770 // HttpMethod::Flush - Dump the buffer into the file /*{{{*/
771 // ---------------------------------------------------------------------
772 /* This takes the current input buffer from the Server FD and writes it
774 bool HttpMethod::Flush(ServerState
*Srv
)
778 SetNonBlock(File
->Fd(),false);
779 if (Srv
->In
.WriteSpace() == false)
782 while (Srv
->In
.WriteSpace() == true)
784 if (Srv
->In
.Write(File
->Fd()) == false)
785 return _error
->Errno("write","Error writing to file");
786 if (Srv
->In
.IsLimit() == true)
790 if (Srv
->In
.IsLimit() == true || Srv
->Encoding
== ServerState::Closes
)
796 // HttpMethod::ServerDie - The server has closed the connection. /*{{{*/
797 // ---------------------------------------------------------------------
799 bool HttpMethod::ServerDie(ServerState
*Srv
)
801 unsigned int LErrno
= errno
;
803 // Dump the buffer to the file
804 if (Srv
->State
== ServerState::Data
)
806 SetNonBlock(File
->Fd(),false);
807 while (Srv
->In
.WriteSpace() == true)
809 if (Srv
->In
.Write(File
->Fd()) == false)
810 return _error
->Errno("write","Error writing to the file");
813 if (Srv
->In
.IsLimit() == true)
818 // See if this is because the server finished the data stream
819 if (Srv
->In
.IsLimit() == false && Srv
->State
!= ServerState::Header
&&
820 Srv
->Encoding
!= ServerState::Closes
)
824 return _error
->Error("Error reading from server Remote end closed connection");
826 return _error
->Errno("read","Error reading from server");
832 // Nothing left in the buffer
833 if (Srv
->In
.WriteSpace() == false)
836 // We may have got multiple responses back in one packet..
844 // HttpMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
845 // ---------------------------------------------------------------------
846 /* We look at the header data we got back from the server and decide what
850 3 - Unrecoverable error
851 4 - Error with error content page
852 5 - Unrecoverable non-server error (close the connection) */
853 int HttpMethod::DealWithHeaders(FetchResult
&Res
,ServerState
*Srv
)
856 if (Srv
->Result
== 304)
858 unlink(Queue
->DestFile
.c_str());
860 Res
.LastModified
= Queue
->LastModified
;
864 /* We have a reply we dont handle. This should indicate a perm server
866 if (Srv
->Result
< 200 || Srv
->Result
>= 300)
868 _error
->Error("%u %s",Srv
->Result
,Srv
->Code
);
869 if (Srv
->HaveContent
== true)
874 // This is some sort of 2xx 'data follows' reply
875 Res
.LastModified
= Srv
->Date
;
876 Res
.Size
= Srv
->Size
;
880 File
= new FileFd(Queue
->DestFile
,FileFd::WriteAny
);
881 if (_error
->PendingError() == true)
884 FailFile
= Queue
->DestFile
;
885 FailFile
.c_str(); // Make sure we dont do a malloc in the signal handler
887 FailTime
= Srv
->Date
;
889 // Set the expected size
890 if (Srv
->StartPos
>= 0)
892 Res
.ResumePoint
= Srv
->StartPos
;
893 ftruncate(File
->Fd(),Srv
->StartPos
);
896 // Set the start point
897 lseek(File
->Fd(),0,SEEK_END
);
900 Srv
->In
.MD5
= new MD5Summation
;
902 // Fill the MD5 Hash if the file is non-empty (resume)
903 if (Srv
->StartPos
> 0)
905 lseek(File
->Fd(),0,SEEK_SET
);
906 if (Srv
->In
.MD5
->AddFD(File
->Fd(),Srv
->StartPos
) == false)
908 _error
->Errno("read","Problem hashing file");
911 lseek(File
->Fd(),0,SEEK_END
);
914 SetNonBlock(File
->Fd(),true);
918 // HttpMethod::SigTerm - Handle a fatal signal /*{{{*/
919 // ---------------------------------------------------------------------
920 /* This closes and timestamps the open file. This is neccessary to get
921 resume behavoir on user abort */
922 void HttpMethod::SigTerm(int)
930 UBuf
.actime
= FailTime
;
931 UBuf
.modtime
= FailTime
;
932 utime(FailFile
.c_str(),&UBuf
);
937 // HttpMethod::Fetch - Fetch an item /*{{{*/
938 // ---------------------------------------------------------------------
939 /* This adds an item to the pipeline. We keep the pipeline at a fixed
941 bool HttpMethod::Fetch(FetchItem
*)
946 // Queue the requests
949 for (FetchItem
*I
= Queue
; I
!= 0 && Depth
< (signed)PipelineDepth
; I
= I
->Next
, Depth
++)
951 // Make sure we stick with the same server
952 if (Server
->Comp(I
->Uri
) == false)
959 SendReq(I
,Server
->Out
);
967 // HttpMethod::Configuration - Handle a configuration message /*{{{*/
968 // ---------------------------------------------------------------------
969 /* We stash the desired pipeline depth */
970 bool HttpMethod::Configuration(string Message
)
972 if (pkgAcqMethod::Configuration(Message
) == false)
975 TimeOut
= _config
->FindI("Acquire::http::Timeout",TimeOut
);
976 PipelineDepth
= _config
->FindI("Acquire::http::Pipeline-Depth",
982 // HttpMethod::Loop - Main loop /*{{{*/
983 // ---------------------------------------------------------------------
985 int HttpMethod::Loop()
987 signal(SIGTERM
,SigTerm
);
988 signal(SIGINT
,SigTerm
);
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..
1054 if (FailCounter
>= 2)
1056 Fail("Connection timed out",true);
1064 // Decide what to do.
1066 Res
.Filename
= Queue
->DestFile
;
1067 switch (DealWithHeaders(Res
,Server
))
1069 // Ok, the file is Open
1075 bool Result
= Server
->RunData();
1077 // Close the file, destroy the FD object and timestamp it
1083 struct utimbuf UBuf
;
1085 UBuf
.actime
= Server
->Date
;
1086 UBuf
.modtime
= Server
->Date
;
1087 utime(Queue
->DestFile
.c_str(),&UBuf
);
1089 // Send status to APT
1092 Res
.MD5Sum
= Server
->In
.MD5
->Result();
1108 // Hard server error, not found or something
1115 // Hard internal error, kill the connection and fail
1123 // We need to flush the data, the header is like a 404 w/ error text
1128 // Send to content to dev/null
1129 File
= new FileFd("/dev/null",FileFd::WriteExists
);
1137 Fail("Internal error");