1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qhttpnetworkreply_p.h"
41#include "qhttpnetworkconnection_p.h"
42
43#ifndef QT_NO_SSL
44# include <QtNetwork/qsslkey.h>
45# include <QtNetwork/qsslcipher.h>
46# include <QtNetwork/qsslconfiguration.h>
47#endif
48
49QT_BEGIN_NAMESPACE
50
51QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
52 : QObject(*new QHttpNetworkReplyPrivate(url), parent)
53{
54}
55
56QHttpNetworkReply::~QHttpNetworkReply()
57{
58 Q_D(QHttpNetworkReply);
59 if (d->connection) {
60 d->connection->d_func()->removeReply(this);
61 }
62}
63
64QUrl QHttpNetworkReply::url() const
65{
66 return d_func()->url;
67}
68void QHttpNetworkReply::setUrl(const QUrl &url)
69{
70 Q_D(QHttpNetworkReply);
71 d->url = url;
72}
73
74QUrl QHttpNetworkReply::redirectUrl() const
75{
76 return d_func()->redirectUrl;
77}
78
79void QHttpNetworkReply::setRedirectUrl(const QUrl &url)
80{
81 Q_D(QHttpNetworkReply);
82 d->redirectUrl = url;
83}
84
85bool QHttpNetworkReply::isHttpRedirect(int statusCode)
86{
87 return (statusCode == 301 || statusCode == 302 || statusCode == 303
88 || statusCode == 305 || statusCode == 307 || statusCode == 308);
89}
90
91qint64 QHttpNetworkReply::contentLength() const
92{
93 return d_func()->contentLength();
94}
95
96void QHttpNetworkReply::setContentLength(qint64 length)
97{
98 Q_D(QHttpNetworkReply);
99 d->setContentLength(length);
100}
101
102QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
103{
104 return d_func()->fields;
105}
106
107QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
108{
109 return d_func()->headerField(name, defaultValue);
110}
111
112void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
113{
114 Q_D(QHttpNetworkReply);
115 d->setHeaderField(name, data);
116}
117
118void QHttpNetworkReply::parseHeader(const QByteArray &header)
119{
120 Q_D(QHttpNetworkReply);
121 d->parseHeader(header);
122}
123
124QHttpNetworkRequest QHttpNetworkReply::request() const
125{
126 return d_func()->request;
127}
128
129void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
130{
131 Q_D(QHttpNetworkReply);
132 d->request = request;
133 d->ssl = request.isSsl();
134}
135
136int QHttpNetworkReply::statusCode() const
137{
138 return d_func()->statusCode;
139}
140
141void QHttpNetworkReply::setStatusCode(int code)
142{
143 Q_D(QHttpNetworkReply);
144 d->statusCode = code;
145}
146
147QString QHttpNetworkReply::errorString() const
148{
149 return d_func()->errorString;
150}
151
152QNetworkReply::NetworkError QHttpNetworkReply::errorCode() const
153{
154 return d_func()->httpErrorCode;
155}
156
157QString QHttpNetworkReply::reasonPhrase() const
158{
159 return d_func()->reasonPhrase;
160}
161
162void QHttpNetworkReply::setErrorString(const QString &error)
163{
164 Q_D(QHttpNetworkReply);
165 d->errorString = error;
166}
167
168int QHttpNetworkReply::majorVersion() const
169{
170 return d_func()->majorVersion;
171}
172
173int QHttpNetworkReply::minorVersion() const
174{
175 return d_func()->minorVersion;
176}
177
178qint64 QHttpNetworkReply::bytesAvailable() const
179{
180 Q_D(const QHttpNetworkReply);
181 if (d->connection)
182 return d->connection->d_func()->uncompressedBytesAvailable(*this);
183 else
184 return -1;
185}
186
187qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
188{
189 Q_D(const QHttpNetworkReply);
190 if (d->connection)
191 return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
192 else
193 return -1;
194}
195
196bool QHttpNetworkReply::readAnyAvailable() const
197{
198 Q_D(const QHttpNetworkReply);
199 return (d->responseData.bufferCount() > 0);
200}
201
202QByteArray QHttpNetworkReply::readAny()
203{
204 Q_D(QHttpNetworkReply);
205 if (d->responseData.bufferCount() == 0)
206 return QByteArray();
207
208 // we'll take the last buffer, so schedule another read from http
209 if (d->downstreamLimited && d->responseData.bufferCount() == 1 && !isFinished())
210 d->connection->d_func()->readMoreLater(this);
211 return d->responseData.read();
212}
213
214QByteArray QHttpNetworkReply::readAll()
215{
216 Q_D(QHttpNetworkReply);
217 return d->responseData.readAll();
218}
219
220QByteArray QHttpNetworkReply::read(qint64 amount)
221{
222 Q_D(QHttpNetworkReply);
223 return d->responseData.read(amount);
224}
225
226
227qint64 QHttpNetworkReply::sizeNextBlock()
228{
229 Q_D(QHttpNetworkReply);
230 return d->responseData.sizeNextBlock();
231}
232
233void QHttpNetworkReply::setDownstreamLimited(bool dsl)
234{
235 Q_D(QHttpNetworkReply);
236 d->downstreamLimited = dsl;
237 d->connection->d_func()->readMoreLater(this);
238}
239
240void QHttpNetworkReply::setReadBufferSize(qint64 size)
241{
242 Q_D(QHttpNetworkReply);
243 d->readBufferMaxSize = size;
244}
245
246bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
247{
248 Q_D(QHttpNetworkReply);
249 return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0 && d->statusCode == 200);
250}
251
252void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
253{
254 Q_D(QHttpNetworkReply);
255 if (supportsUserProvidedDownloadBuffer())
256 d->userProvidedDownloadBuffer = b;
257}
258
259char* QHttpNetworkReply::userProvidedDownloadBuffer()
260{
261 Q_D(QHttpNetworkReply);
262 return d->userProvidedDownloadBuffer;
263}
264
265void QHttpNetworkReply::abort()
266{
267 Q_D(QHttpNetworkReply);
268 d->state = QHttpNetworkReplyPrivate::Aborted;
269}
270
271bool QHttpNetworkReply::isAborted() const
272{
273 return d_func()->state == QHttpNetworkReplyPrivate::Aborted;
274}
275
276bool QHttpNetworkReply::isFinished() const
277{
278 return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
279}
280
281bool QHttpNetworkReply::isPipeliningUsed() const
282{
283 return d_func()->pipeliningUsed;
284}
285
286bool QHttpNetworkReply::isHttp2Used() const
287{
288 return d_func()->h2Used;
289}
290
291void QHttpNetworkReply::setHttp2WasUsed(bool h2)
292{
293 d_func()->h2Used = h2;
294}
295
296qint64 QHttpNetworkReply::removedContentLength() const
297{
298 return d_func()->removedContentLength;
299}
300
301bool QHttpNetworkReply::isRedirecting() const
302{
303 return d_func()->isRedirecting();
304}
305
306QHttpNetworkConnection* QHttpNetworkReply::connection()
307{
308 return d_func()->connection;
309}
310
311
312QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
313 : QHttpNetworkHeaderPrivate(newUrl)
314 , state(NothingDoneState)
315 , ssl(false)
316 , statusCode(100),
317 majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
318 chunkedTransferEncoding(false),
319 connectionCloseEnabled(true),
320 forceConnectionCloseEnabled(false),
321 lastChunkRead(false),
322 currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0),
323 totallyUploadedData(0),
324 removedContentLength(-1),
325 connection(nullptr),
326 autoDecompress(false), responseData(), requestIsPrepared(false)
327 ,pipeliningUsed(false), h2Used(false), downstreamLimited(false)
328 ,userProvidedDownloadBuffer(nullptr)
329
330{
331 QString scheme = newUrl.scheme();
332 if (scheme == QLatin1String("preconnect-http")
333 || scheme == QLatin1String("preconnect-https"))
334 // make sure we do not close the socket after preconnecting
335 connectionCloseEnabled = false;
336}
337
338QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate() = default;
339
340void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
341{
342 state = NothingDoneState;
343 statusCode = 100;
344 bodyLength = 0;
345 contentRead = 0;
346 totalProgress = 0;
347 currentChunkSize = 0;
348 currentChunkRead = 0;
349 lastChunkRead = false;
350 connectionCloseEnabled = true;
351 decompressHelper.clear();
352 fields.clear();
353}
354
355// TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
356void QHttpNetworkReplyPrivate::clear()
357{
358 connection = nullptr;
359 connectionChannel = nullptr;
360 autoDecompress = false;
361 clearHttpLayerInformation();
362}
363
364// QHttpNetworkReplyPrivate
365qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
366{
367 return (state != ReadingDataState ? 0 : fragment.size());
368}
369
370bool QHttpNetworkReplyPrivate::isCompressed() const
371{
372 return QDecompressHelper::isSupportedEncoding(headerField("content-encoding"));
373}
374
375bool QHttpNetworkReply::isCompressed() const
376{
377 Q_D(const QHttpNetworkReply);
378 return d->isCompressed();
379}
380
381void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
382{
383 // The header "Content-Encoding = gzip" is retained.
384 // Content-Length is removed since the actual one sent by the server is for compressed data
385 QByteArray name("content-length");
386 QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
387 end = fields.end();
388 while (it != end) {
389 if (name.compare(it->first, Qt::CaseInsensitive) == 0) {
390 removedContentLength = strtoull(it->second.constData(), nullptr, 0);
391 fields.erase(it);
392 break;
393 }
394 ++it;
395 }
396}
397
398bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
399{
400 challenge.clear();
401 // find out the type of authentication protocol requested.
402 QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
403 // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
404 QList<QByteArray> challenges = headerFieldValues(header);
405 for (int i = 0; i<challenges.size(); i++) {
406 QByteArray line = challenges.at(i);
407 // todo use qstrincmp
408 if (!line.toLower().startsWith("negotiate"))
409 challenge = line;
410 }
411 return !challenge.isEmpty();
412}
413
414QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
415{
416 // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
417 QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
418 QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
419 QList<QByteArray> challenges = headerFieldValues(header);
420 for (int i = 0; i<challenges.size(); i++) {
421 QByteArray line = challenges.at(i).trimmed().toLower();
422 if (method < QAuthenticatorPrivate::Basic
423 && line.startsWith("basic")) {
424 method = QAuthenticatorPrivate::Basic;
425 } else if (method < QAuthenticatorPrivate::Ntlm
426 && line.startsWith("ntlm")) {
427 method = QAuthenticatorPrivate::Ntlm;
428 } else if (method < QAuthenticatorPrivate::DigestMd5
429 && line.startsWith("digest")) {
430 method = QAuthenticatorPrivate::DigestMd5;
431 } else if (method < QAuthenticatorPrivate::Negotiate
432 && line.startsWith("negotiate")) {
433 method = QAuthenticatorPrivate::Negotiate;
434 }
435 }
436 return method;
437}
438
439qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
440{
441 if (fragment.isEmpty()) {
442 // reserve bytes for the status line. This is better than always append() which reallocs the byte array
443 fragment.reserve(32);
444 }
445
446 qint64 bytes = 0;
447 char c;
448 qint64 haveRead = 0;
449
450 do {
451 haveRead = socket->read(&c, 1);
452 if (haveRead == -1)
453 return -1; // unexpected EOF
454 else if (haveRead == 0)
455 break; // read more later
456 else if (haveRead == 1 && fragment.size() == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
457 continue; // Ignore all whitespace that was trailing froma previous request on that socket
458
459 bytes++;
460
461 // allow both CRLF & LF (only) line endings
462 if (c == '\n') {
463 // remove the CR at the end
464 if (fragment.endsWith('\r')) {
465 fragment.truncate(fragment.length()-1);
466 }
467 bool ok = parseStatus(fragment);
468 state = ReadingHeaderState;
469 fragment.clear();
470 if (!ok) {
471 return -1;
472 }
473 break;
474 } else {
475 fragment.append(c);
476 }
477
478 // is this a valid reply?
479 if (fragment.length() == 5 && !fragment.startsWith("HTTP/")) {
480 fragment.clear();
481 return -1;
482 }
483 } while (haveRead == 1);
484
485 return bytes;
486}
487
488bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
489{
490 // from RFC 2616:
491 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
492 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
493 // that makes: 'HTTP/n.n xxx Message'
494 // byte count: 0123456789012
495
496 static const int minLength = 11;
497 static const int dotPos = 6;
498 static const int spacePos = 8;
499 static const char httpMagic[] = "HTTP/";
500
501 if (status.length() < minLength
502 || !status.startsWith(httpMagic)
503 || status.at(dotPos) != '.'
504 || status.at(spacePos) != ' ') {
505 // I don't know how to parse this status line
506 return false;
507 }
508
509 // optimize for the valid case: defer checking until the end
510 majorVersion = status.at(dotPos - 1) - '0';
511 minorVersion = status.at(dotPos + 1) - '0';
512
513 int i = spacePos;
514 int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
515 const QByteArray code = status.mid(i + 1, j - i - 1);
516
517 bool ok;
518 statusCode = code.toInt(&ok);
519 reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
520
521 return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
522}
523
524qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
525{
526 if (fragment.isEmpty()) {
527 // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
528 // block is 381 bytes.
529 // reserve bytes. This is better than always append() which reallocs the byte array.
530 fragment.reserve(512);
531 }
532
533 qint64 bytes = 0;
534 char c = 0;
535 bool allHeaders = false;
536 qint64 haveRead = 0;
537 do {
538 haveRead = socket->read(&c, 1);
539 if (haveRead == 0) {
540 // read more later
541 break;
542 } else if (haveRead == -1) {
543 // connection broke down
544 return -1;
545 } else {
546 fragment.append(c);
547 bytes++;
548
549 if (c == '\n') {
550 // check for possible header endings. As per HTTP rfc,
551 // the header endings will be marked by CRLFCRLF. But
552 // we will allow CRLFCRLF, CRLFLF, LFCRLF, LFLF
553 if (fragment.endsWith("\n\r\n")
554 || fragment.endsWith("\n\n"))
555 allHeaders = true;
556
557 // there is another case: We have no headers. Then the fragment equals just the line ending
558 if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
559 || (fragment.length() == 1 && fragment.endsWith("\n")))
560 allHeaders = true;
561 }
562 }
563 } while (!allHeaders && haveRead > 0);
564
565 // we received all headers now parse them
566 if (allHeaders) {
567 parseHeader(fragment);
568 state = ReadingDataState;
569 fragment.clear(); // next fragment
570 bodyLength = contentLength(); // cache the length
571
572 // cache isChunked() since it is called often
573 chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
574
575 // cache isConnectionCloseEnabled since it is called often
576 QByteArray connectionHeaderField = headerField("connection");
577 // check for explicit indication of close or the implicit connection close of HTTP/1.0
578 connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
579 headerField("proxy-connection").toLower().contains("close")) ||
580 (majorVersion == 1 && minorVersion == 0 &&
581 (connectionHeaderField.isEmpty() && !headerField("proxy-connection").toLower().contains("keep-alive")));
582 if (autoDecompress && isCompressed()) {
583 if (!decompressHelper.setEncoding(headerField("content-encoding")))
584 return -1; // Either the encoding was unsupported or the decoder could not be set up
585 if (request.ignoreDecompressionRatio())
586 decompressHelper.setArchiveBombDetectionEnabled(false);
587 }
588 }
589 return bytes;
590}
591
592void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
593{
594 // see rfc2616, sec 4 for information about HTTP/1.1 headers.
595 // allows relaxed parsing here, accepts both CRLF & LF line endings
596 int i = 0;
597 while (i < header.count()) {
598 int j = header.indexOf(':', i); // field-name
599 if (j == -1)
600 break;
601 const QByteArray field = header.mid(i, j - i).trimmed();
602 j++;
603 // any number of LWS is allowed before and after the value
604 QByteArray value;
605 do {
606 i = header.indexOf('\n', j);
607 if (i == -1)
608 break;
609 if (!value.isEmpty())
610 value += ' ';
611 // check if we have CRLF or only LF
612 bool hasCR = (i && header[i-1] == '\r');
613 int length = i -(hasCR ? 1: 0) - j;
614 value += header.mid(j, length).trimmed();
615 j = ++i;
616 } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
617 if (i == -1)
618 break; // something is wrong
619
620 fields.append(qMakePair(field, value));
621 }
622}
623
624bool QHttpNetworkReplyPrivate::isChunked()
625{
626 return chunkedTransferEncoding;
627}
628
629bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
630{
631 return connectionCloseEnabled || forceConnectionCloseEnabled;
632}
633
634// note this function can only be used for non-chunked, non-compressed with
635// known content length
636qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
637{
638 // This first read is to flush the buffer inside the socket
639 qint64 haveRead = 0;
640 haveRead = socket->read(b, bodyLength - contentRead);
641 if (haveRead == -1) {
642 return -1;
643 }
644 contentRead += haveRead;
645
646 if (contentRead == bodyLength) {
647 state = AllDoneState;
648 }
649
650 return haveRead;
651}
652
653// note this function can only be used for non-chunked, non-compressed with
654// known content length
655qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
656{
657
658 qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
659 if (readBufferMaxSize)
660 toBeRead = qMin(toBeRead, readBufferMaxSize);
661
662 if (!toBeRead)
663 return 0;
664
665 QByteArray bd;
666 bd.resize(toBeRead);
667 qint64 haveRead = socket->read(bd.data(), toBeRead);
668 if (haveRead == -1) {
669 bd.clear();
670 return 0; // ### error checking here;
671 }
672 bd.resize(haveRead);
673
674 rb->append(bd);
675
676 if (contentRead + haveRead == bodyLength) {
677 state = AllDoneState;
678 }
679
680 contentRead += haveRead;
681 return haveRead;
682}
683
684
685qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
686{
687 qint64 bytes = 0;
688
689 // for compressed data we'll allocate a temporary one that we then decompress
690 QByteDataBuffer *tempOutDataBuffer = (autoDecompress ? new QByteDataBuffer : out);
691
692
693 if (isChunked()) {
694 // chunked transfer encoding (rfc 2616, sec 3.6)
695 bytes += readReplyBodyChunked(socket, tempOutDataBuffer);
696 } else if (bodyLength > 0) {
697 // we have a Content-Length
698 bytes += readReplyBodyRaw(socket, tempOutDataBuffer, bodyLength - contentRead);
699 if (contentRead + bytes == bodyLength)
700 state = AllDoneState;
701 } else {
702 // no content length. just read what's possible
703 bytes += readReplyBodyRaw(socket, tempOutDataBuffer, socket->bytesAvailable());
704 }
705
706 // This is true if there is compressed encoding and we're supposed to use it.
707 if (autoDecompress) {
708 QScopedPointer holder(tempOutDataBuffer);
709 if (!decompressHelper.isValid())
710 return -1;
711
712 decompressHelper.feed(std::move(*tempOutDataBuffer));
713 while (decompressHelper.hasData()) {
714 QByteArray output(4 * 1024, Qt::Uninitialized);
715 qint64 read = decompressHelper.read(output.data(), output.size());
716 if (read < 0) {
717 return -1;
718 } else if (read > 0) {
719 output.resize(read);
720 out->append(std::move(output));
721 }
722 }
723 }
724
725 contentRead += bytes;
726 return bytes;
727}
728
729qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
730{
731 // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
732 qint64 bytes = 0;
733 Q_ASSERT(socket);
734 Q_ASSERT(out);
735
736 int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
737
738 if (readBufferMaxSize)
739 toBeRead = qMin<qint64>(toBeRead, readBufferMaxSize);
740
741 while (toBeRead > 0) {
742 QByteArray byteData;
743 byteData.resize(toBeRead);
744 qint64 haveRead = socket->read(byteData.data(), byteData.size());
745 if (haveRead <= 0) {
746 // ### error checking here
747 byteData.clear();
748 return bytes;
749 }
750
751 byteData.resize(haveRead);
752 out->append(byteData);
753 bytes += haveRead;
754 size -= haveRead;
755
756 toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
757 }
758 return bytes;
759
760}
761
762qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
763{
764 qint64 bytes = 0;
765 while (socket->bytesAvailable()) {
766
767 if (readBufferMaxSize && (bytes > readBufferMaxSize))
768 break;
769
770 if (!lastChunkRead && currentChunkRead >= currentChunkSize) {
771 // For the first chunk and when we're done with a chunk
772 currentChunkSize = 0;
773 currentChunkRead = 0;
774 if (bytes) {
775 // After a chunk
776 char crlf[2];
777 // read the "\r\n" after the chunk
778 qint64 haveRead = socket->read(crlf, 2);
779 // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
780 // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
781 // it right now still works, but we should definitely fix this.
782
783 if (haveRead != 2)
784 return bytes; // FIXME
785 bytes += haveRead;
786 }
787 // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
788 bytes += getChunkSize(socket, &currentChunkSize);
789 if (currentChunkSize == -1)
790 break;
791 }
792 // if the chunk size is 0, end of the stream
793 if (currentChunkSize == 0 || lastChunkRead) {
794 lastChunkRead = true;
795 // try to read the "\r\n" after the chunk
796 char crlf[2];
797 qint64 haveRead = socket->read(crlf, 2);
798 if (haveRead > 0)
799 bytes += haveRead;
800
801 if ((haveRead == 2 && crlf[0] == '\r' && crlf[1] == '\n') || (haveRead == 1 && crlf[0] == '\n'))
802 state = AllDoneState;
803 else if (haveRead == 1 && crlf[0] == '\r')
804 break; // Still waiting for the last \n
805 else if (haveRead > 0) {
806 // If we read something else then CRLF, we need to close the channel.
807 forceConnectionCloseEnabled = true;
808 state = AllDoneState;
809 }
810 break;
811 }
812
813 // otherwise, try to begin reading this chunk / to read what is missing for this chunk
814 qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead);
815 currentChunkRead += haveRead;
816 bytes += haveRead;
817
818 // ### error checking here
819
820 }
821 return bytes;
822}
823
824qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
825{
826 qint64 bytes = 0;
827 char crlf[2];
828 *chunkSize = -1;
829
830 int bytesAvailable = socket->bytesAvailable();
831 // FIXME rewrite to permanent loop without bytesAvailable
832 while (bytesAvailable > bytes) {
833 qint64 sniffedBytes = socket->peek(crlf, 2);
834 int fragmentSize = fragment.size();
835
836 // check the next two bytes for a "\r\n", skip blank lines
837 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
838 ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
839 {
840 bytes += socket->read(crlf, 1); // read the \r or \n
841 if (crlf[0] == '\r')
842 bytes += socket->read(crlf, 1); // read the \n
843 bool ok = false;
844 // ignore the chunk-extension
845 fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
846 *chunkSize = fragment.toLong(&ok, 16);
847 fragment.clear();
848 break; // size done
849 } else {
850 // read the fragment to the buffer
851 char c = 0;
852 qint64 haveRead = socket->read(&c, 1);
853 if (haveRead < 0) {
854 return -1; // FIXME
855 }
856 bytes += haveRead;
857 fragment.append(c);
858 }
859 }
860
861 return bytes;
862}
863
864bool QHttpNetworkReplyPrivate::isRedirecting() const
865{
866 // We're in the process of redirecting - if the HTTP status code says so and
867 // followRedirect is switched on
868 return (QHttpNetworkReply::isHttpRedirect(statusCode)
869 && request.isFollowRedirects());
870}
871
872bool QHttpNetworkReplyPrivate::shouldEmitSignals()
873{
874 // for 401 & 407 don't emit the data signals. Content along with these
875 // responses are sent only if the authentication fails.
876 return (statusCode != 401 && statusCode != 407);
877}
878
879bool QHttpNetworkReplyPrivate::expectContent()
880{
881 // check whether we can expect content after the headers (rfc 2616, sec4.4)
882 if ((statusCode >= 100 && statusCode < 200)
883 || statusCode == 204 || statusCode == 304)
884 return false;
885 if (request.operation() == QHttpNetworkRequest::Head)
886 return false; // no body expected for HEAD request
887 qint64 expectedContentLength = contentLength();
888 if (expectedContentLength == 0)
889 return false;
890 if (expectedContentLength == -1 && bodyLength == 0) {
891 // The content-length header was stripped, but its value was 0.
892 // This would be the case for an explicitly zero-length compressed response.
893 return false;
894 }
895 return true;
896}
897
898void QHttpNetworkReplyPrivate::eraseData()
899{
900 responseData.clear();
901}
902
903
904// SSL support below
905#ifndef QT_NO_SSL
906
907QSslConfiguration QHttpNetworkReply::sslConfiguration() const
908{
909 Q_D(const QHttpNetworkReply);
910
911 if (!d->connectionChannel)
912 return QSslConfiguration();
913
914 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
915 if (!sslSocket)
916 return QSslConfiguration();
917
918 return sslSocket->sslConfiguration();
919}
920
921void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
922{
923 Q_D(QHttpNetworkReply);
924 if (d->connection)
925 d->connection->setSslConfiguration(config);
926}
927
928void QHttpNetworkReply::ignoreSslErrors()
929{
930 Q_D(QHttpNetworkReply);
931 if (d->connection)
932 d->connection->ignoreSslErrors();
933}
934
935void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
936{
937 Q_D(QHttpNetworkReply);
938 if (d->connection)
939 d->connection->ignoreSslErrors(errors);
940}
941
942
943#endif //QT_NO_SSL
944
945
946QT_END_NAMESPACE
947