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