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 QObjectPrivate::deleteInOwnThread(d->realFile);
33}
34
35QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
36 : QNetworkReply(*new QNetworkReplyFileImplPrivate(), manager)
37{
38 setRequest(req);
39 setUrl(req.url());
40 setOperation(op);
41 QNetworkReply::open(QIODevice::ReadOnly);
42
44
45 d->managerPrivate = manager->d_func();
46
47 QUrl url = req.url();
48 if (url.host() == "localhost"_L1)
49 url.setHost(QString());
50
51#if !defined(Q_OS_WIN)
52 // do not allow UNC paths on Unix
53 if (!url.host().isEmpty()) {
54 // we handle only local files
55 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
56 setError(QNetworkReply::ProtocolInvalidOperationError, msg);
57 setFinished(true); // We're finished, will emit finished() after ctor is done.
58 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
59 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
60 QMetaObject::invokeMethod(this, &QNetworkReplyFileImpl::fileOpenFinished, Qt::QueuedConnection, false);
61 return;
62 }
63#endif
64 if (url.path().isEmpty())
65 url.setPath("/"_L1);
66 setUrl(url);
67
68 QString fileName = url.toLocalFile();
69 if (fileName.isEmpty()) {
70 const QString scheme = url.scheme();
71 if (scheme == "qrc"_L1) {
72 fileName = u':' + url.path();
73 } else {
74#if defined(Q_OS_ANDROID)
75 if (scheme == "assets"_L1)
76 fileName = "assets:"_L1 + url.path();
77 else
78#endif
79 fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
80 }
81 }
82
83 if (req.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) { // Asynchronous open
84 auto realFile = new QNetworkFile(fileName);
85 connect(realFile, &QNetworkFile::headerRead, this, &QNetworkReplyFileImpl::setWellKnownHeader,
86 Qt::QueuedConnection);
87 connect(realFile, &QNetworkFile::networkError, this, &QNetworkReplyFileImpl::setError,
88 Qt::QueuedConnection);
89 connect(realFile, SIGNAL(finished(bool)), SLOT(fileOpenFinished(bool)),
90 Qt::QueuedConnection);
91
92 realFile->moveToThread(d->managerPrivate->createThread());
93 QMetaObject::invokeMethod(realFile, "open", Qt::QueuedConnection);
94
95 d->realFile = realFile;
96 } else { // Synch open
97 setFinished(true);
98
99 QFileInfo fi(fileName);
100 if (fi.isDir()) {
101 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString());
102 setError(QNetworkReply::ContentOperationNotPermittedError, msg);
103 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
104 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
105 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
106 return;
107 }
108 d->realFile = new QFile(fileName, this);
109 bool opened = d->realFile->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
110
111 // could we open the file?
112 if (!opened) {
113 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
114 .arg(d->realFile->fileName(), d->realFile->errorString());
115
116 if (fi.exists()) {
117 setError(QNetworkReply::ContentAccessDenied, msg);
118 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
119 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
120 } else {
121 setError(QNetworkReply::ContentNotFoundError, msg);
122 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
123 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
124 }
125 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
126 return;
127 }
128 auto h = headers();
129 h.replaceOrAppend(QHttpHeaders::WellKnownHeader::LastModified,
130 QNetworkHeadersPrivate::toHttpDate(fi.lastModified()));
131 h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentLength,
132 QByteArray::number(fi.size()));
133 setHeaders(std::move(h));
134
135 QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
136 QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
137 Q_ARG(qint64, fi.size()), Q_ARG(qint64, fi.size()));
138 QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
139 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
140 }
141}
142
144{
145 Q_D(QNetworkReplyFileImpl);
146 QNetworkReply::close();
147 if (d->realFile) {
148 if (d->realFile->thread() == thread())
149 d->realFile->close();
150 else
151 QMetaObject::invokeMethod(d->realFile, "close", Qt::QueuedConnection);
152 }
153}
154
156{
157 close();
158}
159
161{
162 Q_D(const QNetworkReplyFileImpl);
163 if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
164 return QNetworkReply::bytesAvailable();
165 return QNetworkReply::bytesAvailable() + d->realFile->bytesAvailable();
166}
167
169{
170 return true;
171}
172
174{
175 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
176 headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
177 return totalSizeOpt.value_or(0);
178}
179
180/*!
181 \internal
182*/
183qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
184{
185 Q_D(QNetworkReplyFileImpl);
186 if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
187 return -1;
188 qint64 ret = d->realFile->read(data, maxlen);
189 if (bytesAvailable() == 0)
190 d->realFile->close();
191 if (ret == 0 && bytesAvailable() == 0)
192 return -1;
193 else {
194 setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
195 setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, "OK"_L1);
196 return ret;
197 }
198}
199
200void QNetworkReplyFileImpl::fileOpenFinished(bool isOpen)
201{
202 setFinished(true);
203 if (isOpen) {
204 const auto fileSize = size();
205 Q_EMIT metaDataChanged();
206 Q_EMIT downloadProgress(fileSize, fileSize);
207 Q_EMIT readyRead();
208 }
210}
211
212QT_END_NAMESPACE
213
214#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
\reimp
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.
Combined button and popup list for selecting options.