$darkmode
handler.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Robert Bosch GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * SPDX-License-Identifier: Apache-2.0
17  */
36 #pragma once
37 #ifndef CLOE_HANDLER_HPP_
38 #define CLOE_HANDLER_HPP_
39 
40 #include <functional> // for function
41 #include <map> // for map, map<>::mapped_type
42 #include <string> // for string, basic_string
43 #include <utility> // for pair
44 
45 #include <cloe/core.hpp> // for Confable, Json
46 
47 namespace cloe {
48 
55 enum class RequestMethod {
56  GET = 1,
57  POST = 2,
58  PUT = 4,
59  DELETE = 8,
60 };
61 
62 const char* as_cstr(const RequestMethod& m);
63 void from_string(const std::string& s, RequestMethod& m);
64 
72 enum class ContentType {
73  NOT_APPLICABLE,
74  UNKNOWN,
75  JSON,
76  HTML,
77  CSS,
78  CSV,
79  JAVASCRIPT,
80  TEXT,
81  SVG,
82  PNG,
83 };
84 
85 const char* as_cstr(const ContentType& t);
86 
95 class Request {
96  public:
97  virtual ~Request() {}
98 
102  virtual RequestMethod method() const = 0;
103 
115  virtual ContentType type() const = 0;
116 
123  virtual const std::string& body() const = 0;
124 
128  virtual const std::string& uri() const = 0;
129 
137  virtual const std::string& endpoint() const = 0;
138 
142  virtual const std::map<std::string, std::string>& query_map() const = 0;
143 
148  virtual bool has_json() const { return this->type() == ContentType::JSON; }
149 
153  virtual Json as_json() const { return fable::parse_json(body()); }
154 };
155 
162 enum class StatusCode {
163  OK = 200,
164  CREATED = 201,
165  ACCEPTED = 202,
166  NO_CONTENT = 204,
167  RESET_CONTENT = 205,
168  PARTIAL_CONTENT = 206,
169  MULTIPLE_CHOICES = 300,
170  MOVED_PERMANENTLY = 301,
171  FOUND = 302,
172  SEE_OTHER = 303,
173  NOT_MODIFIED = 304,
174  USE_PROXY = 305,
175  TEMPORARY_REDIRECT = 307,
176  BAD_REQUEST = 400,
177  UNAUTHORIZED = 401,
178  FORBIDDEN = 403,
179  NOT_FOUND = 404,
180  NOT_ALLOWED = 405,
181  NOT_ACCEPTABLE = 406,
182  REQUEST_TIMEOUT = 408,
183  CONFLICT = 409,
184  GONE = 410,
185  SERVER_ERROR = 500,
186  NOT_IMPLEMENTED = 501,
187  SERVICE_UNAVAILABLE = 503,
188 };
189 
197 class Response {
198  public:
202  Response() : status_(StatusCode::NO_CONTENT), type_(ContentType::NOT_APPLICABLE) {}
203 
227  const std::map<std::string, std::string>& headers() const { return headers_; }
228  std::map<std::string, std::string>& headers() { return headers_; }
229 
233  bool has_header(const std::string& key) { return this->headers().count(key) != 0; }
234 
241  const std::string& header(const std::string& key) { return this->headers().at(key); }
242 
247  void set_header(const std::string& key, const std::string& value) {
248  this->headers()[key] = value;
249  }
250 
251  StatusCode status() const { return status_; }
252  void set_status(StatusCode code) { status_ = code; }
253 
254  ContentType type() const { return type_; }
255  void set_type(ContentType type) {
256  type_ = type;
257  this->set_header("Content-Type", as_cstr(type));
258  }
259 
260  const std::string& body() const { return body_; }
261 
265  void set_body(const std::string& s, ContentType type) {
266  if (!s.empty() && status_ == StatusCode::NO_CONTENT) {
267  status_ = StatusCode::OK;
268  }
269  this->set_type(type);
270  body_ = s;
271  }
272 
279  void set_body(const Json& js) {
280 #ifdef NDEBUG
281  this->set_body(js.dump(), ContentType::JSON);
282 #else
283  this->set_body(js.dump(4), ContentType::JSON);
284 #endif
285  }
286 
290  void write(const Json& js) { this->set_body(js); }
291 
296  void bad_request(const Json& js) { this->error(StatusCode::BAD_REQUEST, js); }
297 
301  void not_found(const Json& js) { this->error(StatusCode::NOT_FOUND, js); }
302 
311  void not_allowed(const RequestMethod& allow, const Json& js) {
312  this->set_status(StatusCode::NOT_ALLOWED);
313  this->set_header("Allow", as_cstr(allow));
314  this->set_body(js);
315  }
316 
321  void not_implemented(const Json& js) { this->error(StatusCode::NOT_IMPLEMENTED, js); }
322 
326  void server_error(const Json& js) { this->error(StatusCode::SERVER_ERROR, js); }
327 
328  void error(StatusCode code, const Json& js) {
329  this->set_body(js);
330  this->set_status(code);
331  }
332 
333  private:
334  StatusCode status_;
335  ContentType type_;
336  std::string body_;
337  std::map<std::string, std::string> headers_;
338 };
339 
348 using Handler = std::function<void(const Request&, Response&)>;
349 
350 namespace handler {
351 
362 class Redirect {
363  public:
364  explicit Redirect(const std::string& location) : location_(location) {}
365  void operator()(const cloe::Request&, cloe::Response& r) {
366  r.set_status(StatusCode::FOUND);
367  r.set_header("Location", location_);
368  }
369 
370  private:
371  std::string location_;
372 };
373 
377 class StaticJson {
378  public:
379  StaticJson(Json j) : data_(j) {} // NOLINT
380  void operator()(const cloe::Request&, cloe::Response& r) { r.write(data_); }
381 
382  private:
383  const Json data_;
384 };
385 
394 template <typename T>
395 class ToJson {
396  public:
397  explicit ToJson(const T* ptr) : ptr_(ptr) {}
398  void operator()(const cloe::Request&, cloe::Response& r) {
399  Json j;
400  to_json(j, *ptr_);
401  r.set_body(j);
402  }
403 
404  private:
405  const T* ptr_;
406 };
407 
420 class FromConf {
421  public:
422  explicit FromConf(Confable* ptr, bool query_map_as_json = true)
423  : ptr_(ptr), convert_(query_map_as_json) {}
424  void operator()(const cloe::Request& q, cloe::Response& r);
425 
426  private:
427  Confable* ptr_;
428  bool convert_;
429 };
430 
431 } // namespace handler
432 } // namespace cloe
433 
434 #endif // CLOE_HANDLER_HPP_
bool has_header(const std::string &key)
Definition: handler.hpp:233
const std::string & header(const std::string &key)
Definition: handler.hpp:241
const std::map< std::string, std::string > & headers() const
Definition: handler.hpp:227
RequestMethod
Definition: handler.hpp:55
Response()
Definition: handler.hpp:202
Definition: confable.hpp:46
Definition: handler.hpp:197
StatusCode
Definition: handler.hpp:162
Definition: handler.hpp:95
void set_body(const Json &js)
Definition: handler.hpp:279
void set_body(const std::string &s, ContentType type)
Definition: handler.hpp:265
ContentType
Definition: handler.hpp:72
void write(const Json &js)
Definition: handler.hpp:290
std::function< void(const Request &, Response &)> Handler
Definition: handler.hpp:348
Definition: coordinator.hpp:36
Definition: handler.hpp:395
void bad_request(const Json &js)
Definition: handler.hpp:296
void not_implemented(const Json &js)
Definition: handler.hpp:321
void set_header(const std::string &key, const std::string &value)
Definition: handler.hpp:247
virtual bool has_json() const
Definition: handler.hpp:148
virtual Json as_json() const
Definition: handler.hpp:153
void not_found(const Json &js)
Definition: handler.hpp:301
void server_error(const Json &js)
Definition: handler.hpp:326
Definition: handler.hpp:377
Definition: handler.hpp:420
Definition: handler.hpp:362
void not_allowed(const RequestMethod &allow, const Json &js)
Definition: handler.hpp:311