1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qnetworkaccessdebugpipebackend_p.h"
41#include "QtCore/qdatastream.h"
42#include <QCoreApplication>
43#include <QStringList>
44#include <QUrlQuery>
45#include "private/qnoncontiguousbytedevice_p.h"
46
47QT_BEGIN_NAMESPACE
48
49#ifdef QT_BUILD_INTERNAL
50
51enum {
52 ReadBufferSize = 16384,
53 WriteBufferSize = ReadBufferSize
54};
55
56QStringList QNetworkAccessDebugPipeBackendFactory::supportedSchemes() const
57{
58 return QStringList(QStringLiteral("debugpipe"));
59}
60
61QNetworkAccessBackend *
62QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation op,
63 const QNetworkRequest &request) const
64{
65 // is it an operation we know of?
66 switch (op) {
67 case QNetworkAccessManager::GetOperation:
68 case QNetworkAccessManager::PutOperation:
69 break;
70
71 default:
72 // no, we can't handle this operation
73 return nullptr;
74 }
75
76 QUrl url = request.url();
77 if (url.scheme() == QLatin1String("debugpipe"))
78 return new QNetworkAccessDebugPipeBackend;
79 return nullptr;
80}
81
82QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
83 : bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false),
84 hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0)
85{
86}
87
88QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend()
89{
90 // this is signals disconnect, not network!
91 socket.disconnect(receiver: this); // we're not interested in the signals at this point
92}
93
94void QNetworkAccessDebugPipeBackend::open()
95{
96 socket.connectToHost(hostName: url().host(), port: url().port(defaultPort: 12345));
97 socket.setReadBufferSize(ReadBufferSize);
98
99 // socket ready read -> we can push from socket to downstream
100 connect(asender: &socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
101 connect(asender: &socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), SLOT(socketError()));
102 connect(asender: &socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
103 connect(asender: &socket, SIGNAL(connected()), SLOT(socketConnected()));
104 // socket bytes written -> we can push more from upstream to socket
105 connect(asender: &socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
106
107 bareProtocol = QUrlQuery(url()).queryItemValue(key: QLatin1String("bare")) == QLatin1String("1");
108
109 if (operation() == QNetworkAccessManager::PutOperation) {
110 createUploadByteDevice();
111 QObject::connect(sender: uploadByteDevice.data(), SIGNAL(readyRead()), receiver: this, SLOT(uploadReadyReadSlot()));
112 QMetaObject::invokeMethod(obj: this, member: "uploadReadyReadSlot", type: Qt::QueuedConnection);
113 }
114}
115
116void QNetworkAccessDebugPipeBackend::socketReadyRead()
117{
118 pushFromSocketToDownstream();
119}
120
121void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
122{
123 pushFromSocketToDownstream();
124}
125
126void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
127{
128 pushFromUpstreamToSocket();
129}
130
131void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
132{
133 pushFromUpstreamToSocket();
134}
135
136void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
137{
138 QByteArray buffer;
139
140 if (socket.state() == QAbstractSocket::ConnectingState) {
141 return;
142 }
143
144 forever {
145 if (hasDownloadFinished)
146 return;
147
148 buffer.resize(size: ReadBufferSize);
149 qint64 haveRead = socket.read(data: buffer.data(), maxlen: ReadBufferSize);
150
151 if (haveRead == -1) {
152 hasDownloadFinished = true;
153 // this ensures a good last downloadProgress is emitted
154 setHeader(header: QNetworkRequest::ContentLengthHeader, value: QVariant());
155 possiblyFinish();
156 break;
157 } else if (haveRead == 0) {
158 break;
159 } else {
160 // have read something
161 buffer.resize(size: haveRead);
162 bytesDownloaded += haveRead;
163
164 QByteDataBuffer list;
165 list.append(bd: buffer);
166 buffer.clear(); // important because of implicit sharing!
167 writeDownstreamData(list);
168 }
169 }
170}
171
172void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
173{
174 // FIXME
175 if (operation() == QNetworkAccessManager::PutOperation) {
176 if (hasUploadFinished)
177 return;
178
179 forever {
180 if (socket.bytesToWrite() >= WriteBufferSize)
181 return;
182
183 qint64 haveRead;
184 const char *readPointer = uploadByteDevice->readPointer(maximumLength: WriteBufferSize, len&: haveRead);
185 if (haveRead == -1) {
186 // EOF
187 hasUploadFinished = true;
188 emitReplyUploadProgress(bytesSent: bytesUploaded, bytesTotal: bytesUploaded);
189 possiblyFinish();
190 break;
191 } else if (haveRead == 0 || readPointer == nullptr) {
192 // nothing to read right now, we will be called again later
193 break;
194 } else {
195 qint64 haveWritten;
196 haveWritten = socket.write(data: readPointer, len: haveRead);
197
198 if (haveWritten < 0) {
199 // write error!
200 QString msg = QCoreApplication::translate(context: "QNetworkAccessDebugPipeBackend", key: "Write error writing to %1: %2")
201 .arg(args: url().toString(), args: socket.errorString());
202 error(code: QNetworkReply::ProtocolFailure, errorString: msg);
203 finished();
204 return;
205 } else {
206 uploadByteDevice->advanceReadPointer(amount: haveWritten);
207 bytesUploaded += haveWritten;
208 emitReplyUploadProgress(bytesSent: bytesUploaded, bytesTotal: -1);
209 }
210
211 //QCoreApplication::processEvents();
212
213 }
214 }
215 }
216}
217
218void QNetworkAccessDebugPipeBackend::possiblyFinish()
219{
220 if (hasEverythingFinished)
221 return;
222 hasEverythingFinished = true;
223
224 if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
225 socket.close();
226 finished();
227 } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
228 socket.close();
229 finished();
230 }
231
232
233}
234
235void QNetworkAccessDebugPipeBackend::closeDownstreamChannel()
236{
237 qWarning(msg: "QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
238 //if (operation() == QNetworkAccessManager::GetOperation)
239 // socket.disconnectFromHost();
240}
241
242
243void QNetworkAccessDebugPipeBackend::socketError()
244{
245 qWarning(msg: "QNetworkAccessDebugPipeBackend::socketError() %d",socket.error());
246 QNetworkReply::NetworkError code;
247 switch (socket.error()) {
248 case QAbstractSocket::RemoteHostClosedError:
249 return; // socketDisconnected will be called
250
251 case QAbstractSocket::NetworkError:
252 code = QNetworkReply::UnknownNetworkError;
253 break;
254
255 default:
256 code = QNetworkReply::ProtocolFailure;
257 break;
258 }
259
260 error(code, errorString: QNetworkAccessDebugPipeBackend::tr(s: "Socket error on %1: %2")
261 .arg(args: url().toString(), args: socket.errorString()));
262 finished();
263 disconnect(sender: &socket, SIGNAL(disconnected()), receiver: this, SLOT(socketDisconnected()));
264
265}
266
267void QNetworkAccessDebugPipeBackend::socketDisconnected()
268{
269 pushFromSocketToDownstream();
270
271 if (socket.bytesToWrite() == 0) {
272 // normal close
273 } else {
274 // abnormal close
275 QString msg = QNetworkAccessDebugPipeBackend::tr(s: "Remote host closed the connection prematurely on %1")
276 .arg(a: url().toString());
277 error(code: QNetworkReply::RemoteHostClosedError, errorString: msg);
278 finished();
279 }
280}
281
282void QNetworkAccessDebugPipeBackend::socketConnected()
283{
284}
285
286
287#endif
288
289QT_END_NAMESPACE
290

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