Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qnetworkreplywasmimpl.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 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
5#include "qnetworkrequest.h"
6
7#include <QtCore/qtimer.h>
8#include <QtCore/qdatetime.h>
9#include <QtCore/qcoreapplication.h>
10#include <QtCore/qfileinfo.h>
11#include <QtCore/qthread.h>
12#include <QtCore/private/qoffsetstringarray_p.h>
13#include <QtCore/private/qtools_p.h>
14
15#include <private/qnetworkaccessmanager_p.h>
16#include <private/qnetworkfile_p.h>
17
18#include <emscripten.h>
19#include <emscripten/fetch.h>
20
22
23using namespace Qt::StringLiterals;
24
25namespace {
26
27static constexpr auto BannedHeaders = qOffsetStringArray(
28 "accept-charset",
29 "accept-encoding",
30 "access-control-request-headers",
31 "access-control-request-method",
32 "connection",
33 "content-length",
34 "cookie",
35 "cookie2",
36 "date",
37 "dnt",
38 "expect",
39 "host",
40 "keep-alive",
41 "origin",
42 "referer",
43 "te",
44 "trailer",
45 "transfer-encoding",
46 "upgrade",
47 "via"
48);
49
50bool isUnsafeHeader(QLatin1StringView header) noexcept
51{
52 return header.startsWith("proxy-"_L1, Qt::CaseInsensitive)
54 || BannedHeaders.contains(header, Qt::CaseInsensitive);
55}
56} // namespace
57
60 , managerPrivate(0)
61 , downloadBufferReadPosition(0)
62 , downloadBufferCurrentSize(0)
63 , totalDownloadSize(0)
64 , percentFinished(0)
65 , m_fetch(0)
66{
67}
68
70{
71}
72
75{
78}
79
81{
82}
83
84QByteArray QNetworkReplyWasmImpl::methodName() const
85{
86 const Q_D( QNetworkReplyWasmImpl);
87 switch (operation()) {
89 return "HEAD";
91 return "GET";
93 return "PUT";
95 return "POST";
97 return "DELETE";
99 return d->request.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray();
100 default:
101 break;
102 }
103 return QByteArray();
104}
105
107{
109
110 if (d->state != QNetworkReplyPrivate::Aborted &&
112 d->state != QNetworkReplyPrivate::Idle) {
114 d->setCanceled();
115 }
116
118}
119
121{
123
125 return;
126
128 d->setCanceled();
129}
130
132{
134 m_fetch->userData = nullptr;
135
137 q->setFinished(true);
138 emit q->finished();
139}
140
142{
143 Q_D(const QNetworkReplyWasmImpl);
144
145 return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
146}
147
149{
150 return true;
151}
152
154{
155 return QNetworkReply::size();
156}
157
162{
164
165 qint64 howMuch = qMin(maxlen, (d->downloadBuffer.size() - d->downloadBufferReadPosition));
166 memcpy(data, d->downloadBuffer.constData() + d->downloadBufferReadPosition, howMuch);
167 d->downloadBufferReadPosition += howMuch;
168
169 return howMuch;
170}
171
173{
175
177 request = req;
178 url = request.url();
179 operation = op;
180
181 q->QIODevice::open(QIODevice::ReadOnly);
183 bool bufferingDisallowed =
185
186 if (bufferingDisallowed) {
187 // if a valid content-length header for the request was supplied, we can disable buffering
188 // if not, we will buffer anyway
192 return;
193 }
194 } else {
195 // doSendRequest will be called when the buffering has finished.
198 return;
199 }
200 }
201 // No outgoing data (POST, ..)
203}
204
206{
208 Q_ASSERT(handler);
209
210 handler->q_func()->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
211 if (!statusReason.isEmpty())
212 handler->q_func()->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusReason);
213}
214
215constexpr int getArraySize (int factor) {
216 return 2 * factor + 1;
217}
218
220{
223
224 emscripten_fetch_attr_t attr;
225 emscripten_fetch_attr_init(&attr);
226 strcpy(attr.requestMethod, q->methodName().constData());
227
229 int arrayLength = getArraySize(headersData.count());
230 const char *customHeaders[arrayLength];
231 QStringList trimmedHeaders;
232
233 if (headersData.count() > 0) {
234 int i = 0;
235 for (const auto &headerName : headersData) {
236 if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
237 trimmedHeaders.push_back(QString::fromLatin1(headerName));
238 } else {
239 customHeaders[i++] = headerName.constData();
240 customHeaders[i++] = request.rawHeader(headerName).constData();
241 }
242 }
243 if (!trimmedHeaders.isEmpty()) {
244 qWarning() << "Qt has trimmed the following forbidden headers from the request:"
245 << trimmedHeaders.join(QLatin1StringView(", "));
246 }
247 customHeaders[i] = nullptr;
248 attr.requestHeaders = customHeaders;
249 }
250
251 if (outgoingData) { // data from post request
252 // handle extra data
253 requestData = outgoingData->readAll(); // is there a size restriction here?
254 if (!requestData.isEmpty()) {
255 attr.requestData = requestData.data();
256 attr.requestDataSize = requestData.size();
257 }
258 }
259
260 QByteArray userName, password;
261 // username & password
262 if (!request.url().userInfo().isEmpty()) {
263 userName = request.url().userName().toUtf8();
264 password = request.url().password().toUtf8();
265 attr.userName = userName.constData();
266 attr.password = password.constData();
267 }
268
269 attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
270
271 QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
273
274 if (CacheLoadControlAttribute == QNetworkRequest::AlwaysCache) {
275 attr.attributes += EMSCRIPTEN_FETCH_NO_DOWNLOAD;
276 }
277 if (CacheLoadControlAttribute == QNetworkRequest::PreferCache) {
278 attr.attributes += EMSCRIPTEN_FETCH_APPEND;
279 }
280
281 if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork ||
283 attr.attributes -= EMSCRIPTEN_FETCH_PERSIST_FILE;
284 }
285
286 attr.withCredentials = request.attribute(QNetworkRequest::UseCredentialsAttribute, false).toBool();
290 attr.onreadystatechange = QNetworkReplyWasmImplPrivate::stateChange;
291 attr.timeoutMSecs = request.transferTimeout();
292 attr.userData = reinterpret_cast<void *>(this);
293
294 QString dPath = QStringLiteral("/home/web_user/") + request.url().fileName();
295 QByteArray destinationPath = dPath.toUtf8();
296 attr.destinationPath = destinationPath.constData();
297
298 m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8());
299 state = Working;
300}
301
303{
305
306 q->setError(errorCode, errorString);
307 emit q->errorOccurred(errorCode);
308}
309
311{
313
314 totalDownloadSize = bytesTotal;
315
316 percentFinished = bytesTotal ? (bytesReceived / bytesTotal) * 100 : 100;
317
318 emit q->downloadProgress(bytesReceived, bytesTotal);
319}
320
322{
324
325 if (bufferSize > 0)
326 q->setReadBufferSize(bufferSize);
327
328 bytesDownloaded = bufferSize;
329
330 if (percentFinished != 100)
331 downloadBufferCurrentSize += bufferSize;
332 else
333 downloadBufferCurrentSize = bufferSize;
334
336
337 downloadBuffer.append(buffer, bufferSize);
338
339 emit q->readyRead();
340}
341
342//taken from qnetworkrequest.cpp
344{
345 if (headerName.isEmpty())
346 return -1;
347
348 auto is = [&](const char *what) {
349 return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
350 };
351
353 case 'c':
354 if (is("content-type"))
356 else if (is("content-length"))
358 else if (is("cookie"))
360 break;
361
362 case 'l':
363 if (is("location"))
365 else if (is("last-modified"))
367 break;
368
369 case 's':
370 if (is("set-cookie"))
372 else if (is("server"))
374 break;
375
376 case 'u':
377 if (is("user-agent"))
379 break;
380 }
381
382 return -1; // nothing found
383}
384
385
387{
389
390 if (!buffer.isEmpty()) {
391 QList<QByteArray> headers = buffer.split('\n');
392
393 for (int i = 0; i < headers.size(); i++) {
394 if (headers.at(i).contains(':')) { // headers include final \x00, so skip
395 QByteArray headerName = headers.at(i).split(':').at(0).trimmed();
396 QByteArray headersValue = headers.at(i).split(':').at(1).trimmed();
397
398 if (headerName.isEmpty() || headersValue.isEmpty())
399 continue;
400
401 int headerIndex = parseHeaderName(headerName);
402
403 if (headerIndex == -1)
404 q->setRawHeader(headerName, headersValue);
405 else
406 q->setHeader(static_cast<QNetworkRequest::KnownHeaders>(headerIndex), (QVariant)headersValue);
407 }
408 }
409 }
410 emit q->metaDataChanged();
411}
412
414{
416
417 // make sure this is only called once, ever.
418 //_q_bufferOutgoingData may call it or the readChannelFinished emission
419 if (state != Buffering)
420 return;
421
422 // disconnect signals
425
426 // finally, start the request
428}
429
431{
433
434 if (!outgoingDataBuffer) {
435 // first call, create our buffer
436 outgoingDataBuffer = std::make_shared<QRingBuffer>();
437
440 }
441
443 qint64 bytesToBuffer = 0;
444
445 // read data into our buffer
446 forever {
447 bytesToBuffer = outgoingData->bytesAvailable();
448 // unknown? just try 2 kB, this also ensures we always try to read the EOF
449 if (bytesToBuffer <= 0)
450 bytesToBuffer = 2*1024;
451
452 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
453 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
454
455 if (bytesBuffered == -1) {
456 // EOF has been reached.
457 outgoingDataBuffer->chop(bytesToBuffer);
458
460 break;
461 } else if (bytesBuffered == 0) {
462 // nothing read right now, just wait until we get called again
463 outgoingDataBuffer->chop(bytesToBuffer);
464
465 break;
466 } else {
467 // don't break, try to read() again
468 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
469 }
470 }
471}
472
474{
475 auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
476 if (reply) {
477 if (reply->state != QNetworkReplyPrivate::Aborted) {
478 QByteArray buffer(fetch->data, fetch->numBytes);
479 reply->dataReceived(buffer, buffer.size());
480 QByteArray statusText(fetch->statusText);
481 reply->setStatusCode(fetch->status, statusText);
482 reply->setReplyFinished();
483 }
484 reply->m_fetch = nullptr;
485 }
486 emscripten_fetch_close(fetch);
487}
488
490{
492 q->setFinished(true);
493 emit q->readChannelFinished();
494 emit q->finished();
495}
496
498{
500 q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
501 q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusText);
502}
503
504void QNetworkReplyWasmImplPrivate::stateChange(emscripten_fetch_t *fetch)
505{
506 auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
507 if (reply && reply->state != QNetworkReplyPrivate::Aborted) {
508 if (fetch->readyState == /*HEADERS_RECEIVED*/ 2) {
509 size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
510 QByteArray str(headerLength, Qt::Uninitialized);
511 emscripten_fetch_get_response_headers(fetch, str.data(), str.size());
512 reply->headersReceived(str);
513 }
514 }
515}
516
518{
519 auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
520 if (reply && reply->state != QNetworkReplyPrivate::Aborted) {
521 if (fetch->status < 400) {
522 uint64_t bytes = fetch->dataOffset + fetch->numBytes;
523 uint64_t tBytes = fetch->totalBytes; // totalBytes can be 0 if server not reporting content length
524 if (tBytes == 0)
525 tBytes = bytes;
526 reply->emitDataReadProgress(bytes, tBytes);
527 }
528 }
529}
530
531void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
532{
533 auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
534 if (reply) {
535 if (reply->state != QNetworkReplyPrivate::Aborted) {
536 QString reasonStr;
537 if (fetch->status > 600)
538 reasonStr = QStringLiteral("Operation canceled");
539 else
540 reasonStr = QString::fromUtf8(fetch->statusText);
541 QByteArray buffer(fetch->data, fetch->numBytes);
542 reply->dataReceived(buffer, buffer.size());
543 QByteArray statusText(fetch->statusText);
544 reply->setStatusCode(fetch->status, statusText);
545 reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()), reasonStr);
546 reply->setReplyFinished();
547 }
548 reply->m_fetch = nullptr;
549 }
550 emscripten_fetch_close(fetch);
551}
552
553//taken from qhttpthreaddelegate.cpp
555{
557 // we've got an error
558 switch (httpStatusCode) {
559 case 400: // Bad Request
561 break;
562
563 case 401: // Authorization required
565 break;
566
567 case 403: // Access denied
569 break;
570
571 case 404: // Not Found
573 break;
574
575 case 405: // Method Not Allowed
577 break;
578
579 case 407:
581 break;
582
583 case 409: // Resource Conflict
585 break;
586
587 case 410: // Content no longer available
589 break;
590
591 case 418: // I'm a teapot
593 break;
594
595 case 500: // Internal Server Error
597 break;
598
599 case 501: // Server does not support this functionality
601 break;
602
603 case 503: // Service unavailable
605 break;
606
607 case 65535: //emscripten reply when aborted
609 break;
610 default:
611 if (httpStatusCode > 500) {
612 // some kind of server error
614 } else if (httpStatusCode >= 400) {
615 // content error we did not handle above
617 } else {
618 qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
619 httpStatusCode, qPrintable(url.toString()));
621 }
622 };
623
624 return code;
625}
626
628
629#include "moc_qnetworkreplywasmimpl_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
char front() const
Definition qbytearray.h:132
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QRingBufferRef buffer
Definition qiodevice_p.h:92
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
virtual bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
qsizetype count() const noexcept
Definition qlist.h:387
Operation
Indicates the operation this reply is processing.
QNetworkRequest request
QNetworkAccessManager::Operation operation
QNetworkReply::NetworkError errorCode
std::shared_ptr< QRingBuffer > outgoingDataBuffer
static void setReplyAttributes(quintptr data, int statusCode, const QString &statusReason)
void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
static void downloadFailed(emscripten_fetch_t *fetch)
static void downloadSucceeded(emscripten_fetch_t *fetch)
void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &)
void setStatusCode(int status, const QByteArray &statusText)
void dataReceived(const QByteArray &buffer, int bufferSize)
void headersReceived(const QByteArray &buffer)
static void stateChange(emscripten_fetch_t *fetch)
void emitDataReadProgress(qint64 done, qint64 total)
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
static void downloadProgress(emscripten_fetch_t *fetch)
qint64 size() const override
For open random-access devices, this function returns the size of the device.
virtual qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
virtual qint64 readData(char *data, qint64 maxlen) override
virtual void abort() override
Aborts the operation immediately and close down any network connections still open.
virtual bool isSequential() const override
QNetworkReplyWasmImpl(QObject *parent=nullptr)
virtual void close() override
Closes this device for reading.
The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager.
virtual void close() override
Closes this device for reading.
QNetworkAccessManager::Operation operation() const
Returns the operation that was posted for this reply.
NetworkError
Indicates all possible error conditions found during the processing of the request.
@ ContentOperationNotPermittedError
@ OperationNotImplementedError
@ ProtocolInvalidOperationError
@ ProxyAuthenticationRequiredError
@ AuthenticationRequiredError
QNetworkRequest request() const
Returns the request that was posted for this reply.
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
KnownHeaders
List of known header types that QNetworkRequest parses.
QVariant attribute(Attribute code, const QVariant &defaultValue=QVariant()) const
Returns the attribute associated with the code code.
QVariant header(KnownHeaders header) const
Returns the value of the known network header header if it is present in this request.
QList< QByteArray > rawHeaderList() const
Returns a list of all raw headers that are set in this network request.
QUrl url() const
Returns the URL this network request is referring to.
QByteArray rawHeader(const QByteArray &headerName) const
Returns the raw form of header headerName.
CacheLoadControl
Controls the caching mechanism of QNetworkAccessManager.
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1095
QByteArray toUtf8() const &
Definition qstring.h:563
\inmodule QtCore
Definition qurl.h:94
QString userInfo(ComponentFormattingOptions options=PrettyDecoded) const
Returns the user info of the URL, or an empty string if the user info is undefined.
Definition qurl.cpp:2126
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2494
QString userName(ComponentFormattingOptions options=FullyDecoded) const
Returns the user name of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2196
QString password(ComponentFormattingOptions=FullyDecoded) const
Returns the password of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2259
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
\inmodule QtCore
Definition qvariant.h:64
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:707
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
QString str
[2]
Combined button and popup list for selecting options.
constexpr char toAsciiLower(char ch) noexcept
Definition qtools_p.h:87
@ CaseInsensitive
constexpr Initialization Uninitialized
static int arrayLength(const QString &rawType)
Definition provider.cpp:52
int qstrnicmp(const char *str1, qsizetype len1, const char *str2, qsizetype len2)
static QString header(const QString &name)
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:162
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr int getArraySize(int factor)
static int parseHeaderName(const QByteArray &headerName)
static QByteArray headerName(QNetworkRequest::KnownHeaders header)
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
constexpr auto qOffsetStringArray(const char(&...strings)[Nx]) noexcept
GLenum GLuint buffer
GLenum GLenum dst
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1391
#define QStringLiteral(str)
#define emit
size_t quintptr
Definition qtypes.h:72
long long qint64
Definition qtypes.h:55
QUrl url("example.com")
[constructor-url-reference]
QNetworkReply * reply
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent