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
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "connection_p.h"
11#include "connectionbackend_p.h"
12#include "kiocoredebug.h"
13#include <QDebug>
14
15#include <cerrno>
16
17using namespace KIO;
18
19void ConnectionPrivate::dequeue()
20{
21 if (!backend || suspended) {
22 return;
23 }
24
25 for (const Task &task : std::as_const(outgoingTasks)) {
26 q->sendnow(task.cmd, task.data);
27 }
28 outgoingTasks.clear();
29
30 if (!incomingTasks.isEmpty()) {
31 Q_EMIT q->readyRead();
32 }
33}
34
35void ConnectionPrivate::commandReceived(const Task &task)
36{
37 // qDebug() << this << "Command" << task.cmd << "added to the queue";
38 if (!suspended && incomingTasks.isEmpty() && readMode == Connection::ReadMode::EventDriven) {
39 auto dequeueFunc = [this]() {
40 dequeue();
41 };
43 }
44 incomingTasks.append(task);
45}
46
47void ConnectionPrivate::disconnected()
48{
49 q->close();
50 if (readMode == Connection::ReadMode::EventDriven) {
51 QMetaObject::invokeMethod(q, &Connection::readyRead, Qt::QueuedConnection);
52 }
53}
54
55void ConnectionPrivate::setBackend(ConnectionBackend *b)
56{
57 delete backend;
58 backend = b;
59 if (backend) {
60 q->connect(backend, &ConnectionBackend::commandReceived, q, [this](const Task &task) {
61 commandReceived(task);
62 });
63 q->connect(backend, &ConnectionBackend::disconnected, q, [this]() {
64 disconnected();
65 });
66 backend->setSuspended(suspended);
67 }
68}
69
70Connection::Connection(QObject *parent)
71 : QObject(parent)
72 , d(new ConnectionPrivate)
73{
74 d->q = this;
75}
76
77Connection::~Connection()
78{
79 close();
80}
81
82void Connection::suspend()
83{
84 // qDebug() << this << "Suspended";
85 d->suspended = true;
86 if (d->backend) {
87 d->backend->setSuspended(true);
88 }
89}
90
91void Connection::resume()
92{
93 // send any outgoing or incoming commands that may be in queue
94 if (d->readMode == Connection::ReadMode::EventDriven) {
95 auto dequeueFunc = [this]() {
96 d->dequeue();
97 };
99 }
100
101 // qDebug() << this << "Resumed";
102 d->suspended = false;
103 if (d->backend) {
104 d->backend->setSuspended(false);
105 }
106}
107
108void Connection::close()
109{
110 if (d->backend) {
111 d->backend->disconnect(this);
112 d->backend->deleteLater();
113 d->backend = nullptr;
114 }
115 d->outgoingTasks.clear();
116 d->incomingTasks.clear();
117}
118
119bool Connection::isConnected() const
120{
121 return d->backend && d->backend->state == ConnectionBackend::Connected;
122}
123
124bool Connection::inited() const
125{
126 return d->backend;
127}
128
129bool Connection::suspended() const
130{
131 return d->suspended;
132}
133
134void Connection::connectToRemote(const QUrl &address)
135{
136 // qDebug() << "Connection requested to" << address;
137 const QString scheme = address.scheme();
138
139 if (scheme == QLatin1String("local")) {
140 d->setBackend(new ConnectionBackend(this));
141 } else {
142 qCWarning(KIO_CORE) << "Unknown protocol requested:" << scheme << "(" << address << ")";
143 Q_ASSERT(0);
144 return;
145 }
146
147 // connection succeeded
148 if (!d->backend->connectToRemote(address)) {
149 // qCWarning(KIO_CORE) << "could not connect to" << address << "using scheme" << scheme;
150 delete d->backend;
151 d->backend = nullptr;
152 return;
153 }
154
155 d->dequeue();
156}
157
158QString Connection::errorString() const
159{
160 if (d->backend) {
161 return d->backend->errorString;
162 }
163 return QString();
164}
165
166bool Connection::send(int cmd, const QByteArray &data)
167{
168 if (!inited() || !d->outgoingTasks.isEmpty()) {
169 Task task;
170 task.cmd = cmd;
171 task.data = data;
172 d->outgoingTasks.append(std::move(task));
173 return true;
174 } else {
175 return sendnow(cmd, data);
176 }
177}
178
179bool Connection::sendnow(int cmd, const QByteArray &data)
180{
181 if (!d->backend || data.size() > 0xffffff || !isConnected()) {
182 return false;
183 }
184
185 // qDebug() << this << "Sending command" << cmd << "of size" << data.size();
186 return d->backend->sendCommand(cmd, data);
187}
188
189bool Connection::hasTaskAvailable() const
190{
191 return !d->incomingTasks.isEmpty();
192}
193
194bool Connection::waitForIncomingTask(int ms)
195{
196 if (!isConnected()) {
197 return false;
198 }
199
200 if (d->backend) {
201 return d->backend->waitForIncomingTask(ms);
202 }
203 return false;
204}
205
206int Connection::read(int *_cmd, QByteArray &data)
207{
208 // if it's still empty, then it's an error
209 if (d->incomingTasks.isEmpty()) {
210 // qCWarning(KIO_CORE) << this << "Task list is empty!";
211 return -1;
212 }
213 const Task &task = d->incomingTasks.constFirst();
214 // qDebug() << this << "Command" << task.cmd << "removed from the queue (size" << task.data.size() << ")";
215 *_cmd = task.cmd;
216 data = task.data;
217
218 d->incomingTasks.removeFirst();
219
220 // if we didn't empty our reading queue, emit again
221 if (!d->suspended && !d->incomingTasks.isEmpty() && d->readMode == Connection::ReadMode::EventDriven) {
222 auto dequeueFunc = [this]() {
223 d->dequeue();
224 };
226 }
227
228 return data.size();
229}
230
231void Connection::setReadMode(ReadMode readMode)
232{
233 d->readMode = readMode;
234}
235
236#include "moc_connection_p.cpp"
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 Tue Mar 26 2024 11:18:51 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.