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 : QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Networked),
84 bareProtocol(false),
85 hasUploadFinished(false),
86 hasDownloadFinished(false),
87 hasEverythingFinished(false),
88 bytesDownloaded(0),
89 bytesUploaded(0)
90{
91}
92
93QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend()
94{
95 // this is signals disconnect, not network!
96 socket.disconnect(this); // we're not interested in the signals at this point
97}
98
99void QNetworkAccessDebugPipeBackend::open()
100{
101 socket.connectToHost(url().host(), url().port(12345));
102 socket.setReadBufferSize(ReadBufferSize);
103
104 // socket ready read -> we can push from socket to downstream
105 connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
106 connect(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), SLOT(socketError()));
107 connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
108 connect(&socket, SIGNAL(connected()), SLOT(socketConnected()));
109 // socket bytes written -> we can push more from upstream to socket
110 connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
111
112 bareProtocol = QUrlQuery(url()).queryItemValue(QLatin1String("bare")) == QLatin1String("1");
113
114 if (operation() == QNetworkAccessManager::PutOperation) {
115 createUploadByteDevice();
116 QObject::connect(uploadByteDevice(), SIGNAL(readyRead()), this,
117 SLOT(uploadReadyReadSlot()));
118 QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
119 }
120}
121
122void QNetworkAccessDebugPipeBackend::socketReadyRead()
123{
124 readyRead();
125}
126
127qint64 QNetworkAccessDebugPipeBackend::read(char *data, qint64 maxlen)
128{
129 qint64 haveRead = socket.read(data, maxlen);
130
131 if (haveRead == -1) {
132 hasDownloadFinished = true;
133 // this ensures a good last downloadProgress is emitted
134 setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
135 possiblyFinish();
136 return haveRead;
137 }
138
139 bytesDownloaded += haveRead;
140 return haveRead;
141}
142
143qint64 QNetworkAccessDebugPipeBackend::bytesAvailable() const
144{
145 return socket.bytesAvailable();
146}
147
148void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
149{
150 pushFromUpstreamToSocket();
151}
152
153void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
154{
155 pushFromUpstreamToSocket();
156}
157
158void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
159{
160 // FIXME
161 if (operation() == QNetworkAccessManager::PutOperation) {
162 if (hasUploadFinished)
163 return;
164
165 forever {
166 if (socket.bytesToWrite() >= WriteBufferSize)
167 return;
168
169 QByteArray data(WriteBufferSize, Qt::Uninitialized);
170 qint64 haveRead = uploadByteDevice()->peek(data.data(), data.size());
171 if (haveRead == -1) {
172 // EOF
173 hasUploadFinished = true;
174 possiblyFinish();
175 break;
176 } else if (haveRead == 0) {
177 // nothing to read right now, we will be called again later
178 break;
179 } else {
180 qint64 haveWritten;
181 data.truncate(haveRead);
182 haveWritten = socket.write(std::move(data));
183
184 if (haveWritten < 0) {
185 // write error!
186 QString msg = QCoreApplication::translate("QNetworkAccessDebugPipeBackend", "Write error writing to %1: %2")
187 .arg(url().toString(), socket.errorString());
188 error(QNetworkReply::ProtocolFailure, msg);
189 finished();
190 return;
191 } else {
192 uploadByteDevice()->skip(haveWritten);
193 bytesUploaded += haveWritten;
194 }
195
196 //QCoreApplication::processEvents();
197 }
198 }
199 }
200}
201
202void QNetworkAccessDebugPipeBackend::possiblyFinish()
203{
204 if (hasEverythingFinished)
205 return;
206 hasEverythingFinished = true;
207
208 if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
209 socket.close();
210 finished();
211 } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
212 socket.close();
213 finished();
214 }
215
216
217}
218
219void QNetworkAccessDebugPipeBackend::close()
220{
221 qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
222 //if (operation() == QNetworkAccessManager::GetOperation)
223 // socket.disconnectFromHost();
224}
225
226
227void QNetworkAccessDebugPipeBackend::socketError()
228{
229 qWarning("QNetworkAccessDebugPipeBackend::socketError() %d",socket.error());
230 QNetworkReply::NetworkError code;
231 switch (socket.error()) {
232 case QAbstractSocket::RemoteHostClosedError:
233 return; // socketDisconnected will be called
234
235 case QAbstractSocket::NetworkError:
236 code = QNetworkReply::UnknownNetworkError;
237 break;
238
239 default:
240 code = QNetworkReply::ProtocolFailure;
241 break;
242 }
243
244 error(code, QNetworkAccessDebugPipeBackend::tr("Socket error on %1: %2")
245 .arg(url().toString(), socket.errorString()));
246 finished();
247 disconnect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
248
249}
250
251void QNetworkAccessDebugPipeBackend::socketDisconnected()
252{
253 if (socket.bytesToWrite() == 0) {
254 // normal close
255 } else {
256 readyRead(); // @todo this is odd
257 // abnormal close
258 QString msg = QNetworkAccessDebugPipeBackend::tr("Remote host closed the connection prematurely on %1")
259 .arg(url().toString());
260 error(QNetworkReply::RemoteHostClosedError, msg);
261 finished();
262 }
263}
264
265void QNetworkAccessDebugPipeBackend::socketConnected()
266{
267}
268
269
270#endif
271
272QT_END_NAMESPACE
273