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