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 421 if (fSocket != NULL) { 422 fSocket->Disconnect(); 423 // Unlock any pending connect, read or write operation. 424 } 425 return BNetworkRequest::Stop(); 426 } 427 428 429 void 430 BHttpRequest::_ResetOptions() 431 { 432 delete fOptPostFields; 433 delete fOptHeaders; 434 435 fOptFollowLocation = true; 436 fOptMaxRedirs = 8; 437 fOptReferer = ""; 438 fOptUserAgent = "Services Kit (Haiku)"; 439 fOptUsername = ""; 440 fOptPassword = ""; 441 fOptAuthMethods = B_HTTP_AUTHENTICATION_BASIC | B_HTTP_AUTHENTICATION_DIGEST 442 | B_HTTP_AUTHENTICATION_IE_DIGEST; 443 fOptHeaders = NULL; 444 fOptPostFields = NULL; 445 fOptSetCookies = true; 446 fOptDiscardData = false; 447 fOptDisableListener = false; 448 fOptAutoReferer = true; 449 } 450 451 452 status_t 453 BHttpRequest::_ProtocolLoop() 454 { 455 // Initialize the request redirection loop 456 int8 maxRedirs = fOptMaxRedirs; 457 bool newRequest; 458 459 do { 460 newRequest = false; 461 462 // Result reset 463 fHeaders.Clear(); 464 _ResultHeaders().Clear(); 465 466 BString host = fUrl.Host(); 467 int port = fSSL ? 443 : 80; 468 469 if (fUrl.HasPort()) 470 port = fUrl.Port(); 471 472 if (fContext->UseProxy()) { 473 host = fContext->GetProxyHost(); 474 port = fContext->GetProxyPort(); 475 } 476 477 status_t result = fInputBuffer.InitCheck(); 478 if (result != B_OK) 479 return result; 480 481 if (!_ResolveHostName(host, port)) { 482 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, 483 "Unable to resolve hostname (%s), aborting.", 484 fUrl.Host().String()); 485 return B_SERVER_NOT_FOUND; 486 } 487 488 status_t requestStatus = _MakeRequest(); 489 if (requestStatus != B_OK) 490 return requestStatus; 491 492 // Prepare the referer for the next request if needed 493 if (fOptAutoReferer) 494 fOptReferer = fUrl.UrlString(); 495 496 switch (StatusCodeClass(fResult.StatusCode())) { 497 case B_HTTP_STATUS_CLASS_INFORMATIONAL: 498 // Header 100:continue should have been 499 // handled in the _MakeRequest read loop 500 break; 501 502 case B_HTTP_STATUS_CLASS_SUCCESS: 503 break; 504 505 case B_HTTP_STATUS_CLASS_REDIRECTION: 506 { 507 // Redirection has been explicitly disabled 508 if (!fOptFollowLocation) 509 break; 510 511 int code = fResult.StatusCode(); 512 if (code == B_HTTP_STATUS_MOVED_PERMANENTLY 513 || code == B_HTTP_STATUS_FOUND 514 || code == B_HTTP_STATUS_SEE_OTHER 515 || code == B_HTTP_STATUS_TEMPORARY_REDIRECT) { 516 BString locationUrl = fHeaders["Location"]; 517 518 fUrl = BUrl(fUrl, locationUrl); 519 520 // 302 and 303 redirections also convert POST requests to GET 521 // (and remove the posted form data) 522 if ((code == B_HTTP_STATUS_FOUND 523 || code == B_HTTP_STATUS_SEE_OTHER) 524 && fRequestMethod == B_HTTP_POST) { 525 SetMethod(B_HTTP_GET); 526 delete fOptPostFields; 527 fOptPostFields = NULL; 528 delete fOptInputData; 529 fOptInputData = NULL; 530 fOptInputDataSize = 0; 531 } 532 533 if (--maxRedirs > 0) { 534 newRequest = true; 535 536 // Redirections may need a switch from http to https. 537 if (fUrl.Protocol() == "https") 538 fSSL = true; 539 else if (fUrl.Protocol() == "http") 540 fSSL = false; 541 542 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 543 "Following: %s\n", 544 fUrl.UrlString().String()); 545 } 546 } 547 break; 548 } 549 550 case B_HTTP_STATUS_CLASS_CLIENT_ERROR: 551 if (fResult.StatusCode() == B_HTTP_STATUS_UNAUTHORIZED) { 552 BHttpAuthentication* authentication 553 = &fContext->GetAuthentication(fUrl); 554 status_t status = B_OK; 555 556 if (authentication->Method() == B_HTTP_AUTHENTICATION_NONE) { 557 // There is no authentication context for this 558 // url yet, so let's create one. 559 BHttpAuthentication newAuth; 560 newAuth.Initialize(fHeaders["WWW-Authenticate"]); 561 fContext->AddAuthentication(fUrl, newAuth); 562 563 // Get the copy of the authentication we just added. 564 // That copy is owned by the BUrlContext and won't be 565 // deleted (unlike the temporary object above) 566 authentication = &fContext->GetAuthentication(fUrl); 567 } 568 569 newRequest = false; 570 if (fOptUsername.Length() > 0 && status == B_OK) { 571 // If we received an username and password, add them 572 // to the request. This will either change the 573 // credentials for an existing request, or set them 574 // for a new one we created just above. 575 // 576 // If this request handles HTTP redirections, it will 577 // also automatically retry connecting and send the 578 // login information. 579 authentication->SetUserName(fOptUsername); 580 authentication->SetPassword(fOptPassword); 581 newRequest = true; 582 } 583 } 584 break; 585 586 case B_HTTP_STATUS_CLASS_SERVER_ERROR: 587 break; 588 589 default: 590 case B_HTTP_STATUS_CLASS_INVALID: 591 break; 592 } 593 } while (newRequest); 594 595 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 596 "%" B_PRId32 " headers and %" B_PRIuSIZE " bytes of data remaining", 597 fHeaders.CountHeaders(), fInputBuffer.Size()); 598 599 if (fResult.StatusCode() == 404) 600 return B_RESOURCE_NOT_FOUND; 601 602 return B_OK; 603 } 604 605 606 #ifdef LIBNETAPI_DEPRECATED 607 status_t 608 BHttpRequest::_MakeRequest() 609 { 610 delete fSocket; 611 612 if (fSSL) { 613 if (fContext->UseProxy()) { 614 BNetworkAddress proxy(fContext->GetProxyHost(), fContext->GetProxyPort()); 615 fSocket = new(std::nothrow) BPrivate::CheckedProxySecureSocket(proxy, this); 616 } else 617 fSocket = new(std::nothrow) BPrivate::CheckedSecureSocket(this); 618 } else 619 fSocket = new(std::nothrow) BSocket(); 620 621 if (fSocket == NULL) 622 return B_NO_MEMORY; 623 624 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.", 625 fUrl.Authority().String(), fRemoteAddr.Port()); 626 status_t connectError = fSocket->Connect(fRemoteAddr); 627 628 if (connectError != B_OK) { 629 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s", 630 strerror(connectError)); 631 return connectError; 632 } 633 634 //! ProtocolHook:ConnectionOpened 635 if (fListener != NULL) 636 fListener->ConnectionOpened(this); 637 638 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 639 "Connection opened, sending request."); 640 641 BString requestHeaders; 642 requestHeaders.Append(_SerializeRequest()); 643 requestHeaders.Append(_SerializeHeaders()); 644 requestHeaders.Append("\r\n"); 645 fSocket->Write(requestHeaders.String(), requestHeaders.Length()); 646 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent."); 647 648 _SendPostData(); 649 fRequestStatus = kRequestInitialState; 650 651 652 653 // Receive loop 654 bool disableListener = false; 655 bool receiveEnd = false; 656 bool parseEnd = false; 657 bool readByChunks = false; 658 bool decompress = false; 659 status_t readError = B_OK; 660 ssize_t bytesRead = 0; 661 off_t bytesReceived = 0; 662 off_t bytesTotal = 0; 663 size_t previousBufferSize = 0; 664 off_t bytesUnpacked = 0; 665 char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize]; 666 ArrayDeleter<char> inputTempBufferDeleter(inputTempBuffer); 667 ssize_t inputTempSize = kHttpBufferSize; 668 ssize_t chunkSize = -1; 669 DynamicBuffer decompressorStorage; 670 BDataIO* decompressingStream; 671 ObjectDeleter<BDataIO> decompressingStreamDeleter; 672 673 while (!fQuit && !(receiveEnd && parseEnd)) { 674 if ((!receiveEnd) && (fInputBuffer.Size() == previousBufferSize)) { 675 BStackOrHeapArray<char, 4096> chunk(kHttpBufferSize); 676 bytesRead = fSocket->Read(chunk, kHttpBufferSize); 677 678 if (bytesRead < 0) { 679 readError = bytesRead; 680 break; 681 } else if (bytesRead == 0) { 682 // Check if we got the expected number of bytes. 683 // Exceptions: 684 // - If the content-length is not known (bytesTotal is 0), for 685 // example in the case of a chunked transfer, we can't know 686 // - If the request method is "HEAD" which explicitly asks the 687 // server to not send any data (only the headers) 688 if (bytesTotal > 0 && bytesReceived != bytesTotal) { 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 if (fRequestMethod == B_HTTP_HEAD 757 || fResult.StatusCode() == 204) { 758 // In the case of a HEAD request or if the server replies 759 // 204 ("no content"), we don't expect to receive anything 760 // more, and the socket will be closed. 761 receiveEnd = true; 762 } 763 } 764 } 765 766 if (fRequestStatus >= kRequestHeadersReceived) { 767 // If Transfer-Encoding is chunked, we should read a complete 768 // chunk in buffer before handling it 769 if (readByChunks) { 770 if (chunkSize >= 0) { 771 if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) { 772 // 2 more bytes to handle the closing CR+LF 773 bytesRead = chunkSize; 774 if (inputTempSize < chunkSize + 2) { 775 inputTempSize = chunkSize + 2; 776 inputTempBuffer 777 = new(std::nothrow) char[inputTempSize]; 778 inputTempBufferDeleter.SetTo(inputTempBuffer); 779 } 780 781 if (inputTempBuffer == NULL) { 782 readError = B_NO_MEMORY; 783 break; 784 } 785 786 fInputBuffer.RemoveData(inputTempBuffer, 787 chunkSize + 2); 788 chunkSize = -1; 789 } else { 790 // Not enough data, try again later 791 bytesRead = -1; 792 } 793 } else { 794 BString chunkHeader; 795 if (_GetLine(chunkHeader) == B_ERROR) { 796 chunkSize = -1; 797 bytesRead = -1; 798 } else { 799 // Format of a chunk header: 800 // <chunk size in hex>[; optional data] 801 int32 semiColonIndex = chunkHeader.FindFirst(';', 0); 802 803 // Cut-off optional data if present 804 if (semiColonIndex != -1) { 805 chunkHeader.Remove(semiColonIndex, 806 chunkHeader.Length() - semiColonIndex); 807 } 808 809 chunkSize = strtol(chunkHeader.String(), NULL, 16); 810 if (chunkSize == 0) 811 fRequestStatus = kRequestContentReceived; 812 813 bytesRead = -1; 814 } 815 } 816 817 // A chunk of 0 bytes indicates the end of the chunked transfer 818 if (bytesRead == 0) 819 receiveEnd = true; 820 } else { 821 bytesRead = fInputBuffer.Size(); 822 823 if (bytesRead > 0) { 824 if (inputTempSize < bytesRead) { 825 inputTempSize = bytesRead; 826 inputTempBuffer = new(std::nothrow) char[bytesRead]; 827 inputTempBufferDeleter.SetTo(inputTempBuffer); 828 } 829 830 if (inputTempBuffer == NULL) { 831 readError = B_NO_MEMORY; 832 break; 833 } 834 fInputBuffer.RemoveData(inputTempBuffer, bytesRead); 835 } 836 } 837 838 if (bytesRead >= 0) { 839 bytesReceived += bytesRead; 840 841 if (fListener != NULL && !disableListener) { 842 if (decompress) { 843 readError = decompressingStream->WriteExactly( 844 inputTempBuffer, bytesRead); 845 if (readError != B_OK) 846 break; 847 848 ssize_t size = decompressorStorage.Size(); 849 BStackOrHeapArray<char, 4096> buffer(size); 850 size = decompressorStorage.Read(buffer, size); 851 _NotifyDataReceived(buffer, bytesUnpacked, size, 852 bytesReceived, bytesTotal); 853 bytesUnpacked += size; 854 } else if (bytesRead > 0) { 855 _NotifyDataReceived(inputTempBuffer, 856 bytesReceived - bytesRead, bytesRead, 857 bytesReceived, bytesTotal); 858 } 859 } 860 861 if (bytesTotal >= 0 && bytesReceived >= bytesTotal) 862 receiveEnd = true; 863 864 if (decompress && receiveEnd && !disableListener) { 865 readError = decompressingStream->Flush(); 866 867 if (readError == B_BUFFER_OVERFLOW) 868 readError = B_OK; 869 870 if (readError != B_OK) 871 break; 872 873 ssize_t size = decompressorStorage.Size(); 874 BStackOrHeapArray<char, 4096> buffer(size); 875 size = decompressorStorage.Read(buffer, size); 876 if (fListener != NULL) { 877 _NotifyDataReceived(buffer, bytesUnpacked, size, 878 bytesReceived, bytesTotal); 879 bytesUnpacked += size; 880 } 881 } 882 } 883 } 884 885 parseEnd = (fInputBuffer.Size() == 0); 886 } 887 888 fSocket->Disconnect(); 889 890 if (readError != B_OK) 891 return readError; 892 893 return fQuit ? B_INTERRUPTED : B_OK; 894 } 895 896 #else 897 898 status_t 899 BHttpRequest::_MakeRequest() 900 { 901 delete fSocket; 902 903 if (fSSL) { 904 if (fContext->UseProxy()) { 905 BNetworkAddress proxy(fContext->GetProxyHost(), fContext->GetProxyPort()); 906 fSocket = new(std::nothrow) BPrivate::CheckedProxySecureSocket(proxy, this); 907 } else 908 fSocket = new(std::nothrow) BPrivate::CheckedSecureSocket(this); 909 } else 910 fSocket = new(std::nothrow) BSocket(); 911 912 if (fSocket == NULL) 913 return B_NO_MEMORY; 914 915 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.", 916 fUrl.Authority().String(), fRemoteAddr.Port()); 917 status_t connectError = fSocket->Connect(fRemoteAddr); 918 919 if (connectError != B_OK) { 920 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s", 921 strerror(connectError)); 922 return connectError; 923 } 924 925 //! ProtocolHook:ConnectionOpened 926 if (fListener != NULL) 927 fListener->ConnectionOpened(this); 928 929 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 930 "Connection opened, sending request."); 931 932 BString requestHeaders; 933 requestHeaders.Append(_SerializeRequest()); 934 requestHeaders.Append(_SerializeHeaders()); 935 requestHeaders.Append("\r\n"); 936 fSocket->Write(requestHeaders.String(), requestHeaders.Length()); 937 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent."); 938 939 _SendPostData(); 940 fRequestStatus = kRequestInitialState; 941 942 943 944 // Receive loop 945 bool disableListener = false; 946 bool receiveEnd = false; 947 bool parseEnd = false; 948 bool readByChunks = false; 949 bool decompress = false; 950 status_t readError = B_OK; 951 ssize_t bytesRead = 0; 952 off_t bytesReceived = 0; 953 off_t bytesTotal = 0; 954 size_t previousBufferSize = 0; 955 off_t bytesUnpacked = 0; 956 char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize]; 957 ArrayDeleter<char> inputTempBufferDeleter(inputTempBuffer); 958 ssize_t inputTempSize = kHttpBufferSize; 959 ssize_t chunkSize = -1; 960 DynamicBuffer decompressorStorage; 961 BDataIO* decompressingStream; 962 ObjectDeleter<BDataIO> decompressingStreamDeleter; 963 964 while (!fQuit && !(receiveEnd && parseEnd)) { 965 if ((!receiveEnd) && (fInputBuffer.Size() == previousBufferSize)) { 966 BStackOrHeapArray<char, 4096> chunk(kHttpBufferSize); 967 bytesRead = fSocket->Read(chunk, kHttpBufferSize); 968 969 if (bytesRead < 0) { 970 readError = bytesRead; 971 break; 972 } else if (bytesRead == 0) { 973 // Check if we got the expected number of bytes. 974 // Exceptions: 975 // - If the content-length is not known (bytesTotal is 0), for 976 // example in the case of a chunked transfer, we can't know 977 // - If the request method is "HEAD" which explicitly asks the 978 // server to not send any data (only the headers) 979 if (bytesTotal > 0 && bytesReceived != bytesTotal) { 980 readError = B_IO_ERROR; 981 break; 982 } 983 receiveEnd = true; 984 } 985 986 fInputBuffer.AppendData(chunk, bytesRead); 987 } else 988 bytesRead = 0; 989 990 previousBufferSize = fInputBuffer.Size(); 991 992 if (fRequestStatus < kRequestStatusReceived) { 993 _ParseStatus(); 994 995 if (fOptFollowLocation 996 && IsRedirectionStatusCode(fResult.StatusCode())) 997 disableListener = true; 998 999 if (fOptStopOnError 1000 && fResult.StatusCode() >= B_HTTP_STATUS_CLASS_CLIENT_ERROR) 1001 { 1002 fQuit = true; 1003 break; 1004 } 1005 1006 //! ProtocolHook:ResponseStarted 1007 if (fRequestStatus >= kRequestStatusReceived && fListener != NULL 1008 && !disableListener) 1009 fListener->ResponseStarted(this); 1010 } 1011 1012 if (fRequestStatus < kRequestHeadersReceived) { 1013 _ParseHeaders(); 1014 1015 if (fRequestStatus >= kRequestHeadersReceived) { 1016 _ResultHeaders() = fHeaders; 1017 1018 // Parse received cookies 1019 if (fContext != NULL) { 1020 for (int32 i = 0; i < fHeaders.CountHeaders(); i++) { 1021 if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) { 1022 fContext->GetCookieJar().AddCookie( 1023 fHeaders.HeaderAt(i).Value(), fUrl); 1024 } 1025 } 1026 } 1027 1028 //! ProtocolHook:HeadersReceived 1029 if (fListener != NULL && !disableListener) 1030 fListener->HeadersReceived(this); 1031 1032 1033 if (BString(fHeaders["Transfer-Encoding"]) == "chunked") 1034 readByChunks = true; 1035 1036 BString contentEncoding(fHeaders["Content-Encoding"]); 1037 // We don't advertise "deflate" support (see above), but we 1038 // still try to decompress it, if a server ever sends a deflate 1039 // stream despite it not being in our Accept-Encoding list. 1040 if (contentEncoding == "gzip" 1041 || contentEncoding == "deflate") { 1042 decompress = true; 1043 readError = BZlibCompressionAlgorithm() 1044 .CreateDecompressingOutputStream(&decompressorStorage, 1045 NULL, decompressingStream); 1046 if (readError != B_OK) 1047 break; 1048 1049 decompressingStreamDeleter.SetTo(decompressingStream); 1050 } 1051 1052 int32 index = fHeaders.HasHeader("Content-Length"); 1053 if (index != B_ERROR) 1054 bytesTotal = atoll(fHeaders.HeaderAt(index).Value()); 1055 else 1056 bytesTotal = -1; 1057 1058 if (fRequestMethod == B_HTTP_HEAD 1059 || fResult.StatusCode() == 204) { 1060 // In the case of a HEAD request or if the server replies 1061 // 204 ("no content"), we don't expect to receive anything 1062 // more, and the socket will be closed. 1063 receiveEnd = true; 1064 } 1065 } 1066 } 1067 1068 if (fRequestStatus >= kRequestHeadersReceived) { 1069 // If Transfer-Encoding is chunked, we should read a complete 1070 // chunk in buffer before handling it 1071 if (readByChunks) { 1072 if (chunkSize >= 0) { 1073 if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) { 1074 // 2 more bytes to handle the closing CR+LF 1075 bytesRead = chunkSize; 1076 if (inputTempSize < chunkSize + 2) { 1077 inputTempSize = chunkSize + 2; 1078 inputTempBuffer 1079 = new(std::nothrow) char[inputTempSize]; 1080 inputTempBufferDeleter.SetTo(inputTempBuffer); 1081 } 1082 1083 if (inputTempBuffer == NULL) { 1084 readError = B_NO_MEMORY; 1085 break; 1086 } 1087 1088 fInputBuffer.RemoveData(inputTempBuffer, 1089 chunkSize + 2); 1090 chunkSize = -1; 1091 } else { 1092 // Not enough data, try again later 1093 bytesRead = -1; 1094 } 1095 } else { 1096 BString chunkHeader; 1097 if (_GetLine(chunkHeader) == B_ERROR) { 1098 chunkSize = -1; 1099 bytesRead = -1; 1100 } else { 1101 // Format of a chunk header: 1102 // <chunk size in hex>[; optional data] 1103 int32 semiColonIndex = chunkHeader.FindFirst(';', 0); 1104 1105 // Cut-off optional data if present 1106 if (semiColonIndex != -1) { 1107 chunkHeader.Remove(semiColonIndex, 1108 chunkHeader.Length() - semiColonIndex); 1109 } 1110 1111 chunkSize = strtol(chunkHeader.String(), NULL, 16); 1112 if (chunkSize == 0) 1113 fRequestStatus = kRequestContentReceived; 1114 1115 bytesRead = -1; 1116 } 1117 } 1118 1119 // A chunk of 0 bytes indicates the end of the chunked transfer 1120 if (bytesRead == 0) 1121 receiveEnd = true; 1122 } else { 1123 bytesRead = fInputBuffer.Size(); 1124 1125 if (bytesRead > 0) { 1126 if (inputTempSize < bytesRead) { 1127 inputTempSize = bytesRead; 1128 inputTempBuffer = new(std::nothrow) char[bytesRead]; 1129 inputTempBufferDeleter.SetTo(inputTempBuffer); 1130 } 1131 1132 if (inputTempBuffer == NULL) { 1133 readError = B_NO_MEMORY; 1134 break; 1135 } 1136 fInputBuffer.RemoveData(inputTempBuffer, bytesRead); 1137 } 1138 } 1139 1140 if (bytesRead >= 0) { 1141 bytesReceived += bytesRead; 1142 1143 if (fOutput != NULL && !disableListener) { 1144 if (decompress) { 1145 readError = decompressingStream->WriteExactly( 1146 inputTempBuffer, bytesRead); 1147 if (readError != B_OK) 1148 break; 1149 1150 ssize_t size = decompressorStorage.Size(); 1151 BStackOrHeapArray<char, 4096> buffer(size); 1152 size = decompressorStorage.Read(buffer, size); 1153 if (size > 0) { 1154 size_t written = 0; 1155 readError = fOutput->WriteExactly(buffer, 1156 size, &written); 1157 if (fListener != NULL && written > 0) 1158 fListener->BytesWritten(this, written); 1159 if (readError != B_OK) 1160 break; 1161 bytesUnpacked += size; 1162 } 1163 } else if (bytesRead > 0) { 1164 size_t written = 0; 1165 readError = fOutput->WriteExactly(inputTempBuffer, 1166 bytesRead, &written); 1167 if (fListener != NULL && written > 0) 1168 fListener->BytesWritten(this, written); 1169 if (readError != B_OK) 1170 break; 1171 } 1172 } 1173 1174 if (fListener != NULL && !disableListener) 1175 fListener->DownloadProgress(this, bytesReceived, 1176 std::max((off_t)0, bytesTotal)); 1177 1178 if (bytesTotal >= 0 && bytesReceived >= bytesTotal) 1179 receiveEnd = true; 1180 1181 if (decompress && receiveEnd && !disableListener) { 1182 readError = decompressingStream->Flush(); 1183 1184 if (readError == B_BUFFER_OVERFLOW) 1185 readError = B_OK; 1186 1187 if (readError != B_OK) 1188 break; 1189 1190 ssize_t size = decompressorStorage.Size(); 1191 BStackOrHeapArray<char, 4096> buffer(size); 1192 size = decompressorStorage.Read(buffer, size); 1193 if (fOutput != NULL && size > 0 && !disableListener) { 1194 size_t written = 0; 1195 readError = fOutput->WriteExactly(buffer, size, 1196 &written); 1197 if (fListener != NULL && written > 0) 1198 fListener->BytesWritten(this, written); 1199 if (readError != B_OK) 1200 break; 1201 bytesUnpacked += size; 1202 } 1203 } 1204 } 1205 } 1206 1207 parseEnd = (fInputBuffer.Size() == 0); 1208 } 1209 1210 fSocket->Disconnect(); 1211 1212 if (readError != B_OK) 1213 return readError; 1214 1215 return fQuit ? B_INTERRUPTED : B_OK; 1216 } 1217 #endif // LIBNETAPI_DEPRECATED 1218 1219 1220 void 1221 BHttpRequest::_ParseStatus() 1222 { 1223 // Status line should be formatted like: HTTP/M.m SSS ... 1224 // With: M = Major version of the protocol 1225 // m = Minor version of the protocol 1226 // SSS = three-digit status code of the response 1227 // ... = additional text info 1228 BString statusLine; 1229 if (_GetLine(statusLine) == B_ERROR) 1230 return; 1231 1232 if (statusLine.CountChars() < 12) 1233 return; 1234 1235 fRequestStatus = kRequestStatusReceived; 1236 1237 BString statusCodeStr; 1238 BString statusText; 1239 statusLine.CopyInto(statusCodeStr, 9, 3); 1240 _SetResultStatusCode(atoi(statusCodeStr.String())); 1241 1242 statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13); 1243 1244 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)", 1245 atoi(statusCodeStr.String()), _ResultStatusText().String()); 1246 } 1247 1248 1249 void 1250 BHttpRequest::_ParseHeaders() 1251 { 1252 BString currentHeader; 1253 while (_GetLine(currentHeader) != B_ERROR) { 1254 // An empty line means the end of the header section 1255 if (currentHeader.Length() == 0) { 1256 fRequestStatus = kRequestHeadersReceived; 1257 return; 1258 } 1259 1260 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s", 1261 currentHeader.String()); 1262 fHeaders.AddHeader(currentHeader.String()); 1263 } 1264 } 1265 1266 1267 BString 1268 BHttpRequest::_SerializeRequest() 1269 { 1270 BString request(fRequestMethod); 1271 request << ' '; 1272 1273 if (fContext->UseProxy()) { 1274 // When there is a proxy, the request must include the host and port so 1275 // the proxy knows where to send the request. 1276 request << Url().Protocol() << "://" << Url().Host(); 1277 if (Url().HasPort()) 1278 request << ':' << Url().Port(); 1279 } 1280 1281 if (Url().HasPath() && Url().Path().Length() > 0) 1282 request << Url().Path(); 1283 else 1284 request << '/'; 1285 1286 if (Url().HasRequest()) 1287 request << '?' << Url().Request(); 1288 1289 switch (fHttpVersion) { 1290 case B_HTTP_11: 1291 request << " HTTP/1.1\r\n"; 1292 break; 1293 1294 default: 1295 case B_HTTP_10: 1296 request << " HTTP/1.0\r\n"; 1297 break; 1298 } 1299 1300 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", request.String()); 1301 1302 return request; 1303 } 1304 1305 1306 BString 1307 BHttpRequest::_SerializeHeaders() 1308 { 1309 BHttpHeaders outputHeaders; 1310 1311 // HTTP 1.1 additional headers 1312 if (fHttpVersion == B_HTTP_11) { 1313 BString host = Url().Host(); 1314 if (Url().HasPort() && !_IsDefaultPort()) 1315 host << ':' << Url().Port(); 1316 1317 outputHeaders.AddHeader("Host", host); 1318 1319 outputHeaders.AddHeader("Accept", "*/*"); 1320 outputHeaders.AddHeader("Accept-Encoding", "gzip"); 1321 // Allows the server to compress data using the "gzip" format. 1322 // "deflate" is not supported, because there are two interpretations 1323 // of what it means (the RFC and Microsoft products), and we don't 1324 // want to handle this. Very few websites support only deflate, 1325 // and most of them will send gzip, or at worst, uncompressed data. 1326 1327 outputHeaders.AddHeader("Connection", "close"); 1328 // Let the remote server close the connection after response since 1329 // we don't handle multiple request on a single connection 1330 } 1331 1332 // Classic HTTP headers 1333 if (fOptUserAgent.CountChars() > 0) 1334 outputHeaders.AddHeader("User-Agent", fOptUserAgent.String()); 1335 1336 if (fOptReferer.CountChars() > 0) 1337 outputHeaders.AddHeader("Referer", fOptReferer.String()); 1338 1339 // Optional range requests headers 1340 if (fOptRangeStart != -1 || fOptRangeEnd != -1) { 1341 if (fOptRangeStart == -1) 1342 fOptRangeStart = 0; 1343 BString range; 1344 if (fOptRangeEnd != -1) { 1345 range.SetToFormat("bytes=%" B_PRIdOFF "-%" B_PRIdOFF, 1346 fOptRangeStart, fOptRangeEnd); 1347 } else { 1348 range.SetToFormat("bytes=%" B_PRIdOFF "-", fOptRangeStart); 1349 } 1350 outputHeaders.AddHeader("Range", range.String()); 1351 } 1352 1353 // Authentication 1354 if (fContext != NULL) { 1355 BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl); 1356 if (authentication.Method() != B_HTTP_AUTHENTICATION_NONE) { 1357 if (fOptUsername.Length() > 0) { 1358 authentication.SetUserName(fOptUsername); 1359 authentication.SetPassword(fOptPassword); 1360 } 1361 1362 BString request(fRequestMethod); 1363 outputHeaders.AddHeader("Authorization", 1364 authentication.Authorization(fUrl, request)); 1365 } 1366 } 1367 1368 // Required headers for POST data 1369 if (fOptPostFields != NULL && fRequestMethod == B_HTTP_POST) { 1370 BString contentType; 1371 1372 switch (fOptPostFields->GetFormType()) { 1373 case B_HTTP_FORM_MULTIPART: 1374 contentType << "multipart/form-data; boundary=" 1375 << fOptPostFields->GetMultipartBoundary() << ""; 1376 break; 1377 1378 case B_HTTP_FORM_URL_ENCODED: 1379 contentType << "application/x-www-form-urlencoded"; 1380 break; 1381 } 1382 1383 outputHeaders.AddHeader("Content-Type", contentType); 1384 outputHeaders.AddHeader("Content-Length", 1385 fOptPostFields->ContentLength()); 1386 } else if (fOptInputData != NULL 1387 && (fRequestMethod == B_HTTP_POST 1388 || fRequestMethod == B_HTTP_PUT)) { 1389 if (fOptInputDataSize >= 0) 1390 outputHeaders.AddHeader("Content-Length", fOptInputDataSize); 1391 else 1392 outputHeaders.AddHeader("Transfer-Encoding", "chunked"); 1393 } 1394 1395 // Optional headers specified by the user 1396 if (fOptHeaders != NULL) { 1397 for (int32 headerIndex = 0; headerIndex < fOptHeaders->CountHeaders(); 1398 headerIndex++) { 1399 BHttpHeader& optHeader = (*fOptHeaders)[headerIndex]; 1400 int32 replaceIndex = outputHeaders.HasHeader(optHeader.Name()); 1401 1402 // Add or replace the current option header to the 1403 // output header list 1404 if (replaceIndex == -1) 1405 outputHeaders.AddHeader(optHeader.Name(), optHeader.Value()); 1406 else 1407 outputHeaders[replaceIndex].SetValue(optHeader.Value()); 1408 } 1409 } 1410 1411 // Context cookies 1412 if (fOptSetCookies && fContext != NULL) { 1413 BString cookieString; 1414 1415 BNetworkCookieJar::UrlIterator iterator 1416 = fContext->GetCookieJar().GetUrlIterator(fUrl); 1417 const BNetworkCookie* cookie = iterator.Next(); 1418 if (cookie != NULL) { 1419 while (true) { 1420 cookieString << cookie->RawCookie(false); 1421 cookie = iterator.Next(); 1422 if (cookie == NULL) 1423 break; 1424 cookieString << "; "; 1425 } 1426 1427 outputHeaders.AddHeader("Cookie", cookieString); 1428 } 1429 } 1430 1431 // Write output headers to output stream 1432 BString headerData; 1433 1434 for (int32 headerIndex = 0; headerIndex < outputHeaders.CountHeaders(); 1435 headerIndex++) { 1436 const char* header = outputHeaders.HeaderAt(headerIndex).Header(); 1437 1438 headerData << header; 1439 headerData << "\r\n"; 1440 1441 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", header); 1442 } 1443 1444 return headerData; 1445 } 1446 1447 1448 void 1449 BHttpRequest::_SendPostData() 1450 { 1451 if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) { 1452 if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) { 1453 BString outputBuffer = fOptPostFields->RawData(); 1454 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 1455 "%s", outputBuffer.String()); 1456 fSocket->Write(outputBuffer.String(), outputBuffer.Length()); 1457 } else { 1458 for (BHttpForm::Iterator it = fOptPostFields->GetIterator(); 1459 const BHttpFormData* currentField = it.Next(); 1460 ) { 1461 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 1462 it.MultipartHeader().String()); 1463 fSocket->Write(it.MultipartHeader().String(), 1464 it.MultipartHeader().Length()); 1465 1466 switch (currentField->Type()) { 1467 default: 1468 case B_HTTPFORM_UNKNOWN: 1469 ASSERT(0); 1470 break; 1471 1472 case B_HTTPFORM_STRING: 1473 fSocket->Write(currentField->String().String(), 1474 currentField->String().Length()); 1475 break; 1476 1477 case B_HTTPFORM_FILE: 1478 { 1479 BFile upFile(currentField->File().Path(), 1480 B_READ_ONLY); 1481 char readBuffer[kHttpBufferSize]; 1482 ssize_t readSize; 1483 off_t totalSize; 1484 1485 if (upFile.GetSize(&totalSize) != B_OK) 1486 ASSERT(0); 1487 1488 readSize = upFile.Read(readBuffer, 1489 sizeof(readBuffer)); 1490 while (readSize > 0) { 1491 fSocket->Write(readBuffer, readSize); 1492 readSize = upFile.Read(readBuffer, 1493 sizeof(readBuffer)); 1494 fListener->UploadProgress(this, readSize, 1495 std::max((off_t)0, totalSize)); 1496 } 1497 1498 break; 1499 } 1500 case B_HTTPFORM_BUFFER: 1501 fSocket->Write(currentField->Buffer(), 1502 currentField->BufferSize()); 1503 break; 1504 } 1505 1506 fSocket->Write("\r\n", 2); 1507 } 1508 1509 BString footer = fOptPostFields->GetMultipartFooter(); 1510 fSocket->Write(footer.String(), footer.Length()); 1511 } 1512 } else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT) 1513 && fOptInputData != NULL) { 1514 1515 // If the input data is seekable, we rewind it for each new request. 1516 BPositionIO* seekableData 1517 = dynamic_cast<BPositionIO*>(fOptInputData); 1518 if (seekableData) 1519 seekableData->Seek(0, SEEK_SET); 1520 1521 for (;;) { 1522 char outputTempBuffer[kHttpBufferSize]; 1523 ssize_t read = fOptInputData->Read(outputTempBuffer, 1524 sizeof(outputTempBuffer)); 1525 1526 if (read <= 0) 1527 break; 1528 1529 if (fOptInputDataSize < 0) { 1530 // Input data size unknown, so we have to use chunked transfer 1531 char hexSize[18]; 1532 // The string does not need to be NULL terminated. 1533 size_t hexLength = snprintf(hexSize, sizeof(hexSize), "%lx\r\n", 1534 read); 1535 1536 fSocket->Write(hexSize, hexLength); 1537 fSocket->Write(outputTempBuffer, read); 1538 fSocket->Write("\r\n", 2); 1539 } else { 1540 fSocket->Write(outputTempBuffer, read); 1541 } 1542 } 1543 1544 if (fOptInputDataSize < 0) { 1545 // Chunked transfer terminating sequence 1546 fSocket->Write("0\r\n\r\n", 5); 1547 } 1548 } 1549 1550 } 1551 1552 1553 BHttpHeaders& 1554 BHttpRequest::_ResultHeaders() 1555 { 1556 return fResult.fHeaders; 1557 } 1558 1559 1560 void 1561 BHttpRequest::_SetResultStatusCode(int32 statusCode) 1562 { 1563 fResult.fStatusCode = statusCode; 1564 } 1565 1566 1567 BString& 1568 BHttpRequest::_ResultStatusText() 1569 { 1570 return fResult.fStatusString; 1571 } 1572 1573 1574 bool 1575 BHttpRequest::_CertificateVerificationFailed(BCertificate& certificate, 1576 const char* message) 1577 { 1578 if (fContext->HasCertificateException(certificate)) 1579 return true; 1580 1581 if (fListener != NULL 1582 && fListener->CertificateVerificationFailed(this, certificate, message)) { 1583 // User asked us to continue anyway, let's add a temporary exception for this certificate 1584 fContext->AddCertificateException(certificate); 1585 return true; 1586 } 1587 1588 return false; 1589 } 1590 1591 1592 bool 1593 BHttpRequest::_IsDefaultPort() 1594 { 1595 if (fSSL && Url().Port() == 443) 1596 return true; 1597 if (!fSSL && Url().Port() == 80) 1598 return true; 1599 return false; 1600 } 1601 1602 1603 #ifdef LIBNETAPI_DEPRECATED 1604 void 1605 BHttpRequest::_NotifyDataReceived(const char* data, off_t pos, ssize_t size, 1606 off_t bytesReceived, ssize_t bytesTotal) 1607 { 1608 if (fListener == NULL || size <= 0) 1609 return; 1610 if (fOptRangeStart > 0) { 1611 pos += fOptRangeStart; 1612 // bytesReceived and bytesTotal refer to the requested range, 1613 // so that should technically not be adjusted for the range start. 1614 // For displaying progress to the user, this is not ideal, though. 1615 // But only for the case where we request the remainder of a file. 1616 // Range requests can also be used to request any portion of a 1617 // resource, so not modifying them is technically more correct. 1618 // We can use a little trick, though: We know when the remainder 1619 // is requested, because then fOptRangeEnd is -1. 1620 if (fOptRangeEnd == -1) { 1621 bytesReceived += fOptRangeStart; 1622 if (bytesTotal > 0) 1623 bytesTotal += fOptRangeStart; 1624 } 1625 } 1626 fListener->DataReceived(this, data, pos, size); 1627 fListener->DownloadProgress(this, bytesReceived, 1628 std::max((ssize_t)0, bytesTotal)); 1629 } 1630 #endif 1631