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