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