src/network/access/qnetworkaccessdebugpipebackend.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessdebugpipebackend_p.h"
+#include "QtCore/qdatastream.h"
+#include <QCoreApplication>
+#include "private/qnoncontiguousbytedevice_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_BUILD_INTERNAL
+
+enum {
+    ReadBufferSize = 16384,
+    WriteBufferSize = ReadBufferSize
+};
+
+QNetworkAccessBackend *
+QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation op,
+                                              const QNetworkRequest &request) const
+{
+    // is it an operation we know of?
+    switch (op) {
+    case QNetworkAccessManager::GetOperation:
+    case QNetworkAccessManager::PutOperation:
+        break;
+
+    default:
+        // no, we can't handle this operation
+        return 0;
+    }
+
+    QUrl url = request.url();
+    if (url.scheme() == QLatin1String("debugpipe"))
+        return new QNetworkAccessDebugPipeBackend;
+    return 0;
+}
+
+QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
+    : bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false),
+    hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0)
+{
+}
+
+QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend()
+{
+    // this is signals disconnect, not network!
+    socket.disconnect(this);    // we're not interested in the signals at this point
+}
+
+void QNetworkAccessDebugPipeBackend::open()
+{
+    socket.connectToHost(url().host(), url().port(12345));
+    socket.setReadBufferSize(ReadBufferSize);
+
+    // socket ready read -> we can push from socket to downstream
+    connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
+    connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError()));
+    connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
+    connect(&socket, SIGNAL(connected()), SLOT(socketConnected()));
+    // socket bytes written -> we can push more from upstream to socket
+    connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
+
+    bareProtocol = url().queryItemValue(QLatin1String("bare")) == QLatin1String("1");
+
+    if (operation() == QNetworkAccessManager::PutOperation) {
+        uploadByteDevice = createUploadByteDevice();
+        QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
+        QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
+    }
+}
+
+void QNetworkAccessDebugPipeBackend::socketReadyRead()
+{
+    pushFromSocketToDownstream();
+}
+
+void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
+{
+    pushFromSocketToDownstream();
+}
+
+void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
+{
+    pushFromUpstreamToSocket();
+}
+
+void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
+{
+    pushFromUpstreamToSocket();
+}
+
+void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
+{
+    QByteArray buffer;
+
+    if (socket.state() == QAbstractSocket::ConnectingState) {
+        return;
+    }
+
+    forever {
+        if (hasDownloadFinished)
+            return;
+
+        buffer.resize(ReadBufferSize);
+        qint64 haveRead = socket.read(buffer.data(), ReadBufferSize);
+        
+        if (haveRead == -1) {
+            hasDownloadFinished = true;
+            // this ensures a good last downloadProgress is emitted
+            setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
+            possiblyFinish();
+            break;
+        } else if (haveRead == 0) {
+            break;
+        } else {
+            // have read something
+            buffer.resize(haveRead);
+            bytesDownloaded += haveRead;
+
+            QByteDataBuffer list;
+            list.append(buffer);
+            buffer.clear(); // important because of implicit sharing!
+            writeDownstreamData(list);
+        }
+    }
+}
+
+void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
+{
+    // FIXME
+    if (operation() == QNetworkAccessManager::PutOperation) {
+        if (hasUploadFinished)
+            return;
+
+        forever {
+            if (socket.bytesToWrite() >= WriteBufferSize)
+                return;
+
+            qint64 haveRead;
+            const char *readPointer = uploadByteDevice->readPointer(WriteBufferSize, haveRead);
+            if (haveRead == -1) {
+                // EOF
+                hasUploadFinished = true;
+                emitReplyUploadProgress(bytesUploaded, bytesUploaded);
+                possiblyFinish();
+                break;
+            } else if (haveRead == 0 || readPointer == 0) {
+                // nothing to read right now, we will be called again later
+                break;
+            } else {
+                qint64 haveWritten;
+                haveWritten = socket.write(readPointer, haveRead);
+
+                if (haveWritten < 0) {
+                    // write error!
+                    QString msg = QCoreApplication::translate("QNetworkAccessDebugPipeBackend", "Write error writing to %1: %2")
+                                  .arg(url().toString(), socket.errorString());
+                    error(QNetworkReply::ProtocolFailure, msg);
+                    finished();
+                    return;
+                } else {
+                    uploadByteDevice->advanceReadPointer(haveWritten);
+                    bytesUploaded += haveWritten;
+                    emitReplyUploadProgress(bytesUploaded, -1);
+                }
+
+                //QCoreApplication::processEvents();
+
+            }
+        }
+    }
+}
+
+void QNetworkAccessDebugPipeBackend::possiblyFinish()
+{
+    if (hasEverythingFinished)
+        return;
+    hasEverythingFinished = true;
+
+    if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
+        socket.close();
+        finished();
+    } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
+        socket.close();
+        finished();
+    }
+
+
+}
+
+void QNetworkAccessDebugPipeBackend::closeDownstreamChannel()
+{
+    qWarning() << "QNetworkAccessDebugPipeBackend::closeDownstreamChannel()" << operation();
+    //if (operation() == QNetworkAccessManager::GetOperation)
+    //    socket.disconnectFromHost();
+}
+
+
+void QNetworkAccessDebugPipeBackend::socketError()
+{
+    qWarning() << "QNetworkAccessDebugPipeBackend::socketError()" << socket.error();
+    QNetworkReply::NetworkError code;
+    switch (socket.error()) {
+    case QAbstractSocket::RemoteHostClosedError:
+        return;                 // socketDisconnected will be called
+
+    case QAbstractSocket::NetworkError:
+        code = QNetworkReply::UnknownNetworkError;
+        break;
+
+    default:
+        code = QNetworkReply::ProtocolFailure;
+        break;
+    }
+
+    error(code, QObject::tr("Socket error on %1: %2")
+          .arg(url().toString(), socket.errorString()));
+    finished();
+    disconnect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+
+}
+
+void QNetworkAccessDebugPipeBackend::socketDisconnected()
+{
+    pushFromSocketToDownstream();
+
+    if (socket.bytesToWrite() == 0) {
+        // normal close
+    } else {
+        // abnormal close
+        QString msg = QObject::tr("Remote host closed the connection prematurely on %1")
+                             .arg(url().toString());
+        error(QNetworkReply::RemoteHostClosedError, msg);
+        finished();
+    }
+}
+
+void QNetworkAccessDebugPipeBackend::socketConnected()
+{
+}
+
+bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms)
+{
+    Q_UNUSED(ms);
+    qCritical("QNetworkAccess: Debug pipe backend does not support waitForReadyRead()");
+    return false;
+}
+
+#endif
+
+QT_END_NAMESPACE