248 Q_Q(QNetworkReplyWasmImpl);
249 totalDownloadSize = 0;
251 emscripten_fetch_attr_t attr;
252 emscripten_fetch_attr_init(&attr);
253 qstrncpy(attr.requestMethod, q->methodName().constData(), 32);
255 attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
257 QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
258 (QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
260 if (CacheLoadControlAttribute == QNetworkRequest::AlwaysCache) {
261 attr.attributes += EMSCRIPTEN_FETCH_NO_DOWNLOAD;
263 if (CacheLoadControlAttribute == QNetworkRequest::PreferCache) {
264 attr.attributes += EMSCRIPTEN_FETCH_APPEND;
267 attr.withCredentials = request.attribute(QNetworkRequest::UseCredentialsAttribute,
false).toBool();
268 attr.onsuccess = QNetworkReplyWasmImplPrivate::downloadSucceeded;
269 attr.onerror = QNetworkReplyWasmImplPrivate::downloadFailed;
270 attr.onprogress = QNetworkReplyWasmImplPrivate::downloadProgress;
271 attr.onreadystatechange = QNetworkReplyWasmImplPrivate::stateChange;
272 attr.timeoutMSecs = request.transferTimeout();
284 qwasmglobal::runOnMainThread([attr, fetchContext = m_fetchContext]()
mutable {
285 std::unique_lock lock{ fetchContext->mutex };
286 if (fetchContext->state == FetchContext::State::CANCELED) {
287 fetchContext->state = FetchContext::State::FINISHED;
289 }
else if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
294 const auto reply = fetchContext->reply;
295 const auto &request = reply->request;
297 QByteArray userName, password;
298 if (!request.url().userInfo().isEmpty()) {
299 userName = request.url().userName().toUtf8();
300 password = request.url().password().toUtf8();
301 attr.userName = userName.constData();
302 attr.password = password.constData();
305 QList<QByteArray> headersData = request.rawHeaderList();
306 int arrayLength = getArraySize(headersData.count());
307 const char *customHeaders[arrayLength];
308 QStringList trimmedHeaders;
309 if (headersData.count() > 0) {
311 for (
const auto &headerName : headersData) {
312 if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
313 trimmedHeaders.push_back(QString::fromLatin1(headerName));
315 customHeaders[i++] = headerName.constData();
316 customHeaders[i++] = request.rawHeader(headerName).constData();
319 if (!trimmedHeaders.isEmpty()) {
320 qWarning() <<
"Qt has trimmed the following forbidden headers from the request:"
321 << trimmedHeaders.join(QLatin1StringView(
", "));
323 customHeaders[i] =
nullptr;
324 attr.requestHeaders = customHeaders;
327 auto url = request.url().toString().toUtf8();
328 QString dPath =
"/home/web_user/"_L1 + request.url().fileName();
329 QByteArray destinationPath = dPath.toUtf8();
330 attr.destinationPath = destinationPath.constData();
331 reply->m_fetch = emscripten_fetch(&attr, url.constData());
332 fetchContext->state = FetchContext::State::SENT;
381 if (headerName.isEmpty())
384 auto is = [&](
const char *what) {
385 return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
388 switch (QtMiscUtils::toAsciiLower(headerName.front())) {
390 if (is(
"content-type"))
391 return QNetworkRequest::ContentTypeHeader;
392 else if (is(
"content-length"))
393 return QNetworkRequest::ContentLengthHeader;
394 else if (is(
"cookie"))
395 return QNetworkRequest::CookieHeader;
400 return QNetworkRequest::LocationHeader;
401 else if (is(
"last-modified"))
402 return QNetworkRequest::LastModifiedHeader;
406 if (is(
"set-cookie"))
407 return QNetworkRequest::SetCookieHeader;
408 else if (is(
"server"))
409 return QNetworkRequest::ServerHeader;
413 if (is(
"user-agent"))
414 return QNetworkRequest::UserAgentHeader;
424 Q_Q(QNetworkReplyWasmImpl);
426 if (!buffer.isEmpty()) {
427 QList<QByteArray> headers = buffer.split(
'\n');
429 for (
auto &&header : headers) {
430 if (
auto splitPos = header.indexOf(
':');
432 auto headerName = header.first(splitPos).trimmed();
433 auto headerValue = header.sliced(splitPos + 1).trimmed();
435 if (headerName.isEmpty() || headerValue.isEmpty())
438 int headerIndex = parseHeaderName(headerName);
440 if (headerIndex == -1)
441 q->setRawHeader(headerName, headerValue);
443 q->setHeader(
static_cast<QNetworkRequest::KnownHeaders>(headerIndex),
444 (QVariant)headerValue);
448 emit q->metaDataChanged();
470 Q_Q(QNetworkReplyWasmImpl);
472 if (!outgoingDataBuffer) {
474 outgoingDataBuffer = std::make_shared<QRingBuffer>();
476 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
477 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
480 qint64 bytesBuffered = 0;
481 qint64 bytesToBuffer = 0;
485 bytesToBuffer = outgoingData->bytesAvailable();
487 if (bytesToBuffer <= 0)
488 bytesToBuffer = 2*1024;
490 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
491 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
493 if (bytesBuffered == -1) {
495 outgoingDataBuffer->chop(bytesToBuffer);
497 _q_bufferOutgoingDataFinished();
499 }
else if (bytesBuffered == 0) {
501 outgoingDataBuffer->chop(bytesToBuffer);
506 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
555 const auto fetchContext =
static_cast<
FetchContext*>(fetch->userData);
556 const auto reply = fetchContext->reply;
557 if (reply && reply->state != QNetworkReplyPrivate::Aborted) {
558 if (fetch->readyState == 2) {
559 size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
560 QByteArray str(headerLength, Qt::Uninitialized);
561 emscripten_fetch_get_response_headers(fetch, str.data(), str.size());
562 reply->headersReceived(str);
584 const auto fetchContext =
static_cast<
FetchContext*>(fetch->userData);
585 std::unique_lock lock{ fetchContext->mutex };
595 const auto reply = fetchContext->reply;
596 if (reply->state != QNetworkReplyPrivate::Aborted) {
598 if (fetch->status > 600)
599 reasonStr = QStringLiteral(
"Operation canceled");
601 reasonStr = QString::fromUtf8(fetch->statusText);
603 reply->setStatusCode(fetch->status, statusText);
604 QByteArray buffer(fetch->data, fetch->numBytes);
605 reply->dataReceived(buffer);
606 reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()),
608 reply->setReplyFinished();
610 reply->m_fetch =
nullptr;
618 QNetworkReply::NetworkError code;
620 switch (httpStatusCode) {
622 code = QNetworkReply::ProtocolInvalidOperationError;
626 code = QNetworkReply::AuthenticationRequiredError;
630 code = QNetworkReply::ContentAccessDenied;
634 code = QNetworkReply::ContentNotFoundError;
638 code = QNetworkReply::ContentOperationNotPermittedError;
642 code = QNetworkReply::ProxyAuthenticationRequiredError;
646 code = QNetworkReply::ContentConflictError;
650 code = QNetworkReply::ContentGoneError;
654 code = QNetworkReply::ProtocolInvalidOperationError;
658 code = QNetworkReply::InternalServerError;
662 code = QNetworkReply::OperationNotImplementedError;
666 code = QNetworkReply::ServiceUnavailableError;
670 code = QNetworkReply::OperationCanceledError;
673 if (httpStatusCode > 500) {
675 code = QNetworkReply::UnknownServerError;
676 }
else if (httpStatusCode >= 400) {
678 code = QNetworkReply::UnknownContentError;
680 qWarning(
"QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
681 httpStatusCode, qPrintable(url.toString()));
682 code = QNetworkReply::ProtocolFailure;
The FetchContext class ensures the requestData object remains valid while a fetch operation is pendin...
FetchContext(QNetworkReplyWasmImplPrivate *networkReply)
QNetworkReplyWasmImplPrivate * reply