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