typo fix
[pykit.git] / Publisher.cc
index 02c3521..30a6789 100644 (file)
@@ -32,7 +32,7 @@ namespace detail {
     public:
         InternalServerReply(QNetworkAccessManager::Operation operation,
                             QNetworkRequest const & networkRequest,
-                            Publisher * publisher);
+                            QIODevice * postData, Publisher * publisher);
 
         // Forwarded to buffer
         virtual bool atEnd() const;
@@ -50,28 +50,40 @@ namespace detail {
         virtual void abort();
 
         using QNetworkReply::setHeader;
+        using QNetworkReply::setRawHeader;
+        using QNetworkReply::setAttribute;
 
         void clearResponse();
+        QByteArray postData() const;
+        QString postContentType() const;
 
     protected:
-        qint64 readData(char * data, qint64 maxSize);
-        qint64 writeData(char const * data, qint64 maxSize);
-
-    signals:
-        void initSignal();
+        virtual qint64 readData(char * data, qint64 maxSize);
+        virtual qint64 writeData(char const * data, qint64 maxSize);
 
     private slots:
-        void initSlot();
+        void bufferPostData();
+        void postDataReadable();
+        void postDataComplete();
+        void processRequest();
+        void complete();
 
     private:
+        bool readPostData();
+
+        Publisher * publisher_;
+        QIODevice * postDevice_;
         QBuffer buffer_;
+        QBuffer postData_;
     };
 
 }}
 
 prefix_ pykit::detail::InternalServerReply::
 InternalServerReply(QNetworkAccessManager::Operation operation,
-                    QNetworkRequest const & networkRequest, Publisher * publisher)
+                    QNetworkRequest const & networkRequest, QIODevice * postData,
+                    Publisher * publisher)
+    : publisher_ (publisher), postDevice_ (postData)
 {
     setRequest(networkRequest);
     setOperation(operation);
@@ -79,23 +91,70 @@ InternalServerReply(QNetworkAccessManager::Operation operation,
     open(ReadWrite | Unbuffered);
     buffer_.open(ReadWrite | Unbuffered);
 
+    if (postDevice_)
+        QMetaObject::invokeMethod(this, "bufferPostData", Qt::QueuedConnection);
+    else
+        QMetaObject::invokeMethod(this, "processRequest", Qt::QueuedConnection);
+}
+
+prefix_ void pykit::detail::InternalServerReply::bufferPostData()
+{
+    QVariant cl (request().header(QNetworkRequest::ContentLengthHeader));
+    connect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
+    connect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
+    postData_.open(WriteOnly);
+    postDataReadable();
+}
+
+prefix_ bool pykit::detail::InternalServerReply::readPostData()
+{
+    while (true) {
+        qint64 n (postDevice_->bytesAvailable());
+        if (n <= 0)
+            n = 2048;
+        QByteArray data;
+        data.resize(n);
+        n  = postDevice_->read(data.data(), n);
+        if (n < 0)
+            return true;
+        else {
+            data.resize(n);
+            postData_.write(data);
+        }
+        if (n == 0)
+            return false;
+    }
+}
+
+prefix_ void pykit::detail::InternalServerReply::postDataReadable()
+{
+    if (readPostData()) {
+        disconnect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
+        disconnect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
+        processRequest();
+    }
+}
+
+prefix_ void pykit::detail::InternalServerReply::postDataComplete()
+{
+    readPostData();
+    disconnect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
+    disconnect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
+    processRequest();
+}
+
+prefix_ void pykit::detail::InternalServerReply::processRequest()
+{
     Request request (*this);
-    publisher->publish(request);
+    publisher_->publish(request);
 
     seek(0);
     setHeader(QNetworkRequest::ContentLengthHeader, QVariant(size()));
-    setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/html; charset=UTF-8"));
 
-    // The following rigamole is needed to delay emitting readyRead() / finished() after until the
-    // connections to those signals have been set up.
-    //
-    // We use a queued signal here. This will call initSlot after processing has returned to the
-    // event loop.
-    connect(this, SIGNAL(initSignal()), this, SLOT(initSlot()), Qt::QueuedConnection);
-    emit initSignal();
+    QMetaObject::invokeMethod(this, "complete", Qt::QueuedConnection);
 }
 
-prefix_ void pykit::detail::InternalServerReply::initSlot()
+prefix_ void pykit::detail::InternalServerReply::complete()
 {
     emit readyRead();
     emit finished();
@@ -142,7 +201,12 @@ prefix_ bool pykit::detail::InternalServerReply::waitForReadyRead(int msecs)
 { return buffer_.waitForReadyRead(msecs); }
 
 prefix_ qint64 pykit::detail::InternalServerReply::readData(char * data, qint64 maxSize)
-{ return buffer_.read(data,maxSize); }
+{
+    qint64 n (buffer_.read(data,maxSize));
+    if (buffer_.atEnd())
+        emit readChannelFinished();
+    return n;
+}
 
 prefix_ qint64 pykit::detail::InternalServerReply::writeData(char const * data, qint64 maxSize)
 { return buffer_.write(data,maxSize); }
@@ -155,12 +219,24 @@ prefix_ void pykit::detail::InternalServerReply::clearResponse()
     buffer_.buffer().clear();
 }
 
+prefix_ QByteArray pykit::detail::InternalServerReply::postData()
+    const
+{
+    return postData_.buffer();
+}
+
+prefix_ QString pykit::detail::InternalServerReply::postContentType()
+    const
+{
+    return request().header(QNetworkRequest::ContentTypeHeader).toString();
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // pykit::Request
 
 prefix_ void pykit::Request::write(std::string const & data)
 {
-    reply_.write(data.c_str());
+    reply_.write(QByteArray(data.data(),data.size()));
 }
 
 prefix_ void pykit::Request::reset()
@@ -178,12 +254,42 @@ prefix_ void pykit::Request::setLocation(std::string const & location)
     reply_.setHeader(QNetworkRequest::LocationHeader, QVariant(location.c_str()));
 }
 
+prefix_ void pykit::Request::setHeader(std::string const & header, std::string const & value)
+{
+    reply_.setRawHeader(header.c_str(), value.c_str());
+}
+
+prefix_ void pykit::Request::setStatusCode(int code)
+{
+    reply_.setAttribute(QNetworkRequest::HttpStatusCodeAttribute, code);
+}
+
 prefix_ QUrl pykit::Request::url()
     const
 {
     return reply_.url();
 }
 
+prefix_ QByteArray pykit::Request::postData()
+    const
+{
+    return reply_.postData();
+}
+
+prefix_ QByteArray pykit::Request::operation()
+    const
+{
+    static char const * const operationNames[]
+        = { "", "HEAD", "GET", "PUT", "POST", "DELETE", "" };
+    return operationNames[reply_.operation()];
+}
+
+prefix_ QString pykit::Request::postContentType()
+    const
+{
+    return reply_.postContentType();
+}
+
 prefix_ pykit::Request::Request(detail::InternalServerReply & reply)
     : reply_ (reply)
 {}
@@ -207,10 +313,9 @@ pykit::InternalNetworkAccessManager::createRequest(Operation operation,
                                                         QNetworkRequest const & request,
                                                         QIODevice * device)
 {
-    if (request.url().scheme() != "srv")
+    if (request.url().host() != "pykit")
         return QNetworkAccessManager::createRequest(operation, request, device);
-
-    return new detail::InternalServerReply(operation, request, publisher_);
+    return new detail::InternalServerReply(operation, request, device, publisher_);
 }
 
 #include "Publisher.moc"