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