replace srv:/// with http://pykit/ for jquery POST 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     connect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
102     connect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
103     postData_.open(WriteOnly);
104     postDataReadable();
105 }
106
107 prefix_ bool pykit::detail::InternalServerReply::readPostData()
108 {
109     while (true) {
110         qint64 n (postDevice_->bytesAvailable());
111         if (n <= 0)
112             n = 2048;
113         QByteArray data;
114         data.resize(n);
115         n  = postDevice_->read(data.data(), n);
116         if (n < 0)
117             return true;
118         else {
119             data.resize(n);
120             postData_.write(data);
121         }
122         if (n == 0)
123             return false;
124     }
125 }
126
127 prefix_ void pykit::detail::InternalServerReply::postDataReadable()
128 {
129     if (readPostData()) {
130         disconnect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
131         disconnect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
132         processRequest();
133     }
134 }
135
136 prefix_ void pykit::detail::InternalServerReply::postDataComplete()
137 {
138     readPostData();
139     disconnect(postDevice_, SIGNAL(readyRead()), this, SLOT(postDataReadable()));
140     disconnect(postDevice_, SIGNAL(readChannelFinished()), this, SLOT(postDataComplete()));
141     processRequest();
142 }
143
144 prefix_ void pykit::detail::InternalServerReply::processRequest()
145 {
146     Request request (*this);
147     publisher_->publish(request);
148
149     seek(0);
150     setHeader(QNetworkRequest::ContentLengthHeader, QVariant(size()));
151
152     QMetaObject::invokeMethod(this, "complete", Qt::QueuedConnection);
153 }
154
155 prefix_ void pykit::detail::InternalServerReply::complete()
156 {
157     emit readyRead();
158     emit readChannelFinished();
159     emit finished();
160 }
161
162 prefix_ bool pykit::detail::InternalServerReply::atEnd()
163     const
164 { return buffer_.atEnd(); }
165
166 prefix_ qint64 pykit::detail::InternalServerReply::bytesAvailable()
167     const
168 { return buffer_.bytesAvailable(); }
169
170 prefix_ qint64 pykit::detail::InternalServerReply::bytesToWrite()
171     const
172 { return buffer_.bytesToWrite(); }
173
174 prefix_ bool pykit::detail::InternalServerReply::canReadLine()
175     const
176 { return buffer_.canReadLine(); }
177
178 prefix_ bool pykit::detail::InternalServerReply::isSequential()
179     const
180 { return buffer_.isSequential(); }
181
182 prefix_ qint64 pykit::detail::InternalServerReply::pos()
183     const
184 { return buffer_.pos(); }
185
186 prefix_ bool pykit::detail::InternalServerReply::reset()
187 { return buffer_.reset(); }
188
189 prefix_ bool pykit::detail::InternalServerReply::seek(qint64 pos)
190 { return buffer_.seek(pos); }
191
192 prefix_ qint64 pykit::detail::InternalServerReply::size()
193     const
194 { return buffer_.size(); }
195
196 prefix_ bool pykit::detail::InternalServerReply::waitForBytesWritten(int msecs)
197 { return buffer_.waitForBytesWritten(msecs); }
198
199 prefix_ bool pykit::detail::InternalServerReply::waitForReadyRead(int msecs)
200 { return buffer_.waitForReadyRead(msecs); }
201
202 prefix_ qint64 pykit::detail::InternalServerReply::readData(char * data, qint64 maxSize)
203 { return buffer_.read(data,maxSize); }
204
205 prefix_ qint64 pykit::detail::InternalServerReply::writeData(char const * data, qint64 maxSize)
206 { return buffer_.write(data,maxSize); }
207
208 prefix_ void pykit::detail::InternalServerReply::abort()
209 {}
210
211 prefix_ void pykit::detail::InternalServerReply::clearResponse()
212 {
213     buffer_.buffer().clear();
214 }
215
216 prefix_ QByteArray pykit::detail::InternalServerReply::postData()
217     const
218 {
219     return postData_.buffer();
220 }
221
222 prefix_ QString pykit::detail::InternalServerReply::postContentType()
223     const
224 {
225     return request().header(QNetworkRequest::ContentTypeHeader).toString();
226 }
227
228 ///////////////////////////////////////////////////////////////////////////
229 // pykit::Request
230
231 prefix_ void pykit::Request::write(std::string const & data)
232 {
233     reply_.write(QByteArray(data.data(),data.size()));
234 }
235
236 prefix_ void pykit::Request::reset()
237 {
238     reply_.clearResponse();
239 }
240
241 prefix_ void pykit::Request::setContentType(std::string const & contentType)
242 {
243     reply_.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType.c_str()));
244 }
245
246 prefix_ void pykit::Request::setLocation(std::string const & location)
247 {
248     reply_.setHeader(QNetworkRequest::LocationHeader, QVariant(location.c_str()));
249 }
250
251 prefix_ QUrl pykit::Request::url()
252     const
253 {
254     return reply_.url();
255 }
256
257 prefix_ QByteArray pykit::Request::postData()
258     const
259 {
260     return reply_.postData();
261 }
262
263 prefix_ QByteArray pykit::Request::operation()
264     const
265 {
266     static char const * const operationNames[]
267         = { "", "HEAD", "GET", "PUT", "POST", "DELETE", "" };
268     return operationNames[reply_.operation()];
269 }
270
271 prefix_ QString pykit::Request::postContentType()
272     const
273 {
274     return reply_.postContentType();
275 }
276
277 prefix_ pykit::Request::Request(detail::InternalServerReply & reply)
278     : reply_ (reply)
279 {}
280
281 ///////////////////////////////////////////////////////////////////////////
282 // pykit::InternalNetworkAccessManager
283
284 prefix_ pykit::InternalNetworkAccessManager::
285 InternalNetworkAccessManager(QNetworkAccessManager * manager, QObject * parent,
286                              Publisher * publisher)
287     : QNetworkAccessManager(parent), publisher_ (publisher)
288 {
289     setCache(manager->cache());
290     setCookieJar(manager->cookieJar());
291     setProxy(manager->proxy());
292     setProxyFactory(manager->proxyFactory());
293 }
294
295 prefix_ QNetworkReply *
296 pykit::InternalNetworkAccessManager::createRequest(Operation operation,
297                                                         QNetworkRequest const & request,
298                                                         QIODevice * device)
299 {
300     if (request.url().host() != "pykit")
301         return QNetworkAccessManager::createRequest(operation, request, device);
302     return new detail::InternalServerReply(operation, request, device, publisher_);
303 }
304
305 #include "Publisher.moc"
306
307 ///////////////////////////////cc.e////////////////////////////////////////
308 #undef prefix_
309 //#include "Publisher.mpp"
310
311 \f
312 // Local Variables:
313 // mode: c++
314 // fill-column: 100
315 // comment-column: 40
316 // c-file-style: "j32"
317 // indent-tabs-mode: nil
318 // ispell-local-dictionary: "american"
319 // compile-command: "scons -U"
320 // End: