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