1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qhttpnetworkreply_p.h"
43 #include "qhttpnetworkconnection_p.h"
45 #include <qbytearraymatcher.h>
50 # include <QtNetwork/qsslkey.h>
51 # include <QtNetwork/qsslcipher.h>
52 # include <QtNetwork/qsslconfiguration.h>
55 #ifndef QT_NO_COMPRESS
61 QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
62 : QObject(*new QHttpNetworkReplyPrivate(url), parent)
66 QHttpNetworkReply::~QHttpNetworkReply()
68 Q_D(QHttpNetworkReply);
70 d->connection->d_func()->removeReply(this);
73 #ifndef QT_NO_COMPRESS
74 if (d->autoDecompress && d->isCompressed() && d->inflateStrm)
75 inflateEnd(d->inflateStrm);
79 QUrl QHttpNetworkReply::url() const
83 void QHttpNetworkReply::setUrl(const QUrl &url)
85 Q_D(QHttpNetworkReply);
89 qint64 QHttpNetworkReply::contentLength() const
91 return d_func()->contentLength();
94 void QHttpNetworkReply::setContentLength(qint64 length)
96 Q_D(QHttpNetworkReply);
97 d->setContentLength(length);
100 QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
102 return d_func()->fields;
105 QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
107 return d_func()->headerField(name, defaultValue);
110 void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
112 Q_D(QHttpNetworkReply);
113 d->setHeaderField(name, data);
116 void QHttpNetworkReply::parseHeader(const QByteArray &header)
118 Q_D(QHttpNetworkReply);
119 d->parseHeader(header);
122 QHttpNetworkRequest QHttpNetworkReply::request() const
124 return d_func()->request;
127 void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
129 Q_D(QHttpNetworkReply);
130 d->request = request;
131 d->ssl = request.isSsl();
134 int QHttpNetworkReply::statusCode() const
136 return d_func()->statusCode;
139 void QHttpNetworkReply::setStatusCode(int code)
141 Q_D(QHttpNetworkReply);
142 d->statusCode = code;
145 QString QHttpNetworkReply::errorString() const
147 return d_func()->errorString;
150 QString QHttpNetworkReply::reasonPhrase() const
152 return d_func()->reasonPhrase;
155 void QHttpNetworkReply::setErrorString(const QString &error)
157 Q_D(QHttpNetworkReply);
158 d->errorString = error;
161 int QHttpNetworkReply::majorVersion() const
163 return d_func()->majorVersion;
166 int QHttpNetworkReply::minorVersion() const
168 return d_func()->minorVersion;
171 qint64 QHttpNetworkReply::bytesAvailable() const
173 Q_D(const QHttpNetworkReply);
175 return d->connection->d_func()->uncompressedBytesAvailable(*this);
180 qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
182 Q_D(const QHttpNetworkReply);
184 return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
189 bool QHttpNetworkReply::readAnyAvailable() const
191 Q_D(const QHttpNetworkReply);
192 return (d->responseData.bufferCount() > 0);
195 QByteArray QHttpNetworkReply::readAny()
197 Q_D(QHttpNetworkReply);
198 if (d->responseData.bufferCount() == 0)
201 // we'll take the last buffer, so schedule another read from http
202 if (d->downstreamLimited && d->responseData.bufferCount() == 1)
203 d->connection->d_func()->readMoreLater(this);
204 return d->responseData.read();
207 QByteArray QHttpNetworkReply::readAll()
209 Q_D(QHttpNetworkReply);
210 return d->responseData.readAll();
213 QByteArray QHttpNetworkReply::read(qint64 amount)
215 Q_D(QHttpNetworkReply);
216 return d->responseData.read(amount);
220 qint64 QHttpNetworkReply::sizeNextBlock()
222 Q_D(QHttpNetworkReply);
223 return d->responseData.sizeNextBlock();
226 void QHttpNetworkReply::setDownstreamLimited(bool dsl)
228 Q_D(QHttpNetworkReply);
229 d->downstreamLimited = dsl;
230 d->connection->d_func()->readMoreLater(this);
233 void QHttpNetworkReply::setReadBufferSize(qint64 size)
235 Q_D(QHttpNetworkReply);
236 d->readBufferMaxSize = size;
239 bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
241 Q_D(QHttpNetworkReply);
242 return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0 && d->statusCode == 200);
245 void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
247 Q_D(QHttpNetworkReply);
248 if (supportsUserProvidedDownloadBuffer())
249 d->userProvidedDownloadBuffer = b;
252 char* QHttpNetworkReply::userProvidedDownloadBuffer()
254 Q_D(QHttpNetworkReply);
255 return d->userProvidedDownloadBuffer;
258 bool QHttpNetworkReply::isFinished() const
260 return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
263 bool QHttpNetworkReply::isPipeliningUsed() const
265 return d_func()->pipeliningUsed;
268 QHttpNetworkConnection* QHttpNetworkReply::connection()
270 return d_func()->connection;
274 QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
275 : QHttpNetworkHeaderPrivate(newUrl)
276 , state(NothingDoneState)
279 majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
280 chunkedTransferEncoding(false),
281 connectionCloseEnabled(true),
282 forceConnectionCloseEnabled(false),
283 lastChunkRead(false),
284 currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0), connection(0),
285 autoDecompress(false), responseData(), requestIsPrepared(false)
286 ,pipeliningUsed(false), downstreamLimited(false)
287 ,userProvidedDownloadBuffer(0)
288 #ifndef QT_NO_COMPRESS
295 QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
297 #ifndef QT_NO_COMPRESS
303 void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
305 state = NothingDoneState;
310 currentChunkSize = 0;
311 currentChunkRead = 0;
312 lastChunkRead = false;
313 connectionCloseEnabled = true;
314 #ifndef QT_NO_COMPRESS
315 if (autoDecompress && inflateStrm)
316 inflateEnd(inflateStrm);
321 // TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
322 void QHttpNetworkReplyPrivate::clear()
325 connectionChannel = 0;
326 autoDecompress = false;
327 clearHttpLayerInformation();
330 // QHttpNetworkReplyPrivate
331 qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
333 return (state != ReadingDataState ? 0 : fragment.size());
336 bool QHttpNetworkReplyPrivate::isCompressed()
338 QByteArray encoding = headerField("content-encoding");
339 return qstricmp(encoding.constData(), "gzip") == 0 || qstricmp(encoding.constData(), "deflate") == 0;
342 void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
344 // The header "Content-Encoding = gzip" is retained.
345 // Content-Length is removed since the actual one send by the server is for compressed data
346 QByteArray name("content-length");
347 QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
350 if (qstricmp(name.constData(), it->first.constData()) == 0) {
359 bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
362 // find out the type of authentication protocol requested.
363 QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
364 // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
365 QList<QByteArray> challenges = headerFieldValues(header);
366 for (int i = 0; i<challenges.size(); i++) {
367 QByteArray line = challenges.at(i);
368 // todo use qstrincmp
369 if (!line.toLower().startsWith("negotiate"))
372 return !challenge.isEmpty();
375 QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
377 // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
378 QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
379 QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
380 QList<QByteArray> challenges = headerFieldValues(header);
381 for (int i = 0; i<challenges.size(); i++) {
382 QByteArray line = challenges.at(i).trimmed().toLower();
383 if (method < QAuthenticatorPrivate::Basic
384 && line.startsWith("basic")) {
385 method = QAuthenticatorPrivate::Basic;
386 } else if (method < QAuthenticatorPrivate::Ntlm
387 && line.startsWith("ntlm")) {
388 method = QAuthenticatorPrivate::Ntlm;
389 } else if (method < QAuthenticatorPrivate::DigestMd5
390 && line.startsWith("digest")) {
391 method = QAuthenticatorPrivate::DigestMd5;
397 qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
399 if (fragment.isEmpty()) {
400 // reserve bytes for the status line. This is better than always append() which reallocs the byte array
401 fragment.reserve(32);
409 haveRead = socket->read(&c, 1);
411 return -1; // unexpected EOF
412 else if (haveRead == 0)
413 break; // read more later
414 else if (haveRead == 1 && bytes == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
415 continue; // Ignore all whitespace that was trailing froma previous request on that socket
419 // allow both CRLF & LF (only) line endings
421 // remove the CR at the end
422 if (fragment.endsWith('\r')) {
423 fragment.truncate(fragment.length()-1);
425 bool ok = parseStatus(fragment);
426 state = ReadingHeaderState;
436 // is this a valid reply?
437 if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
442 } while (haveRead == 1);
447 bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
450 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
451 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
452 // that makes: 'HTTP/n.n xxx Message'
453 // byte count: 0123456789012
455 static const int minLength = 11;
456 static const int dotPos = 6;
457 static const int spacePos = 8;
458 static const char httpMagic[] = "HTTP/";
460 if (status.length() < minLength
461 || !status.startsWith(httpMagic)
462 || status.at(dotPos) != '.'
463 || status.at(spacePos) != ' ') {
464 // I don't know how to parse this status line
468 // optimize for the valid case: defer checking until the end
469 majorVersion = status.at(dotPos - 1) - '0';
470 minorVersion = status.at(dotPos + 1) - '0';
473 int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
474 const QByteArray code = status.mid(i + 1, j - i - 1);
477 statusCode = code.toInt(&ok);
478 reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
480 return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
483 qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
485 if (fragment.isEmpty()) {
486 // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
487 // block is 381 bytes.
488 // reserve bytes. This is better than always append() which reallocs the byte array.
489 fragment.reserve(512);
494 bool allHeaders = false;
497 haveRead = socket->read(&c, 1);
501 } else if (haveRead == -1) {
502 // connection broke down
509 // check for possible header endings. As per HTTP rfc,
510 // the header endings will be marked by CRLFCRLF. But
511 // we will allow CRLFCRLF, CRLFLF, LFLF
512 if (fragment.endsWith("\r\n\r\n")
513 || fragment.endsWith("\r\n\n")
514 || fragment.endsWith("\n\n"))
517 // there is another case: We have no headers. Then the fragment equals just the line ending
518 if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
519 || (fragment.length() == 1 && fragment.endsWith("\n")))
523 } while (!allHeaders && haveRead > 0);
525 // we received all headers now parse them
527 parseHeader(fragment);
528 state = ReadingDataState;
529 fragment.clear(); // next fragment
530 bodyLength = contentLength(); // cache the length
532 // cache isChunked() since it is called often
533 chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
535 // cache isConnectionCloseEnabled since it is called often
536 QByteArray connectionHeaderField = headerField("connection");
537 // check for explicit indication of close or the implicit connection close of HTTP/1.0
538 connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
539 headerField("proxy-connection").toLower().contains("close")) ||
540 (majorVersion == 1 && minorVersion == 0 && connectionHeaderField.isEmpty());
542 #ifndef QT_NO_COMPRESS
543 if (autoDecompress && isCompressed()) {
544 // allocate inflate state
546 inflateStrm = new z_stream;
547 inflateStrm->zalloc = Z_NULL;
548 inflateStrm->zfree = Z_NULL;
549 inflateStrm->opaque = Z_NULL;
550 inflateStrm->avail_in = 0;
551 inflateStrm->next_in = Z_NULL;
552 // "windowBits can also be greater than 15 for optional gzip decoding.
553 // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
554 // http://www.zlib.net/manual.html
555 int ret = inflateInit2(inflateStrm, MAX_WBITS+32);
565 void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
567 // see rfc2616, sec 4 for information about HTTP/1.1 headers.
568 // allows relaxed parsing here, accepts both CRLF & LF line endings
569 const QByteArrayMatcher lf("\n");
570 const QByteArrayMatcher colon(":");
572 while (i < header.count()) {
573 int j = colon.indexIn(header, i); // field-name
576 const QByteArray field = header.mid(i, j - i).trimmed();
578 // any number of LWS is allowed before and after the value
581 i = lf.indexIn(header, j);
584 if (!value.isEmpty())
586 // check if we have CRLF or only LF
587 bool hasCR = (i && header[i-1] == '\r');
588 int length = i -(hasCR ? 1: 0) - j;
589 value += header.mid(j, length).trimmed();
591 } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
593 break; // something is wrong
595 fields.append(qMakePair(field, value));
599 bool QHttpNetworkReplyPrivate::isChunked()
601 return chunkedTransferEncoding;
604 bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
606 return connectionCloseEnabled || forceConnectionCloseEnabled;
609 // note this function can only be used for non-chunked, non-compressed with
610 // known content length
611 qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
613 // This first read is to flush the buffer inside the socket
615 haveRead = socket->read(b, bodyLength - contentRead);
616 if (haveRead == -1) {
619 contentRead += haveRead;
621 if (contentRead == bodyLength) {
622 state = AllDoneState;
628 // note this function can only be used for non-chunked, non-compressed with
629 // known content length
630 qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
633 qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
634 if (readBufferMaxSize)
635 toBeRead = qMin(toBeRead, readBufferMaxSize);
642 qint64 haveRead = socket->read(bd.data(), toBeRead);
643 if (haveRead == -1) {
645 return 0; // ### error checking here;
651 if (contentRead + haveRead == bodyLength) {
652 state = AllDoneState;
655 contentRead += haveRead;
660 qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
664 #ifndef QT_NO_COMPRESS
665 // for gzip we'll allocate a temporary one that we then decompress
666 QByteDataBuffer *tempOutDataBuffer = (autoDecompress ? new QByteDataBuffer : out);
668 QByteDataBuffer *tempOutDataBuffer = out;
673 // chunked transfer encoding (rfc 2616, sec 3.6)
674 bytes += readReplyBodyChunked(socket, tempOutDataBuffer);
675 } else if (bodyLength > 0) {
676 // we have a Content-Length
677 bytes += readReplyBodyRaw(socket, tempOutDataBuffer, bodyLength - contentRead);
678 if (contentRead + bytes == bodyLength)
679 state = AllDoneState;
681 // no content length. just read what's possible
682 bytes += readReplyBodyRaw(socket, tempOutDataBuffer, socket->bytesAvailable());
685 #ifndef QT_NO_COMPRESS
686 // This is true if there is compressed encoding and we're supposed to use it.
687 if (autoDecompress) {
688 qint64 uncompressRet = uncompressBodyData(tempOutDataBuffer, out);
689 delete tempOutDataBuffer;
690 if (uncompressRet < 0)
695 contentRead += bytes;
699 #ifndef QT_NO_COMPRESS
700 qint64 QHttpNetworkReplyPrivate::uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out)
705 bool triedRawDeflate = false;
706 for (int i = 0; i < in->bufferCount(); i++) {
707 QByteArray &bIn = (*in)[i];
709 inflateStrm->avail_in = bIn.size();
710 inflateStrm->next_in = reinterpret_cast<Bytef*>(bIn.data());
714 // make a wild guess about the uncompressed size.
715 bOut.reserve(inflateStrm->avail_in * 3 + 512);
716 inflateStrm->avail_out = bOut.capacity();
717 inflateStrm->next_out = reinterpret_cast<Bytef*>(bOut.data());
719 int ret = inflate(inflateStrm, Z_NO_FLUSH);
720 //All negative return codes are errors, in the context of HTTP compression, Z_NEED_DICT is also an error.
721 // in the case where we get Z_DATA_ERROR this could be because we recieved raw deflate compressed data.
722 if (ret == Z_DATA_ERROR && !triedRawDeflate) {
723 inflateEnd(inflateStrm);
724 triedRawDeflate = true;
725 inflateStrm->zalloc = Z_NULL;
726 inflateStrm->zfree = Z_NULL;
727 inflateStrm->opaque = Z_NULL;
728 inflateStrm->avail_in = 0;
729 inflateStrm->next_in = Z_NULL;
730 int ret = inflateInit2(inflateStrm, -MAX_WBITS);
734 inflateStrm->avail_in = bIn.size();
735 inflateStrm->next_in = reinterpret_cast<Bytef*>(bIn.data());
738 } else if (ret < 0 || ret == Z_NEED_DICT) {
741 bOut.resize(bOut.capacity() - inflateStrm->avail_out);
743 if (ret == Z_STREAM_END)
744 return out->byteAmount();
745 } while (inflateStrm->avail_in > 0);
748 return out->byteAmount();
752 qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
754 // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
759 int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
761 if (readBufferMaxSize)
762 toBeRead = qMin<qint64>(toBeRead, readBufferMaxSize);
764 while (toBeRead > 0) {
766 byteData.resize(toBeRead);
767 qint64 haveRead = socket->read(byteData.data(), byteData.size());
769 // ### error checking here
774 byteData.resize(haveRead);
775 out->append(byteData);
779 toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
785 qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
788 while (socket->bytesAvailable()) {
790 if (readBufferMaxSize && (bytes > readBufferMaxSize))
793 if (!lastChunkRead && currentChunkRead >= currentChunkSize) {
794 // For the first chunk and when we're done with a chunk
795 currentChunkSize = 0;
796 currentChunkRead = 0;
800 // read the "\r\n" after the chunk
801 qint64 haveRead = socket->read(crlf, 2);
802 // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
803 // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
804 // it right now still works, but we should definitely fix this.
807 return bytes; // FIXME
810 // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
811 bytes += getChunkSize(socket, ¤tChunkSize);
812 if (currentChunkSize == -1)
815 // if the chunk size is 0, end of the stream
816 if (currentChunkSize == 0 || lastChunkRead) {
817 lastChunkRead = true;
818 // try to read the "\r\n" after the chunk
820 qint64 haveRead = socket->read(crlf, 2);
824 if ((haveRead == 2 && crlf[0] == '\r' && crlf[1] == '\n') || (haveRead == 1 && crlf[0] == '\n'))
825 state = AllDoneState;
826 else if (haveRead == 1 && crlf[0] == '\r')
827 break; // Still waiting for the last \n
828 else if (haveRead > 0) {
829 // If we read something else then CRLF, we need to close the channel.
830 forceConnectionCloseEnabled = true;
831 state = AllDoneState;
836 // otherwise, try to begin reading this chunk / to read what is missing for this chunk
837 qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead);
838 currentChunkRead += haveRead;
841 // ### error checking here
847 qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
853 int bytesAvailable = socket->bytesAvailable();
854 // FIXME rewrite to permanent loop without bytesAvailable
855 while (bytesAvailable > bytes) {
856 qint64 sniffedBytes = socket->peek(crlf, 2);
857 int fragmentSize = fragment.size();
859 // check the next two bytes for a "\r\n", skip blank lines
860 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
861 ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
863 bytes += socket->read(crlf, 1); // read the \r or \n
865 bytes += socket->read(crlf, 1); // read the \n
867 // ignore the chunk-extension
868 fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
869 *chunkSize = fragment.toLong(&ok, 16);
873 // read the fragment to the buffer
875 qint64 haveRead = socket->read(&c, 1);
887 bool QHttpNetworkReplyPrivate::shouldEmitSignals()
889 // for 401 & 407 don't emit the data signals. Content along with these
890 // responses are send only if the authentication fails.
891 return (statusCode != 401 && statusCode != 407);
894 bool QHttpNetworkReplyPrivate::expectContent()
896 // check whether we can expect content after the headers (rfc 2616, sec4.4)
897 if ((statusCode >= 100 && statusCode < 200)
898 || statusCode == 204 || statusCode == 304)
900 if (request.operation() == QHttpNetworkRequest::Head)
901 return false; // no body expected for HEAD request
902 qint64 expectedContentLength = contentLength();
903 if (expectedContentLength == 0)
905 if (expectedContentLength == -1 && bodyLength == 0) {
906 // The content-length header was stripped, but its value was 0.
907 // This would be the case for an explicitly zero-length compressed response.
913 void QHttpNetworkReplyPrivate::eraseData()
915 compressedData.clear();
916 responseData.clear();
923 QSslConfiguration QHttpNetworkReply::sslConfiguration() const
925 Q_D(const QHttpNetworkReply);
927 if (!d->connectionChannel)
928 return QSslConfiguration();
930 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
932 return QSslConfiguration();
934 return sslSocket->sslConfiguration();
937 void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
939 Q_D(QHttpNetworkReply);
941 d->connection->setSslConfiguration(config);
944 void QHttpNetworkReply::ignoreSslErrors()
946 Q_D(QHttpNetworkReply);
948 d->connection->ignoreSslErrors();
951 void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
953 Q_D(QHttpNetworkReply);
955 d->connection->ignoreSslErrors(errors);