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 
53 QT_BEGIN_NAMESPACE
54 
QHttpNetworkReply(const QUrl & url,QObject * parent)55 QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
56     : QObject(*new QHttpNetworkReplyPrivate(url), parent)
57 {
58 }
59 
~QHttpNetworkReply()60 QHttpNetworkReply::~QHttpNetworkReply()
61 {
62     Q_D(QHttpNetworkReply);
63     if (d->connection) {
64         d->connection->d_func()->removeReply(this);
65     }
66 
67 #ifndef QT_NO_COMPRESS
68     if (d->autoDecompress && d->isCompressed() && d->inflateStrm)
69         inflateEnd(d->inflateStrm);
70 #endif
71 }
72 
url() const73 QUrl QHttpNetworkReply::url() const
74 {
75     return d_func()->url;
76 }
setUrl(const QUrl & url)77 void QHttpNetworkReply::setUrl(const QUrl &url)
78 {
79     Q_D(QHttpNetworkReply);
80     d->url = url;
81 }
82 
redirectUrl() const83 QUrl QHttpNetworkReply::redirectUrl() const
84 {
85     return d_func()->redirectUrl;
86 }
87 
setRedirectUrl(const QUrl & url)88 void QHttpNetworkReply::setRedirectUrl(const QUrl &url)
89 {
90     Q_D(QHttpNetworkReply);
91     d->redirectUrl = url;
92 }
93 
isHttpRedirect(int statusCode)94 bool QHttpNetworkReply::isHttpRedirect(int statusCode)
95 {
96     return (statusCode == 301 || statusCode == 302 || statusCode == 303
97             || statusCode == 305 || statusCode == 307 || statusCode == 308);
98 }
99 
contentLength() const100 qint64 QHttpNetworkReply::contentLength() const
101 {
102     return d_func()->contentLength();
103 }
104 
setContentLength(qint64 length)105 void QHttpNetworkReply::setContentLength(qint64 length)
106 {
107     Q_D(QHttpNetworkReply);
108     d->setContentLength(length);
109 }
110 
header() const111 QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
112 {
113     return d_func()->fields;
114 }
115 
headerField(const QByteArray & name,const QByteArray & defaultValue) const116 QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
117 {
118     return d_func()->headerField(name, defaultValue);
119 }
120 
setHeaderField(const QByteArray & name,const QByteArray & data)121 void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
122 {
123     Q_D(QHttpNetworkReply);
124     d->setHeaderField(name, data);
125 }
126 
parseHeader(const QByteArray & header)127 void QHttpNetworkReply::parseHeader(const QByteArray &header)
128 {
129     Q_D(QHttpNetworkReply);
130     d->parseHeader(header);
131 }
132 
request() const133 QHttpNetworkRequest QHttpNetworkReply::request() const
134 {
135     return d_func()->request;
136 }
137 
setRequest(const QHttpNetworkRequest & request)138 void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
139 {
140     Q_D(QHttpNetworkReply);
141     d->request = request;
142     d->ssl = request.isSsl();
143 }
144 
statusCode() const145 int QHttpNetworkReply::statusCode() const
146 {
147     return d_func()->statusCode;
148 }
149 
setStatusCode(int code)150 void QHttpNetworkReply::setStatusCode(int code)
151 {
152     Q_D(QHttpNetworkReply);
153     d->statusCode = code;
154 }
155 
errorString() const156 QString QHttpNetworkReply::errorString() const
157 {
158     return d_func()->errorString;
159 }
160 
errorCode() const161 QNetworkReply::NetworkError QHttpNetworkReply::errorCode() const
162 {
163     return d_func()->httpErrorCode;
164 }
165 
reasonPhrase() const166 QString QHttpNetworkReply::reasonPhrase() const
167 {
168     return d_func()->reasonPhrase;
169 }
170 
setErrorString(const QString & error)171 void QHttpNetworkReply::setErrorString(const QString &error)
172 {
173     Q_D(QHttpNetworkReply);
174     d->errorString = error;
175 }
176 
majorVersion() const177 int QHttpNetworkReply::majorVersion() const
178 {
179     return d_func()->majorVersion;
180 }
181 
minorVersion() const182 int QHttpNetworkReply::minorVersion() const
183 {
184     return d_func()->minorVersion;
185 }
186 
bytesAvailable() const187 qint64 QHttpNetworkReply::bytesAvailable() const
188 {
189     Q_D(const QHttpNetworkReply);
190     if (d->connection)
191         return d->connection->d_func()->uncompressedBytesAvailable(*this);
192     else
193         return -1;
194 }
195 
bytesAvailableNextBlock() const196 qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
197 {
198     Q_D(const QHttpNetworkReply);
199     if (d->connection)
200         return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
201     else
202         return -1;
203 }
204 
readAnyAvailable() const205 bool QHttpNetworkReply::readAnyAvailable() const
206 {
207     Q_D(const QHttpNetworkReply);
208     return (d->responseData.bufferCount() > 0);
209 }
210 
readAny()211 QByteArray 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(this);
220     return d->responseData.read();
221 }
222 
readAll()223 QByteArray QHttpNetworkReply::readAll()
224 {
225     Q_D(QHttpNetworkReply);
226     return d->responseData.readAll();
227 }
228 
read(qint64 amount)229 QByteArray QHttpNetworkReply::read(qint64 amount)
230 {
231     Q_D(QHttpNetworkReply);
232     return d->responseData.read(amount);
233 }
234 
235 
sizeNextBlock()236 qint64 QHttpNetworkReply::sizeNextBlock()
237 {
238     Q_D(QHttpNetworkReply);
239     return d->responseData.sizeNextBlock();
240 }
241 
setDownstreamLimited(bool dsl)242 void QHttpNetworkReply::setDownstreamLimited(bool dsl)
243 {
244     Q_D(QHttpNetworkReply);
245     d->downstreamLimited = dsl;
246     d->connection->d_func()->readMoreLater(this);
247 }
248 
setReadBufferSize(qint64 size)249 void QHttpNetworkReply::setReadBufferSize(qint64 size)
250 {
251     Q_D(QHttpNetworkReply);
252     d->readBufferMaxSize = size;
253 }
254 
supportsUserProvidedDownloadBuffer()255 bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
256 {
257     Q_D(QHttpNetworkReply);
258     return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0 && d->statusCode == 200);
259 }
260 
setUserProvidedDownloadBuffer(char * b)261 void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
262 {
263     Q_D(QHttpNetworkReply);
264     if (supportsUserProvidedDownloadBuffer())
265         d->userProvidedDownloadBuffer = b;
266 }
267 
userProvidedDownloadBuffer()268 char* QHttpNetworkReply::userProvidedDownloadBuffer()
269 {
270     Q_D(QHttpNetworkReply);
271     return d->userProvidedDownloadBuffer;
272 }
273 
abort()274 void QHttpNetworkReply::abort()
275 {
276     Q_D(QHttpNetworkReply);
277     d->state = QHttpNetworkReplyPrivate::Aborted;
278 }
279 
isAborted() const280 bool QHttpNetworkReply::isAborted() const
281 {
282     return d_func()->state == QHttpNetworkReplyPrivate::Aborted;
283 }
284 
isFinished() const285 bool QHttpNetworkReply::isFinished() const
286 {
287     return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
288 }
289 
isPipeliningUsed() const290 bool QHttpNetworkReply::isPipeliningUsed() const
291 {
292     return d_func()->pipeliningUsed;
293 }
294 
isSpdyUsed() const295 bool QHttpNetworkReply::isSpdyUsed() const
296 {
297     return d_func()->spdyUsed;
298 }
299 
setSpdyWasUsed(bool spdy)300 void QHttpNetworkReply::setSpdyWasUsed(bool spdy)
301 {
302     d_func()->spdyUsed = spdy;
303 }
304 
removedContentLength() const305 qint64 QHttpNetworkReply::removedContentLength() const
306 {
307     return d_func()->removedContentLength;
308 }
309 
isRedirecting() const310 bool QHttpNetworkReply::isRedirecting() const
311 {
312     return d_func()->isRedirecting();
313 }
314 
connection()315 QHttpNetworkConnection* QHttpNetworkReply::connection()
316 {
317     return d_func()->connection;
318 }
319 
320 
QHttpNetworkReplyPrivate(const QUrl & newUrl)321 QHttpNetworkReplyPrivate::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 
~QHttpNetworkReplyPrivate()354 QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
355 {
356 #ifndef QT_NO_COMPRESS
357       if (inflateStrm)
358           delete inflateStrm;
359 #endif
360 }
361 
clearHttpLayerInformation()362 void 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(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
clear()381 void QHttpNetworkReplyPrivate::clear()
382 {
383     connection = nullptr;
384     connectionChannel = nullptr;
385     autoDecompress = false;
386     clearHttpLayerInformation();
387 }
388 
389 // QHttpNetworkReplyPrivate
bytesAvailable() const390 qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
391 {
392     return (state != ReadingDataState ? 0 : fragment.size());
393 }
394 
isCompressed()395 bool QHttpNetworkReplyPrivate::isCompressed()
396 {
397     QByteArray encoding = headerField("content-encoding");
398     return encoding.compare("gzip", Qt::CaseInsensitive) == 0 ||
399             encoding.compare("deflate", Qt::CaseInsensitive) == 0;
400 }
401 
removeAutoDecompressHeader()402 void 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(it->first, Qt::CaseInsensitive) == 0) {
411             removedContentLength = strtoull(it->second.constData(), nullptr, 0);
412             fields.erase(it);
413             break;
414         }
415         ++it;
416     }
417 }
418 
findChallenge(bool forProxy,QByteArray & challenge) const419 bool 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(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("negotiate"))
430             challenge = line;
431     }
432     return !challenge.isEmpty();
433 }
434 
authenticationMethod(bool isProxy) const435 QAuthenticatorPrivate::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(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("basic")) {
445             method = QAuthenticatorPrivate::Basic;
446         } else if (method < QAuthenticatorPrivate::Ntlm
447             && line.startsWith("ntlm")) {
448             method = QAuthenticatorPrivate::Ntlm;
449         } else if (method < QAuthenticatorPrivate::DigestMd5
450             && line.startsWith("digest")) {
451             method = QAuthenticatorPrivate::DigestMd5;
452         } else if (method < QAuthenticatorPrivate::Negotiate
453             && line.startsWith("negotiate")) {
454             method = QAuthenticatorPrivate::Negotiate;
455         }
456     }
457     return method;
458 }
459 
readStatus(QAbstractSocket * socket)460 qint64 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(32);
465     }
466 
467     qint64 bytes = 0;
468     char c;
469     qint64 haveRead = 0;
470 
471     do {
472         haveRead = socket->read(&c, 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('\r')) {
486                 fragment.truncate(fragment.length()-1);
487             }
488             bool ok = parseStatus(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("HTTP/")) {
501             fragment.clear();
502             return -1;
503         }
504     } while (haveRead == 1);
505 
506     return bytes;
507 }
508 
parseStatus(const QByteArray & status)509 bool 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(httpMagic)
524         || status.at(dotPos) != '.'
525         || status.at(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(dotPos - 1) - '0';
532     minorVersion = status.at(dotPos + 1) - '0';
533 
534     int i = spacePos;
535     int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
536     const QByteArray code = status.mid(i + 1, j - i - 1);
537 
538     bool ok;
539     statusCode = code.toInt(&ok);
540     reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
541 
542     return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
543 }
544 
readHeader(QAbstractSocket * socket)545 qint64 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(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(&c, 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("\n\r\n")
575                     || fragment.endsWith("\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("\r\n"))
580                     || (fragment.length() == 1 && fragment.endsWith("\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(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("transfer-encoding").toLower().contains("chunked");
595 
596         // cache isConnectionCloseEnabled since it is called often
597         QByteArray connectionHeaderField = headerField("connection");
598         // check for explicit indication of close or the implicit connection close of HTTP/1.0
599         connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
600             headerField("proxy-connection").toLower().contains("close")) ||
601             (majorVersion == 1 && minorVersion == 0 &&
602             (connectionHeaderField.isEmpty() && !headerField("proxy-connection").toLower().contains("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 
parseHeader(const QByteArray & header)619 void 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(':', i); // field-name
626         if (j == -1)
627             break;
628         const QByteArray field = header.mid(i, 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('\n', 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(j, 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(qMakePair(field, value));
648     }
649 }
650 
isChunked()651 bool QHttpNetworkReplyPrivate::isChunked()
652 {
653     return chunkedTransferEncoding;
654 }
655 
isConnectionCloseEnabled()656 bool 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
readBodyVeryFast(QAbstractSocket * socket,char * b)663 qint64 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(b, 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
readBodyFast(QAbstractSocket * socket,QByteDataBuffer * rb)682 qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
683 {
684 
685     qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
686     if (readBufferMaxSize)
687         toBeRead = qMin(toBeRead, readBufferMaxSize);
688 
689     if (!toBeRead)
690         return 0;
691 
692     QByteArray bd;
693     bd.resize(toBeRead);
694     qint64 haveRead = socket->read(bd.data(), toBeRead);
695     if (haveRead == -1) {
696         bd.clear();
697         return 0; // ### error checking here;
698     }
699     bd.resize(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 
readBody(QAbstractSocket * socket,QByteDataBuffer * out)712 qint64 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(socket, tempOutDataBuffer);
727     } else if (bodyLength > 0) {
728         // we have a Content-Length
729         bytes += readReplyBodyRaw(socket, tempOutDataBuffer, bodyLength - contentRead);
730         if (contentRead + bytes == bodyLength)
731             state = AllDoneState;
732     } else {
733         // no content length. just read what's possible
734         bytes += readReplyBodyRaw(socket, tempOutDataBuffer, 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(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
initializeInflateStream()752 int 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 
uncompressBodyData(QByteDataBuffer * in,QByteDataBuffer * out)769 qint64 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(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(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(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(bOut.capacity() - inflateStrm->avail_out);
816             out->append(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 
readReplyBodyRaw(QAbstractSocket * socket,QByteDataBuffer * out,qint64 size)826 qint64 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>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
834 
835     if (readBufferMaxSize)
836         toBeRead = qMin<qint64>(toBeRead, readBufferMaxSize);
837 
838     while (toBeRead > 0) {
839         QByteArray byteData;
840         byteData.resize(toBeRead);
841         qint64 haveRead = socket->read(byteData.data(), byteData.size());
842         if (haveRead <= 0) {
843             // ### error checking here
844             byteData.clear();
845             return bytes;
846         }
847 
848         byteData.resize(haveRead);
849         out->append(byteData);
850         bytes += haveRead;
851         size -= haveRead;
852 
853         toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
854     }
855     return bytes;
856 
857 }
858 
readReplyBodyChunked(QAbstractSocket * socket,QByteDataBuffer * out)859 qint64 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(crlf, 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(socket, &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(crlf, 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, currentChunkSize - currentChunkRead);
912         currentChunkRead += haveRead;
913         bytes += haveRead;
914 
915         // ### error checking here
916 
917     }
918     return bytes;
919 }
920 
getChunkSize(QAbstractSocket * socket,qint64 * chunkSize)921 qint64 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(crlf, 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('\r')  && crlf[0] == '\n'))
936         {
937             bytes += socket->read(crlf, 1);     // read the \r or \n
938             if (crlf[0] == '\r')
939                 bytes += socket->read(crlf, 1); // read the \n
940             bool ok = false;
941             // ignore the chunk-extension
942             fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
943             *chunkSize = fragment.toLong(&ok, 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(&c, 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 
isRedirecting() const961 bool 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 
shouldEmitSignals()969 bool 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 
expectContent()976 bool 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 
eraseData()995 void QHttpNetworkReplyPrivate::eraseData()
996 {
997     compressedData.clear();
998     responseData.clear();
999 }
1000 
1001 
1002 // SSL support below
1003 #ifndef QT_NO_SSL
1004 
sslConfiguration() const1005 QSslConfiguration QHttpNetworkReply::sslConfiguration() const
1006 {
1007     Q_D(const QHttpNetworkReply);
1008 
1009     if (!d->connectionChannel)
1010         return QSslConfiguration();
1011 
1012     QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
1013     if (!sslSocket)
1014         return QSslConfiguration();
1015 
1016     return sslSocket->sslConfiguration();
1017 }
1018 
setSslConfiguration(const QSslConfiguration & config)1019 void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
1020 {
1021     Q_D(QHttpNetworkReply);
1022     if (d->connection)
1023         d->connection->setSslConfiguration(config);
1024 }
1025 
ignoreSslErrors()1026 void QHttpNetworkReply::ignoreSslErrors()
1027 {
1028     Q_D(QHttpNetworkReply);
1029     if (d->connection)
1030         d->connection->ignoreSslErrors();
1031 }
1032 
ignoreSslErrors(const QList<QSslError> & errors)1033 void 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 
1044 QT_END_NAMESPACE
1045