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