1/*
2 * Copyright 2010-2015 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 */
10
11
12#include <HttpRequest.h>
13
14#include <arpa/inet.h>
15#include <stdio.h>
16
17#include <cstdlib>
18#include <deque>
19#include <new>
20
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>
31
32
33static const int32 kHttpBufferSize = 4096;
34
35
36namespace BPrivate {
37
38	class CheckedSecureSocket: public BSecureSocket
39	{
40		public:
41			CheckedSecureSocket(BHttpRequest* request);
42
43			bool			CertificateVerificationFailed(BCertificate& certificate,
44					const char* message);
45
46		private:
47			BHttpRequest*	fRequest;
48	};
49
50
51	CheckedSecureSocket::CheckedSecureSocket(BHttpRequest* request)
52		:
53		BSecureSocket(),
54		fRequest(request)
55	{
56	}
57
58
59	bool
60	CheckedSecureSocket::CertificateVerificationFailed(BCertificate& certificate,
61		const char* message)
62	{
63		return fRequest->_CertificateVerificationFailed(certificate, message);
64	}
65
66
67	class CheckedProxySecureSocket: public BProxySecureSocket
68	{
69		public:
70			CheckedProxySecureSocket(const BNetworkAddress& proxy, BHttpRequest* request);
71
72			bool			CertificateVerificationFailed(BCertificate& certificate,
73					const char* message);
74
75		private:
76			BHttpRequest*	fRequest;
77	};
78
79
80	CheckedProxySecureSocket::CheckedProxySecureSocket(const BNetworkAddress& proxy,
81		BHttpRequest* request)
82		:
83		BProxySecureSocket(proxy),
84		fRequest(request)
85	{
86	}
87
88
89	bool
90	CheckedProxySecureSocket::CertificateVerificationFailed(BCertificate& certificate,
91		const char* message)
92	{
93		return fRequest->_CertificateVerificationFailed(certificate, message);
94	}
95};
96
97
98BHttpRequest::BHttpRequest(const BUrl& url, bool ssl, const char* protocolName,
99	BUrlProtocolListener* listener, BUrlContext* context)
100	:
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)
114{
115	_ResetOptions();
116	fSocket = NULL;
117}
118
119
120BHttpRequest::BHttpRequest(const BHttpRequest& other)
121	:
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)
136{
137	_ResetOptions();
138		// FIXME some options may be copied from other instead.
139	fSocket = NULL;
140}
141
142
143BHttpRequest::~BHttpRequest()
144{
145	Stop();
146
147	delete fSocket;
148
149	delete fOptInputData;
150	delete fOptHeaders;
151	delete fOptPostFields;
152}
153
154
155void
156BHttpRequest::SetMethod(const char* const method)
157{
158	fRequestMethod = method;
159}
160
161
162void
163BHttpRequest::SetFollowLocation(bool follow)
164{
165	fOptFollowLocation = follow;
166}
167
168
169void
170BHttpRequest::SetMaxRedirections(int8 redirections)
171{
172	fOptMaxRedirs = redirections;
173}
174
175
176void
177BHttpRequest::SetReferrer(const BString& referrer)
178{
179	fOptReferer = referrer;
180}
181
182
183void
184BHttpRequest::SetUserAgent(const BString& agent)
185{
186	fOptUserAgent = agent;
187}
188
189
190void
191BHttpRequest::SetDiscardData(bool discard)
192{
193	fOptDiscardData = discard;
194}
195
196
197void
198BHttpRequest::SetDisableListener(bool disable)
199{
200	fOptDisableListener = disable;
201}
202
203
204void
205BHttpRequest::SetAutoReferrer(bool enable)
206{
207	fOptAutoReferer = enable;
208}
209
210
211void
212BHttpRequest::SetHeaders(const BHttpHeaders& headers)
213{
214	AdoptHeaders(new(std::nothrow) BHttpHeaders(headers));
215}
216
217
218void
219BHttpRequest::AdoptHeaders(BHttpHeaders* const headers)
220{
221	delete fOptHeaders;
222	fOptHeaders = headers;
223}
224
225
226void
227BHttpRequest::SetPostFields(const BHttpForm& fields)
228{
229	AdoptPostFields(new(std::nothrow) BHttpForm(fields));
230}
231
232
233void
234BHttpRequest::AdoptPostFields(BHttpForm* const fields)
235{
236	delete fOptPostFields;
237	fOptPostFields = fields;
238
239	if (fOptPostFields != NULL)
240		fRequestMethod = B_HTTP_POST;
241}
242
243
244void
245BHttpRequest::AdoptInputData(BDataIO* const data, const ssize_t size)
246{
247	delete fOptInputData;
248	fOptInputData = data;
249	fOptInputDataSize = size;
250}
251
252
253void
254BHttpRequest::SetUserName(const BString& name)
255{
256	fOptUsername = name;
257}
258
259
260void
261BHttpRequest::SetPassword(const BString& password)
262{
263	fOptPassword = password;
264}
265
266
267/*static*/ bool
268BHttpRequest::IsInformationalStatusCode(int16 code)
269{
270	return (code >= B_HTTP_STATUS__INFORMATIONAL_BASE)
271		&& (code <  B_HTTP_STATUS__INFORMATIONAL_END);
272}
273
274
275/*static*/ bool
276BHttpRequest::IsSuccessStatusCode(int16 code)
277{
278	return (code >= B_HTTP_STATUS__SUCCESS_BASE)
279		&& (code <  B_HTTP_STATUS__SUCCESS_END);
280}
281
282
283/*static*/ bool
284BHttpRequest::IsRedirectionStatusCode(int16 code)
285{
286	return (code >= B_HTTP_STATUS__REDIRECTION_BASE)
287		&& (code <  B_HTTP_STATUS__REDIRECTION_END);
288}
289
290
291/*static*/ bool
292BHttpRequest::IsClientErrorStatusCode(int16 code)
293{
294	return (code >= B_HTTP_STATUS__CLIENT_ERROR_BASE)
295		&& (code <  B_HTTP_STATUS__CLIENT_ERROR_END);
296}
297
298
299/*static*/ bool
300BHttpRequest::IsServerErrorStatusCode(int16 code)
301{
302	return (code >= B_HTTP_STATUS__SERVER_ERROR_BASE)
303		&& (code <  B_HTTP_STATUS__SERVER_ERROR_END);
304}
305
306
307/*static*/ int16
308BHttpRequest::StatusCodeClass(int16 code)
309{
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;
320
321	return B_HTTP_STATUS_CLASS_INVALID;
322}
323
324
325const BUrlResult&
326BHttpRequest::Result() const
327{
328	return fResult;
329}
330
331
332status_t
333BHttpRequest::Stop()
334{
335	if (fSocket != NULL) {
336		fSocket->Disconnect();
337			// Unlock any pending connect, read or write operation.
338	}
339	return BNetworkRequest::Stop();
340}
341
342
343void
344BHttpRequest::_ResetOptions()
345{
346	delete fOptPostFields;
347	delete fOptHeaders;
348
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;
363}
364
365
366status_t
367BHttpRequest::_ProtocolLoop()
368{
369	// Initialize the request redirection loop
370	int8 maxRedirs = fOptMaxRedirs;
371	bool newRequest;
372
373	do {
374		newRequest = false;
375
376		// Result reset
377		fHeaders.Clear();
378		_ResultHeaders().Clear();
379
380		BString host = fUrl.Host();
381		int port = fSSL ? 443 : 80;
382
383		if (fUrl.HasPort())
384			port = fUrl.Port();
385
386		if (fContext->UseProxy()) {
387			host = fContext->GetProxyHost();
388			port = fContext->GetProxyPort();
389		}
390
391		status_t result = fInputBuffer.InitCheck();
392		if (result != B_OK)
393			return result;
394
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;
400		}
401
402		status_t requestStatus = _MakeRequest();
403		if (requestStatus != B_OK)
404			return requestStatus;
405
406		// Prepare the referer for the next request if needed
407		if (fOptAutoReferer)
408			fOptReferer = fUrl.UrlString();
409
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;
415
416			case B_HTTP_STATUS_CLASS_SUCCESS:
417				break;
418
419			case B_HTTP_STATUS_CLASS_REDIRECTION:
420			{
421				// Redirection has been explicitly disabled
422				if (!fOptFollowLocation)
423					break;
424
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"];
431
432					fUrl = BUrl(fUrl, locationUrl);
433
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;
445					}
446
447					if (--maxRedirs > 0) {
448						newRequest = true;
449
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;
455
456						_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
457							"Following: %s\n",
458							fUrl.UrlString().String());
459					}
460				}
461				break;
462			}
463
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;
469
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);
476
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);
481					}
482
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.
489						//
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;
496					}
497				}
498				break;
499
500			case B_HTTP_STATUS_CLASS_SERVER_ERROR:
501				break;
502
503			default:
504			case B_HTTP_STATUS_CLASS_INVALID:
505				break;
506		}
507	} while (newRequest);
508
509	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
510		"%ld headers and %ld bytes of data remaining",
511		fHeaders.CountHeaders(), fInputBuffer.Size());
512
513	if (fResult.StatusCode() == 404)
514		return B_RESOURCE_NOT_FOUND;
515
516	return B_OK;
517}
518
519
520status_t
521BHttpRequest::_MakeRequest()
522{
523	delete fSocket;
524
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();
533
534	if (fSocket == NULL)
535		return B_NO_MEMORY;
536
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);
540
541	if (connectError != B_OK) {
542		_EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s",
543			strerror(connectError));
544		return connectError;
545	}
546
547	//! ProtocolHook:ConnectionOpened
548	if (fListener != NULL)
549		fListener->ConnectionOpened(this);
550
551	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
552		"Connection opened, sending request.");
553
554	BString requestHeaders;
555	requestHeaders.Append(_SerializeRequest());
556	requestHeaders.Append(_SerializeHeaders());
557	requestHeaders.Append("\r\n");
558	fSocket->Write(requestHeaders.String(), requestHeaders.Length());
559	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent.");
560
561	_SendPostData();
562	fRequestStatus = kRequestInitialState;
563
564
565
566	// Receive loop
567	bool receiveEnd = false;
568	bool parseEnd = false;
569	bool readByChunks = false;
570	bool decompress = false;
571	status_t readError = B_OK;
572	ssize_t bytesRead = 0;
573	ssize_t bytesReceived = 0;
574	ssize_t bytesTotal = 0;
575	size_t previousBufferSize = 0;
576	off_t bytesUnpacked = 0;
577	char* inputTempBuffer = new(std::nothrow) char[kHttpBufferSize];
578	ssize_t inputTempSize = kHttpBufferSize;
579	ssize_t chunkSize = -1;
580	DynamicBuffer decompressorStorage;
581	BDataIO* decompressingStream;
582	ObjectDeleter<BDataIO> decompressingStreamDeleter;
583
584	while (!fQuit && !(receiveEnd && parseEnd)) {
585		if ((!receiveEnd) && (fInputBuffer.Size() == previousBufferSize)) {
586			fSocket->WaitForReadable();
587			BStackOrHeapArray<char, 4096> chunk(kHttpBufferSize);
588			bytesRead = fSocket->Read(chunk, kHttpBufferSize);
589
590			if (bytesRead < 0) {
591				readError = bytesRead;
592				break;
593			} else if (bytesRead == 0)
594				receiveEnd = true;
595
596			fInputBuffer.AppendData(chunk, bytesRead);
597		} else
598			bytesRead = 0;
599
600		previousBufferSize = fInputBuffer.Size();
601
602		if (fRequestStatus < kRequestStatusReceived) {
603			_ParseStatus();
604
605			//! ProtocolHook:ResponseStarted
606			if (fRequestStatus >= kRequestStatusReceived && fListener != NULL)
607				fListener->ResponseStarted(this);
608		}
609
610		if (fRequestStatus < kRequestHeadersReceived) {
611			_ParseHeaders();
612
613			if (fRequestStatus >= kRequestHeadersReceived) {
614				_ResultHeaders() = fHeaders;
615
616				// Parse received cookies
617				if (fContext != NULL) {
618					for (int32 i = 0;  i < fHeaders.CountHeaders(); i++) {
619						if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) {
620							fContext->GetCookieJar().AddCookie(
621								fHeaders.HeaderAt(i).Value(), fUrl);
622						}
623					}
624				}
625
626				//! ProtocolHook:HeadersReceived
627				if (fListener != NULL)
628					fListener->HeadersReceived(this, fResult);
629
630
631				if (BString(fHeaders["Transfer-Encoding"]) == "chunked")
632					readByChunks = true;
633
634				BString contentEncoding(fHeaders["Content-Encoding"]);
635				// We don't advertise "deflate" support (see above), but we
636				// still try to decompress it, if a server ever sends a deflate
637				// stream despite it not being in our Accept-Encoding list.
638				if (contentEncoding == "gzip"
639						|| contentEncoding == "deflate") {
640					decompress = true;
641					readError = BZlibCompressionAlgorithm()
642						.CreateDecompressingOutputStream(&decompressorStorage,
643							NULL, decompressingStream);
644					if (readError != B_OK)
645						break;
646
647					decompressingStreamDeleter.SetTo(decompressingStream);
648				}
649
650				int32 index = fHeaders.HasHeader("Content-Length");
651				if (index != B_ERROR)
652					bytesTotal = atoi(fHeaders.HeaderAt(index).Value());
653				else
654					bytesTotal = -1;
655			}
656		}
657
658		if (fRequestStatus >= kRequestHeadersReceived) {
659			// If Transfer-Encoding is chunked, we should read a complete
660			// chunk in buffer before handling it
661			if (readByChunks) {
662				if (chunkSize >= 0) {
663					if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) {
664							// 2 more bytes to handle the closing CR+LF
665						bytesRead = chunkSize;
666						if (inputTempSize < chunkSize + 2) {
667							delete[] inputTempBuffer;
668							inputTempSize = chunkSize + 2;
669							inputTempBuffer
670								= new(std::nothrow) char[inputTempSize];
671						}
672
673						if (inputTempBuffer == NULL) {
674							readError = B_NO_MEMORY;
675							break;
676						}
677
678						fInputBuffer.RemoveData(inputTempBuffer,
679							chunkSize + 2);
680						chunkSize = -1;
681					} else {
682						// Not enough data, try again later
683						bytesRead = -1;
684					}
685				} else {
686					BString chunkHeader;
687					if (_GetLine(chunkHeader) == B_ERROR) {
688						chunkSize = -1;
689						bytesRead = -1;
690					} else {
691						// Format of a chunk header:
692						// <chunk size in hex>[; optional data]
693						int32 semiColonIndex = chunkHeader.FindFirst(';', 0);
694
695						// Cut-off optional data if present
696						if (semiColonIndex != -1) {
697							chunkHeader.Remove(semiColonIndex,
698								chunkHeader.Length() - semiColonIndex);
699						}
700
701						chunkSize = strtol(chunkHeader.String(), NULL, 16);
702						if (chunkSize == 0)
703							fRequestStatus = kRequestContentReceived;
704
705						bytesRead = -1;
706					}
707				}
708
709				// A chunk of 0 bytes indicates the end of the chunked transfer
710				if (bytesRead == 0)
711					receiveEnd = true;
712			} else {
713				bytesRead = fInputBuffer.Size();
714
715				if (bytesRead > 0) {
716					if (inputTempSize < bytesRead) {
717						inputTempSize = bytesRead;
718						delete[] inputTempBuffer;
719						inputTempBuffer = new(std::nothrow) char[bytesRead];
720					}
721
722					if (inputTempBuffer == NULL) {
723						readError = B_NO_MEMORY;
724						break;
725					}
726					fInputBuffer.RemoveData(inputTempBuffer, bytesRead);
727				}
728			}
729
730			if (bytesRead >= 0) {
731				bytesReceived += bytesRead;
732
733				if (fListener != NULL) {
734					if (decompress) {
735						readError = decompressingStream->WriteExactly(
736							inputTempBuffer, bytesRead);
737						if (readError != B_OK)
738							break;
739
740						ssize_t size = decompressorStorage.Size();
741						BStackOrHeapArray<char, 4096> buffer(size);
742						size = decompressorStorage.Read(buffer, size);
743						if (size > 0) {
744							fListener->DataReceived(this, buffer, bytesUnpacked,
745								size);
746							bytesUnpacked += size;
747						}
748					} else if (bytesRead > 0) {
749						fListener->DataReceived(this, inputTempBuffer,
750							bytesReceived - bytesRead, bytesRead);
751					}
752					fListener->DownloadProgress(this, bytesReceived,
753						std::max((ssize_t)0, bytesTotal));
754				}
755
756				if (bytesTotal >= 0 && bytesReceived >= bytesTotal)
757					receiveEnd = true;
758
759				if (decompress && receiveEnd) {
760					readError = decompressingStream->Flush();
761
762					if (readError == B_BUFFER_OVERFLOW)
763						readError = B_OK;
764
765					if (readError != B_OK)
766						break;
767
768					ssize_t size = decompressorStorage.Size();
769					BStackOrHeapArray<char, 4096> buffer(size);
770					size = decompressorStorage.Read(buffer, size);
771					if (fListener != NULL && size > 0) {
772						fListener->DataReceived(this, buffer,
773							bytesUnpacked, size);
774						bytesUnpacked += size;
775					}
776				}
777			}
778		}
779
780		parseEnd = (fInputBuffer.Size() == 0);
781	}
782
783	fSocket->Disconnect();
784	delete[] inputTempBuffer;
785
786	if (readError != B_OK)
787		return readError;
788
789	return fQuit ? B_INTERRUPTED : B_OK;
790}
791
792
793void
794BHttpRequest::_ParseStatus()
795{
796	// Status line should be formatted like: HTTP/M.m SSS ...
797	// With:   M = Major version of the protocol
798	//         m = Minor version of the protocol
799	//       SSS = three-digit status code of the response
800	//       ... = additional text info
801	BString statusLine;
802	if (_GetLine(statusLine) == B_ERROR)
803		return;
804
805	if (statusLine.CountChars() < 12)
806		return;
807
808	fRequestStatus = kRequestStatusReceived;
809
810	BString statusCodeStr;
811	BString statusText;
812	statusLine.CopyInto(statusCodeStr, 9, 3);
813	_SetResultStatusCode(atoi(statusCodeStr.String()));
814
815	statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13);
816
817	_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)",
818		atoi(statusCodeStr.String()), _ResultStatusText().String());
819}
820
821
822void
823BHttpRequest::_ParseHeaders()
824{
825	BString currentHeader;
826	while (_GetLine(currentHeader) != B_ERROR) {
827		// An empty line means the end of the header section
828		if (currentHeader.Length() == 0) {
829			fRequestStatus = kRequestHeadersReceived;
830			return;
831		}
832
833		_EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s",
834			currentHeader.String());
835		fHeaders.AddHeader(currentHeader.String());
836	}
837}
838
839
840BString
841BHttpRequest::_SerializeRequest()
842{
843	BString request(fRequestMethod);
844	request << ' ';
845
846	if (fContext->UseProxy()) {
847		// When there is a proxy, the request must include the host and port so
848		// the proxy knows where to send the request.
849		request << Url().Protocol() << "://" << Url().Host();
850		if (Url().HasPort())
851			request << ':' << Url().Port();
852	}
853
854	if (Url().HasPath() && Url().Path().Length() > 0)
855		request << Url().Path();
856	else
857		request << '/';
858
859	if (Url().HasRequest())
860		request << '?' << Url().Request();
861
862	switch (fHttpVersion) {
863		case B_HTTP_11:
864			request << " HTTP/1.1\r\n";
865			break;
866
867		default:
868		case B_HTTP_10:
869			request << " HTTP/1.0\r\n";
870			break;
871	}
872
873	_EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", request.String());
874
875	return request;
876}
877
878
879BString
880BHttpRequest::_SerializeHeaders()
881{
882	BHttpHeaders outputHeaders;
883
884	// HTTP 1.1 additional headers
885	if (fHttpVersion == B_HTTP_11) {
886		BString host = Url().Host();
887		if (Url().HasPort() && !_IsDefaultPort())
888			host << ':' << Url().Port();
889
890		outputHeaders.AddHeader("Host", host);
891
892		outputHeaders.AddHeader("Accept", "*/*");
893		outputHeaders.AddHeader("Accept-Encoding", "gzip");
894			// Allows the server to compress data using the "gzip" format.
895			// "deflate" is not supported, because there are two interpretations
896			// of what it means (the RFC and Microsoft products), and we don't
897			// want to handle this. Very few websites support only deflate,
898			// and most of them will send gzip, or at worst, uncompressed data.
899
900		outputHeaders.AddHeader("Connection", "close");
901			// Let the remote server close the connection after response since
902			// we don't handle multiple request on a single connection
903	}
904
905	// Classic HTTP headers
906	if (fOptUserAgent.CountChars() > 0)
907		outputHeaders.AddHeader("User-Agent", fOptUserAgent.String());
908
909	if (fOptReferer.CountChars() > 0)
910		outputHeaders.AddHeader("Referer", fOptReferer.String());
911
912	// Authentication
913	if (fContext != NULL) {
914		BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl);
915		if (authentication.Method() != B_HTTP_AUTHENTICATION_NONE) {
916			if (fOptUsername.Length() > 0) {
917				authentication.SetUserName(fOptUsername);
918				authentication.SetPassword(fOptPassword);
919			}
920
921			BString request(fRequestMethod);
922			outputHeaders.AddHeader("Authorization",
923				authentication.Authorization(fUrl, request));
924		}
925	}
926
927	// Required headers for POST data
928	if (fOptPostFields != NULL && fRequestMethod == B_HTTP_POST) {
929		BString contentType;
930
931		switch (fOptPostFields->GetFormType()) {
932			case B_HTTP_FORM_MULTIPART:
933				contentType << "multipart/form-data; boundary="
934					<< fOptPostFields->GetMultipartBoundary() << "";
935				break;
936
937			case B_HTTP_FORM_URL_ENCODED:
938				contentType << "application/x-www-form-urlencoded";
939				break;
940		}
941
942		outputHeaders.AddHeader("Content-Type", contentType);
943		outputHeaders.AddHeader("Content-Length",
944			fOptPostFields->ContentLength());
945	} else if (fOptInputData != NULL
946			&& (fRequestMethod == B_HTTP_POST
947			|| fRequestMethod == B_HTTP_PUT)) {
948		if (fOptInputDataSize >= 0)
949			outputHeaders.AddHeader("Content-Length", fOptInputDataSize);
950		else
951			outputHeaders.AddHeader("Transfer-Encoding", "chunked");
952	}
953
954	// Optional headers specified by the user
955	if (fOptHeaders != NULL) {
956		for (int32 headerIndex = 0; headerIndex < fOptHeaders->CountHeaders();
957				headerIndex++) {
958			BHttpHeader& optHeader = (*fOptHeaders)[headerIndex];
959			int32 replaceIndex = outputHeaders.HasHeader(optHeader.Name());
960
961			// Add or replace the current option header to the
962			// output header list
963			if (replaceIndex == -1)
964				outputHeaders.AddHeader(optHeader.Name(), optHeader.Value());
965			else
966				outputHeaders[replaceIndex].SetValue(optHeader.Value());
967		}
968	}
969
970	// Context cookies
971	if (fOptSetCookies && fContext != NULL) {
972		BString cookieString;
973
974		BNetworkCookieJar::UrlIterator iterator
975			= fContext->GetCookieJar().GetUrlIterator(fUrl);
976		const BNetworkCookie* cookie = iterator.Next();
977		if (cookie != NULL) {
978			while (true) {
979				cookieString << cookie->RawCookie(false);
980				cookie = iterator.Next();
981				if (cookie == NULL)
982					break;
983				cookieString << "; ";
984			}
985
986			outputHeaders.AddHeader("Cookie", cookieString);
987		}
988	}
989
990	// Write output headers to output stream
991	BString headerData;
992
993	for (int32 headerIndex = 0; headerIndex < outputHeaders.CountHeaders();
994			headerIndex++) {
995		const char* header = outputHeaders.HeaderAt(headerIndex).Header();
996
997		headerData << header;
998		headerData << "\r\n";
999
1000		_EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", header);
1001	}
1002
1003	return headerData;
1004}
1005
1006
1007void
1008BHttpRequest::_SendPostData()
1009{
1010	if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) {
1011		if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) {
1012			BString outputBuffer = fOptPostFields->RawData();
1013			_EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
1014				"%s", outputBuffer.String());
1015			fSocket->Write(outputBuffer.String(), outputBuffer.Length());
1016		} else {
1017			for (BHttpForm::Iterator it = fOptPostFields->GetIterator();
1018				const BHttpFormData* currentField = it.Next();
1019				) {
1020				_EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
1021					it.MultipartHeader().String());
1022				fSocket->Write(it.MultipartHeader().String(),
1023					it.MultipartHeader().Length());
1024
1025				switch (currentField->Type()) {
1026					default:
1027					case B_HTTPFORM_UNKNOWN:
1028						ASSERT(0);
1029						break;
1030
1031					case B_HTTPFORM_STRING:
1032						fSocket->Write(currentField->String().String(),
1033							currentField->String().Length());
1034						break;
1035
1036					case B_HTTPFORM_FILE:
1037						{
1038							BFile upFile(currentField->File().Path(),
1039								B_READ_ONLY);
1040							char readBuffer[kHttpBufferSize];
1041							ssize_t readSize;
1042
1043							readSize = upFile.Read(readBuffer,
1044								sizeof(readBuffer));
1045							while (readSize > 0) {
1046								fSocket->Write(readBuffer, readSize);
1047								readSize = upFile.Read(readBuffer,
1048									sizeof(readBuffer));
1049							}
1050
1051							break;
1052						}
1053					case B_HTTPFORM_BUFFER:
1054						fSocket->Write(currentField->Buffer(),
1055							currentField->BufferSize());
1056						break;
1057				}
1058
1059				fSocket->Write("\r\n", 2);
1060			}
1061
1062			BString footer = fOptPostFields->GetMultipartFooter();
1063			fSocket->Write(footer.String(), footer.Length());
1064		}
1065	} else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT)
1066		&& fOptInputData != NULL) {
1067
1068		// If the input data is seekable, we rewind it for each new request.
1069		BPositionIO* seekableData
1070			= dynamic_cast<BPositionIO*>(fOptInputData);
1071		if (seekableData)
1072			seekableData->Seek(0, SEEK_SET);
1073
1074		for (;;) {
1075			char outputTempBuffer[kHttpBufferSize];
1076			ssize_t read = fOptInputData->Read(outputTempBuffer,
1077				sizeof(outputTempBuffer));
1078
1079			if (read <= 0)
1080				break;
1081
1082			if (fOptInputDataSize < 0) {
1083				// Input data size unknown, so we have to use chunked transfer
1084				char hexSize[18];
1085				// The string does not need to be NULL terminated.
1086				size_t hexLength = snprintf(hexSize, sizeof(hexSize), "%lx\r\n",
1087					read);
1088
1089				fSocket->Write(hexSize, hexLength);
1090				fSocket->Write(outputTempBuffer, read);
1091				fSocket->Write("\r\n", 2);
1092			} else {
1093				fSocket->Write(outputTempBuffer, read);
1094			}
1095		}
1096
1097		if (fOptInputDataSize < 0) {
1098			// Chunked transfer terminating sequence
1099			fSocket->Write("0\r\n\r\n", 5);
1100		}
1101	}
1102
1103}
1104
1105
1106BHttpHeaders&
1107BHttpRequest::_ResultHeaders()
1108{
1109	return fResult.fHeaders;
1110}
1111
1112
1113void
1114BHttpRequest::_SetResultStatusCode(int32 statusCode)
1115{
1116	fResult.fStatusCode = statusCode;
1117}
1118
1119
1120BString&
1121BHttpRequest::_ResultStatusText()
1122{
1123	return fResult.fStatusString;
1124}
1125
1126
1127bool
1128BHttpRequest::_CertificateVerificationFailed(BCertificate& certificate,
1129	const char* message)
1130{
1131	if (fContext->HasCertificateException(certificate))
1132		return true;
1133
1134	if (fListener != NULL
1135		&& fListener->CertificateVerificationFailed(this, certificate, message)) {
1136		// User asked us to continue anyway, let's add a temporary exception for this certificate
1137		fContext->AddCertificateException(certificate);
1138		return true;
1139	}
1140
1141	return false;
1142}
1143
1144
1145bool
1146BHttpRequest::_IsDefaultPort()
1147{
1148	if (fSSL && Url().Port() == 443)
1149		return true;
1150	if (!fSSL && Url().Port() == 80)
1151		return true;
1152	return false;
1153}
1154
1155
1156