252 Q_Q(QNetworkReplyWasmImpl);
253 totalDownloadSize = 0;
255 emscripten_fetch_attr_t attr;
256 emscripten_fetch_attr_init(&attr);
257 qstrncpy(attr.requestMethod, q->methodName().constData(), 32);
259 attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
261 QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
262 (QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
264 if (CacheLoadControlAttribute == QNetworkRequest::AlwaysCache) {
265 attr.attributes += EMSCRIPTEN_FETCH_NO_DOWNLOAD;
267 if (CacheLoadControlAttribute == QNetworkRequest::PreferCache) {
268 attr.attributes += EMSCRIPTEN_FETCH_APPEND;
271 attr.withCredentials = request.attribute(QNetworkRequest::UseCredentialsAttribute,
false).toBool();
272 attr.onsuccess = QNetworkReplyWasmImplPrivate::downloadSucceeded;
273 attr.onerror = QNetworkReplyWasmImplPrivate::downloadFailed;
274 attr.onprogress = QNetworkReplyWasmImplPrivate::downloadProgress;
275 attr.onreadystatechange = QNetworkReplyWasmImplPrivate::stateChange;
276 attr.timeoutMSecs = request.transferTimeout();
288 qwasmglobal::runOnMainThread([attr, fetchContext = m_fetchContext]()
mutable {
289 std::unique_lock lock{ fetchContext->mutex };
290 if (fetchContext->state == FetchContext::State::CANCELED) {
291 fetchContext->state = FetchContext::State::FINISHED;
293 }
else if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
298 const auto reply = fetchContext->reply;
299 const auto &request = reply->request;
301 QByteArray userName, password;
302 if (!request.url().userInfo().isEmpty()) {
303 userName = request.url().userName().toUtf8();
304 password = request.url().password().toUtf8();
305 attr.userName = userName.constData();
306 attr.password = password.constData();
309 QList<QByteArray> headersData = request.rawHeaderList();
310 int arrayLength = getArraySize(headersData.count());
311 const char *customHeaders[arrayLength];
312 QStringList trimmedHeaders;
313 if (headersData.count() > 0) {
315 for (
const auto &headerName : headersData) {
316 if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
317 trimmedHeaders.push_back(QString::fromLatin1(headerName));
319 customHeaders[i++] = headerName.constData();
320 customHeaders[i++] = request.rawHeader(headerName).constData();
323 if (!trimmedHeaders.isEmpty()) {
324 qWarning() <<
"Qt has trimmed the following forbidden headers from the request:"
325 << trimmedHeaders.join(QLatin1StringView(
", "));
327 customHeaders[i] =
nullptr;
328 attr.requestHeaders = customHeaders;
331 auto url = request.url().toString().toUtf8();
332 QString dPath =
"/home/web_user/"_L1 + request.url().fileName();
333 QByteArray destinationPath = dPath.toUtf8();
334 attr.destinationPath = destinationPath.constData();
335 reply->m_fetch = emscripten_fetch(&attr, url.constData());
336 fetchContext->state = FetchContext::State::SENT;
385 if (headerName.isEmpty())
388 auto is = [&](
const char *what) {
389 return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
392 switch (QtMiscUtils::toAsciiLower(headerName.front())) {
394 if (is(
"content-type"))
395 return QNetworkRequest::ContentTypeHeader;
396 else if (is(
"content-length"))
397 return QNetworkRequest::ContentLengthHeader;
398 else if (is(
"cookie"))
399 return QNetworkRequest::CookieHeader;
404 return QNetworkRequest::LocationHeader;
405 else if (is(
"last-modified"))
406 return QNetworkRequest::LastModifiedHeader;
410 if (is(
"set-cookie"))
411 return QNetworkRequest::SetCookieHeader;
412 else if (is(
"server"))
413 return QNetworkRequest::ServerHeader;
417 if (is(
"user-agent"))
418 return QNetworkRequest::UserAgentHeader;
428 Q_Q(QNetworkReplyWasmImpl);
430 if (!buffer.isEmpty()) {
431 QList<QByteArray> headers = buffer.split(
'\n');
433 for (
auto &&header : headers) {
434 if (
auto splitPos = header.indexOf(
':');
436 auto headerName = header.first(splitPos).trimmed();
437 auto headerValue = header.sliced(splitPos + 1).trimmed();
438 headerValue = headerValue.replace(
'\x00',
"").trimmed();
440 if (headerName.isEmpty() || headerValue.isEmpty())
443 int headerIndex = parseHeaderName(headerName);
445 if (headerIndex == -1)
446 q->setRawHeader(headerName, headerValue);
448 q->setHeader(
static_cast<QNetworkRequest::KnownHeaders>(headerIndex),
449 (QVariant)headerValue);
453 emit q->metaDataChanged();
475 Q_Q(QNetworkReplyWasmImpl);
477 if (!outgoingDataBuffer) {
479 outgoingDataBuffer = std::make_shared<QRingBuffer>();
481 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
482 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
485 qint64 bytesBuffered = 0;
486 qint64 bytesToBuffer = 0;
490 bytesToBuffer = outgoingData->bytesAvailable();
492 if (bytesToBuffer <= 0)
493 bytesToBuffer = 2*1024;
495 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
496 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
498 if (bytesBuffered == -1) {
500 outgoingDataBuffer->chop(bytesToBuffer);
502 _q_bufferOutgoingDataFinished();
504 }
else if (bytesBuffered == 0) {
506 outgoingDataBuffer->chop(bytesToBuffer);
511 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
560 const auto fetchContext =
static_cast<
FetchContext*>(fetch->userData);
561 const auto reply = fetchContext->reply;
563 if (fetch->readyState == 2) {
564 size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
565 QByteArray str(headerLength, Qt::Uninitialized);
566 emscripten_fetch_get_response_headers(fetch, str.data(), str.size());
567 reply->headersReceived(str);
589 const auto fetchContext =
static_cast<
FetchContext*>(fetch->userData);
590 std::unique_lock lock{ fetchContext->mutex };
600 const auto reply = fetchContext->reply;
603 if (fetch->status > 600)
604 reasonStr = QStringLiteral(
"Operation canceled");
606 reasonStr = QString::fromUtf8(fetch->statusText);
608 reply->setStatusCode(fetch->status, statusText);
609 QByteArray buffer(fetch->data, fetch->numBytes);
610 reply->dataReceived(buffer);
611 reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()),
613 reply->setReplyFinished();
615 reply->m_fetch =
nullptr;
623 QNetworkReply::NetworkError code;
625 switch (httpStatusCode) {
627 code = QNetworkReply::ProtocolInvalidOperationError;
631 code = QNetworkReply::AuthenticationRequiredError;
635 code = QNetworkReply::ContentAccessDenied;
639 code = QNetworkReply::ContentNotFoundError;
643 code = QNetworkReply::ContentOperationNotPermittedError;
647 code = QNetworkReply::ProxyAuthenticationRequiredError;
651 code = QNetworkReply::ContentConflictError;
655 code = QNetworkReply::ContentGoneError;
659 code = QNetworkReply::ProtocolInvalidOperationError;
663 code = QNetworkReply::InternalServerError;
667 code = QNetworkReply::OperationNotImplementedError;
671 code = QNetworkReply::ServiceUnavailableError;
675 code = QNetworkReply::OperationCanceledError;
678 if (httpStatusCode > 500) {
680 code = QNetworkReply::UnknownServerError;
681 }
else if (httpStatusCode >= 400) {
683 code = QNetworkReply::UnknownContentError;
685 qWarning(
"QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
686 httpStatusCode, qPrintable(url.toString()));
687 code = QNetworkReply::ProtocolFailure;
The FetchContext class ensures the requestData object remains valid while a fetch operation is pendin...
FetchContext(QNetworkReplyWasmImplPrivate *networkReply)
QNetworkReplyWasmImplPrivate * reply