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