source: trunk/src/network/access/qhttpnetworkreply.cpp@ 1054

Last change on this file since 1054 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 25.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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_OPENSSL
50# include <QtNetwork/qsslkey.h>
51# include <QtNetwork/qsslcipher.h>
52# include <QtNetwork/qsslconfiguration.h>
53#endif
54
55QT_BEGIN_NAMESPACE
56
57QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
58 : QObject(*new QHttpNetworkReplyPrivate(url), parent)
59{
60}
61
62QHttpNetworkReply::~QHttpNetworkReply()
63{
64 Q_D(QHttpNetworkReply);
65 if (d->connection) {
66 d->connection->d_func()->removeReply(this);
67 }
68}
69
70QUrl QHttpNetworkReply::url() const
71{
72 return d_func()->url;
73}
74void QHttpNetworkReply::setUrl(const QUrl &url)
75{
76 Q_D(QHttpNetworkReply);
77 d->url = url;
78}
79
80qint64 QHttpNetworkReply::contentLength() const
81{
82 return d_func()->contentLength();
83}
84
85void QHttpNetworkReply::setContentLength(qint64 length)
86{
87 Q_D(QHttpNetworkReply);
88 d->setContentLength(length);
89}
90
91QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
92{
93 return d_func()->fields;
94}
95
96QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
97{
98 return d_func()->headerField(name, defaultValue);
99}
100
101void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
102{
103 Q_D(QHttpNetworkReply);
104 d->setHeaderField(name, data);
105}
106
107void QHttpNetworkReply::parseHeader(const QByteArray &header)
108{
109 Q_D(QHttpNetworkReply);
110 d->parseHeader(header);
111}
112
113QHttpNetworkRequest QHttpNetworkReply::request() const
114{
115 return d_func()->request;
116}
117
118void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
119{
120 Q_D(QHttpNetworkReply);
121 d->request = request;
122}
123
124int QHttpNetworkReply::statusCode() const
125{
126 return d_func()->statusCode;
127}
128
129void QHttpNetworkReply::setStatusCode(int code)
130{
131 Q_D(QHttpNetworkReply);
132 d->statusCode = code;
133}
134
135QString QHttpNetworkReply::errorString() const
136{
137 return d_func()->errorString;
138}
139
140QString QHttpNetworkReply::reasonPhrase() const
141{
142 return d_func()->reasonPhrase;
143}
144
145void QHttpNetworkReply::setErrorString(const QString &error)
146{
147 Q_D(QHttpNetworkReply);
148 d->errorString = error;
149}
150
151int QHttpNetworkReply::majorVersion() const
152{
153 return d_func()->majorVersion;
154}
155
156int QHttpNetworkReply::minorVersion() const
157{
158 return d_func()->minorVersion;
159}
160
161qint64 QHttpNetworkReply::bytesAvailable() const
162{
163 Q_D(const QHttpNetworkReply);
164 if (d->connection)
165 return d->connection->d_func()->uncompressedBytesAvailable(*this);
166 else
167 return -1;
168}
169
170qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
171{
172 Q_D(const QHttpNetworkReply);
173 if (d->connection)
174 return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
175 else
176 return -1;
177}
178
179QByteArray QHttpNetworkReply::readAny()
180{
181 Q_D(QHttpNetworkReply);
182 if (d->responseData.bufferCount() == 0)
183 return QByteArray();
184
185 // we'll take the last buffer, so schedule another read from http
186 if (d->downstreamLimited && d->responseData.bufferCount() == 1)
187 d->connection->d_func()->readMoreLater(this);
188 return d->responseData.read();
189}
190
191QByteArray QHttpNetworkReply::readAll()
192{
193 Q_D(QHttpNetworkReply);
194 return d->responseData.readAll();
195}
196
197void QHttpNetworkReply::setDownstreamLimited(bool dsl)
198{
199 Q_D(QHttpNetworkReply);
200 d->downstreamLimited = dsl;
201 d->connection->d_func()->readMoreLater(this);
202}
203
204bool QHttpNetworkReply::isFinished() const
205{
206 return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
207}
208
209bool QHttpNetworkReply::isPipeliningUsed() const
210{
211 return d_func()->pipeliningUsed;
212}
213
214QHttpNetworkConnection* QHttpNetworkReply::connection()
215{
216 return d_func()->connection;
217}
218
219
220QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
221 : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
222 majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
223 chunkedTransferEncoding(false),
224 connectionCloseEnabled(true),
225 forceConnectionCloseEnabled(false),
226 currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
227 autoDecompress(false), responseData(), requestIsPrepared(false)
228 ,pipeliningUsed(false), downstreamLimited(false)
229{
230}
231
232QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
233{
234}
235
236void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
237{
238 state = NothingDoneState;
239 statusCode = 100;
240 bodyLength = 0;
241 contentRead = 0;
242 totalProgress = 0;
243 currentChunkSize = 0;
244 currentChunkRead = 0;
245 connectionCloseEnabled = true;
246#ifndef QT_NO_COMPRESS
247 if (initInflate)
248 inflateEnd(&inflateStrm);
249#endif
250 initInflate = false;
251 streamEnd = false;
252 fields.clear();
253}
254
255// TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
256void QHttpNetworkReplyPrivate::clear()
257{
258 connection = 0;
259 connectionChannel = 0;
260 autoDecompress = false;
261 clearHttpLayerInformation();
262}
263
264// QHttpNetworkReplyPrivate
265qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
266{
267 return (state != ReadingDataState ? 0 : fragment.size());
268}
269
270bool QHttpNetworkReplyPrivate::isGzipped()
271{
272 QByteArray encoding = headerField("content-encoding");
273 return qstricmp(encoding.constData(), "gzip") == 0;
274}
275
276void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
277{
278 // The header "Content-Encoding = gzip" is retained.
279 // Content-Length is removed since the actual one send by the server is for compressed data
280 QByteArray name("content-length");
281 QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
282 end = fields.end();
283 while (it != end) {
284 if (qstricmp(name.constData(), it->first.constData()) == 0) {
285 fields.erase(it);
286 break;
287 }
288 ++it;
289 }
290
291}
292
293bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
294{
295 challenge.clear();
296 // find out the type of authentication protocol requested.
297 QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
298 // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
299 QList<QByteArray> challenges = headerFieldValues(header);
300 for (int i = 0; i<challenges.size(); i++) {
301 QByteArray line = challenges.at(i);
302 // todo use qstrincmp
303 if (!line.toLower().startsWith("negotiate"))
304 challenge = line;
305 }
306 return !challenge.isEmpty();
307}
308
309QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
310{
311 // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
312 QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
313 QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
314 QList<QByteArray> challenges = headerFieldValues(header);
315 for (int i = 0; i<challenges.size(); i++) {
316 QByteArray line = challenges.at(i).trimmed().toLower();
317 if (method < QAuthenticatorPrivate::Basic
318 && line.startsWith("basic")) {
319 method = QAuthenticatorPrivate::Basic;
320 } else if (method < QAuthenticatorPrivate::Ntlm
321 && line.startsWith("ntlm")) {
322 method = QAuthenticatorPrivate::Ntlm;
323 } else if (method < QAuthenticatorPrivate::DigestMd5
324 && line.startsWith("digest")) {
325 method = QAuthenticatorPrivate::DigestMd5;
326 }
327 }
328 return method;
329}
330
331#ifndef QT_NO_COMPRESS
332bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos)
333{
334 int method = 0; // method byte
335 int flags = 0; // flags byte
336 bool ret = false;
337
338 // Assure two bytes in the buffer so we can peek ahead -- handle case
339 // where first byte of header is at the end of the buffer after the last
340 // gzip segment
341 pos = -1;
342 QByteArray &body = content;
343 int maxPos = body.size()-1;
344 if (maxPos < 1) {
345 return ret;
346 }
347
348 // Peek ahead to check the gzip magic header
349 if (body[0] != char(gz_magic[0]) ||
350 body[1] != char(gz_magic[1])) {
351 return ret;
352 }
353 pos += 2;
354 // Check the rest of the gzip header
355 if (++pos <= maxPos)
356 method = body[pos];
357 if (pos++ <= maxPos)
358 flags = body[pos];
359 if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
360 return ret;
361 }
362
363 // Discard time, xflags and OS code:
364 pos += 6;
365 if (pos > maxPos)
366 return ret;
367 if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field
368 unsigned len = (unsigned)body[++pos];
369 len += ((unsigned)body[++pos])<<8;
370 pos += len;
371 if (pos > maxPos)
372 return ret;
373 }
374 if ((flags & ORIG_NAME) != 0) { // skip the original file name
375 while(++pos <= maxPos && body[pos]) {}
376 }
377 if ((flags & COMMENT) != 0) { // skip the .gz file comment
378 while(++pos <= maxPos && body[pos]) {}
379 }
380 if ((flags & HEAD_CRC) != 0) { // skip the header crc
381 pos += 2;
382 if (pos > maxPos)
383 return ret;
384 }
385 ret = (pos < maxPos); // return failed, if no more bytes left
386 return ret;
387}
388
389int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated)
390{
391 int ret = Z_DATA_ERROR;
392 unsigned have;
393 unsigned char out[CHUNK];
394 int pos = -1;
395
396 if (!initInflate) {
397 // check the header
398 if (!gzipCheckHeader(compressed, pos))
399 return ret;
400 // allocate inflate state
401 inflateStrm.zalloc = Z_NULL;
402 inflateStrm.zfree = Z_NULL;
403 inflateStrm.opaque = Z_NULL;
404 inflateStrm.avail_in = 0;
405 inflateStrm.next_in = Z_NULL;
406 ret = inflateInit2(&inflateStrm, -MAX_WBITS);
407 if (ret != Z_OK)
408 return ret;
409 initInflate = true;
410 streamEnd = false;
411 }
412
413 //remove the header.
414 compressed.remove(0, pos+1);
415 // expand until deflate stream ends
416 inflateStrm.next_in = (unsigned char *)compressed.data();
417 inflateStrm.avail_in = compressed.size();
418 do {
419 inflateStrm.avail_out = sizeof(out);
420 inflateStrm.next_out = out;
421 ret = inflate(&inflateStrm, Z_NO_FLUSH);
422 switch (ret) {
423 case Z_NEED_DICT:
424 ret = Z_DATA_ERROR;
425 // and fall through
426 case Z_DATA_ERROR:
427 case Z_MEM_ERROR:
428 inflateEnd(&inflateStrm);
429 initInflate = false;
430 return ret;
431 }
432 have = sizeof(out) - inflateStrm.avail_out;
433 inflated.append(QByteArray((const char *)out, have));
434 } while (inflateStrm.avail_out == 0);
435 // clean up and return
436 if (ret <= Z_ERRNO || ret == Z_STREAM_END) {
437 inflateEnd(&inflateStrm);
438 initInflate = false;
439 }
440 streamEnd = (ret == Z_STREAM_END);
441 return ret;
442}
443#endif
444
445qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
446{
447 if (fragment.isEmpty()) {
448 // reserve bytes for the status line. This is better than always append() which reallocs the byte array
449 fragment.reserve(32);
450 }
451
452 qint64 bytes = 0;
453 char c;
454 qint64 haveRead = 0;
455
456 do {
457 haveRead = socket->read(&c, 1);
458 if (haveRead == -1)
459 return -1; // unexpected EOF
460 else if (haveRead == 0)
461 break; // read more later
462
463 bytes++;
464
465 // allow both CRLF & LF (only) line endings
466 if (c == '\n') {
467 // remove the CR at the end
468 if (fragment.endsWith('\r')) {
469 fragment.truncate(fragment.length()-1);
470 }
471 bool ok = parseStatus(fragment);
472 state = ReadingHeaderState;
473 fragment.clear();
474 if (!ok) {
475 return -1;
476 }
477 break;
478 } else {
479 fragment.append(c);
480 }
481
482 // is this a valid reply?
483 if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
484 {
485 fragment.clear();
486 return -1;
487 }
488 } while (haveRead == 1);
489
490 return bytes;
491}
492
493bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
494{
495 // from RFC 2616:
496 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
497 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
498 // that makes: 'HTTP/n.n xxx Message'
499 // byte count: 0123456789012
500
501 static const int minLength = 11;
502 static const int dotPos = 6;
503 static const int spacePos = 8;
504 static const char httpMagic[] = "HTTP/";
505
506 if (status.length() < minLength
507 || !status.startsWith(httpMagic)
508 || status.at(dotPos) != '.'
509 || status.at(spacePos) != ' ') {
510 // I don't know how to parse this status line
511 return false;
512 }
513
514 // optimize for the valid case: defer checking until the end
515 majorVersion = status.at(dotPos - 1) - '0';
516 minorVersion = status.at(dotPos + 1) - '0';
517
518 int i = spacePos;
519 int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
520 const QByteArray code = status.mid(i + 1, j - i - 1);
521
522 bool ok;
523 statusCode = code.toInt(&ok);
524 reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
525
526 return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
527}
528
529qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
530{
531 if (fragment.isEmpty()) {
532 // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
533 // block is 381 bytes.
534 // reserve bytes. This is better than always append() which reallocs the byte array.
535 fragment.reserve(512);
536 }
537
538 qint64 bytes = 0;
539 char c = 0;
540 bool allHeaders = false;
541 qint64 haveRead = 0;
542 do {
543 haveRead = socket->read(&c, 1);
544 if (haveRead == 0) {
545 // read more later
546 break;
547 } else if (haveRead == -1) {
548 // connection broke down
549 return -1;
550 } else {
551 fragment.append(c);
552 bytes++;
553
554 if (c == '\n') {
555 // check for possible header endings. As per HTTP rfc,
556 // the header endings will be marked by CRLFCRLF. But
557 // we will allow CRLFCRLF, CRLFLF, LFLF
558 if (fragment.endsWith("\r\n\r\n")
559 || fragment.endsWith("\r\n\n")
560 || fragment.endsWith("\n\n"))
561 allHeaders = true;
562
563 // there is another case: We have no headers. Then the fragment equals just the line ending
564 if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
565 || (fragment.length() == 1 && fragment.endsWith("\n")))
566 allHeaders = true;
567 }
568 }
569 } while (!allHeaders && haveRead > 0);
570
571 // we received all headers now parse them
572 if (allHeaders) {
573 parseHeader(fragment);
574 state = ReadingDataState;
575 fragment.clear(); // next fragment
576 bodyLength = contentLength(); // cache the length
577
578 // cache isChunked() since it is called often
579 chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
580
581 // cache isConnectionCloseEnabled since it is called often
582 QByteArray connectionHeaderField = headerField("connection");
583 // check for explicit indication of close or the implicit connection close of HTTP/1.0
584 connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
585 headerField("proxy-connection").toLower().contains("close")) ||
586 (majorVersion == 1 && minorVersion == 0 && connectionHeaderField.isEmpty());
587 }
588 return bytes;
589}
590
591void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
592{
593 // see rfc2616, sec 4 for information about HTTP/1.1 headers.
594 // allows relaxed parsing here, accepts both CRLF & LF line endings
595 const QByteArrayMatcher lf("\n");
596 const QByteArrayMatcher colon(":");
597 int i = 0;
598 while (i < header.count()) {
599 int j = colon.indexIn(header, i); // field-name
600 if (j == -1)
601 break;
602 const QByteArray field = header.mid(i, j - i).trimmed();
603 j++;
604 // any number of LWS is allowed before and after the value
605 QByteArray value;
606 do {
607 i = lf.indexIn(header, j);
608 if (i == -1)
609 break;
610 if (!value.isEmpty())
611 value += ' ';
612 // check if we have CRLF or only LF
613 bool hasCR = (i && header[i-1] == '\r');
614 int length = i -(hasCR ? 1: 0) - j;
615 value += header.mid(j, length).trimmed();
616 j = ++i;
617 } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
618 if (i == -1)
619 break; // something is wrong
620
621 fields.append(qMakePair(field, value));
622 }
623}
624
625bool QHttpNetworkReplyPrivate::isChunked()
626{
627 return chunkedTransferEncoding;
628}
629
630bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
631{
632 return connectionCloseEnabled || forceConnectionCloseEnabled;
633}
634
635// note this function can only be used for non-chunked, non-compressed with
636// known content length
637qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
638{
639 qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
640 QByteArray bd;
641 bd.resize(toBeRead);
642 qint64 haveRead = socket->read(bd.data(), bd.size());
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
660qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
661{
662 qint64 bytes = 0;
663 if (isChunked()) {
664 bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
665 } else if (bodyLength > 0) { // we have a Content-Length
666 bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
667 if (contentRead + bytes == bodyLength)
668 state = AllDoneState;
669 } else {
670 bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
671 }
672 contentRead += bytes;
673 return bytes;
674}
675
676qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size)
677{
678 qint64 bytes = 0;
679 Q_ASSERT(in);
680 Q_ASSERT(out);
681
682 int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
683 while (toBeRead > 0) {
684 QByteArray byteData;
685 byteData.resize(toBeRead);
686 qint64 haveRead = in->read(byteData.data(), byteData.size());
687 if (haveRead <= 0) {
688 // ### error checking here
689 byteData.clear();
690 return bytes;
691 }
692
693 byteData.resize(haveRead);
694 out->append(byteData);
695 bytes += haveRead;
696 size -= haveRead;
697
698 toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
699 }
700 return bytes;
701
702}
703
704qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out)
705{
706 qint64 bytes = 0;
707 while (in->bytesAvailable()) { // while we can read from input
708 // if we are done with the current chunk, get the size of the new chunk
709 if (currentChunkRead >= currentChunkSize) {
710 currentChunkSize = 0;
711 currentChunkRead = 0;
712 if (bytes) {
713 char crlf[2];
714 bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
715 }
716 bytes += getChunkSize(in, &currentChunkSize);
717 if (currentChunkSize == -1)
718 break;
719 }
720 // if the chunk size is 0, end of the stream
721 if (currentChunkSize == 0) {
722 state = AllDoneState;
723 break;
724 }
725
726 // otherwise, try to read what is missing for this chunk
727 qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead);
728 currentChunkRead += haveRead;
729 bytes += haveRead;
730
731 // ### error checking here
732
733 }
734 return bytes;
735}
736
737qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
738{
739 qint64 bytes = 0;
740 char crlf[2];
741 *chunkSize = -1;
742 int bytesAvailable = in->bytesAvailable();
743 while (bytesAvailable > bytes) {
744 qint64 sniffedBytes = in->peek(crlf, 2);
745 int fragmentSize = fragment.size();
746 // check the next two bytes for a "\r\n", skip blank lines
747 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
748 ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
749 {
750 bytes += in->read(crlf, 1); // read the \r or \n
751 if (crlf[0] == '\r')
752 bytes += in->read(crlf, 1); // read the \n
753 bool ok = false;
754 // ignore the chunk-extension
755 fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
756 *chunkSize = fragment.toLong(&ok, 16);
757 fragment.clear();
758 break; // size done
759 } else {
760 // read the fragment to the buffer
761 char c = 0;
762 bytes += in->read(&c, 1);
763 fragment.append(c);
764 }
765 }
766 return bytes;
767}
768
769void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba)
770{
771 responseData.append(qba);
772
773 // clear the original! helps with implicit sharing and
774 // avoiding memcpy when the user is reading the data
775 qba.clear();
776}
777
778void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data)
779{
780 responseData.append(data);
781
782 // clear the original! helps with implicit sharing and
783 // avoiding memcpy when the user is reading the data
784 data.clear();
785}
786
787void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data)
788{
789 // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer
790 // instead of one QByteArray.
791 for(int i = 0; i < data.bufferCount(); i++) {
792 QByteArray &byteData = data[i];
793 compressedData.append(byteData.constData(), byteData.size());
794 }
795 data.clear();
796}
797
798
799bool QHttpNetworkReplyPrivate::shouldEmitSignals()
800{
801 // for 401 & 407 don't emit the data signals. Content along with these
802 // responses are send only if the authentication fails.
803 return (statusCode != 401 && statusCode != 407);
804}
805
806bool QHttpNetworkReplyPrivate::expectContent()
807{
808 // check whether we can expect content after the headers (rfc 2616, sec4.4)
809 if ((statusCode >= 100 && statusCode < 200)
810 || statusCode == 204 || statusCode == 304)
811 return false;
812 if (request.operation() == QHttpNetworkRequest::Head)
813 return !shouldEmitSignals();
814 if (contentLength() == 0)
815 return false;
816 return true;
817}
818
819void QHttpNetworkReplyPrivate::eraseData()
820{
821 compressedData.clear();
822 responseData.clear();
823}
824
825
826// SSL support below
827#ifndef QT_NO_OPENSSL
828
829QSslConfiguration QHttpNetworkReply::sslConfiguration() const
830{
831 Q_D(const QHttpNetworkReply);
832
833 if (!d->connectionChannel)
834 return QSslConfiguration();
835
836 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
837 if (!sslSocket)
838 return QSslConfiguration();
839
840 return sslSocket->sslConfiguration();
841}
842
843void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
844{
845 Q_D(QHttpNetworkReply);
846 if (d->connection)
847 d->connection->setSslConfiguration(config);
848}
849
850void QHttpNetworkReply::ignoreSslErrors()
851{
852 Q_D(QHttpNetworkReply);
853 if (d->connection)
854 d->connection->ignoreSslErrors();
855}
856
857void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
858{
859 Q_D(QHttpNetworkReply);
860 if (d->connection)
861 d->connection->ignoreSslErrors(errors);
862}
863
864
865#endif //QT_NO_OPENSSL
866
867
868QT_END_NAMESPACE
869
870#endif // QT_NO_HTTP
Note: See TracBrowser for help on using the repository browser.