Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qhttpnetworkreply.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:network-protocol
4
7
8#ifndef QT_NO_SSL
9# include <QtNetwork/qsslkey.h>
10# include <QtNetwork/qsslcipher.h>
11# include <QtNetwork/qsslconfiguration.h>
12#endif
13
14#include <private/qdecompresshelper_p.h>
15
17
18using namespace Qt::StringLiterals;
19
20QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
21 : QObject(*new QHttpNetworkReplyPrivate(url), parent)
22{
23}
24
25QHttpNetworkReply::~QHttpNetworkReply()
26{
27 Q_D(QHttpNetworkReply);
28 if (d->connection) {
29 d->connection->d_func()->removeReply(this);
30 }
31}
32
33QUrl QHttpNetworkReply::url() const
34{
35 return d_func()->url;
36}
37void QHttpNetworkReply::setUrl(const QUrl &url)
38{
39 Q_D(QHttpNetworkReply);
40 d->url = url;
41}
42
43QUrl QHttpNetworkReply::redirectUrl() const
44{
45 return d_func()->redirectUrl;
46}
47
48void QHttpNetworkReply::setRedirectUrl(const QUrl &url)
49{
50 Q_D(QHttpNetworkReply);
51 d->redirectUrl = url;
52}
53
54bool QHttpNetworkReply::isHttpRedirect(int statusCode)
55{
56 return (statusCode == 301 || statusCode == 302 || statusCode == 303
57 || statusCode == 305 || statusCode == 307 || statusCode == 308);
58}
59
60qint64 QHttpNetworkReply::contentLength() const
61{
62 return d_func()->contentLength();
63}
64
65void QHttpNetworkReply::setContentLength(qint64 length)
66{
67 Q_D(QHttpNetworkReply);
68 d->setContentLength(length);
69}
70
71QHttpHeaders QHttpNetworkReply::header() const
72{
73 return d_func()->parser.headers();
74}
75
76QByteArray QHttpNetworkReply::headerField(QByteArrayView name, const QByteArray &defaultValue) const
77{
78 return d_func()->headerField(name, defaultValue);
79}
80
81void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
82{
83 Q_D(QHttpNetworkReply);
84 d->setHeaderField(name, data);
85}
86
87void QHttpNetworkReply::appendHeaderField(const QByteArray &name, const QByteArray &data)
88{
89 Q_D(QHttpNetworkReply);
90 d->appendHeaderField(name, data);
91}
92
93void QHttpNetworkReply::parseHeader(QByteArrayView header)
94{
95 Q_D(QHttpNetworkReply);
96 d->parseHeader(header);
97}
98
99QHttpNetworkRequest QHttpNetworkReply::request() const
100{
101 return d_func()->request;
102}
103
104void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
105{
106 Q_D(QHttpNetworkReply);
107 d->request = request;
108 d->ssl = request.isSsl();
109}
110
111int QHttpNetworkReply::statusCode() const
112{
113 return d_func()->parser.getStatusCode();
114}
115
116void QHttpNetworkReply::setStatusCode(int code)
117{
118 Q_D(QHttpNetworkReply);
119 d->parser.setStatusCode(code);
120}
121
122QString QHttpNetworkReply::errorString() const
123{
124 return d_func()->errorString;
125}
126
127QNetworkReply::NetworkError QHttpNetworkReply::errorCode() const
128{
129 return d_func()->httpErrorCode;
130}
131
132QString QHttpNetworkReply::reasonPhrase() const
133{
134 return d_func()->parser.getReasonPhrase();
135}
136
137void QHttpNetworkReply::setReasonPhrase(const QString &reason)
138{
139 d_func()->parser.setReasonPhrase(reason);
140}
141
142void QHttpNetworkReply::setErrorString(const QString &error)
143{
144 Q_D(QHttpNetworkReply);
145 d->errorString = error;
146}
147
148int QHttpNetworkReply::majorVersion() const
149{
150 return d_func()->parser.getMajorVersion();
151}
152
153int QHttpNetworkReply::minorVersion() const
154{
155 return d_func()->parser.getMinorVersion();
156}
157
158void QHttpNetworkReply::setMajorVersion(int version)
159{
160 d_func()->parser.setMajorVersion(version);
161}
162
163void QHttpNetworkReply::setMinorVersion(int version)
164{
165 d_func()->parser.setMinorVersion(version);
166}
167
168qint64 QHttpNetworkReply::bytesAvailable() const
169{
170 Q_D(const QHttpNetworkReply);
171 if (d->connection)
172 return d->connection->d_func()->uncompressedBytesAvailable(*this);
173 else
174 return -1;
175}
176
177qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
178{
179 Q_D(const QHttpNetworkReply);
180 if (d->connection)
181 return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
182 else
183 return -1;
184}
185
186bool QHttpNetworkReply::readAnyAvailable() const
187{
188 Q_D(const QHttpNetworkReply);
189 return (d->responseData.bufferCount() > 0);
190}
191
192QByteArray QHttpNetworkReply::readAny()
193{
194 Q_D(QHttpNetworkReply);
195 if (d->responseData.bufferCount() == 0)
196 return QByteArray();
197
198 // we'll take the last buffer, so schedule another read from http
199 if (d->downstreamLimited && d->responseData.bufferCount() == 1 && !isFinished())
200 d->connection->d_func()->readMoreLater(this);
201 return d->responseData.read();
202}
203
204QByteArray QHttpNetworkReply::readAll()
205{
206 Q_D(QHttpNetworkReply);
207 return d->responseData.readAll();
208}
209
210QByteArray QHttpNetworkReply::read(qint64 amount)
211{
212 Q_D(QHttpNetworkReply);
213 return d->responseData.read(amount);
214}
215
216
217qint64 QHttpNetworkReply::sizeNextBlock()
218{
219 Q_D(QHttpNetworkReply);
220 return d->responseData.sizeNextBlock();
221}
222
223void QHttpNetworkReply::setDownstreamLimited(bool dsl)
224{
225 Q_D(QHttpNetworkReply);
226 d->downstreamLimited = dsl;
227 d->connection->d_func()->readMoreLater(this);
228}
229
230void QHttpNetworkReply::setReadBufferSize(qint64 size)
231{
232 Q_D(QHttpNetworkReply);
233 d->readBufferMaxSize = size;
234}
235
236bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
237{
238 Q_D(QHttpNetworkReply);
239 return !d->isChunked() && !d->autoDecompress &&
240 d->bodyLength > 0 && d->parser.getStatusCode() == 200;
241}
242
243void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
244{
245 Q_D(QHttpNetworkReply);
246 if (supportsUserProvidedDownloadBuffer())
247 d->userProvidedDownloadBuffer = b;
248}
249
250char* QHttpNetworkReply::userProvidedDownloadBuffer()
251{
252 Q_D(QHttpNetworkReply);
253 return d->userProvidedDownloadBuffer;
254}
255
256void QHttpNetworkReply::abort()
257{
258 Q_D(QHttpNetworkReply);
259 d->state = QHttpNetworkReplyPrivate::Aborted;
260}
261
262bool QHttpNetworkReply::isAborted() const
263{
264 return d_func()->state == QHttpNetworkReplyPrivate::Aborted;
265}
266
267bool QHttpNetworkReply::isFinished() const
268{
269 return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
270}
271
272bool QHttpNetworkReply::isPipeliningUsed() const
273{
274 return d_func()->pipeliningUsed;
275}
276
277bool QHttpNetworkReply::isHttp2Used() const
278{
279 return d_func()->h2Used;
280}
281
282void QHttpNetworkReply::setHttp2WasUsed(bool h2)
283{
284 d_func()->h2Used = h2;
285}
286
287qint64 QHttpNetworkReply::removedContentLength() const
288{
289 return d_func()->removedContentLength;
290}
291
292bool QHttpNetworkReply::isRedirecting() const
293{
294 return d_func()->isRedirecting();
295}
296
297QHttpNetworkConnection* QHttpNetworkReply::connection()
298{
299 return d_func()->connection;
300}
301
302
303QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
304 : QHttpNetworkHeaderPrivate(newUrl)
305 , state(NothingDoneState)
306 , ssl(false),
307 bodyLength(0), contentRead(0), totalProgress(0),
308 chunkedTransferEncoding(false),
309 connectionCloseEnabled(true),
310 forceConnectionCloseEnabled(false),
311 lastChunkRead(false),
312 currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0),
313 totallyUploadedData(0),
314 removedContentLength(-1),
315 connection(nullptr),
316 autoDecompress(false), responseData(), requestIsPrepared(false)
317 ,pipeliningUsed(false), h2Used(false), downstreamLimited(false)
318 ,userProvidedDownloadBuffer(nullptr)
319
320{
321 QString scheme = newUrl.scheme();
322 if (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1)
323 // make sure we do not close the socket after preconnecting
324 connectionCloseEnabled = false;
325}
326
327QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate() = default;
328
329void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
330{
331 state = NothingDoneState;
332 bodyLength = 0;
333 contentRead = 0;
334 totalProgress = 0;
335 currentChunkSize = 0;
336 currentChunkRead = 0;
337 lastChunkRead = false;
338 connectionCloseEnabled = true;
339 parser.clear();
340}
341
342// TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
343void QHttpNetworkReplyPrivate::clear()
344{
345 connection = nullptr;
346 connectionChannel = nullptr;
347 autoDecompress = false;
348 clearHttpLayerInformation();
349}
350
351// QHttpNetworkReplyPrivate
352qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
353{
354 return (state != ReadingDataState ? 0 : fragment.size());
355}
356
357bool QHttpNetworkReplyPrivate::isCompressed() const
358{
359 return QDecompressHelper::isSupportedEncoding(headerField("content-encoding"));
360}
361
362bool QHttpNetworkReply::isCompressed() const
363{
364 Q_D(const QHttpNetworkReply);
365 return d->isCompressed();
366}
367
368void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
369{
370 // The header "Content-Encoding = gzip" is retained.
371 // Content-Length is removed since the actual one sent by the server is for compressed data
372 constexpr auto name = QByteArrayView("content-length");
373 QByteArray contentLength = parser.firstHeaderField(name);
374 bool parseOk = false;
375 qint64 value = contentLength.toLongLong(&parseOk);
376 if (parseOk) {
377 removedContentLength = value;
378 parser.removeHeaderField(name);
379 }
380}
381
382qint64 QHttpNetworkReplyPrivate::readStatus(QIODevice *socket)
383{
384 if (fragment.isEmpty()) {
385 // reserve bytes for the status line. This is better than always append() which reallocs the byte array
386 fragment.reserve(32);
387 }
388
389 qint64 bytes = 0;
390 char c;
391 qint64 haveRead = 0;
392
393 do {
394 haveRead = socket->read(&c, 1);
395 if (haveRead == -1)
396 return -1; // unexpected EOF
397 else if (haveRead == 0)
398 break; // read more later
399 else if (haveRead == 1 && fragment.size() == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
400 continue; // Ignore all whitespace that was trailing froma previous request on that socket
401
402 bytes++;
403
404 // allow both CRLF & LF (only) line endings
405 if (c == '\n') {
406 // remove the CR at the end
407 if (fragment.endsWith('\r')) {
408 fragment.truncate(fragment.size()-1);
409 }
410 bool ok = parseStatus(fragment);
411 state = ReadingHeaderState;
412 fragment.clear();
413 if (!ok) {
414 return -1;
415 }
416 break;
417 } else {
418 fragment.append(c);
419 }
420
421 // is this a valid reply?
422 if (fragment.size() == 5 && !fragment.startsWith("HTTP/")) {
423 fragment.clear();
424 return -1;
425 }
426
427 // parseStatus() rejects reason phrases longer than MAX_REASON_PHRASE_SIZE anyway,
428 // so anything beyond that plus a generous allowance for the rest of the line
429 // can never be valid.
430 if (fragment.size() > HeaderConstants::MAX_REASON_PHRASE_SIZE + 64) {
431 fragment.clear();
432 return -1;
433 }
434 } while (haveRead == 1);
435
436 return bytes;
437}
438
439bool QHttpNetworkReplyPrivate::parseStatus(QByteArrayView status)
440{
441 return parser.parseStatus(status);
442}
443
444qint64 QHttpNetworkReplyPrivate::readHeader(QIODevice *socket)
445{
446 if (fragment.isEmpty()) {
447 // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
448 // block is 381 bytes.
449 // reserve bytes. This is better than always append() which reallocs the byte array.
450 fragment.reserve(512);
451 }
452
453 qint64 bytes = 0;
454 char c = 0;
455 bool allHeaders = false;
456 qint64 haveRead = 0;
457 do {
458 haveRead = socket->read(&c, 1);
459 if (haveRead == 0) {
460 // read more later
461 break;
462 } else if (haveRead == -1) {
463 // connection broke down
464 return -1;
465 } else {
466 fragment.append(c);
467 bytes++;
468
469 // Enforce the total header size limit while accumulating.
470 if (fragment.size() > parser.maxRawHeaderSize()) {
471 fragment.clear();
472 return -1;
473 }
474
475 if (c == '\n') {
476 // check for possible header endings. As per HTTP rfc,
477 // the header endings will be marked by CRLFCRLF. But
478 // we will allow CRLFCRLF, CRLFLF, LFCRLF, LFLF
479 if (fragment.endsWith("\n\r\n")
480 || fragment.endsWith("\n\n"))
481 allHeaders = true;
482
483 // there is another case: We have no headers. Then the fragment equals just the line ending
484 if ((fragment.size() == 2 && fragment.endsWith("\r\n"))
485 || (fragment.size() == 1 && fragment.endsWith("\n")))
486 allHeaders = true;
487 }
488 }
489 } while (!allHeaders && haveRead > 0);
490
491 // we received all headers now parse them
492 if (allHeaders) {
493 parseHeader(fragment);
494 state = ReadingDataState;
495 fragment.clear(); // next fragment
496 bodyLength = contentLength(); // cache the length
497
498 // cache isChunked() since it is called often
499 chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
500
501 // cache isConnectionCloseEnabled since it is called often
502 QByteArray connectionHeaderField = headerField("connection");
503 // check for explicit indication of close or the implicit connection close of HTTP/1.0
504 connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
505 headerField("proxy-connection").toLower().contains("close")) ||
506 (parser.getMajorVersion() == 1 && parser.getMinorVersion() == 0 &&
507 (connectionHeaderField.isEmpty() && !headerField("proxy-connection").toLower().contains("keep-alive")));
508 }
509 return bytes;
510}
511
512void QHttpNetworkReplyPrivate::parseHeader(QByteArrayView header)
513{
514 parser.parseHeaders(header);
515}
516
517void QHttpNetworkReplyPrivate::appendHeaderField(const QByteArray &name, const QByteArray &data)
518{
519 parser.appendHeaderField(name, data);
520}
521
522bool QHttpNetworkReplyPrivate::isChunked()
523{
524 return chunkedTransferEncoding;
525}
526
527bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
528{
529 return connectionCloseEnabled || forceConnectionCloseEnabled;
530}
531
532// note this function can only be used for non-chunked, non-compressed with
533// known content length
534qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QIODevice *socket, char *b)
535{
536 // This first read is to flush the buffer inside the socket
537 qint64 haveRead = 0;
538 haveRead = socket->read(b, bodyLength - contentRead);
539 if (haveRead == -1) {
540 return -1;
541 }
542 contentRead += haveRead;
543
544 if (contentRead == bodyLength) {
545 state = AllDoneState;
546 }
547
548 return haveRead;
549}
550
551// note this function can only be used for non-chunked, non-compressed with
552// known content length
553qint64 QHttpNetworkReplyPrivate::readBodyFast(QIODevice *socket, QByteDataBuffer *rb)
554{
555
556 qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
557 if (readBufferMaxSize)
558 toBeRead = qMin(toBeRead, readBufferMaxSize);
559
560 if (!toBeRead)
561 return 0;
562
563 QByteArray bd;
564 bd.resize(toBeRead);
565 qint64 haveRead = socket->read(bd.data(), toBeRead);
566 if (haveRead == -1) {
567 bd.clear();
568 return 0; // ### error checking here;
569 }
570 bd.resize(haveRead);
571
572 rb->append(bd);
573
574 if (contentRead + haveRead == bodyLength) {
575 state = AllDoneState;
576 }
577
578 contentRead += haveRead;
579 return haveRead;
580}
581
582
583qint64 QHttpNetworkReplyPrivate::readBody(QIODevice *socket, QByteDataBuffer *out)
584{
585 qint64 bytes = 0;
586
587 if (isChunked()) {
588 // chunked transfer encoding (rfc 2616, sec 3.6)
589 bytes += readReplyBodyChunked(socket, out);
590 } else if (bodyLength > 0) {
591 // we have a Content-Length
592 bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
593 if (contentRead + bytes == bodyLength)
594 state = AllDoneState;
595 } else {
596 // no content length. just read what's possible
597 bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
598 }
599 contentRead += bytes;
600 return bytes;
601}
602
603qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *socket, QByteDataBuffer *out, qint64 size)
604{
605 // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
606 qint64 bytes = 0;
607 Q_ASSERT(socket);
608 Q_ASSERT(out);
609
610 int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
611
612 if (readBufferMaxSize)
613 toBeRead = qMin<qint64>(toBeRead, readBufferMaxSize);
614
615 while (toBeRead > 0) {
616 QByteArray byteData;
617 byteData.resize(toBeRead);
618 qint64 haveRead = socket->read(byteData.data(), byteData.size());
619 if (haveRead <= 0) {
620 // ### error checking here
621 byteData.clear();
622 return bytes;
623 }
624
625 byteData.resize(haveRead);
626 out->append(byteData);
627 bytes += haveRead;
628 size -= haveRead;
629
630 toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
631 }
632 return bytes;
633
634}
635
636qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *socket, QByteDataBuffer *out)
637{
638 qint64 bytes = 0;
639 while (socket->bytesAvailable()) {
640
641 if (readBufferMaxSize && (bytes > readBufferMaxSize))
642 break;
643
644 if (!lastChunkRead && currentChunkRead >= currentChunkSize) {
645 // For the first chunk and when we're done with a chunk
646 currentChunkSize = 0;
647 currentChunkRead = 0;
648 if (bytes) {
649 // After a chunk
650 char crlf[2];
651 // read the "\r\n" after the chunk
652 qint64 haveRead = socket->read(crlf, 2);
653 // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
654 // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
655 // it right now still works, but we should definitely fix this.
656
657 if (haveRead != 2)
658 return bytes; // FIXME
659 bytes += haveRead;
660 }
661 // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
662 bytes += getChunkSize(socket, &currentChunkSize);
663 if (currentChunkSize == -1)
664 break;
665 }
666 // if the chunk size is 0, end of the stream
667 if (currentChunkSize == 0 || lastChunkRead) {
668 lastChunkRead = true;
669 // try to read the "\r\n" after the chunk
670 char crlf[2];
671 qint64 haveRead = socket->read(crlf, 2);
672 if (haveRead > 0)
673 bytes += haveRead;
674
675 if ((haveRead == 2 && crlf[0] == '\r' && crlf[1] == '\n') || (haveRead == 1 && crlf[0] == '\n'))
676 state = AllDoneState;
677 else if (haveRead == 1 && crlf[0] == '\r')
678 break; // Still waiting for the last \n
679 else if (haveRead > 0) {
680 // If we read something else then CRLF, we need to close the channel.
681 forceConnectionCloseEnabled = true;
682 state = AllDoneState;
683 }
684 break;
685 }
686
687 // otherwise, try to begin reading this chunk / to read what is missing for this chunk
688 qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead);
689 currentChunkRead += haveRead;
690 bytes += haveRead;
691
692 // ### error checking here
693
694 }
695 return bytes;
696}
697
698qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *socket, qint64 *chunkSize)
699{
700 qint64 bytes = 0;
701 char crlf[2];
702 *chunkSize = -1;
703
704 int bytesAvailable = socket->bytesAvailable();
705 // FIXME rewrite to permanent loop without bytesAvailable
706 while (bytesAvailable > bytes) {
707 qint64 sniffedBytes = socket->peek(crlf, 2);
708 int fragmentSize = fragment.size();
709
710 // check the next two bytes for a "\r\n", skip blank lines
711 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
712 ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
713 {
714 bytes += socket->read(crlf, 1); // read the \r or \n
715 if (crlf[0] == '\r')
716 bytes += socket->read(crlf, 1); // read the \n
717 bool ok = false;
718 // ignore the chunk-extension
719 const auto fragmentView = QByteArrayView(fragment).mid(0, fragment.indexOf(';')).trimmed();
720 *chunkSize = fragmentView.toLong(&ok, 16);
721 fragment.clear();
722 break; // size done
723 } else {
724 // read the fragment to the buffer
725 char c = 0;
726 qint64 haveRead = socket->read(&c, 1);
727 if (haveRead < 0) {
728 return -1; // FIXME
729 }
730 bytes += haveRead;
731 fragment.append(c);
732 }
733 }
734
735 return bytes;
736}
737
738bool QHttpNetworkReplyPrivate::isRedirecting() const
739{
740 // We're in the process of redirecting - if the HTTP status code says so and
741 // followRedirect is switched on
742 return (QHttpNetworkReply::isHttpRedirect(parser.getStatusCode())
743 && request.isFollowRedirects());
744}
745
746bool QHttpNetworkReplyPrivate::shouldEmitSignals()
747{
748 // for 401 & 407 don't emit the data signals. Content along with these
749 // responses are sent only if the authentication fails.
750 return parser.getStatusCode() != 401 && parser.getStatusCode() != 407;
751}
752
753bool QHttpNetworkReplyPrivate::expectContent()
754{
755 int statusCode = parser.getStatusCode();
756 // check whether we can expect content after the headers (rfc 2616, sec4.4)
757 if ((statusCode >= 100 && statusCode < 200)
758 || statusCode == 204 || statusCode == 304)
759 return false;
760 if (request.operation() == QHttpNetworkRequest::Head)
761 return false; // no body expected for HEAD request
762 qint64 expectedContentLength = contentLength();
763 if (expectedContentLength == 0)
764 return false;
765 if (expectedContentLength == -1 && bodyLength == 0) {
766 // The content-length header was stripped, but its value was 0.
767 // This would be the case for an explicitly zero-length compressed response.
768 return false;
769 }
770 return true;
771}
772
773void QHttpNetworkReplyPrivate::eraseData()
774{
775 responseData.clear();
776}
777
778
779// SSL support below
780#ifndef QT_NO_SSL
781
782QSslConfiguration QHttpNetworkReply::sslConfiguration() const
783{
784 Q_D(const QHttpNetworkReply);
785
786 if (!d->connectionChannel)
787 return QSslConfiguration();
788
789 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
790 if (!sslSocket)
791 return QSslConfiguration();
792
793 return sslSocket->sslConfiguration();
794}
795
796void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
797{
798 Q_D(QHttpNetworkReply);
799 if (d->connection)
800 d->connection->setSslConfiguration(config);
801}
802
803void QHttpNetworkReply::ignoreSslErrors()
804{
805 Q_D(QHttpNetworkReply);
806 if (d->connection)
807 d->connection->ignoreSslErrors();
808}
809
810void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
811{
812 Q_D(QHttpNetworkReply);
813 if (d->connection)
814 d->connection->ignoreSslErrors(errors);
815}
816
817
818#endif //QT_NO_SSL
819
820
821QT_END_NAMESPACE
822
823#include "moc_qhttpnetworkreply_p.cpp"
Combined button and popup list for selecting options.