$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 
38 #include <functional> // for function
39 #include <map> // for map
40 #include <string> // for string
41 #include <utility> // for pair
42 
43 #include <fable/confable.hpp> // for Confable
44 #include <fable/json.hpp> // for Json
45 
46 namespace cloe {
47 
54 enum class RequestMethod {
55  GET = 1,
56  POST = 2,
57  PUT = 4,
58  DELETE = 8,
59 };
60 
61 const char* as_cstr(const RequestMethod& m);
62 void from_string(const std::string& s, RequestMethod& m);
63 
71 enum class ContentType {
72  NOT_APPLICABLE,
73  UNKNOWN,
74  JSON,
75  HTML,
76  CSS,
77  CSV,
78  JAVASCRIPT,
79  TEXT,
80  SVG,
81  PNG,
82 };
83 
84 const char* as_cstr(const ContentType& t);
85 
94 class Request {
95  public:
96  virtual ~Request() {}
97 
101  virtual RequestMethod method() const = 0;
102 
114  virtual ContentType type() const = 0;
115 
122  virtual const std::string& body() const = 0;
123 
127  virtual const std::string& uri() const = 0;
128 
136  virtual const std::string& endpoint() const = 0;
137 
141  virtual const std::map<std::string, std::string>& query_map() const = 0;
142 
147  virtual bool has_json() const { return this->type() == ContentType::JSON; }
148 
152  virtual fable::Json as_json() const { return fable::parse_json(body()); }
153 };
154 
161 enum class StatusCode {
162  OK = 200,
163  CREATED = 201,
164  ACCEPTED = 202,
165  NO_CONTENT = 204,
166  RESET_CONTENT = 205,
167  PARTIAL_CONTENT = 206,
168  MULTIPLE_CHOICES = 300,
169  MOVED_PERMANENTLY = 301,
170  FOUND = 302,
171  SEE_OTHER = 303,
172  NOT_MODIFIED = 304,
173  USE_PROXY = 305,
174  TEMPORARY_REDIRECT = 307,
175  BAD_REQUEST = 400,
176  UNAUTHORIZED = 401,
177  FORBIDDEN = 403,
178  NOT_FOUND = 404,
179  NOT_ALLOWED = 405,
180  NOT_ACCEPTABLE = 406,
181  REQUEST_TIMEOUT = 408,
182  CONFLICT = 409,
183  GONE = 410,
184  SERVER_ERROR = 500,
185  NOT_IMPLEMENTED = 501,
186  SERVICE_UNAVAILABLE = 503,
187 };
188 
196 class Response {
197  public:
201  Response() : status_(StatusCode::NO_CONTENT), type_(ContentType::NOT_APPLICABLE) {}
202 
226  const std::map<std::string, std::string>& headers() const { return headers_; }
227  std::map<std::string, std::string>& headers() { return headers_; }
228 
232  bool has_header(const std::string& key) { return this->headers().count(key) != 0; }
233 
240  const std::string& header(const std::string& key) { return this->headers().at(key); }
241 
246  void set_header(const std::string& key, const std::string& value) {
247  this->headers()[key] = value;
248  }
249 
250  StatusCode status() const { return status_; }
251  void set_status(StatusCode code) { status_ = code; }
252 
253  ContentType type() const { return type_; }
254  void set_type(ContentType type) {
255  type_ = type;
256  this->set_header("Content-Type", as_cstr(type));
257  }
258 
259  const std::string& body() const { return body_; }
260 
264  void set_body(const std::string& s, ContentType type) {
265  if (!s.empty() && status_ == StatusCode::NO_CONTENT) {
266  status_ = StatusCode::OK;
267  }
268  this->set_type(type);
269  body_ = s;
270  }
271 
278  void set_body(const fable::Json& js) {
279 #ifdef NDEBUG
280  this->set_body(js.dump(), ContentType::JSON);
281 #else
282  this->set_body(js.dump(4), ContentType::JSON);
283 #endif
284  }
285 
289  void write(const fable::Json& js) { this->set_body(js); }
290 
295  void bad_request(const fable::Json& js) { this->error(StatusCode::BAD_REQUEST, js); }
296 
300  void not_found(const fable::Json& js) { this->error(StatusCode::NOT_FOUND, js); }
301 
310  void not_allowed(const RequestMethod& allow, const fable::Json& js) {
311  this->set_status(StatusCode::NOT_ALLOWED);
312  this->set_header("Allow", as_cstr(allow));
313  this->set_body(js);
314  }
315 
320  void not_implemented(const fable::Json& js) { this->error(StatusCode::NOT_IMPLEMENTED, js); }
321 
325  void server_error(const fable::Json& js) { this->error(StatusCode::SERVER_ERROR, js); }
326 
327  void error(StatusCode code, const fable::Json& js) {
328  this->set_body(js);
329  this->set_status(code);
330  }
331 
332  private:
333  StatusCode status_;
334  ContentType type_;
335  std::string body_;
336  std::map<std::string, std::string> headers_;
337 };
338 
347 using Handler = std::function<void(const Request&, Response&)>;
348 
349 namespace handler {
350 
361 class Redirect {
362  public:
363  explicit Redirect(const std::string& location) : location_(location) {}
364  void operator()(const cloe::Request&, cloe::Response& r) {
365  r.set_status(StatusCode::FOUND);
366  r.set_header("Location", location_);
367  }
368 
369  private:
370  std::string location_;
371 };
372 
376 class StaticJson {
377  public:
378  StaticJson(fable::Json j) : data_(j) {} // NOLINT
379  void operator()(const cloe::Request&, cloe::Response& r) { r.write(data_); }
380 
381  private:
382  const fable::Json data_;
383 };
384 
393 template <typename T>
394 class ToJson {
395  public:
396  explicit ToJson(const T* ptr) : ptr_(ptr) {}
397  void operator()(const cloe::Request&, cloe::Response& r) {
398  fable::Json j;
399  to_json(j, *ptr_);
400  r.set_body(j);
401  }
402 
403  private:
404  const T* ptr_;
405 };
406 
419 class FromConf {
420  public:
421  explicit FromConf(fable::Confable* ptr, bool query_map_as_json = true)
422  : ptr_(ptr), convert_(query_map_as_json) {}
423  void operator()(const cloe::Request& q, cloe::Response& r);
424 
425  private:
426  fable::Confable* ptr_;
427  bool convert_;
428 };
429 
430 } // namespace handler
431 } // namespace cloe
Definition: handler.hpp:94
virtual const std::string & endpoint() const =0
virtual const std::map< std::string, std::string > & query_map() const =0
virtual const std::string & body() const =0
virtual bool has_json() const
Definition: handler.hpp:147
virtual ContentType type() const =0
virtual RequestMethod method() const =0
virtual fable::Json as_json() const
Definition: handler.hpp:152
virtual const std::string & uri() const =0
Definition: handler.hpp:196
void set_body(const fable::Json &js)
Definition: handler.hpp:278
void write(const fable::Json &js)
Definition: handler.hpp:289
const std::string & header(const std::string &key)
Definition: handler.hpp:240
Response()
Definition: handler.hpp:201
bool has_header(const std::string &key)
Definition: handler.hpp:232
void not_implemented(const fable::Json &js)
Definition: handler.hpp:320
void server_error(const fable::Json &js)
Definition: handler.hpp:325
const std::map< std::string, std::string > & headers() const
Definition: handler.hpp:226
void not_found(const fable::Json &js)
Definition: handler.hpp:300
void set_body(const std::string &s, ContentType type)
Definition: handler.hpp:264
void bad_request(const fable::Json &js)
Definition: handler.hpp:295
void not_allowed(const RequestMethod &allow, const fable::Json &js)
Definition: handler.hpp:310
void set_header(const std::string &key, const std::string &value)
Definition: handler.hpp:246
Definition: handler.hpp:419
Definition: handler.hpp:361
Definition: handler.hpp:376
Definition: handler.hpp:394
Definition: confable.hpp:98
std::function< void(const Request &, Response &)> Handler
Definition: cloe_fwd.hpp:65
nlohmann::json Json
Definition: fable_fwd.hpp:35
StatusCode
Definition: handler.hpp:161
RequestMethod
Definition: handler.hpp:54
ContentType
Definition: handler.hpp:71