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