New http method
[ntk/apt.git] / methods / http.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: http.cc,v 1.1 1998/11/01 05:30:47 jgg Exp $
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>
39 #include <stdio.h>
40
41 // Internet stuff
42 #include <netinet/in.h>
43 #include <sys/socket.h>
44 #include <arpa/inet.h>
45 #include <netdb.h>
46
47 #include "http.h"
48 /*}}}*/
49
50 // CircleBuf::CircleBuf - Circular input buffer /*{{{*/
51 // ---------------------------------------------------------------------
52 /* */
53 CircleBuf::CircleBuf(unsigned long Size) : Size(Size), MD5(0)
54 {
55 Buf = new unsigned char[Size];
56 Reset();
57 }
58 /*}}}*/
59 // CircleBuf::Reset - Reset to the default state /*{{{*/
60 // ---------------------------------------------------------------------
61 /* */
62 void CircleBuf::Reset()
63 {
64 InP = 0;
65 OutP = 0;
66 StrPos = 0;
67 MaxGet = (unsigned int)-1;
68 OutQueue = string();
69 if (MD5 != 0)
70 {
71 delete MD5;
72 MD5 = new MD5Summation;
73 }
74 };
75 /*}}}*/
76 // CircleBuf::Read - Read from a FD into the circular buffer /*{{{*/
77 // ---------------------------------------------------------------------
78 /* This fills up the buffer with as much data as is in the FD, assuming it
79 is non-blocking.. */
80 bool CircleBuf::Read(int Fd)
81 {
82 while (1)
83 {
84 // Woops, buffer is full
85 if (InP - OutP == Size)
86 return true;
87
88 // Write the buffer segment
89 int Res;
90 Res = read(Fd,Buf + (InP%Size),LeftRead());
91
92 if (Res == 0)
93 return false;
94 if (Res < 0)
95 {
96 if (errno == EAGAIN)
97 return true;
98 return false;
99 }
100
101 if (InP == 0)
102 gettimeofday(&Start,0);
103 InP += Res;
104 }
105 }
106 /*}}}*/
107 // CircleBuf::Read - Put the string into the buffer /*{{{*/
108 // ---------------------------------------------------------------------
109 /* This will hold the string in and fill the buffer with it as it empties */
110 bool CircleBuf::Read(string Data)
111 {
112 OutQueue += Data;
113 FillOut();
114 return true;
115 }
116 /*}}}*/
117 // CircleBuf::FillOut - Fill the buffer from the output queue /*{{{*/
118 // ---------------------------------------------------------------------
119 /* */
120 void CircleBuf::FillOut()
121 {
122 if (OutQueue.empty() == true)
123 return;
124 while (1)
125 {
126 // Woops, buffer is full
127 if (InP - OutP == Size)
128 return;
129
130 // Write the buffer segment
131 unsigned long Sz = LeftRead();
132 if (OutQueue.length() - StrPos < Sz)
133 Sz = OutQueue.length() - StrPos;
134 memcpy(Buf + (InP%Size),OutQueue.begin() + StrPos,Sz);
135
136 // Advance
137 StrPos += Sz;
138 InP += Sz;
139 if (OutQueue.length() == StrPos)
140 {
141 StrPos = 0;
142 OutQueue = "";
143 return;
144 }
145 }
146 }
147 /*}}}*/
148 // CircleBuf::Write - Write from the buffer into a FD /*{{{*/
149 // ---------------------------------------------------------------------
150 /* This empties the buffer into the FD. */
151 bool CircleBuf::Write(int Fd)
152 {
153 while (1)
154 {
155 FillOut();
156
157 // Woops, buffer is empty
158 if (OutP == InP)
159 return true;
160
161 if (OutP == MaxGet)
162 return true;
163
164 // Write the buffer segment
165 int Res;
166 Res = write(Fd,Buf + (OutP%Size),LeftWrite());
167
168 if (Res == 0)
169 return false;
170 if (Res < 0)
171 {
172 if (errno == EAGAIN)
173 return true;
174
175 return false;
176 }
177
178 if (MD5 != 0)
179 MD5->Add(Buf + (OutP%Size),Res);
180
181 OutP += Res;
182 }
183 }
184 /*}}}*/
185 // CircleBuf::WriteTillEl - Write from the buffer to a string /*{{{*/
186 // ---------------------------------------------------------------------
187 /* This copies till the first empty line */
188 bool CircleBuf::WriteTillEl(string &Data,bool Single)
189 {
190 // We cheat and assume it is unneeded to have more than one buffer load
191 for (unsigned long I = OutP; I < InP; I++)
192 {
193 if (Buf[I%Size] != '\n')
194 continue;
195 for (I++; I < InP && Buf[I%Size] == '\r'; I++);
196
197 if (Single == false)
198 {
199 if (Buf[I%Size] != '\n')
200 continue;
201 for (I++; I < InP && Buf[I%Size] == '\r'; I++);
202 }
203
204 if (I > InP)
205 I = InP;
206
207 Data = "";
208 while (OutP < I)
209 {
210 unsigned long Sz = LeftWrite();
211 if (Sz == 0)
212 return false;
213 if (I - OutP < LeftWrite())
214 Sz = I - OutP;
215 Data += string((char *)(Buf + (OutP%Size)),Sz);
216 OutP += Sz;
217 }
218 return true;
219 }
220 return false;
221 }
222 /*}}}*/
223 // CircleBuf::Stats - Print out stats information /*{{{*/
224 // ---------------------------------------------------------------------
225 /* */
226 void CircleBuf::Stats()
227 {
228 if (InP == 0)
229 return;
230
231 struct timeval Stop;
232 gettimeofday(&Stop,0);
233 /* float Diff = Stop.tv_sec - Start.tv_sec +
234 (float)(Stop.tv_usec - Start.tv_usec)/1000000;
235 clog << "Got " << InP << " in " << Diff << " at " << InP/Diff << endl;*/
236 }
237 /*}}}*/
238
239 // ServerState::ServerState - Constructor /*{{{*/
240 // ---------------------------------------------------------------------
241 /* */
242 ServerState::ServerState(URI Srv,HttpMethod *Owner) : Owner(Owner),
243 In(64*1024), Out(1*1024),
244 ServerName(Srv)
245 {
246 Reset();
247 }
248 /*}}}*/
249 // ServerState::Open - Open a connection to the server /*{{{*/
250 // ---------------------------------------------------------------------
251 /* This opens a connection to the server. */
252 string LastHost;
253 in_addr LastHostA;
254 bool ServerState::Open()
255 {
256 Close();
257
258 int Port;
259 string Host;
260
261 if (Proxy.empty() == false)
262 {
263 Port = ServerName.Port;
264 Host = ServerName.Host;
265 }
266 else
267 {
268 Port = Proxy.Port;
269 Host = Proxy.Host;
270 }
271
272 if (LastHost != Host)
273 {
274 Owner->Status("Connecting to %s",Host.c_str());
275
276 // Lookup the host
277 hostent *Addr = gethostbyname(Host.c_str());
278 if (Addr == 0)
279 return _error->Errno("gethostbyname","Could not lookup host %s",Host.c_str());
280 LastHost = Host;
281 LastHostA = *(in_addr *)(Addr->h_addr_list[0]);
282 }
283
284 Owner->Status("Connecting to %s (%s)",Host.c_str(),inet_ntoa(LastHostA));
285
286 // Get a socket
287 if ((ServerFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
288 return _error->Errno("socket","Could not create a socket");
289
290 // Connect to the server
291 struct sockaddr_in server;
292 server.sin_family = AF_INET;
293 server.sin_port = htons(Port);
294 server.sin_addr = LastHostA;
295 if (connect(ServerFd,(sockaddr *)&server,sizeof(server)) < 0)
296 return _error->Errno("socket","Could not create a socket");
297
298 SetNonBlock(ServerFd,true);
299 return true;
300 }
301 /*}}}*/
302 // ServerState::Close - Close a connection to the server /*{{{*/
303 // ---------------------------------------------------------------------
304 /* */
305 bool ServerState::Close()
306 {
307 close(ServerFd);
308 ServerFd = -1;
309 In.Reset();
310 Out.Reset();
311 return true;
312 }
313 /*}}}*/
314 // ServerState::RunHeaders - Get the headers before the data /*{{{*/
315 // ---------------------------------------------------------------------
316 /* */
317 bool ServerState::RunHeaders()
318 {
319 State = Header;
320
321 Owner->Status("Waiting for file");
322
323 Major = 0;
324 Minor = 0;
325 Result = 0;
326 Size = 0;
327 StartPos = 0;
328 Encoding = Closes;
329 time(&Date);
330
331 do
332 {
333 string Data;
334 if (In.WriteTillEl(Data) == false)
335 continue;
336
337 for (string::const_iterator I = Data.begin(); I < Data.end(); I++)
338 {
339 string::const_iterator J = I;
340 for (; J != Data.end() && *J != '\n' && *J != '\r';J++);
341 if (HeaderLine(string(I,J-I)) == false)
342 return false;
343 I = J;
344 }
345 return true;
346 }
347 while (Owner->Go(false,this) == true);
348
349 return false;
350 }
351 /*}}}*/
352 // ServerState::RunData - Transfer the data from the socket /*{{{*/
353 // ---------------------------------------------------------------------
354 /* */
355 bool ServerState::RunData()
356 {
357 State = Data;
358
359 // Chunked transfer encoding is fun..
360 if (Encoding == Chunked)
361 {
362 while (1)
363 {
364 // Grab the block size
365 bool Last = true;
366 string Data;
367 In.Limit(-1);
368 do
369 {
370 if (In.WriteTillEl(Data,true) == true)
371 break;
372 }
373 while ((Last = Owner->Go(false,this)) == true);
374
375 if (Last == false)
376 return false;
377
378 // See if we are done
379 unsigned long Len = strtol(Data.c_str(),0,16);
380 if (Len == 0)
381 {
382 In.Limit(-1);
383
384 // We have to remove the entity trailer
385 Last = true;
386 do
387 {
388 if (In.WriteTillEl(Data,true) == true && Data.length() <= 2)
389 break;
390 }
391 while ((Last = Owner->Go(false,this)) == true);
392 if (Last == false)
393 return false;
394 return true;
395 }
396
397 // Transfer the block
398 In.Limit(Len);
399 while (Owner->Go(true,this) == true)
400 if (In.IsLimit() == true)
401 break;
402
403 // Error
404 if (In.IsLimit() == false)
405 return false;
406
407 // The server sends an extra new line before the next block specifier..
408 In.Limit(-1);
409 Last = true;
410 do
411 {
412 if (In.WriteTillEl(Data,true) == true)
413 break;
414 }
415 while ((Last = Owner->Go(false,this)) == true);
416 if (Last == false)
417 return false;
418 }
419 }
420 else
421 {
422 /* Closes encoding is used when the server did not specify a size, the
423 loss of the connection means we are done */
424 if (Encoding == Closes)
425 In.Limit(-1);
426 else
427 In.Limit(Size - StartPos);
428
429 // Just transfer the whole block.
430 do
431 {
432 if (In.IsLimit() == false)
433 continue;
434
435 In.Limit(-1);
436 return true;
437 }
438 while (Owner->Go(true,this) == true);
439 }
440
441 return Owner->Flush(this);
442 }
443 /*}}}*/
444 // ServerState::HeaderLine - Process a header line /*{{{*/
445 // ---------------------------------------------------------------------
446 /* */
447 bool ServerState::HeaderLine(string Line)
448 {
449 if (Line.empty() == true)
450 return true;
451
452 // The http server might be trying to do something evil.
453 if (Line.length() >= MAXLEN)
454 return _error->Error("Got a single header line over %u chars",MAXLEN);
455
456 string::size_type Pos = Line.find(' ');
457 if (Pos == string::npos || Pos+1 > Line.length())
458 return _error->Error("Bad header line");
459
460 string Tag = string(Line,0,Pos);
461 string Val = string(Line,Pos+1);
462
463 if (stringcasecmp(Tag,"HTTP") == 0)
464 {
465 // Evil servers return no version
466 if (Line[4] == '/')
467 {
468 if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor,
469 &Result,Code) != 4)
470 return _error->Error("The http server sent an invalid reply header");
471 }
472 else
473 {
474 Major = 0;
475 Minor = 9;
476 if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
477 return _error->Error("The http server sent an invalid reply header");
478 }
479
480 return true;
481 }
482
483 if (stringcasecmp(Tag,"Content-Length:"))
484 {
485 if (Encoding == Closes)
486 Encoding = Stream;
487
488 // The length is already set from the Content-Range header
489 if (StartPos != 0)
490 return true;
491
492 if (sscanf(Val.c_str(),"%lu",&Size) != 1)
493 return _error->Error("The http server sent an invalid Content-Length header");
494 return true;
495 }
496
497 if (stringcasecmp(Tag,"Content-Range:"))
498 {
499 if (sscanf(Val.c_str(),"bytes %lu-%*u/%lu",&StartPos,&Size) != 2)
500 return _error->Error("The http server sent an invalid Content-Range header");
501 if ((unsigned)StartPos > Size)
502 return _error->Error("This http server has broken range support");
503 return true;
504 }
505
506 if (stringcasecmp(Tag,"Transfer-Encoding:"))
507 {
508 if (stringcasecmp(Val,"chunked"))
509 Encoding = Chunked;
510 return true;
511 }
512
513 if (stringcasecmp(Tag,"Last-Modified:"))
514 {
515 if (StrToTime(Val,Date) == false)
516 return _error->Error("Unknown date format");
517 return true;
518 }
519
520 return true;
521 }
522 /*}}}*/
523
524 // HttpMethod::SendReq - Send the HTTP request /*{{{*/
525 // ---------------------------------------------------------------------
526 /* This places the http request in the outbound buffer */
527 void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
528 {
529 URI Uri = Itm->Uri;
530
531 // The HTTP server expects a hostname with a trailing :port
532 char Buf[300];
533 string ProperHost = Uri.Host;
534 if (Uri.Port != 0)
535 {
536 sprintf(Buf,":%u",Uri.Port);
537 ProperHost += Buf;
538 }
539
540 // Build the request
541 if (Proxy.empty() == true)
542 sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
543 Uri.Path.c_str(),ProperHost.c_str());
544 else
545 sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\n",
546 Itm->Uri.c_str(),ProperHost.c_str());
547 string Req = Buf;
548
549 // Check for a partial file
550 struct stat SBuf;
551 if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
552 {
553 // In this case we send an if-range query with a range header
554 sprintf(Buf,"Range: bytes=%li-\r\nIf-Range: %s\r\n",SBuf.st_size - 1,
555 TimeRFC1123(SBuf.st_mtime).c_str());
556 Req += Buf;
557 }
558 else
559 {
560 if (Itm->LastModified != 0)
561 {
562 sprintf(Buf,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm->LastModified).c_str());
563 Req += Buf;
564 }
565 }
566
567 /* if (ProxyAuth.empty() == false)
568 Req += string("Proxy-Authorization: Basic ") + Base64Encode(ProxyAuth) + "\r\n";*/
569
570 Req += "User-Agent: Debian APT-HTTP/1.2\r\n\r\n";
571 Out.Read(Req);
572 }
573 /*}}}*/
574 // HttpMethod::Go - Run a single loop /*{{{*/
575 // ---------------------------------------------------------------------
576 /* This runs the select loop over the server FDs, Output file FDs and
577 stdin. */
578 bool HttpMethod::Go(bool ToFile,ServerState *Srv)
579 {
580 // Server has closed the connection
581 if (Srv->ServerFd == -1 && Srv->In.WriteSpace() == false)
582 return false;
583
584 fd_set rfds,wfds,efds;
585 FD_ZERO(&rfds);
586 FD_ZERO(&wfds);
587 FD_ZERO(&efds);
588
589 // Add the server
590 if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1)
591 FD_SET(Srv->ServerFd,&wfds);
592 if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1)
593 FD_SET(Srv->ServerFd,&rfds);
594
595 // Add the file
596 int FileFD = -1;
597 if (File != 0)
598 FileFD = File->Fd();
599
600 if (Srv->In.WriteSpace() == true && ToFile == true && FileFD != -1)
601 FD_SET(FileFD,&wfds);
602
603 // Add stdin
604 FD_SET(STDIN_FILENO,&rfds);
605
606 // Error Set
607 if (FileFD != -1)
608 FD_SET(FileFD,&efds);
609 if (Srv->ServerFd != -1)
610 FD_SET(Srv->ServerFd,&efds);
611
612 // Figure out the max fd
613 int MaxFd = FileFD;
614 if (MaxFd < Srv->ServerFd)
615 MaxFd = Srv->ServerFd;
616
617 // Select
618 struct timeval tv;
619 tv.tv_sec = 120;
620 tv.tv_usec = 0;
621 int Res = 0;
622 if ((Res = select(MaxFd+1,&rfds,&wfds,&efds,&tv)) < 0)
623 return _error->Errno("select","Select failed");
624
625 if (Res == 0)
626 {
627 _error->Error("Connection timed out");
628 return ServerDie(Srv);
629 }
630
631 // Some kind of exception (error) on the sockets, die
632 if ((FileFD != -1 && FD_ISSET(FileFD,&efds)) ||
633 (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&efds)))
634 return _error->Error("Socket Exception");
635
636 // Handle server IO
637 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds))
638 {
639 errno = 0;
640 if (Srv->In.Read(Srv->ServerFd) == false)
641 return ServerDie(Srv);
642 }
643
644 if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&wfds))
645 {
646 errno = 0;
647 if (Srv->Out.Write(Srv->ServerFd) == false)
648 return ServerDie(Srv);
649 }
650
651 // Send data to the file
652 if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
653 {
654 if (Srv->In.Write(FileFD) == false)
655 return _error->Errno("write","Error writing to output file");
656 }
657
658 // Handle commands from APT
659 if (FD_ISSET(STDIN_FILENO,&rfds))
660 {
661 if (Run(true) != 0)
662 exit(100);
663 }
664
665 return true;
666 }
667 /*}}}*/
668 // HttpMethod::Flush - Dump the buffer into the file /*{{{*/
669 // ---------------------------------------------------------------------
670 /* This takes the current input buffer from the Server FD and writes it
671 into the file */
672 bool HttpMethod::Flush(ServerState *Srv)
673 {
674 if (File != 0)
675 {
676 SetNonBlock(File->Fd(),false);
677 if (Srv->In.WriteSpace() == false)
678 return true;
679
680 while (Srv->In.WriteSpace() == true)
681 {
682 if (Srv->In.Write(File->Fd()) == false)
683 return _error->Errno("write","Error writing to file");
684 }
685
686 if (Srv->In.IsLimit() == true || Srv->Encoding == ServerState::Closes)
687 return true;
688 }
689 return false;
690 }
691 /*}}}*/
692 // HttpMethod::ServerDie - The server has closed the connection. /*{{{*/
693 // ---------------------------------------------------------------------
694 /* */
695 bool HttpMethod::ServerDie(ServerState *Srv)
696 {
697 // Dump the buffer to the file
698 if (Srv->State == ServerState::Data)
699 {
700 SetNonBlock(File->Fd(),false);
701 while (Srv->In.WriteSpace() == true)
702 {
703 if (Srv->In.Write(File->Fd()) == false)
704 return _error->Errno("write","Error writing to the file");
705 }
706 }
707
708 // See if this is because the server finished the data stream
709 if (Srv->In.IsLimit() == false && Srv->State != ServerState::Header &&
710 Srv->Encoding != ServerState::Closes)
711 {
712 if (errno == 0)
713 return _error->Error("Error reading from server Remote end closed connection");
714 return _error->Errno("read","Error reading from server");
715 }
716 else
717 {
718 Srv->In.Limit(-1);
719
720 // Nothing left in the buffer
721 if (Srv->In.WriteSpace() == false)
722 return false;
723
724 // We may have got multiple responses back in one packet..
725 Srv->Close();
726 return true;
727 }
728
729 return false;
730 }
731 /*}}}*/
732 // HttpMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
733 // ---------------------------------------------------------------------
734 /* We look at the header data we got back from the server and decide what
735 to do. Returns
736 0 - File is open,
737 1 - IMS hit
738 3 - Unrecoverable error */
739 int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
740 {
741 // Not Modified
742 if (Srv->Result == 304)
743 {
744 unlink(Queue->DestFile.c_str());
745 Res.IMSHit = true;
746 Res.LastModified = Queue->LastModified;
747 return 1;
748 }
749
750 /* We have a reply we dont handle. This should indicate a perm server
751 failure */
752 if (Srv->Result < 200 || Srv->Result >= 300)
753 {
754 _error->Error("%u %s",Srv->Result,Srv->Code);
755 return 3;
756 }
757
758 // This is some sort of 2xx 'data follows' reply
759 Res.LastModified = Srv->Date;
760 Res.Size = Srv->Size;
761
762 // Open the file
763 delete File;
764 File = new FileFd(Queue->DestFile,FileFd::WriteAny);
765 if (_error->PendingError() == true)
766 return 3;
767
768 // Set the expected size
769 if (Srv->StartPos >= 0)
770 {
771 Res.ResumePoint = Srv->StartPos;
772 ftruncate(File->Fd(),Srv->StartPos);
773 }
774
775 // Set the start point
776 lseek(File->Fd(),0,SEEK_END);
777
778 delete Srv->In.MD5;
779 Srv->In.MD5 = new MD5Summation;
780
781 // Fill the MD5 Hash if the file is non-empty (resume)
782 if (Srv->StartPos > 0)
783 {
784 lseek(File->Fd(),0,SEEK_SET);
785 if (Srv->In.MD5->AddFD(File->Fd(),Srv->StartPos) == false)
786 {
787 _error->Errno("read","Problem hashing file");
788 return 3;
789 }
790 lseek(File->Fd(),0,SEEK_END);
791 }
792
793 SetNonBlock(File->Fd(),true);
794 return 0;
795 }
796 /*}}}*/
797 // HttpMethod::Loop /*{{{*/
798 // ---------------------------------------------------------------------
799 /* */
800 int HttpMethod::Loop()
801 {
802 ServerState *Server = 0;
803
804 while (1)
805 {
806 // We have no commands, wait for some to arrive
807 if (Queue == 0)
808 {
809 if (WaitFd(STDIN_FILENO) == false)
810 return 0;
811 }
812
813 // Run messages
814 if (Run(true) != 0)
815 return 100;
816
817 if (Queue == 0)
818 continue;
819
820 // Connect to the server
821 if (Server == 0 || Server->Comp(Queue->Uri) == false)
822 {
823 delete Server;
824 Server = new ServerState(Queue->Uri,this);
825 }
826
827 // Connnect to the host
828 if (Server->Open() == false)
829 {
830 Fail();
831 continue;
832 }
833
834 // Queue the request
835 SendReq(Queue,Server->In);
836
837 // Handle the header data
838 if (Server->RunHeaders() == false)
839 {
840 Fail();
841 continue;
842 }
843
844 // Decide what to do.
845 FetchResult Res;
846 switch (DealWithHeaders(Res,Server))
847 {
848 // Ok, the file is Open
849 case 0:
850 {
851 URIStart(Res);
852
853 // Run the data
854 if (Server->RunData() == false)
855 Fail();
856
857 Res.MD5Sum = Srv->In.MD5->Result();
858 delete File;
859 File = 0;
860 break;
861 }
862
863 // IMS hit
864 case 1:
865 {
866 URIDone(Res);
867 break;
868 }
869
870 // Hard server error, not found or something
871 case 3:
872 {
873 Fail();
874 break;
875 }
876
877 default:
878 Fail("Internal error");
879 break;
880 }
881 }
882
883 return 0;
884 }
885 /*}}}*/
886
887 int main()
888 {
889 HttpMethod Mth;
890
891 return Mth.Loop();
892 }