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