2 * Copyright 2010-2014 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Christophe Huriaux, c.huriaux@gmail.com
7 * Niels Sascha Reedijk, niels.reedijk@gmail.com
8 * Adrien Destugues, pulkomandy@pulkomandy.tk
12 #include <HttpRequest.h>
14 #include <arpa/inet.h>
21 #include <AutoDeleter.h>
22 #include <Certificate.h>
24 #include <DynamicBuffer.h>
27 #include <SecureSocket.h>
28 #include <StackOrHeapArray.h>
29 #include <ZlibCompressionAlgorithm.h>
32 static const int32 kHttpBufferSize
= 4096;
35 class CheckedSecureSocket
: public BSecureSocket
38 CheckedSecureSocket(BHttpRequest
* request
);
40 bool CertificateVerificationFailed(BCertificate
& certificate
,
44 BHttpRequest
* fRequest
;
48 CheckedSecureSocket::CheckedSecureSocket(BHttpRequest
* request
)
57 CheckedSecureSocket::CertificateVerificationFailed(BCertificate
& certificate
,
60 return fRequest
->_CertificateVerificationFailed(certificate
, message
);
64 BHttpRequest::BHttpRequest(const BUrl
& url
, bool ssl
, const char* protocolName
,
65 BUrlProtocolListener
* listener
, BUrlContext
* context
)
67 BNetworkRequest(url
, listener
, context
, "BUrlProtocol.HTTP", protocolName
),
69 fRequestMethod(B_HTTP_GET
),
70 fHttpVersion(B_HTTP_11
),
72 fRequestStatus(kRequestInitialState
),
76 fOptInputDataSize(-1),
79 fOptFollowLocation(true)
86 BHttpRequest::BHttpRequest(const BHttpRequest
& other
)
88 BNetworkRequest(other
.Url(), other
.fListener
, other
.fContext
,
89 "BUrlProtocol.HTTP", other
.fSSL
? "HTTPS" : "HTTP"),
91 fRequestMethod(other
.fRequestMethod
),
92 fHttpVersion(other
.fHttpVersion
),
94 fRequestStatus(kRequestInitialState
),
98 fOptInputDataSize(-1),
99 fOptRangeStart(other
.fOptRangeStart
),
100 fOptRangeEnd(other
.fOptRangeEnd
),
101 fOptFollowLocation(other
.fOptFollowLocation
)
104 // FIXME some options may be copied from other instead.
109 BHttpRequest::~BHttpRequest()
115 delete fOptInputData
;
117 delete fOptPostFields
;
122 BHttpRequest::SetMethod(const char* const method
)
124 fRequestMethod
= method
;
129 BHttpRequest::SetFollowLocation(bool follow
)
131 fOptFollowLocation
= follow
;
136 BHttpRequest::SetMaxRedirections(int8 redirections
)
138 fOptMaxRedirs
= redirections
;
143 BHttpRequest::SetReferrer(const BString
& referrer
)
145 fOptReferer
= referrer
;
150 BHttpRequest::SetUserAgent(const BString
& agent
)
152 fOptUserAgent
= agent
;
157 BHttpRequest::SetDiscardData(bool discard
)
159 fOptDiscardData
= discard
;
164 BHttpRequest::SetDisableListener(bool disable
)
166 fOptDisableListener
= disable
;
171 BHttpRequest::SetAutoReferrer(bool enable
)
173 fOptAutoReferer
= enable
;
178 BHttpRequest::SetHeaders(const BHttpHeaders
& headers
)
180 AdoptHeaders(new(std::nothrow
) BHttpHeaders(headers
));
185 BHttpRequest::AdoptHeaders(BHttpHeaders
* const headers
)
188 fOptHeaders
= headers
;
193 BHttpRequest::SetPostFields(const BHttpForm
& fields
)
195 AdoptPostFields(new(std::nothrow
) BHttpForm(fields
));
200 BHttpRequest::AdoptPostFields(BHttpForm
* const fields
)
202 delete fOptPostFields
;
203 fOptPostFields
= fields
;
205 if (fOptPostFields
!= NULL
)
206 fRequestMethod
= B_HTTP_POST
;
211 BHttpRequest::AdoptInputData(BDataIO
* const data
, const ssize_t size
)
213 delete fOptInputData
;
214 fOptInputData
= data
;
215 fOptInputDataSize
= size
;
220 BHttpRequest::SetUserName(const BString
& name
)
227 BHttpRequest::SetPassword(const BString
& password
)
229 fOptPassword
= password
;
234 BHttpRequest::IsInformationalStatusCode(int16 code
)
236 return (code
>= B_HTTP_STATUS__INFORMATIONAL_BASE
)
237 && (code
< B_HTTP_STATUS__INFORMATIONAL_END
);
242 BHttpRequest::IsSuccessStatusCode(int16 code
)
244 return (code
>= B_HTTP_STATUS__SUCCESS_BASE
)
245 && (code
< B_HTTP_STATUS__SUCCESS_END
);
250 BHttpRequest::IsRedirectionStatusCode(int16 code
)
252 return (code
>= B_HTTP_STATUS__REDIRECTION_BASE
)
253 && (code
< B_HTTP_STATUS__REDIRECTION_END
);
258 BHttpRequest::IsClientErrorStatusCode(int16 code
)
260 return (code
>= B_HTTP_STATUS__CLIENT_ERROR_BASE
)
261 && (code
< B_HTTP_STATUS__CLIENT_ERROR_END
);
266 BHttpRequest::IsServerErrorStatusCode(int16 code
)
268 return (code
>= B_HTTP_STATUS__SERVER_ERROR_BASE
)
269 && (code
< B_HTTP_STATUS__SERVER_ERROR_END
);
274 BHttpRequest::StatusCodeClass(int16 code
)
276 if (BHttpRequest::IsInformationalStatusCode(code
))
277 return B_HTTP_STATUS_CLASS_INFORMATIONAL
;
278 else if (BHttpRequest::IsSuccessStatusCode(code
))
279 return B_HTTP_STATUS_CLASS_SUCCESS
;
280 else if (BHttpRequest::IsRedirectionStatusCode(code
))
281 return B_HTTP_STATUS_CLASS_REDIRECTION
;
282 else if (BHttpRequest::IsClientErrorStatusCode(code
))
283 return B_HTTP_STATUS_CLASS_CLIENT_ERROR
;
284 else if (BHttpRequest::IsServerErrorStatusCode(code
))
285 return B_HTTP_STATUS_CLASS_SERVER_ERROR
;
287 return B_HTTP_STATUS_CLASS_INVALID
;
292 BHttpRequest::Result() const
301 if (fSocket
!= NULL
) {
302 fSocket
->Disconnect();
303 // Unlock any pending connect, read or write operation.
305 return BNetworkRequest::Stop();
310 BHttpRequest::_ResetOptions()
312 delete fOptPostFields
;
315 fOptFollowLocation
= true;
318 fOptUserAgent
= "Services Kit (Haiku)";
321 fOptAuthMethods
= B_HTTP_AUTHENTICATION_BASIC
| B_HTTP_AUTHENTICATION_DIGEST
322 | B_HTTP_AUTHENTICATION_IE_DIGEST
;
324 fOptPostFields
= NULL
;
325 fOptSetCookies
= true;
326 fOptDiscardData
= false;
327 fOptDisableListener
= false;
328 fOptAutoReferer
= true;
333 BHttpRequest::_ProtocolLoop()
335 // Initialize the request redirection loop
336 int8 maxRedirs
= fOptMaxRedirs
;
344 _ResultHeaders().Clear();
346 BString host
= fUrl
.Host();
347 int port
= fSSL
? 443 : 80;
352 if (fContext
->UseProxy()) {
353 host
= fContext
->GetProxyHost();
354 port
= fContext
->GetProxyPort();
357 status_t result
= fInputBuffer
.InitCheck();
361 if (!_ResolveHostName(host
, port
)) {
362 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR
,
363 "Unable to resolve hostname (%s), aborting.",
364 fUrl
.Host().String());
365 return B_SERVER_NOT_FOUND
;
368 status_t requestStatus
= _MakeRequest();
369 if (requestStatus
!= B_OK
)
370 return requestStatus
;
372 // Prepare the referer for the next request if needed
374 fOptReferer
= fUrl
.UrlString();
376 switch (StatusCodeClass(fResult
.StatusCode())) {
377 case B_HTTP_STATUS_CLASS_INFORMATIONAL
:
378 // Header 100:continue should have been
379 // handled in the _MakeRequest read loop
382 case B_HTTP_STATUS_CLASS_SUCCESS
:
385 case B_HTTP_STATUS_CLASS_REDIRECTION
:
387 // Redirection has been explicitly disabled
388 if (!fOptFollowLocation
)
391 int code
= fResult
.StatusCode();
392 if (code
== B_HTTP_STATUS_MOVED_PERMANENTLY
393 || code
== B_HTTP_STATUS_FOUND
394 || code
== B_HTTP_STATUS_SEE_OTHER
395 || code
== B_HTTP_STATUS_TEMPORARY_REDIRECT
) {
396 BString locationUrl
= fHeaders
["Location"];
398 fUrl
= BUrl(fUrl
, locationUrl
);
400 // 302 and 303 redirections also convert POST requests to GET
401 // (and remove the posted form data)
402 if ((code
== B_HTTP_STATUS_FOUND
403 || code
== B_HTTP_STATUS_SEE_OTHER
)
404 && fRequestMethod
== B_HTTP_POST
) {
405 SetMethod(B_HTTP_GET
);
406 delete fOptPostFields
;
407 fOptPostFields
= NULL
;
408 delete fOptInputData
;
409 fOptInputData
= NULL
;
410 fOptInputDataSize
= 0;
413 if (--maxRedirs
> 0) {
416 // Redirections may need a switch from http to https.
417 if (fUrl
.Protocol() == "https")
419 else if (fUrl
.Protocol() == "http")
422 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT
,
424 fUrl
.UrlString().String());
430 case B_HTTP_STATUS_CLASS_CLIENT_ERROR
:
431 if (fResult
.StatusCode() == B_HTTP_STATUS_UNAUTHORIZED
) {
432 BHttpAuthentication
* authentication
433 = &fContext
->GetAuthentication(fUrl
);
434 status_t status
= B_OK
;
436 if (authentication
->Method() == B_HTTP_AUTHENTICATION_NONE
) {
437 // There is no authentication context for this
438 // url yet, so let's create one.
439 BHttpAuthentication newAuth
;
440 newAuth
.Initialize(fHeaders
["WWW-Authenticate"]);
441 fContext
->AddAuthentication(fUrl
, newAuth
);
443 // Get the copy of the authentication we just added.
444 // That copy is owned by the BUrlContext and won't be
445 // deleted (unlike the temporary object above)
446 authentication
= &fContext
->GetAuthentication(fUrl
);
450 if (fOptUsername
.Length() > 0 && status
== B_OK
) {
451 // If we received an username and password, add them
452 // to the request. This will either change the
453 // credentials for an existing request, or set them
454 // for a new one we created just above.
456 // If this request handles HTTP redirections, it will
457 // also automatically retry connecting and send the
458 // login information.
459 authentication
->SetUserName(fOptUsername
);
460 authentication
->SetPassword(fOptPassword
);
466 case B_HTTP_STATUS_CLASS_SERVER_ERROR
:
470 case B_HTTP_STATUS_CLASS_INVALID
:
473 } while (newRequest
);
475 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT
,
476 "%ld headers and %ld bytes of data remaining",
477 fHeaders
.CountHeaders(), fInputBuffer
.Size());
479 if (fResult
.StatusCode() == 404)
480 return B_RESOURCE_NOT_FOUND
;
487 BHttpRequest::_MakeRequest()
492 fSocket
= new(std::nothrow
) CheckedSecureSocket(this);
494 fSocket
= new(std::nothrow
) BSocket();
499 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT
, "Connection to %s on port %d.",
500 fUrl
.Authority().String(), fRemoteAddr
.Port());
501 status_t connectError
= fSocket
->Connect(fRemoteAddr
);
503 if (connectError
!= B_OK
) {
504 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR
, "Socket connection error %s",
505 strerror(connectError
));
509 //! ProtocolHook:ConnectionOpened
510 if (fListener
!= NULL
)
511 fListener
->ConnectionOpened(this);
513 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT
,
514 "Connection opened, sending request.");
518 fSocket
->Write("\r\n", 2);
519 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT
, "Request sent.");
522 fRequestStatus
= kRequestInitialState
;
527 bool receiveEnd
= false;
528 bool parseEnd
= false;
529 bool readByChunks
= false;
530 bool decompress
= false;
531 status_t readError
= B_OK
;
532 ssize_t bytesRead
= 0;
533 ssize_t bytesReceived
= 0;
534 ssize_t bytesTotal
= 0;
535 off_t bytesUnpacked
= 0;
536 char* inputTempBuffer
= new(std::nothrow
) char[kHttpBufferSize
];
537 ssize_t inputTempSize
= kHttpBufferSize
;
538 ssize_t chunkSize
= -1;
539 DynamicBuffer decompressorStorage
;
540 BDataIO
* decompressingStream
;
541 ObjectDeleter
<BDataIO
> decompressingStreamDeleter
;
543 while (!fQuit
&& !(receiveEnd
&& parseEnd
)) {
545 fSocket
->WaitForReadable();
546 BStackOrHeapArray
<char, 4096> chunk(kHttpBufferSize
);
547 bytesRead
= fSocket
->Read(chunk
, kHttpBufferSize
);
550 readError
= bytesRead
;
552 } else if (bytesRead
== 0)
555 fInputBuffer
.AppendData(chunk
, bytesRead
);
559 if (fRequestStatus
< kRequestStatusReceived
) {
562 //! ProtocolHook:ResponseStarted
563 if (fRequestStatus
>= kRequestStatusReceived
&& fListener
!= NULL
)
564 fListener
->ResponseStarted(this);
567 if (fRequestStatus
< kRequestHeadersReceived
) {
570 if (fRequestStatus
>= kRequestHeadersReceived
) {
571 _ResultHeaders() = fHeaders
;
573 //! ProtocolHook:HeadersReceived
574 if (fListener
!= NULL
)
575 fListener
->HeadersReceived(this);
577 // Parse received cookies
578 if (fContext
!= NULL
) {
579 for (int32 i
= 0; i
< fHeaders
.CountHeaders(); i
++) {
580 if (fHeaders
.HeaderAt(i
).NameIs("Set-Cookie")) {
581 fContext
->GetCookieJar().AddCookie(
582 fHeaders
.HeaderAt(i
).Value(), fUrl
);
587 if (BString(fHeaders
["Transfer-Encoding"]) == "chunked")
590 BString
contentEncoding(fHeaders
["Content-Encoding"]);
591 // We don't advertise "deflate" support (see above), but we
592 // still try to decompress it, if a server ever sends a deflate
593 // stream despite it not being in our Accept-Encoding list.
594 if (contentEncoding
== "gzip"
595 || contentEncoding
== "deflate") {
597 readError
= BZlibCompressionAlgorithm()
598 .CreateDecompressingOutputStream(&decompressorStorage
,
599 NULL
, decompressingStream
);
600 if (readError
!= B_OK
)
603 decompressingStreamDeleter
.SetTo(decompressingStream
);
606 int32 index
= fHeaders
.HasHeader("Content-Length");
607 if (index
!= B_ERROR
)
608 bytesTotal
= atoi(fHeaders
.HeaderAt(index
).Value());
614 if (fRequestStatus
>= kRequestHeadersReceived
) {
615 // If Transfer-Encoding is chunked, we should read a complete
616 // chunk in buffer before handling it
618 if (chunkSize
>= 0) {
619 if ((ssize_t
)fInputBuffer
.Size() >= chunkSize
+ 2) {
620 // 2 more bytes to handle the closing CR+LF
621 bytesRead
= chunkSize
;
622 if (inputTempSize
< chunkSize
+ 2) {
623 delete[] inputTempBuffer
;
624 inputTempSize
= chunkSize
+ 2;
626 = new(std::nothrow
) char[inputTempSize
];
629 if (inputTempBuffer
== NULL
) {
630 readError
= B_NO_MEMORY
;
634 fInputBuffer
.RemoveData(inputTempBuffer
,
638 // Not enough data, try again later
643 if (_GetLine(chunkHeader
) == B_ERROR
) {
647 // Format of a chunk header:
648 // <chunk size in hex>[; optional data]
649 int32 semiColonIndex
= chunkHeader
.FindFirst(';', 0);
651 // Cut-off optional data if present
652 if (semiColonIndex
!= -1) {
653 chunkHeader
.Remove(semiColonIndex
,
654 chunkHeader
.Length() - semiColonIndex
);
657 chunkSize
= strtol(chunkHeader
.String(), NULL
, 16);
658 PRINT(("BHP[%p] Chunk %s=%ld\n", this,
659 chunkHeader
.String(), chunkSize
));
661 fRequestStatus
= kRequestContentReceived
;
667 // A chunk of 0 bytes indicates the end of the chunked transfer
671 bytesRead
= fInputBuffer
.Size();
674 if (inputTempSize
< bytesRead
) {
675 inputTempSize
= bytesRead
;
676 delete[] inputTempBuffer
;
677 inputTempBuffer
= new(std::nothrow
) char[bytesRead
];
680 if (inputTempBuffer
== NULL
) {
681 readError
= B_NO_MEMORY
;
684 fInputBuffer
.RemoveData(inputTempBuffer
, bytesRead
);
688 if (bytesRead
>= 0) {
689 bytesReceived
+= bytesRead
;
691 if (fListener
!= NULL
) {
693 readError
= decompressingStream
->WriteExactly(
694 inputTempBuffer
, bytesRead
);
695 if (readError
!= B_OK
)
698 ssize_t size
= decompressorStorage
.Size();
699 BStackOrHeapArray
<char, 4096> buffer(size
);
700 size
= decompressorStorage
.Read(buffer
, size
);
702 fListener
->DataReceived(this, buffer
, bytesUnpacked
,
704 bytesUnpacked
+= size
;
706 } else if (bytesRead
> 0) {
707 fListener
->DataReceived(this, inputTempBuffer
,
708 bytesReceived
- bytesRead
, bytesRead
);
710 fListener
->DownloadProgress(this, bytesReceived
,
711 std::max((ssize_t
)0, bytesTotal
));
714 if (bytesTotal
>= 0 && bytesReceived
>= bytesTotal
)
717 if (decompress
&& receiveEnd
) {
718 readError
= decompressingStream
->Flush();
720 if (readError
== B_BUFFER_OVERFLOW
)
723 if (readError
!= B_OK
)
726 ssize_t size
= decompressorStorage
.Size();
727 BStackOrHeapArray
<char, 4096> buffer(size
);
728 size
= decompressorStorage
.Read(buffer
, size
);
729 if (fListener
!= NULL
&& size
> 0) {
730 fListener
->DataReceived(this, buffer
,
731 bytesUnpacked
, size
);
732 bytesUnpacked
+= size
;
738 parseEnd
= (fInputBuffer
.Size() == 0);
741 fSocket
->Disconnect();
742 delete[] inputTempBuffer
;
744 if (readError
!= B_OK
)
747 return fQuit
? B_INTERRUPTED
: B_OK
;
752 BHttpRequest::_ParseStatus()
754 // Status line should be formatted like: HTTP/M.m SSS ...
755 // With: M = Major version of the protocol
756 // m = Minor version of the protocol
757 // SSS = three-digit status code of the response
758 // ... = additional text info
760 if (_GetLine(statusLine
) == B_ERROR
)
763 if (statusLine
.CountChars() < 12)
766 fRequestStatus
= kRequestStatusReceived
;
768 BString statusCodeStr
;
770 statusLine
.CopyInto(statusCodeStr
, 9, 3);
771 _SetResultStatusCode(atoi(statusCodeStr
.String()));
773 statusLine
.CopyInto(_ResultStatusText(), 13, statusLine
.Length() - 13);
775 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT
, "Status line received: Code %d (%s)",
776 atoi(statusCodeStr
.String()), _ResultStatusText().String());
781 BHttpRequest::_ParseHeaders()
783 BString currentHeader
;
784 while (_GetLine(currentHeader
) != B_ERROR
) {
785 // An empty line means the end of the header section
786 if (currentHeader
.Length() == 0) {
787 fRequestStatus
= kRequestHeadersReceived
;
791 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN
, "%s",
792 currentHeader
.String());
793 fHeaders
.AddHeader(currentHeader
.String());
799 BHttpRequest::_SendRequest()
801 BString
request(fRequestMethod
);
804 if (fContext
->UseProxy()) {
805 // When there is a proxy, the request must include the host and port so
806 // the proxy knows where to send the request.
807 request
<< Url().Protocol() << "://" << Url().Host();
809 request
<< ':' << Url().Port();
813 request
<< Url().Path();
817 if (Url().HasRequest())
818 request
<< '?' << Url().Request();
820 switch (fHttpVersion
) {
822 request
<< " HTTP/1.1\r\n";
827 request
<< " HTTP/1.0\r\n";
831 fSocket
->Write(request
.String(), request
.Length());
836 BHttpRequest::_SendHeaders()
838 BHttpHeaders outputHeaders
;
840 // HTTP 1.1 additional headers
841 if (fHttpVersion
== B_HTTP_11
) {
842 BString host
= Url().Host();
843 if (Url().HasPort() && !_IsDefaultPort())
844 host
<< ':' << Url().Port();
846 outputHeaders
.AddHeader("Host", host
);
848 outputHeaders
.AddHeader("Accept", "*/*");
849 outputHeaders
.AddHeader("Accept-Encoding", "gzip");
850 // Allows the server to compress data using the "gzip" format.
851 // "deflate" is not supported, because there are two interpretations
852 // of what it means (the RFC and Microsoft products), and we don't
853 // want to handle this. Very few websites support only deflate,
854 // and most of them will send gzip, or at worst, uncompressed data.
856 outputHeaders
.AddHeader("Connection", "close");
857 // Let the remote server close the connection after response since
858 // we don't handle multiple request on a single connection
861 // Classic HTTP headers
862 if (fOptUserAgent
.CountChars() > 0)
863 outputHeaders
.AddHeader("User-Agent", fOptUserAgent
.String());
865 if (fOptReferer
.CountChars() > 0)
866 outputHeaders
.AddHeader("Referer", fOptReferer
.String());
869 if (fContext
!= NULL
) {
870 BHttpAuthentication
& authentication
= fContext
->GetAuthentication(fUrl
);
871 if (authentication
.Method() != B_HTTP_AUTHENTICATION_NONE
) {
872 if (fOptUsername
.Length() > 0) {
873 authentication
.SetUserName(fOptUsername
);
874 authentication
.SetPassword(fOptPassword
);
877 BString
request(fRequestMethod
);
878 outputHeaders
.AddHeader("Authorization",
879 authentication
.Authorization(fUrl
, request
));
883 // Required headers for POST data
884 if (fOptPostFields
!= NULL
&& fRequestMethod
== B_HTTP_POST
) {
887 switch (fOptPostFields
->GetFormType()) {
888 case B_HTTP_FORM_MULTIPART
:
889 contentType
<< "multipart/form-data; boundary="
890 << fOptPostFields
->GetMultipartBoundary() << "";
893 case B_HTTP_FORM_URL_ENCODED
:
894 contentType
<< "application/x-www-form-urlencoded";
898 outputHeaders
.AddHeader("Content-Type", contentType
);
899 outputHeaders
.AddHeader("Content-Length",
900 fOptPostFields
->ContentLength());
901 } else if (fOptInputData
!= NULL
902 && (fRequestMethod
== B_HTTP_POST
903 || fRequestMethod
== B_HTTP_PUT
)) {
904 if (fOptInputDataSize
>= 0)
905 outputHeaders
.AddHeader("Content-Length", fOptInputDataSize
);
907 outputHeaders
.AddHeader("Transfer-Encoding", "chunked");
910 // Optional headers specified by the user
911 if (fOptHeaders
!= NULL
) {
912 for (int32 headerIndex
= 0; headerIndex
< fOptHeaders
->CountHeaders();
914 BHttpHeader
& optHeader
= (*fOptHeaders
)[headerIndex
];
915 int32 replaceIndex
= outputHeaders
.HasHeader(optHeader
.Name());
917 // Add or replace the current option header to the
918 // output header list
919 if (replaceIndex
== -1)
920 outputHeaders
.AddHeader(optHeader
.Name(), optHeader
.Value());
922 outputHeaders
[replaceIndex
].SetValue(optHeader
.Value());
927 if (fOptSetCookies
&& fContext
!= NULL
) {
928 BString cookieString
;
930 BNetworkCookieJar::UrlIterator iterator
931 = fContext
->GetCookieJar().GetUrlIterator(fUrl
);
932 const BNetworkCookie
* cookie
= iterator
.Next();
933 if (cookie
!= NULL
) {
935 cookieString
<< cookie
->RawCookie(false);
936 cookie
= iterator
.Next();
939 cookieString
<< "; ";
942 outputHeaders
.AddHeader("Cookie", cookieString
);
946 // Write output headers to output stream
949 for (int32 headerIndex
= 0; headerIndex
< outputHeaders
.CountHeaders();
951 const char* header
= outputHeaders
.HeaderAt(headerIndex
).Header();
953 headerData
<< header
;
954 headerData
<< "\r\n";
956 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT
, "%s", header
);
959 fSocket
->Write(headerData
.String(), headerData
.Length());
964 BHttpRequest::_SendPostData()
966 if (fRequestMethod
== B_HTTP_POST
&& fOptPostFields
!= NULL
) {
967 if (fOptPostFields
->GetFormType() != B_HTTP_FORM_MULTIPART
) {
968 BString outputBuffer
= fOptPostFields
->RawData();
969 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT
,
970 "%s", outputBuffer
.String());
971 fSocket
->Write(outputBuffer
.String(), outputBuffer
.Length());
973 for (BHttpForm::Iterator it
= fOptPostFields
->GetIterator();
974 const BHttpFormData
* currentField
= it
.Next();
976 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT
,
977 it
.MultipartHeader().String());
978 fSocket
->Write(it
.MultipartHeader().String(),
979 it
.MultipartHeader().Length());
981 switch (currentField
->Type()) {
983 case B_HTTPFORM_UNKNOWN
:
987 case B_HTTPFORM_STRING
:
988 fSocket
->Write(currentField
->String().String(),
989 currentField
->String().Length());
992 case B_HTTPFORM_FILE
:
994 BFile
upFile(currentField
->File().Path(),
996 char readBuffer
[kHttpBufferSize
];
999 readSize
= upFile
.Read(readBuffer
,
1000 sizeof(readBuffer
));
1001 while (readSize
> 0) {
1002 fSocket
->Write(readBuffer
, readSize
);
1003 readSize
= upFile
.Read(readBuffer
,
1004 sizeof(readBuffer
));
1009 case B_HTTPFORM_BUFFER
:
1010 fSocket
->Write(currentField
->Buffer(),
1011 currentField
->BufferSize());
1015 fSocket
->Write("\r\n", 2);
1018 BString footer
= fOptPostFields
->GetMultipartFooter();
1019 fSocket
->Write(footer
.String(), footer
.Length());
1021 } else if ((fRequestMethod
== B_HTTP_POST
|| fRequestMethod
== B_HTTP_PUT
)
1022 && fOptInputData
!= NULL
) {
1024 // If the input data is seekable, we rewind it for each new request.
1025 BPositionIO
* seekableData
1026 = dynamic_cast<BPositionIO
*>(fOptInputData
);
1028 seekableData
->Seek(0, SEEK_SET
);
1031 char outputTempBuffer
[kHttpBufferSize
];
1032 ssize_t read
= fOptInputData
->Read(outputTempBuffer
,
1033 sizeof(outputTempBuffer
));
1038 if (fOptInputDataSize
< 0) {
1041 size_t hexLength
= sprintf(hexSize
, "%ld", read
);
1043 fSocket
->Write(hexSize
, hexLength
);
1044 fSocket
->Write("\r\n", 2);
1045 fSocket
->Write(outputTempBuffer
, read
);
1046 fSocket
->Write("\r\n", 2);
1048 fSocket
->Write(outputTempBuffer
, read
);
1052 if (fOptInputDataSize
< 0) {
1053 // Chunked transfer terminating sequence
1054 fSocket
->Write("0\r\n\r\n", 5);
1062 BHttpRequest::_ResultHeaders()
1064 return fResult
.fHeaders
;
1069 BHttpRequest::_SetResultStatusCode(int32 statusCode
)
1071 fResult
.fStatusCode
= statusCode
;
1076 BHttpRequest::_ResultStatusText()
1078 return fResult
.fStatusString
;
1083 BHttpRequest::_CertificateVerificationFailed(BCertificate
& certificate
,
1084 const char* message
)
1086 if (fListener
!= NULL
) {
1087 return fListener
->CertificateVerificationFailed(this, certificate
,
1096 BHttpRequest::_IsDefaultPort()
1098 if (fSSL
&& Url().Port() == 443)
1100 if (!fSSL
&& Url().Port() == 80)