add setRawHeader to Rquest API
[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         using QNetworkReply::setRawHeader;
54
55         void clearResponse();
56         QByteArray postData() const;
57         QString postContentType() const;
58
59     protected:
60         virtual qint64 readData(char * data, qint64 maxSize);
61         virtual qint64 writeData(char const * data, qint64 maxSize);
62
63     private slots:
64         void bufferPostData();
65         void postDataReadable();
66         void postDataComplete();
67         void processRequest();
68         void complete();
69
70     private:
71         bool readPostData();
72
73         Publisher * publisher_;
74         QIODevice * postDevice_;
75         QBuffer buffer_;
76         QBuffer postData_;
77     };
78
79 }}
80
81 prefix_ pykit::detail::InternalServerReply::
82 InternalServerReply(QNetworkAccessManager::Operation operation,
83                     QNetworkRequest const & networkRequest, QIODevice * postData,
84                     Publisher * publisher)
85     : publisher_ (publisher), postDevice_ (postData)
86 {
87     setRequest(networkRequest);
88     setOperation(operation);
89     setUrl(networkRequest.url());
90     open(ReadWrite | Unbuffered);
91     buffer_.open(ReadWrite | Unbuffered);
92
93     if (postDevice_)
94         QMetaObject::invokeMethod(this, "bufferPostData", Qt::QueuedConnection);
95     else
96         QMetaObject::invokeMethod(this, "processRequest", Qt::QueuedConnection);
97 }
98
99 prefix_ void pykit::detail::InternalServerReply::bufferPostData()
100 {
101     QVariant cl (request().header(QNetworkRequest::ContentLengthHeader));
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_ void pykit::Request::setHeader(std::string const & header, std::string const & value)
253 {
254     reply_.setRawHeader(header.c_str(), value.c_str());
255 }
256
257 prefix_ QUrl pykit::Request::url()
258     const
259 {
260     return reply_.url();
261 }
262
263 prefix_ QByteArray pykit::Request::postData()
264     const
265 {
266     return reply_.postData();
267 }
268
269 prefix_ QByteArray pykit::Request::operation()
270     const
271 {
272     static char const * const operationNames[]
273         = { "", "HEAD", "GET", "PUT", "POST", "DELETE", "" };
274     return operationNames[reply_.operation()];
275 }
276
277 prefix_ QString pykit::Request::postContentType()
278     const
279 {
280     return reply_.postContentType();
281 }
282
283 prefix_ pykit::Request::Request(detail::InternalServerReply & reply)
284     : reply_ (reply)
285 {}
286
287 ///////////////////////////////////////////////////////////////////////////
288 // pykit::InternalNetworkAccessManager
289
290 prefix_ pykit::InternalNetworkAccessManager::
291 InternalNetworkAccessManager(QNetworkAccessManager * manager, QObject * parent,
292                              Publisher * publisher)
293     : QNetworkAccessManager(parent), publisher_ (publisher)
294 {
295     setCache(manager->cache());
296     setCookieJar(manager->cookieJar());
297     setProxy(manager->proxy());
298     setProxyFactory(manager->proxyFactory());
299 }
300
301 prefix_ QNetworkReply *
302 pykit::InternalNetworkAccessManager::createRequest(Operation operation,
303                                                         QNetworkRequest const & request,
304                                                         QIODevice * device)
305 {
306     if (request.url().host() != "pykit")
307         return QNetworkAccessManager::createRequest(operation, request, device);
308     return new detail::InternalServerReply(operation, request, device, publisher_);
309 }
310
311 #include "Publisher.moc"
312
313 ///////////////////////////////cc.e////////////////////////////////////////
314 #undef prefix_
315 //#include "Publisher.mpp"
316
317 \f
318 // Local Variables:
319 // mode: c++
320 // fill-column: 100
321 // comment-column: 40
322 // c-file-style: "j32"
323 // indent-tabs-mode: nil
324 // ispell-local-dictionary: "american"
325 // compile-command: "scons -U"
326 // End: