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