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 readError = B_IO_ERROR; 689 break; 690 } 691 receiveEnd = true; 692 } 693 694 fInputBuffer.AppendData(chunk, bytesRead); 695 } else 696 bytesRead = 0; 697 698 previousBufferSize = fInputBuffer.Size(); 699 700 if (fRequestStatus < kRequestStatusReceived) { 701 _ParseStatus(); 702 703 //! ProtocolHook:ResponseStarted 704 if (fRequestStatus >= kRequestStatusReceived && fListener != NULL 705 && !disableListener) 706 fListener->ResponseStarted(this); 707 } 708 709 if (fRequestStatus < kRequestHeadersReceived) { 710 _ParseHeaders(); 711 712 if (fRequestStatus >= kRequestHeadersReceived) { 713 _ResultHeaders() = fHeaders; 714 715 // Parse received cookies 716 if (fContext != NULL) { 717 for (int32 i = 0; i < fHeaders.CountHeaders(); i++) { 718 if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) { 719 fContext->GetCookieJar().AddCookie( 720 fHeaders.HeaderAt(i).Value(), fUrl); 721 } 722 } 723 } 724 725 //! ProtocolHook:HeadersReceived 726 if (fListener != NULL && !disableListener) 727 fListener->HeadersReceived(this, fResult); 728 729 730 if (BString(fHeaders["Transfer-Encoding"]) == "chunked") 731 readByChunks = true; 732 733 BString contentEncoding(fHeaders["Content-Encoding"]); 734 // We don't advertise "deflate" support (see above), but we 735 // still try to decompress it, if a server ever sends a deflate 736 // stream despite it not being in our Accept-Encoding list. 737 if (contentEncoding == "gzip" 738 || contentEncoding == "deflate") { 739 decompress = true; 740 readError = BZlibCompressionAlgorithm() 741 .CreateDecompressingOutputStream(&decompressorStorage, 742 NULL, decompressingStream); 743 if (readError != B_OK) 744 break; 745 746 decompressingStreamDeleter.SetTo(decompressingStream); 747 } 748 749 int32 index = fHeaders.HasHeader("Content-Length"); 750 if (index != B_ERROR) 751 bytesTotal = atoll(fHeaders.HeaderAt(index).Value()); 752 else 753 bytesTotal = -1; 754 755 if (fRequestMethod == B_HTTP_HEAD 756 || fResult.StatusCode() == 204) { 757 // In the case of a HEAD request or if the server replies 758 // 204 ("no content"), we don't expect to receive anything 759 // more, and the socket will be closed. 760 receiveEnd = true; 761 } 762 } 763 } 764 765 if (fRequestStatus >= kRequestHeadersReceived) { 766 // If Transfer-Encoding is chunked, we should read a complete 767 // chunk in buffer before handling it 768 if (readByChunks) { 769 if (chunkSize >= 0) { 770 if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) { 771 // 2 more bytes to handle the closing CR+LF 772 bytesRead = chunkSize; 773 if (inputTempSize < chunkSize + 2) { 774 inputTempSize = chunkSize + 2; 775 inputTempBuffer 776 = new(std::nothrow) char[inputTempSize]; 777 inputTempBufferDeleter.SetTo(inputTempBuffer); 778 } 779 780 if (inputTempBuffer == NULL) { 781 readError = B_NO_MEMORY; 782 break; 783 } 784 785 fInputBuffer.RemoveData(inputTempBuffer, 786 chunkSize + 2); 787 chunkSize = -1; 788 } else { 789 // Not enough data, try again later 790 bytesRead = -1; 791 } 792 } else { 793 BString chunkHeader; 794 if (_GetLine(chunkHeader) == B_ERROR) { 795 chunkSize = -1; 796 bytesRead = -1; 797 } else { 798 // Format of a chunk header: 799 // <chunk size in hex>[; optional data] 800 int32 semiColonIndex = chunkHeader.FindFirst(';', 0); 801 802 // Cut-off optional data if present 803 if (semiColonIndex != -1) { 804 chunkHeader.Remove(semiColonIndex, 805 chunkHeader.Length() - semiColonIndex); 806 } 807 808 chunkSize = strtol(chunkHeader.String(), NULL, 16); 809 if (chunkSize == 0) 810 fRequestStatus = kRequestContentReceived; 811 812 bytesRead = -1; 813 } 814 } 815 816 // A chunk of 0 bytes indicates the end of the chunked transfer 817 if (bytesRead == 0) 818 receiveEnd = true; 819 } else { 820 bytesRead = fInputBuffer.Size(); 821 822 if (bytesRead > 0) { 823 if (inputTempSize < bytesRead) { 824 inputTempSize = bytesRead; 825 inputTempBuffer = new(std::nothrow) char[bytesRead]; 826 inputTempBufferDeleter.SetTo(inputTempBuffer); 827 } 828 829 if (inputTempBuffer == NULL) { 830 readError = B_NO_MEMORY; 831 break; 832 } 833 fInputBuffer.RemoveData(inputTempBuffer, bytesRead); 834 } 835 } 836 837 if (bytesRead >= 0) { 838 bytesReceived += bytesRead; 839 840 if (fListener != NULL && !disableListener) { 841 if (decompress) { 842 readError = decompressingStream->WriteExactly( 843 inputTempBuffer, bytesRead); 844 if (readError != B_OK) 845 break; 846 847 ssize_t size = decompressorStorage.Size(); 848 BStackOrHeapArray<char, 4096> buffer(size); 849 size = decompressorStorage.Read(buffer, size); 850 _NotifyDataReceived(buffer, bytesUnpacked, size, 851 bytesReceived, bytesTotal); 852 bytesUnpacked += size; 853 } else if (bytesRead > 0) { 854 _NotifyDataReceived(inputTempBuffer, 855 bytesReceived - bytesRead, bytesRead, 856 bytesReceived, bytesTotal); 857 } 858 } 859 860 if (bytesTotal >= 0 && bytesReceived >= bytesTotal) 861 receiveEnd = true; 862 863 if (decompress && receiveEnd && !disableListener) { 864 readError = decompressingStream->Flush(); 865 866 if (readError == B_BUFFER_OVERFLOW) 867 readError = B_OK; 868 869 if (readError != B_OK) 870 break; 871 872 ssize_t size = decompressorStorage.Size(); 873 BStackOrHeapArray<char, 4096> buffer(size); 874 size = decompressorStorage.Read(buffer, size); 875 if (fListener != NULL) { 876 _NotifyDataReceived(buffer, bytesUnpacked, size, 877 bytesReceived, bytesTotal); 878 bytesUnpacked += size; 879 } 880 } 881 } 882 } 883 884 parseEnd = (fInputBuffer.Size() == 0); 885 } 886 887 fSocket->Disconnect(); 888 889 if (readError != B_OK) 890 return readError; 891 892 return fQuit ? B_INTERRUPTED : B_OK; 893 } 894 895 #else 896 897 status_t 898 BHttpRequest::_MakeRequest() 899 { 900 delete fSocket; 901 902 if (fSSL) { 903 if (fContext->UseProxy()) { 904 BNetworkAddress proxy(fContext->GetProxyHost(), fContext->GetProxyPort()); 905 fSocket = new(std::nothrow) BPrivate::CheckedProxySecureSocket(proxy, this); 906 } else 907 fSocket = new(std::nothrow) BPrivate::CheckedSecureSocket(this); 908 } else 909 fSocket = new(std::nothrow) BSocket(); 910 911 if (fSocket == NULL) 912 return B_NO_MEMORY; 913 914 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.", 915 fUrl.Authority().String(), fRemoteAddr.Port()); 916 status_t connectError = fSocket->Connect(fRemoteAddr); 917 918 if (connectError != B_OK) { 919 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s", 920 strerror(connectError)); 921 return connectError; 922 } 923 924 //! ProtocolHook:ConnectionOpened 925 if (fListener != NULL) 926 fListener->ConnectionOpened(this); 927 928 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 929 "Connection opened, sending request."); 930 931 BString requestHeaders; 932 requestHeaders.Append(_SerializeRequest()); 933 requestHeaders.Append(_SerializeHeaders()); 934 requestHeaders.Append("\r\n"); 935 fSocket->Write(requestHeaders.String(), requestHeaders.Length()); 936 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent."); 937 938 _SendPostData(); 939 fRequestStatus = kRequestInitialState; 940 941 942 943 // Receive loop 944 bool disableListener = false; 945 bool receiveEnd = false; 946 bool parseEnd = false; 947 bool readByChunks = false; 948 bool decompress = false; 949 status_t readError = B_OK; 950 ssize_t bytesRead = 0; 951 off_t bytesReceived = 0; 952 off_t bytesTotal = 0; 953 size_t previousBufferSize = 0; 954 off_t bytesUnpacked = 0; 955 char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize]; 956 ArrayDeleter<char> inputTempBufferDeleter(inputTempBuffer); 957 ssize_t inputTempSize = kHttpBufferSize; 958 ssize_t chunkSize = -1; 959 DynamicBuffer decompressorStorage; 960 BDataIO* decompressingStream; 961 ObjectDeleter<BDataIO> decompressingStreamDeleter; 962 963 while (!fQuit && !(receiveEnd && parseEnd)) { 964 if ((!receiveEnd) && (fInputBuffer.Size() == previousBufferSize)) { 965 BStackOrHeapArray<char, 4096> chunk(kHttpBufferSize); 966 bytesRead = fSocket->Read(chunk, kHttpBufferSize); 967 968 if (bytesRead < 0) { 969 readError = bytesRead; 970 break; 971 } else if (bytesRead == 0) { 972 // Check if we got the expected number of bytes. 973 // Exceptions: 974 // - If the content-length is not known (bytesTotal is 0), for 975 // example in the case of a chunked transfer, we can't know 976 // - If the request method is "HEAD" which explicitly asks the 977 // server to not send any data (only the headers) 978 if (bytesTotal > 0 && bytesReceived != bytesTotal) { 979 readError = B_IO_ERROR; 980 break; 981 } 982 receiveEnd = true; 983 } 984 985 fInputBuffer.AppendData(chunk, bytesRead); 986 } else 987 bytesRead = 0; 988 989 previousBufferSize = fInputBuffer.Size(); 990 991 if (fRequestStatus < kRequestStatusReceived) { 992 _ParseStatus(); 993 994 if (fOptFollowLocation 995 && IsRedirectionStatusCode(fResult.StatusCode())) 996 disableListener = true; 997 998 if (fOptStopOnError 999 && fResult.StatusCode() >= B_HTTP_STATUS_CLASS_CLIENT_ERROR) 1000 { 1001 fQuit = true; 1002 break; 1003 } 1004 1005 //! ProtocolHook:ResponseStarted 1006 if (fRequestStatus >= kRequestStatusReceived && fListener != NULL 1007 && !disableListener) 1008 fListener->ResponseStarted(this); 1009 } 1010 1011 if (fRequestStatus < kRequestHeadersReceived) { 1012 _ParseHeaders(); 1013 1014 if (fRequestStatus >= kRequestHeadersReceived) { 1015 _ResultHeaders() = fHeaders; 1016 1017 // Parse received cookies 1018 if (fContext != NULL) { 1019 for (int32 i = 0; i < fHeaders.CountHeaders(); i++) { 1020 if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) { 1021 fContext->GetCookieJar().AddCookie( 1022 fHeaders.HeaderAt(i).Value(), fUrl); 1023 } 1024 } 1025 } 1026 1027 //! ProtocolHook:HeadersReceived 1028 if (fListener != NULL && !disableListener) 1029 fListener->HeadersReceived(this); 1030 1031 1032 if (BString(fHeaders["Transfer-Encoding"]) == "chunked") 1033 readByChunks = true; 1034 1035 BString contentEncoding(fHeaders["Content-Encoding"]); 1036 // We don't advertise "deflate" support (see above), but we 1037 // still try to decompress it, if a server ever sends a deflate 1038 // stream despite it not being in our Accept-Encoding list. 1039 if (contentEncoding == "gzip" 1040 || contentEncoding == "deflate") { 1041 decompress = true; 1042 readError = BZlibCompressionAlgorithm() 1043 .CreateDecompressingOutputStream(&decompressorStorage, 1044 NULL, decompressingStream); 1045 if (readError != B_OK) 1046 break; 1047 1048 decompressingStreamDeleter.SetTo(decompressingStream); 1049 } 1050 1051 int32 index = fHeaders.HasHeader("Content-Length"); 1052 if (index != B_ERROR) 1053 bytesTotal = atoll(fHeaders.HeaderAt(index).Value()); 1054 else 1055 bytesTotal = -1; 1056 1057 if (fRequestMethod == B_HTTP_HEAD 1058 || fResult.StatusCode() == 204) { 1059 // In the case of a HEAD request or if the server replies 1060 // 204 ("no content"), we don't expect to receive anything 1061 // more, and the socket will be closed. 1062 receiveEnd = true; 1063 } 1064 } 1065 } 1066 1067 if (fRequestStatus >= kRequestHeadersReceived) { 1068 // If Transfer-Encoding is chunked, we should read a complete 1069 // chunk in buffer before handling it 1070 if (readByChunks) { 1071 if (chunkSize >= 0) { 1072 if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) { 1073 // 2 more bytes to handle the closing CR+LF 1074 bytesRead = chunkSize; 1075 if (inputTempSize < chunkSize + 2) { 1076 inputTempSize = chunkSize + 2; 1077 inputTempBuffer 1078 = new(std::nothrow) char[inputTempSize]; 1079 inputTempBufferDeleter.SetTo(inputTempBuffer); 1080 } 1081 1082 if (inputTempBuffer == NULL) { 1083 readError = B_NO_MEMORY; 1084 break; 1085 } 1086 1087 fInputBuffer.RemoveData(inputTempBuffer, 1088 chunkSize + 2); 1089 chunkSize = -1; 1090 } else { 1091 // Not enough data, try again later 1092 bytesRead = -1; 1093 } 1094 } else { 1095 BString chunkHeader; 1096 if (_GetLine(chunkHeader) == B_ERROR) { 1097 chunkSize = -1; 1098 bytesRead = -1; 1099 } else { 1100 // Format of a chunk header: 1101 // <chunk size in hex>[; optional data] 1102 int32 semiColonIndex = chunkHeader.FindFirst(';', 0); 1103 1104 // Cut-off optional data if present 1105 if (semiColonIndex != -1) { 1106 chunkHeader.Remove(semiColonIndex, 1107 chunkHeader.Length() - semiColonIndex); 1108 } 1109 1110 chunkSize = strtol(chunkHeader.String(), NULL, 16); 1111 if (chunkSize == 0) 1112 fRequestStatus = kRequestContentReceived; 1113 1114 bytesRead = -1; 1115 } 1116 } 1117 1118 // A chunk of 0 bytes indicates the end of the chunked transfer 1119 if (bytesRead == 0) 1120 receiveEnd = true; 1121 } else { 1122 bytesRead = fInputBuffer.Size(); 1123 1124 if (bytesRead > 0) { 1125 if (inputTempSize < bytesRead) { 1126 inputTempSize = bytesRead; 1127 inputTempBuffer = new(std::nothrow) char[bytesRead]; 1128 inputTempBufferDeleter.SetTo(inputTempBuffer); 1129 } 1130 1131 if (inputTempBuffer == NULL) { 1132 readError = B_NO_MEMORY; 1133 break; 1134 } 1135 fInputBuffer.RemoveData(inputTempBuffer, bytesRead); 1136 } 1137 } 1138 1139 if (bytesRead >= 0) { 1140 bytesReceived += bytesRead; 1141 1142 if (fOutput != NULL && !disableListener) { 1143 if (decompress) { 1144 readError = decompressingStream->WriteExactly( 1145 inputTempBuffer, bytesRead); 1146 if (readError != B_OK) 1147 break; 1148 1149 ssize_t size = decompressorStorage.Size(); 1150 BStackOrHeapArray<char, 4096> buffer(size); 1151 size = decompressorStorage.Read(buffer, size); 1152 if (size > 0) { 1153 size_t written = 0; 1154 readError = fOutput->WriteExactly(buffer, 1155 size, &written); 1156 if (fListener != NULL && written > 0) 1157 fListener->BytesWritten(this, written); 1158 if (readError != B_OK) 1159 break; 1160 bytesUnpacked += size; 1161 } 1162 } else if (bytesRead > 0) { 1163 size_t written = 0; 1164 readError = fOutput->WriteExactly(inputTempBuffer, 1165 bytesRead, &written); 1166 if (fListener != NULL && written > 0) 1167 fListener->BytesWritten(this, written); 1168 if (readError != B_OK) 1169 break; 1170 } 1171 } 1172 1173 if (fListener != NULL && !disableListener) 1174 fListener->DownloadProgress(this, bytesReceived, 1175 std::max((off_t)0, bytesTotal)); 1176 1177 if (bytesTotal >= 0 && bytesReceived >= bytesTotal) 1178 receiveEnd = true; 1179 1180 if (decompress && receiveEnd && !disableListener) { 1181 readError = decompressingStream->Flush(); 1182 1183 if (readError == B_BUFFER_OVERFLOW) 1184 readError = B_OK; 1185 1186 if (readError != B_OK) 1187 break; 1188 1189 ssize_t size = decompressorStorage.Size(); 1190 BStackOrHeapArray<char, 4096> buffer(size); 1191 size = decompressorStorage.Read(buffer, size); 1192 if (fOutput != NULL && size > 0 && !disableListener) { 1193 size_t written = 0; 1194 readError = fOutput->WriteExactly(buffer, size, 1195 &written); 1196 if (fListener != NULL && written > 0) 1197 fListener->BytesWritten(this, written); 1198 if (readError != B_OK) 1199 break; 1200 bytesUnpacked += size; 1201 } 1202 } 1203 } 1204 } 1205 1206 parseEnd = (fInputBuffer.Size() == 0); 1207 } 1208 1209 fSocket->Disconnect(); 1210 1211 if (readError != B_OK) 1212 return readError; 1213 1214 return fQuit ? B_INTERRUPTED : B_OK; 1215 } 1216 #endif // LIBNETAPI_DEPRECATED 1217 1218 1219 void 1220 BHttpRequest::_ParseStatus() 1221 { 1222 // Status line should be formatted like: HTTP/M.m SSS ... 1223 // With: M = Major version of the protocol 1224 // m = Minor version of the protocol 1225 // SSS = three-digit status code of the response 1226 // ... = additional text info 1227 BString statusLine; 1228 if (_GetLine(statusLine) == B_ERROR) 1229 return; 1230 1231 if (statusLine.CountChars() < 12) 1232 return; 1233 1234 fRequestStatus = kRequestStatusReceived; 1235 1236 BString statusCodeStr; 1237 BString statusText; 1238 statusLine.CopyInto(statusCodeStr, 9, 3); 1239 _SetResultStatusCode(atoi(statusCodeStr.String())); 1240 1241 statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13); 1242 1243 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)", 1244 atoi(statusCodeStr.String()), _ResultStatusText().String()); 1245 } 1246 1247 1248 void 1249 BHttpRequest::_ParseHeaders() 1250 { 1251 BString currentHeader; 1252 while (_GetLine(currentHeader) != B_ERROR) { 1253 // An empty line means the end of the header section 1254 if (currentHeader.Length() == 0) { 1255 fRequestStatus = kRequestHeadersReceived; 1256 return; 1257 } 1258 1259 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s", 1260 currentHeader.String()); 1261 fHeaders.AddHeader(currentHeader.String()); 1262 } 1263 } 1264 1265 1266 BString 1267 BHttpRequest::_SerializeRequest() 1268 { 1269 BString request(fRequestMethod); 1270 request << ' '; 1271 1272 if (fContext->UseProxy()) { 1273 // When there is a proxy, the request must include the host and port so 1274 // the proxy knows where to send the request. 1275 request << Url().Protocol() << "://" << Url().Host(); 1276 if (Url().HasPort()) 1277 request << ':' << Url().Port(); 1278 } 1279 1280 if (Url().HasPath() && Url().Path().Length() > 0) 1281 request << Url().Path(); 1282 else 1283 request << '/'; 1284 1285 if (Url().HasRequest()) 1286 request << '?' << Url().Request(); 1287 1288 switch (fHttpVersion) { 1289 case B_HTTP_11: 1290 request << " HTTP/1.1\r\n"; 1291 break; 1292 1293 default: 1294 case B_HTTP_10: 1295 request << " HTTP/1.0\r\n"; 1296 break; 1297 } 1298 1299 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", request.String()); 1300 1301 return request; 1302 } 1303 1304 1305 BString 1306 BHttpRequest::_SerializeHeaders() 1307 { 1308 BHttpHeaders outputHeaders; 1309 1310 // HTTP 1.1 additional headers 1311 if (fHttpVersion == B_HTTP_11) { 1312 BString host = Url().Host(); 1313 if (Url().HasPort() && !_IsDefaultPort()) 1314 host << ':' << Url().Port(); 1315 1316 outputHeaders.AddHeader("Host", host); 1317 1318 outputHeaders.AddHeader("Accept", "*/*"); 1319 outputHeaders.AddHeader("Accept-Encoding", "gzip"); 1320 // Allows the server to compress data using the "gzip" format. 1321 // "deflate" is not supported, because there are two interpretations 1322 // of what it means (the RFC and Microsoft products), and we don't 1323 // want to handle this. Very few websites support only deflate, 1324 // and most of them will send gzip, or at worst, uncompressed data. 1325 1326 outputHeaders.AddHeader("Connection", "close"); 1327 // Let the remote server close the connection after response since 1328 // we don't handle multiple request on a single connection 1329 } 1330 1331 // Classic HTTP headers 1332 if (fOptUserAgent.CountChars() > 0) 1333 outputHeaders.AddHeader("User-Agent", fOptUserAgent.String()); 1334 1335 if (fOptReferer.CountChars() > 0) 1336 outputHeaders.AddHeader("Referer", fOptReferer.String()); 1337 1338 // Optional range requests headers 1339 if (fOptRangeStart != -1 || fOptRangeEnd != -1) { 1340 if (fOptRangeStart == -1) 1341 fOptRangeStart = 0; 1342 BString range; 1343 if (fOptRangeEnd != -1) { 1344 range.SetToFormat("bytes=%" B_PRIdOFF "-%" B_PRIdOFF, 1345 fOptRangeStart, fOptRangeEnd); 1346 } else { 1347 range.SetToFormat("bytes=%" B_PRIdOFF "-", fOptRangeStart); 1348 } 1349 outputHeaders.AddHeader("Range", range.String()); 1350 } 1351 1352 // Authentication 1353 if (fContext != NULL) { 1354 BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl); 1355 if (authentication.Method() != B_HTTP_AUTHENTICATION_NONE) { 1356 if (fOptUsername.Length() > 0) { 1357 authentication.SetUserName(fOptUsername); 1358 authentication.SetPassword(fOptPassword); 1359 } 1360 1361 BString request(fRequestMethod); 1362 outputHeaders.AddHeader("Authorization", 1363 authentication.Authorization(fUrl, request)); 1364 } 1365 } 1366 1367 // Required headers for POST data 1368 if (fOptPostFields != NULL && fRequestMethod == B_HTTP_POST) { 1369 BString contentType; 1370 1371 switch (fOptPostFields->GetFormType()) { 1372 case B_HTTP_FORM_MULTIPART: 1373 contentType << "multipart/form-data; boundary=" 1374 << fOptPostFields->GetMultipartBoundary() << ""; 1375 break; 1376 1377 case B_HTTP_FORM_URL_ENCODED: 1378 contentType << "application/x-www-form-urlencoded"; 1379 break; 1380 } 1381 1382 outputHeaders.AddHeader("Content-Type", contentType); 1383 outputHeaders.AddHeader("Content-Length", 1384 fOptPostFields->ContentLength()); 1385 } else if (fOptInputData != NULL 1386 && (fRequestMethod == B_HTTP_POST 1387 || fRequestMethod == B_HTTP_PUT)) { 1388 if (fOptInputDataSize >= 0) 1389 outputHeaders.AddHeader("Content-Length", fOptInputDataSize); 1390 else 1391 outputHeaders.AddHeader("Transfer-Encoding", "chunked"); 1392 } 1393 1394 // Optional headers specified by the user 1395 if (fOptHeaders != NULL) { 1396 for (int32 headerIndex = 0; headerIndex < fOptHeaders->CountHeaders(); 1397 headerIndex++) { 1398 BHttpHeader& optHeader = (*fOptHeaders)[headerIndex]; 1399 int32 replaceIndex = outputHeaders.HasHeader(optHeader.Name()); 1400 1401 // Add or replace the current option header to the 1402 // output header list 1403 if (replaceIndex == -1) 1404 outputHeaders.AddHeader(optHeader.Name(), optHeader.Value()); 1405 else 1406 outputHeaders[replaceIndex].SetValue(optHeader.Value()); 1407 } 1408 } 1409 1410 // Context cookies 1411 if (fOptSetCookies && fContext != NULL) { 1412 BString cookieString; 1413 1414 BNetworkCookieJar::UrlIterator iterator 1415 = fContext->GetCookieJar().GetUrlIterator(fUrl); 1416 const BNetworkCookie* cookie = iterator.Next(); 1417 if (cookie != NULL) { 1418 while (true) { 1419 cookieString << cookie->RawCookie(false); 1420 cookie = iterator.Next(); 1421 if (cookie == NULL) 1422 break; 1423 cookieString << "; "; 1424 } 1425 1426 outputHeaders.AddHeader("Cookie", cookieString); 1427 } 1428 } 1429 1430 // Write output headers to output stream 1431 BString headerData; 1432 1433 for (int32 headerIndex = 0; headerIndex < outputHeaders.CountHeaders(); 1434 headerIndex++) { 1435 const char* header = outputHeaders.HeaderAt(headerIndex).Header(); 1436 1437 headerData << header; 1438 headerData << "\r\n"; 1439 1440 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", header); 1441 } 1442 1443 return headerData; 1444 } 1445 1446 1447 void 1448 BHttpRequest::_SendPostData() 1449 { 1450 if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) { 1451 if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) { 1452 BString outputBuffer = fOptPostFields->RawData(); 1453 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 1454 "%s", outputBuffer.String()); 1455 fSocket->Write(outputBuffer.String(), outputBuffer.Length()); 1456 } else { 1457 for (BHttpForm::Iterator it = fOptPostFields->GetIterator(); 1458 const BHttpFormData* currentField = it.Next(); 1459 ) { 1460 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 1461 it.MultipartHeader().String()); 1462 fSocket->Write(it.MultipartHeader().String(), 1463 it.MultipartHeader().Length()); 1464 1465 switch (currentField->Type()) { 1466 default: 1467 case B_HTTPFORM_UNKNOWN: 1468 ASSERT(0); 1469 break; 1470 1471 case B_HTTPFORM_STRING: 1472 fSocket->Write(currentField->String().String(), 1473 currentField->String().Length()); 1474 break; 1475 1476 case B_HTTPFORM_FILE: 1477 { 1478 BFile upFile(currentField->File().Path(), 1479 B_READ_ONLY); 1480 char readBuffer[kHttpBufferSize]; 1481 ssize_t readSize; 1482 off_t totalSize; 1483 1484 if (upFile.GetSize(&totalSize) != B_OK) 1485 ASSERT(0); 1486 1487 readSize = upFile.Read(readBuffer, 1488 sizeof(readBuffer)); 1489 while (readSize > 0) { 1490 fSocket->Write(readBuffer, readSize); 1491 readSize = upFile.Read(readBuffer, 1492 sizeof(readBuffer)); 1493 fListener->UploadProgress(this, readSize, 1494 std::max((off_t)0, totalSize)); 1495 } 1496 1497 break; 1498 } 1499 case B_HTTPFORM_BUFFER: 1500 fSocket->Write(currentField->Buffer(), 1501 currentField->BufferSize()); 1502 break; 1503 } 1504 1505 fSocket->Write("\r\n", 2); 1506 } 1507 1508 BString footer = fOptPostFields->GetMultipartFooter(); 1509 fSocket->Write(footer.String(), footer.Length()); 1510 } 1511 } else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT) 1512 && fOptInputData != NULL) { 1513 1514 // If the input data is seekable, we rewind it for each new request. 1515 BPositionIO* seekableData 1516 = dynamic_cast<BPositionIO*>(fOptInputData); 1517 if (seekableData) 1518 seekableData->Seek(0, SEEK_SET); 1519 1520 for (;;) { 1521 char outputTempBuffer[kHttpBufferSize]; 1522 ssize_t read = fOptInputData->Read(outputTempBuffer, 1523 sizeof(outputTempBuffer)); 1524 1525 if (read <= 0) 1526 break; 1527 1528 if (fOptInputDataSize < 0) { 1529 // Input data size unknown, so we have to use chunked transfer 1530 char hexSize[18]; 1531 // The string does not need to be NULL terminated. 1532 size_t hexLength = snprintf(hexSize, sizeof(hexSize), "%lx\r\n", 1533 read); 1534 1535 fSocket->Write(hexSize, hexLength); 1536 fSocket->Write(outputTempBuffer, read); 1537 fSocket->Write("\r\n", 2); 1538 } else { 1539 fSocket->Write(outputTempBuffer, read); 1540 } 1541 } 1542 1543 if (fOptInputDataSize < 0) { 1544 // Chunked transfer terminating sequence 1545 fSocket->Write("0\r\n\r\n", 5); 1546 } 1547 } 1548 1549 } 1550 1551 1552 BHttpHeaders& 1553 BHttpRequest::_ResultHeaders() 1554 { 1555 return fResult.fHeaders; 1556 } 1557 1558 1559 void 1560 BHttpRequest::_SetResultStatusCode(int32 statusCode) 1561 { 1562 fResult.fStatusCode = statusCode; 1563 } 1564 1565 1566 BString& 1567 BHttpRequest::_ResultStatusText() 1568 { 1569 return fResult.fStatusString; 1570 } 1571 1572 1573 bool 1574 BHttpRequest::_CertificateVerificationFailed(BCertificate& certificate, 1575 const char* message) 1576 { 1577 if (fContext->HasCertificateException(certificate)) 1578 return true; 1579 1580 if (fListener != NULL 1581 && fListener->CertificateVerificationFailed(this, certificate, message)) { 1582 // User asked us to continue anyway, let's add a temporary exception for this certificate 1583 fContext->AddCertificateException(certificate); 1584 return true; 1585 } 1586 1587 return false; 1588 } 1589 1590 1591 bool 1592 BHttpRequest::_IsDefaultPort() 1593 { 1594 if (fSSL && Url().Port() == 443) 1595 return true; 1596 if (!fSSL && Url().Port() == 80) 1597 return true; 1598 return false; 1599 } 1600 1601 1602 #ifdef LIBNETAPI_DEPRECATED 1603 void 1604 BHttpRequest::_NotifyDataReceived(const char* data, off_t pos, ssize_t size, 1605 off_t bytesReceived, ssize_t bytesTotal) 1606 { 1607 if (fListener == NULL || size <= 0) 1608 return; 1609 if (fOptRangeStart > 0) { 1610 pos += fOptRangeStart; 1611 // bytesReceived and bytesTotal refer to the requested range, 1612 // so that should technically not be adjusted for the range start. 1613 // For displaying progress to the user, this is not ideal, though. 1614 // But only for the case where we request the remainder of a file. 1615 // Range requests can also be used to request any portion of a 1616 // resource, so not modifying them is technically more correct. 1617 // We can use a little trick, though: We know when the remainder 1618 // is requested, because then fOptRangeEnd is -1. 1619 if (fOptRangeEnd == -1) { 1620 bytesReceived += fOptRangeStart; 1621 if (bytesTotal > 0) 1622 bytesTotal += fOptRangeStart; 1623 } 1624 } 1625 fListener->DataReceived(this, data, pos, size); 1626 fListener->DownloadProgress(this, bytesReceived, 1627 std::max((ssize_t)0, bytesTotal)); 1628 } 1629 #endif 1630