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

source code of qtbase/src/network/access/qhttpnetworkreply.cpp