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