vfs: check userland buffers before reading them.
[haiku.git] / src / kits / network / libnetapi / HttpRequest.cpp
blob3b015debcc9b90b974ededb8eadb90c70751ff1f
1 /*
2 * Copyright 2010-2015 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Christophe Huriaux, c.huriaux@gmail.com
7 * Niels Sascha Reedijk, niels.reedijk@gmail.com
8 * Adrien Destugues, pulkomandy@pulkomandy.tk
9 */
12 #include <HttpRequest.h>
14 #include <arpa/inet.h>
15 #include <stdio.h>
17 #include <cstdlib>
18 #include <deque>
19 #include <new>
21 #include <AutoDeleter.h>
22 #include <Certificate.h>
23 #include <Debug.h>
24 #include <DynamicBuffer.h>
25 #include <File.h>
26 #include <ProxySecureSocket.h>
27 #include <Socket.h>
28 #include <SecureSocket.h>
29 #include <StackOrHeapArray.h>
30 #include <ZlibCompressionAlgorithm.h>
33 static const int32 kHttpBufferSize = 4096;
36 namespace BPrivate {
38 class CheckedSecureSocket: public BSecureSocket
40 public:
41 CheckedSecureSocket(BHttpRequest* request);
43 bool CertificateVerificationFailed(BCertificate& certificate,
44 const char* message);
46 private:
47 BHttpRequest* fRequest;
51 CheckedSecureSocket::CheckedSecureSocket(BHttpRequest* request)
53 BSecureSocket(),
54 fRequest(request)
59 bool
60 CheckedSecureSocket::CertificateVerificationFailed(BCertificate& certificate,
61 const char* message)
63 return fRequest->_CertificateVerificationFailed(certificate, message);
67 class CheckedProxySecureSocket: public BProxySecureSocket
69 public:
70 CheckedProxySecureSocket(const BNetworkAddress& proxy, BHttpRequest* request);
72 bool CertificateVerificationFailed(BCertificate& certificate,
73 const char* message);
75 private:
76 BHttpRequest* fRequest;
80 CheckedProxySecureSocket::CheckedProxySecureSocket(const BNetworkAddress& proxy,
81 BHttpRequest* request)
83 BProxySecureSocket(proxy),
84 fRequest(request)
89 bool
90 CheckedProxySecureSocket::CertificateVerificationFailed(BCertificate& certificate,
91 const char* message)
93 return fRequest->_CertificateVerificationFailed(certificate, message);
98 BHttpRequest::BHttpRequest(const BUrl& url, bool ssl, const char* protocolName,
99 BUrlProtocolListener* listener, BUrlContext* context)
101 BNetworkRequest(url, listener, context, "BUrlProtocol.HTTP", protocolName),
102 fSSL(ssl),
103 fRequestMethod(B_HTTP_GET),
104 fHttpVersion(B_HTTP_11),
105 fResult(url),
106 fRequestStatus(kRequestInitialState),
107 fOptHeaders(NULL),
108 fOptPostFields(NULL),
109 fOptInputData(NULL),
110 fOptInputDataSize(-1),
111 fOptRangeStart(-1),
112 fOptRangeEnd(-1),
113 fOptFollowLocation(true)
115 _ResetOptions();
116 fSocket = NULL;
120 BHttpRequest::BHttpRequest(const BHttpRequest& other)
122 BNetworkRequest(other.Url(), other.fListener, other.fContext,
123 "BUrlProtocol.HTTP", other.fSSL ? "HTTPS" : "HTTP"),
124 fSSL(other.fSSL),
125 fRequestMethod(other.fRequestMethod),
126 fHttpVersion(other.fHttpVersion),
127 fResult(other.fUrl),
128 fRequestStatus(kRequestInitialState),
129 fOptHeaders(NULL),
130 fOptPostFields(NULL),
131 fOptInputData(NULL),
132 fOptInputDataSize(-1),
133 fOptRangeStart(other.fOptRangeStart),
134 fOptRangeEnd(other.fOptRangeEnd),
135 fOptFollowLocation(other.fOptFollowLocation)
137 _ResetOptions();
138 // FIXME some options may be copied from other instead.
139 fSocket = NULL;
143 BHttpRequest::~BHttpRequest()
145 Stop();
147 delete fSocket;
149 delete fOptInputData;
150 delete fOptHeaders;
151 delete fOptPostFields;
155 void
156 BHttpRequest::SetMethod(const char* const method)
158 fRequestMethod = method;
162 void
163 BHttpRequest::SetFollowLocation(bool follow)
165 fOptFollowLocation = follow;
169 void
170 BHttpRequest::SetMaxRedirections(int8 redirections)
172 fOptMaxRedirs = redirections;
176 void
177 BHttpRequest::SetReferrer(const BString& referrer)
179 fOptReferer = referrer;
183 void
184 BHttpRequest::SetUserAgent(const BString& agent)
186 fOptUserAgent = agent;
190 void
191 BHttpRequest::SetDiscardData(bool discard)
193 fOptDiscardData = discard;
197 void
198 BHttpRequest::SetDisableListener(bool disable)
200 fOptDisableListener = disable;
204 void
205 BHttpRequest::SetAutoReferrer(bool enable)
207 fOptAutoReferer = enable;
211 void
212 BHttpRequest::SetHeaders(const BHttpHeaders& headers)
214 AdoptHeaders(new(std::nothrow) BHttpHeaders(headers));
218 void
219 BHttpRequest::AdoptHeaders(BHttpHeaders* const headers)
221 delete fOptHeaders;
222 fOptHeaders = headers;
226 void
227 BHttpRequest::SetPostFields(const BHttpForm& fields)
229 AdoptPostFields(new(std::nothrow) BHttpForm(fields));
233 void
234 BHttpRequest::AdoptPostFields(BHttpForm* const fields)
236 delete fOptPostFields;
237 fOptPostFields = fields;
239 if (fOptPostFields != NULL)
240 fRequestMethod = B_HTTP_POST;
244 void
245 BHttpRequest::AdoptInputData(BDataIO* const data, const ssize_t size)
247 delete fOptInputData;
248 fOptInputData = data;
249 fOptInputDataSize = size;
253 void
254 BHttpRequest::SetUserName(const BString& name)
256 fOptUsername = name;
260 void
261 BHttpRequest::SetPassword(const BString& password)
263 fOptPassword = password;
267 /*static*/ bool
268 BHttpRequest::IsInformationalStatusCode(int16 code)
270 return (code >= B_HTTP_STATUS__INFORMATIONAL_BASE)
271 && (code < B_HTTP_STATUS__INFORMATIONAL_END);
275 /*static*/ bool
276 BHttpRequest::IsSuccessStatusCode(int16 code)
278 return (code >= B_HTTP_STATUS__SUCCESS_BASE)
279 && (code < B_HTTP_STATUS__SUCCESS_END);
283 /*static*/ bool
284 BHttpRequest::IsRedirectionStatusCode(int16 code)
286 return (code >= B_HTTP_STATUS__REDIRECTION_BASE)
287 && (code < B_HTTP_STATUS__REDIRECTION_END);
291 /*static*/ bool
292 BHttpRequest::IsClientErrorStatusCode(int16 code)
294 return (code >= B_HTTP_STATUS__CLIENT_ERROR_BASE)
295 && (code < B_HTTP_STATUS__CLIENT_ERROR_END);
299 /*static*/ bool
300 BHttpRequest::IsServerErrorStatusCode(int16 code)
302 return (code >= B_HTTP_STATUS__SERVER_ERROR_BASE)
303 && (code < B_HTTP_STATUS__SERVER_ERROR_END);
307 /*static*/ int16
308 BHttpRequest::StatusCodeClass(int16 code)
310 if (BHttpRequest::IsInformationalStatusCode(code))
311 return B_HTTP_STATUS_CLASS_INFORMATIONAL;
312 else if (BHttpRequest::IsSuccessStatusCode(code))
313 return B_HTTP_STATUS_CLASS_SUCCESS;
314 else if (BHttpRequest::IsRedirectionStatusCode(code))
315 return B_HTTP_STATUS_CLASS_REDIRECTION;
316 else if (BHttpRequest::IsClientErrorStatusCode(code))
317 return B_HTTP_STATUS_CLASS_CLIENT_ERROR;
318 else if (BHttpRequest::IsServerErrorStatusCode(code))
319 return B_HTTP_STATUS_CLASS_SERVER_ERROR;
321 return B_HTTP_STATUS_CLASS_INVALID;
325 const BUrlResult&
326 BHttpRequest::Result() const
328 return fResult;
332 status_t
333 BHttpRequest::Stop()
335 if (fSocket != NULL) {
336 fSocket->Disconnect();
337 // Unlock any pending connect, read or write operation.
339 return BNetworkRequest::Stop();
343 void
344 BHttpRequest::_ResetOptions()
346 delete fOptPostFields;
347 delete fOptHeaders;
349 fOptFollowLocation = true;
350 fOptMaxRedirs = 8;
351 fOptReferer = "";
352 fOptUserAgent = "Services Kit (Haiku)";
353 fOptUsername = "";
354 fOptPassword = "";
355 fOptAuthMethods = B_HTTP_AUTHENTICATION_BASIC | B_HTTP_AUTHENTICATION_DIGEST
356 | B_HTTP_AUTHENTICATION_IE_DIGEST;
357 fOptHeaders = NULL;
358 fOptPostFields = NULL;
359 fOptSetCookies = true;
360 fOptDiscardData = false;
361 fOptDisableListener = false;
362 fOptAutoReferer = true;
366 status_t
367 BHttpRequest::_ProtocolLoop()
369 // Initialize the request redirection loop
370 int8 maxRedirs = fOptMaxRedirs;
371 bool newRequest;
373 do {
374 newRequest = false;
376 // Result reset
377 fHeaders.Clear();
378 _ResultHeaders().Clear();
380 BString host = fUrl.Host();
381 int port = fSSL ? 443 : 80;
383 if (fUrl.HasPort())
384 port = fUrl.Port();
386 if (fContext->UseProxy()) {
387 host = fContext->GetProxyHost();
388 port = fContext->GetProxyPort();
391 status_t result = fInputBuffer.InitCheck();
392 if (result != B_OK)
393 return result;
395 if (!_ResolveHostName(host, port)) {
396 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR,
397 "Unable to resolve hostname (%s), aborting.",
398 fUrl.Host().String());
399 return B_SERVER_NOT_FOUND;
402 status_t requestStatus = _MakeRequest();
403 if (requestStatus != B_OK)
404 return requestStatus;
406 // Prepare the referer for the next request if needed
407 if (fOptAutoReferer)
408 fOptReferer = fUrl.UrlString();
410 switch (StatusCodeClass(fResult.StatusCode())) {
411 case B_HTTP_STATUS_CLASS_INFORMATIONAL:
412 // Header 100:continue should have been
413 // handled in the _MakeRequest read loop
414 break;
416 case B_HTTP_STATUS_CLASS_SUCCESS:
417 break;
419 case B_HTTP_STATUS_CLASS_REDIRECTION:
421 // Redirection has been explicitly disabled
422 if (!fOptFollowLocation)
423 break;
425 int code = fResult.StatusCode();
426 if (code == B_HTTP_STATUS_MOVED_PERMANENTLY
427 || code == B_HTTP_STATUS_FOUND
428 || code == B_HTTP_STATUS_SEE_OTHER
429 || code == B_HTTP_STATUS_TEMPORARY_REDIRECT) {
430 BString locationUrl = fHeaders["Location"];
432 fUrl = BUrl(fUrl, locationUrl);
434 // 302 and 303 redirections also convert POST requests to GET
435 // (and remove the posted form data)
436 if ((code == B_HTTP_STATUS_FOUND
437 || code == B_HTTP_STATUS_SEE_OTHER)
438 && fRequestMethod == B_HTTP_POST) {
439 SetMethod(B_HTTP_GET);
440 delete fOptPostFields;
441 fOptPostFields = NULL;
442 delete fOptInputData;
443 fOptInputData = NULL;
444 fOptInputDataSize = 0;
447 if (--maxRedirs > 0) {
448 newRequest = true;
450 // Redirections may need a switch from http to https.
451 if (fUrl.Protocol() == "https")
452 fSSL = true;
453 else if (fUrl.Protocol() == "http")
454 fSSL = false;
456 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
457 "Following: %s\n",
458 fUrl.UrlString().String());
461 break;
464 case B_HTTP_STATUS_CLASS_CLIENT_ERROR:
465 if (fResult.StatusCode() == B_HTTP_STATUS_UNAUTHORIZED) {
466 BHttpAuthentication* authentication
467 = &fContext->GetAuthentication(fUrl);
468 status_t status = B_OK;
470 if (authentication->Method() == B_HTTP_AUTHENTICATION_NONE) {
471 // There is no authentication context for this
472 // url yet, so let's create one.
473 BHttpAuthentication newAuth;
474 newAuth.Initialize(fHeaders["WWW-Authenticate"]);
475 fContext->AddAuthentication(fUrl, newAuth);
477 // Get the copy of the authentication we just added.
478 // That copy is owned by the BUrlContext and won't be
479 // deleted (unlike the temporary object above)
480 authentication = &fContext->GetAuthentication(fUrl);
483 newRequest = false;
484 if (fOptUsername.Length() > 0 && status == B_OK) {
485 // If we received an username and password, add them
486 // to the request. This will either change the
487 // credentials for an existing request, or set them
488 // for a new one we created just above.
490 // If this request handles HTTP redirections, it will
491 // also automatically retry connecting and send the
492 // login information.
493 authentication->SetUserName(fOptUsername);
494 authentication->SetPassword(fOptPassword);
495 newRequest = true;
498 break;
500 case B_HTTP_STATUS_CLASS_SERVER_ERROR:
501 break;
503 default:
504 case B_HTTP_STATUS_CLASS_INVALID:
505 break;
507 } while (newRequest);
509 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
510 "%ld headers and %ld bytes of data remaining",
511 fHeaders.CountHeaders(), fInputBuffer.Size());
513 if (fResult.StatusCode() == 404)
514 return B_RESOURCE_NOT_FOUND;
516 return B_OK;
520 status_t
521 BHttpRequest::_MakeRequest()
523 delete fSocket;
525 if (fSSL) {
526 if (fContext->UseProxy()) {
527 BNetworkAddress proxy(fContext->GetProxyHost(), fContext->GetProxyPort());
528 fSocket = new(std::nothrow) BPrivate::CheckedProxySecureSocket(proxy, this);
529 } else
530 fSocket = new(std::nothrow) BPrivate::CheckedSecureSocket(this);
531 } else
532 fSocket = new(std::nothrow) BSocket();
534 if (fSocket == NULL)
535 return B_NO_MEMORY;
537 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.",
538 fUrl.Authority().String(), fRemoteAddr.Port());
539 status_t connectError = fSocket->Connect(fRemoteAddr);
541 if (connectError != B_OK) {
542 _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s",
543 strerror(connectError));
544 return connectError;
547 //! ProtocolHook:ConnectionOpened
548 if (fListener != NULL)
549 fListener->ConnectionOpened(this);
551 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
552 "Connection opened, sending request.");
554 _SendRequest();
555 _SendHeaders();
556 fSocket->Write("\r\n", 2);
557 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent.");
559 _SendPostData();
560 fRequestStatus = kRequestInitialState;
564 // Receive loop
565 bool receiveEnd = false;
566 bool parseEnd = false;
567 bool readByChunks = false;
568 bool decompress = false;
569 status_t readError = B_OK;
570 ssize_t bytesRead = 0;
571 ssize_t bytesReceived = 0;
572 ssize_t bytesTotal = 0;
573 size_t previousBufferSize = 0;
574 off_t bytesUnpacked = 0;
575 char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize];
576 ssize_t inputTempSize = kHttpBufferSize;
577 ssize_t chunkSize = -1;
578 DynamicBuffer decompressorStorage;
579 BDataIO* decompressingStream;
580 ObjectDeleter<BDataIO> decompressingStreamDeleter;
582 while (!fQuit && !(receiveEnd && parseEnd)) {
583 if ((!receiveEnd) && (fInputBuffer.Size() == previousBufferSize)) {
584 fSocket->WaitForReadable();
585 BStackOrHeapArray<char, 4096> chunk(kHttpBufferSize);
586 bytesRead = fSocket->Read(chunk, kHttpBufferSize);
588 if (bytesRead < 0) {
589 readError = bytesRead;
590 break;
591 } else if (bytesRead == 0)
592 receiveEnd = true;
594 fInputBuffer.AppendData(chunk, bytesRead);
595 } else
596 bytesRead = 0;
598 previousBufferSize = fInputBuffer.Size();
600 if (fRequestStatus < kRequestStatusReceived) {
601 _ParseStatus();
603 //! ProtocolHook:ResponseStarted
604 if (fRequestStatus >= kRequestStatusReceived && fListener != NULL)
605 fListener->ResponseStarted(this);
608 if (fRequestStatus < kRequestHeadersReceived) {
609 _ParseHeaders();
611 if (fRequestStatus >= kRequestHeadersReceived) {
612 _ResultHeaders() = fHeaders;
614 // Parse received cookies
615 if (fContext != NULL) {
616 for (int32 i = 0; i < fHeaders.CountHeaders(); i++) {
617 if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) {
618 fContext->GetCookieJar().AddCookie(
619 fHeaders.HeaderAt(i).Value(), fUrl);
624 //! ProtocolHook:HeadersReceived
625 if (fListener != NULL)
626 fListener->HeadersReceived(this, fResult);
629 if (BString(fHeaders["Transfer-Encoding"]) == "chunked")
630 readByChunks = true;
632 BString contentEncoding(fHeaders["Content-Encoding"]);
633 // We don't advertise "deflate" support (see above), but we
634 // still try to decompress it, if a server ever sends a deflate
635 // stream despite it not being in our Accept-Encoding list.
636 if (contentEncoding == "gzip"
637 || contentEncoding == "deflate") {
638 decompress = true;
639 readError = BZlibCompressionAlgorithm()
640 .CreateDecompressingOutputStream(&decompressorStorage,
641 NULL, decompressingStream);
642 if (readError != B_OK)
643 break;
645 decompressingStreamDeleter.SetTo(decompressingStream);
648 int32 index = fHeaders.HasHeader("Content-Length");
649 if (index != B_ERROR)
650 bytesTotal = atoi(fHeaders.HeaderAt(index).Value());
651 else
652 bytesTotal = -1;
656 if (fRequestStatus >= kRequestHeadersReceived) {
657 // If Transfer-Encoding is chunked, we should read a complete
658 // chunk in buffer before handling it
659 if (readByChunks) {
660 if (chunkSize >= 0) {
661 if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) {
662 // 2 more bytes to handle the closing CR+LF
663 bytesRead = chunkSize;
664 if (inputTempSize < chunkSize + 2) {
665 delete[] inputTempBuffer;
666 inputTempSize = chunkSize + 2;
667 inputTempBuffer
668 = new(std::nothrow) char[inputTempSize];
671 if (inputTempBuffer == NULL) {
672 readError = B_NO_MEMORY;
673 break;
676 fInputBuffer.RemoveData(inputTempBuffer,
677 chunkSize + 2);
678 chunkSize = -1;
679 } else {
680 // Not enough data, try again later
681 bytesRead = -1;
683 } else {
684 BString chunkHeader;
685 if (_GetLine(chunkHeader) == B_ERROR) {
686 chunkSize = -1;
687 bytesRead = -1;
688 } else {
689 // Format of a chunk header:
690 // <chunk size in hex>[; optional data]
691 int32 semiColonIndex = chunkHeader.FindFirst(';', 0);
693 // Cut-off optional data if present
694 if (semiColonIndex != -1) {
695 chunkHeader.Remove(semiColonIndex,
696 chunkHeader.Length() - semiColonIndex);
699 chunkSize = strtol(chunkHeader.String(), NULL, 16);
700 if (chunkSize == 0)
701 fRequestStatus = kRequestContentReceived;
703 bytesRead = -1;
707 // A chunk of 0 bytes indicates the end of the chunked transfer
708 if (bytesRead == 0)
709 receiveEnd = true;
710 } else {
711 bytesRead = fInputBuffer.Size();
713 if (bytesRead > 0) {
714 if (inputTempSize < bytesRead) {
715 inputTempSize = bytesRead;
716 delete[] inputTempBuffer;
717 inputTempBuffer = new(std::nothrow) char[bytesRead];
720 if (inputTempBuffer == NULL) {
721 readError = B_NO_MEMORY;
722 break;
724 fInputBuffer.RemoveData(inputTempBuffer, bytesRead);
728 if (bytesRead >= 0) {
729 bytesReceived += bytesRead;
731 if (fListener != NULL) {
732 if (decompress) {
733 readError = decompressingStream->WriteExactly(
734 inputTempBuffer, bytesRead);
735 if (readError != B_OK)
736 break;
738 ssize_t size = decompressorStorage.Size();
739 BStackOrHeapArray<char, 4096> buffer(size);
740 size = decompressorStorage.Read(buffer, size);
741 if (size > 0) {
742 fListener->DataReceived(this, buffer, bytesUnpacked,
743 size);
744 bytesUnpacked += size;
746 } else if (bytesRead > 0) {
747 fListener->DataReceived(this, inputTempBuffer,
748 bytesReceived - bytesRead, bytesRead);
750 fListener->DownloadProgress(this, bytesReceived,
751 std::max((ssize_t)0, bytesTotal));
754 if (bytesTotal >= 0 && bytesReceived >= bytesTotal)
755 receiveEnd = true;
757 if (decompress && receiveEnd) {
758 readError = decompressingStream->Flush();
760 if (readError == B_BUFFER_OVERFLOW)
761 readError = B_OK;
763 if (readError != B_OK)
764 break;
766 ssize_t size = decompressorStorage.Size();
767 BStackOrHeapArray<char, 4096> buffer(size);
768 size = decompressorStorage.Read(buffer, size);
769 if (fListener != NULL && size > 0) {
770 fListener->DataReceived(this, buffer,
771 bytesUnpacked, size);
772 bytesUnpacked += size;
778 parseEnd = (fInputBuffer.Size() == 0);
781 fSocket->Disconnect();
782 delete[] inputTempBuffer;
784 if (readError != B_OK)
785 return readError;
787 return fQuit ? B_INTERRUPTED : B_OK;
791 void
792 BHttpRequest::_ParseStatus()
794 // Status line should be formatted like: HTTP/M.m SSS ...
795 // With: M = Major version of the protocol
796 // m = Minor version of the protocol
797 // SSS = three-digit status code of the response
798 // ... = additional text info
799 BString statusLine;
800 if (_GetLine(statusLine) == B_ERROR)
801 return;
803 if (statusLine.CountChars() < 12)
804 return;
806 fRequestStatus = kRequestStatusReceived;
808 BString statusCodeStr;
809 BString statusText;
810 statusLine.CopyInto(statusCodeStr, 9, 3);
811 _SetResultStatusCode(atoi(statusCodeStr.String()));
813 statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13);
815 _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)",
816 atoi(statusCodeStr.String()), _ResultStatusText().String());
820 void
821 BHttpRequest::_ParseHeaders()
823 BString currentHeader;
824 while (_GetLine(currentHeader) != B_ERROR) {
825 // An empty line means the end of the header section
826 if (currentHeader.Length() == 0) {
827 fRequestStatus = kRequestHeadersReceived;
828 return;
831 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s",
832 currentHeader.String());
833 fHeaders.AddHeader(currentHeader.String());
838 void
839 BHttpRequest::_SendRequest()
841 BString request(fRequestMethod);
842 request << ' ';
844 if (fContext->UseProxy()) {
845 // When there is a proxy, the request must include the host and port so
846 // the proxy knows where to send the request.
847 request << Url().Protocol() << "://" << Url().Host();
848 if (Url().HasPort())
849 request << ':' << Url().Port();
852 if (Url().HasPath() && Url().Path().Length() > 0)
853 request << Url().Path();
854 else
855 request << '/';
857 if (Url().HasRequest())
858 request << '?' << Url().Request();
860 switch (fHttpVersion) {
861 case B_HTTP_11:
862 request << " HTTP/1.1\r\n";
863 break;
865 default:
866 case B_HTTP_10:
867 request << " HTTP/1.0\r\n";
868 break;
871 fSocket->Write(request.String(), request.Length());
875 void
876 BHttpRequest::_SendHeaders()
878 BHttpHeaders outputHeaders;
880 // HTTP 1.1 additional headers
881 if (fHttpVersion == B_HTTP_11) {
882 BString host = Url().Host();
883 if (Url().HasPort() && !_IsDefaultPort())
884 host << ':' << Url().Port();
886 outputHeaders.AddHeader("Host", host);
888 outputHeaders.AddHeader("Accept", "*/*");
889 outputHeaders.AddHeader("Accept-Encoding", "gzip");
890 // Allows the server to compress data using the "gzip" format.
891 // "deflate" is not supported, because there are two interpretations
892 // of what it means (the RFC and Microsoft products), and we don't
893 // want to handle this. Very few websites support only deflate,
894 // and most of them will send gzip, or at worst, uncompressed data.
896 outputHeaders.AddHeader("Connection", "close");
897 // Let the remote server close the connection after response since
898 // we don't handle multiple request on a single connection
901 // Classic HTTP headers
902 if (fOptUserAgent.CountChars() > 0)
903 outputHeaders.AddHeader("User-Agent", fOptUserAgent.String());
905 if (fOptReferer.CountChars() > 0)
906 outputHeaders.AddHeader("Referer", fOptReferer.String());
908 // Authentication
909 if (fContext != NULL) {
910 BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl);
911 if (authentication.Method() != B_HTTP_AUTHENTICATION_NONE) {
912 if (fOptUsername.Length() > 0) {
913 authentication.SetUserName(fOptUsername);
914 authentication.SetPassword(fOptPassword);
917 BString request(fRequestMethod);
918 outputHeaders.AddHeader("Authorization",
919 authentication.Authorization(fUrl, request));
923 // Required headers for POST data
924 if (fOptPostFields != NULL && fRequestMethod == B_HTTP_POST) {
925 BString contentType;
927 switch (fOptPostFields->GetFormType()) {
928 case B_HTTP_FORM_MULTIPART:
929 contentType << "multipart/form-data; boundary="
930 << fOptPostFields->GetMultipartBoundary() << "";
931 break;
933 case B_HTTP_FORM_URL_ENCODED:
934 contentType << "application/x-www-form-urlencoded";
935 break;
938 outputHeaders.AddHeader("Content-Type", contentType);
939 outputHeaders.AddHeader("Content-Length",
940 fOptPostFields->ContentLength());
941 } else if (fOptInputData != NULL
942 && (fRequestMethod == B_HTTP_POST
943 || fRequestMethod == B_HTTP_PUT)) {
944 if (fOptInputDataSize >= 0)
945 outputHeaders.AddHeader("Content-Length", fOptInputDataSize);
946 else
947 outputHeaders.AddHeader("Transfer-Encoding", "chunked");
950 // Optional headers specified by the user
951 if (fOptHeaders != NULL) {
952 for (int32 headerIndex = 0; headerIndex < fOptHeaders->CountHeaders();
953 headerIndex++) {
954 BHttpHeader& optHeader = (*fOptHeaders)[headerIndex];
955 int32 replaceIndex = outputHeaders.HasHeader(optHeader.Name());
957 // Add or replace the current option header to the
958 // output header list
959 if (replaceIndex == -1)
960 outputHeaders.AddHeader(optHeader.Name(), optHeader.Value());
961 else
962 outputHeaders[replaceIndex].SetValue(optHeader.Value());
966 // Context cookies
967 if (fOptSetCookies && fContext != NULL) {
968 BString cookieString;
970 BNetworkCookieJar::UrlIterator iterator
971 = fContext->GetCookieJar().GetUrlIterator(fUrl);
972 const BNetworkCookie* cookie = iterator.Next();
973 if (cookie != NULL) {
974 while (true) {
975 cookieString << cookie->RawCookie(false);
976 cookie = iterator.Next();
977 if (cookie == NULL)
978 break;
979 cookieString << "; ";
982 outputHeaders.AddHeader("Cookie", cookieString);
986 // Write output headers to output stream
987 BString headerData;
989 for (int32 headerIndex = 0; headerIndex < outputHeaders.CountHeaders();
990 headerIndex++) {
991 const char* header = outputHeaders.HeaderAt(headerIndex).Header();
993 headerData << header;
994 headerData << "\r\n";
996 _EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", header);
999 fSocket->Write(headerData.String(), headerData.Length());
1003 void
1004 BHttpRequest::_SendPostData()
1006 if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) {
1007 if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) {
1008 BString outputBuffer = fOptPostFields->RawData();
1009 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
1010 "%s", outputBuffer.String());
1011 fSocket->Write(outputBuffer.String(), outputBuffer.Length());
1012 } else {
1013 for (BHttpForm::Iterator it = fOptPostFields->GetIterator();
1014 const BHttpFormData* currentField = it.Next();
1016 _EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
1017 it.MultipartHeader().String());
1018 fSocket->Write(it.MultipartHeader().String(),
1019 it.MultipartHeader().Length());
1021 switch (currentField->Type()) {
1022 default:
1023 case B_HTTPFORM_UNKNOWN:
1024 ASSERT(0);
1025 break;
1027 case B_HTTPFORM_STRING:
1028 fSocket->Write(currentField->String().String(),
1029 currentField->String().Length());
1030 break;
1032 case B_HTTPFORM_FILE:
1034 BFile upFile(currentField->File().Path(),
1035 B_READ_ONLY);
1036 char readBuffer[kHttpBufferSize];
1037 ssize_t readSize;
1039 readSize = upFile.Read(readBuffer,
1040 sizeof(readBuffer));
1041 while (readSize > 0) {
1042 fSocket->Write(readBuffer, readSize);
1043 readSize = upFile.Read(readBuffer,
1044 sizeof(readBuffer));
1047 break;
1049 case B_HTTPFORM_BUFFER:
1050 fSocket->Write(currentField->Buffer(),
1051 currentField->BufferSize());
1052 break;
1055 fSocket->Write("\r\n", 2);
1058 BString footer = fOptPostFields->GetMultipartFooter();
1059 fSocket->Write(footer.String(), footer.Length());
1061 } else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT)
1062 && fOptInputData != NULL) {
1064 // If the input data is seekable, we rewind it for each new request.
1065 BPositionIO* seekableData
1066 = dynamic_cast<BPositionIO*>(fOptInputData);
1067 if (seekableData)
1068 seekableData->Seek(0, SEEK_SET);
1070 for (;;) {
1071 char outputTempBuffer[kHttpBufferSize];
1072 ssize_t read = fOptInputData->Read(outputTempBuffer,
1073 sizeof(outputTempBuffer));
1075 if (read <= 0)
1076 break;
1078 if (fOptInputDataSize < 0) {
1079 // Chunked transfer
1080 char hexSize[16];
1081 size_t hexLength = sprintf(hexSize, "%ld", read);
1083 fSocket->Write(hexSize, hexLength);
1084 fSocket->Write("\r\n", 2);
1085 fSocket->Write(outputTempBuffer, read);
1086 fSocket->Write("\r\n", 2);
1087 } else {
1088 fSocket->Write(outputTempBuffer, read);
1092 if (fOptInputDataSize < 0) {
1093 // Chunked transfer terminating sequence
1094 fSocket->Write("0\r\n\r\n", 5);
1101 BHttpHeaders&
1102 BHttpRequest::_ResultHeaders()
1104 return fResult.fHeaders;
1108 void
1109 BHttpRequest::_SetResultStatusCode(int32 statusCode)
1111 fResult.fStatusCode = statusCode;
1115 BString&
1116 BHttpRequest::_ResultStatusText()
1118 return fResult.fStatusString;
1122 bool
1123 BHttpRequest::_CertificateVerificationFailed(BCertificate& certificate,
1124 const char* message)
1126 if (fContext->HasCertificateException(certificate))
1127 return true;
1129 if (fListener != NULL
1130 && fListener->CertificateVerificationFailed(this, certificate, message)) {
1131 // User asked us to continue anyway, let's add a temporary exception for this certificate
1132 fContext->AddCertificateException(certificate);
1133 return true;
1136 return false;
1140 bool
1141 BHttpRequest::_IsDefaultPort()
1143 if (fSSL && Url().Port() == 443)
1144 return true;
1145 if (!fSSL && Url().Port() == 80)
1146 return true;
1147 return false;