KIO

connection.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
4 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
5 SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
6 SPDX-FileCopyrightText: 2024 Harald Sitter <sitter@kde.org>
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9*/
10
11#include "connection_p.h"
12#include "connectionbackend_p.h"
13#include "kiocoredebug.h"
14#include <QDebug>
15
16#include <cerrno>
17
18using namespace KIO;
19
20void ConnectionPrivate::dequeue()
21{
22 if (!backend || suspended) {
23 return;
24 }
25
26 for (const Task &task : std::as_const(outgoingTasks)) {
27 q->sendnow(task.cmd, task.data);
28 }
29 outgoingTasks.clear();
30
31 if (!incomingTasks.isEmpty()) {
32 Q_EMIT q->readyRead();
33 }
34}
35
36void ConnectionPrivate::commandReceived(const Task &task)
37{
38 // qDebug() << this << "Command" << task.cmd << "added to the queue";
39 if (!suspended && incomingTasks.isEmpty() && readMode == Connection::ReadMode::EventDriven) {
40 auto dequeueFunc = [this]() {
41 dequeue();
42 };
44 }
45 incomingTasks.append(task);
46}
47
48void ConnectionPrivate::disconnected()
49{
50 q->close();
51 if (readMode == Connection::ReadMode::EventDriven) {
52 QMetaObject::invokeMethod(q, &Connection::readyRead, Qt::QueuedConnection);
53 }
54}
55
56void ConnectionPrivate::setBackend(ConnectionBackend *b)
57{
58 delete backend;
59 backend = b;
60 if (backend) {
61 q->connect(backend, &ConnectionBackend::commandReceived, q, [this](const Task &task) {
62 commandReceived(task);
63 });
64 q->connect(backend, &ConnectionBackend::disconnected, q, [this]() {
65 disconnected();
66 });
67 backend->setSuspended(suspended);
68 }
69}
70
71Connection::Connection(Type type, QObject *parent)
72 : QObject(parent)
73 , d(new ConnectionPrivate)
74 , m_type(type)
75{
76 d->q = this;
77}
78
79Connection::~Connection()
80{
81 close();
82}
83
84void Connection::suspend()
85{
86 // qDebug() << this << "Suspended";
87 d->suspended = true;
88 if (d->backend) {
89 d->backend->setSuspended(true);
90 }
91}
92
93void Connection::resume()
94{
95 // send any outgoing or incoming commands that may be in queue
96 if (d->readMode == Connection::ReadMode::EventDriven) {
97 auto dequeueFunc = [this]() {
98 d->dequeue();
99 };
101 }
102
103 // qDebug() << this << "Resumed";
104 d->suspended = false;
105 if (d->backend) {
106 d->backend->setSuspended(false);
107 }
108}
109
110void Connection::close()
111{
112 if (d->backend) {
113 d->backend->disconnect(this);
114 d->backend->deleteLater();
115 d->backend = nullptr;
116 }
117 d->outgoingTasks.clear();
118 d->incomingTasks.clear();
119}
120
121bool Connection::isConnected() const
122{
123 return d->backend && d->backend->state == ConnectionBackend::Connected;
124}
125
126bool Connection::inited() const
127{
128 return d->backend;
129}
130
131bool Connection::suspended() const
132{
133 return d->suspended;
134}
135
136void Connection::connectToRemote(const QUrl &address)
137{
138 // qDebug() << "Connection requested to" << address;
139 const QString scheme = address.scheme();
140
141 if (scheme == QLatin1String("local")) {
142 d->setBackend(new ConnectionBackend(this));
143 } else {
144 qCWarning(KIO_CORE) << "Unknown protocol requested:" << scheme << "(" << address << ")";
145 Q_ASSERT(0);
146 return;
147 }
148
149 // connection succeeded
150 if (!d->backend->connectToRemote(address)) {
151 // qCWarning(KIO_CORE) << "could not connect to" << address << "using scheme" << scheme;
152 delete d->backend;
153 d->backend = nullptr;
154 return;
155 }
156
157 d->dequeue();
158}
159
160bool Connection::send(int cmd, const QByteArray &data)
161{
162 // Remember that a Connection instance exists in the Application and the Worker. If the application terminates
163 // we potentially get disconnected while looping on data to send in the worker, terminate the worker when this
164 // happens. Specifically while reading a possible answer from the Application we may get socketDisconnected()
165 // we'll never get an answer in that case.
166 if (m_type == Type::Worker && !inited()) {
167 qCWarning(KIO_CORE) << "Connection::send() called with connection not inited";
168 return false;
169 }
170 if (!inited() || !d->outgoingTasks.isEmpty()) {
171 Task task;
172 task.cmd = cmd;
173 task.data = data;
174 d->outgoingTasks.append(std::move(task));
175 return true;
176 } else {
177 return sendnow(cmd, data);
178 }
179}
180
181bool Connection::sendnow(int cmd, const QByteArray &data)
182{
183 if (!d->backend) {
184 qCWarning(KIO_CORE) << "Connection::sendnow has no backend";
185 return false;
186 }
187
188 if (data.size() > 0xffffff) {
189 qCWarning(KIO_CORE) << "Connection::sendnow too much data";
190 return false;
191 }
192
193 if (!isConnected()) {
194 qCWarning(KIO_CORE) << "Connection::sendnow not connected";
195 return false;
196 }
197
198 // qDebug() << this << "Sending command" << cmd << "of size" << data.size();
199 return d->backend->sendCommand(cmd, data);
200}
201
202bool Connection::hasTaskAvailable() const
203{
204 return !d->incomingTasks.isEmpty();
205}
206
207bool Connection::waitForIncomingTask(int ms)
208{
209 if (!isConnected()) {
210 return false;
211 }
212
213 if (d->backend) {
214 return d->backend->waitForIncomingTask(ms);
215 }
216 return false;
217}
218
219int Connection::read(int *_cmd, QByteArray &data)
220{
221 // if it's still empty, then it's an error
222 if (d->incomingTasks.isEmpty()) {
223 // qCWarning(KIO_CORE) << this << "Task list is empty!";
224 return -1;
225 }
226 const Task &task = d->incomingTasks.constFirst();
227 // qDebug() << this << "Command" << task.cmd << "removed from the queue (size" << task.data.size() << ")";
228 *_cmd = task.cmd;
229 data = task.data;
230
231 d->incomingTasks.removeFirst();
232
233 // if we didn't empty our reading queue, emit again
234 if (!d->suspended && !d->incomingTasks.isEmpty() && d->readMode == Connection::ReadMode::EventDriven) {
235 auto dequeueFunc = [this]() {
236 d->dequeue();
237 };
239 }
240
241 return data.size();
242}
243
244void Connection::setReadMode(ReadMode readMode)
245{
246 d->readMode = readMode;
247}
248
249#include "moc_connection_p.cpp"
Type type(const QSqlDatabase &db)
A namespace for KIO globals.
PostalAddress address(const QVariant &location)
const QList< QKeySequence > & close()
QByteArray & append(QByteArrayView data)
qsizetype size() const const
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QueuedConnection
QTaskBuilder< Task > task(Task &&task)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.