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 setHeader(header: QNetworkRequest::ContentLengthHeader, value: QVariant());
101 possiblyFinish();
102 return haveRead;
103 }
104
105 bytesDownloaded += haveRead;
106 return haveRead;
107}
108
109qint64 QNetworkAccessDebugPipeBackend::bytesAvailable() const
110{
111 return socket.bytesAvailable();
112}
113
114void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
115{
116 pushFromUpstreamToSocket();
117}
118
119void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
120{
121 pushFromUpstreamToSocket();
122}
123
124void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
125{
126 // FIXME
127 if (operation() == QNetworkAccessManager::PutOperation) {
128 if (hasUploadFinished)
129 return;
130
131 forever {
132 if (socket.bytesToWrite() >= WriteBufferSize)
133 return;
134
135 QByteArray data(WriteBufferSize, Qt::Uninitialized);
136 qint64 haveRead = uploadByteDevice()->peek(data: data.data(), maxlen: data.size());
137 if (haveRead == -1) {
138 // EOF
139 hasUploadFinished = true;
140 possiblyFinish();
141 break;
142 } else if (haveRead == 0) {
143 // nothing to read right now, we will be called again later
144 break;
145 } else {
146 qint64 haveWritten;
147 data.truncate(pos: haveRead);
148 haveWritten = socket.write(data: std::move(data));
149
150 if (haveWritten < 0) {
151 // write error!
152 QString msg = QCoreApplication::translate(context: "QNetworkAccessDebugPipeBackend", key: "Write error writing to %1: %2")
153 .arg(args: url().toString(), args: socket.errorString());
154 error(code: QNetworkReply::ProtocolFailure, errorString: msg);
155 finished();
156 return;
157 } else {
158 uploadByteDevice()->skip(maxSize: haveWritten);
159 bytesUploaded += haveWritten;
160 }
161
162 //QCoreApplication::processEvents();
163 }
164 }
165 }
166}
167
168void QNetworkAccessDebugPipeBackend::possiblyFinish()
169{
170 if (hasEverythingFinished)
171 return;
172 hasEverythingFinished = true;
173
174 if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
175 socket.close();
176 finished();
177 } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
178 socket.close();
179 finished();
180 }
181
182
183}
184
185void QNetworkAccessDebugPipeBackend::close()
186{
187 qWarning(msg: "QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
188 //if (operation() == QNetworkAccessManager::GetOperation)
189 // socket.disconnectFromHost();
190}
191
192
193void QNetworkAccessDebugPipeBackend::socketError()
194{
195 qWarning(msg: "QNetworkAccessDebugPipeBackend::socketError() %d",socket.error());
196 QNetworkReply::NetworkError code;
197 switch (socket.error()) {
198 case QAbstractSocket::RemoteHostClosedError:
199 return; // socketDisconnected will be called
200
201 case QAbstractSocket::NetworkError:
202 code = QNetworkReply::UnknownNetworkError;
203 break;
204
205 default:
206 code = QNetworkReply::ProtocolFailure;
207 break;
208 }
209
210 error(code, errorString: QNetworkAccessDebugPipeBackend::tr(s: "Socket error on %1: %2")
211 .arg(args: url().toString(), args: socket.errorString()));
212 finished();
213 disconnect(sender: &socket, SIGNAL(disconnected()), receiver: this, SLOT(socketDisconnected()));
214
215}
216
217void QNetworkAccessDebugPipeBackend::socketDisconnected()
218{
219 if (socket.bytesToWrite() == 0) {
220 // normal close
221 } else {
222 readyRead(); // @todo this is odd
223 // abnormal close
224 QString msg = QNetworkAccessDebugPipeBackend::tr(s: "Remote host closed the connection prematurely on %1")
225 .arg(a: url().toString());
226 error(code: QNetworkReply::RemoteHostClosedError, errorString: msg);
227 finished();
228 }
229}
230
231void QNetworkAccessDebugPipeBackend::socketConnected()
232{
233}
234
235
236#endif
237
238QT_END_NAMESPACE
239
240#include "moc_qnetworkaccessdebugpipebackend_p.cpp"
241

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