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
qnetworkreplyfileimpl.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:significant reason:default
4
6
7#include "QtCore/qdatetime.h"
9#include <QtCore/QCoreApplication>
10#include <QtCore/QFileInfo>
11#include <QtCore/QThread>
12#include "qnetworkfile_p.h"
14
16
17using namespace Qt::StringLiterals;
18
19QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::KnownHeaders, QNetworkRequest__KnownHeaders)
20
21QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
22 : QNetworkReplyPrivate(), managerPrivate(nullptr), realFile(nullptr)
23{
24 qRegisterMetaType<QNetworkRequest::KnownHeaders>();
25 qRegisterMetaType<QNetworkReply::NetworkError>();
26}
27
29{
31 if (d->realFile) {
32 if (d->realFile->thread() == QThread::currentThread())
33 delete d->realFile;
34 else
35 QMetaObject::invokeMethod(d->realFile, "deleteLater", Qt::QueuedConnection);
36 }
37}
38
39QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
40 : QNetworkReply(*new QNetworkReplyFileImplPrivate(), manager)
41{
42 setRequest(req);
43 setUrl(req.url());
44 setOperation(op);
45 QNetworkReply::open(QIODevice::ReadOnly);
46
48
49 d->managerPrivate = manager->d_func();
50
51 QUrl url = req.url();
52 if (url.host() == "localhost"_L1)
53 url.setHost(QString());
54
55#if !defined(Q_OS_WIN)
56 // do not allow UNC paths on Unix
57 if (!url.host().isEmpty()) {
58 // we handle only local files
59 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
60 setError(QNetworkReply::ProtocolInvalidOperationError, msg);
61 setFinished(true); // We're finished, will emit finished() after ctor is done.
62 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
63 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
64 QMetaObject::invokeMethod(this, &QNetworkReplyFileImpl::fileOpenFinished, Qt::QueuedConnection, false);
65 return;
66 }
67#endif
68 if (url.path().isEmpty())
69 url.setPath("/"_L1);
70 setUrl(url);
71
72 QString fileName = url.toLocalFile();
73 if (fileName.isEmpty()) {
74 const QString scheme = url.scheme();
75 if (scheme == "qrc"_L1) {
76 fileName = u':' + url.path();
77 } else {
78#if defined(Q_OS_ANDROID)
79 if (scheme == "assets"_L1)
80 fileName = "assets:"_L1 + url.path();
81 else
82#endif
83 fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
84 }
85 }
86
87 if (req.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) { // Asynchronous open
88 auto realFile = new QNetworkFile(fileName);
89 connect(realFile, &QNetworkFile::headerRead, this, &QNetworkReplyFileImpl::setWellKnownHeader,
90 Qt::QueuedConnection);
91 connect(realFile, &QNetworkFile::networkError, this, &QNetworkReplyFileImpl::setError,
92 Qt::QueuedConnection);
93 connect(realFile, SIGNAL(finished(bool)), SLOT(fileOpenFinished(bool)),
94 Qt::QueuedConnection);
95
96 realFile->moveToThread(d->managerPrivate->createThread());
97 QMetaObject::invokeMethod(realFile, "open", Qt::QueuedConnection);
98
99 d->realFile = realFile;
100 } else { // Synch open
101 setFinished(true);
102
103 QFileInfo fi(fileName);
104 if (fi.isDir()) {
105 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString());
106 setError(QNetworkReply::ContentOperationNotPermittedError, msg);
107 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
108 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
109 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
110 return;
111 }
112 d->realFile = new QFile(fileName, this);
113 bool opened = d->realFile->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
114
115 // could we open the file?
116 if (!opened) {
117 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
118 .arg(d->realFile->fileName(), d->realFile->errorString());
119
120 if (fi.exists()) {
121 setError(QNetworkReply::ContentAccessDenied, msg);
122 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
123 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
124 } else {
125 setError(QNetworkReply::ContentNotFoundError, msg);
126 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
127 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
128 }
129 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
130 return;
131 }
132 auto h = headers();
133 h.replaceOrAppend(QHttpHeaders::WellKnownHeader::LastModified,
134 QNetworkHeadersPrivate::toHttpDate(fi.lastModified()));
135 h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentLength,
136 QByteArray::number(fi.size()));
137 setHeaders(std::move(h));
138
139 QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
140 QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
141 Q_ARG(qint64, fi.size()), Q_ARG(qint64, fi.size()));
142 QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
143 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
144 }
145}
146
148{
149 Q_D(QNetworkReplyFileImpl);
150 QNetworkReply::close();
151 if (d->realFile) {
152 if (d->realFile->thread() == thread())
153 d->realFile->close();
154 else
155 QMetaObject::invokeMethod(d->realFile, "close", Qt::QueuedConnection);
156 }
157}
158
160{
161 close();
162}
163
165{
166 Q_D(const QNetworkReplyFileImpl);
167 if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
168 return QNetworkReply::bytesAvailable();
169 return QNetworkReply::bytesAvailable() + d->realFile->bytesAvailable();
170}
171
173{
174 return true;
175}
176
178{
179 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
180 headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
181 return totalSizeOpt.value_or(0);
182}
183
184/*!
185 \internal
186*/
187qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
188{
189 Q_D(QNetworkReplyFileImpl);
190 if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
191 return -1;
192 qint64 ret = d->realFile->read(data, maxlen);
193 if (bytesAvailable() == 0)
194 d->realFile->close();
195 if (ret == 0 && bytesAvailable() == 0)
196 return -1;
197 else {
198 setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
199 setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, "OK"_L1);
200 return ret;
201 }
202}
203
204void QNetworkReplyFileImpl::fileOpenFinished(bool isOpen)
205{
206 setFinished(true);
207 if (isOpen) {
208 const auto fileSize = size();
209 Q_EMIT metaDataChanged();
210 Q_EMIT downloadProgress(fileSize, fileSize);
211 Q_EMIT readyRead();
212 }
213 Q_EMIT finished();
214}
215
216QT_END_NAMESPACE
217
218#include "moc_qnetworkreplyfileimpl_p.cpp"
QNetworkAccessManagerPrivate * managerPrivate
virtual void close() override
Closes this device for reading.
virtual qint64 readData(char *data, qint64 maxlen) override
virtual bool isSequential() const override
virtual qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
virtual void abort() override
Aborts the operation immediately and closes any network connections still open.
qint64 size() const override
For open random-access devices, this function returns the size of the device.