POST data support
[pykit.git] / Publisher.cc
1 // $Id$
2 //
3 // Copyright (C) 2010
4
5 /** \file
6     \brief InternalNetworkAccessManager non-inline non-template implementation */
7
8 #include "Publisher.hh"
9 //#include "Publisher.ih"
10
11 // Custom includes
12 #include <iostream>
13 #include <QNetworkProxy>
14 #include <QVariant>
15 #include "Publisher.hh"
16 #include <boost/python.hpp>
17
18 //#include "Publisher.mpp"
19 #define prefix_
20 ///////////////////////////////cc.p////////////////////////////////////////
21
22 ///////////////////////////////////////////////////////////////////////////
23 // pykit::detail::InternalServerReply
24
25 namespace pykit {
26 namespace detail {
27
28     class InternalServerReply
29         : public QNetworkReply
30     {
31         Q_OBJECT;
32     public:
33         InternalServerReply(QNetworkAccessManager::Operation operation,
34                             QNetworkRequest const & networkRequest,
35                             QIODevice * postData, Publisher * publisher);
36
37         // Forwarded to buffer
38         virtual bool atEnd() const;
39         virtual qint64 bytesAvailable() const;
40         virtual qint64 bytesToWrite() const;
41         virtual bool canReadLine() const;
42         virtual bool isSequential() const;
43         virtual qint64 pos() const;
44         virtual bool reset();
45         virtual bool seek(qint64 pos);
46         virtual qint64 size() const;
47         virtual bool waitForBytesWritten(int msecs);
48         virtual bool waitForReadyRead(int msecs);
49
50         virtual void abort();
51
52         using QNetworkReply::setHeader;
53
54         void clearResponse();
55         QByteArray postData() const;
56         QString postContentType() const;
57
58     protected:
59         virtual qint64 readData(char * data, qint64 maxSize);
60         virtual qint64 writeData(char const * data, qint64 maxSize);
61
62     private slots:
63         void bufferPostData();
64         void postDataReadable();
65         void postDataComplete();
66         void processRequest();
67         void complete();
68
69     private:
70         bool readPostData();
71
72         Publisher * publisher_;
73         QIODevice * postDevice_;
74         QBuffer buffer_;
75         QBuffer postData_;
76     };
77
78 }}
79
80 prefix_ pykit::detail::InternalServerReply::
81 InternalServerReply(QNetworkAccessManager::Operation operation,
82                     QNetworkRequest const & networkRequest, QIODevice * postData,
83                     Publisher * publisher)
84     : publisher_ (publisher), postDevice_ (postData)
85 {
86     setRequest(networkRequest);
87     setOperation(operation);
88     setUrl(networkRequest.url());
89     open(ReadWrite | Unbuffered);
90     buffer_.open(ReadWrite | Unbuffered);
91
92     if (postDevice_)
93         QMetaObject::invokeMethod(this, "bufferPostData", Qt::QueuedConnection);
94     else
95         QMetaObject::invokeMethod(this, "processRequest", Qt::QueuedConnection);
96 }
97
98 prefix_ void pykit::detail::InternalServerReply::bufferPostData()
99 {
100     QVariant cl (request().header(QNetworkRequest::ContentLengthHeader));
101     std::cerr << "postdata contentlength: " << (cl.isValid() ? cl.toString().toStdString() : "invalid") << "\n";
102     connect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
103     connect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
104     postData_.open(WriteOnly);
105     postDataReadable();
106 }
107
108 prefix_ bool pykit::detail::InternalServerReply::readPostData()
109 {
110     while (true) {
111         qint64 n (postDevice_->bytesAvailable());
112         if (n <= 0)
113             n = 2048;
114         QByteArray data;
115         data.resize(n);
116         n  = postDevice_->read(data.data(), n);
117         if (n < 0)
118             return true;
119         else {
120             data.resize(n);
121             postData_.write(data);
122         }
123         if (n == 0)
124             return false;
125     }
126 }
127
128 prefix_ void pykit::detail::InternalServerReply::postDataReadable()
129 {
130     if (readPostData()) {
131         disconnect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
132         disconnect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
133         processRequest();
134     }
135 }
136
137 prefix_ void pykit::detail::InternalServerReply::postDataComplete()
138 {
139     readPostData();
140     disconnect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
141     disconnect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
142     processRequest();
143 }
144
145 prefix_ void pykit::detail::InternalServerReply::processRequest()
146 {
147     Request request (*this);
148     publisher_->publish(request);
149
150     seek(0);
151     setHeader(QNetworkRequest::ContentLengthHeader, QVariant(size()));
152
153     QMetaObject::invokeMethod(this, "complete", Qt::QueuedConnection);
154 }
155
156 prefix_ void pykit::detail::InternalServerReply::complete()
157 {
158     emit readyRead();
159     emit readChannelFinished();
160     emit finished();
161 }
162
163 prefix_ bool pykit::detail::InternalServerReply::atEnd()
164     const
165 { return buffer_.atEnd(); }
166
167 prefix_ qint64 pykit::detail::InternalServerReply::bytesAvailable()
168     const
169 { return buffer_.bytesAvailable(); }
170
171 prefix_ qint64 pykit::detail::InternalServerReply::bytesToWrite()
172     const
173 { return buffer_.bytesToWrite(); }
174
175 prefix_ bool pykit::detail::InternalServerReply::canReadLine()
176     const
177 { return buffer_.canReadLine(); }
178
179 prefix_ bool pykit::detail::InternalServerReply::isSequential()
180     const
181 { return buffer_.isSequential(); }
182
183 prefix_ qint64 pykit::detail::InternalServerReply::pos()
184     const
185 { return buffer_.pos(); }
186
187 prefix_ bool pykit::detail::InternalServerReply::reset()
188 { return buffer_.reset(); }
189
190 prefix_ bool pykit::detail::InternalServerReply::seek(qint64 pos)
191 { return buffer_.seek(pos); }
192
193 prefix_ qint64 pykit::detail::InternalServerReply::size()
194     const
195 { return buffer_.size(); }
196
197 prefix_ bool pykit::detail::InternalServerReply::waitForBytesWritten(int msecs)
198 { return buffer_.waitForBytesWritten(msecs); }
199
200 prefix_ bool pykit::detail::InternalServerReply::waitForReadyRead(int msecs)
201 { return buffer_.waitForReadyRead(msecs); }
202
203 prefix_ qint64 pykit::detail::InternalServerReply::readData(char * data, qint64 maxSize)
204 { return buffer_.read(data,maxSize); }
205
206 prefix_ qint64 pykit::detail::InternalServerReply::writeData(char const * data, qint64 maxSize)
207 { return buffer_.write(data,maxSize); }
208
209 prefix_ void pykit::detail::InternalServerReply::abort()
210 {}
211
212 prefix_ void pykit::detail::InternalServerReply::clearResponse()
213 {
214     buffer_.buffer().clear();
215 }
216
217 prefix_ QByteArray pykit::detail::InternalServerReply::postData()
218     const
219 {
220     return postData_.buffer();
221 }
222
223 prefix_ QString pykit::detail::InternalServerReply::postContentType()
224     const
225 {
226     return request().header(QNetworkRequest::ContentTypeHeader).toString();
227 }
228
229 ///////////////////////////////////////////////////////////////////////////
230 // pykit::Request
231
232 prefix_ void pykit::Request::write(std::string const & data)
233 {
234     reply_.write(QByteArray(data.data(),data.size()));
235 }
236
237 prefix_ void pykit::Request::reset()
238 {
239     reply_.clearResponse();
240 }
241
242 prefix_ void pykit::Request::setContentType(std::string const & contentType)
243 {
244     reply_.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType.c_str()));
245 }
246
247 prefix_ void pykit::Request::setLocation(std::string const & location)
248 {
249     reply_.setHeader(QNetworkRequest::LocationHeader, QVariant(location.c_str()));
250 }
251
252 prefix_ QUrl pykit::Request::url()
253     const
254 {
255     return reply_.url();
256 }
257
258 prefix_ QByteArray pykit::Request::postData()
259     const
260 {
261     return reply_.postData();
262 }
263
264 prefix_ QByteArray pykit::Request::operation()
265     const
266 {
267     static char const * const operationNames[]
268         = { "", "HEAD", "GET", "PUT", "POST", "DELETE", "" };
269     return operationNames[reply_.operation()];
270 }
271
272 prefix_ QString pykit::Request::postContentType()
273     const
274 {
275     return reply_.postContentType();
276 }
277
278 prefix_ pykit::Request::Request(detail::InternalServerReply & reply)
279     : reply_ (reply)
280 {}
281
282 ///////////////////////////////////////////////////////////////////////////
283 // pykit::InternalNetworkAccessManager
284
285 prefix_ pykit::InternalNetworkAccessManager::
286 InternalNetworkAccessManager(QNetworkAccessManager * manager, QObject * parent,
287                              Publisher * publisher)
288     : QNetworkAccessManager(parent), publisher_ (publisher)
289 {
290     setCache(manager->cache());
291     setCookieJar(manager->cookieJar());
292     setProxy(manager->proxy());
293     setProxyFactory(manager->proxyFactory());
294 }
295
296 prefix_ QNetworkReply *
297 pykit::InternalNetworkAccessManager::createRequest(Operation operation,
298                                                         QNetworkRequest const & request,
299                                                         QIODevice * device)
300 {
301     if (request.url().scheme() != "srv")
302         return QNetworkAccessManager::createRequest(operation, request, device);
303
304     return new detail::InternalServerReply(operation, request, device, publisher_);
305 }
306
307 #include "Publisher.moc"
308
309 ///////////////////////////////cc.e////////////////////////////////////////
310 #undef prefix_
311 //#include "Publisher.mpp"
312
313 \f
314 // Local Variables:
315 // mode: c++
316 // fill-column: 100
317 // comment-column: 40
318 // c-file-style: "j32"
319 // indent-tabs-mode: nil
320 // ispell-local-dictionary: "american"
321 // compile-command: "scons -U"
322 // End: