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

Last change on this file since 632 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 23.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 return d->responseData.read();
183}
184
185bool QHttpNetworkReply::isFinished() const
186{
187 return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
188}
189
190bool QHttpNetworkReply::isPipeliningUsed() const
191{
192 return d_func()->pipeliningUsed;
193}
194
195
196QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
197 : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
198 majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
199 chunkedTransferEncoding(false),
200 connectionCloseEnabled(true),
201 forceConnectionCloseEnabled(false),
202 currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
203 autoDecompress(false), responseData(), requestIsPrepared(false)
204 ,pipeliningUsed(false)
205{
206}
207
208QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
209{
210}
211
212void QHttpNetworkReplyPrivate::clear()
213{
214 state = NothingDoneState;
215 statusCode = 100;
216 bodyLength = 0;
217 contentRead = 0;
218 totalProgress = 0;
219 currentChunkSize = 0;
220 currentChunkRead = 0;
221 connectionCloseEnabled = true;
222 connection = 0;
223#ifndef QT_NO_COMPRESS
224 if (initInflate)
225 inflateEnd(&inflateStrm);
226#endif
227 initInflate = false;
228 streamEnd = false;
229 autoDecompress = false;
230 fields.clear();
231}
232
233// QHttpNetworkReplyPrivate
234qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
235{
236 return (state != ReadingDataState ? 0 : fragment.size());
237}
238
239bool QHttpNetworkReplyPrivate::isGzipped()
240{
241 QByteArray encoding = headerField("content-encoding");
242 return qstricmp(encoding.constData(), "gzip") == 0;
243}
244
245void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
246{
247 // The header "Content-Encoding = gzip" is retained.
248 // Content-Length is removed since the actual one send by the server is for compressed data
249 QByteArray name("content-length");
250 QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
251 end = fields.end();
252 while (it != end) {
253 if (qstricmp(name.constData(), it->first.constData()) == 0) {
254 fields.erase(it);
255 break;
256 }
257 ++it;
258 }
259
260}
261
262bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
263{
264 challenge.clear();
265 // find out the type of authentication protocol requested.
266 QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
267 // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
268 QList<QByteArray> challenges = headerFieldValues(header);
269 for (int i = 0; i<challenges.size(); i++) {
270 QByteArray line = challenges.at(i);
271 // todo use qstrincmp
272 if (!line.toLower().startsWith("negotiate"))
273 challenge = line;
274 }
275 return !challenge.isEmpty();
276}
277
278QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
279{
280 // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
281 QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
282 QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
283 QList<QByteArray> challenges = headerFieldValues(header);
284 for (int i = 0; i<challenges.size(); i++) {
285 QByteArray line = challenges.at(i).trimmed().toLower();
286 if (method < QAuthenticatorPrivate::Basic
287 && line.startsWith("basic")) {
288 method = QAuthenticatorPrivate::Basic;
289 } else if (method < QAuthenticatorPrivate::Ntlm
290 && line.startsWith("ntlm")) {
291 method = QAuthenticatorPrivate::Ntlm;
292 } else if (method < QAuthenticatorPrivate::DigestMd5
293 && line.startsWith("digest")) {
294 method = QAuthenticatorPrivate::DigestMd5;
295 }
296 }
297 return method;
298}
299
300#ifndef QT_NO_COMPRESS
301bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos)
302{
303 int method = 0; // method byte
304 int flags = 0; // flags byte
305 bool ret = false;
306
307 // Assure two bytes in the buffer so we can peek ahead -- handle case
308 // where first byte of header is at the end of the buffer after the last
309 // gzip segment
310 pos = -1;
311 QByteArray &body = content;
312 int maxPos = body.size()-1;
313 if (maxPos < 1) {
314 return ret;
315 }
316
317 // Peek ahead to check the gzip magic header
318 if (body[0] != char(gz_magic[0]) ||
319 body[1] != char(gz_magic[1])) {
320 return ret;
321 }
322 pos += 2;
323 // Check the rest of the gzip header
324 if (++pos <= maxPos)
325 method = body[pos];
326 if (pos++ <= maxPos)
327 flags = body[pos];
328 if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
329 return ret;
330 }
331
332 // Discard time, xflags and OS code:
333 pos += 6;
334 if (pos > maxPos)
335 return ret;
336 if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field
337 unsigned len = (unsigned)body[++pos];
338 len += ((unsigned)body[++pos])<<8;
339 pos += len;
340 if (pos > maxPos)
341 return ret;
342 }
343 if ((flags & ORIG_NAME) != 0) { // skip the original file name
344 while(++pos <= maxPos && body[pos]) {}
345 }
346 if ((flags & COMMENT) != 0) { // skip the .gz file comment
347 while(++pos <= maxPos && body[pos]) {}
348 }
349 if ((flags & HEAD_CRC) != 0) { // skip the header crc
350 pos += 2;
351 if (pos > maxPos)
352 return ret;
353 }
354 ret = (pos < maxPos); // return failed, if no more bytes left
355 return ret;
356}
357
358int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated)
359{
360 int ret = Z_DATA_ERROR;
361 unsigned have;
362 unsigned char out[CHUNK];
363 int pos = -1;
364
365 if (!initInflate) {
366 // check the header
367 if (!gzipCheckHeader(compressed, pos))
368 return ret;
369 // allocate inflate state
370 inflateStrm.zalloc = Z_NULL;
371 inflateStrm.zfree = Z_NULL;
372 inflateStrm.opaque = Z_NULL;
373 inflateStrm.avail_in = 0;
374 inflateStrm.next_in = Z_NULL;
375 ret = inflateInit2(&inflateStrm, -MAX_WBITS);
376 if (ret != Z_OK)
377 return ret;
378 initInflate = true;
379 streamEnd = false;
380 }
381
382 //remove the header.
383 compressed.remove(0, pos+1);
384 // expand until deflate stream ends
385 inflateStrm.next_in = (unsigned char *)compressed.data();
386 inflateStrm.avail_in = compressed.size();
387 do {
388 inflateStrm.avail_out = sizeof(out);
389 inflateStrm.next_out = out;
390 ret = inflate(&inflateStrm, Z_NO_FLUSH);
391 switch (ret) {
392 case Z_NEED_DICT:
393 ret = Z_DATA_ERROR;
394 // and fall through
395 case Z_DATA_ERROR:
396 case Z_MEM_ERROR:
397 inflateEnd(&inflateStrm);
398 initInflate = false;
399 return ret;
400 }
401 have = sizeof(out) - inflateStrm.avail_out;
402 inflated.append(QByteArray((const char *)out, have));
403 } while (inflateStrm.avail_out == 0);
404 // clean up and return
405 if (ret <= Z_ERRNO || ret == Z_STREAM_END) {
406 inflateEnd(&inflateStrm);
407 initInflate = false;
408 }
409 streamEnd = (ret == Z_STREAM_END);
410 return ret;
411}
412#endif
413
414qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
415{
416 qint64 bytes = 0;
417 char c;
418
419 while (socket->bytesAvailable()) {
420 // allow both CRLF & LF (only) line endings
421 if (socket->peek(&c, 1) == 1 && c == '\n') {
422 bytes += socket->read(&c, 1); // read the "n"
423 // remove the CR at the end
424 if (fragment.endsWith('\r')) {
425 fragment.truncate(fragment.length()-1);
426 }
427 bool ok = parseStatus(fragment);
428 state = ReadingHeaderState;
429 fragment.clear();
430 if (!ok) {
431 return -1;
432 }
433 break;
434 } else {
435 c = 0;
436 int haveRead = socket->read(&c, 1);
437 if (haveRead == -1)
438 return -1;
439 bytes += haveRead;
440 fragment.append(c);
441 }
442
443 // is this a valid reply?
444 if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
445 {
446 fragment.clear();
447 return -1;
448 }
449
450 }
451
452 return bytes;
453}
454
455bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
456{
457 // from RFC 2616:
458 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
459 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
460 // that makes: 'HTTP/n.n xxx Message'
461 // byte count: 0123456789012
462
463 static const int minLength = 11;
464 static const int dotPos = 6;
465 static const int spacePos = 8;
466 static const char httpMagic[] = "HTTP/";
467
468 if (status.length() < minLength
469 || !status.startsWith(httpMagic)
470 || status.at(dotPos) != '.'
471 || status.at(spacePos) != ' ') {
472 // I don't know how to parse this status line
473 return false;
474 }
475
476 // optimize for the valid case: defer checking until the end
477 majorVersion = status.at(dotPos - 1) - '0';
478 minorVersion = status.at(dotPos + 1) - '0';
479
480 int i = spacePos;
481 int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
482 const QByteArray code = status.mid(i + 1, j - i - 1);
483
484 bool ok;
485 statusCode = code.toInt(&ok);
486 reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
487
488 return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
489}
490
491qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
492{
493 qint64 bytes = 0;
494 char c = 0;
495 bool allHeaders = false;
496 while (!allHeaders && socket->bytesAvailable()) {
497 if (socket->peek(&c, 1) == 1 && c == '\n') {
498 // check for possible header endings. As per HTTP rfc,
499 // the header endings will be marked by CRLFCRLF. But
500 // we will allow CRLFLF, LFLF & CRLFCRLF
501 if (fragment.endsWith("\n\r") || fragment.endsWith('\n'))
502 allHeaders = true;
503 }
504 bytes += socket->read(&c, 1);
505 fragment.append(c);
506 }
507 // we received all headers now parse them
508 if (allHeaders) {
509 parseHeader(fragment);
510 state = ReadingDataState;
511 fragment.clear(); // next fragment
512 bodyLength = contentLength(); // cache the length
513
514 // cache isChunked() since it is called often
515 chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
516
517 // cache isConnectionCloseEnabled since it is called often
518 QByteArray connectionHeaderField = headerField("connection");
519 // check for explicit indication of close or the implicit connection close of HTTP/1.0
520 connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
521 headerField("proxy-connection").toLower().contains("close")) ||
522 (majorVersion == 1 && minorVersion == 0 && connectionHeaderField.isEmpty());
523 }
524 return bytes;
525}
526
527void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
528{
529 // see rfc2616, sec 4 for information about HTTP/1.1 headers.
530 // allows relaxed parsing here, accepts both CRLF & LF line endings
531 const QByteArrayMatcher lf("\n");
532 const QByteArrayMatcher colon(":");
533 int i = 0;
534 while (i < header.count()) {
535 int j = colon.indexIn(header, i); // field-name
536 if (j == -1)
537 break;
538 const QByteArray field = header.mid(i, j - i).trimmed();
539 j++;
540 // any number of LWS is allowed before and after the value
541 QByteArray value;
542 do {
543 i = lf.indexIn(header, j);
544 if (i == -1)
545 break;
546 if (!value.isEmpty())
547 value += ' ';
548 // check if we have CRLF or only LF
549 bool hasCR = (i && header[i-1] == '\r');
550 int length = i -(hasCR ? 1: 0) - j;
551 value += header.mid(j, length).trimmed();
552 j = ++i;
553 } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
554 if (i == -1)
555 break; // something is wrong
556
557 fields.append(qMakePair(field, value));
558 }
559}
560
561bool QHttpNetworkReplyPrivate::isChunked()
562{
563 return chunkedTransferEncoding;
564}
565
566bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
567{
568 return connectionCloseEnabled || forceConnectionCloseEnabled;
569}
570
571// note this function can only be used for non-chunked, non-compressed with
572// known content length
573qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
574{
575 qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
576 QByteArray bd;
577 bd.resize(toBeRead);
578 qint64 haveRead = socket->read(bd.data(), bd.size());
579 if (haveRead == -1) {
580 bd.clear();
581 return 0; // ### error checking here;
582 }
583 bd.resize(haveRead);
584
585 rb->append(bd);
586
587 if (contentRead + haveRead == bodyLength) {
588 state = AllDoneState;
589 }
590
591 contentRead += haveRead;
592 return haveRead;
593}
594
595
596qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
597{
598 qint64 bytes = 0;
599 if (isChunked()) {
600 bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
601 } else if (bodyLength > 0) { // we have a Content-Length
602 bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
603 if (contentRead + bytes == bodyLength)
604 state = AllDoneState;
605 } else {
606 bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
607 }
608 contentRead += bytes;
609 return bytes;
610}
611
612qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size)
613{
614 qint64 bytes = 0;
615 Q_ASSERT(in);
616 Q_ASSERT(out);
617
618 int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
619 while (toBeRead > 0) {
620 QByteArray byteData;
621 byteData.resize(toBeRead);
622 qint64 haveRead = in->read(byteData.data(), byteData.size());
623 if (haveRead <= 0) {
624 // ### error checking here
625 byteData.clear();
626 return bytes;
627 }
628
629 byteData.resize(haveRead);
630 out->append(byteData);
631 bytes += haveRead;
632 size -= haveRead;
633
634 toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
635 }
636 return bytes;
637
638}
639
640qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out)
641{
642 qint64 bytes = 0;
643 while (in->bytesAvailable()) { // while we can read from input
644 // if we are done with the current chunk, get the size of the new chunk
645 if (currentChunkRead >= currentChunkSize) {
646 currentChunkSize = 0;
647 currentChunkRead = 0;
648 if (bytes) {
649 char crlf[2];
650 bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
651 }
652 bytes += getChunkSize(in, &currentChunkSize);
653 if (currentChunkSize == -1)
654 break;
655 }
656 // if the chunk size is 0, end of the stream
657 if (currentChunkSize == 0) {
658 state = AllDoneState;
659 break;
660 }
661
662 // otherwise, try to read what is missing for this chunk
663 qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead);
664 currentChunkRead += haveRead;
665 bytes += haveRead;
666
667 // ### error checking here
668
669 }
670 return bytes;
671}
672
673qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
674{
675 qint64 bytes = 0;
676 char crlf[2];
677 *chunkSize = -1;
678 int bytesAvailable = in->bytesAvailable();
679 while (bytesAvailable > bytes) {
680 qint64 sniffedBytes = in->peek(crlf, 2);
681 int fragmentSize = fragment.size();
682 // check the next two bytes for a "\r\n", skip blank lines
683 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
684 ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
685 {
686 bytes += in->read(crlf, 1); // read the \r or \n
687 if (crlf[0] == '\r')
688 bytes += in->read(crlf, 1); // read the \n
689 bool ok = false;
690 // ignore the chunk-extension
691 fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
692 *chunkSize = fragment.toLong(&ok, 16);
693 fragment.clear();
694 break; // size done
695 } else {
696 // read the fragment to the buffer
697 char c = 0;
698 bytes += in->read(&c, 1);
699 fragment.append(c);
700 }
701 }
702 return bytes;
703}
704
705void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba)
706{
707 responseData.append(qba);
708
709 // clear the original! helps with implicit sharing and
710 // avoiding memcpy when the user is reading the data
711 qba.clear();
712}
713
714void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data)
715{
716 responseData.append(data);
717
718 // clear the original! helps with implicit sharing and
719 // avoiding memcpy when the user is reading the data
720 data.clear();
721}
722
723void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data)
724{
725 // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer
726 // instead of one QByteArray.
727 for(int i = 0; i < data.bufferCount(); i++) {
728 QByteArray &byteData = data[i];
729 compressedData.append(byteData.constData(), byteData.size());
730 }
731 data.clear();
732}
733
734
735bool QHttpNetworkReplyPrivate::shouldEmitSignals()
736{
737 // for 401 & 407 don't emit the data signals. Content along with these
738 // responses are send only if the authentication fails.
739 return (statusCode != 401 && statusCode != 407);
740}
741
742bool QHttpNetworkReplyPrivate::expectContent()
743{
744 // check whether we can expect content after the headers (rfc 2616, sec4.4)
745 if ((statusCode >= 100 && statusCode < 200)
746 || statusCode == 204 || statusCode == 304)
747 return false;
748 if (request.operation() == QHttpNetworkRequest::Head)
749 return !shouldEmitSignals();
750 if (contentLength() == 0)
751 return false;
752 return true;
753}
754
755void QHttpNetworkReplyPrivate::eraseData()
756{
757 compressedData.clear();
758 responseData.clear();
759}
760
761
762// SSL support below
763#ifndef QT_NO_OPENSSL
764
765QSslConfiguration QHttpNetworkReply::sslConfiguration() const
766{
767 Q_D(const QHttpNetworkReply);
768 if (d->connection)
769 return d->connection->d_func()->sslConfiguration(*this);
770 return QSslConfiguration();
771}
772
773void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
774{
775 Q_D(QHttpNetworkReply);
776 if (d->connection)
777 d->connection->setSslConfiguration(config);
778}
779
780void QHttpNetworkReply::ignoreSslErrors()
781{
782 Q_D(QHttpNetworkReply);
783 if (d->connection)
784 d->connection->ignoreSslErrors();
785}
786
787void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
788{
789 Q_D(QHttpNetworkReply);
790 if (d->connection)
791 d->connection->ignoreSslErrors(errors);
792}
793
794
795#endif //QT_NO_OPENSSL
796
797
798QT_END_NAMESPACE
799
800#endif // QT_NO_HTTP
Note: See TracBrowser for help on using the repository browser.