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