Simplified time calculations
[ntk/apt.git] / methods / http.cc
CommitLineData
be4401bf
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
2b154e53 3// $Id: http.cc,v 1.27 1999/02/27 22:29:11 jgg Exp $
be4401bf
AL
4/* ######################################################################
5
6 HTTP Aquire Method - This is the HTTP aquire method for APT.
7
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.
12
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.
18
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.
26
27 ##################################################################### */
28 /*}}}*/
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>
34
35#include <sys/stat.h>
36#include <sys/time.h>
37#include <utime.h>
38#include <unistd.h>
492f957a 39#include <signal.h>
be4401bf 40#include <stdio.h>
65a1e968 41#include <errno.h>
be4401bf
AL
42
43// Internet stuff
44#include <netinet/in.h>
45#include <sys/socket.h>
46#include <arpa/inet.h>
47#include <netdb.h>
48
49#include "http.h"
50 /*}}}*/
51
492f957a
AL
52string HttpMethod::FailFile;
53int HttpMethod::FailFd = -1;
54time_t HttpMethod::FailTime = 0;
3000ccea
AL
55unsigned long PipelineDepth = 10;
56unsigned long TimeOut = 120;
492f957a 57
be4401bf
AL
58// CircleBuf::CircleBuf - Circular input buffer /*{{{*/
59// ---------------------------------------------------------------------
60/* */
61CircleBuf::CircleBuf(unsigned long Size) : Size(Size), MD5(0)
62{
63 Buf = new unsigned char[Size];
64 Reset();
65}
66 /*}}}*/
67// CircleBuf::Reset - Reset to the default state /*{{{*/
68// ---------------------------------------------------------------------
69/* */
70void CircleBuf::Reset()
71{
72 InP = 0;
73 OutP = 0;
74 StrPos = 0;
75 MaxGet = (unsigned int)-1;
76 OutQueue = string();
77 if (MD5 != 0)
78 {
79 delete MD5;
80 MD5 = new MD5Summation;
81 }
82};
83 /*}}}*/
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
87 is non-blocking.. */
88bool CircleBuf::Read(int Fd)
89{
90 while (1)
91 {
92 // Woops, buffer is full
93 if (InP - OutP == Size)
94 return true;
95
96 // Write the buffer segment
97 int Res;
98 Res = read(Fd,Buf + (InP%Size),LeftRead());
99
100 if (Res == 0)
101 return false;
102 if (Res < 0)
103 {
104 if (errno == EAGAIN)
105 return true;
106 return false;
107 }
108
109 if (InP == 0)
110 gettimeofday(&Start,0);
111 InP += Res;
112 }
113}
114 /*}}}*/
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 */
118bool CircleBuf::Read(string Data)
119{
120 OutQueue += Data;
121 FillOut();
122 return true;
123}
124 /*}}}*/
125// CircleBuf::FillOut - Fill the buffer from the output queue /*{{{*/
126// ---------------------------------------------------------------------
127/* */
128void CircleBuf::FillOut()
129{
130 if (OutQueue.empty() == true)
131 return;
132 while (1)
133 {
134 // Woops, buffer is full
135 if (InP - OutP == Size)
136 return;
137
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);
143
144 // Advance
145 StrPos += Sz;
146 InP += Sz;
147 if (OutQueue.length() == StrPos)
148 {
149 StrPos = 0;
150 OutQueue = "";
151 return;
152 }
153 }
154}
155 /*}}}*/
156// CircleBuf::Write - Write from the buffer into a FD /*{{{*/
157// ---------------------------------------------------------------------
158/* This empties the buffer into the FD. */
159bool CircleBuf::Write(int Fd)
160{
161 while (1)
162 {
163 FillOut();
164
165 // Woops, buffer is empty
166 if (OutP == InP)
167 return true;
168
169 if (OutP == MaxGet)
170 return true;
171
172 // Write the buffer segment
173 int Res;
174 Res = write(Fd,Buf + (OutP%Size),LeftWrite());
175
176 if (Res == 0)
177 return false;
178 if (Res < 0)
179 {
180 if (errno == EAGAIN)
181 return true;
182
183 return false;
184 }
185
186 if (MD5 != 0)
187 MD5->Add(Buf + (OutP%Size),Res);
188
189 OutP += Res;
190 }
191}
192 /*}}}*/
193// CircleBuf::WriteTillEl - Write from the buffer to a string /*{{{*/
194// ---------------------------------------------------------------------
195/* This copies till the first empty line */
196bool CircleBuf::WriteTillEl(string &Data,bool Single)
197{
198 // We cheat and assume it is unneeded to have more than one buffer load
199 for (unsigned long I = OutP; I < InP; I++)
200 {
201 if (Buf[I%Size] != '\n')
202 continue;
203 for (I++; I < InP && Buf[I%Size] == '\r'; I++);
204
205 if (Single == false)
206 {
207 if (Buf[I%Size] != '\n')
208 continue;
209 for (I++; I < InP && Buf[I%Size] == '\r'; I++);
210 }
211
212 if (I > InP)
213 I = InP;
214
215 Data = "";
216 while (OutP < I)
217 {
218 unsigned long Sz = LeftWrite();
219 if (Sz == 0)
220 return false;
221 if (I - OutP < LeftWrite())
222 Sz = I - OutP;
223 Data += string((char *)(Buf + (OutP%Size)),Sz);
224 OutP += Sz;
225 }
226 return true;
227 }
228 return false;
229}
230 /*}}}*/
231// CircleBuf::Stats - Print out stats information /*{{{*/
232// ---------------------------------------------------------------------
233/* */
234void CircleBuf::Stats()
235{
236 if (InP == 0)
237 return;
238
239 struct timeval Stop;
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;*/
244}
245 /*}}}*/
246
247// ServerState::ServerState - Constructor /*{{{*/
248// ---------------------------------------------------------------------
249/* */
250ServerState::ServerState(URI Srv,HttpMethod *Owner) : Owner(Owner),
3000ccea 251 In(64*1024), Out(4*1024),
be4401bf
AL
252 ServerName(Srv)
253{
254 Reset();
255}
256 /*}}}*/
257// ServerState::Open - Open a connection to the server /*{{{*/
258// ---------------------------------------------------------------------
259/* This opens a connection to the server. */
260string LastHost;
261in_addr LastHostA;
262bool ServerState::Open()
263{
92e889c8
AL
264 // Use the already open connection if possible.
265 if (ServerFd != -1)
266 return true;
267
be4401bf 268 Close();
492f957a
AL
269 In.Reset();
270 Out.Reset();
271
272 // Determine the proxy setting
52e7839a 273 if (getenv("http_proxy") == 0)
492f957a 274 {
352c2768
AL
275 string DefProxy = _config->Find("Acquire::http::Proxy");
276 string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host);
277 if (SpecificProxy.empty() == false)
278 {
279 if (SpecificProxy == "DIRECT")
280 Proxy = "";
281 else
282 Proxy = SpecificProxy;
283 }
492f957a 284 else
352c2768
AL
285 Proxy = DefProxy;
286 }
492f957a 287 else
352c2768
AL
288 Proxy = getenv("http_proxy");
289
492f957a 290 // Determine what host and port to use based on the proxy settings
92e889c8 291 int Port = 80;
492f957a 292 string Host;
92e889c8 293 if (Proxy.empty() == true)
be4401bf 294 {
92e889c8
AL
295 if (ServerName.Port != 0)
296 Port = ServerName.Port;
be4401bf
AL
297 Host = ServerName.Host;
298 }
299 else
300 {
92e889c8
AL
301 if (Proxy.Port != 0)
302 Port = Proxy.Port;
be4401bf
AL
303 Host = Proxy.Host;
304 }
305
492f957a
AL
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
308 sensible */
be4401bf
AL
309 if (LastHost != Host)
310 {
311 Owner->Status("Connecting to %s",Host.c_str());
312
313 // Lookup the host
314 hostent *Addr = gethostbyname(Host.c_str());
3000ccea 315 if (Addr == 0 || Addr->h_addr_list[0] == 0)
92e889c8 316 return _error->Error("Could not resolve '%s'",Host.c_str());
be4401bf
AL
317 LastHost = Host;
318 LastHostA = *(in_addr *)(Addr->h_addr_list[0]);
319 }
320
321 Owner->Status("Connecting to %s (%s)",Host.c_str(),inet_ntoa(LastHostA));
322
323 // Get a socket
324 if ((ServerFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
325 return _error->Errno("socket","Could not create a socket");
326
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;
3000ccea
AL
332 SetNonBlock(ServerFd,true);
333 if (connect(ServerFd,(sockaddr *)&server,sizeof(server)) < 0 &&
334 errno != EINPROGRESS)
be4401bf
AL
335 return _error->Errno("socket","Could not create a socket");
336
3000ccea
AL
337 /* This implements a timeout for connect by opening the connection
338 nonblocking */
339 fd_set wfds;
340 FD_ZERO(&wfds);
341 FD_SET(ServerFd,&wfds);
342 struct timeval tv;
343 tv.tv_sec = TimeOut;
344 tv.tv_usec = 0;
345 int Res = 0;
346 if ((Res = select(ServerFd+1,0,&wfds,0,&tv)) < 0)
347 return _error->Errno("select","Select failed");
348 if (Res == 0)
349 return _error->Error("Could not connect, connection timed out");
7f9b781e 350 unsigned int Err,Len=sizeof(Err);
3000ccea
AL
351 if (getsockopt(ServerFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
352 return _error->Errno("getsockopt","Failed");
353 if (Err != 0)
354 return _error->Error("Could not connect.");
355
be4401bf
AL
356 return true;
357}
358 /*}}}*/
359// ServerState::Close - Close a connection to the server /*{{{*/
360// ---------------------------------------------------------------------
361/* */
362bool ServerState::Close()
363{
364 close(ServerFd);
365 ServerFd = -1;
be4401bf
AL
366 return true;
367}
368 /*}}}*/
369// ServerState::RunHeaders - Get the headers before the data /*{{{*/
370// ---------------------------------------------------------------------
92e889c8
AL
371/* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
372 parse error occured */
373int ServerState::RunHeaders()
be4401bf
AL
374{
375 State = Header;
376
377 Owner->Status("Waiting for file");
378
379 Major = 0;
380 Minor = 0;
381 Result = 0;
382 Size = 0;
383 StartPos = 0;
92e889c8
AL
384 Encoding = Closes;
385 HaveContent = false;
be4401bf
AL
386 time(&Date);
387
388 do
389 {
390 string Data;
391 if (In.WriteTillEl(Data) == false)
392 continue;
393
394 for (string::const_iterator I = Data.begin(); I < Data.end(); I++)
395 {
396 string::const_iterator J = I;
397 for (; J != Data.end() && *J != '\n' && *J != '\r';J++);
398 if (HeaderLine(string(I,J-I)) == false)
92e889c8 399 return 2;
be4401bf
AL
400 I = J;
401 }
92e889c8 402 return 0;
be4401bf
AL
403 }
404 while (Owner->Go(false,this) == true);
92e889c8
AL
405
406 return 1;
be4401bf
AL
407}
408 /*}}}*/
409// ServerState::RunData - Transfer the data from the socket /*{{{*/
410// ---------------------------------------------------------------------
411/* */
412bool ServerState::RunData()
413{
414 State = Data;
415
416 // Chunked transfer encoding is fun..
417 if (Encoding == Chunked)
418 {
419 while (1)
420 {
421 // Grab the block size
422 bool Last = true;
423 string Data;
424 In.Limit(-1);
425 do
426 {
427 if (In.WriteTillEl(Data,true) == true)
428 break;
429 }
430 while ((Last = Owner->Go(false,this)) == true);
431
432 if (Last == false)
433 return false;
434
435 // See if we are done
436 unsigned long Len = strtol(Data.c_str(),0,16);
437 if (Len == 0)
438 {
439 In.Limit(-1);
440
441 // We have to remove the entity trailer
442 Last = true;
443 do
444 {
445 if (In.WriteTillEl(Data,true) == true && Data.length() <= 2)
446 break;
447 }
448 while ((Last = Owner->Go(false,this)) == true);
449 if (Last == false)
450 return false;
e1b96638 451 return !_error->PendingError();
be4401bf
AL
452 }
453
454 // Transfer the block
455 In.Limit(Len);
456 while (Owner->Go(true,this) == true)
457 if (In.IsLimit() == true)
458 break;
459
460 // Error
461 if (In.IsLimit() == false)
462 return false;
463
464 // The server sends an extra new line before the next block specifier..
465 In.Limit(-1);
466 Last = true;
467 do
468 {
469 if (In.WriteTillEl(Data,true) == true)
470 break;
471 }
472 while ((Last = Owner->Go(false,this)) == true);
473 if (Last == false)
474 return false;
92e889c8 475 }
be4401bf
AL
476 }
477 else
478 {
479 /* Closes encoding is used when the server did not specify a size, the
480 loss of the connection means we are done */
481 if (Encoding == Closes)
482 In.Limit(-1);
483 else
484 In.Limit(Size - StartPos);
485
486 // Just transfer the whole block.
487 do
488 {
489 if (In.IsLimit() == false)
490 continue;
491
492 In.Limit(-1);
e1b96638 493 return !_error->PendingError();
be4401bf
AL
494 }
495 while (Owner->Go(true,this) == true);
496 }
497
e1b96638 498 return Owner->Flush(this) && !_error->PendingError();
be4401bf
AL
499}
500 /*}}}*/
501// ServerState::HeaderLine - Process a header line /*{{{*/
502// ---------------------------------------------------------------------
503/* */
504bool ServerState::HeaderLine(string Line)
505{
506 if (Line.empty() == true)
507 return true;
30456e14 508
be4401bf
AL
509 // The http server might be trying to do something evil.
510 if (Line.length() >= MAXLEN)
511 return _error->Error("Got a single header line over %u chars",MAXLEN);
512
513 string::size_type Pos = Line.find(' ');
514 if (Pos == string::npos || Pos+1 > Line.length())
515 return _error->Error("Bad header line");
516
517 string Tag = string(Line,0,Pos);
518 string Val = string(Line,Pos+1);
519
92e889c8 520 if (stringcasecmp(Tag.begin(),Tag.begin()+4,"HTTP") == 0)
be4401bf
AL
521 {
522 // Evil servers return no version
523 if (Line[4] == '/')
524 {
525 if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor,
526 &Result,Code) != 4)
527 return _error->Error("The http server sent an invalid reply header");
528 }
529 else
530 {
531 Major = 0;
532 Minor = 9;
533 if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
534 return _error->Error("The http server sent an invalid reply header");
535 }
536
537 return true;
538 }
539
92e889c8 540 if (stringcasecmp(Tag,"Content-Length:") == 0)
be4401bf
AL
541 {
542 if (Encoding == Closes)
543 Encoding = Stream;
92e889c8 544 HaveContent = true;
be4401bf
AL
545
546 // The length is already set from the Content-Range header
547 if (StartPos != 0)
548 return true;
549
550 if (sscanf(Val.c_str(),"%lu",&Size) != 1)
551 return _error->Error("The http server sent an invalid Content-Length header");
552 return true;
553 }
554
92e889c8
AL
555 if (stringcasecmp(Tag,"Content-Type:") == 0)
556 {
557 HaveContent = true;
558 return true;
559 }
560
561 if (stringcasecmp(Tag,"Content-Range:") == 0)
be4401bf 562 {
92e889c8
AL
563 HaveContent = true;
564
be4401bf
AL
565 if (sscanf(Val.c_str(),"bytes %lu-%*u/%lu",&StartPos,&Size) != 2)
566 return _error->Error("The http server sent an invalid Content-Range header");
567 if ((unsigned)StartPos > Size)
568 return _error->Error("This http server has broken range support");
569 return true;
570 }
571
92e889c8 572 if (stringcasecmp(Tag,"Transfer-Encoding:") == 0)
be4401bf 573 {
92e889c8
AL
574 HaveContent = true;
575 if (stringcasecmp(Val,"chunked") == 0)
be4401bf 576 Encoding = Chunked;
92e889c8 577
be4401bf
AL
578 return true;
579 }
580
92e889c8 581 if (stringcasecmp(Tag,"Last-Modified:") == 0)
be4401bf
AL
582 {
583 if (StrToTime(Val,Date) == false)
584 return _error->Error("Unknown date format");
585 return true;
586 }
587
588 return true;
589}
590 /*}}}*/
591
592// HttpMethod::SendReq - Send the HTTP request /*{{{*/
593// ---------------------------------------------------------------------
594/* This places the http request in the outbound buffer */
595void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
596{
597 URI Uri = Itm->Uri;
c1a22377 598
be4401bf 599 // The HTTP server expects a hostname with a trailing :port
c1a22377 600 char Buf[1000];
be4401bf
AL
601 string ProperHost = Uri.Host;
602 if (Uri.Port != 0)
603 {
604 sprintf(Buf,":%u",Uri.Port);
605 ProperHost += Buf;
606 }
607
c1a22377
AL
608 // Just in case.
609 if (Itm->Uri.length() >= sizeof(Buf))
610 abort();
611
492f957a
AL
612 /* Build the request. We include a keep-alive header only for non-proxy
613 requests. This is to tweak old http/1.0 servers that do support keep-alive
614 but not HTTP/1.1 automatic keep-alive. Doing this with a proxy server
615 will glitch HTTP/1.0 proxies because they do not filter it out and
616 pass it on, HTTP/1.1 says the connection should default to keep alive
617 and we expect the proxy to do this */
be4401bf
AL
618 if (Proxy.empty() == true)
619 sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
a4edf53b 620 QuoteString(Uri.Path,"~").c_str(),ProperHost.c_str());
be4401bf 621 else
c1a22377
AL
622 {
623 /* Generate a cache control header if necessary. We place a max
624 cache age on index files, optionally set a no-cache directive
625 and a no-store directive for archives. */
be4401bf
AL
626 sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\n",
627 Itm->Uri.c_str(),ProperHost.c_str());
c1a22377 628 if (_config->FindB("Acquire::http::No-Cache",false) == true)
b53b7926 629 strcat(Buf,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
c1a22377
AL
630 else
631 {
632 if (Itm->IndexFile == true)
633 sprintf(Buf+strlen(Buf),"Cache-Control: max-age=%u\r\n",
634 _config->FindI("Acquire::http::Max-Age",60*60*24));
635 else
636 {
637 if (_config->FindB("Acquire::http::No-Store",false) == true)
638 strcat(Buf,"Cache-Control: no-store\r\n");
639 }
640 }
641 }
642
be4401bf 643 string Req = Buf;
492f957a 644
be4401bf
AL
645 // Check for a partial file
646 struct stat SBuf;
647 if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
648 {
649 // In this case we send an if-range query with a range header
650 sprintf(Buf,"Range: bytes=%li-\r\nIf-Range: %s\r\n",SBuf.st_size - 1,
651 TimeRFC1123(SBuf.st_mtime).c_str());
652 Req += Buf;
653 }
654 else
655 {
656 if (Itm->LastModified != 0)
657 {
658 sprintf(Buf,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm->LastModified).c_str());
659 Req += Buf;
660 }
661 }
662
8d64c395
AL
663 if (Proxy.User.empty() == false || Proxy.Password.empty() == false)
664 Req += string("Proxy-Authorization: Basic ") +
665 Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n";
be4401bf
AL
666
667 Req += "User-Agent: Debian APT-HTTP/1.2\r\n\r\n";
39536c62 668// cerr << Req << endl;
c1a22377 669
be4401bf
AL
670 Out.Read(Req);
671}
672 /*}}}*/
673// HttpMethod::Go - Run a single loop /*{{{*/
674// ---------------------------------------------------------------------
675/* This runs the select loop over the server FDs, Output file FDs and
676 stdin. */
677bool HttpMethod::Go(bool ToFile,ServerState *Srv)
678{
679 // Server has closed the connection
680 if (Srv->ServerFd == -1 && Srv->In.WriteSpace() == false)
681 return false;
682
683 fd_set rfds,wfds,efds;
684 FD_ZERO(&rfds);
685 FD_ZERO(&wfds);
686 FD_ZERO(&efds);
687
688 // Add the server
689 if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1)
690 FD_SET(Srv->ServerFd,&wfds);
691 if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1)
692 FD_SET(Srv->ServerFd,&rfds);
693
694 // Add the file
695 int FileFD = -1;
696 if (File != 0)
697 FileFD = File->Fd();
698
699 if (Srv->In.WriteSpace() == true && ToFile == true && FileFD != -1)
700 FD_SET(FileFD,&wfds);
701
702 // Add stdin
703 FD_SET(STDIN_FILENO,&rfds);
704
705 // Error Set
706 if (FileFD != -1)
707 FD_SET(FileFD,&efds);
708 if (Srv->ServerFd != -1)
709 FD_SET(Srv->ServerFd,&efds);
710
711 // Figure out the max fd
712 int MaxFd = FileFD;
713 if (MaxFd < Srv->ServerFd)
714 MaxFd = Srv->ServerFd;
715
716 // Select
717 struct timeval tv;
3000ccea 718 tv.tv_sec = TimeOut;
be4401bf
AL
719 tv.tv_usec = 0;
720 int Res = 0;
721 if ((Res = select(MaxFd+1,&rfds,&wfds,&efds,&tv)) < 0)
722 return _error->Errno("select","Select failed");
723
724 if (Res == 0)
725 {
726 _error->Error("Connection timed out");
727 return ServerDie(Srv);
728 }
729
730 // Some kind of exception (error) on the sockets, die
731 if ((FileFD != -1 && FD_ISSET(FileFD,&efds)) ||
732 (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&efds)))
733 return _error->Error("Socket Exception");
734
735 // Handle server IO
736 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds))
737 {
738 errno = 0;
739 if (Srv->In.Read(Srv->ServerFd) == false)
740 return ServerDie(Srv);
741 }
742
743 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&wfds))
744 {
745 errno = 0;
746 if (Srv->Out.Write(Srv->ServerFd) == false)
747 return ServerDie(Srv);
748 }
749
750 // Send data to the file
751 if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
752 {
753 if (Srv->In.Write(FileFD) == false)
754 return _error->Errno("write","Error writing to output file");
755 }
756
757 // Handle commands from APT
758 if (FD_ISSET(STDIN_FILENO,&rfds))
759 {
760 if (Run(true) != 0)
761 exit(100);
762 }
763
764 return true;
765}
766 /*}}}*/
767// HttpMethod::Flush - Dump the buffer into the file /*{{{*/
768// ---------------------------------------------------------------------
769/* This takes the current input buffer from the Server FD and writes it
770 into the file */
771bool HttpMethod::Flush(ServerState *Srv)
772{
773 if (File != 0)
774 {
775 SetNonBlock(File->Fd(),false);
776 if (Srv->In.WriteSpace() == false)
777 return true;
778
779 while (Srv->In.WriteSpace() == true)
780 {
781 if (Srv->In.Write(File->Fd()) == false)
782 return _error->Errno("write","Error writing to file");
92e889c8
AL
783 if (Srv->In.IsLimit() == true)
784 return true;
be4401bf
AL
785 }
786
787 if (Srv->In.IsLimit() == true || Srv->Encoding == ServerState::Closes)
788 return true;
789 }
790 return false;
791}
792 /*}}}*/
793// HttpMethod::ServerDie - The server has closed the connection. /*{{{*/
794// ---------------------------------------------------------------------
795/* */
796bool HttpMethod::ServerDie(ServerState *Srv)
797{
2b154e53
AL
798 unsigned int LErrno = errno;
799
be4401bf
AL
800 // Dump the buffer to the file
801 if (Srv->State == ServerState::Data)
802 {
803 SetNonBlock(File->Fd(),false);
804 while (Srv->In.WriteSpace() == true)
805 {
806 if (Srv->In.Write(File->Fd()) == false)
807 return _error->Errno("write","Error writing to the file");
92e889c8
AL
808
809 // Done
810 if (Srv->In.IsLimit() == true)
811 return true;
be4401bf
AL
812 }
813 }
814
815 // See if this is because the server finished the data stream
816 if (Srv->In.IsLimit() == false && Srv->State != ServerState::Header &&
817 Srv->Encoding != ServerState::Closes)
818 {
3d615484 819 Srv->Close();
2b154e53 820 if (LErrno == 0)
be4401bf 821 return _error->Error("Error reading from server Remote end closed connection");
2b154e53 822 errno = LErrno;
be4401bf
AL
823 return _error->Errno("read","Error reading from server");
824 }
825 else
826 {
827 Srv->In.Limit(-1);
828
829 // Nothing left in the buffer
830 if (Srv->In.WriteSpace() == false)
831 return false;
832
833 // We may have got multiple responses back in one packet..
834 Srv->Close();
835 return true;
836 }
837
838 return false;
839}
840 /*}}}*/
841// HttpMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
842// ---------------------------------------------------------------------
843/* We look at the header data we got back from the server and decide what
844 to do. Returns
845 0 - File is open,
846 1 - IMS hit
92e889c8 847 3 - Unrecoverable error
94235cfb
AL
848 4 - Error with error content page
849 5 - Unrecoverable non-server error (close the connection) */
be4401bf
AL
850int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
851{
852 // Not Modified
853 if (Srv->Result == 304)
854 {
855 unlink(Queue->DestFile.c_str());
856 Res.IMSHit = true;
857 Res.LastModified = Queue->LastModified;
858 return 1;
859 }
860
861 /* We have a reply we dont handle. This should indicate a perm server
862 failure */
863 if (Srv->Result < 200 || Srv->Result >= 300)
864 {
865 _error->Error("%u %s",Srv->Result,Srv->Code);
92e889c8
AL
866 if (Srv->HaveContent == true)
867 return 4;
be4401bf
AL
868 return 3;
869 }
870
871 // This is some sort of 2xx 'data follows' reply
872 Res.LastModified = Srv->Date;
873 Res.Size = Srv->Size;
874
875 // Open the file
876 delete File;
877 File = new FileFd(Queue->DestFile,FileFd::WriteAny);
878 if (_error->PendingError() == true)
94235cfb 879 return 5;
492f957a
AL
880
881 FailFile = Queue->DestFile;
91cb4c6b 882 FailFile.c_str(); // Make sure we don't do a malloc in the signal handler
492f957a
AL
883 FailFd = File->Fd();
884 FailTime = Srv->Date;
885
be4401bf
AL
886 // Set the expected size
887 if (Srv->StartPos >= 0)
888 {
889 Res.ResumePoint = Srv->StartPos;
890 ftruncate(File->Fd(),Srv->StartPos);
891 }
892
893 // Set the start point
894 lseek(File->Fd(),0,SEEK_END);
895
896 delete Srv->In.MD5;
897 Srv->In.MD5 = new MD5Summation;
898
899 // Fill the MD5 Hash if the file is non-empty (resume)
900 if (Srv->StartPos > 0)
901 {
902 lseek(File->Fd(),0,SEEK_SET);
903 if (Srv->In.MD5->AddFD(File->Fd(),Srv->StartPos) == false)
904 {
905 _error->Errno("read","Problem hashing file");
94235cfb 906 return 5;
be4401bf
AL
907 }
908 lseek(File->Fd(),0,SEEK_END);
909 }
910
911 SetNonBlock(File->Fd(),true);
912 return 0;
913}
914 /*}}}*/
492f957a
AL
915// HttpMethod::SigTerm - Handle a fatal signal /*{{{*/
916// ---------------------------------------------------------------------
917/* This closes and timestamps the open file. This is neccessary to get
918 resume behavoir on user abort */
919void HttpMethod::SigTerm(int)
920{
921 if (FailFd == -1)
922 exit(100);
923 close(FailFd);
924
925 // Timestamp
926 struct utimbuf UBuf;
927 time(&UBuf.actime);
928 UBuf.actime = FailTime;
929 UBuf.modtime = FailTime;
930 utime(FailFile.c_str(),&UBuf);
931
932 exit(100);
933}
934 /*}}}*/
5cb5d8dc
AL
935// HttpMethod::Fetch - Fetch an item /*{{{*/
936// ---------------------------------------------------------------------
937/* This adds an item to the pipeline. We keep the pipeline at a fixed
938 depth. */
939bool HttpMethod::Fetch(FetchItem *)
940{
941 if (Server == 0)
942 return true;
3000ccea 943
5cb5d8dc
AL
944 // Queue the requests
945 int Depth = -1;
946 bool Tail = false;
c1a22377 947 for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; I = I->Next, Depth++)
5cb5d8dc
AL
948 {
949 // Make sure we stick with the same server
950 if (Server->Comp(I->Uri) == false)
951 break;
5cb5d8dc
AL
952 if (QueueBack == I)
953 Tail = true;
954 if (Tail == true)
955 {
5cb5d8dc
AL
956 QueueBack = I->Next;
957 SendReq(I,Server->Out);
958 continue;
959 }
960 }
961
962 return true;
963};
964 /*}}}*/
85f72a56
AL
965// HttpMethod::Configuration - Handle a configuration message /*{{{*/
966// ---------------------------------------------------------------------
967/* We stash the desired pipeline depth */
968bool HttpMethod::Configuration(string Message)
969{
970 if (pkgAcqMethod::Configuration(Message) == false)
971 return false;
972
30456e14
AL
973 TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut);
974 PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth",
975 PipelineDepth);
3000ccea 976
85f72a56
AL
977 return true;
978}
979 /*}}}*/
492f957a 980// HttpMethod::Loop - Main loop /*{{{*/
be4401bf
AL
981// ---------------------------------------------------------------------
982/* */
983int HttpMethod::Loop()
984{
492f957a
AL
985 signal(SIGTERM,SigTerm);
986 signal(SIGINT,SigTerm);
987
5cb5d8dc 988 Server = 0;
be4401bf 989
92e889c8 990 int FailCounter = 0;
be4401bf 991 while (1)
2b154e53 992 {
be4401bf
AL
993 // We have no commands, wait for some to arrive
994 if (Queue == 0)
995 {
996 if (WaitFd(STDIN_FILENO) == false)
997 return 0;
998 }
999
1000 // Run messages
1001 if (Run(true) != 0)
1002 return 100;
1003
1004 if (Queue == 0)
1005 continue;
1006
1007 // Connect to the server
1008 if (Server == 0 || Server->Comp(Queue->Uri) == false)
1009 {
1010 delete Server;
1011 Server = new ServerState(Queue->Uri,this);
1012 }
1013
a7fb252c
AL
1014 // Reset the pipeline
1015 if (Server->ServerFd == -1)
1016 QueueBack = Queue;
1017
be4401bf
AL
1018 // Connnect to the host
1019 if (Server->Open() == false)
1020 {
43252d15 1021 Fail(true);
a1459f52
AL
1022 delete Server;
1023 Server = 0;
be4401bf
AL
1024 continue;
1025 }
be4401bf 1026
5cb5d8dc
AL
1027 // Fill the pipeline.
1028 Fetch(0);
1029
92e889c8
AL
1030 // Fetch the next URL header data from the server.
1031 switch (Server->RunHeaders())
be4401bf 1032 {
92e889c8
AL
1033 case 0:
1034 break;
1035
1036 // The header data is bad
1037 case 2:
1038 {
1039 _error->Error("Bad header Data");
43252d15 1040 Fail(true);
92e889c8
AL
1041 continue;
1042 }
1043
1044 // The server closed a connection during the header get..
1045 default:
1046 case 1:
1047 {
1048 FailCounter++;
3d615484 1049 _error->Discard();
92e889c8 1050 Server->Close();
2b154e53
AL
1051
1052 if (FailCounter >= 2)
1053 {
1054 Fail("Connection timed out",true);
1055 FailCounter = 0;
1056 }
1057
92e889c8
AL
1058 continue;
1059 }
1060 };
5cb5d8dc 1061
be4401bf
AL
1062 // Decide what to do.
1063 FetchResult Res;
bfd22fc0 1064 Res.Filename = Queue->DestFile;
be4401bf
AL
1065 switch (DealWithHeaders(Res,Server))
1066 {
1067 // Ok, the file is Open
1068 case 0:
1069 {
1070 URIStart(Res);
1071
1072 // Run the data
492f957a
AL
1073 bool Result = Server->RunData();
1074
1075 // Close the file, destroy the FD object and timestamp it
1076 FailFd = -1;
1077 delete File;
1078 File = 0;
1079
1080 // Timestamp
1081 struct utimbuf UBuf;
1082 time(&UBuf.actime);
1083 UBuf.actime = Server->Date;
1084 UBuf.modtime = Server->Date;
1085 utime(Queue->DestFile.c_str(),&UBuf);
1086
1087 // Send status to APT
1088 if (Result == true)
92e889c8
AL
1089 {
1090 Res.MD5Sum = Server->In.MD5->Result();
1091 URIDone(Res);
1092 }
492f957a 1093 else
2b154e53 1094 Fail(true);
492f957a 1095
be4401bf
AL
1096 break;
1097 }
1098
1099 // IMS hit
1100 case 1:
1101 {
1102 URIDone(Res);
1103 break;
1104 }
1105
1106 // Hard server error, not found or something
1107 case 3:
1108 {
1109 Fail();
1110 break;
1111 }
94235cfb
AL
1112
1113 // Hard internal error, kill the connection and fail
1114 case 5:
1115 {
1116 Fail();
1117 Server->Close();
1118 break;
1119 }
92e889c8
AL
1120
1121 // We need to flush the data, the header is like a 404 w/ error text
1122 case 4:
1123 {
1124 Fail();
1125
1126 // Send to content to dev/null
1127 File = new FileFd("/dev/null",FileFd::WriteExists);
1128 Server->RunData();
1129 delete File;
1130 File = 0;
1131 break;
1132 }
be4401bf
AL
1133
1134 default:
1135 Fail("Internal error");
1136 break;
92e889c8
AL
1137 }
1138
1139 FailCounter = 0;
be4401bf
AL
1140 }
1141
1142 return 0;
1143}
1144 /*}}}*/
1145
1146int main()
1147{
1148 HttpMethod Mth;
1149
1150 return Mth.Loop();
1151}