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