]> git.saurik.com Git - apt-legacy.git/blob - methods/http.cc
f27408e9b1d974ff9564d7fd15e528e168a71c84
[apt-legacy.git] / methods / http.cc
1 extern "C" {
2 #include <mach-o/nlist.h>
3 }
4
5 // -*- mode: cpp; mode: fold -*-
6 // Description /*{{{*/
7 // $Id: http.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
8 /* ######################################################################
9
10 HTTP Aquire Method - This is the HTTP aquire method for APT.
11
12 It uses HTTP/1.1 and many of the fancy options there-in, such as
13 pipelining, range, if-range and so on.
14
15 It is based on a doubly buffered select loop. A groupe of requests are
16 fed into a single output buffer that is constantly fed out the
17 socket. This provides ideal pipelining as in many cases all of the
18 requests will fit into a single packet. The input socket is buffered
19 the same way and fed into the fd for the file (may be a pipe in future).
20
21 This double buffering provides fairly substantial transfer rates,
22 compared to wget the http method is about 4% faster. Most importantly,
23 when HTTP is compared with FTP as a protocol the speed difference is
24 huge. In tests over the internet from two sites to llug (via ATM) this
25 program got 230k/s sustained http transfer rates. FTP on the other
26 hand topped out at 170k/s. That combined with the time to setup the
27 FTP connection makes HTTP a vastly superior protocol.
28
29 ##################################################################### */
30 /*}}}*/
31 // Include Files /*{{{*/
32 #include <apt-pkg/fileutl.h>
33 #include <apt-pkg/acquire-method.h>
34 #include <apt-pkg/error.h>
35 #include <apt-pkg/hashes.h>
36
37 #include <sys/sysctl.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include <utime.h>
41 #include <unistd.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <iostream>
47 #include <apti18n.h>
48
49 // Internet stuff
50 #include <netdb.h>
51 #include <arpa/inet.h>
52
53 #include <CoreFoundation/CoreFoundation.h>
54 #include <CoreServices/CoreServices.h>
55 #include <SystemConfiguration/SystemConfiguration.h>
56
57 #include "connect.h"
58 #include "rfc2553emu.h"
59 #include "http.h"
60
61 /*}}}*/
62 using namespace std;
63
64 const char *Machine_;
65 const char *SerialNumber_;
66
67 void CfrsError(const char *name, CFReadStreamRef rs) {
68 CFStreamError se = CFReadStreamGetError(rs);
69
70 if (se.domain == kCFStreamErrorDomainCustom) {
71 } else if (se.domain == kCFStreamErrorDomainPOSIX) {
72 _error->Error("POSIX: %s", strerror(se.error));
73 } else if (se.domain == kCFStreamErrorDomainMacOSStatus) {
74 _error->Error("MacOSStatus: %ld", se.error);
75 } else if (se.domain == kCFStreamErrorDomainNetDB) {
76 _error->Error("NetDB: %s %s", name, gai_strerror(se.error));
77 } else if (se.domain == kCFStreamErrorDomainMach) {
78 _error->Error("Mach: %ld", se.error);
79 } else if (se.domain == kCFStreamErrorDomainHTTP) {
80 switch (se.error) {
81 case kCFStreamErrorHTTPParseFailure:
82 _error->Error("Parse failure");
83 break;
84
85 case kCFStreamErrorHTTPRedirectionLoop:
86 _error->Error("Redirection loop");
87 break;
88
89 case kCFStreamErrorHTTPBadURL:
90 _error->Error("Bad URL");
91 break;
92
93 default:
94 _error->Error("Unknown HTTP error: %ld", se.error);
95 break;
96 }
97 } else if (se.domain == kCFStreamErrorDomainSOCKS) {
98 _error->Error("SOCKS: %ld", se.error);
99 } else if (se.domain == kCFStreamErrorDomainSystemConfiguration) {
100 _error->Error("SystemConfiguration: %ld", se.error);
101 } else if (se.domain == kCFStreamErrorDomainSSL) {
102 _error->Error("SSL: %ld", se.error);
103 } else {
104 _error->Error("Domain #%ld: %ld", se.domain, se.error);
105 }
106 }
107
108 string HttpMethod::FailFile;
109 int HttpMethod::FailFd = -1;
110 time_t HttpMethod::FailTime = 0;
111 unsigned long PipelineDepth = 10;
112 unsigned long TimeOut = 120;
113 bool Debug = false;
114
115 unsigned long CircleBuf::BwReadLimit=0;
116 unsigned long CircleBuf::BwTickReadData=0;
117 struct timeval CircleBuf::BwReadTick={0,0};
118 const unsigned int CircleBuf::BW_HZ=10;
119
120 // CircleBuf::CircleBuf - Circular input buffer /*{{{*/
121 // ---------------------------------------------------------------------
122 /* */
123 CircleBuf::CircleBuf(unsigned long Size) : Size(Size), Hash(0)
124 {
125 Buf = new unsigned char[Size];
126 Reset();
127
128 CircleBuf::BwReadLimit = _config->FindI("Acquire::http::Dl-Limit",0)*1024;
129 }
130 /*}}}*/
131 // CircleBuf::Reset - Reset to the default state /*{{{*/
132 // ---------------------------------------------------------------------
133 /* */
134 void CircleBuf::Reset()
135 {
136 InP = 0;
137 OutP = 0;
138 StrPos = 0;
139 MaxGet = (unsigned int)-1;
140 OutQueue = string();
141 if (Hash != 0)
142 {
143 delete Hash;
144 Hash = new Hashes;
145 }
146 };
147 /*}}}*/
148 // CircleBuf::Read - Read from a FD into the circular buffer /*{{{*/
149 // ---------------------------------------------------------------------
150 /* This fills up the buffer with as much data as is in the FD, assuming it
151 is non-blocking.. */
152 bool CircleBuf::Read(int Fd)
153 {
154 unsigned long BwReadMax;
155
156 while (1)
157 {
158 // Woops, buffer is full
159 if (InP - OutP == Size)
160 return true;
161
162 // what's left to read in this tick
163 BwReadMax = CircleBuf::BwReadLimit/BW_HZ;
164
165 if(CircleBuf::BwReadLimit) {
166 struct timeval now;
167 gettimeofday(&now,0);
168
169 unsigned long d = (now.tv_sec-CircleBuf::BwReadTick.tv_sec)*1000000 +
170 now.tv_usec-CircleBuf::BwReadTick.tv_usec;
171 if(d > 1000000/BW_HZ) {
172 CircleBuf::BwReadTick = now;
173 CircleBuf::BwTickReadData = 0;
174 }
175
176 if(CircleBuf::BwTickReadData >= BwReadMax) {
177 usleep(1000000/BW_HZ);
178 return true;
179 }
180 }
181
182 // Write the buffer segment
183 int Res;
184 if(CircleBuf::BwReadLimit) {
185 Res = read(Fd,Buf + (InP%Size),
186 BwReadMax > LeftRead() ? LeftRead() : BwReadMax);
187 } else
188 Res = read(Fd,Buf + (InP%Size),LeftRead());
189
190 if(Res > 0 && BwReadLimit > 0)
191 CircleBuf::BwTickReadData += Res;
192
193 if (Res == 0)
194 return false;
195 if (Res < 0)
196 {
197 if (errno == EAGAIN)
198 return true;
199 return false;
200 }
201
202 if (InP == 0)
203 gettimeofday(&Start,0);
204 InP += Res;
205 }
206 }
207 /*}}}*/
208 // CircleBuf::Read - Put the string into the buffer /*{{{*/
209 // ---------------------------------------------------------------------
210 /* This will hold the string in and fill the buffer with it as it empties */
211 bool CircleBuf::Read(string Data)
212 {
213 OutQueue += Data;
214 FillOut();
215 return true;
216 }
217 /*}}}*/
218 // CircleBuf::FillOut - Fill the buffer from the output queue /*{{{*/
219 // ---------------------------------------------------------------------
220 /* */
221 void CircleBuf::FillOut()
222 {
223 if (OutQueue.empty() == true)
224 return;
225 while (1)
226 {
227 // Woops, buffer is full
228 if (InP - OutP == Size)
229 return;
230
231 // Write the buffer segment
232 unsigned long Sz = LeftRead();
233 if (OutQueue.length() - StrPos < Sz)
234 Sz = OutQueue.length() - StrPos;
235 memcpy(Buf + (InP%Size),OutQueue.c_str() + StrPos,Sz);
236
237 // Advance
238 StrPos += Sz;
239 InP += Sz;
240 if (OutQueue.length() == StrPos)
241 {
242 StrPos = 0;
243 OutQueue = "";
244 return;
245 }
246 }
247 }
248 /*}}}*/
249 // CircleBuf::Write - Write from the buffer into a FD /*{{{*/
250 // ---------------------------------------------------------------------
251 /* This empties the buffer into the FD. */
252 bool CircleBuf::Write(int Fd)
253 {
254 while (1)
255 {
256 FillOut();
257
258 // Woops, buffer is empty
259 if (OutP == InP)
260 return true;
261
262 if (OutP == MaxGet)
263 return true;
264
265 // Write the buffer segment
266 int Res;
267 Res = write(Fd,Buf + (OutP%Size),LeftWrite());
268
269 if (Res == 0)
270 return false;
271 if (Res < 0)
272 {
273 if (errno == EAGAIN)
274 return true;
275
276 return false;
277 }
278
279 if (Hash != 0)
280 Hash->Add(Buf + (OutP%Size),Res);
281
282 OutP += Res;
283 }
284 }
285 /*}}}*/
286 // CircleBuf::WriteTillEl - Write from the buffer to a string /*{{{*/
287 // ---------------------------------------------------------------------
288 /* This copies till the first empty line */
289 bool CircleBuf::WriteTillEl(string &Data,bool Single)
290 {
291 // We cheat and assume it is unneeded to have more than one buffer load
292 for (unsigned long I = OutP; I < InP; I++)
293 {
294 if (Buf[I%Size] != '\n')
295 continue;
296 ++I;
297
298 if (Single == false)
299 {
300 if (I < InP && Buf[I%Size] == '\r')
301 ++I;
302 if (I >= InP || Buf[I%Size] != '\n')
303 continue;
304 ++I;
305 }
306
307 Data = "";
308 while (OutP < I)
309 {
310 unsigned long Sz = LeftWrite();
311 if (Sz == 0)
312 return false;
313 if (I - OutP < Sz)
314 Sz = I - OutP;
315 Data += string((char *)(Buf + (OutP%Size)),Sz);
316 OutP += Sz;
317 }
318 return true;
319 }
320 return false;
321 }
322 /*}}}*/
323 // CircleBuf::Stats - Print out stats information /*{{{*/
324 // ---------------------------------------------------------------------
325 /* */
326 void CircleBuf::Stats()
327 {
328 if (InP == 0)
329 return;
330
331 struct timeval Stop;
332 gettimeofday(&Stop,0);
333 /* float Diff = Stop.tv_sec - Start.tv_sec +
334 (float)(Stop.tv_usec - Start.tv_usec)/1000000;
335 clog << "Got " << InP << " in " << Diff << " at " << InP/Diff << endl;*/
336 }
337 /*}}}*/
338
339 // ServerState::ServerState - Constructor /*{{{*/
340 // ---------------------------------------------------------------------
341 /* */
342 ServerState::ServerState(URI Srv,HttpMethod *Owner) : Owner(Owner),
343 In(64*1024), Out(4*1024),
344 ServerName(Srv)
345 {
346 Reset();
347 }
348 /*}}}*/
349 // ServerState::Open - Open a connection to the server /*{{{*/
350 // ---------------------------------------------------------------------
351 /* This opens a connection to the server. */
352 bool ServerState::Open()
353 {
354 // Use the already open connection if possible.
355 if (ServerFd != -1)
356 return true;
357
358 Close();
359 In.Reset();
360 Out.Reset();
361 Persistent = true;
362
363 // Determine the proxy setting
364 if (getenv("http_proxy") == 0)
365 {
366 string DefProxy = _config->Find("Acquire::http::Proxy");
367 string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host);
368 if (SpecificProxy.empty() == false)
369 {
370 if (SpecificProxy == "DIRECT")
371 Proxy = "";
372 else
373 Proxy = SpecificProxy;
374 }
375 else
376 Proxy = DefProxy;
377 }
378 else
379 Proxy = getenv("http_proxy");
380
381 // Parse no_proxy, a , separated list of domains
382 if (getenv("no_proxy") != 0)
383 {
384 if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
385 Proxy = "";
386 }
387
388 // Determine what host and port to use based on the proxy settings
389 int Port = 0;
390 string Host;
391 if (Proxy.empty() == true || Proxy.Host.empty() == true)
392 {
393 if (ServerName.Port != 0)
394 Port = ServerName.Port;
395 Host = ServerName.Host;
396 }
397 else
398 {
399 if (Proxy.Port != 0)
400 Port = Proxy.Port;
401 Host = Proxy.Host;
402 }
403
404 // Connect to the remote server
405 if (Connect(Host,Port,"http",80,ServerFd,TimeOut,Owner) == false)
406 return false;
407
408 return true;
409 }
410 /*}}}*/
411 // ServerState::Close - Close a connection to the server /*{{{*/
412 // ---------------------------------------------------------------------
413 /* */
414 bool ServerState::Close()
415 {
416 close(ServerFd);
417 ServerFd = -1;
418 return true;
419 }
420 /*}}}*/
421 // ServerState::RunHeaders - Get the headers before the data /*{{{*/
422 // ---------------------------------------------------------------------
423 /* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
424 parse error occured */
425 int ServerState::RunHeaders()
426 {
427 State = Header;
428
429 Owner->Status(_("Waiting for headers"));
430
431 Major = 0;
432 Minor = 0;
433 Result = 0;
434 Size = 0;
435 StartPos = 0;
436 Encoding = Closes;
437 HaveContent = false;
438 time(&Date);
439
440 do
441 {
442 string Data;
443 if (In.WriteTillEl(Data) == false)
444 continue;
445
446 if (Debug == true)
447 clog << Data;
448
449 for (string::const_iterator I = Data.begin(); I < Data.end(); I++)
450 {
451 string::const_iterator J = I;
452 for (; J != Data.end() && *J != '\n' && *J != '\r';J++);
453 if (HeaderLine(string(I,J)) == false)
454 return 2;
455 I = J;
456 }
457
458 // 100 Continue is a Nop...
459 if (Result == 100)
460 continue;
461
462 // Tidy up the connection persistance state.
463 if (Encoding == Closes && HaveContent == true)
464 Persistent = false;
465
466 return 0;
467 }
468 while (Owner->Go(false,this) == true);
469
470 return 1;
471 }
472 /*}}}*/
473 // ServerState::RunData - Transfer the data from the socket /*{{{*/
474 // ---------------------------------------------------------------------
475 /* */
476 bool ServerState::RunData()
477 {
478 State = Data;
479
480 // Chunked transfer encoding is fun..
481 if (Encoding == Chunked)
482 {
483 while (1)
484 {
485 // Grab the block size
486 bool Last = true;
487 string Data;
488 In.Limit(-1);
489 do
490 {
491 if (In.WriteTillEl(Data,true) == true)
492 break;
493 }
494 while ((Last = Owner->Go(false,this)) == true);
495
496 if (Last == false)
497 return false;
498
499 // See if we are done
500 unsigned long Len = strtol(Data.c_str(),0,16);
501 if (Len == 0)
502 {
503 In.Limit(-1);
504
505 // We have to remove the entity trailer
506 Last = true;
507 do
508 {
509 if (In.WriteTillEl(Data,true) == true && Data.length() <= 2)
510 break;
511 }
512 while ((Last = Owner->Go(false,this)) == true);
513 if (Last == false)
514 return false;
515 return !_error->PendingError();
516 }
517
518 // Transfer the block
519 In.Limit(Len);
520 while (Owner->Go(true,this) == true)
521 if (In.IsLimit() == true)
522 break;
523
524 // Error
525 if (In.IsLimit() == false)
526 return false;
527
528 // The server sends an extra new line before the next block specifier..
529 In.Limit(-1);
530 Last = true;
531 do
532 {
533 if (In.WriteTillEl(Data,true) == true)
534 break;
535 }
536 while ((Last = Owner->Go(false,this)) == true);
537 if (Last == false)
538 return false;
539 }
540 }
541 else
542 {
543 /* Closes encoding is used when the server did not specify a size, the
544 loss of the connection means we are done */
545 if (Encoding == Closes)
546 In.Limit(-1);
547 else
548 In.Limit(Size - StartPos);
549
550 // Just transfer the whole block.
551 do
552 {
553 if (In.IsLimit() == false)
554 continue;
555
556 In.Limit(-1);
557 return !_error->PendingError();
558 }
559 while (Owner->Go(true,this) == true);
560 }
561
562 return Owner->Flush(this) && !_error->PendingError();
563 }
564 /*}}}*/
565 // ServerState::HeaderLine - Process a header line /*{{{*/
566 // ---------------------------------------------------------------------
567 /* */
568 bool ServerState::HeaderLine(string Line)
569 {
570 if (Line.empty() == true)
571 return true;
572
573 // The http server might be trying to do something evil.
574 if (Line.length() >= MAXLEN)
575 return _error->Error(_("Got a single header line over %u chars"),MAXLEN);
576
577 string::size_type Pos = Line.find(' ');
578 if (Pos == string::npos || Pos+1 > Line.length())
579 {
580 // Blah, some servers use "connection:closes", evil.
581 Pos = Line.find(':');
582 if (Pos == string::npos || Pos + 2 > Line.length())
583 return _error->Error(_("Bad header line"));
584 Pos++;
585 }
586
587 // Parse off any trailing spaces between the : and the next word.
588 string::size_type Pos2 = Pos;
589 while (Pos2 < Line.length() && isspace(Line[Pos2]) != 0)
590 Pos2++;
591
592 string Tag = string(Line,0,Pos);
593 string Val = string(Line,Pos2);
594
595 if (stringcasecmp(Tag.c_str(),Tag.c_str()+4,"HTTP") == 0)
596 {
597 // Evil servers return no version
598 if (Line[4] == '/')
599 {
600 if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor,
601 &Result,Code) != 4)
602 return _error->Error(_("The HTTP server sent an invalid reply header"));
603 }
604 else
605 {
606 Major = 0;
607 Minor = 9;
608 if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
609 return _error->Error(_("The HTTP server sent an invalid reply header"));
610 }
611
612 /* Check the HTTP response header to get the default persistance
613 state. */
614 if (Major < 1)
615 Persistent = false;
616 else
617 {
618 if (Major == 1 && Minor <= 0)
619 Persistent = false;
620 else
621 Persistent = true;
622 }
623
624 return true;
625 }
626
627 if (stringcasecmp(Tag,"Content-Length:") == 0)
628 {
629 if (Encoding == Closes)
630 Encoding = Stream;
631 HaveContent = true;
632
633 // The length is already set from the Content-Range header
634 if (StartPos != 0)
635 return true;
636
637 if (sscanf(Val.c_str(),"%lu",&Size) != 1)
638 return _error->Error(_("The HTTP server sent an invalid Content-Length header"));
639 return true;
640 }
641
642 if (stringcasecmp(Tag,"Content-Type:") == 0)
643 {
644 HaveContent = true;
645 return true;
646 }
647
648 if (stringcasecmp(Tag,"Content-Range:") == 0)
649 {
650 HaveContent = true;
651
652 if (sscanf(Val.c_str(),"bytes %lu-%*u/%lu",&StartPos,&Size) != 2)
653 return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
654 if ((unsigned)StartPos > Size)
655 return _error->Error(_("This HTTP server has broken range support"));
656 return true;
657 }
658
659 if (stringcasecmp(Tag,"Transfer-Encoding:") == 0)
660 {
661 HaveContent = true;
662 if (stringcasecmp(Val,"chunked") == 0)
663 Encoding = Chunked;
664 return true;
665 }
666
667 if (stringcasecmp(Tag,"Connection:") == 0)
668 {
669 if (stringcasecmp(Val,"close") == 0)
670 Persistent = false;
671 if (stringcasecmp(Val,"keep-alive") == 0)
672 Persistent = true;
673 return true;
674 }
675
676 if (stringcasecmp(Tag,"Last-Modified:") == 0)
677 {
678 if (StrToTime(Val,Date) == false)
679 return _error->Error(_("Unknown date format"));
680 return true;
681 }
682
683 return true;
684 }
685 /*}}}*/
686
687 // HttpMethod::SendReq - Send the HTTP request /*{{{*/
688 // ---------------------------------------------------------------------
689 /* This places the http request in the outbound buffer */
690 void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
691 {
692 URI Uri = Itm->Uri;
693
694 // The HTTP server expects a hostname with a trailing :port
695 char Buf[1000];
696 string ProperHost = Uri.Host;
697 if (Uri.Port != 0)
698 {
699 sprintf(Buf,":%u",Uri.Port);
700 ProperHost += Buf;
701 }
702
703 // Just in case.
704 if (Itm->Uri.length() >= sizeof(Buf))
705 abort();
706
707 /* Build the request. We include a keep-alive header only for non-proxy
708 requests. This is to tweak old http/1.0 servers that do support keep-alive
709 but not HTTP/1.1 automatic keep-alive. Doing this with a proxy server
710 will glitch HTTP/1.0 proxies because they do not filter it out and
711 pass it on, HTTP/1.1 says the connection should default to keep alive
712 and we expect the proxy to do this */
713 if (Proxy.empty() == true || Proxy.Host.empty())
714 sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
715 QuoteString(Uri.Path,"~").c_str(),ProperHost.c_str());
716 else
717 {
718 /* Generate a cache control header if necessary. We place a max
719 cache age on index files, optionally set a no-cache directive
720 and a no-store directive for archives. */
721 sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\n",
722 Itm->Uri.c_str(),ProperHost.c_str());
723 // only generate a cache control header if we actually want to
724 // use a cache
725 if (_config->FindB("Acquire::http::No-Cache",false) == false)
726 {
727 if (Itm->IndexFile == true)
728 sprintf(Buf+strlen(Buf),"Cache-Control: max-age=%u\r\n",
729 _config->FindI("Acquire::http::Max-Age",0));
730 else
731 {
732 if (_config->FindB("Acquire::http::No-Store",false) == true)
733 strcat(Buf,"Cache-Control: no-store\r\n");
734 }
735 }
736 }
737 // generate a no-cache header if needed
738 if (_config->FindB("Acquire::http::No-Cache",false) == true)
739 strcat(Buf,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
740
741
742 string Req = Buf;
743
744 // Check for a partial file
745 struct stat SBuf;
746 if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
747 {
748 // In this case we send an if-range query with a range header
749 sprintf(Buf,"Range: bytes=%li-\r\nIf-Range: %s\r\n",(long)SBuf.st_size - 1,
750 TimeRFC1123(SBuf.st_mtime).c_str());
751 Req += Buf;
752 }
753 else
754 {
755 if (Itm->LastModified != 0)
756 {
757 sprintf(Buf,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm->LastModified).c_str());
758 Req += Buf;
759 }
760 }
761
762 if (Proxy.User.empty() == false || Proxy.Password.empty() == false)
763 Req += string("Proxy-Authorization: Basic ") +
764 Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n";
765
766 if (Uri.User.empty() == false || Uri.Password.empty() == false)
767 Req += string("Authorization: Basic ") +
768 Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n";
769
770 Req += "User-Agent: Debian APT-HTTP/1.3\r\n\r\n";
771
772 if (Debug == true)
773 cerr << Req << endl;
774
775 Out.Read(Req);
776 }
777 /*}}}*/
778 // HttpMethod::Go - Run a single loop /*{{{*/
779 // ---------------------------------------------------------------------
780 /* This runs the select loop over the server FDs, Output file FDs and
781 stdin. */
782 bool HttpMethod::Go(bool ToFile,ServerState *Srv)
783 {
784 // Server has closed the connection
785 if (Srv->ServerFd == -1 && (Srv->In.WriteSpace() == false ||
786 ToFile == false))
787 return false;
788
789 fd_set rfds,wfds;
790 FD_ZERO(&rfds);
791 FD_ZERO(&wfds);
792
793 /* Add the server. We only send more requests if the connection will
794 be persisting */
795 if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1
796 && Srv->Persistent == true)
797 FD_SET(Srv->ServerFd,&wfds);
798 if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1)
799 FD_SET(Srv->ServerFd,&rfds);
800
801 // Add the file
802 int FileFD = -1;
803 if (File != 0)
804 FileFD = File->Fd();
805
806 if (Srv->In.WriteSpace() == true && ToFile == true && FileFD != -1)
807 FD_SET(FileFD,&wfds);
808
809 // Add stdin
810 FD_SET(STDIN_FILENO,&rfds);
811
812 // Figure out the max fd
813 int MaxFd = FileFD;
814 if (MaxFd < Srv->ServerFd)
815 MaxFd = Srv->ServerFd;
816
817 // Select
818 struct timeval tv;
819 tv.tv_sec = TimeOut;
820 tv.tv_usec = 0;
821 int Res = 0;
822 if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0)
823 {
824 if (errno == EINTR)
825 return true;
826 return _error->Errno("select",_("Select failed"));
827 }
828
829 if (Res == 0)
830 {
831 _error->Error(_("Connection timed out"));
832 return ServerDie(Srv);
833 }
834
835 // Handle server IO
836 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds))
837 {
838 errno = 0;
839 if (Srv->In.Read(Srv->ServerFd) == false)
840 return ServerDie(Srv);
841 }
842
843 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&wfds))
844 {
845 errno = 0;
846 if (Srv->Out.Write(Srv->ServerFd) == false)
847 return ServerDie(Srv);
848 }
849
850 // Send data to the file
851 if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
852 {
853 if (Srv->In.Write(FileFD) == false)
854 return _error->Errno("write",_("Error writing to output file"));
855 }
856
857 // Handle commands from APT
858 if (FD_ISSET(STDIN_FILENO,&rfds))
859 {
860 if (Run(true) != -1)
861 exit(100);
862 }
863
864 return true;
865 }
866 /*}}}*/
867 // HttpMethod::Flush - Dump the buffer into the file /*{{{*/
868 // ---------------------------------------------------------------------
869 /* This takes the current input buffer from the Server FD and writes it
870 into the file */
871 bool HttpMethod::Flush(ServerState *Srv)
872 {
873 if (File != 0)
874 {
875 // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking
876 // can't be set
877 if (File->Name() != "/dev/null")
878 SetNonBlock(File->Fd(),false);
879 if (Srv->In.WriteSpace() == false)
880 return true;
881
882 while (Srv->In.WriteSpace() == true)
883 {
884 if (Srv->In.Write(File->Fd()) == false)
885 return _error->Errno("write",_("Error writing to file"));
886 if (Srv->In.IsLimit() == true)
887 return true;
888 }
889
890 if (Srv->In.IsLimit() == true || Srv->Encoding == ServerState::Closes)
891 return true;
892 }
893 return false;
894 }
895 /*}}}*/
896 // HttpMethod::ServerDie - The server has closed the connection. /*{{{*/
897 // ---------------------------------------------------------------------
898 /* */
899 bool HttpMethod::ServerDie(ServerState *Srv)
900 {
901 unsigned int LErrno = errno;
902
903 // Dump the buffer to the file
904 if (Srv->State == ServerState::Data)
905 {
906 // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking
907 // can't be set
908 if (File->Name() != "/dev/null")
909 SetNonBlock(File->Fd(),false);
910 while (Srv->In.WriteSpace() == true)
911 {
912 if (Srv->In.Write(File->Fd()) == false)
913 return _error->Errno("write",_("Error writing to the file"));
914
915 // Done
916 if (Srv->In.IsLimit() == true)
917 return true;
918 }
919 }
920
921 // See if this is because the server finished the data stream
922 if (Srv->In.IsLimit() == false && Srv->State != ServerState::Header &&
923 Srv->Encoding != ServerState::Closes)
924 {
925 Srv->Close();
926 if (LErrno == 0)
927 return _error->Error(_("Error reading from server. Remote end closed connection"));
928 errno = LErrno;
929 return _error->Errno("read",_("Error reading from server"));
930 }
931 else
932 {
933 Srv->In.Limit(-1);
934
935 // Nothing left in the buffer
936 if (Srv->In.WriteSpace() == false)
937 return false;
938
939 // We may have got multiple responses back in one packet..
940 Srv->Close();
941 return true;
942 }
943
944 return false;
945 }
946 /*}}}*/
947 // HttpMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
948 // ---------------------------------------------------------------------
949 /* We look at the header data we got back from the server and decide what
950 to do. Returns
951 0 - File is open,
952 1 - IMS hit
953 3 - Unrecoverable error
954 4 - Error with error content page
955 5 - Unrecoverable non-server error (close the connection) */
956 int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
957 {
958 // Not Modified
959 if (Srv->Result == 304)
960 {
961 unlink(Queue->DestFile.c_str());
962 Res.IMSHit = true;
963 Res.LastModified = Queue->LastModified;
964 return 1;
965 }
966
967 /* We have a reply we dont handle. This should indicate a perm server
968 failure */
969 if (Srv->Result < 200 || Srv->Result >= 300)
970 {
971 _error->Error("%u %s",Srv->Result,Srv->Code);
972 if (Srv->HaveContent == true)
973 return 4;
974 return 3;
975 }
976
977 // This is some sort of 2xx 'data follows' reply
978 Res.LastModified = Srv->Date;
979 Res.Size = Srv->Size;
980
981 // Open the file
982 delete File;
983 File = new FileFd(Queue->DestFile,FileFd::WriteAny);
984 if (_error->PendingError() == true)
985 return 5;
986
987 FailFile = Queue->DestFile;
988 FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
989 FailFd = File->Fd();
990 FailTime = Srv->Date;
991
992 // Set the expected size
993 if (Srv->StartPos >= 0)
994 {
995 Res.ResumePoint = Srv->StartPos;
996 ftruncate(File->Fd(),Srv->StartPos);
997 }
998
999 // Set the start point
1000 lseek(File->Fd(),0,SEEK_END);
1001
1002 delete Srv->In.Hash;
1003 Srv->In.Hash = new Hashes;
1004
1005 // Fill the Hash if the file is non-empty (resume)
1006 if (Srv->StartPos > 0)
1007 {
1008 lseek(File->Fd(),0,SEEK_SET);
1009 if (Srv->In.Hash->AddFD(File->Fd(),Srv->StartPos) == false)
1010 {
1011 _error->Errno("read",_("Problem hashing file"));
1012 return 5;
1013 }
1014 lseek(File->Fd(),0,SEEK_END);
1015 }
1016
1017 SetNonBlock(File->Fd(),true);
1018 return 0;
1019 }
1020 /*}}}*/
1021 // HttpMethod::SigTerm - Handle a fatal signal /*{{{*/
1022 // ---------------------------------------------------------------------
1023 /* This closes and timestamps the open file. This is neccessary to get
1024 resume behavoir on user abort */
1025 void HttpMethod::SigTerm(int)
1026 {
1027 if (FailFd == -1)
1028 _exit(100);
1029 close(FailFd);
1030
1031 // Timestamp
1032 struct utimbuf UBuf;
1033 UBuf.actime = FailTime;
1034 UBuf.modtime = FailTime;
1035 utime(FailFile.c_str(),&UBuf);
1036
1037 _exit(100);
1038 }
1039 /*}}}*/
1040 // HttpMethod::Fetch - Fetch an item /*{{{*/
1041 // ---------------------------------------------------------------------
1042 /* This adds an item to the pipeline. We keep the pipeline at a fixed
1043 depth. */
1044 bool HttpMethod::Fetch(FetchItem *)
1045 {
1046 if (Server == 0)
1047 return true;
1048
1049 // Queue the requests
1050 int Depth = -1;
1051 bool Tail = false;
1052 for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth;
1053 I = I->Next, Depth++)
1054 {
1055 // If pipelining is disabled, we only queue 1 request
1056 if (Server->Pipeline == false && Depth >= 0)
1057 break;
1058
1059 // Make sure we stick with the same server
1060 if (Server->Comp(I->Uri) == false)
1061 break;
1062 if (QueueBack == I)
1063 Tail = true;
1064 if (Tail == true)
1065 {
1066 QueueBack = I->Next;
1067 SendReq(I,Server->Out);
1068 continue;
1069 }
1070 }
1071
1072 return true;
1073 };
1074 /*}}}*/
1075 // HttpMethod::Configuration - Handle a configuration message /*{{{*/
1076 // ---------------------------------------------------------------------
1077 /* We stash the desired pipeline depth */
1078 bool HttpMethod::Configuration(string Message)
1079 {
1080 if (pkgAcqMethod::Configuration(Message) == false)
1081 return false;
1082
1083 TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut);
1084 PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth",
1085 PipelineDepth);
1086 Debug = _config->FindB("Debug::Acquire::http",false);
1087
1088 return true;
1089 }
1090 /*}}}*/
1091 // HttpMethod::Loop - Main loop /*{{{*/
1092 // ---------------------------------------------------------------------
1093 /* */
1094 int HttpMethod::Loop()
1095 {
1096 signal(SIGTERM,SigTerm);
1097 signal(SIGINT,SigTerm);
1098
1099 Server = 0;
1100
1101 int FailCounter = 0;
1102 while (1)
1103 {
1104 // We have no commands, wait for some to arrive
1105 if (Queue == 0)
1106 {
1107 if (WaitFd(STDIN_FILENO) == false)
1108 return 0;
1109 }
1110
1111 /* Run messages, we can accept 0 (no message) if we didn't
1112 do a WaitFd above.. Otherwise the FD is closed. */
1113 int Result = Run(true);
1114 if (Result != -1 && (Result != 0 || Queue == 0))
1115 return 100;
1116
1117 if (Queue == 0)
1118 continue;
1119
1120 CFStringEncoding se = kCFStringEncodingUTF8;
1121
1122 char *url = strdup(Queue->Uri.c_str());
1123 url:
1124 URI uri = std::string(url);
1125 std::string hs = uri.Host;
1126
1127 struct hostent *he = gethostbyname(hs.c_str());
1128 if (he == NULL || he->h_addr_list[0] == NULL) {
1129 _error->Error(hstrerror(h_errno));
1130 Fail(true);
1131 free(url);
1132 }
1133
1134 uri.Host = inet_ntoa(* (struct in_addr *) he->h_addr_list[0]);
1135
1136 std::string urs = uri;
1137
1138 CFStringRef sr = CFStringCreateWithCString(kCFAllocatorDefault, urs.c_str(), se);
1139 CFURLRef ur = CFURLCreateWithString(kCFAllocatorDefault, sr, NULL);
1140 CFRelease(sr);
1141 CFHTTPMessageRef hm = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), ur, kCFHTTPVersion1_1);
1142 CFRelease(ur);
1143
1144 struct stat SBuf;
1145 if (stat(Queue->DestFile.c_str(), &SBuf) >= 0 && SBuf.st_size > 0) {
1146 sr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%li-"), (long) SBuf.st_size - 1);
1147 CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Range"), sr);
1148 CFRelease(sr);
1149
1150 sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(SBuf.st_mtime).c_str(), se);
1151 CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Range"), sr);
1152 CFRelease(sr);
1153 } else if (Queue->LastModified != 0) {
1154 sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(SBuf.st_mtime).c_str(), se);
1155 CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Modified-Since"), sr);
1156 CFRelease(sr);
1157 }
1158
1159 sr = CFStringCreateWithCString(kCFAllocatorDefault, Machine_, se);
1160 CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Machine"), sr);
1161 CFRelease(sr);
1162
1163 sr = CFStringCreateWithCString(kCFAllocatorDefault, SerialNumber_, se);
1164 CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Serial-Number"), sr);
1165 CFRelease(sr);
1166
1167 CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("User-Agent"), CFSTR("Telesphoreo APT-HTTP/1.0.98"));
1168
1169 sr = CFStringCreateWithCString(kCFAllocatorDefault, hs.c_str(), se);
1170 CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Host"), sr);
1171 CFRelease(sr);
1172
1173 CFReadStreamRef rs = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, hm);
1174 CFRelease(hm);
1175
1176 CFDictionaryRef dr = SCDynamicStoreCopyProxies(NULL);
1177 CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPProxy, dr);
1178 CFRelease(dr);
1179
1180 //CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue);
1181 CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
1182
1183 FetchResult Res;
1184 CFIndex rd;
1185 UInt32 sc;
1186
1187 uint8_t data[10240];
1188 size_t offset = 0;
1189
1190 Status("Connecting to %s", hs.c_str());
1191
1192 if (!CFReadStreamOpen(rs)) {
1193 CfrsError("Open", rs);
1194 Fail(true);
1195 goto done;
1196 }
1197
1198 rd = CFReadStreamRead(rs, data, sizeof(data));
1199
1200 if (rd == -1) {
1201 CfrsError(uri.Host.c_str(), rs);
1202 Fail(true);
1203 goto done;
1204 }
1205
1206 Res.Filename = Queue->DestFile;
1207
1208 hm = (CFHTTPMessageRef) CFReadStreamCopyProperty(rs, kCFStreamPropertyHTTPResponseHeader);
1209 sc = CFHTTPMessageGetResponseStatusCode(hm);
1210
1211 if (sc == 301 || sc == 302) {
1212 sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Location"));
1213 if (sr == NULL) {
1214 Fail();
1215 goto done_;
1216 } else {
1217 size_t ln = CFStringGetLength(sr) + 1;
1218 free(url);
1219 url = static_cast<char *>(malloc(ln));
1220
1221 if (!CFStringGetCString(sr, url, ln, se)) {
1222 Fail();
1223 goto done_;
1224 }
1225
1226 CFRelease(sr);
1227 goto url;
1228 }
1229 }
1230
1231 sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Range"));
1232 if (sr != NULL) {
1233 size_t ln = CFStringGetLength(sr) + 1;
1234 char cr[ln];
1235
1236 if (!CFStringGetCString(sr, cr, ln, se)) {
1237 Fail();
1238 goto done_;
1239 }
1240
1241 CFRelease(sr);
1242
1243 if (sscanf(cr, "bytes %lu-%*u/%lu", &offset, &Res.Size) != 2) {
1244 _error->Error(_("The HTTP server sent an invalid Content-Range header"));
1245 Fail();
1246 goto done_;
1247 }
1248
1249 if (offset > Res.Size) {
1250 _error->Error(_("This HTTP server has broken range support"));
1251 Fail();
1252 goto done_;
1253 }
1254 } else {
1255 sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Length"));
1256 if (sr != NULL) {
1257 Res.Size = CFStringGetIntValue(sr);
1258 CFRelease(sr);
1259 }
1260 }
1261
1262 time(&Res.LastModified);
1263
1264 sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Last-Modified"));
1265 if (sr != NULL) {
1266 size_t ln = CFStringGetLength(sr) + 1;
1267 char cr[ln];
1268
1269 if (!CFStringGetCString(sr, cr, ln, se)) {
1270 Fail();
1271 goto done_;
1272 }
1273
1274 CFRelease(sr);
1275
1276 if (!StrToTime(cr, Res.LastModified)) {
1277 _error->Error(_("Unknown date format"));
1278 Fail();
1279 goto done_;
1280 }
1281 }
1282
1283 CFRelease(hm);
1284
1285 if (sc == 304) {
1286 unlink(Queue->DestFile.c_str());
1287 Res.IMSHit = true;
1288 Res.LastModified = Queue->LastModified;
1289 URIDone(Res);
1290 } else if (sc < 200 || sc >= 300)
1291 Fail();
1292 else {
1293 Hashes hash;
1294
1295 File = new FileFd(Queue->DestFile, FileFd::WriteAny);
1296 if (_error->PendingError() == true) {
1297 delete File;
1298 File = NULL;
1299 Fail();
1300 goto done;
1301 }
1302
1303 FailFile = Queue->DestFile;
1304 FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
1305 FailFd = File->Fd();
1306 FailTime = Res.LastModified;
1307
1308 Res.ResumePoint = offset;
1309 ftruncate(File->Fd(), offset);
1310
1311 if (offset != 0) {
1312 lseek(File->Fd(), 0, SEEK_SET);
1313 if (!hash.AddFD(File->Fd(), offset)) {
1314 _error->Errno("read", _("Problem hashing file"));
1315 delete File;
1316 File = NULL;
1317 Fail();
1318 goto done;
1319 }
1320 }
1321
1322 lseek(File->Fd(), 0, SEEK_END);
1323
1324 URIStart(Res);
1325
1326 read: if (rd == -1) {
1327 CfrsError("rd", rs);
1328 Fail(true);
1329 } else if (rd == 0) {
1330 if (Res.Size == 0)
1331 Res.Size = File->Size();
1332
1333 struct utimbuf UBuf;
1334 time(&UBuf.actime);
1335 UBuf.actime = Res.LastModified;
1336 UBuf.modtime = Res.LastModified;
1337 utime(Queue->DestFile.c_str(), &UBuf);
1338
1339 Res.TakeHashes(hash);
1340 URIDone(Res);
1341 } else {
1342 hash.Add(data, rd);
1343
1344 uint8_t *dt = data;
1345 while (rd != 0) {
1346 int sz = write(File->Fd(), dt, rd);
1347
1348 if (sz == -1) {
1349 delete File;
1350 File = NULL;
1351 Fail();
1352 goto done;
1353 }
1354
1355 dt += sz;
1356 rd -= sz;
1357 }
1358
1359 rd = CFReadStreamRead(rs, data, sizeof(data));
1360 goto read;
1361 }
1362 }
1363
1364 goto done;
1365 done_:
1366 CFRelease(hm);
1367 done:
1368 CFReadStreamClose(rs);
1369 CFRelease(rs);
1370 free(url);
1371
1372 FailCounter = 0;
1373 }
1374
1375 return 0;
1376 }
1377 /*}}}*/
1378
1379 int main()
1380 {
1381 #if !defined(__ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__) || __ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__ < 10200
1382 struct nlist nl[2];
1383 memset(nl, 0, sizeof(nl));
1384 nl[0].n_un.n_name = (char *) "_useMDNSResponder";
1385 nlist("/usr/lib/libc.dylib", nl);
1386 if (nl[0].n_type != N_UNDF)
1387 *(int *) nl[0].n_value = 0;
1388 #endif
1389
1390 setlocale(LC_ALL, "");
1391
1392 HttpMethod Mth;
1393
1394 size_t size;
1395 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
1396 char *machine = new char[size];
1397 sysctlbyname("hw.machine", machine, &size, NULL, 0);
1398 Machine_ = machine;
1399
1400 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
1401 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
1402 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
1403 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
1404 CFRelease(serial);
1405 }
1406
1407 IOObjectRelease(service);
1408 }
1409
1410 return Mth.Loop();
1411 }
1412
1413