choke uploadProgress signals
[profile/ivi/qtbase.git] / src / network / access / qhttpnetworkreply.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qhttpnetworkreply_p.h"
43 #include "qhttpnetworkconnection_p.h"
44
45 #include <qbytearraymatcher.h>
46
47 #ifndef QT_NO_HTTP
48
49 #ifndef QT_NO_SSL
50 #    include <QtNetwork/qsslkey.h>
51 #    include <QtNetwork/qsslcipher.h>
52 #    include <QtNetwork/qsslconfiguration.h>
53 #endif
54
55 #ifndef QT_NO_COMPRESS
56 #include <zlib.h>
57 #endif
58
59 QT_BEGIN_NAMESPACE
60
61 QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
62     : QObject(*new QHttpNetworkReplyPrivate(url), parent)
63 {
64 }
65
66 QHttpNetworkReply::~QHttpNetworkReply()
67 {
68     Q_D(QHttpNetworkReply);
69     if (d->connection) {
70         d->connection->d_func()->removeReply(this);
71     }
72
73 #ifndef QT_NO_COMPRESS
74     if (d->autoDecompress && d->isCompressed() && d->inflateStrm)
75         inflateEnd(d->inflateStrm);
76 #endif
77 }
78
79 QUrl QHttpNetworkReply::url() const
80 {
81     return d_func()->url;
82 }
83 void QHttpNetworkReply::setUrl(const QUrl &url)
84 {
85     Q_D(QHttpNetworkReply);
86     d->url = url;
87 }
88
89 qint64 QHttpNetworkReply::contentLength() const
90 {
91     return d_func()->contentLength();
92 }
93
94 void QHttpNetworkReply::setContentLength(qint64 length)
95 {
96     Q_D(QHttpNetworkReply);
97     d->setContentLength(length);
98 }
99
100 QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
101 {
102     return d_func()->fields;
103 }
104
105 QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
106 {
107     return d_func()->headerField(name, defaultValue);
108 }
109
110 void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
111 {
112     Q_D(QHttpNetworkReply);
113     d->setHeaderField(name, data);
114 }
115
116 void QHttpNetworkReply::parseHeader(const QByteArray &header)
117 {
118     Q_D(QHttpNetworkReply);
119     d->parseHeader(header);
120 }
121
122 QHttpNetworkRequest QHttpNetworkReply::request() const
123 {
124     return d_func()->request;
125 }
126
127 void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
128 {
129     Q_D(QHttpNetworkReply);
130     d->request = request;
131     d->ssl = request.isSsl();
132 }
133
134 int QHttpNetworkReply::statusCode() const
135 {
136     return d_func()->statusCode;
137 }
138
139 void QHttpNetworkReply::setStatusCode(int code)
140 {
141     Q_D(QHttpNetworkReply);
142     d->statusCode = code;
143 }
144
145 QString QHttpNetworkReply::errorString() const
146 {
147     return d_func()->errorString;
148 }
149
150 QString QHttpNetworkReply::reasonPhrase() const
151 {
152     return d_func()->reasonPhrase;
153 }
154
155 void QHttpNetworkReply::setErrorString(const QString &error)
156 {
157     Q_D(QHttpNetworkReply);
158     d->errorString = error;
159 }
160
161 int QHttpNetworkReply::majorVersion() const
162 {
163     return d_func()->majorVersion;
164 }
165
166 int QHttpNetworkReply::minorVersion() const
167 {
168     return d_func()->minorVersion;
169 }
170
171 qint64 QHttpNetworkReply::bytesAvailable() const
172 {
173     Q_D(const QHttpNetworkReply);
174     if (d->connection)
175         return d->connection->d_func()->uncompressedBytesAvailable(*this);
176     else
177         return -1;
178 }
179
180 qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
181 {
182     Q_D(const QHttpNetworkReply);
183     if (d->connection)
184         return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
185     else
186         return -1;
187 }
188
189 bool QHttpNetworkReply::readAnyAvailable() const
190 {
191     Q_D(const QHttpNetworkReply);
192     return (d->responseData.bufferCount() > 0);
193 }
194
195 QByteArray QHttpNetworkReply::readAny()
196 {
197     Q_D(QHttpNetworkReply);
198     if (d->responseData.bufferCount() == 0)
199         return QByteArray();
200
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();
205 }
206
207 QByteArray QHttpNetworkReply::readAll()
208 {
209     Q_D(QHttpNetworkReply);
210     return d->responseData.readAll();
211 }
212
213 QByteArray QHttpNetworkReply::read(qint64 amount)
214 {
215     Q_D(QHttpNetworkReply);
216     return d->responseData.read(amount);
217 }
218
219
220 qint64 QHttpNetworkReply::sizeNextBlock()
221 {
222     Q_D(QHttpNetworkReply);
223     return d->responseData.sizeNextBlock();
224 }
225
226 void QHttpNetworkReply::setDownstreamLimited(bool dsl)
227 {
228     Q_D(QHttpNetworkReply);
229     d->downstreamLimited = dsl;
230     d->connection->d_func()->readMoreLater(this);
231 }
232
233 void QHttpNetworkReply::setReadBufferSize(qint64 size)
234 {
235     Q_D(QHttpNetworkReply);
236     d->readBufferMaxSize = size;
237 }
238
239 bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
240 {
241     Q_D(QHttpNetworkReply);
242     return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0 && d->statusCode == 200);
243 }
244
245 void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
246 {
247     Q_D(QHttpNetworkReply);
248     if (supportsUserProvidedDownloadBuffer())
249         d->userProvidedDownloadBuffer = b;
250 }
251
252 char* QHttpNetworkReply::userProvidedDownloadBuffer()
253 {
254     Q_D(QHttpNetworkReply);
255     return d->userProvidedDownloadBuffer;
256 }
257
258 bool QHttpNetworkReply::isFinished() const
259 {
260     return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
261 }
262
263 bool QHttpNetworkReply::isPipeliningUsed() const
264 {
265     return d_func()->pipeliningUsed;
266 }
267
268 QHttpNetworkConnection* QHttpNetworkReply::connection()
269 {
270     return d_func()->connection;
271 }
272
273
274 QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
275     : QHttpNetworkHeaderPrivate(newUrl)
276     , state(NothingDoneState)
277     , ssl(false)
278     , statusCode(100),
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
289       ,inflateStrm(0)
290 #endif
291
292 {
293 }
294
295 QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
296 {
297 #ifndef QT_NO_COMPRESS
298       if (inflateStrm)
299           delete inflateStrm;
300 #endif
301 }
302
303 void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
304 {
305     state = NothingDoneState;
306     statusCode = 100;
307     bodyLength = 0;
308     contentRead = 0;
309     totalProgress = 0;
310     currentChunkSize = 0;
311     currentChunkRead = 0;
312     lastChunkRead = false;
313     connectionCloseEnabled = true;
314 #ifndef QT_NO_COMPRESS
315     if (autoDecompress && inflateStrm)
316         inflateEnd(inflateStrm);
317 #endif
318     fields.clear();
319 }
320
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()
323 {
324     connection = 0;
325     connectionChannel = 0;
326     autoDecompress = false;
327     clearHttpLayerInformation();
328 }
329
330 // QHttpNetworkReplyPrivate
331 qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
332 {
333     return (state != ReadingDataState ? 0 : fragment.size());
334 }
335
336 bool QHttpNetworkReplyPrivate::isCompressed()
337 {
338     QByteArray encoding = headerField("content-encoding");
339     return qstricmp(encoding.constData(), "gzip") == 0 || qstricmp(encoding.constData(), "deflate") == 0;
340 }
341
342 void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
343 {
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(),
348                                                    end = fields.end();
349     while (it != end) {
350         if (qstricmp(name.constData(), it->first.constData()) == 0) {
351             fields.erase(it);
352             break;
353         }
354         ++it;
355     }
356
357 }
358
359 bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
360 {
361     challenge.clear();
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"))
370             challenge = line;
371     }
372     return !challenge.isEmpty();
373 }
374
375 QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
376 {
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;
392         }
393     }
394     return method;
395 }
396
397 qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
398 {
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);
402     }
403
404     qint64 bytes = 0;
405     char c;
406     qint64 haveRead = 0;
407
408     do {
409         haveRead = socket->read(&c, 1);
410         if (haveRead == -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
416
417         bytes++;
418
419         // allow both CRLF & LF (only) line endings
420         if (c == '\n') {
421             // remove the CR at the end
422             if (fragment.endsWith('\r')) {
423                 fragment.truncate(fragment.length()-1);
424             }
425             bool ok = parseStatus(fragment);
426             state = ReadingHeaderState;
427             fragment.clear();
428             if (!ok) {
429                 return -1;
430             }
431             break;
432         } else {
433             fragment.append(c);
434         }
435
436         // is this a valid reply?
437         if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
438         {
439             fragment.clear();
440             return -1;
441         }
442     } while (haveRead == 1);
443
444     return bytes;
445 }
446
447 bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
448 {
449     // from RFC 2616:
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
454
455     static const int minLength = 11;
456     static const int dotPos = 6;
457     static const int spacePos = 8;
458     static const char httpMagic[] = "HTTP/";
459
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
465         return false;
466     }
467
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';
471
472     int i = spacePos;
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);
475
476     bool ok;
477     statusCode = code.toInt(&ok);
478     reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
479
480     return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
481 }
482
483 qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
484 {
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);
490     }
491
492     qint64 bytes = 0;
493     char c = 0;
494     bool allHeaders = false;
495     qint64 haveRead = 0;
496     do {
497         haveRead = socket->read(&c, 1);
498         if (haveRead == 0) {
499             // read more later
500             break;
501         } else if (haveRead == -1) {
502             // connection broke down
503             return -1;
504         } else {
505             fragment.append(c);
506             bytes++;
507
508             if (c == '\n') {
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"))
515                     allHeaders = true;
516
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")))
520                     allHeaders = true;
521             }
522         }
523     } while (!allHeaders && haveRead > 0);
524
525     // we received all headers now parse them
526     if (allHeaders) {
527         parseHeader(fragment);
528         state = ReadingDataState;
529         fragment.clear(); // next fragment
530         bodyLength = contentLength(); // cache the length
531
532         // cache isChunked() since it is called often
533         chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
534
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());
541
542 #ifndef QT_NO_COMPRESS
543         if (autoDecompress && isCompressed()) {
544             // allocate inflate state
545             if (!inflateStrm)
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);
556             if (ret != Z_OK)
557                 return -1;
558         }
559 #endif
560
561     }
562     return bytes;
563 }
564
565 void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
566 {
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(":");
571     int i = 0;
572     while (i < header.count()) {
573         int j = colon.indexIn(header, i); // field-name
574         if (j == -1)
575             break;
576         const QByteArray field = header.mid(i, j - i).trimmed();
577         j++;
578         // any number of LWS is allowed before and after the value
579         QByteArray value;
580         do {
581             i = lf.indexIn(header, j);
582             if (i == -1)
583                 break;
584             if (!value.isEmpty())
585                 value += ' ';
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();
590             j = ++i;
591         } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
592         if (i == -1)
593             break; // something is wrong
594
595         fields.append(qMakePair(field, value));
596     }
597 }
598
599 bool QHttpNetworkReplyPrivate::isChunked()
600 {
601     return chunkedTransferEncoding;
602 }
603
604 bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
605 {
606     return connectionCloseEnabled || forceConnectionCloseEnabled;
607 }
608
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)
612 {
613     // This first read is to flush the buffer inside the socket
614     qint64 haveRead = 0;
615     haveRead = socket->read(b, bodyLength - contentRead);
616     if (haveRead == -1) {
617         return -1;
618     }
619     contentRead += haveRead;
620
621     if (contentRead == bodyLength) {
622         state = AllDoneState;
623     }
624
625     return haveRead;
626 }
627
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)
631 {
632
633     qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
634     if (readBufferMaxSize)
635         toBeRead = qMin(toBeRead, readBufferMaxSize);
636
637     if (!toBeRead)
638         return 0;
639
640     QByteArray bd;
641     bd.resize(toBeRead);
642     qint64 haveRead = socket->read(bd.data(), toBeRead);
643     if (haveRead == -1) {
644         bd.clear();
645         return 0; // ### error checking here;
646     }
647     bd.resize(haveRead);
648
649     rb->append(bd);
650
651     if (contentRead + haveRead == bodyLength) {
652         state = AllDoneState;
653     }
654
655     contentRead += haveRead;
656     return haveRead;
657 }
658
659
660 qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
661 {
662     qint64 bytes = 0;
663
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);
667 #else
668     QByteDataBuffer *tempOutDataBuffer = out;
669 #endif
670
671
672     if (isChunked()) {
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;
680     } else {
681         // no content length. just read what's possible
682         bytes += readReplyBodyRaw(socket, tempOutDataBuffer, socket->bytesAvailable());
683     }
684
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)
691             return -1;
692     }
693 #endif
694
695     contentRead += bytes;
696     return bytes;
697 }
698
699 #ifndef QT_NO_COMPRESS
700 qint64 QHttpNetworkReplyPrivate::uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out)
701 {
702     if (!inflateStrm)
703         return -1;
704
705     bool triedRawDeflate = false;
706     for (int i = 0; i < in->bufferCount(); i++) {
707         QByteArray &bIn = (*in)[i];
708
709         inflateStrm->avail_in = bIn.size();
710         inflateStrm->next_in = reinterpret_cast<Bytef*>(bIn.data());
711
712         do {
713             QByteArray bOut;
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());
718
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);
731                 if (ret != Z_OK) {
732                     return -1;
733                 } else {
734                     inflateStrm->avail_in = bIn.size();
735                     inflateStrm->next_in = reinterpret_cast<Bytef*>(bIn.data());
736                     continue;
737                 }
738             } else if (ret < 0 || ret == Z_NEED_DICT) {
739                 return -1;
740             }
741             bOut.resize(bOut.capacity() - inflateStrm->avail_out);
742             out->append(bOut);
743             if (ret == Z_STREAM_END)
744                 return out->byteAmount();
745         } while (inflateStrm->avail_in > 0);
746     }
747
748     return out->byteAmount();
749 }
750 #endif
751
752 qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
753 {
754     // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
755     qint64 bytes = 0;
756     Q_ASSERT(socket);
757     Q_ASSERT(out);
758
759     int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
760
761     if (readBufferMaxSize)
762         toBeRead = qMin<qint64>(toBeRead, readBufferMaxSize);
763
764     while (toBeRead > 0) {
765         QByteArray byteData;
766         byteData.resize(toBeRead);
767         qint64 haveRead = socket->read(byteData.data(), byteData.size());
768         if (haveRead <= 0) {
769             // ### error checking here
770             byteData.clear();
771             return bytes;
772         }
773
774         byteData.resize(haveRead);
775         out->append(byteData);
776         bytes += haveRead;
777         size -= haveRead;
778
779         toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
780     }
781     return bytes;
782
783 }
784
785 qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
786 {
787     qint64 bytes = 0;
788     while (socket->bytesAvailable()) {
789
790         if (readBufferMaxSize && (bytes > readBufferMaxSize))
791             break;
792
793         if (!lastChunkRead && currentChunkRead >= currentChunkSize) {
794             // For the first chunk and when we're done with a chunk
795             currentChunkSize = 0;
796             currentChunkRead = 0;
797             if (bytes) {
798                 // After a chunk
799                 char crlf[2];
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.
805
806                 if (haveRead != 2)
807                     return bytes; // FIXME
808                 bytes += haveRead;
809             }
810             // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
811             bytes += getChunkSize(socket, &currentChunkSize);
812             if (currentChunkSize == -1)
813                 break;
814         }
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
819             char crlf[2];
820             qint64 haveRead = socket->read(crlf, 2);
821             if (haveRead > 0)
822                 bytes += haveRead;
823
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;
832             }
833             break;
834         }
835
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;
839         bytes += haveRead;
840
841         // ### error checking here
842
843     }
844     return bytes;
845 }
846
847 qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
848 {
849     qint64 bytes = 0;
850     char crlf[2];
851     *chunkSize = -1;
852
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();
858
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'))
862         {
863             bytes += socket->read(crlf, 1);     // read the \r or \n
864             if (crlf[0] == '\r')
865                 bytes += socket->read(crlf, 1); // read the \n
866             bool ok = false;
867             // ignore the chunk-extension
868             fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
869             *chunkSize = fragment.toLong(&ok, 16);
870             fragment.clear();
871             break; // size done
872         } else {
873             // read the fragment to the buffer
874             char c = 0;
875             qint64 haveRead = socket->read(&c, 1);
876             if (haveRead < 0) {
877                 return -1; // FIXME
878             }
879             bytes += haveRead;
880             fragment.append(c);
881         }
882     }
883
884     return bytes;
885 }
886
887 bool QHttpNetworkReplyPrivate::shouldEmitSignals()
888 {
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);
892 }
893
894 bool QHttpNetworkReplyPrivate::expectContent()
895 {
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)
899         return false;
900     if (request.operation() == QHttpNetworkRequest::Head)
901         return false; // no body expected for HEAD request
902     qint64 expectedContentLength = contentLength();
903     if (expectedContentLength == 0)
904         return false;
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.
908         return false;
909     }
910     return true;
911 }
912
913 void QHttpNetworkReplyPrivate::eraseData()
914 {
915     compressedData.clear();
916     responseData.clear();
917 }
918
919
920 // SSL support below
921 #ifndef QT_NO_SSL
922
923 QSslConfiguration QHttpNetworkReply::sslConfiguration() const
924 {
925     Q_D(const QHttpNetworkReply);
926
927     if (!d->connectionChannel)
928         return QSslConfiguration();
929
930     QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
931     if (!sslSocket)
932         return QSslConfiguration();
933
934     return sslSocket->sslConfiguration();
935 }
936
937 void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
938 {
939     Q_D(QHttpNetworkReply);
940     if (d->connection)
941         d->connection->setSslConfiguration(config);
942 }
943
944 void QHttpNetworkReply::ignoreSslErrors()
945 {
946     Q_D(QHttpNetworkReply);
947     if (d->connection)
948         d->connection->ignoreSslErrors();
949 }
950
951 void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
952 {
953     Q_D(QHttpNetworkReply);
954     if (d->connection)
955         d->connection->ignoreSslErrors(errors);
956 }
957
958
959 #endif //QT_NO_SSL
960
961
962 QT_END_NAMESPACE
963
964 #endif // QT_NO_HTTP