I am trying to make simple application that downloads info from server via REST api. For downloading data, I use QNetworkAccessManager . My application works well when built for desktop and I am trying to build it for WebAssembly .
I am using:
I have built Qt webassembly from source with -feature-thread
to support multithreading, and memory / workers (threads) settings is
QMAKE_WASM_PTHREAD_POOL_SIZE = 8
QMAKE_WASM_TOTAL_MEMORY = 3GB
When I use it as native desktop, everything works ok. When I launch it via browser (WebAssembly), application starts, the GUI shows up and I can input connection address. When I write a valid one (server that supports CORS), the request goes without problem and returns valid JSON document. But when I type in invalid address, the application crashes .
The crash is caused by abort()
function somewhere in Qt ( call stack ). Also, the application behave inconsistently - sometimes crash happen for first request, sometimes for third.
There are two possible errors I encountered - Uncaught RuntimeError: abort(undefined)
and Uncaught RuntimeError: abort(Runtime error: The application has corrupted its heap memory area (address zero)!)
.
I tried multiple tutorials for QNetworkAccessManager , but with same result.
main.cpp
#include "DummyRequestManager.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
DummyRequestManager c;
c.init();
return app.exec();
}
DummyRequestManager.hpp
#ifndef DUMMYREQUESTMANAGER_H
#define DUMMYREQUESTMANAGER_H
#include <QQuickView>
#include <QObject>
#include <QNetworkAccessManager>
#include <QJsonDocument>
#include <memory>
enum HttpRespStatus
{
NoResponse = 0,
Ok = 200,
NoContent = 204,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404,
MethodNotAllowed = 405,
NotAcceptable = 406, // Shouldn't be used.
Conflict = 409,
Gone = 410,
UnsupportedMediaType = 415,
InternalError = 500,
NotImplemented = 501,
ServiceUnavailable = 503
};
class DummyRequestManager : public QNetworkAccessManager
{
Q_OBJECT
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
public:
DummyRequestManager();
void init();
QString url() const { return url_; }
public slots:
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData);
QNetworkReply* get(QNetworkRequest &request);
void sendRequest();
public slots:
// Setters
void setUrl(QString url);
signals:
void urlChanged(QString url);
private:
int port_{0};
QString scheme_{"https"};
QString url_{""};
QString finalUrl_{""};
QQuickView view_;
};
#endif // DUMMYREQUESTMANAGER_H
DummyRequestManager.cpp
#include "DummyRequestManager.h"
#include <QQmlContext>
#include <QNetworkReply>
#include <QJsonObject>
DummyRequestManager::DummyRequestManager()
{
connect(this, &DummyRequestManager::urlChanged, this, [=](){
QStringList split = url_.split(":");
QString addr = "";
if(split.size() == 2)
{
addr = split.first();
port_ = split.last().toInt();
finalUrl_ = QString("https://%1:%2/api").arg(addr).arg(QString::number(port_));
qDebug() << finalUrl_;
}
});
}
void DummyRequestManager::init()
{
port_ = 8080;
// WARNING Code below is supposed to be only for testing, it's necessary to add certificate
#ifndef QT_NO_SSL
QSslConfiguration sslConf = QSslConfiguration::defaultConfiguration();
sslConf.setPeerVerifyMode(QSslSocket::VerifyNone);
QSslConfiguration::setDefaultConfiguration(sslConf);
#endif
scheme_ = "https";
view_.rootContext()->setContextProperty("dummy", this);
view_.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view_.show();
}
QNetworkReply *DummyRequestManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
{
return QNetworkAccessManager::createRequest(op, request, outgoingData);
}
QNetworkReply *DummyRequestManager::get(QNetworkRequest &request)
{
qDebug() << "request url" << request.url();
request.setRawHeader("accept", "application/json");
request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
return QNetworkAccessManager::get(request);
}
void DummyRequestManager::sendRequest()
{
QUrl url(finalUrl_ + QString("/info"));
url.setScheme(scheme_);
QNetworkRequest request(url);
QNetworkReply *reply = get(request);
QObject::connect(reply, &QNetworkReply::finished, [=] {
qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString();;
qDebug() << reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
qDebug().noquote() << doc.toJson(QJsonDocument::Indented);
reply->deleteLater();
});
}
void DummyRequestManager::setUrl(QString url)
{
if (url_ == url)
return;
url_ = url;
emit urlChanged(url_);
}
main.qml
import QtQuick 2.12
Item {
width: 640
height: 480
visible: true
Rectangle {
id: centerRect
width: 200
height: 200
anchors.centerIn: parent
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
dummy.sendRequest()
}
}
Text {
anchors.centerIn: parent
text: qsTr("Send")
color: "white"
}
}
Rectangle {
color: "gray"
width: centerRect.width
height: 50
anchors.top: centerRect.top
anchors.horizontalCenter: centerRect.horizontalCenter
TextInput {
anchors.fill: parent
onTextChanged: {
dummy.url = text
}
}
}
}
Do you know where the problem could be?
Thank you for your help!
EDIT: It do not crash when I remove reply->deleteLater()
in lambda function.
In general the QNetworkAccessManager support is limited in WebAssembly, because the browser is in charge of the actual HTTP request, so SSL handling is not supported, you can not set any client side certs for instance. Also multi thread support is really limited and buggy in Qt (at least 5.14), but you should be fine with a single threaded build. QML apps in general run just fine in WebAssembly in single threaded builds, I know that because I was part of the Felgo efort to bring production apps to wasm, you can test our results (all single threaded) in our web editor https://felgo.com/web-editor or our samples section http://felgo.com/try-wasm
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.