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