Coverage Report

Created: 2026-02-26 07:48

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