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