More tests
[ntk/apt.git] / methods / http.cc
CommitLineData
be4401bf
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
42195eb2 3// $Id: http.cc,v 1.52 2001/05/27 23:53:55 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)
be4401bf
AL
729 return _error->Errno("select","Select failed");
730
731 if (Res == 0)
732 {
733 _error->Error("Connection timed out");
734 return ServerDie(Srv);
735 }
736
be4401bf
AL
737 // Handle server IO
738 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds))
739 {
740 errno = 0;
741 if (Srv->In.Read(Srv->ServerFd) == false)
742 return ServerDie(Srv);
743 }
744
745 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&wfds))
746 {
747 errno = 0;
748 if (Srv->Out.Write(Srv->ServerFd) == false)
749 return ServerDie(Srv);
750 }
751
752 // Send data to the file
753 if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
754 {
755 if (Srv->In.Write(FileFD) == false)
756 return _error->Errno("write","Error writing to output file");
757 }
758
759 // Handle commands from APT
760 if (FD_ISSET(STDIN_FILENO,&rfds))
761 {
6920216d 762 if (Run(true) != -1)
be4401bf
AL
763 exit(100);
764 }
765
766 return true;
767}
768 /*}}}*/
769// HttpMethod::Flush - Dump the buffer into the file /*{{{*/
770// ---------------------------------------------------------------------
771/* This takes the current input buffer from the Server FD and writes it
772 into the file */
773bool HttpMethod::Flush(ServerState *Srv)
774{
775 if (File != 0)
776 {
777 SetNonBlock(File->Fd(),false);
778 if (Srv->In.WriteSpace() == false)
779 return true;
780
781 while (Srv->In.WriteSpace() == true)
782 {
783 if (Srv->In.Write(File->Fd()) == false)
784 return _error->Errno("write","Error writing to file");
92e889c8
AL
785 if (Srv->In.IsLimit() == true)
786 return true;
be4401bf
AL
787 }
788
789 if (Srv->In.IsLimit() == true || Srv->Encoding == ServerState::Closes)
790 return true;
791 }
792 return false;
793}
794 /*}}}*/
795// HttpMethod::ServerDie - The server has closed the connection. /*{{{*/
796// ---------------------------------------------------------------------
797/* */
798bool HttpMethod::ServerDie(ServerState *Srv)
799{
2b154e53
AL
800 unsigned int LErrno = errno;
801
be4401bf
AL
802 // Dump the buffer to the file
803 if (Srv->State == ServerState::Data)
804 {
805 SetNonBlock(File->Fd(),false);
806 while (Srv->In.WriteSpace() == true)
807 {
808 if (Srv->In.Write(File->Fd()) == false)
809 return _error->Errno("write","Error writing to the file");
92e889c8
AL
810
811 // Done
812 if (Srv->In.IsLimit() == true)
813 return true;
be4401bf
AL
814 }
815 }
816
817 // See if this is because the server finished the data stream
818 if (Srv->In.IsLimit() == false && Srv->State != ServerState::Header &&
819 Srv->Encoding != ServerState::Closes)
820 {
3d615484 821 Srv->Close();
2b154e53 822 if (LErrno == 0)
be4401bf 823 return _error->Error("Error reading from server Remote end closed connection");
2b154e53 824 errno = LErrno;
be4401bf
AL
825 return _error->Errno("read","Error reading from server");
826 }
827 else
828 {
829 Srv->In.Limit(-1);
830
831 // Nothing left in the buffer
832 if (Srv->In.WriteSpace() == false)
833 return false;
834
835 // We may have got multiple responses back in one packet..
836 Srv->Close();
837 return true;
838 }
839
840 return false;
841}
842 /*}}}*/
843// HttpMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
844// ---------------------------------------------------------------------
845/* We look at the header data we got back from the server and decide what
846 to do. Returns
847 0 - File is open,
848 1 - IMS hit
92e889c8 849 3 - Unrecoverable error
94235cfb
AL
850 4 - Error with error content page
851 5 - Unrecoverable non-server error (close the connection) */
be4401bf
AL
852int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
853{
854 // Not Modified
855 if (Srv->Result == 304)
856 {
857 unlink(Queue->DestFile.c_str());
858 Res.IMSHit = true;
859 Res.LastModified = Queue->LastModified;
860 return 1;
861 }
862
863 /* We have a reply we dont handle. This should indicate a perm server
864 failure */
865 if (Srv->Result < 200 || Srv->Result >= 300)
866 {
867 _error->Error("%u %s",Srv->Result,Srv->Code);
92e889c8
AL
868 if (Srv->HaveContent == true)
869 return 4;
be4401bf
AL
870 return 3;
871 }
872
873 // This is some sort of 2xx 'data follows' reply
874 Res.LastModified = Srv->Date;
875 Res.Size = Srv->Size;
876
877 // Open the file
878 delete File;
879 File = new FileFd(Queue->DestFile,FileFd::WriteAny);
880 if (_error->PendingError() == true)
94235cfb 881 return 5;
492f957a
AL
882
883 FailFile = Queue->DestFile;
30b30ec1 884 FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
492f957a
AL
885 FailFd = File->Fd();
886 FailTime = Srv->Date;
887
be4401bf
AL
888 // Set the expected size
889 if (Srv->StartPos >= 0)
890 {
891 Res.ResumePoint = Srv->StartPos;
892 ftruncate(File->Fd(),Srv->StartPos);
893 }
894
895 // Set the start point
896 lseek(File->Fd(),0,SEEK_END);
897
63b1700f
AL
898 delete Srv->In.Hash;
899 Srv->In.Hash = new Hashes;
be4401bf 900
63b1700f 901 // Fill the Hash if the file is non-empty (resume)
be4401bf
AL
902 if (Srv->StartPos > 0)
903 {
904 lseek(File->Fd(),0,SEEK_SET);
63b1700f 905 if (Srv->In.Hash->AddFD(File->Fd(),Srv->StartPos) == false)
be4401bf
AL
906 {
907 _error->Errno("read","Problem hashing file");
94235cfb 908 return 5;
be4401bf
AL
909 }
910 lseek(File->Fd(),0,SEEK_END);
911 }
912
913 SetNonBlock(File->Fd(),true);
914 return 0;
915}
916 /*}}}*/
492f957a
AL
917// HttpMethod::SigTerm - Handle a fatal signal /*{{{*/
918// ---------------------------------------------------------------------
919/* This closes and timestamps the open file. This is neccessary to get
920 resume behavoir on user abort */
921void HttpMethod::SigTerm(int)
922{
923 if (FailFd == -1)
ffe9323a 924 _exit(100);
492f957a
AL
925 close(FailFd);
926
927 // Timestamp
928 struct utimbuf UBuf;
492f957a
AL
929 UBuf.actime = FailTime;
930 UBuf.modtime = FailTime;
931 utime(FailFile.c_str(),&UBuf);
932
ffe9323a 933 _exit(100);
492f957a
AL
934}
935 /*}}}*/
5cb5d8dc
AL
936// HttpMethod::Fetch - Fetch an item /*{{{*/
937// ---------------------------------------------------------------------
938/* This adds an item to the pipeline. We keep the pipeline at a fixed
939 depth. */
940bool HttpMethod::Fetch(FetchItem *)
941{
942 if (Server == 0)
943 return true;
3000ccea 944
5cb5d8dc
AL
945 // Queue the requests
946 int Depth = -1;
947 bool Tail = false;
f93d1355
AL
948 for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth;
949 I = I->Next, Depth++)
5cb5d8dc 950 {
f93d1355
AL
951 // If pipelining is disabled, we only queue 1 request
952 if (Server->Pipeline == false && Depth >= 0)
953 break;
954
5cb5d8dc
AL
955 // Make sure we stick with the same server
956 if (Server->Comp(I->Uri) == false)
957 break;
5cb5d8dc
AL
958 if (QueueBack == I)
959 Tail = true;
960 if (Tail == true)
961 {
5cb5d8dc
AL
962 QueueBack = I->Next;
963 SendReq(I,Server->Out);
964 continue;
f93d1355 965 }
5cb5d8dc
AL
966 }
967
968 return true;
969};
970 /*}}}*/
85f72a56
AL
971// HttpMethod::Configuration - Handle a configuration message /*{{{*/
972// ---------------------------------------------------------------------
973/* We stash the desired pipeline depth */
974bool HttpMethod::Configuration(string Message)
975{
976 if (pkgAcqMethod::Configuration(Message) == false)
977 return false;
978
30456e14
AL
979 TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut);
980 PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth",
981 PipelineDepth);
c98b1307 982 Debug = _config->FindB("Debug::Acquire::http",false);
3000ccea 983
85f72a56
AL
984 return true;
985}
986 /*}}}*/
492f957a 987// HttpMethod::Loop - Main loop /*{{{*/
be4401bf
AL
988// ---------------------------------------------------------------------
989/* */
990int HttpMethod::Loop()
991{
492f957a
AL
992 signal(SIGTERM,SigTerm);
993 signal(SIGINT,SigTerm);
994
5cb5d8dc 995 Server = 0;
be4401bf 996
92e889c8 997 int FailCounter = 0;
be4401bf 998 while (1)
2b154e53 999 {
be4401bf
AL
1000 // We have no commands, wait for some to arrive
1001 if (Queue == 0)
1002 {
1003 if (WaitFd(STDIN_FILENO) == false)
1004 return 0;
1005 }
1006
6920216d
AL
1007 /* Run messages, we can accept 0 (no message) if we didn't
1008 do a WaitFd above.. Otherwise the FD is closed. */
1009 int Result = Run(true);
1010 if (Result != -1 && (Result != 0 || Queue == 0))
be4401bf
AL
1011 return 100;
1012
1013 if (Queue == 0)
1014 continue;
1015
1016 // Connect to the server
1017 if (Server == 0 || Server->Comp(Queue->Uri) == false)
1018 {
1019 delete Server;
1020 Server = new ServerState(Queue->Uri,this);
1021 }
e836f356
AL
1022
1023 /* If the server has explicitly said this is the last connection
1024 then we pre-emptively shut down the pipeline and tear down
1025 the connection. This will speed up HTTP/1.0 servers a tad
1026 since we don't have to wait for the close sequence to
1027 complete */
1028 if (Server->Persistent == false)
1029 Server->Close();
1030
a7fb252c
AL
1031 // Reset the pipeline
1032 if (Server->ServerFd == -1)
1033 QueueBack = Queue;
1034
be4401bf
AL
1035 // Connnect to the host
1036 if (Server->Open() == false)
1037 {
43252d15 1038 Fail(true);
a1459f52
AL
1039 delete Server;
1040 Server = 0;
be4401bf
AL
1041 continue;
1042 }
be4401bf 1043
5cb5d8dc
AL
1044 // Fill the pipeline.
1045 Fetch(0);
1046
92e889c8
AL
1047 // Fetch the next URL header data from the server.
1048 switch (Server->RunHeaders())
be4401bf 1049 {
92e889c8
AL
1050 case 0:
1051 break;
1052
1053 // The header data is bad
1054 case 2:
1055 {
1056 _error->Error("Bad header Data");
43252d15 1057 Fail(true);
b2e465d6 1058 RotateDNS();
92e889c8
AL
1059 continue;
1060 }
1061
1062 // The server closed a connection during the header get..
1063 default:
1064 case 1:
1065 {
1066 FailCounter++;
3d615484 1067 _error->Discard();
92e889c8 1068 Server->Close();
f93d1355
AL
1069 Server->Pipeline = false;
1070
2b154e53
AL
1071 if (FailCounter >= 2)
1072 {
8195ae46 1073 Fail("Connection failed",true);
2b154e53
AL
1074 FailCounter = 0;
1075 }
1076
b2e465d6 1077 RotateDNS();
92e889c8
AL
1078 continue;
1079 }
1080 };
5cb5d8dc 1081
be4401bf
AL
1082 // Decide what to do.
1083 FetchResult Res;
bfd22fc0 1084 Res.Filename = Queue->DestFile;
be4401bf
AL
1085 switch (DealWithHeaders(Res,Server))
1086 {
1087 // Ok, the file is Open
1088 case 0:
1089 {
1090 URIStart(Res);
1091
1092 // Run the data
492f957a
AL
1093 bool Result = Server->RunData();
1094
b2e465d6
AL
1095 /* If the server is sending back sizeless responses then fill in
1096 the size now */
1097 if (Res.Size == 0)
1098 Res.Size = File->Size();
1099
492f957a
AL
1100 // Close the file, destroy the FD object and timestamp it
1101 FailFd = -1;
1102 delete File;
1103 File = 0;
1104
1105 // Timestamp
1106 struct utimbuf UBuf;
1107 time(&UBuf.actime);
1108 UBuf.actime = Server->Date;
1109 UBuf.modtime = Server->Date;
1110 utime(Queue->DestFile.c_str(),&UBuf);
1111
1112 // Send status to APT
1113 if (Result == true)
92e889c8 1114 {
a7c835af 1115 Res.TakeHashes(*Server->In.Hash);
92e889c8
AL
1116 URIDone(Res);
1117 }
492f957a 1118 else
2b154e53 1119 Fail(true);
e836f356 1120
be4401bf
AL
1121 break;
1122 }
1123
1124 // IMS hit
1125 case 1:
1126 {
1127 URIDone(Res);
1128 break;
1129 }
1130
1131 // Hard server error, not found or something
1132 case 3:
1133 {
1134 Fail();
1135 break;
1136 }
94235cfb
AL
1137
1138 // Hard internal error, kill the connection and fail
1139 case 5:
1140 {
1141 Fail();
b2e465d6 1142 RotateDNS();
94235cfb
AL
1143 Server->Close();
1144 break;
1145 }
92e889c8
AL
1146
1147 // We need to flush the data, the header is like a 404 w/ error text
1148 case 4:
1149 {
1150 Fail();
1151
1152 // Send to content to dev/null
1153 File = new FileFd("/dev/null",FileFd::WriteExists);
1154 Server->RunData();
1155 delete File;
1156 File = 0;
1157 break;
1158 }
be4401bf
AL
1159
1160 default:
1161 Fail("Internal error");
1162 break;
92e889c8
AL
1163 }
1164
1165 FailCounter = 0;
be4401bf
AL
1166 }
1167
1168 return 0;
1169}
1170 /*}}}*/
1171
1172int main()
1173{
1174 HttpMethod Mth;
1175
1176 return Mth.Loop();
1177}