123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- #ifndef KBF_WEB_SERVICE_H
- #define KBF_WEB_SERVICE_H
- #include <string>
- #include <vector>
- #include "kbf/http/server.h"
- #include "kbf/macros.h"
- namespace kbf {
- /**
- * @brief Controller-based HTTP server.
- */
- class WebService {
- public:
- /** @brief Tag used for logging. */
- static const constexpr char *TAG = "WebService";
- /**
- * @brief Constructor.
- */
- WebService();
- /**
- * @brief Destructor.
- *
- * Will call stop() if running.
- */
- ~WebService();
- /**
- * @brief Starts the service.
- *
- * @note All Controllers must be attached before calling start().
- *
- * @param port web server port; default is 80
- */
- void start(int port = 80);
- /**
- * @brief Starts the service in SSL (HTTPS) mode.
- *
- * @param cert_start start of the certificate
- * @param cert_end end of the certificate
- * @param key_start start of the private key
- * @param key_end end of the private key
- * @param port TCP port, default is 443
- */
- void startSSL(const unsigned char *cert_start, const unsigned char *cert_end,
- const unsigned char *key_start, const unsigned char *key_end,
- int port = 443);
- /**
- * @brief Stops the service.
- */
- void stop();
- class Controller;
- /**
- * @brief Attaches a Controller to the service.
- *
- * @tparam T Controller class
- */
- template<class T>
- void controller() {
- static_assert(std::is_base_of<Controller, T>::value, "controller(): type must be a Controller");
- Controller *controller = new T();
- void (*handler)(const http::Response &, void *) = nullptr;
- if ((void *) (controller->*(&Controller::onResponseSent)) !=
- (void *) (&Controller::onResponseSent)) { // NOLINT
- // set handler only if onResponseSent is implemented
- handler = Controller::responseSentHandler;
- }
- auto *arg = new HandlerArg{*this, *controller};
- server.route({http::Method::GET, controller->path, handleGet, arg, handler});
- server.route({http::Method::POST, controller->path, handlePost, arg, handler});
- }
- class Middleware;
- /**
- * @brief Adds a #Middleware to the service.
- *
- * @tparam T Middleware class
- */
- template<class T>
- void middleware() {
- static_assert(std::is_base_of<Middleware, T>::value, "middleware(): type must be a Middleware");
- middlewares.push_back(new T());
- }
- /**
- * @brief Continue execution of the middleware pipeline.
- *
- * @note Should be called from middlewares if you want the pipeline to continue.
- *
- * @param request
- * @return response
- */
- http::Response next(const http::Request &request);
- /**
- * @brief Controller for the web service.
- *
- * Pass derived classes to WebService::controller().
- */
- class Controller {
- friend class WebService;
- protected:
- /**
- * @brief Constructor.
- *
- * Call from child constructor.
- *
- * @param path URI to handle
- */
- explicit Controller(std::string path);
- /** @brief URI to handle */
- const std::string path;
- /**
- * @brief Method to run on incoming GET requests.
- *
- * @param request
- * @return HTTP response
- */
- virtual http::Response get(const http::Request &request) {
- return http::Response("method not allowed", 405);
- }
- /**
- * @brief Method to run on incoming POST requests.
- *
- * @param request
- * @return HTTP response
- */
- virtual http::Response post(const http::Request &request) {
- return http::Response("method not allowed", 405);
- }
- /**
- * @brief Called after a response is sent successfully.
- *
- * @param response
- */
- virtual void onResponseSent(const http::Response &response) {}
- private:
- static void responseSentHandler(const http::Response &response, void *data);
- };
- /**
- * @brief Base class for HTTP middlewares.
- *
- * Pass derived classes to #WebService::middleware().
- *
- * The #run() method will be called for every request. Implementations should call webService->next() for
- * the pipeline to continue.
- *
- * @note If multiple middlewares are added to a WebService, they will be called in the order they were added.
- */
- class Middleware {
- public:
- /**
- * @brief Function to run for every request. Should call webService->next() for the pipeline to continue.
- *
- * @param request incoming request
- * @param webService webService instance
- * @return response
- */
- virtual http::Response run(const http::Request &request, WebService &webService) = 0;
- };
- private:
- http::Server server;
- bool running = false;
- struct HandlerArg {
- WebService &webService;
- Controller &controller;
- };
- typedef http::Response (WebService::Controller::*MethodFunction)(const http::Request &request);
- MethodFunction currentMethod = nullptr;
- Controller *currentController = nullptr;
- static http::Response handleGet(const http::Request &request, void *data);
- static http::Response handlePost(const http::Request &request, void *data);
- http::Response startPipeline(const http::Request &request, HandlerArg *arg);
- std::vector<Middleware *> middlewares;
- std::optional<std::vector<Middleware *>::iterator>
- middlewareIt = std::nullopt;
- };
- }
- #endif //KBF_WEB_SERVICE_H
|