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
70QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
71{
72 return d_func()->parser.headers();
73}
74
75QByteArray QHttpNetworkReply::headerField(const QByteArray &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(const QByteArray &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 QByteArray name("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
381bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
382{
383 challenge.clear();
384 // find out the type of authentication protocol requested.
385 QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
386 // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
387 QList<QByteArray> challenges = headerFieldValues(name: header);
388 for (int i = 0; i<challenges.size(); i++) {
389 QByteArray line = challenges.at(i);
390 // todo use qstrincmp
391 if (!line.toLower().startsWith(bv: "negotiate"))
392 challenge = line;
393 }
394 return !challenge.isEmpty();
395}
396
397qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
398{
399 if (fragment.isEmpty()) {
400 // reserve bytes for the status line. This is better than always append() which reallocs the byte array
401 fragment.reserve(asize: 32);
402 }
403
404 qint64 bytes = 0;
405 char c;
406 qint64 haveRead = 0;
407
408 do {
409 haveRead = socket->read(data: &c, maxlen: 1);
410 if (haveRead == -1)
411 return -1; // unexpected EOF
412 else if (haveRead == 0)
413 break; // read more later
414 else if (haveRead == 1 && fragment.size() == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
415 continue; // Ignore all whitespace that was trailing froma previous request on that socket
416
417 bytes++;
418
419 // allow both CRLF & LF (only) line endings
420 if (c == '\n') {
421 // remove the CR at the end
422 if (fragment.endsWith(c: '\r')) {
423 fragment.truncate(pos: fragment.size()-1);
424 }
425 bool ok = parseStatus(status: fragment);
426 state = ReadingHeaderState;
427 fragment.clear();
428 if (!ok) {
429 return -1;
430 }
431 break;
432 } else {
433 fragment.append(c);
434 }
435
436 // is this a valid reply?
437 if (fragment.size() == 5 && !fragment.startsWith(bv: "HTTP/")) {
438 fragment.clear();
439 return -1;
440 }
441 } while (haveRead == 1);
442
443 return bytes;
444}
445
446bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
447{
448 return parser.parseStatus(status);
449}
450
451qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
452{
453 if (fragment.isEmpty()) {
454 // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
455 // block is 381 bytes.
456 // reserve bytes. This is better than always append() which reallocs the byte array.
457 fragment.reserve(asize: 512);
458 }
459
460 qint64 bytes = 0;
461 char c = 0;
462 bool allHeaders = false;
463 qint64 haveRead = 0;
464 do {
465 haveRead = socket->read(data: &c, maxlen: 1);
466 if (haveRead == 0) {
467 // read more later
468 break;
469 } else if (haveRead == -1) {
470 // connection broke down
471 return -1;
472 } else {
473 fragment.append(c);
474 bytes++;
475
476 if (c == '\n') {
477 // check for possible header endings. As per HTTP rfc,
478 // the header endings will be marked by CRLFCRLF. But
479 // we will allow CRLFCRLF, CRLFLF, LFCRLF, LFLF
480 if (fragment.endsWith(bv: "\n\r\n")
481 || fragment.endsWith(bv: "\n\n"))
482 allHeaders = true;
483
484 // there is another case: We have no headers. Then the fragment equals just the line ending
485 if ((fragment.size() == 2 && fragment.endsWith(bv: "\r\n"))
486 || (fragment.size() == 1 && fragment.endsWith(bv: "\n")))
487 allHeaders = true;
488 }
489 }
490 } while (!allHeaders && haveRead > 0);
491
492 // we received all headers now parse them
493 if (allHeaders) {
494 parseHeader(header: fragment);
495 state = ReadingDataState;
496 fragment.clear(); // next fragment
497 bodyLength = contentLength(); // cache the length
498
499 // cache isChunked() since it is called often
500 chunkedTransferEncoding = headerField(name: "transfer-encoding").toLower().contains(bv: "chunked");
501
502 // cache isConnectionCloseEnabled since it is called often
503 QByteArray connectionHeaderField = headerField(name: "connection");
504 // check for explicit indication of close or the implicit connection close of HTTP/1.0
505 connectionCloseEnabled = (connectionHeaderField.toLower().contains(bv: "close") ||
506 headerField(name: "proxy-connection").toLower().contains(bv: "close")) ||
507 (parser.getMajorVersion() == 1 && parser.getMinorVersion() == 0 &&
508 (connectionHeaderField.isEmpty() && !headerField(name: "proxy-connection").toLower().contains(bv: "keep-alive")));
509 }
510 return bytes;
511}
512
513void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
514{
515 parser.parseHeaders(headers: header);
516}
517
518void QHttpNetworkReplyPrivate::appendHeaderField(const QByteArray &name, const QByteArray &data)
519{
520 parser.appendHeaderField(name, data);
521}
522
523bool QHttpNetworkReplyPrivate::isChunked()
524{
525 return chunkedTransferEncoding;
526}
527
528bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
529{
530 return connectionCloseEnabled || forceConnectionCloseEnabled;
531}
532
533// note this function can only be used for non-chunked, non-compressed with
534// known content length
535qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
536{
537 // This first read is to flush the buffer inside the socket
538 qint64 haveRead = 0;
539 haveRead = socket->read(data: b, maxlen: bodyLength - contentRead);
540 if (haveRead == -1) {
541 return -1;
542 }
543 contentRead += haveRead;
544
545 if (contentRead == bodyLength) {
546 state = AllDoneState;
547 }
548
549 return haveRead;
550}
551
552// note this function can only be used for non-chunked, non-compressed with
553// known content length
554qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
555{
556
557 qint64 toBeRead = qMin(a: socket->bytesAvailable(), b: bodyLength - contentRead);
558 if (readBufferMaxSize)
559 toBeRead = qMin(a: toBeRead, b: readBufferMaxSize);
560
561 if (!toBeRead)
562 return 0;
563
564 QByteArray bd;
565 bd.resize(size: toBeRead);
566 qint64 haveRead = socket->read(data: bd.data(), maxlen: toBeRead);
567 if (haveRead == -1) {
568 bd.clear();
569 return 0; // ### error checking here;
570 }
571 bd.resize(size: haveRead);
572
573 rb->append(bd);
574
575 if (contentRead + haveRead == bodyLength) {
576 state = AllDoneState;
577 }
578
579 contentRead += haveRead;
580 return haveRead;
581}
582
583
584qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
585{
586 qint64 bytes = 0;
587
588 if (isChunked()) {
589 // chunked transfer encoding (rfc 2616, sec 3.6)
590 bytes += readReplyBodyChunked(in: socket, out);
591 } else if (bodyLength > 0) {
592 // we have a Content-Length
593 bytes += readReplyBodyRaw(in: socket, out, size: bodyLength - contentRead);
594 if (contentRead + bytes == bodyLength)
595 state = AllDoneState;
596 } else {
597 // no content length. just read what's possible
598 bytes += readReplyBodyRaw(in: socket, out, size: socket->bytesAvailable());
599 }
600 contentRead += bytes;
601 return bytes;
602}
603
604qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
605{
606 // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
607 qint64 bytes = 0;
608 Q_ASSERT(socket);
609 Q_ASSERT(out);
610
611 int toBeRead = qMin<qint64>(a: 128*1024, b: qMin<qint64>(a: size, b: socket->bytesAvailable()));
612
613 if (readBufferMaxSize)
614 toBeRead = qMin<qint64>(a: toBeRead, b: readBufferMaxSize);
615
616 while (toBeRead > 0) {
617 QByteArray byteData;
618 byteData.resize(size: toBeRead);
619 qint64 haveRead = socket->read(data: byteData.data(), maxlen: byteData.size());
620 if (haveRead <= 0) {
621 // ### error checking here
622 byteData.clear();
623 return bytes;
624 }
625
626 byteData.resize(size: haveRead);
627 out->append(bd: byteData);
628 bytes += haveRead;
629 size -= haveRead;
630
631 toBeRead = qMin<qint64>(a: 128*1024, b: qMin<qint64>(a: size, b: socket->bytesAvailable()));
632 }
633 return bytes;
634
635}
636
637qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
638{
639 qint64 bytes = 0;
640 while (socket->bytesAvailable()) {
641
642 if (readBufferMaxSize && (bytes > readBufferMaxSize))
643 break;
644
645 if (!lastChunkRead && currentChunkRead >= currentChunkSize) {
646 // For the first chunk and when we're done with a chunk
647 currentChunkSize = 0;
648 currentChunkRead = 0;
649 if (bytes) {
650 // After a chunk
651 char crlf[2];
652 // read the "\r\n" after the chunk
653 qint64 haveRead = socket->read(data: crlf, maxlen: 2);
654 // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
655 // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
656 // it right now still works, but we should definitely fix this.
657
658 if (haveRead != 2)
659 return bytes; // FIXME
660 bytes += haveRead;
661 }
662 // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
663 bytes += getChunkSize(in: socket, chunkSize: &currentChunkSize);
664 if (currentChunkSize == -1)
665 break;
666 }
667 // if the chunk size is 0, end of the stream
668 if (currentChunkSize == 0 || lastChunkRead) {
669 lastChunkRead = true;
670 // try to read the "\r\n" after the chunk
671 char crlf[2];
672 qint64 haveRead = socket->read(data: crlf, maxlen: 2);
673 if (haveRead > 0)
674 bytes += haveRead;
675
676 if ((haveRead == 2 && crlf[0] == '\r' && crlf[1] == '\n') || (haveRead == 1 && crlf[0] == '\n'))
677 state = AllDoneState;
678 else if (haveRead == 1 && crlf[0] == '\r')
679 break; // Still waiting for the last \n
680 else if (haveRead > 0) {
681 // If we read something else then CRLF, we need to close the channel.
682 forceConnectionCloseEnabled = true;
683 state = AllDoneState;
684 }
685 break;
686 }
687
688 // otherwise, try to begin reading this chunk / to read what is missing for this chunk
689 qint64 haveRead = readReplyBodyRaw (socket, out, size: currentChunkSize - currentChunkRead);
690 currentChunkRead += haveRead;
691 bytes += haveRead;
692
693 // ### error checking here
694
695 }
696 return bytes;
697}
698
699qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
700{
701 qint64 bytes = 0;
702 char crlf[2];
703 *chunkSize = -1;
704
705 int bytesAvailable = socket->bytesAvailable();
706 // FIXME rewrite to permanent loop without bytesAvailable
707 while (bytesAvailable > bytes) {
708 qint64 sniffedBytes = socket->peek(data: crlf, maxlen: 2);
709 int fragmentSize = fragment.size();
710
711 // check the next two bytes for a "\r\n", skip blank lines
712 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
713 ||(fragmentSize > 1 && fragment.endsWith(c: '\r') && crlf[0] == '\n'))
714 {
715 bytes += socket->read(data: crlf, maxlen: 1); // read the \r or \n
716 if (crlf[0] == '\r')
717 bytes += socket->read(data: crlf, maxlen: 1); // read the \n
718 bool ok = false;
719 // ignore the chunk-extension
720 fragment = fragment.mid(index: 0, len: fragment.indexOf(c: ';')).trimmed();
721 *chunkSize = fragment.toLong(ok: &ok, base: 16);
722 fragment.clear();
723 break; // size done
724 } else {
725 // read the fragment to the buffer
726 char c = 0;
727 qint64 haveRead = socket->read(data: &c, maxlen: 1);
728 if (haveRead < 0) {
729 return -1; // FIXME
730 }
731 bytes += haveRead;
732 fragment.append(c);
733 }
734 }
735
736 return bytes;
737}
738
739bool QHttpNetworkReplyPrivate::isRedirecting() const
740{
741 // We're in the process of redirecting - if the HTTP status code says so and
742 // followRedirect is switched on
743 return (QHttpNetworkReply::isHttpRedirect(statusCode: parser.getStatusCode())
744 && request.isFollowRedirects());
745}
746
747bool QHttpNetworkReplyPrivate::shouldEmitSignals()
748{
749 // for 401 & 407 don't emit the data signals. Content along with these
750 // responses are sent only if the authentication fails.
751 return parser.getStatusCode() != 401 && parser.getStatusCode() != 407;
752}
753
754bool QHttpNetworkReplyPrivate::expectContent()
755{
756 int statusCode = parser.getStatusCode();
757 // check whether we can expect content after the headers (rfc 2616, sec4.4)
758 if ((statusCode >= 100 && statusCode < 200)
759 || statusCode == 204 || statusCode == 304)
760 return false;
761 if (request.operation() == QHttpNetworkRequest::Head)
762 return false; // no body expected for HEAD request
763 qint64 expectedContentLength = contentLength();
764 if (expectedContentLength == 0)
765 return false;
766 if (expectedContentLength == -1 && bodyLength == 0) {
767 // The content-length header was stripped, but its value was 0.
768 // This would be the case for an explicitly zero-length compressed response.
769 return false;
770 }
771 return true;
772}
773
774void QHttpNetworkReplyPrivate::eraseData()
775{
776 responseData.clear();
777}
778
779
780// SSL support below
781#ifndef QT_NO_SSL
782
783QSslConfiguration QHttpNetworkReply::sslConfiguration() const
784{
785 Q_D(const QHttpNetworkReply);
786
787 if (!d->connectionChannel)
788 return QSslConfiguration();
789
790 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(object: d->connectionChannel->socket);
791 if (!sslSocket)
792 return QSslConfiguration();
793
794 return sslSocket->sslConfiguration();
795}
796
797void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
798{
799 Q_D(QHttpNetworkReply);
800 if (d->connection)
801 d->connection->setSslConfiguration(config);
802}
803
804void QHttpNetworkReply::ignoreSslErrors()
805{
806 Q_D(QHttpNetworkReply);
807 if (d->connection)
808 d->connection->ignoreSslErrors();
809}
810
811void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
812{
813 Q_D(QHttpNetworkReply);
814 if (d->connection)
815 d->connection->ignoreSslErrors(errors);
816}
817
818
819#endif //QT_NO_SSL
820
821
822QT_END_NAMESPACE
823
824#include "moc_qhttpnetworkreply_p.cpp"
825

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