Fraxinus  17.12
An IGT application
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  CX_ASSERT(req->path().startsWith("/layout"));
127 
128  if (req->path() == "/layout")
129  {
130  this->process_layout(req, resp);
131  }
132  else if (req->path()=="/layout/display/stream")
133  {
134  this->process_stream(req, resp);
135  }
136  else if (req->path() == "/layout/display")
137  {
138  this->process_display(req, resp);
139  }
140  else
141  {
142  this->reply_notfound(resp);
143  }
144 }
145 
146 void HttpRequestHandler::process_layout(QHttpRequest *req, QHttpResponse *resp)
147 {
148  CX_ASSERT(req->path()=="/layout");
149 
150  if (req->method()==QHttpRequest::HTTP_GET)
151  {
152  this->reply_layout_list(resp);
153  }
154  else
155  {
156  this->reply_method_not_allowed(resp);
157  }
158 }
159 
160 void HttpRequestHandler::process_stream(QHttpRequest *req, QHttpResponse *resp)
161 {
162  CX_ASSERT(req->path()=="/layout/display/stream");
163 
164  if (req->method()==QHttpRequest::HTTP_PUT)
165  {
166  this->create_stream(req, resp);
167  }
168  else if (req->method()==QHttpRequest::HTTP_DELETE)
169  {
170  this->delete_stream(resp);
171  }
172  else
173  {
174  this->reply_method_not_allowed(resp);
175  }
176 }
177 
178 void HttpRequestHandler::process_display(QHttpRequest *req, QHttpResponse *resp)
179 {
180  CX_ASSERT(req->path()=="/layout/display");
181 
182  if (req->method()==QHttpRequest::HTTP_GET)
183  {
184  this->get_display_image(resp);
185  }
186  else if (req->method()==QHttpRequest::HTTP_PUT)
187  {
188  this->create_display(req, resp);
189  }
190  else if (req->method()==QHttpRequest::HTTP_DELETE)
191  {
192  this->delete_display(resp);
193  }
194  else
195  {
196  this->reply_method_not_allowed(resp);
197  }
198 
199 }
200 
201 void HttpRequestHandler::reply_layout_list(QHttpResponse *resp)
202 {
203  QStringList layouts = mApi->getAvailableLayouts();
204 
205 // {
206 // "Layouts": [
207 // "ACS_3D",
208 // "AnyDual_3D"
209 // ]
210 // }
211 
212  QJsonObject root;
213  root.insert("Layouts", QJsonArray::fromStringList(layouts));
214  QByteArray ba = QJsonDocument(root).toJson();
215 
216  resp->setHeader("Content-Type", "application/json");
217  resp->setHeader("Content-Length", QString::number(ba.size()));
218  resp->writeHead(200); // everything is OK
219  resp->write(ba);
220  resp->end();
221 }
222 
223 void HttpRequestHandler::get_display_image(QHttpResponse *resp)
224 {
225  QImage image = mApi->grabLayout();
226  QByteArray ba = this->generatePNGEncoding(image);
227 
228  resp->setHeader("Content-Type", "image/png");
229  resp->setHeader("Content-Length", QString::number(ba.size()));
230  resp->writeHead(200); // everything is OK
231  resp->write(ba);
232  resp->end();
233 }
234 
235 void HttpRequestHandler::create_display(QHttpRequest *req, QHttpResponse *resp)
236 {
237  // example test line:
238  // curl -H 'Content-Type: application/json' -X PUT -d '{"width":600,"height":300,"layout":"LAYOUT_US_Acquisition"}' http://localhost:8085/layout/display
239 // QByteArray body = req->body();
240  QJsonDocument doc = QJsonDocument::fromJson(req->body());
241  CX_LOG_CHANNEL_DEBUG("CA") << "JSON: " << QString(req->body());
242  QSize size;
243  size.setWidth(doc.object()["width"].toInt());
244  size.setHeight(doc.object()["height"].toInt());
245  QString layout = doc.object()["layout"].toString();
246 
247  CX_LOG_CHANNEL_DEBUG("CA") << "size " << size.width() << "," << size.height() << ", layout " << layout;
248 
249  mApi->createLayoutWidget(size, layout);
250 
251  resp->writeHead(200); // everything is OK
252  resp->end();
253 }
254 
255 void HttpRequestHandler::delete_display(QHttpResponse *resp)
256 {
257  mApi->closeLayoutWidget();
258 }
259 
260 QByteArray HttpRequestHandler::generatePNGEncoding(QImage image)
261 {
262  QByteArray ba;
263  QBuffer buffer(&ba);
264  buffer.open(QIODevice::WriteOnly);
265  image.save(&buffer, "PNG"); // writes image into ba in PNG format// QString ending = "png";
266  return ba;
267 }
268 
269 void HttpRequestHandler::create_stream(QHttpRequest *req, QHttpResponse *resp)
270 {
271  this->reply_notfound(resp); // implement in subclass
272 }
273 
274 void HttpRequestHandler::delete_stream(QHttpResponse *resp)
275 {
276  this->reply_notfound(resp); // implement in subclass
277 }
278 
279 void HttpRequestHandler::process_mainpage(QHttpRequest *req, QHttpResponse *resp)
280 {
281  CX_ASSERT(req->path()=="/");
282 
283  if (req->method()==QHttpRequest::HTTP_GET)
284  {
285  this->reply_mainpage(resp);
286  }
287  else
288  {
289  this->reply_method_not_allowed(resp);
290  }
291 }
292 
293 void HttpRequestHandler::reply_mainpage(QHttpResponse *resp)
294 {
295  QString body("</body>"
296  "%1 REST API"
297  "<p>"
298  "Arguments are passed using JSON format."
299  "</p>"
300  ""
301  "<table border=\"1\">"
302  "<tr><th>method</th><th>resource</th><th>description</th><th>arguments</th></tr>"
303  ""
304  "<tr><td>GET</td><td>/</td><td>main page: short server info</td><td>html page</td></tr>"
305  "<tr><td>GET</td><td>/layout</td><td>return list of all layouts</td><td>layouts</td></tr>"
306  ""
307  "<tr>"
308  "<td>PUT</td><td>/layout/display</td>"
309  "<td>create layout display of given size and layout.</td>"
310  "<td>width=(int),height=(int),layout=(uid)</td>"
311  "</tr>"
312  "<tr><td>GET</td><td>/layout/display</td><td>get image of layout</td><td>png image</td></tr>"
313  "<tr><td>DELETE</td><td>/layout/display</td><td>delete display</td></tr>"
314  ""
315  "%2"
316  ""
317  "</table>"
318  "</body>"
319  "");
320  body = body
321  .arg(qApp->applicationDisplayName())
322  .arg(this->getAdditionalMainPageDescription());
323 
324  QByteArray ba = body.toUtf8();
325 
326  resp->setHeader("Content-Type", "text/html");
327  resp->setHeader("Content-Length", QString::number(ba.size()));
328  resp->writeHead(200); // everything is OK
329  resp->write(ba);
330  resp->end();
331 }
332 
333 void HttpRequestHandler::handle_screen(QHttpRequest *req, QHttpResponse *resp)
334 {
335  CX_ASSERT(req->path().startsWith("/screen"));
336 
337  if (req->path() == "/screen")
338  {
339  this->process_screen(req, resp);
340  }
341  else
342  {
343  this->reply_notfound(resp);
344  }
345 }
346 
347 void HttpRequestHandler::reply_notfound(QHttpResponse *resp)
348 {
349  resp->writeHead(404);
350  resp->end(QByteArray("Not found"));
351 }
352 
354 {
355  resp->writeHead(405);
356  resp->end(QByteArray("Method Not Allowed"));
357 }
358 
359 void HttpRequestHandler::process_screen(QHttpRequest *req, QHttpResponse *resp)
360 {
361  CX_ASSERT(req->path()=="/screen");
362 // GET /screen : get screenshot
363 
364  // disabled: security problem
365 // if (req->method()==QHttpRequest::HTTP_GET)
366 // {
367 // this->reply_screenshot(resp);
368 // }
369 // else
370  {
371  this->reply_method_not_allowed(resp);
372  }
373 }
374 
375 void HttpRequestHandler::reply_screenshot(QHttpResponse *resp)
376 {
377  QImage image = mApi->grabScreen();
378  QByteArray ba = this->generatePNGEncoding(image);
379 
380  resp->setHeader("Content-Type", "image/png");
381  resp->setHeader("Content-Length", QString::number(ba.size()));
382  resp->writeHead(200); // everything is OK
383  resp->write(ba);
384  resp->end();
385 }
386 
387 
388 } // 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:137
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:128
void get_display_image(QHttpResponse *resp)
#define CX_LOG_DEBUG
Definition: cxLogger.h:116
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)
Namespace for all CustusX production code.
void delete_display(QHttpResponse *resp)