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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/network/access/qhttpnetworkreply.cpp