1/* 2 * Copyright 2010 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 */ 8 9 10#include <cstdlib> 11#include <deque> 12#include <new> 13 14#include <arpa/inet.h> 15#include <Debug.h> 16#include <File.h> 17#include <UrlProtocolHttp.h> 18 19 20using BPrivate::BUrlProtocolOption; 21 22 23static const int32 kHttpProtocolReceiveBufferSize = 1024; 24static const char* kHttpProtocolThreadStrStatus[ 25 B_PROT_HTTP_THREAD_STATUS__END - B_PROT_THREAD_STATUS__END] 26 = { 27 "The remote server did not found the requested resource" 28 }; 29 30 31BUrlProtocolHttp::BUrlProtocolHttp(BUrl& url, BUrlProtocolListener* listener, 32 BUrlContext* context, BUrlResult* result) 33 : 34 BUrlProtocol(url, listener, context, result, "BUrlProtocol.HTTP", "HTTP"), 35 fRequestMethod(B_HTTP_GET), 36 fHttpVersion(B_HTTP_11) 37{ 38 _ResetOptions(); 39} 40 41 42status_t 43BUrlProtocolHttp::SetOption(uint32 name, void* value) 44{ 45 BUrlProtocolOption option(value); 46 47 switch (name) { 48 case B_HTTPOPT_METHOD: 49 fRequestMethod = option.Int8(); 50 break; 51 52 case B_HTTPOPT_FOLLOWLOCATION: 53 fOptFollowLocation = option.Bool(); 54 break; 55 56 case B_HTTPOPT_MAXREDIRS: 57 fOptMaxRedirs = option.Int8(); 58 break; 59 60 case B_HTTPOPT_REFERER: 61 fOptReferer = option.String(); 62 break; 63 64 case B_HTTPOPT_USERAGENT: 65 fOptUserAgent = option.String(); 66 break; 67 68 case B_HTTPOPT_HEADERS: 69 fOptHeaders = reinterpret_cast<BHttpHeaders*>(option.Pointer()); 70 break; 71 72 case B_HTTPOPT_DISCARD_DATA: 73 fOptDiscardData = option.Bool(); 74 break; 75 76 case B_HTTPOPT_DISABLE_LISTENER: 77 fOptDisableListener = option.Bool(); 78 break; 79 80 case B_HTTPOPT_AUTOREFERER: 81 fOptAutoReferer = option.Bool(); 82 break; 83 84 case B_HTTPOPT_POSTFIELDS: 85 fOptPostFields = reinterpret_cast<BHttpForm*>(option.Pointer()); 86 87 if (fOptPostFields != NULL) 88 fRequestMethod = B_HTTP_POST; 89 break; 90 91 case B_HTTPOPT_INPUTDATA: 92 fOptInputData = reinterpret_cast<BDataIO*>(option.Pointer()); 93 break; 94 95 case B_HTTPOPT_AUTHUSERNAME: 96 fOptUsername = option.String(); 97 break; 98 99 case B_HTTPOPT_AUTHPASSWORD: 100 fOptPassword = option.String(); 101 break; 102 103 default: 104 return B_ERROR; 105 } 106 107 return B_OK; 108} 109 110 111/*static*/ bool 112BUrlProtocolHttp::IsInformationalStatusCode(int16 code) 113{ 114 return (code >= B_HTTP_STATUS__INFORMATIONAL_BASE) 115 && (code < B_HTTP_STATUS__INFORMATIONAL_END); 116} 117 118 119/*static*/ bool 120BUrlProtocolHttp::IsSuccessStatusCode(int16 code) 121{ 122 return (code >= B_HTTP_STATUS__SUCCESS_BASE) 123 && (code < B_HTTP_STATUS__SUCCESS_END); 124} 125 126 127/*static*/ bool 128BUrlProtocolHttp::IsRedirectionStatusCode(int16 code) 129{ 130 return (code >= B_HTTP_STATUS__REDIRECTION_BASE) 131 && (code < B_HTTP_STATUS__REDIRECTION_END); 132} 133 134 135/*static*/ bool 136BUrlProtocolHttp::IsClientErrorStatusCode(int16 code) 137{ 138 return (code >= B_HTTP_STATUS__CLIENT_ERROR_BASE) 139 && (code < B_HTTP_STATUS__CLIENT_ERROR_END); 140} 141 142 143/*static*/ bool 144BUrlProtocolHttp::IsServerErrorStatusCode(int16 code) 145{ 146 return (code >= B_HTTP_STATUS__SERVER_ERROR_BASE) 147 && (code < B_HTTP_STATUS__SERVER_ERROR_END); 148} 149 150 151/*static*/ int16 152BUrlProtocolHttp::StatusCodeClass(int16 code) 153{ 154 if (BUrlProtocolHttp::IsInformationalStatusCode(code)) 155 return B_HTTP_STATUS_CLASS_INFORMATIONAL; 156 else if (BUrlProtocolHttp::IsSuccessStatusCode(code)) 157 return B_HTTP_STATUS_CLASS_SUCCESS; 158 else if (BUrlProtocolHttp::IsRedirectionStatusCode(code)) 159 return B_HTTP_STATUS_CLASS_REDIRECTION; 160 else if (BUrlProtocolHttp::IsClientErrorStatusCode(code)) 161 return B_HTTP_STATUS_CLASS_CLIENT_ERROR; 162 else if (BUrlProtocolHttp::IsServerErrorStatusCode(code)) 163 return B_HTTP_STATUS_CLASS_SERVER_ERROR; 164 165 return B_HTTP_STATUS_CLASS_INVALID; 166} 167 168 169const char* 170BUrlProtocolHttp::StatusString(status_t threadStatus) const 171{ 172 if (threadStatus < B_PROT_THREAD_STATUS__END) 173 return BUrlProtocol::StatusString(threadStatus); 174 else if (threadStatus >= B_PROT_HTTP_THREAD_STATUS__END) 175 return BUrlProtocol::StatusString(-1); 176 else 177 return kHttpProtocolThreadStrStatus[threadStatus 178 - B_PROT_THREAD_STATUS__END]; 179} 180 181 182void 183BUrlProtocolHttp::_ResetOptions() 184{ 185 fOptFollowLocation = true; 186 fOptMaxRedirs = 8; 187 fOptReferer = ""; 188 fOptUserAgent = "Services Kit (Haiku)"; 189 fOptUsername = ""; 190 fOptPassword = ""; 191 fOptAuthMethods = B_HTTP_AUTHENTICATION_BASIC | B_HTTP_AUTHENTICATION_DIGEST 192 | B_HTTP_AUTHENTICATION_IE_DIGEST; 193 fOptHeaders = NULL; 194 fOptPostFields = NULL; 195 fOptSetCookies = true; 196 fOptDiscardData = false; 197 fOptDisableListener = false; 198 fOptAutoReferer = true; 199} 200 201#include <stdio.h> 202status_t 203BUrlProtocolHttp::_ProtocolLoop() 204{ 205 printf("UHP[%p]::{Loop} %s\n", this, fUrl.UrlString().String()); 206 // Socket initialization 207 fSocket = BNetEndpoint(SOCK_STREAM); 208 if (fSocket.InitCheck() != B_OK) 209 return B_PROT_SOCKET_ERROR; 210 211 // Initialize the request redirection loop 212 int8 maxRedirs = fOptMaxRedirs; 213 bool newRequest; 214 215 do { 216 newRequest = false; 217 218 // Result reset 219 fOutputBuffer.Truncate(0, true); 220 fOutputHeaders.Clear(); 221 fHeaders.Clear(); 222 _ResultHeaders().Clear(); 223 _ResultRawData().Seek(SEEK_SET, 0); 224 _ResultRawData().SetSize(0); 225 226 if (!_ResolveHostName()) { 227 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, 228 "Unable to resolve hostname, aborting."); 229 return B_PROT_CANT_RESOLVE_HOSTNAME; 230 } 231 232 _CreateRequest(); 233 _AddHeaders(); 234 _AddOutputBufferLine(""); 235 236 status_t requestStatus = _MakeRequest(); 237 if (requestStatus != B_PROT_SUCCESS) 238 return requestStatus; 239 240 // Prepare the referer for the next request if needed 241 if (fOptAutoReferer) 242 fOptReferer = fUrl.UrlString(); 243 244 switch (StatusCodeClass(fResult->StatusCode())) { 245 case B_HTTP_STATUS_CLASS_INFORMATIONAL: 246 // Header 100:continue should have been 247 // handled in the _MakeRequest read loop 248 break; 249 250 case B_HTTP_STATUS_CLASS_SUCCESS: 251 break; 252 253 case B_HTTP_STATUS_CLASS_REDIRECTION: 254 // Redirection has been explicitly disabled 255 if (!fOptFollowLocation) 256 break; 257 258 // TODO: Some browsers seems to translate POST requests to 259 // GET when following a 302 redirection 260 if (fResult->StatusCode() == B_HTTP_STATUS_MOVED_PERMANENTLY) { 261 BString locationUrl = fHeaders["Location"]; 262 263 // Absolute path 264 if (locationUrl[0] == '/') 265 fUrl.SetPath(locationUrl); 266 // URI 267 else 268 fUrl.SetUrlString(locationUrl); 269 270 if (--maxRedirs > 0) { 271 newRequest = true; 272 273 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 274 "Following: %s\n", 275 fUrl.UrlString().String()); 276 } 277 } 278 break; 279 280 case B_HTTP_STATUS_CLASS_CLIENT_ERROR: 281 switch (fResult->StatusCode()) { 282 case B_HTTP_STATUS_UNAUTHORIZED: 283 if (fAuthentication.Method() != B_HTTP_AUTHENTICATION_NONE) { 284 newRequest = false; 285 break; 286 } 287 288 newRequest = false; 289 if (fOptUsername.Length() > 0 290 && fAuthentication.Initialize(fHeaders["WWW-Authenticate"]) 291 == B_OK) { 292 fAuthentication.SetUserName(fOptUsername); 293 fAuthentication.SetPassword(fOptPassword); 294 newRequest = true; 295 } 296 break; 297 } 298 break; 299 300 case B_HTTP_STATUS_CLASS_SERVER_ERROR: 301 break; 302 303 default: 304 case B_HTTP_STATUS_CLASS_INVALID: 305 break; 306 } 307 } while (newRequest); 308 309 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, 310 "%ld headers and %ld bytes of data remaining", 311 fHeaders.CountHeaders(), 312 fInputBuffer.Size()); 313 314 if (fResult->StatusCode() == 404) 315 return B_PROT_HTTP_NOT_FOUND; 316 317 return B_PROT_SUCCESS; 318} 319 320 321bool 322BUrlProtocolHttp::_ResolveHostName() 323{ 324 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Resolving %s", 325 fUrl.UrlString().String()); 326 327 if (fUrl.HasPort()) 328 fRemoteAddr = BNetAddress(fUrl.Host(), fUrl.Port()); 329 else 330 fRemoteAddr = BNetAddress(fUrl.Host(), 80); 331 332 if (fRemoteAddr.InitCheck() != B_OK) 333 return false; 334 335 char addr[15]; 336 struct in_addr ip; 337 fRemoteAddr.GetAddr(ip); 338 inet_ntop(AF_INET, &ip, addr, 15); 339 340 //! ProtocolHook:HostnameResolved 341 if (fListener != NULL) 342 fListener->HostnameResolved(this, const_cast<const char*>(addr)); 343 344 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Hostname resolved to: %s", addr); 345 346 return true; 347} 348 349 350status_t 351BUrlProtocolHttp::_MakeRequest() 352{ 353 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s.", 354 fUrl.Authority().String()); 355 status_t connectError = fSocket.Connect(fRemoteAddr); 356 357 if (connectError != B_OK) { 358 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Connection error: %s.", 359 fSocket.ErrorStr()); 360 return B_PROT_CONNECTION_FAILED; 361 } 362 363 //! ProtocolHook:ConnectionOpened 364 if (fListener != NULL) 365 fListener->ConnectionOpened(this); 366 367 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection opened."); 368 369 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Sending request (size=%d)", 370 fOutputBuffer.Length()); 371 fSocket.Send(fOutputBuffer.String(), fOutputBuffer.Length()); 372 fOutputBuffer.Truncate(0); 373 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent."); 374 375 if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) { 376 if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) { 377 fOutputBuffer = fOptPostFields->RawData(); 378 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 379 fOutputBuffer.String()); 380 fSocket.Send(fOutputBuffer.String(), fOutputBuffer.Length()); 381 } else { 382 for (BHttpForm::Iterator it = fOptPostFields->GetIterator(); 383 const BHttpFormData* currentField = it.Next(); 384 ) { 385 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT, 386 it.MultipartHeader().String()); 387 fSocket.Send(it.MultipartHeader().String(), 388 it.MultipartHeader().Length()); 389 390 switch (currentField->Type()) { 391 case B_HTTPFORM_UNKNOWN: 392 ASSERT(0); 393 break; 394 395 case B_HTTPFORM_STRING: 396 fSocket.Send(currentField->String().String(), 397 currentField->String().Length()); 398 break; 399 400 case B_HTTPFORM_FILE: 401 { 402 BFile upFile(currentField->File().Path(), 403 B_READ_ONLY); 404 char readBuffer[1024]; 405 ssize_t readSize; 406 407 readSize = upFile.Read(readBuffer, 1024); 408 while (readSize > 0) { 409 fSocket.Send(readBuffer, readSize); 410 readSize = upFile.Read(readBuffer, 1024); 411 } 412 } 413 break; 414 415 case B_HTTPFORM_BUFFER: 416 fSocket.Send(currentField->Buffer(), 417 currentField->BufferSize()); 418 break; 419 } 420 421 fSocket.Send("\r\n", 2); 422 } 423 424 fSocket.Send(fOptPostFields->GetMultipartFooter().String(), 425 fOptPostFields->GetMultipartFooter().Length()); 426 } 427 } else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT) 428 && fOptInputData != NULL) { 429 char outputTempBuffer[1024]; 430 ssize_t read = 0; 431 432 while (read != -1) { 433 read = fOptInputData->Read(outputTempBuffer, 1024); 434 435 if (read > 0) { 436 char hexSize[16]; 437 size_t hexLength = sprintf(hexSize, "%ld", read); 438 439 fSocket.Send(hexSize, hexLength); 440 fSocket.Send("\r\n", 2); 441 fSocket.Send(outputTempBuffer, read); 442 fSocket.Send("\r\n", 2); 443 } 444 } 445 446 fSocket.Send("0\r\n\r\n", 5); 447 } 448 fOutputBuffer.Truncate(0, true); 449 450 fSocket.SetNonBlocking(false); 451 452 fStatusReceived = false; 453 fHeadersReceived = false; 454 455 // Receive loop 456 bool receiveEnd = false; 457 bool parseEnd = false; 458 bool readByChunks = false; 459 bool readError = false; 460 int32 receiveBufferSize = 32; 461 ssize_t bytesRead = 0; 462 ssize_t bytesReceived = 0; 463 ssize_t bytesTotal = 0; 464 char* inputTempBuffer = NULL; 465 fQuit = false; 466 467 while (!fQuit && !(receiveEnd && parseEnd)) { 468 if (!receiveEnd) { 469 bytesRead = fSocket.Receive(fInputBuffer, receiveBufferSize); 470 471 if (bytesRead < 0) { 472 readError = true; 473 fQuit = true; 474 continue; 475 } else if (bytesRead == 0) 476 receiveEnd = true; 477 } 478 else 479 bytesRead = 0; 480 481 if (!fStatusReceived) { 482 _ParseStatus(); 483 484 //! ProtocolHook:ResponseStarted 485 if (fStatusReceived && fListener != NULL) 486 fListener->ResponseStarted(this); 487 } else if (!fHeadersReceived) { 488 _ParseHeaders(); 489 490 if (fHeadersReceived) { 491 receiveBufferSize = kHttpProtocolReceiveBufferSize; 492 _ResultHeaders() = fHeaders; 493 494 //! ProtocolHook:HeadersReceived 495 if (fListener != NULL) 496 fListener->HeadersReceived(this); 497 498 // Parse received cookies 499 if ((fContext != NULL) && fHeaders.HasHeader("Set-Cookie")) { 500 for (int32 i = 0; i < fHeaders.CountHeaders(); i++) { 501 if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) { 502 } 503 } 504 } 505 506 if (BString(fHeaders["Transfer-Encoding"]) == "chunked") 507 readByChunks = true; 508 509 int32 index = fHeaders.HasHeader("Content-Length"); 510 if (index != B_ERROR) 511 bytesTotal = atoi(fHeaders.HeaderAt(index).Value()); 512 else 513 bytesTotal = 0; 514 } 515 } else { 516 // If Transfer-Encoding is chunked, we should read a complete 517 // chunk in buffer before handling it 518 if (readByChunks) { 519 _CopyChunkInBuffer(&inputTempBuffer, &bytesRead); 520 521 // A chunk of 0 bytes indicates the end of the chunked transfer 522 if (bytesRead == 0) { 523 receiveEnd = true; 524 } 525 } 526 else { 527 bytesRead = fInputBuffer.Size(); 528 529 if (bytesRead > 0) { 530 inputTempBuffer = new char[bytesRead]; 531 fInputBuffer.RemoveData(inputTempBuffer, bytesRead); 532 } 533 } 534 535 if (bytesRead > 0) { 536 bytesReceived += bytesRead; 537 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_IN, "%d bytes", 538 bytesRead); 539 540 if (fListener != NULL) { 541 fListener->DataReceived(this, inputTempBuffer, bytesRead); 542 fListener->DownloadProgress(this, bytesReceived, 543 bytesTotal); 544 } 545 546 ssize_t dataWrite = _ResultRawData().Write(inputTempBuffer, 547 bytesRead); 548 549 if (dataWrite != bytesRead) { 550 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, 551 "Unable to write %dbytes of data (%d).", bytesRead, 552 dataWrite); 553 return B_PROT_NO_MEMORY; 554 } 555 556 if (bytesTotal > 0 && bytesReceived >= bytesTotal) 557 receiveEnd = true; 558 559 delete[] inputTempBuffer; 560 } 561 } 562 563 parseEnd = (fInputBuffer.Size() == 0); 564 } 565 566 fSocket.Close(); 567 568 if (readError) 569 return B_PROT_READ_FAILED; 570 571 return fQuit?B_PROT_ABORTED:B_PROT_SUCCESS; 572} 573 574 575status_t 576BUrlProtocolHttp::_GetLine(BString& destString) 577{ 578 // Find a complete line in inputBuffer 579 uint32 characterIndex = 0; 580 581 while ((characterIndex < fInputBuffer.Size()) 582 && ((fInputBuffer.Data())[characterIndex] != '\n')) 583 characterIndex++; 584 585 if (characterIndex == fInputBuffer.Size()) 586 return B_ERROR; 587 588 char* temporaryBuffer = new(std::nothrow) char[characterIndex + 1]; 589 fInputBuffer.RemoveData(temporaryBuffer, characterIndex + 1); 590 591 // Strip end-of-line character(s) 592 if (temporaryBuffer[characterIndex-1] == '\r') 593 destString.SetTo(temporaryBuffer, characterIndex - 1); 594 else 595 destString.SetTo(temporaryBuffer, characterIndex); 596 597 delete[] temporaryBuffer; 598 return B_OK; 599} 600 601 602void 603BUrlProtocolHttp::_ParseStatus() 604{ 605 // Status line should be formatted like: HTTP/M.m SSS ... 606 // With: M = Major version of the protocol 607 // m = Minor version of the protocol 608 // SSS = three-digit status code of the response 609 // ... = additional text info 610 BString statusLine; 611 if (_GetLine(statusLine) == B_ERROR) 612 return; 613 614 if (statusLine.CountChars() < 12) 615 return; 616 617 fStatusReceived = true; 618 619 BString statusCodeStr; 620 BString statusText; 621 statusLine.CopyInto(statusCodeStr, 9, 3); 622 _SetResultStatusCode(atoi(statusCodeStr.String())); 623 624 statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13); 625 626 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)", 627 atoi(statusCodeStr.String()), _ResultStatusText().String()); 628} 629 630 631void 632BUrlProtocolHttp::_ParseHeaders() 633{ 634 BString currentHeader; 635 if (_GetLine(currentHeader) == B_ERROR) 636 return; 637 638 // Empty line 639 if (currentHeader.Length() == 0) { 640 fHeadersReceived = true; 641 return; 642 } 643 644 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s", currentHeader.String()); 645 fHeaders.AddHeader(currentHeader.String()); 646} 647 648 649void 650BUrlProtocolHttp::_CopyChunkInBuffer(char** buffer, ssize_t* bytesReceived) 651{ 652 static ssize_t chunkSize = -1; 653 BString chunkHeader; 654 655 if (chunkSize >= 0) { 656 if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) { 657 // 2 more bytes to handle the closing CR+LF 658 *bytesReceived = chunkSize; 659 *buffer = new char[chunkSize+2]; 660 fInputBuffer.RemoveData(*buffer, chunkSize+2); 661 chunkSize = -1; 662 } else { 663 *bytesReceived = -1; 664 *buffer = NULL; 665 } 666 } else { 667 if (_GetLine(chunkHeader) == B_ERROR) { 668 chunkSize = -1; 669 *buffer = NULL; 670 *bytesReceived = -1; 671 return; 672 } 673 674 // Format of a chunk header: 675 // <chunk size in hex>[; optional data] 676 int32 semiColonIndex = chunkHeader.FindFirst(";", 0); 677 678 // Cut-off optional data if present 679 if (semiColonIndex != -1) 680 chunkHeader.Remove(semiColonIndex, 681 chunkHeader.Length() - semiColonIndex); 682 683 chunkSize = strtol(chunkHeader.String(), NULL, 16); 684 PRINT(("BHP[%p] Chunk %s=%d\n", this, chunkHeader.String(), chunkSize)); 685 if (chunkSize == 0) { 686 fContentReceived = true; 687 } 688 689 *bytesReceived = -1; 690 *buffer = NULL; 691 } 692} 693 694 695void 696BUrlProtocolHttp::_CreateRequest() 697{ 698 BString request; 699 700 switch (fRequestMethod) { 701 case B_HTTP_POST: 702 request << "POST"; 703 break; 704 705 case B_HTTP_PUT: 706 request << "PUT"; 707 break; 708 709 default: 710 case B_HTTP_GET: 711 request << "GET"; 712 break; 713 } 714 715 if (Url().HasPath()) 716 request << ' ' << Url().Path(); 717 else 718 request << " /"; 719 720 if (Url().HasRequest()) 721 request << '?' << Url().Request(); 722 723 if (Url().HasFragment()) 724 request << '#' << Url().Fragment(); 725 726 request << ' '; 727 728 switch (fHttpVersion) { 729 case B_HTTP_11: 730 request << "HTTP/1.1"; 731 break; 732 733 default: 734 case B_HTTP_10: 735 request << "HTTP/1.0"; 736 break; 737 } 738 739 _AddOutputBufferLine(request.String()); 740} 741 742 743void 744BUrlProtocolHttp::_AddHeaders() 745{ 746 // HTTP 1.1 additional headers 747 if (fHttpVersion == B_HTTP_11) { 748 fOutputHeaders.AddHeader("Host", Url().Host()); 749 750 fOutputHeaders.AddHeader("Accept", "*/*"); 751 fOutputHeaders.AddHeader("Accept-Encoding", "chunked"); 752 // Allow the remote server to send dynamic content by chunks 753 // rather than waiting for the full content to be generated and 754 // sending us data. 755 756 fOutputHeaders.AddHeader("Connection", "close"); 757 // Let the remote server close the connection after response since 758 // we don't handle multiple request on a single connection 759 } 760 761 // Classic HTTP headers 762 if (fOptUserAgent.CountChars() > 0) 763 fOutputHeaders.AddHeader("User-Agent", fOptUserAgent.String()); 764 765 if (fOptReferer.CountChars() > 0) 766 fOutputHeaders.AddHeader("Referer", fOptReferer.String()); 767 768 // Authentication 769 if (fAuthentication.Method() != B_HTTP_AUTHENTICATION_NONE) { 770 BString request; 771 switch (fRequestMethod) { 772 case B_HTTP_POST: 773 request = "POST"; 774 break; 775 776 case B_HTTP_PUT: 777 request = "PUT"; 778 break; 779 780 default: 781 case B_HTTP_GET: 782 request = "GET"; 783 break; 784 } 785 786 fOutputHeaders.AddHeader("Authorization", 787 fAuthentication.Authorization(fUrl, request)); 788 } 789 790 // Required headers for POST data 791 if (fOptPostFields != NULL && fRequestMethod == B_HTTP_POST) { 792 BString contentType; 793 794 switch (fOptPostFields->GetFormType()) { 795 case B_HTTP_FORM_MULTIPART: 796 contentType << "multipart/form-data; boundary=" 797 << fOptPostFields->GetMultipartBoundary() << ""; 798 break; 799 800 case B_HTTP_FORM_URL_ENCODED: 801 contentType << "application/x-www-form-urlencoded"; 802 break; 803 } 804 805 fOutputHeaders.AddHeader("Content-Type", contentType); 806 fOutputHeaders.AddHeader("Content-Length", 807 fOptPostFields->ContentLength()); 808 } else if (fOptInputData != NULL 809 && (fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT)) 810 fOutputHeaders.AddHeader("Transfer-Encoding", "chunked"); 811 812 // Request headers 813 for (int32 headerIndex = 0; 814 headerIndex < fRequestHeaders.CountHeaders(); 815 headerIndex++) { 816 BHttpHeader& optHeader = fRequestHeaders[headerIndex]; 817 int32 replaceIndex = fOutputHeaders.HasHeader(optHeader.Name()); 818 819 // Add or replace the current option header to the 820 // output header list 821 if (replaceIndex == -1) 822 fOutputHeaders.AddHeader(optHeader.Name(), optHeader.Value()); 823 else 824 fOutputHeaders[replaceIndex].SetValue(optHeader.Value()); 825 } 826 827 // Optional headers specified by the user 828 if (fOptHeaders != NULL) { 829 for (int32 headerIndex = 0; 830 headerIndex < fOptHeaders->CountHeaders(); 831 headerIndex++) { 832 BHttpHeader& optHeader = (*fOptHeaders)[headerIndex]; 833 int32 replaceIndex = fOutputHeaders.HasHeader(optHeader.Name()); 834 835 // Add or replace the current option header to the 836 // output header list 837 if (replaceIndex == -1) 838 fOutputHeaders.AddHeader(optHeader.Name(), optHeader.Value()); 839 else 840 fOutputHeaders[replaceIndex].SetValue(optHeader.Value()); 841 } 842 } 843 844 // Context cookies 845 if (fOptSetCookies && (fContext != NULL)) { 846 BNetworkCookie* cookie; 847 848 for (BNetworkCookieJar::UrlIterator 849 it(fContext->GetCookieJar().GetUrlIterator(fUrl)); 850 (cookie = it.Next()) != NULL; 851 ) 852 fOutputHeaders.AddHeader("Cookie", cookie->RawCookie(false)); 853 } 854 855 // Write output headers to output stream 856 for (int32 headerIndex = 0; 857 headerIndex < fOutputHeaders.CountHeaders(); 858 headerIndex++) 859 _AddOutputBufferLine(fOutputHeaders.HeaderAt(headerIndex).Header()); 860} 861 862 863void 864BUrlProtocolHttp::_AddOutputBufferLine(const char* line) 865{ 866 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", line); 867 fOutputBuffer << line << "\r\n"; 868} 869