Implementing a custom QWebEngineUrlSchemeHandler, cannot reply with a QNetworkReply - c++

Using Qt 5.8, we are setting to implement a custom QWebEngineUrlSchemeHandler. Its behaviour should be to issue a GET query and respond with the returned content.
Our understanding is that the content is returned to the web engine through QWebEngineUrlRequestJob::reply second argument, which should derive from QIODevice. And as QNetworkReply derives from it, we expected an instance of this type to be a valid content provider.
A minimal example (not concerned with freeing dynamically allocated memory) would be:
#include <QBuffer>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QWebEngineUrlRequestJob>
#include <QWebEngineUrlSchemeHandler>
class CustomHandler : public QWebEngineUrlSchemeHandler
{
Q_OBJECT
public:
void requestStarted(QWebEngineUrlRequestJob *aRequestJob) override
{
QUrl requestedUrl("http://stackoverflow.com/");
QNetworkRequest *request = new QNetworkRequest(requestedUrl);
QNetworkReply *reply = mManager.get(*request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
// Reply segment
aRequestJob->reply("text/html", reply);
}
public slots:
void slotError(QNetworkReply::NetworkError aError)
{
std::cout << "Error: " << aError << std::endl;
}
private:
QNetworkAccessManager mManager;
};
When executed, this outputs:
Error: 5
The code thus fails by calling the error slot CustomHandler::slotError with QNetworkReply::NetworkError, which corresponds to a canceled operation.
Is there a way to reply with a QNetworkReply, or is it mandatory to wait for the request to complete and then extract its reply content into a QBuffer first?

Theoretically it should work the way you coded it, but in practice it worked reliably for me only when aRequestJob->reply("text/html", reply); was called from the QNetworkReply::finished signal. You can also try to use readyRead signal, then you have to call reply every time you receive the signal, but as far as I remember it wasn't very reliable, so I decided to stick with the finished signal.
You can also move the data to QBuffer upon finished, but for any reason you have to close() the buffer before replying with it, otherwise it is not get read and appears as an empty response.

I reported this QTBUG-106461 5 days ago. The fixes are already merged into Qt dev branch and cherry-picked into several release branches. The example code in this question description should work in Qt WebEngine 6.4.1 or later.
It is unfortunate that developers choose to discuss bugs on StackOverflow and forums, apply workarounds and forget, instead of reporting them upstream. This bug could have been fixed long ago.

to avoid this error you have to insert
reply.deleteLater()
But in my case I don't see any result displayed. Don't know what to do get this working with QNetworkReply

Related

Qt infinite loop in another thread

I am trying to setup an infinite loop in another thread. The purpose is to download some data from an URL, send the data to the main thread and sleep for some seconds:
DataFetcher::DataFetcher(QUrl url, int fetchRateSec) :
url {url},
fetchRateSec {fetchRateSec}
{
}
void DataFetcher::run()
{
QNetworkAccessManager* manager = new QNetworkAccessManager();
QObject::connect(manager, &QNetworkAccessManager::finished, this, &DataFetcher::onReply);
while (true) {
QNetworkRequest req;
req.setUrl(url);
manager->get(req);
qDebug() << "run";
sleep(fetchRateSec);
}
}
void DataFetcher::onReply(QNetworkReply* reply)
{
qDebug() << "repl";
emit fetched(reply->readAll());
}
class DataFetcher : public QThread
{
Q_OBJECT
public:
DataFetcher(QUrl, int);
void run() override;
signals:
void fetched(QString);
private:
const QUrl url;
const int fetchRateSec;
private slots:
void onReply(QNetworkReply*);
};
But onReply is never called.
It is interesting because the qDebug in the while loop is executed as it should.
Im a bit nooby with QT so i might have missed something regarding how to connect the slots / signals but i think i got it right following some other examples.
It looks about right from the accepted answer in How do I write a Qt HTTP GET request?
what could be the problem here?
Per the discussion in the comments, it seems like the whole business of threading is not needed.
One of Qt's strongest attributes is that it is event-driven. You generally create a program by describing what you want to happen (slots) in response to certain events (signals). Explicitly waiting or sleeping is very rare in Qt-based applications (usually only testing), and is generally considered a no-no in event-driven development.
For your specific problem, a solution might look like this. You can create a QTimer in the main thread and connect its timeout signal to a function to make your HTTP request. You can then connect a slot to the QNetworkAccessManager::finished signal, which will run when your response completes. All of this can take place in the main thread, relying on the thread's event loop to manage the callbacks. No need to manage a separate thread yourself, and no looping, sleeping, blocking, or anything like that.

QT: How to download from url while pressing a button

I have this code:
QNetworkAccessManager man;
QNetworkRequest req(QUrl("URL"));
QString ua("HttpRequestDemo/0.1 (Win64) Qt/5.14.0");
req.setHeader(QNetworkRequest::UserAgentHeader, QVariant(ua));
QNetworkReply* reply = man.get(req);
QObject::connect(reply){
QByteArray read = reply->readLine();
QFile out("file.txt");
out.open(QIODevice::WriteOnly|QIODevice::Text);
out.write(read);
out.close();
})
This works on the main.cpp file, using the QCoreApplication, but I want to use the QApplication and download a specific data while pressing a button.
I put the same code on the on_pushButton_clicked() in the mainwindow.cpp file and it didn't even generate the file from the url.
The problem is that man and req go out of scope and are destroyed as soon as your on_pushButton_clicked() function returns, at which point the request probably hasn't even been sent yet.
You need to make sure that these objects outlive the current scope, either by making them members of the window class, or by allocating them on the heap and setting some QObject (maybe also the window class) as the parent.
The problem is that if you put the same code in a method like X you make QNetworkAccessManager a local variable that will be removed instantly that the connection is asynchronous. The solution is to make QNetworkAccessManager an attribute of the class.
*.h
private:
QNetworkAccessManager man;
*.cpp
void Klass::on_pushButton_clicked(){
QNetworkRequest req(QUrl("URL"));
QString ua("HttpRequestDemo/0.1 (Win64) Qt/5.14.2");
req.setHeader(QNetworkRequest::UserAgentHeader, QVariant(ua));
QNetworkReply* reply = man.get(req);
connect(reply, &QNetworkReply::finished, [&]() {
QByteArray read = reply->readAll();
QFile out("file.txt");
out.open(QIODevice::WriteOnly|QIODevice::Text);
out.write(read);
out.close();
reply->close();
reply->deleteLater();
})
}
If you are planning on potentially queuing very many downloads, I strongly recommend using libcurl in your Qt app. I was using QNetworkAccessManager to down 100+ financial quote files, and it would fail downloading ~ 1/3 of the time, and take a while to download. I switched to libcurl, and after figuring out how to get my crypto root certificates setup for https, it runs much faster, and almost never fails. I run it as a dll.
And yes, you will need to make sure the network manager, whether QNetworkManager or curl, doesn't go out of scope upon exiting the button handler. A more conventional pattern, although not necessarily better, is to either have a pointer to e.g. QNetworkManager in your parent class, and new it, or use a std::unique_ptr and std::make_unique (purportedly safer). Creating large objects on the stack can cause problems (in the old days, dare I say, stack overflows), and so is usually done on the heap. In this case, it's not very big, so it doesn't really matter. Alternatively, a form creating big objects might itself be created on the heap.

QTcpSocket in QThread will commitTransaction but when Write is called "Cannot create children for a parent that is in a different thread."

Disclaimer: I am relatively new to Qt and any type of programming that revolves around Threads and Networking. I have also adopted a lot of code from Qt Examples, API, and other online examples.
All code can be found on GitHub. This code is relatively as simple as it can get minus striping out GUI. I figure supplying it this way would help as well versus just pasting the code below.
I want to use and believe I need to use Threads as I need multiple clients send a request to the server, the server run some SQL code, then spit out the results back to the client (basically deriving a MySQL Server, but specific to what I am doing). Right now though, I am just working on learning the workings of it all.
With all that being said, as the Title states.. My client can connect to the server, the server sets up the thread, and will receive data (a String) through the readReady. After the data is read in, for right now I am just trying to echo it back to the client. It will do this, but only once. Then it spits out:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x266cca92ea0), parent's thread is serverThread(0x266cca9ed60), current thread is QThread(0x266cac772e0)
I cannot send any further data to the server unless I have the client reconnect, then after the data is sent, it will do its job but then spit out the same error and cease functioning. I have tried quite a bit of different things, but cannot seem to fix the issue. I even tried setting up a SIGNAL/SLOT for this as suggested in API:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
Anyway, any help would be greatly appreciated! My Code is below..
Server
ServerThread.cpp
// Project
#include "ServerDialog.h"
#include "ServerThread.h"
ServerThread::ServerThread(qintptr _socketDiscriptor, QObject *parent /*= 0*/)
: QThread(parent)
{
socketDiscriptor = _socketDiscriptor;
}
void ServerThread::run()
{
emit threadStarted(socketDiscriptor);
// Start Thread
clientSocket = new QTcpSocket;
// Set SocketDisc
if (!clientSocket->setSocketDescriptor(socketDiscriptor))
{
emit error(clientSocket->error());
return;
}
// Connect Socket and Signal
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(clientSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
//// Loop Thread to Stay Alive for Signals and Slots
exec();
}
void ServerThread::readyRead()
{
QDataStream in(clientSocket);
in.setVersion(QDataStream::Qt_5_7);
in.startTransaction();
QString dataReceived;
in >> dataReceived;
if (!in.commitTransaction())
{
emit readyReadError(socketDiscriptor);
return;
}
emit readyReadMessage(socketDiscriptor, dataReceived);
echoData(dataReceived);
}
void ServerThread::disconnected()
{
emit threadStopped(socketDiscriptor);
clientSocket->disconnect();
clientSocket->deleteLater();
this->exit(0);
}
void ServerThread::echoData(QString &data)
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out << data;
clientSocket->write(block);
}
So in ServerThread.cpp when echoData is called, that is when the error shows up and the Socket ceases functioning.
Any and all help will be appreciated. I know there are a few other posts regarding "Cannot create children for..." in regards to Threads. But I did not find any of them helpful. The one thing that I did find interesting but did not understand was maybe using moveToThread() but a lot of mixed comments on that.
I learn best through code examples along with explanation versus just an explanation or pointer to API. Thank you!
Most of Qt network functions are asynchronous; they do not block the calling thread. There is no need to mess up with threads if you are using QTcpSockets. In fact, creating a thread for every socket is an overkill, since that thread will spend most of its time just waiting for some network operation to finish. Here is how I would implement a single-threaded echo server in Qt:
#include <QtNetwork>
#include <QtCore>
//separate class for the protocol's implementation
class EchoSocket : public QTcpSocket{
Q_OBJECT
public:
explicit EchoSocket(QObject* parent=nullptr):QTcpSocket(parent){
connect(this, &EchoSocket::readyRead, this, &EchoSocket::EchoBack);
connect(this, &EchoSocket::disconnected, this, &EchoSocket::deleteLater);
}
~EchoSocket() = default;
Q_SLOT void EchoBack(){
QByteArray receivedByteArray= readAll();
write(receivedByteArray);
disconnectFromHost();
}
};
class EchoServer : public QTcpServer{
public:
explicit EchoServer(QObject* parent= nullptr):QTcpServer(parent){}
~EchoServer() = default;
//override incomingConnection() and nextPendingConnection()
//to make them deal with EchoSockets instead of QTcpSockets
void incomingConnection(qintptr socketDescriptor){
EchoSocket* socket= new EchoSocket(this);
socket->setSocketDescriptor(socketDescriptor);
addPendingConnection(qobject_cast<QTcpSocket*>(socket));
}
EchoSocket* nextPendingConnection(){
QTcpSocket* ts= QTcpServer::nextPendingConnection();
return qobject_cast<EchoSocket*>(ts);
}
};
int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);
EchoServer echoServer;
echoServer.listen(QHostAddress::Any, 9999);
QObject::connect(&echoServer, &EchoServer::newConnection, [&](){
EchoSocket* socket= echoServer.nextPendingConnection();
qDebug() << "Got new connection from: " << socket->peerAddress().toString();
});
return a.exec();
}
#include "main.moc"
Notes:
This server has the ability to handle more than one client at the same time, since there is no blocking. The thread will just respond to the event that happens with the appropriate action; So, if that event was a new connection, it will create a new EchoSocket object to handle it and prints a statement out to qDebug(), and if that event was receiving something on a previously created socket, the same thread will echo received data back and close the connection. It will never block on a single connection waiting for data to arrive nor it will block waiting for a new connection to arrive.
Since you mention using some SQL queries in response for some connections later in your project. Please avoid threading since an SQL database connection in Qt can be used only from the thread that created it, see docs here. So, You'll have to either create a new database connection for each thread (and thus for each connection) in your application (and this is beyond just overkill), or switch later to a single threaded design.
In this section, I am explaining why threading does not work for you the way you are doing it:
You should not be declaring slots in your QThread subclass, Instead, use worker QObjects and move them to QThreads as needed.
The quote you have provided in your question is the exact explanation for why you get this warning. The ServerThread instance you create will be living in the main thread (or whatever thread that created it). Now let's consider this line from your code:
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
The signal readyRead() will be emitted from the current ServerThread instance (since the clientSocket object that emits it lives there), However, the receiver object is the current ServerThread instance, But that lives in the main thread. Here is what the documentation says:
If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used.
Now, the main point of Qt::QueuedConnection is executing the slot in the receiver object's thread. This means that, your slots ServerThread::readyRead() and ServerThread::disconnected will get executed in the main thread. This is most likely not what you meant to do, since you'll end up accessing clientSocket from the main thread. After that, any call on clientSocket that results in child QObjects being created will result in the warning you get (you can see that QTcpSocket::write() does this here).
Mixed comments of movetothread are linked mostly to usage of it to move thread object to itself.
The quote hints that the members of QThread aren't designed to be called from worker. Strictly proper way to call signal would be by using worker object model, that was shown in Qt examples and explained a few times on QT-related blogs:
class Worker : public QObject
{
Q_OBJECT
private slots:
void onTimeout()
{
qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
}
};
class Thread : public QThread
{
Q_OBJECT
private:
void run()
{
qDebug()<<"From work thread: "<<currentThreadId();
QTimer timer;
Worker worker;
connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
timer.start(1000);
exec();
}
};
worker constructed inside run() is "property" of the thread it created, so figuratively speaking, it is slaved to its context. The same effect maybe achieved if you create worker in other thread, then move it to this thread before connection was made. When you connect signal to slot of the QThread itself, you connect child thread to thread it was created by.
Use of
connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::DirectConnection);
or creating connection from your thread sometimes seems to achieve proper result, but not in this case, where you try use objects constructed in different threads together. Calling moveToThread(this) in constructor is a thing not recommended to do.

C++ - Qt QObject::connect GET request usage across classes

A quick overview of what's happening: I am trying to do a GET request using Qt's QNetworkAccessManager, but the callback function on my QObject::connect(..) function is not being called. My questions is can I call QObject::connect from one object, but connect to a slot of another object (given that I have a pointer to both the object and the slot) - See below for more details.
My ultimate goal is to POST the data (seeing as it's a login function), I had POST Request code that was ultimately suffering from the same issue - callback function not being called. So I would like to be able to do a simple GET request first, once I have that, I think I'll be fine on my own from there.
I currently have a QMainWindow LoginWindow, with a button that calls a slot doLogin() in the LoginWindow class. This all works as you would expect. LoginWindow also has a public slots function called loginResponse(QNetworkReply* response).
//---LoginWindow.h
...
public slots:
void doLogin();
void loginResponse(QNetworkReply* response)
...
//---LoginWindow.cpp
LoginWindow::LoginWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::LoginWindow)
{
ui->setupUi(this);
ui->username_le->setFocus();
}
void LoginWindow::doLogin()
{
MyProduct::Network network(this);
qDebug() << "Logging in...";
//Here I call network.login from LoginWindow and pass
//references to the Slot I want to use and the LoginWindow itself
network.login(
ui->username_le->text(), //username
ui->password_le->text(), //password
this, //reference to this object (LoginWindow*)
SLOT(loginResponse(QNetworkReply*)) //loginResponse slot
);
}
void LoginWindow::loginResponse(QNetworkReply* response)
{
qDebug() << "Log in complete";
}
Next I have another class, under the MyProduct namespace, called Network. As you can see above, Network has a function called login. Here it is:
void MyProduct::Network login(QString username, QString password, QObject *receiver, const char *slot)
{
QNetworkRequest request(QUrl(API_ROOT + LOGIN_PATH)); //"http://localhost/basic/login.php"
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
//nam = QNetworkAccessManager* declared in the constructor
QObject::connect(nam,SIGNAL(finished(QNetworkReply*)), receiver, slot);
qDebug() << "Posting login data...";
nam->get(request);
}
The goal here is to create a login function in my Network class that can be used and connected in any number of windows (as users may log in from multiple places). But I'm getting no response - LoginWindow::loginResponse is not run.
I see "Logging in..." and "Posting login data" output in the console, but not "Log in complete".
Can anyone please point me in the right direction or tell me I'm crazy or that this is a bad idea?
Thanks in advance!
Note that QNetworkAccessManager operates asynchronously. The get() method does not block while the network operation occurs; it returns immediately. (See the Detailed Description section of the documentation for more info.)
This is pretty typical of Qt's network-related APIs, because you usually don't want your application to freeze while waiting for data to move across a network.
What this means is what your instance, nam, isn't alive long enough for the GET request to actually finish. Your instance of the Product::Network class is deleted immediately after the call to login() because it's allocated on the stack. Although I can't see the code, I'm guessing it cleans up the QNetworkAccessManager as well.
If you extend the lifetime of your network object, you may find that your slot will eventually be invoked.
Also, this is more a matter of preference, but I think it would be cleaner to avoid passing a receiver and a slot to your login() function. I'd recommend declaring your own signals in the Network class as part of its API, and to connect to those in the LoginWindow class.

C++ QNetworkAccessManager (Qt) in conjunction with openGL

I'm really nooby with C++ (as my previous posts mention), however my friend suggested I work with QNetworkAccessManager if I want to send a HTTP GET request to send information.
I am currently working with openGL-es and want to do the following two lines of code to send the get request:
QNetworkAccessManager* netMan = new QNetworkAccessManager(this);
netMan->get(QNetworkRequest(QUrl("something/?userID=1")));
However, it does not like the "this" because it is in the main() method and it does not reference a QObject (I'm guessing QApplication). When I get rid of the "this" my application builds, but just never loads (I put a "printf(1)" at the top which doesn't even run).
Any suggestions or alternatives on how to fix this? Thanks in advance.
-James
The parameter in the QNetworkAccessManager constructor is only needed to specify a QObject based parent which will be responsible for cleaning up (deleting) your object later and isn't necessary if you plan to call delete on it yourself.
I'm not quite sure what you are referring to by "never loads" or where you put a printf but in order to get anything back, you need to actually keep the QNetworkReply pointer that is returned by the call to get().
And to get anything from that, you need an event loop running. If your application is console only (no GUI), you can use a QCoreApplication object.
Try this minimal code:
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QNetworkAccessManager *netMan = new QNetworkAccessManager();
QNetworkReply *reply = netMan->get(QNetworkRequest(QUrl("http://google.com")));
a.connect(reply, SIGNAL(finished()), SLOT(quit()));
a.exec();
qDebug() << reply->readAll();
delete netMan;
}