1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qnetworkaccessdebugpipebackend_p.h"
5#include "QtCore/qdatastream.h"
6#include <QCoreApplication>
7#include <QStringList>
8#include <QUrlQuery>
9#include "private/qnoncontiguousbytedevice_p.h"
10
11QT_BEGIN_NAMESPACE
12
13using namespace Qt::StringLiterals;
14
15#ifdef QT_BUILD_INTERNAL
16
17enum {
18 ReadBufferSize = 16384,
19 WriteBufferSize = ReadBufferSize
20};
21
22QStringList QNetworkAccessDebugPipeBackendFactory::supportedSchemes() const
23{
24 return QStringList(QStringLiteral("debugpipe"));
25}
26
27QNetworkAccessBackend *
28QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation op,
29 const QNetworkRequest &request) const
30{
31 // is it an operation we know of?
32 switch (op) {
33 case QNetworkAccessManager::GetOperation:
34 case QNetworkAccessManager::PutOperation:
35 break;
36
37 default:
38 // no, we can't handle this operation
39 return nullptr;
40 }
41
42 QUrl url = request.url();
43 if (url.scheme() == "debugpipe"_L1)
44 return new QNetworkAccessDebugPipeBackend;
45 return nullptr;
46}
47
48QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
49 : QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Networked),
50 bareProtocol(false),
51 hasUploadFinished(false),
52 hasDownloadFinished(false),
53 hasEverythingFinished(false),
54 bytesDownloaded(0),
55 bytesUploaded(0)
56{
57}
58
59QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend()
60{
61 // this is signals disconnect, not network!
62 socket.disconnect(receiver: this); // we're not interested in the signals at this point
63}
64
65void QNetworkAccessDebugPipeBackend::open()
66{
67 socket.connectToHost(hostName: url().host(), port: url().port(defaultPort: 12345));
68 socket.setReadBufferSize(ReadBufferSize);
69
70 // socket ready read -> we can push from socket to downstream
71 connect(asender: &socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
72 connect(asender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), SLOT(socketError()));
73 connect(asender: &socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
74 connect(asender: &socket, SIGNAL(connected()), SLOT(socketConnected()));
75 // socket bytes written -> we can push more from upstream to socket
76 connect(asender: &socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
77
78 bareProtocol = QUrlQuery(url()).queryItemValue(key: "bare"_L1) == "1"_L1;
79
80 if (operation() == QNetworkAccessManager::PutOperation) {
81 createUploadByteDevice();
82 QObject::connect(sender: uploadByteDevice(), SIGNAL(readyRead()), receiver: this,
83 SLOT(uploadReadyReadSlot()));
84 QMetaObject::invokeMethod(obj: this, member: "uploadReadyReadSlot", c: Qt::QueuedConnection);
85 }
86}
87
88void QNetworkAccessDebugPipeBackend::socketReadyRead()
89{
90 readyRead();
91}
92
93qint64 QNetworkAccessDebugPipeBackend::read(char *data, qint64 maxlen)
94{
95 qint64 haveRead = socket.read(data, maxlen);
96
97 if (haveRead == -1) {
98 hasDownloadFinished = true;
99 // this ensures a good last downloadProgress is emitted
100 auto h = headers();
101 h.removeAll(name: QHttpHeaders::WellKnownHeader::ContentLength);
102 setHeaders(std::move(h));
103 possiblyFinish();
104 return haveRead;
105 }
106
107 bytesDownloaded += haveRead;
108 return haveRead;
109}
110
111qint64 QNetworkAccessDebugPipeBackend::bytesAvailable() const
112{
113 return socket.bytesAvailable();
114}
115
116void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
117{
118 pushFromUpstreamToSocket();
119}
120
121void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
122{
123 pushFromUpstreamToSocket();
124}
125
126void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
127{
128 // FIXME
129 if (operation() == QNetworkAccessManager::PutOperation) {
130 if (hasUploadFinished)
131 return;
132
133 forever {
134 if (socket.bytesToWrite() >= WriteBufferSize)
135 return;
136
137 QByteArray data(WriteBufferSize, Qt::Uninitialized);
138 qint64 haveRead = uploadByteDevice()->peek(data: data.data(), maxlen: data.size());
139 if (haveRead == -1) {
140 // EOF
141 hasUploadFinished = true;
142 possiblyFinish();
143 break;
144 } else if (haveRead == 0) {
145 // nothing to read right now, we will be called again later
146 break;
147 } else {
148 qint64 haveWritten;
149 data.truncate(pos: haveRead);
150 haveWritten = socket.write(data: std::move(data));
151
152 if (haveWritten < 0) {
153 // write error!
154 QString msg = QCoreApplication::translate(context: "QNetworkAccessDebugPipeBackend", key: "Write error writing to %1: %2")
155 .arg(args: url().toString(), args: socket.errorString());
156 error(code: QNetworkReply::ProtocolFailure, errorString: msg);
157 finished();
158 return;
159 } else {
160 uploadByteDevice()->skip(maxSize: haveWritten);
161 bytesUploaded += haveWritten;
162 }
163
164 //QCoreApplication::processEvents();
165 }
166 }
167 }
168}
169
170void QNetworkAccessDebugPipeBackend::possiblyFinish()
171{
172 if (hasEverythingFinished)
173 return;
174 hasEverythingFinished = true;
175
176 if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
177 socket.close();
178 finished();
179 } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
180 socket.close();
181 finished();
182 }
183
184
185}
186
187void QNetworkAccessDebugPipeBackend::close()
188{
189 qWarning(msg: "QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());
190 //if (operation() == QNetworkAccessManager::GetOperation)
191 // socket.disconnectFromHost();
192}
193
194
195void QNetworkAccessDebugPipeBackend::socketError()
196{
197 qWarning(msg: "QNetworkAccessDebugPipeBackend::socketError() %d",socket.error());
198 QNetworkReply::NetworkError code;
199 switch (socket.error()) {
200 case QAbstractSocket::RemoteHostClosedError:
201 return; // socketDisconnected will be called
202
203 case QAbstractSocket::NetworkError:
204 code = QNetworkReply::UnknownNetworkError;
205 break;
206
207 default:
208 code = QNetworkReply::ProtocolFailure;
209 break;
210 }
211
212 error(code, errorString: QNetworkAccessDebugPipeBackend::tr(s: "Socket error on %1: %2")
213 .arg(args: url().toString(), args: socket.errorString()));
214 finished();
215 disconnect(sender: &socket, SIGNAL(disconnected()), receiver: this, SLOT(socketDisconnected()));
216
217}
218
219void QNetworkAccessDebugPipeBackend::socketDisconnected()
220{
221 if (socket.bytesToWrite() == 0) {
222 // normal close
223 } else {
224 readyRead(); // @todo this is odd
225 // abnormal close
226 QString msg = QNetworkAccessDebugPipeBackend::tr(s: "Remote host closed the connection prematurely on %1")
227 .arg(a: url().toString());
228 error(code: QNetworkReply::RemoteHostClosedError, errorString: msg);
229 finished();
230 }
231}
232
233void QNetworkAccessDebugPipeBackend::socketConnected()
234{
235}
236
237
238#endif
239
240QT_END_NAMESPACE
241
242#include "moc_qnetworkaccessdebugpipebackend_p.cpp"
243

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/network/access/qnetworkaccessdebugpipebackend.cpp