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