Fraxinus  16.5.0-fx-rc1
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxHttpRequestHandler.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 #include "cxHttpRequestHandler.h"
33 
34 #include <iostream>
35 
36 //#include "cxNetworkServiceImpl.h"
37 #include "cxRegisteredService.h"
38 #include <QtConcurrent>
39 #include <QNetworkAccessManager>
40 #include <QHostAddress>
41 #include <qhttpserver.h>
42 #include <qhttprequest.h>
43 #include <qhttpresponse.h>
44 //#include "cxLogger.h"
45 #include "cxScreenVideoProvider.h"
46 #include "cxLogger.h"
47 
48 #include "cxPatientModelService.h"
49 #include "cxRemoteAPI.h"
50 #include <QPixmap>
51 #include <QJsonObject>
52 #include <QJsonDocument>
53 #include <QJsonArray>
54 #include <QJsonValue>
55 #include <QApplication>
56 
57 namespace cx
58 {
59 
61 {
62 }
63 
64 void HttpRequestHandler::handle_request(QHttpRequest *req, QHttpResponse *resp)
65 {
66  mRequests.push_front(RequestType(req,resp));
67  req->storeBody();
68  connect(req, SIGNAL(end()), this, SLOT(onRequestSuccessful()));
69 }
70 
71 void HttpRequestHandler::onRequestSuccessful()
72 {
73  for (QList<RequestType>::iterator iter = mRequests.begin(); iter!=mRequests.end(); )
74  {
75  if (iter->req->successful())
76  {
77  this->handle_complete_request(iter->req, iter->resp);
78  iter = mRequests.erase(iter);
79  }
80  else
81  {
82  ++iter;
83  }
84  }
85 }
86 
87 void HttpRequestHandler::handle_complete_request(QHttpRequest *req, QHttpResponse *resp)
88 {
89  /* v2 - insert into front page
90  GET / : default page: short server info
91  GET /screen : get screenshot
92  GET /layout/ : return list of all layouts
93 
94  PUT /layout/display?width=536,height=320,layout=mg_def : create layout display of given size and layout
95  GET /layout/display : get image of layout
96  DELETE /layout/display : delete display
97 
98  PUT /layout/display/stream?port=8086 : start streamer on port
99  DELETE /layout/display/stream : stop streamer on port
100  */
101 
102  CX_LOG_DEBUG() << QString("Web server request [%1] from [%2]")
103  .arg(req->path())
104  .arg(req->remoteAddress());
105 
106  if (req->path() == "/")
107  {
108  this->process_mainpage(req, resp);
109  }
110  else if (req->path().startsWith("/screen"))
111  {
112  this->handle_screen(req, resp);
113  }
114  else if (req->path().startsWith("/layout"))
115  {
116  this->handle_layout(req, resp);
117  }
118  else
119  {
120  this->reply_notfound(resp);
121  }
122 }
123 
124 void HttpRequestHandler::handle_layout(QHttpRequest *req, QHttpResponse *resp)
125 {
126  std::cout << "handle_layout" << std::endl;
127  CX_ASSERT(req->path().startsWith("/layout"));
128 
129  if (req->path() == "/layout")
130  {
131  this->process_layout(req, resp);
132  }
133  else if (req->path()=="/layout/display/stream")
134  {
135  this->process_stream(req, resp);
136  }
137  else if (req->path() == "/layout/display")
138  {
139  this->process_display(req, resp);
140  }
141  else
142  {
143  this->reply_notfound(resp);
144  }
145 }
146 
147 void HttpRequestHandler::process_layout(QHttpRequest *req, QHttpResponse *resp)
148 {
149  CX_ASSERT(req->path()=="/layout");
150  std::cout << "process_layout" << std::endl;
151 // GET /layout/ : return list of all layouts
152 
153  if (req->method()==QHttpRequest::HTTP_GET)
154  {
155  this->reply_layout_list(resp);
156  }
157  else
158  {
159  this->reply_method_not_allowed(resp);
160  }
161 }
162 
163 void HttpRequestHandler::process_stream(QHttpRequest *req, QHttpResponse *resp)
164 {
165  CX_ASSERT(req->path()=="/layout/display/stream");
166  std::cout << "process_stream" << std::endl;
167 // PUT /layout/display/stream?port=8086 : start streamer on port
168 // DELETE /layout/display/stream : stop streamer on port
169 
170  if (req->method()==QHttpRequest::HTTP_PUT)
171  {
172  this->create_stream(req, resp);
173  }
174  else if (req->method()==QHttpRequest::HTTP_DELETE)
175  {
176  this->delete_stream(resp);
177  }
178  else
179  {
180  this->reply_method_not_allowed(resp);
181  }
182 }
183 
184 void HttpRequestHandler::process_display(QHttpRequest *req, QHttpResponse *resp)
185 {
186  CX_ASSERT(req->path()=="/layout/display");
187  std::cout << "process_display, success=" << req->successful() << std::endl;
188 
189 // PUT /layout/display?width=536,height=320,layout=mg_def : create layout display of given size and layout
190 // GET /layout/display : get image of layout
191 // DELETE /layout/display : delete display
192 
193  if (req->method()==QHttpRequest::HTTP_GET)
194  {
195  this->get_display_image(resp);
196  }
197  else if (req->method()==QHttpRequest::HTTP_PUT)
198  {
199  this->create_display(req, resp);
200  }
201  else if (req->method()==QHttpRequest::HTTP_DELETE)
202  {
203  this->delete_display(resp);
204  }
205  else
206  {
207  this->reply_method_not_allowed(resp);
208  }
209 
210 }
211 
212 void HttpRequestHandler::reply_layout_list(QHttpResponse *resp)
213 {
214  QStringList layouts = mApi->getAvailableLayouts();
215 
216 // {
217 // "Layouts": [
218 // "ACS_3D",
219 // "AnyDual_3D"
220 // ]
221 // }
222 
223  QJsonObject root;
224  root.insert("Layouts", QJsonArray::fromStringList(layouts));
225  QByteArray ba = QJsonDocument(root).toJson();
226 
227  resp->setHeader("Content-Type", "application/json");
228  resp->setHeader("Content-Length", QString::number(ba.size()));
229  resp->writeHead(200); // everything is OK
230  resp->write(ba);
231  resp->end();
232 }
233 
234 void HttpRequestHandler::get_display_image(QHttpResponse *resp)
235 {
236  QImage image = mApi->grabLayout();
237  QByteArray ba = this->generatePNGEncoding(image);
238 
239  resp->setHeader("Content-Type", "image/png");
240  resp->setHeader("Content-Length", QString::number(ba.size()));
241  resp->writeHead(200); // everything is OK
242  resp->write(ba);
243  resp->end();
244 }
245 
246 void HttpRequestHandler::create_display(QHttpRequest *req, QHttpResponse *resp)
247 {
248  // example test line:
249  // curl -H 'Content-Type: application/json' -X PUT -d '{"width":600,"height":300,"layout":"LAYOUT_US_Acquisition"}' http://localhost:8085/layout/display
250 // QByteArray body = req->body();
251  QJsonDocument doc = QJsonDocument::fromJson(req->body());
252  CX_LOG_CHANNEL_DEBUG("CA") << "JSON: " << QString(req->body());
253  QSize size;
254  size.setWidth(doc.object()["width"].toInt());
255  size.setHeight(doc.object()["height"].toInt());
256  QString layout = doc.object()["layout"].toString();
257 
258  CX_LOG_CHANNEL_DEBUG("CA") << "size " << size.width() << "," << size.height() << ", layout " << layout;
259 
260  mApi->createLayoutWidget(size, layout);
261 
262  resp->writeHead(200); // everything is OK
263  resp->end();
264 }
265 
266 void HttpRequestHandler::delete_display(QHttpResponse *resp)
267 {
268  mApi->closeLayoutWidget();
269 }
270 
271 QByteArray HttpRequestHandler::generatePNGEncoding(QImage image)
272 {
273  QByteArray ba;
274  QBuffer buffer(&ba);
275  buffer.open(QIODevice::WriteOnly);
276  image.save(&buffer, "PNG"); // writes image into ba in PNG format// QString ending = "png";
277  return ba;
278 }
279 
280 void HttpRequestHandler::create_stream(QHttpRequest *req, QHttpResponse *resp)
281 {
282  this->reply_notfound(resp); // TODO: create streamer
283 }
284 
285 void HttpRequestHandler::delete_stream(QHttpResponse *resp)
286 {
287  this->reply_notfound(resp); // TODO: delete streamer
288 }
289 
290 void HttpRequestHandler::process_mainpage(QHttpRequest *req, QHttpResponse *resp)
291 {
292  CX_ASSERT(req->path()=="/");
293 
294  if (req->method()==QHttpRequest::HTTP_GET)
295  {
296  this->reply_mainpage(resp);
297  }
298  else
299  {
300  this->reply_method_not_allowed(resp);
301  }
302 }
303 
304 void HttpRequestHandler::reply_mainpage(QHttpResponse *resp)
305 {
306  QString body("</body>"
307  "%1 REST API"
308  "<p>"
309  "Arguments are passed using JSON format."
310  "</p>"
311  ""
312  "<table border=\"1\">"
313  "<tr><th>method</th><th>resource</th><th>description</th><th>arguments</th></tr>"
314  ""
315  "<tr><td>GET</td><td>/</td><td>main page: short server info</td><td>html page</td></tr>"
316  "<tr><td>GET</td><td>/layout</td><td>return list of all layouts</td><td>layouts</td></tr>"
317  ""
318  "<tr>"
319  "<td>PUT</td><td>/layout/display</td>"
320  "<td>create layout display of given size and layout.</td>"
321  "<td>width=(int),height=(int),layout=(uid)</td>"
322  "</tr>"
323  "<tr><td>GET</td><td>/layout/display</td><td>get image of layout</td><td>png image</td></tr>"
324  "<tr><td>DELETE</td><td>/layout/display</td><td>delete display</td></tr>"
325  ""
326  "%2"
327  ""
328  "</table>"
329  "</body>"
330  "");
331  body = body
332  .arg(qApp->applicationDisplayName())
333  .arg(this->getAdditionalMainPageDescription());
334 
335  QByteArray ba = body.toUtf8();
336 
337  resp->setHeader("Content-Type", "text/html");
338  resp->setHeader("Content-Length", QString::number(ba.size()));
339  resp->writeHead(200); // everything is OK
340  resp->write(ba);
341  resp->end();
342 }
343 
344 void HttpRequestHandler::handle_screen(QHttpRequest *req, QHttpResponse *resp)
345 {
346  std::cout << "handle_screen" << std::endl;
347  CX_ASSERT(req->path().startsWith("/screen"));
348 
349  if (req->path() == "/screen")
350  {
351  this->process_screen(req, resp);
352  }
353  else
354  {
355  this->reply_notfound(resp);
356  }
357 }
358 
359 void HttpRequestHandler::reply_notfound(QHttpResponse *resp)
360 {
361  resp->writeHead(404);
362  resp->end(QByteArray("Not found"));
363 }
364 
366 {
367  resp->writeHead(405);
368  resp->end(QByteArray("Method Not Allowed"));
369 }
370 
371 void HttpRequestHandler::process_screen(QHttpRequest *req, QHttpResponse *resp)
372 {
373  CX_ASSERT(req->path()=="/screen");
374 // GET /screen : get screenshot
375 
376  // disabled: security problem
377 // if (req->method()==QHttpRequest::HTTP_GET)
378 // {
379 // this->reply_screenshot(resp);
380 // }
381 // else
382  {
383  this->reply_method_not_allowed(resp);
384  }
385 }
386 
387 void HttpRequestHandler::reply_screenshot(QHttpResponse *resp)
388 {
389  QImage image = mApi->grabScreen();
390  QByteArray ba = this->generatePNGEncoding(image);
391 
392  resp->setHeader("Content-Type", "image/png");
393  resp->setHeader("Content-Length", QString::number(ba.size()));
394  resp->writeHead(200); // everything is OK
395  resp->write(ba);
396  resp->end();
397 }
398 
399 
400 } // namespace cx
void process_mainpage(QHttpRequest *req, QHttpResponse *resp)
Scalar * end()
void handle_request(QHttpRequest *req, QHttpResponse *resp)
virtual QString getAdditionalMainPageDescription() const
void handle_layout(QHttpRequest *req, QHttpResponse *resp)
virtual void create_stream(QHttpRequest *req, QHttpResponse *resp)
#define CX_ASSERT(statement)
Definition: cxLogger.h:131
void handle_complete_request(QHttpRequest *req, QHttpResponse *resp)
void reply_method_not_allowed(QHttpResponse *resp)
void process_stream(QHttpRequest *req, QHttpResponse *resp)
void handle_screen(QHttpRequest *req, QHttpResponse *resp)
virtual void delete_stream(QHttpResponse *resp)
void reply_screenshot(QHttpResponse *resp)
boost::shared_ptr< class RemoteAPI > RemoteAPIPtr
#define CX_LOG_CHANNEL_DEBUG(channel)
Definition: cxLogger.h:122
void get_display_image(QHttpResponse *resp)
#define CX_LOG_DEBUG
Definition: cxLogger.h:110
void reply_notfound(QHttpResponse *resp)
void process_display(QHttpRequest *req, QHttpResponse *resp)
void process_screen(QHttpRequest *req, QHttpResponse *resp)
void process_layout(QHttpRequest *req, QHttpResponse *resp)
void create_display(QHttpRequest *req, QHttpResponse *resp)
HttpRequestHandler(RemoteAPIPtr api)
void reply_layout_list(QHttpResponse *resp)
void reply_mainpage(QHttpResponse *resp)
void delete_display(QHttpResponse *resp)