1 /* 2 * Copyright 2010-2021 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Christophe Huriaux, c.huriaux@gmail.com 7 * Niels Sascha Reedijk, niels.reedijk@gmail.com 8 * Adrien Destugues, pulkomandy@pulkomandy.tk 9 * Stephan Aßmus, superstippi@gmx.de 10 */ 11 12 13 #include <HttpRequest.h> 14 15 #include <arpa/inet.h> 16 #include <stdio.h> 17 18 #include <cstdlib> 19 #include <deque> 20 #include <new> 21 22 #include <AutoDeleter.h> 23 #include <Certificate.h> 24 #include <Debug.h> 25 #include <DynamicBuffer.h> 26 #include <File.h> 27 #include <ProxySecureSocket.h> 28 #include <Socket.h> 29 #include <SecureSocket.h> 30 #include <StackOrHeapArray.h> 31 #include <ZlibCompressionAlgorithm.h> 32 33 using namespace BPrivate::Network; 34 35 36 static const int32 kHttpBufferSize = 4096; 37 38 39 namespace BPrivate { 40 41 class CheckedSecureSocket: public BSecureSocket 42 { 43 public: 44 CheckedSecureSocket(BHttpRequest* request); 45 46 bool CertificateVerificationFailed(BCertificate& certificate, 47 const char* message); 48 49 private: 50 BHttpRequest* fRequest; 51 }; 52 53 54 CheckedSecureSocket::CheckedSecureSocket(BHttpRequest* request) 55 : 56 BSecureSocket(), 57 fRequest(request) 58 { 59 } 60 61 62 bool 63 CheckedSecureSocket::CertificateVerificationFailed(BCertificate& certificate, 64 const char* message) 65 { 66 return fRequest->_CertificateVerificationFailed(certificate, message); 67 } 68 69 70 class CheckedProxySecureSocket: public BProxySecureSocket 71 { 72 public: 73 CheckedProxySecureSocket(const BNetworkAddress& proxy, BHttpRequest* request); 74 75 bool CertificateVerificationFailed(BCertificate& certificate, 76 const char* message); 77 78 private: 79 BHttpRequest* fRequest; 80 }; 81 82 83 CheckedProxySecureSocket::CheckedProxySecureSocket(const BNetworkAddress& proxy, 84 BHttpRequest* request) 85 : 86 BProxySecureSocket(proxy), 87 fRequest(request) 88 { 89 } 90 91 92 bool 93 CheckedProxySecureSocket::CertificateVerificationFailed(BCertificate& certificate, 94 const char* message) 95 { 96 return fRequest->_CertificateVerificationFailed(certificate, message); 97 } 98 }; 99 100 101 BHttpRequest::BHttpRequest(const BUrl& url, BDataIO* output, bool ssl, 102 const char* protocolName, BUrlProtocolListener* listener, 103 BUrlContext* context) 104 : 105 BNetworkRequest(url, output, listener, context, "BUrlProtocol.HTTP", 106 protocolName), 107 fSSL(ssl), 108 fRequestMethod(B_HTTP_GET), 109 fHttpVersion(B_HTTP_11), 110 fResult(url), 111 fRequestStatus(kRequestInitialState), 112 fOptHeaders(NULL), 113 fOptPostFields(NULL), 114 fOptInputData(NULL), 115 fOptInputDataSize(-1), 116 fOptRangeStart(-1), 117 fOptRangeEnd(-1), 118 fOptFollowLocation(true) 119 { 120 _ResetOptions(); 121 fSocket = NULL; 122 } 123 124 125 BHttpRequest::BHttpRequest(const BHttpRequest& other) 126 : 127 BNetworkRequest(other.Url(), other.Output(), other.fListener, 128 other.fContext, "BUrlProtocol.HTTP", other.fSSL ? "HTTPS" : "HTTP"), 129 fSSL(other.fSSL), 130 fRequestMethod(other.fRequestMethod), 131 fHttpVersion(other.fHttpVersion), 132 fResult(other.fUrl), 133 fRequestStatus(kRequestInitialState), 134 fOptHeaders(NULL), 135 fOptPostFields(NULL), 136 fOptInputData(NULL), 137 fOptInputDataSize(-1), 138 fOptRangeStart(other.fOptRangeStart), 139 fOptRangeEnd(other.fOptRangeEnd), 140 fOptFollowLocation(other.fOptFollowLocation) 141 { 142 _ResetOptions(); 143 // FIXME some options may be copied from other instead. 144 fSocket = NULL; 145 } 146 147 148 BHttpRequest::~BHttpRequest() 149 { 150 Stop(); 151 152 delete fSocket; 153 154 delete fOptInputData; 155 delete fOptHeaders; 156 delete fOptPostFields; 157 } 158 159 160 void 161 BHttpRequest::SetMethod(const char* const method) 162 { 163 fRequestMethod = method; 164 } 165 166 167 void 168 BHttpRequest::SetFollowLocation(bool follow) 169 { 170 fOptFollowLocation = follow; 171 } 172 173 174 void 175 BHttpRequest::SetMaxRedirections(int8 redirections) 176 { 177 fOptMaxRedirs = redirections; 178 } 179 180 181 void 182 BHttpRequest::SetReferrer(const BString& referrer) 183 { 184 fOptReferer = referrer; 185 } 186 187 188 void 189 BHttpRequest::SetUserAgent(const BString& agent) 190 { 191 fOptUserAgent = agent; 192 } 193 194 195 void 196 BHttpRequest::SetDiscardData(bool discard) 197 { 198 fOptDiscardData = discard; 199 } 200 201 202 void 203 BHttpRequest::SetDisableListener(bool disable) 204 { 205 fOptDisableListener = disable; 206 } 207 208 209 void 210 BHttpRequest::SetAutoReferrer(bool enable) 211 { 212 fOptAutoReferer = enable; 213 } 214 215 216 void 217 BHttpRequest::SetStopOnError(bool stop) 218 { 219 fOptStopOnError = stop; 220 } 221 222 223 void 224 BHttpRequest::SetUserName(const BString& name) 225 { 226 fOptUsername = name; 227 } 228 229 230 void 231 BHttpRequest::SetPassword(const BString& password) 232 { 233 fOptPassword = password; 234 } 235 236 237 void 238 BHttpRequest::SetRangeStart(off_t position) 239 { 240 // This field is used within the transfer loop, so only 241 // allow setting it before sending the request. 242 if (fRequestStatus == kRequestInitialState) 243 fOptRangeStart = position; 244 } 245 246 247 void 248 BHttpRequest::SetRangeEnd(off_t position) 249 { 250 // This field could be used in the transfer loop, so only 251 // allow setting it before sending the request. 252 if (fRequestStatus == kRequestInitialState) 253 fOptRangeEnd = position; 254 } 255 256 257 void 258 BHttpRequest::SetPostFields(const BHttpForm& fields) 259 { 260 AdoptPostFields(new(std::nothrow) BHttpForm(fields)); 261 } 262 263 264 void 265 BHttpRequest::SetHeaders(const BHttpHeaders& headers) 266 { 267 AdoptHeaders(new(std::nothrow) BHttpHeaders(headers)); 268 } 269 270 271 void 272 BHttpRequest::AdoptPostFields(BHttpForm* const fields) 273 { 274 delete fOptPostFields; 275 fOptPostFields = fields; 276 277 if (fOptPostFields != NULL) 278 fRequestMethod = B_HTTP_POST; 279 } 280 281 282 void 283 BHttpRequest::AdoptInputData(BDataIO* const data, const ssize_t size) 284 { 285 delete fOptInputData; 286 fOptInputData = data; 287 fOptInputDataSize = size; 288 } 289 290 291 void 292 BHttpRequest::AdoptHeaders(BHttpHeaders* const headers) 293 { 294 delete fOptHeaders; 295 fOptHeaders = headers; 296 } 297 298 299 /*static*/ bool 300 BHttpRequest::IsInformationalStatusCode(int16 code) 301 { 302 return (code >= B_HTTP_STATUS__INFORMATIONAL_BASE) 303 && (code < B_HTTP_STATUS__INFORMATIONAL_END); 304 } 305 306 307 /*static*/ bool 308 BHttpRequest::IsSuccessStatusCode(int16 code) 309 { 310 return (code >= B_HTTP_STATUS__SUCCESS_BASE) 311 && (code < B_HTTP_STATUS__SUCCESS_END); 312 } 313 314 315 /*static*/ bool 316 BHttpRequest::IsRedirectionStatusCode(int16 code) 317 { 318 return (code >= B_HTTP_STATUS__REDIRECTION_BASE) 319 && (code < B_HTTP_STATUS__REDIRECTION_END); 320 } 321 322 323 /*static*/ bool 324 BHttpRequest::IsClientErrorStatusCode(int16 code) 325 { 326 return (code >= B_HTTP_STATUS__CLIENT_ERROR_BASE) 327 && (code < B_HTTP_STATUS__CLIENT_ERROR_END); 328 } 329 330 331 /*static*/ bool 332 BHttpRequest::IsServerErrorStatusCode(int16 code) 333 { 334 return (code >= B_HTTP_STATUS__SERVER_ERROR_BASE) 335 && (code < B_HTTP_STATUS__SERVER_ERROR_END); 336 } 337 338 339 /*static*/ int16 340 BHttpRequest::StatusCodeClass(int16 code) 341 { 342 if (BHttpRequest::IsInformationalStatusCode(code)) 343 return B_HTTP_STATUS_CLASS_INFORMATIONAL; 344 else if (BHttpRequest::IsSuccessStatusCode(code)) 345 return B_HTTP_STATUS_CLASS_SUCCESS; 346 else if (BHttpRequest::IsRedirectionStatusCode(code)) 347 return B_HTTP_STATUS_CLASS_REDIRECTION; 348 else if (BHttpRequest::IsClientErrorStatusCode(code)) 349 return B_HTTP_STATUS_CLASS_CLIENT_ERROR; 350 else if (BHttpRequest::IsServerErrorStatusCode(code)) 351 return B_HTTP_STATUS_CLASS_SERVER_ERROR; 352 353 return B_HTTP_STATUS_CLASS_INVALID; 354 } 355 356 357 const BUrlResult& 358 BHttpRequest::Result() const 359 { 360 return fResult; 361 } 362 363 364 status_t 365 BHttpRequest::Stop() 366 { 367 368 if (fSocket != NULL) { 369 fSocket->Disconnect(); 370 // Unlock any pending connect, read or write operation. 371 } 372 return BNetworkRequest::Stop(); 373 } 374 375 376 void 377 BHttpRequest::_ResetOptions() 378 { 379 delete fOptPostFields; 380 delete fOptHeaders; 381 382 fOptFollowLocation = true; 383 fOptMaxRedirs = 8; 384 fOptReferer = ""; 385 fOptUserAgent = "Services Kit (Haiku)"; 386 fOptUsername = ""; 387 fOptPassword = ""; 388 fOptAuthMethods = B_HTTP_AUTHENTICATION_BASIC | B_HTTP_AUTHENTICATION_DIGEST 389 | B_HTTP_AUTHENTICATION_IE_DIGEST; 390 fOptHeaders = NULL; 391 fOptPostFields = NULL; 392 fOptSetCookies = true; 393 fOptDiscardData = false; 394 fOptDisableListener = false; 395 fOptAutoReferer = true; 396 } 397 398 399 status_t 400 BHttpRequest::_ProtocolLoop() 401 { 402 // Initialize the request redirection loop 403 int8 maxRedirs = fOptMaxRedirs; 404 bool newRequest; 405 406 do { 407 newRequest = false; 408 409 // Result reset 410 fHeaders.Clear(); 411 _ResultHeaders().Clear(); 412 413 BString host = fUrl.Host(); 414 int port = fSSL ? 443 : 80; 415 416 if (fUrl.HasPort()) 417 port = fUrl.Port(); 418 419 if (fContext->UseProxy()) { 420 host = fContext->GetProxyHost(); 421 port = fContext->GetProxyPort(); 422 } 423 424 status_t result = fInputBuffer.InitCheck(); 425 if (result != B_OK) 426 return result; 427 428 if (!_ResolveHostName(host, port)) { 429 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, 430 "Unable to resolve hostname (%s), aborting.", 431 fUrl.Host().String()); 432 return B_SERVER_NOT_FOUND; 433 } 434 435 status_t requestStatus = _MakeRequest(); 436 if (requestStatus != B_OK) 437 return requestStatus; 438 439 // Prepare the referer for the next request if needed 440 if (fOptAutoReferer) 441 fOptReferer = fUrl.UrlString(); 442 443 switch (StatusCodeClass(fResult.StatusCode())) { 444 case B_HTTP_STATUS_CLASS_INFORMATIONAL: 445 // Header 100:continue should have been 446 // handled in the _MakeRequest read loop 447 break; 448 449 case B_HTTP_STATUS_CLASS_SUCCESS: 450 break; 451 452 case B_HTTP_STATUS_CLASS_REDIRECTION: 453 { 454 // Redirection has been explicitly disabled 455 if (!fOptFollowLocation) 456 break; 457 458 int code = fResult.StatusCode(); 459 if (code == B_HTTP_STATUS_MOVED_PERMANENTLY 460 || code == B_HTTP_STATUS_FOUND 461 || code == B_HTTP_STATUS_SEE_OTHER 462 || code == B_HTTP_STATUS_TEMPORARY_REDIRECT) { 463 BString locationUrl = fHeaders["Location"]; 464 465 fUrl = BUrl(fUrl, locationUrl); 466 467 // 302 and 303 redirections also convert POST requests to GET 468 // (and remove the posted form data) 469 if ((code == B_HTTP_STATUS_FOUND 470 || code == B_HTTP_STATUS_SEE_OTHER) 471 && fRequestMethod == B_HTTP_POST) { 472 SetMethod(B_HTTP_GET); 473 delete fOptPostFields; 474 fOptPostFields = NULL; 475 delete fOptInputData; 476 fOptInputData = NULL; 477 fOptInputDataSize = 0; 478 } 479 480 if (--maxRedirs > 0) { 481 newRequest = true; 482 483 // Redirections may need a switch from http to https. 484 if (fUrl.Protocol() == "https") 485 fSSL = true; 486 else if (fUrl.Protocol() == "http") 487 fSSL = false; 488 489 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 490 "Following: %s\n", 491 fUrl.UrlString().String()); 492 } 493 } 494 break; 495 } 496 497 case B_HTTP_STATUS_CLASS_CLIENT_ERROR: 498 if (fResult.StatusCode() == B_HTTP_STATUS_UNAUTHORIZED) { 499 BHttpAuthentication* authentication 500 = &fContext->GetAuthentication(fUrl); 501 status_t status = B_OK; 502 503 if (authentication->Method() == B_HTTP_AUTHENTICATION_NONE) { 504 // There is no authentication context for this 505 // url yet, so let's create one. 506 BHttpAuthentication newAuth; 507 newAuth.Initialize(fHeaders["WWW-Authenticate"]); 508 fContext->AddAuthentication(fUrl, newAuth); 509 510 // Get the copy of the authentication we just added. 511 // That copy is owned by the BUrlContext and won't be 512 // deleted (unlike the temporary object above) 513 authentication = &fContext->GetAuthentication(fUrl); 514 } 515 516 newRequest = false; 517 if (fOptUsername.Length() > 0 && status == B_OK) { 518 // If we received an username and password, add them 519 // to the request. This will either change the 520 // credentials for an existing request, or set them 521 // for a new one we created just above. 522 // 523 // If this request handles HTTP redirections, it will 524 // also automatically retry connecting and send the 525 // login information. 526 authentication->SetUserName(fOptUsername); 527 authentication->SetPassword(fOptPassword); 528 newRequest = true; 529 } 530 } 531 break; 532 533 case B_HTTP_STATUS_CLASS_SERVER_ERROR: 534 break; 535 536 default: 537 case B_HTTP_STATUS_CLASS_INVALID: 538 break; 539 } 540 } while (newRequest); 541 542 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 543 "%" B_PRId32 " headers and %" B_PRIuSIZE " bytes of data remaining", 544 fHeaders.CountHeaders(), fInputBuffer.Size()); 545 546 if (fResult.StatusCode() == 404) 547 return B_RESOURCE_NOT_FOUND; 548 549 return B_OK; 550 } 551 552 553 status_t 554 BHttpRequest::_MakeRequest() 555 { 556 delete fSocket; 557 558 if (fSSL) { 559 if (fContext->UseProxy()) { 560 BNetworkAddress proxy(fContext->GetProxyHost(), fContext->GetProxyPort()); 561 fSocket = new(std::nothrow) BPrivate::CheckedProxySecureSocket(proxy, this); 562 } else 563 fSocket = new(std::nothrow) BPrivate::CheckedSecureSocket(this); 564 } else 565 fSocket = new(std::nothrow) BSocket(); 566 567 if (fSocket == NULL) 568 return B_NO_MEMORY; 569 570 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.", 571 fUrl.Authority().String(), fRemoteAddr.Port()); 572 status_t connectError = fSocket->Connect(fRemoteAddr); 573 574 if (connectError != B_OK) { 575 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s", 576 strerror(connectError)); 577 return connectError; 578 } 579 580 //! ProtocolHook:ConnectionOpened 581 if (fListener != NULL) 582 fListener->ConnectionOpened(this); 583 584 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 585 "Connection opened, sending request."); 586 587 BString requestHeaders; 588 requestHeaders.Append(_SerializeRequest()); 589 requestHeaders.Append(_SerializeHeaders()); 590 requestHeaders.Append("\r\n"); 591 fSocket->Write(requestHeaders.String(), requestHeaders.Length()); 592 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent."); 593 594 _SendPostData(); 595 fRequestStatus = kRequestInitialState; 596 597 598 599 // Receive loop 600 bool disableListener = false; 601 bool receiveEnd = false; 602 bool parseEnd = false; 603 bool readByChunks = false; 604 bool decompress = false; 605 status_t readError = B_OK; 606 ssize_t bytesRead = 0; 607 off_t bytesReceived = 0; 608 off_t bytesTotal = 0; 609 size_t previousBufferSize = 0; 610 off_t bytesUnpacked = 0; 611 char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize]; 612 ArrayDeleter<char> inputTempBufferDeleter(inputTempBuffer); 613 ssize_t inputTempSize = kHttpBufferSize; 614 ssize_t chunkSize = -1; 615 DynamicBuffer decompressorStorage; 616 BDataIO* decompressingStream; 617 ObjectDeleter<BDataIO> decompressingStreamDeleter; 618 619 while (!fQuit && !(receiveEnd && parseEnd)) { 620 if ((!receiveEnd) && (fInputBuffer.Size() == previousBufferSize)) { 621 BStackOrHeapArray<char, 4096> chunk(kHttpBufferSize); 622 bytesRead = fSocket->Read(chunk, kHttpBufferSize); 623 624 if (bytesRead < 0) { 625 readError = bytesRead; 626 break; 627 } else if (bytesRead == 0) { 628 // Check if we got the expected number of bytes. 629 // Exceptions: 630 // - If the content-length is not known (bytesTotal is 0), for 631 // example in the case of a chunked transfer, we can't know 632 // - If the request method is "HEAD" which explicitly asks the 633 // server to not send any data (only the headers) 634 if (bytesTotal > 0 && bytesReceived != bytesTotal) { 635 readError = B_IO_ERROR; 636 break; 637 } 638 receiveEnd = true; 639 } 640 641 fInputBuffer.AppendData(chunk, bytesRead); 642 } else 643 bytesRead = 0; 644 645 previousBufferSize = fInputBuffer.Size(); 646 647 if (fRequestStatus < kRequestStatusReceived) { 648 _ParseStatus(); 649 650 if (fOptFollowLocation 651 && IsRedirectionStatusCode(fResult.StatusCode())) 652 disableListener = true; 653 654 if (fOptStopOnError 655 && fResult.StatusCode() >= B_HTTP_STATUS_CLASS_CLIENT_ERROR) 656 { 657 fQuit = true; 658 break; 659 } 660 661 //! ProtocolHook:ResponseStarted 662 if (fRequestStatus >= kRequestStatusReceived && fListener != NULL 663 && !disableListener) 664 fListener->ResponseStarted(this); 665 } 666 667 if (fRequestStatus < kRequestHeadersReceived) { 668 _ParseHeaders(); 669 670 if (fRequestStatus >= kRequestHeadersReceived) { 671 _ResultHeaders() = fHeaders; 672 673 // Parse received cookies 674 if (fContext != NULL) { 675 for (int32 i = 0; i < fHeaders.CountHeaders(); i++) { 676 if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) { 677 fContext->GetCookieJar().AddCookie( 678 fHeaders.HeaderAt(i).Value(), fUrl); 679 } 680 } 681 } 682 683 //! ProtocolHook:HeadersReceived 684 if (fListener != NULL && !disableListener) 685 fListener->HeadersReceived(this); 686 687 688 if (BString(fHeaders["Transfer-Encoding"]) == "chunked") 689 readByChunks = true; 690 691 BString contentEncoding(fHeaders["Content-Encoding"]); 692 // We don't advertise "deflate" support (see above), but we 693 // still try to decompress it, if a server ever sends a deflate 694 // stream despite it not being in our Accept-Encoding list. 695 if (contentEncoding == "gzip" 696 || contentEncoding == "deflate") { 697 decompress = true; 698 readError = BZlibCompressionAlgorithm() 699 .CreateDecompressingOutputStream(&decompressorStorage, 700 NULL, decompressingStream); 701 if (readError != B_OK) 702 break; 703 704 decompressingStreamDeleter.SetTo(decompressingStream); 705 } 706 707 int32 index = fHeaders.HasHeader("Content-Length"); 708 if (index != B_ERROR) 709 bytesTotal = atoll(fHeaders.HeaderAt(index).Value()); 710 else 711 bytesTotal = -1; 712 713 if (fRequestMethod == B_HTTP_HEAD 714 || fResult.StatusCode() == 204) { 715 // In the case of a HEAD request or if the server replies 716 // 204 ("no content"), we don't expect to receive anything 717 // more, and the socket will be closed. 718 receiveEnd = true; 719 } 720 } 721 } 722 723 if (fRequestStatus >= kRequestHeadersReceived) { 724 // If Transfer-Encoding is chunked, we should read a complete 725 // chunk in buffer before handling it 726 if (readByChunks) { 727 if (chunkSize >= 0) { 728 if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) { 729 // 2 more bytes to handle the closing CR+LF 730 bytesRead = chunkSize; 731 if (inputTempSize < chunkSize + 2) { 732 inputTempSize = chunkSize + 2; 733 inputTempBuffer 734 = new(std::nothrow) char[inputTempSize]; 735 inputTempBufferDeleter.SetTo(inputTempBuffer); 736 } 737 738 if (inputTempBuffer == NULL) { 739 readError = B_NO_MEMORY; 740 break; 741 } 742 743 fInputBuffer.RemoveData(inputTempBuffer, 744 chunkSize + 2); 745 chunkSize = -1; 746 } else { 747 // Not enough data, try again later 748 bytesRead = -1; 749 } 750 } else { 751 BString chunkHeader; 752 if (_GetLine(chunkHeader) == B_ERROR) { 753 chunkSize = -1; 754 bytesRead = -1; 755 } else { 756 // Format of a chunk header: 757 // <chunk size in hex>[; optional data] 758 int32 semiColonIndex = chunkHeader.FindFirst(';', 0); 759 760 // Cut-off optional data if present 761 if (semiColonIndex != -1) { 762 chunkHeader.Remove(semiColonIndex, 763 chunkHeader.Length() - semiColonIndex); 764 } 765 766 chunkSize = strtol(chunkHeader.String(), NULL, 16); 767 if (chunkSize == 0) 768 fRequestStatus = kRequestContentReceived; 769 770 bytesRead = -1; 771 } 772 } 773 774 // A chunk of 0 bytes indicates the end of the chunked transfer 775 if (bytesRead == 0) 776 receiveEnd = true; 777 } else { 778 bytesRead = fInputBuffer.Size(); 779 780 if (bytesRead > 0) { 781 if (inputTempSize < bytesRead) { 782 inputTempSize = bytesRead; 783 inputTempBuffer = new(std::nothrow) char[bytesRead]; 784 inputTempBufferDeleter.SetTo(inputTempBuffer); 785 } 786 787 if (inputTempBuffer == NULL) { 788 readError = B_NO_MEMORY; 789 break; 790 } 791 fInputBuffer.RemoveData(inputTempBuffer, bytesRead); 792 } 793 } 794 795 if (bytesRead >= 0) { 796 bytesReceived += bytesRead; 797 798 if (fOutput != NULL && !disableListener) { 799 if (decompress) { 800 readError = decompressingStream->WriteExactly( 801 inputTempBuffer, bytesRead); 802 if (readError != B_OK) 803 break; 804 805 ssize_t size = decompressorStorage.Size(); 806 BStackOrHeapArray<char, 4096> buffer(size); 807 size = decompressorStorage.Read(buffer, size); 808 if (size > 0) { 809 size_t written = 0; 810 readError = fOutput->WriteExactly(buffer, 811 size, &written); 812 if (fListener != NULL && written > 0) 813 fListener->BytesWritten(this, written); 814 if (readError != B_OK) 815 break; 816 bytesUnpacked += size; 817 } 818 } else if (bytesRead > 0) { 819 size_t written = 0; 820 readError = fOutput->WriteExactly(inputTempBuffer, 821 bytesRead, &written); 822 if (fListener != NULL && written > 0) 823 fListener->BytesWritten(this, written); 824 if (readError != B_OK) 825 break; 826 } 827 } 828 829 if (fListener != NULL && !disableListener) 830 fListener->DownloadProgress(this, bytesReceived, 831 std::max((off_t)0, bytesTotal)); 832 833 if (bytesTotal >= 0 && bytesReceived >= bytesTotal) 834 receiveEnd = true; 835 836 if (decompress && receiveEnd && !disableListener) { 837 readError = decompressingStream->Flush(); 838 839 if (readError == B_BUFFER_OVERFLOW) 840 readError = B_OK; 841 842 if (readError != B_OK) 843 break; 844 845 ssize_t size = decompressorStorage.Size(); 846 BStackOrHeapArray<char, 4096> buffer(size); 847 size = decompressorStorage.Read(buffer, size); 848 if (fOutput != NULL && size > 0 && !disableListener) { 849 size_t written = 0; 850 readError = fOutput->WriteExactly(buffer, size, 851 &written); 852 if (fListener != NULL && written > 0) 853 fListener->BytesWritten(this, written); 854 if (readError != B_OK) 855 break; 856 bytesUnpacked += size; 857 } 858 } 859 } 860 } 861 862 parseEnd = (fInputBuffer.Size() == 0); 863 } 864 865 fSocket->Disconnect(); 866 867 if (readError != B_OK) 868 return readError; 869 870 return fQuit ? B_INTERRUPTED : B_OK; 871 } 872 873 874 void 875 BHttpRequest::_ParseStatus() 876 { 877 // Status line should be formatted like: HTTP/M.m SSS ... 878 // With: M = Major version of the protocol 879 // m = Minor version of the protocol 880 // SSS = three-digit status code of the response 881 // ... = additional text info 882 BString statusLine; 883 if (_GetLine(statusLine) == B_ERROR) 884 return; 885 886 if (statusLine.CountChars() < 12) 887 return; 888 889 fRequestStatus = kRequestStatusReceived; 890 891 BString statusCodeStr; 892 BString statusText; 893 statusLine.CopyInto(statusCodeStr, 9, 3); 894 _SetResultStatusCode(atoi(statusCodeStr.String())); 895 896 statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13); 897 898 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)", 899 atoi(statusCodeStr.String()), _ResultStatusText().String()); 900 } 901 902 903 void 904 BHttpRequest::_ParseHeaders() 905 { 906 BString currentHeader; 907 while (_GetLine(currentHeader) != B_ERROR) { 908 // An empty line means the end of the header section 909 if (currentHeader.Length() == 0) { 910 fRequestStatus = kRequestHeadersReceived; 911 return; 912 } 913 914 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s", 915 currentHeader.String()); 916 fHeaders.AddHeader(currentHeader.String()); 917 } 918 } 919 920 921 BString 922 BHttpRequest::_SerializeRequest() 923 { 924 BString request(fRequestMethod); 925 request << ' '; 926 927 if (fContext->UseProxy()) { 928 // When there is a proxy, the request must include the host and port so 929 // the proxy knows where to send the request. 930 request << Url().Protocol() << "://" << Url().Host(); 931 if (Url().HasPort()) 932 request << ':' << Url().Port(); 933 } 934 935 if (Url().HasPath() && Url().Path().Length() > 0) 936 request << Url().Path(); 937 else 938 request << '/'; 939 940 if (Url().HasRequest()) 941 request << '?' << Url().Request(); 942 943 switch (fHttpVersion) { 944 case B_HTTP_11: 945 request << " HTTP/1.1\r\n"; 946 break; 947 948 default: 949 case B_HTTP_10: 950 request << " HTTP/1.0\r\n"; 951 break; 952 } 953 954 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", request.String()); 955 956 return request; 957 } 958 959 960 BString 961 BHttpRequest::_SerializeHeaders() 962 { 963 BHttpHeaders outputHeaders; 964 965 // HTTP 1.1 additional headers 966 if (fHttpVersion == B_HTTP_11) { 967 BString host = Url().Host(); 968 if (Url().HasPort() && !_IsDefaultPort()) 969 host << ':' << Url().Port(); 970 971 outputHeaders.AddHeader("Host", host); 972 973 outputHeaders.AddHeader("Accept", "*/*"); 974 outputHeaders.AddHeader("Accept-Encoding", "gzip"); 975 // Allows the server to compress data using the "gzip" format. 976 // "deflate" is not supported, because there are two interpretations 977 // of what it means (the RFC and Microsoft products), and we don't 978 // want to handle this. Very few websites support only deflate, 979 // and most of them will send gzip, or at worst, uncompressed data. 980 981 outputHeaders.AddHeader("Connection", "close"); 982 // Let the remote server close the connection after response since 983 // we don't handle multiple request on a single connection 984 } 985 986 // Classic HTTP headers 987 if (fOptUserAgent.CountChars() > 0) 988 outputHeaders.AddHeader("User-Agent", fOptUserAgent.String()); 989 990 if (fOptReferer.CountChars() > 0) 991 outputHeaders.AddHeader("Referer", fOptReferer.String()); 992 993 // Optional range requests headers 994 if (fOptRangeStart != -1 || fOptRangeEnd != -1) { 995 if (fOptRangeStart == -1) 996 fOptRangeStart = 0; 997 BString range; 998 if (fOptRangeEnd != -1) { 999 range.SetToFormat("bytes=%" B_PRIdOFF "-%" B_PRIdOFF, 1000 fOptRangeStart, fOptRangeEnd); 1001 } else { 1002 range.SetToFormat("bytes=%" B_PRIdOFF "-", fOptRangeStart); 1003 } 1004 outputHeaders.AddHeader("Range", range.String()); 1005 } 1006 1007 // Authentication 1008 if (fContext != NULL) { 1009 BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl); 1010 if (authentication.Method() != B_HTTP_AUTHENTICATION_NONE) { 1011 if (fOptUsername.Length() > 0) { 1012 authentication.SetUserName(fOptUsername); 1013 authentication.SetPassword(fOptPassword); 1014 } 1015 1016 BString request(fRequestMethod); 1017 outputHeaders.AddHeader("Authorization", 1018 authentication.Authorization(fUrl, request)); 1019 } 1020 } 1021 1022 // Required headers for POST data 1023 if (fOptPostFields != NULL && fRequestMethod == B_HTTP_POST) { 1024 BString contentType; 1025 1026 switch (fOptPostFields->GetFormType()) { 1027 case B_HTTP_FORM_MULTIPART: 1028 contentType << "multipart/form-data; boundary=" 1029 << fOptPostFields->GetMultipartBoundary() << ""; 1030 break; 1031 1032 case B_HTTP_FORM_URL_ENCODED: 1033 contentType << "application/x-www-form-urlencoded"; 1034 break; 1035 } 1036 1037 outputHeaders.AddHeader("Content-Type", contentType); 1038 outputHeaders.AddHeader("Content-Length", 1039 fOptPostFields->ContentLength()); 1040 } else if (fOptInputData != NULL 1041 && (fRequestMethod == B_HTTP_POST 1042 || fRequestMethod == B_HTTP_PUT)) { 1043 if (fOptInputDataSize >= 0) 1044 outputHeaders.AddHeader("Content-Length", fOptInputDataSize); 1045 else 1046 outputHeaders.AddHeader("Transfer-Encoding", "chunked"); 1047 } 1048 1049 // Optional headers specified by the user 1050 if (fOptHeaders != NULL) { 1051 for (int32 headerIndex = 0; headerIndex < fOptHeaders->CountHeaders(); 1052 headerIndex++) { 1053 BHttpHeader& optHeader = (*fOptHeaders)[headerIndex]; 1054 int32 replaceIndex = outputHeaders.HasHeader(optHeader.Name()); 1055 1056 // Add or replace the current option header to the 1057 // output header list 1058 if (replaceIndex == -1) 1059 outputHeaders.AddHeader(optHeader.Name(), optHeader.Value()); 1060 else 1061 outputHeaders[replaceIndex].SetValue(optHeader.Value()); 1062 } 1063 } 1064 1065 // Context cookies 1066 if (fOptSetCookies && fContext != NULL) { 1067 BString cookieString; 1068 1069 BNetworkCookieJar::UrlIterator iterator 1070 = fContext->GetCookieJar().GetUrlIterator(fUrl); 1071 const BNetworkCookie* cookie = iterator.Next(); 1072 if (cookie != NULL) { 1073 while (true) { 1074 cookieString << cookie->RawCookie(false); 1075 cookie = iterator.Next(); 1076 if (cookie == NULL) 1077 break; 1078 cookieString << "; "; 1079 } 1080 1081 outputHeaders.AddHeader("Cookie", cookieString); 1082 } 1083 } 1084 1085 // Write output headers to output stream 1086 BString headerData; 1087 1088 for (int32 headerIndex = 0; headerIndex < outputHeaders.CountHeaders(); 1089 headerIndex++) { 1090 const char* header = outputHeaders.HeaderAt(headerIndex).Header(); 1091 1092 headerData << header; 1093 headerData << "\r\n"; 1094 1095 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", header); 1096 } 1097 1098 return headerData; 1099 } 1100 1101 1102 void 1103 BHttpRequest::_SendPostData() 1104 { 1105 if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) { 1106 if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) { 1107 BString outputBuffer = fOptPostFields->RawData(); 1108 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 1109 "%s", outputBuffer.String()); 1110 fSocket->Write(outputBuffer.String(), outputBuffer.Length()); 1111 } else { 1112 for (BHttpForm::Iterator it = fOptPostFields->GetIterator(); 1113 const BHttpFormData* currentField = it.Next(); 1114 ) { 1115 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 1116 it.MultipartHeader().String()); 1117 fSocket->Write(it.MultipartHeader().String(), 1118 it.MultipartHeader().Length()); 1119 1120 switch (currentField->Type()) { 1121 default: 1122 case B_HTTPFORM_UNKNOWN: 1123 ASSERT(0); 1124 break; 1125 1126 case B_HTTPFORM_STRING: 1127 fSocket->Write(currentField->String().String(), 1128 currentField->String().Length()); 1129 break; 1130 1131 case B_HTTPFORM_FILE: 1132 { 1133 BFile upFile(currentField->File().Path(), 1134 B_READ_ONLY); 1135 char readBuffer[kHttpBufferSize]; 1136 ssize_t readSize; 1137 off_t totalSize; 1138 1139 if (upFile.GetSize(&totalSize) != B_OK) 1140 ASSERT(0); 1141 1142 readSize = upFile.Read(readBuffer, 1143 sizeof(readBuffer)); 1144 while (readSize > 0) { 1145 fSocket->Write(readBuffer, readSize); 1146 readSize = upFile.Read(readBuffer, 1147 sizeof(readBuffer)); 1148 fListener->UploadProgress(this, readSize, 1149 std::max((off_t)0, totalSize)); 1150 } 1151 1152 break; 1153 } 1154 case B_HTTPFORM_BUFFER: 1155 fSocket->Write(currentField->Buffer(), 1156 currentField->BufferSize()); 1157 break; 1158 } 1159 1160 fSocket->Write("\r\n", 2); 1161 } 1162 1163 BString footer = fOptPostFields->GetMultipartFooter(); 1164 fSocket->Write(footer.String(), footer.Length()); 1165 } 1166 } else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT) 1167 && fOptInputData != NULL) { 1168 1169 // If the input data is seekable, we rewind it for each new request. 1170 BPositionIO* seekableData 1171 = dynamic_cast<BPositionIO*>(fOptInputData); 1172 if (seekableData) 1173 seekableData->Seek(0, SEEK_SET); 1174 1175 for (;;) { 1176 char outputTempBuffer[kHttpBufferSize]; 1177 ssize_t read = fOptInputData->Read(outputTempBuffer, 1178 sizeof(outputTempBuffer)); 1179 1180 if (read <= 0) 1181 break; 1182 1183 if (fOptInputDataSize < 0) { 1184 // Input data size unknown, so we have to use chunked transfer 1185 char hexSize[18]; 1186 // The string does not need to be NULL terminated. 1187 size_t hexLength = snprintf(hexSize, sizeof(hexSize), "%lx\r\n", 1188 read); 1189 1190 fSocket->Write(hexSize, hexLength); 1191 fSocket->Write(outputTempBuffer, read); 1192 fSocket->Write("\r\n", 2); 1193 } else { 1194 fSocket->Write(outputTempBuffer, read); 1195 } 1196 } 1197 1198 if (fOptInputDataSize < 0) { 1199 // Chunked transfer terminating sequence 1200 fSocket->Write("0\r\n\r\n", 5); 1201 } 1202 } 1203 1204 } 1205 1206 1207 BHttpHeaders& 1208 BHttpRequest::_ResultHeaders() 1209 { 1210 return fResult.fHeaders; 1211 } 1212 1213 1214 void 1215 BHttpRequest::_SetResultStatusCode(int32 statusCode) 1216 { 1217 fResult.fStatusCode = statusCode; 1218 } 1219 1220 1221 BString& 1222 BHttpRequest::_ResultStatusText() 1223 { 1224 return fResult.fStatusString; 1225 } 1226 1227 1228 bool 1229 BHttpRequest::_CertificateVerificationFailed(BCertificate& certificate, 1230 const char* message) 1231 { 1232 if (fContext->HasCertificateException(certificate)) 1233 return true; 1234 1235 if (fListener != NULL 1236 && fListener->CertificateVerificationFailed(this, certificate, message)) { 1237 // User asked us to continue anyway, let's add a temporary exception for this certificate 1238 fContext->AddCertificateException(certificate); 1239 return true; 1240 } 1241 1242 return false; 1243 } 1244 1245 1246 bool 1247 BHttpRequest::_IsDefaultPort() 1248 { 1249 if (fSSL && Url().Port() == 443) 1250 return true; 1251 if (!fSSL && Url().Port() == 80) 1252 return true; 1253 return false; 1254 } 1255