]> git.saurik.com Git - apt.git/blame - methods/server.cc
Implement CacheDB for source packages in apt-ftparchive
[apt.git] / methods / server.cc
CommitLineData
7330f4df
DK
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3/* ######################################################################
4
5 HTTP and HTTPS share a lot of common code and these classes are
6 exactly the dumping ground for this common code
7
8 ##################################################################### */
9 /*}}}*/
10// Include Files /*{{{*/
11#include <config.h>
12
7330f4df
DK
13#include <apt-pkg/acquire-method.h>
14#include <apt-pkg/configuration.h>
15#include <apt-pkg/error.h>
453b82a3
DK
16#include <apt-pkg/fileutl.h>
17#include <apt-pkg/strutl.h>
7330f4df 18
453b82a3
DK
19#include <ctype.h>
20#include <signal.h>
21#include <stdio.h>
22#include <stdlib.h>
7330f4df
DK
23#include <sys/stat.h>
24#include <sys/time.h>
453b82a3 25#include <time.h>
7330f4df 26#include <unistd.h>
7330f4df 27#include <iostream>
453b82a3 28#include <limits>
7330f4df 29#include <map>
453b82a3
DK
30#include <string>
31#include <vector>
7330f4df 32
453b82a3 33#include "server.h"
7330f4df
DK
34
35#include <apti18n.h>
36 /*}}}*/
37using namespace std;
38
39string ServerMethod::FailFile;
40int ServerMethod::FailFd = -1;
41time_t ServerMethod::FailTime = 0;
42
43// ServerState::RunHeaders - Get the headers before the data /*{{{*/
44// ---------------------------------------------------------------------
45/* Returns 0 if things are OK, 1 if an IO error occurred and 2 if a header
46 parse error occurred */
47ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File)
48{
49 State = Header;
50
51 Owner->Status(_("Waiting for headers"));
52
53 Major = 0;
54 Minor = 0;
55 Result = 0;
56 Size = 0;
57 StartPos = 0;
58 Encoding = Closes;
59 HaveContent = false;
60 time(&Date);
61
62 do
63 {
64 string Data;
65 if (ReadHeaderLines(Data) == false)
66 continue;
67
68 if (Owner->Debug == true)
69 clog << Data;
70
71 for (string::const_iterator I = Data.begin(); I < Data.end(); ++I)
72 {
73 string::const_iterator J = I;
74 for (; J != Data.end() && *J != '\n' && *J != '\r'; ++J);
75 if (HeaderLine(string(I,J)) == false)
76 return RUN_HEADERS_PARSE_ERROR;
77 I = J;
78 }
79
80 // 100 Continue is a Nop...
81 if (Result == 100)
82 continue;
83
1e3f4083 84 // Tidy up the connection persistence state.
7330f4df
DK
85 if (Encoding == Closes && HaveContent == true)
86 Persistent = false;
87
88 return RUN_HEADERS_OK;
89 }
90 while (LoadNextResponse(false, File) == true);
91
92 return RUN_HEADERS_IO_ERROR;
93}
94 /*}}}*/
95// ServerState::HeaderLine - Process a header line /*{{{*/
96// ---------------------------------------------------------------------
97/* */
98bool ServerState::HeaderLine(string Line)
99{
100 if (Line.empty() == true)
101 return true;
102
103 string::size_type Pos = Line.find(' ');
104 if (Pos == string::npos || Pos+1 > Line.length())
105 {
106 // Blah, some servers use "connection:closes", evil.
107 Pos = Line.find(':');
108 if (Pos == string::npos || Pos + 2 > Line.length())
109 return _error->Error(_("Bad header line"));
110 Pos++;
111 }
112
113 // Parse off any trailing spaces between the : and the next word.
114 string::size_type Pos2 = Pos;
115 while (Pos2 < Line.length() && isspace(Line[Pos2]) != 0)
116 Pos2++;
d3e8fbb3 117
7330f4df
DK
118 string Tag = string(Line,0,Pos);
119 string Val = string(Line,Pos2);
d3e8fbb3 120
7330f4df
DK
121 if (stringcasecmp(Tag.c_str(),Tag.c_str()+4,"HTTP") == 0)
122 {
123 // Evil servers return no version
124 if (Line[4] == '/')
125 {
126 int const elements = sscanf(Line.c_str(),"HTTP/%3u.%3u %3u%359[^\n]",&Major,&Minor,&Result,Code);
127 if (elements == 3)
128 {
129 Code[0] = '\0';
130 if (Owner->Debug == true)
131 clog << "HTTP server doesn't give Reason-Phrase for " << Result << std::endl;
132 }
133 else if (elements != 4)
134 return _error->Error(_("The HTTP server sent an invalid reply header"));
135 }
136 else
137 {
138 Major = 0;
139 Minor = 9;
140 if (sscanf(Line.c_str(),"HTTP %3u%359[^\n]",&Result,Code) != 2)
141 return _error->Error(_("The HTTP server sent an invalid reply header"));
142 }
143
1e3f4083 144 /* Check the HTTP response header to get the default persistence
7330f4df
DK
145 state. */
146 if (Major < 1)
147 Persistent = false;
148 else
149 {
150 if (Major == 1 && Minor == 0)
151 Persistent = false;
152 else
153 Persistent = true;
154 }
155
156 return true;
d3e8fbb3
DK
157 }
158
7330f4df
DK
159 if (stringcasecmp(Tag,"Content-Length:") == 0)
160 {
161 if (Encoding == Closes)
162 Encoding = Stream;
163 HaveContent = true;
d3e8fbb3 164
7330f4df
DK
165 // The length is already set from the Content-Range header
166 if (StartPos != 0)
167 return true;
168
169 Size = strtoull(Val.c_str(), NULL, 10);
170 if (Size >= std::numeric_limits<unsigned long long>::max())
171 return _error->Errno("HeaderLine", _("The HTTP server sent an invalid Content-Length header"));
172 else if (Size == 0)
173 HaveContent = false;
174 return true;
175 }
176
177 if (stringcasecmp(Tag,"Content-Type:") == 0)
178 {
179 HaveContent = true;
180 return true;
181 }
d3e8fbb3 182
7330f4df
DK
183 if (stringcasecmp(Tag,"Content-Range:") == 0)
184 {
185 HaveContent = true;
186
187 // ยง14.16 says 'byte-range-resp-spec' should be a '*' in case of 416
188 if (Result == 416 && sscanf(Val.c_str(), "bytes */%llu",&Size) == 1)
189 {
190 StartPos = 1; // ignore Content-Length, it would override Size
191 HaveContent = false;
192 }
193 else if (sscanf(Val.c_str(),"bytes %llu-%*u/%llu",&StartPos,&Size) != 2)
194 return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
195 if ((unsigned long long)StartPos > Size)
196 return _error->Error(_("This HTTP server has broken range support"));
197 return true;
198 }
d3e8fbb3 199
7330f4df
DK
200 if (stringcasecmp(Tag,"Transfer-Encoding:") == 0)
201 {
202 HaveContent = true;
203 if (stringcasecmp(Val,"chunked") == 0)
d3e8fbb3 204 Encoding = Chunked;
7330f4df
DK
205 return true;
206 }
207
208 if (stringcasecmp(Tag,"Connection:") == 0)
209 {
210 if (stringcasecmp(Val,"close") == 0)
211 Persistent = false;
212 if (stringcasecmp(Val,"keep-alive") == 0)
213 Persistent = true;
214 return true;
215 }
d3e8fbb3 216
7330f4df
DK
217 if (stringcasecmp(Tag,"Last-Modified:") == 0)
218 {
219 if (RFC1123StrToTime(Val.c_str(), Date) == false)
220 return _error->Error(_("Unknown date format"));
221 return true;
222 }
223
224 if (stringcasecmp(Tag,"Location:") == 0)
225 {
226 Location = Val;
227 return true;
228 }
229
230 return true;
231}
232 /*}}}*/
233// ServerState::ServerState - Constructor /*{{{*/
234ServerState::ServerState(URI Srv, ServerMethod *Owner) : ServerName(Srv), TimeOut(120), Owner(Owner)
235{
236 Reset();
237}
238 /*}}}*/
239
240bool ServerMethod::Configuration(string Message) /*{{{*/
241{
242 return pkgAcqMethod::Configuration(Message);
243}
244 /*}}}*/
245
246// ServerMethod::DealWithHeaders - Handle the retrieved header data /*{{{*/
247// ---------------------------------------------------------------------
248/* We look at the header data we got back from the server and decide what
249 to do. Returns DealWithHeadersResult (see http.h for details).
250 */
251ServerMethod::DealWithHeadersResult
252ServerMethod::DealWithHeaders(FetchResult &Res)
253{
254 // Not Modified
255 if (Server->Result == 304)
256 {
257 unlink(Queue->DestFile.c_str());
258 Res.IMSHit = true;
259 Res.LastModified = Queue->LastModified;
260 return IMS_HIT;
261 }
262
263 /* Redirect
264 *
265 * Note that it is only OK for us to treat all redirection the same
266 * because we *always* use GET, not other HTTP methods. There are
267 * three redirection codes for which it is not appropriate that we
268 * redirect. Pass on those codes so the error handling kicks in.
269 */
270 if (AllowRedirect
271 && (Server->Result > 300 && Server->Result < 400)
272 && (Server->Result != 300 // Multiple Choices
273 && Server->Result != 304 // Not Modified
274 && Server->Result != 306)) // (Not part of HTTP/1.1, reserved)
275 {
276 if (Server->Location.empty() == true);
277 else if (Server->Location[0] == '/' && Queue->Uri.empty() == false)
278 {
279 URI Uri = Queue->Uri;
280 if (Uri.Host.empty() == false)
281 NextURI = URI::SiteOnly(Uri);
282 else
283 NextURI.clear();
284 NextURI.append(DeQuoteString(Server->Location));
285 return TRY_AGAIN_OR_REDIRECT;
286 }
287 else
288 {
9082a1fc
DK
289 NextURI = DeQuoteString(Server->Location);
290 URI tmpURI = NextURI;
291 URI Uri = Queue->Uri;
292 // same protocol redirects are okay
293 if (tmpURI.Access == Uri.Access)
294 return TRY_AGAIN_OR_REDIRECT;
295 // as well as http to https
296 else if (Uri.Access == "http" && tmpURI.Access == "https")
297 return TRY_AGAIN_OR_REDIRECT;
7330f4df
DK
298 }
299 /* else pass through for error message */
300 }
301 // retry after an invalid range response without partial data
302 else if (Server->Result == 416)
303 {
304 struct stat SBuf;
305 if (stat(Queue->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
306 {
307 if ((unsigned long long)SBuf.st_size == Server->Size)
308 {
309 // the file is completely downloaded, but was not moved
310 Server->StartPos = Server->Size;
311 Server->Result = 200;
312 Server->HaveContent = false;
313 }
314 else if (unlink(Queue->DestFile.c_str()) == 0)
315 {
316 NextURI = Queue->Uri;
317 return TRY_AGAIN_OR_REDIRECT;
318 }
319 }
320 }
321
322 /* We have a reply we dont handle. This should indicate a perm server
323 failure */
324 if (Server->Result < 200 || Server->Result >= 300)
325 {
326 char err[255];
327 snprintf(err,sizeof(err)-1,"HttpError%i",Server->Result);
328 SetFailReason(err);
329 _error->Error("%u %s",Server->Result,Server->Code);
330 if (Server->HaveContent == true)
331 return ERROR_WITH_CONTENT_PAGE;
332 return ERROR_UNRECOVERABLE;
333 }
334
335 // This is some sort of 2xx 'data follows' reply
336 Res.LastModified = Server->Date;
337 Res.Size = Server->Size;
338
339 // Open the file
340 delete File;
341 File = new FileFd(Queue->DestFile,FileFd::WriteAny);
342 if (_error->PendingError() == true)
343 return ERROR_NOT_FROM_SERVER;
344
345 FailFile = Queue->DestFile;
346 FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
347 FailFd = File->Fd();
348 FailTime = Server->Date;
349
350 if (Server->InitHashes(*File) == false)
351 {
352 _error->Errno("read",_("Problem hashing file"));
353 return ERROR_NOT_FROM_SERVER;
354 }
355 if (Server->StartPos > 0)
356 Res.ResumePoint = Server->StartPos;
357
358 SetNonBlock(File->Fd(),true);
359 return FILE_IS_OPEN;
360}
361 /*}}}*/
362// ServerMethod::SigTerm - Handle a fatal signal /*{{{*/
363// ---------------------------------------------------------------------
1e3f4083 364/* This closes and timestamps the open file. This is necessary to get
7330f4df
DK
365 resume behavoir on user abort */
366void ServerMethod::SigTerm(int)
367{
368 if (FailFd == -1)
369 _exit(100);
9ce3cfc9 370
246bbb61 371 struct timeval times[2];
9ce3cfc9
DK
372 times[0].tv_sec = FailTime;
373 times[1].tv_sec = FailTime;
246bbb61
DK
374 times[0].tv_usec = times[1].tv_usec = 0;
375 utimes(FailFile.c_str(), times);
7330f4df 376 close(FailFd);
9ce3cfc9 377
7330f4df
DK
378 _exit(100);
379}
380 /*}}}*/
381// ServerMethod::Fetch - Fetch an item /*{{{*/
382// ---------------------------------------------------------------------
383/* This adds an item to the pipeline. We keep the pipeline at a fixed
384 depth. */
385bool ServerMethod::Fetch(FetchItem *)
386{
387 if (Server == 0)
388 return true;
389
390 // Queue the requests
391 int Depth = -1;
392 for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth;
393 I = I->Next, Depth++)
394 {
395 // If pipelining is disabled, we only queue 1 request
396 if (Server->Pipeline == false && Depth >= 0)
397 break;
398
399 // Make sure we stick with the same server
400 if (Server->Comp(I->Uri) == false)
401 break;
402 if (QueueBack == I)
403 {
404 QueueBack = I->Next;
405 SendReq(I);
406 continue;
407 }
408 }
409
410 return true;
d3e8fbb3 411}
7330f4df
DK
412 /*}}}*/
413// ServerMethod::Loop - Main loop /*{{{*/
414int ServerMethod::Loop()
415{
416 typedef vector<string> StringVector;
417 typedef vector<string>::iterator StringVectorIterator;
418 map<string, StringVector> Redirected;
419
420 signal(SIGTERM,SigTerm);
421 signal(SIGINT,SigTerm);
422
423 Server = 0;
424
425 int FailCounter = 0;
426 while (1)
427 {
428 // We have no commands, wait for some to arrive
429 if (Queue == 0)
430 {
431 if (WaitFd(STDIN_FILENO) == false)
432 return 0;
433 }
434
435 /* Run messages, we can accept 0 (no message) if we didn't
436 do a WaitFd above.. Otherwise the FD is closed. */
437 int Result = Run(true);
438 if (Result != -1 && (Result != 0 || Queue == 0))
439 {
440 if(FailReason.empty() == false ||
441 _config->FindB("Acquire::http::DependOnSTDIN", true) == true)
442 return 100;
443 else
444 return 0;
445 }
446
447 if (Queue == 0)
448 continue;
449
450 // Connect to the server
451 if (Server == 0 || Server->Comp(Queue->Uri) == false)
452 {
453 delete Server;
454 Server = CreateServerState(Queue->Uri);
455 }
456 /* If the server has explicitly said this is the last connection
457 then we pre-emptively shut down the pipeline and tear down
458 the connection. This will speed up HTTP/1.0 servers a tad
459 since we don't have to wait for the close sequence to
460 complete */
461 if (Server->Persistent == false)
462 Server->Close();
463
464 // Reset the pipeline
465 if (Server->IsOpen() == false)
466 QueueBack = Queue;
467
468 // Connnect to the host
469 if (Server->Open() == false)
470 {
471 Fail(true);
472 delete Server;
473 Server = 0;
474 continue;
475 }
476
477 // Fill the pipeline.
478 Fetch(0);
479
480 // Fetch the next URL header data from the server.
481 switch (Server->RunHeaders(File))
482 {
483 case ServerState::RUN_HEADERS_OK:
484 break;
485
486 // The header data is bad
487 case ServerState::RUN_HEADERS_PARSE_ERROR:
488 {
489 _error->Error(_("Bad header data"));
490 Fail(true);
491 RotateDNS();
492 continue;
493 }
494
495 // The server closed a connection during the header get..
496 default:
497 case ServerState::RUN_HEADERS_IO_ERROR:
498 {
499 FailCounter++;
500 _error->Discard();
501 Server->Close();
502 Server->Pipeline = false;
503
504 if (FailCounter >= 2)
505 {
506 Fail(_("Connection failed"),true);
507 FailCounter = 0;
508 }
509
510 RotateDNS();
511 continue;
512 }
513 };
514
515 // Decide what to do.
516 FetchResult Res;
517 Res.Filename = Queue->DestFile;
518 switch (DealWithHeaders(Res))
519 {
520 // Ok, the file is Open
521 case FILE_IS_OPEN:
522 {
523 URIStart(Res);
524
525 // Run the data
526 bool Result = true;
527 if (Server->HaveContent)
528 Result = Server->RunData(File);
529
530 /* If the server is sending back sizeless responses then fill in
531 the size now */
532 if (Res.Size == 0)
533 Res.Size = File->Size();
534
535 // Close the file, destroy the FD object and timestamp it
536 FailFd = -1;
537 delete File;
538 File = 0;
539
540 // Timestamp
246bbb61 541 struct timeval times[2];
9ce3cfc9 542 times[0].tv_sec = times[1].tv_sec = Server->Date;
246bbb61
DK
543 times[0].tv_usec = times[1].tv_usec = 0;
544 utimes(Queue->DestFile.c_str(), times);
7330f4df
DK
545
546 // Send status to APT
547 if (Result == true)
548 {
549 Res.TakeHashes(*Server->GetHashes());
550 URIDone(Res);
551 }
552 else
553 {
554 if (Server->IsOpen() == false)
555 {
556 FailCounter++;
557 _error->Discard();
558 Server->Close();
559
560 if (FailCounter >= 2)
561 {
562 Fail(_("Connection failed"),true);
563 FailCounter = 0;
564 }
565
566 QueueBack = Queue;
567 }
568 else
569 Fail(true);
570 }
571 break;
572 }
573
574 // IMS hit
575 case IMS_HIT:
576 {
577 URIDone(Res);
578 break;
579 }
580
581 // Hard server error, not found or something
582 case ERROR_UNRECOVERABLE:
583 {
584 Fail();
585 break;
586 }
587
588 // Hard internal error, kill the connection and fail
589 case ERROR_NOT_FROM_SERVER:
590 {
591 delete File;
592 File = 0;
593
594 Fail();
595 RotateDNS();
596 Server->Close();
597 break;
598 }
599
600 // We need to flush the data, the header is like a 404 w/ error text
601 case ERROR_WITH_CONTENT_PAGE:
602 {
603 Fail();
604
605 // Send to content to dev/null
606 File = new FileFd("/dev/null",FileFd::WriteExists);
607 Server->RunData(File);
608 delete File;
609 File = 0;
610 break;
611 }
612
613 // Try again with a new URL
614 case TRY_AGAIN_OR_REDIRECT:
615 {
616 // Clear rest of response if there is content
617 if (Server->HaveContent)
618 {
619 File = new FileFd("/dev/null",FileFd::WriteExists);
620 Server->RunData(File);
621 delete File;
622 File = 0;
623 }
624
625 /* Detect redirect loops. No more redirects are allowed
626 after the same URI is seen twice in a queue item. */
627 StringVector &R = Redirected[Queue->DestFile];
628 bool StopRedirects = false;
629 if (R.empty() == true)
630 R.push_back(Queue->Uri);
631 else if (R[0] == "STOP" || R.size() > 10)
632 StopRedirects = true;
633 else
634 {
635 for (StringVectorIterator I = R.begin(); I != R.end(); ++I)
636 if (Queue->Uri == *I)
637 {
638 R[0] = "STOP";
639 break;
640 }
641
642 R.push_back(Queue->Uri);
643 }
644
645 if (StopRedirects == false)
646 Redirect(NextURI);
647 else
648 Fail();
649
650 break;
651 }
652
653 default:
654 Fail(_("Internal error"));
655 break;
656 }
657
658 FailCounter = 0;
659 }
660
661 return 0;
662}
663 /*}}}*/