CustusX  2023.01.05-dev+develop.0da12
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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 #include "cxHttpRequestHandler.h"
12 
13 #include <iostream>
14 
15 //#include "cxNetworkServiceImpl.h"
16 #include "cxRegisteredService.h"
17 #include <QtConcurrent>
18 #include <QNetworkAccessManager>
19 #include <QHostAddress>
20 #include <qhttpserver.h>
21 #include <qhttprequest.h>
22 #include <qhttpresponse.h>
23 //#include "cxLogger.h"
24 #include "cxScreenVideoProvider.h"
25 #include "cxLogger.h"
26 
27 #include "cxPatientModelService.h"
28 #include "cxRemoteAPI.h"
29 #include <QPixmap>
30 #include <QJsonObject>
31 #include <QJsonDocument>
32 #include <QJsonArray>
33 #include <QJsonValue>
34 #include <QApplication>
35 
36 namespace cx
37 {
38 
40 {
41 }
42 
43 void HttpRequestHandler::handle_request(QHttpRequest *req, QHttpResponse *resp)
44 {
45  mRequests.push_front(RequestType(req,resp));
46  req->storeBody();
47  connect(req, SIGNAL(end()), this, SLOT(onRequestSuccessful()));
48 }
49 
50 void HttpRequestHandler::onRequestSuccessful()
51 {
52  for (QList<RequestType>::iterator iter = mRequests.begin(); iter!=mRequests.end(); )
53  {
54  if (iter->req->successful())
55  {
56  this->handle_complete_request(iter->req, iter->resp);
57  iter = mRequests.erase(iter);
58  }
59  else
60  {
61  ++iter;
62  }
63  }
64 }
65 
66 void HttpRequestHandler::handle_complete_request(QHttpRequest *req, QHttpResponse *resp)
67 {
68  /* v2 - insert into front page
69  GET / : default page: short server info
70  GET /screen : get screenshot
71  GET /layout/ : return list of all layouts
72 
73  PUT /layout/display?width=536,height=320,layout=mg_def : create layout display of given size and layout
74  GET /layout/display : get image of layout
75  DELETE /layout/display : delete display
76 
77  PUT /layout/display/stream?port=8086 : start streamer on port
78  DELETE /layout/display/stream : stop streamer on port
79  */
80 
81  CX_LOG_DEBUG() << QString("Web server request [%1] from [%2]")
82  .arg(req->path())
83  .arg(req->remoteAddress());
84 
85  if (req->path() == "/")
86  {
87  this->process_mainpage(req, resp);
88  }
89  else if (req->path().startsWith("/screen"))
90  {
91  this->handle_screen(req, resp);
92  }
93  else if (req->path().startsWith("/layout"))
94  {
95  this->handle_layout(req, resp);
96  }
97  else
98  {
99  this->reply_notfound(resp);
100  }
101 }
102 
103 void HttpRequestHandler::handle_layout(QHttpRequest *req, QHttpResponse *resp)
104 {
105  CX_ASSERT(req->path().startsWith("/layout"));
106 
107  if (req->path() == "/layout")
108  {
109  this->process_layout(req, resp);
110  }
111  else if (req->path()=="/layout/display/stream")
112  {
113  this->process_stream(req, resp);
114  }
115  else if (req->path() == "/layout/display")
116  {
117  this->process_display(req, resp);
118  }
119  else
120  {
121  this->reply_notfound(resp);
122  }
123 }
124 
125 void HttpRequestHandler::process_layout(QHttpRequest *req, QHttpResponse *resp)
126 {
127  CX_ASSERT(req->path()=="/layout");
128 
129  if (req->method()==QHttpRequest::HTTP_GET)
130  {
131  this->reply_layout_list(resp);
132  }
133  else
134  {
135  this->reply_method_not_allowed(resp);
136  }
137 }
138 
139 void HttpRequestHandler::process_stream(QHttpRequest *req, QHttpResponse *resp)
140 {
141  CX_ASSERT(req->path()=="/layout/display/stream");
142 
143  if (req->method()==QHttpRequest::HTTP_PUT)
144  {
145  this->create_stream(req, resp);
146  }
147  else if (req->method()==QHttpRequest::HTTP_DELETE)
148  {
149  this->delete_stream(resp);
150  }
151  else
152  {
153  this->reply_method_not_allowed(resp);
154  }
155 }
156 
157 void HttpRequestHandler::process_display(QHttpRequest *req, QHttpResponse *resp)
158 {
159  CX_ASSERT(req->path()=="/layout/display");
160 
161  if (req->method()==QHttpRequest::HTTP_GET)
162  {
163  this->get_display_image(resp);
164  }
165  else if (req->method()==QHttpRequest::HTTP_PUT)
166  {
167  this->create_display(req, resp);
168  }
169  else if (req->method()==QHttpRequest::HTTP_DELETE)
170  {
171  this->delete_display(resp);
172  }
173  else
174  {
175  this->reply_method_not_allowed(resp);
176  }
177 
178 }
179 
180 void HttpRequestHandler::reply_layout_list(QHttpResponse *resp)
181 {
182  QStringList layouts = mApi->getAvailableLayouts();
183 
184 // {
185 // "Layouts": [
186 // "ACS_3D",
187 // "AnyDual_3D"
188 // ]
189 // }
190 
191  QJsonObject root;
192  root.insert("Layouts", QJsonArray::fromStringList(layouts));
193  QByteArray ba = QJsonDocument(root).toJson();
194 
195  resp->setHeader("Content-Type", "application/json");
196  resp->setHeader("Content-Length", QString::number(ba.size()));
197  resp->writeHead(200); // everything is OK
198  resp->write(ba);
199  resp->end();
200 }
201 
202 void HttpRequestHandler::get_display_image(QHttpResponse *resp)
203 {
204  QImage image = mApi->grabLayout();
205  QByteArray ba = this->generatePNGEncoding(image);
206 
207  resp->setHeader("Content-Type", "image/png");
208  resp->setHeader("Content-Length", QString::number(ba.size()));
209  resp->writeHead(200); // everything is OK
210  resp->write(ba);
211  resp->end();
212 }
213 
214 void HttpRequestHandler::create_display(QHttpRequest *req, QHttpResponse *resp)
215 {
216  // example test line:
217  // curl -H 'Content-Type: application/json' -X PUT -d '{"width":600,"height":300,"layout":"LAYOUT_US_Acquisition"}' http://localhost:8085/layout/display
218 // QByteArray body = req->body();
219  QJsonDocument doc = QJsonDocument::fromJson(req->body());
220  CX_LOG_CHANNEL_DEBUG("CA") << "JSON: " << QString(req->body());
221  QSize size;
222  size.setWidth(doc.object()["width"].toInt());
223  size.setHeight(doc.object()["height"].toInt());
224  QString layout = doc.object()["layout"].toString();
225 
226  CX_LOG_CHANNEL_DEBUG("CA") << "size " << size.width() << "," << size.height() << ", layout " << layout;
227 
228  mApi->createLayoutWidget(size, layout);
229 
230  resp->writeHead(200); // everything is OK
231  resp->end();
232 }
233 
234 void HttpRequestHandler::delete_display(QHttpResponse *resp)
235 {
236  mApi->closeLayoutWidget();
237 }
238 
239 QByteArray HttpRequestHandler::generatePNGEncoding(QImage image)
240 {
241  QByteArray ba;
242  QBuffer buffer(&ba);
243  buffer.open(QIODevice::WriteOnly);
244  image.save(&buffer, "PNG"); // writes image into ba in PNG format// QString ending = "png";
245  return ba;
246 }
247 
248 void HttpRequestHandler::create_stream(QHttpRequest *req, QHttpResponse *resp)
249 {
250  this->reply_notfound(resp); // implement in subclass
251 }
252 
253 void HttpRequestHandler::delete_stream(QHttpResponse *resp)
254 {
255  this->reply_notfound(resp); // implement in subclass
256 }
257 
258 void HttpRequestHandler::process_mainpage(QHttpRequest *req, QHttpResponse *resp)
259 {
260  CX_ASSERT(req->path()=="/");
261 
262  if (req->method()==QHttpRequest::HTTP_GET)
263  {
264  this->reply_mainpage(resp);
265  }
266  else
267  {
268  this->reply_method_not_allowed(resp);
269  }
270 }
271 
272 void HttpRequestHandler::reply_mainpage(QHttpResponse *resp)
273 {
274  QString body("</body>"
275  "%1 REST API"
276  "<p>"
277  "Arguments are passed using JSON format."
278  "</p>"
279  ""
280  "<table border=\"1\">"
281  "<tr><th>method</th><th>resource</th><th>description</th><th>arguments</th></tr>"
282  ""
283  "<tr><td>GET</td><td>/</td><td>main page: short server info</td><td>html page</td></tr>"
284  "<tr><td>GET</td><td>/layout</td><td>return list of all layouts</td><td>layouts</td></tr>"
285  ""
286  "<tr>"
287  "<td>PUT</td><td>/layout/display</td>"
288  "<td>create layout display of given size and layout.</td>"
289  "<td>width=(int),height=(int),layout=(uid)</td>"
290  "</tr>"
291  "<tr><td>GET</td><td>/layout/display</td><td>get image of layout</td><td>png image</td></tr>"
292  "<tr><td>DELETE</td><td>/layout/display</td><td>delete display</td></tr>"
293  ""
294  "%2"
295  ""
296  "</table>"
297  "</body>"
298  "");
299  body = body
300  .arg(qApp->applicationDisplayName())
301  .arg(this->getAdditionalMainPageDescription());
302 
303  QByteArray ba = body.toUtf8();
304 
305  resp->setHeader("Content-Type", "text/html");
306  resp->setHeader("Content-Length", QString::number(ba.size()));
307  resp->writeHead(200); // everything is OK
308  resp->write(ba);
309  resp->end();
310 }
311 
312 void HttpRequestHandler::handle_screen(QHttpRequest *req, QHttpResponse *resp)
313 {
314  CX_ASSERT(req->path().startsWith("/screen"));
315 
316  if (req->path() == "/screen")
317  {
318  this->process_screen(req, resp);
319  }
320  else
321  {
322  this->reply_notfound(resp);
323  }
324 }
325 
326 void HttpRequestHandler::reply_notfound(QHttpResponse *resp)
327 {
328  resp->writeHead(404);
329  resp->end(QByteArray("Not found"));
330 }
331 
333 {
334  resp->writeHead(405);
335  resp->end(QByteArray("Method Not Allowed"));
336 }
337 
338 void HttpRequestHandler::process_screen(QHttpRequest *req, QHttpResponse *resp)
339 {
340  CX_ASSERT(req->path()=="/screen");
341 // GET /screen : get screenshot
342 
343  // disabled: security problem
344 // if (req->method()==QHttpRequest::HTTP_GET)
345 // {
346 // this->reply_screenshot(resp);
347 // }
348 // else
349  {
350  this->reply_method_not_allowed(resp);
351  }
352 }
353 
354 void HttpRequestHandler::reply_screenshot(QHttpResponse *resp)
355 {
356  QImage image = mApi->grabScreen();
357  QByteArray ba = this->generatePNGEncoding(image);
358 
359  resp->setHeader("Content-Type", "image/png");
360  resp->setHeader("Content-Length", QString::number(ba.size()));
361  resp->writeHead(200); // everything is OK
362  resp->write(ba);
363  resp->end();
364 }
365 
366 
367 } // 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:116
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:107
void get_display_image(QHttpResponse *resp)
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
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)