httpserver.h raw

   1  // Copyright (c) 2015-2022 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #ifndef LIMENKA_HTTPSERVER_H
   6  #define LIMENKA_HTTPSERVER_H
   7  
   8  #include <functional>
   9  #include <optional>
  10  #include <span>
  11  #include <string>
  12  
  13  namespace util {
  14  class SignalInterrupt;
  15  } // namespace util
  16  
  17  /**
  18   * The default value for `-rpcthreads`. This number of threads will be created at startup.
  19   */
  20  static const int DEFAULT_HTTP_THREADS=16;
  21  
  22  /**
  23   * The default value for `-rpcworkqueue`. This is the maximum depth of the work queue,
  24   * we don't allocate this number of work queue items upfront.
  25   */
  26  static const int DEFAULT_HTTP_WORKQUEUE=64;
  27  
  28  static const int DEFAULT_HTTP_SERVER_TIMEOUT=30;
  29  
  30  struct evhttp_request;
  31  struct event_base;
  32  class CService;
  33  class HTTPRequest;
  34  
  35  /** Initialize HTTP server.
  36   * Call this before RegisterHTTPHandler or EventBase().
  37   */
  38  bool InitHTTPServer(const util::SignalInterrupt& interrupt);
  39  /** Start HTTP server.
  40   * This is separate from InitHTTPServer to give users race-condition-free time
  41   * to register their handlers between InitHTTPServer and StartHTTPServer.
  42   */
  43  void StartHTTPServer();
  44  /** Interrupt HTTP server threads */
  45  void InterruptHTTPServer();
  46  /** Stop HTTP server */
  47  void StopHTTPServer();
  48  
  49  /** Change logging level for libevent. */
  50  void UpdateHTTPServerLogging(bool enable);
  51  
  52  /** Handler for requests to a certain HTTP path */
  53  typedef std::function<bool(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
  54  /** Register handler for prefix.
  55   * If multiple handlers match a prefix, the first-registered one will
  56   * be invoked.
  57   */
  58  void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler);
  59  /** Unregister handler for prefix */
  60  void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch);
  61  
  62  /** Return evhttp event base. This can be used by submodules to
  63   * queue timers or custom events.
  64   */
  65  struct event_base* EventBase();
  66  
  67  /** In-flight HTTP request.
  68   * Thin C++ wrapper around evhttp_request.
  69   */
  70  class HTTPRequest
  71  {
  72  private:
  73      struct evhttp_request* req;
  74      const util::SignalInterrupt& m_interrupt;
  75      bool replySent;
  76  
  77  public:
  78      explicit HTTPRequest(struct evhttp_request* req, const util::SignalInterrupt& interrupt, bool replySent = false);
  79      ~HTTPRequest();
  80  
  81      enum RequestMethod {
  82          UNKNOWN,
  83          GET,
  84          POST,
  85          HEAD,
  86          PUT
  87      };
  88  
  89      /** Get requested URI.
  90       */
  91      std::string GetURI() const;
  92  
  93      /** Get CService (address:ip) for the origin of the http request.
  94       */
  95      CService GetPeer() const;
  96  
  97      /** Get request method.
  98       */
  99      RequestMethod GetRequestMethod() const;
 100  
 101      /** Get the query parameter value from request uri for a specified key, or std::nullopt if the
 102       * key is not found.
 103       *
 104       * If the query string contains duplicate keys, the first value is returned. Many web frameworks
 105       * would instead parse this as an array of values, but this is not (yet) implemented as it is
 106       * currently not needed in any of the endpoints.
 107       *
 108       * @param[in] key represents the query parameter of which the value is returned
 109       */
 110      std::optional<std::string> GetQueryParameter(const std::string& key) const;
 111  
 112      /**
 113       * Get the request header specified by hdr, or an empty string.
 114       * Return a pair (isPresent,string).
 115       */
 116      std::pair<bool, std::string> GetHeader(const std::string& hdr) const;
 117  
 118      /**
 119       * Read request body.
 120       *
 121       * @note As this consumes the underlying buffer, call this only once.
 122       * Repeated calls will return an empty string.
 123       */
 124      std::string ReadBody();
 125  
 126      /**
 127       * Write output header.
 128       *
 129       * @note call this before calling WriteErrorReply or Reply.
 130       */
 131      void WriteHeader(const std::string& hdr, const std::string& value);
 132  
 133      /**
 134       * Write HTTP reply.
 135       * nStatus is the HTTP status code to send.
 136       * reply is the body of the reply. Keep it empty to send a standard message.
 137       *
 138       * @note Can be called only once. As this will give the request back to the
 139       * main thread, do not call any other HTTPRequest methods after calling this.
 140       */
 141      void WriteReply(int nStatus, std::string_view reply = "")
 142      {
 143          WriteReply(nStatus, std::as_bytes(std::span{reply}));
 144      }
 145      void WriteReply(int nStatus, std::span<const std::byte> reply);
 146  };
 147  
 148  /** Get the query parameter value from request uri for a specified key, or std::nullopt if the key
 149   * is not found.
 150   *
 151   * If the query string contains duplicate keys, the first value is returned. Many web frameworks
 152   * would instead parse this as an array of values, but this is not (yet) implemented as it is
 153   * currently not needed in any of the endpoints.
 154   *
 155   * Helper function for HTTPRequest::GetQueryParameter.
 156   *
 157   * @param[in] uri is the entire request uri
 158   * @param[in] key represents the query parameter of which the value is returned
 159   */
 160  std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key);
 161  
 162  /** Event handler closure.
 163   */
 164  class HTTPClosure
 165  {
 166  public:
 167      virtual void operator()() = 0;
 168      virtual ~HTTPClosure() = default;
 169  };
 170  
 171  /** Event class. This can be used either as a cross-thread trigger or as a timer.
 172   */
 173  class HTTPEvent
 174  {
 175  public:
 176      /** Create a new event.
 177       * deleteWhenTriggered deletes this event object after the event is triggered (and the handler called)
 178       * handler is the handler to call when the event is triggered.
 179       */
 180      HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void()>& handler);
 181      ~HTTPEvent();
 182  
 183      /** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after
 184       * the given time has elapsed.
 185       */
 186      void trigger(struct timeval* tv);
 187  
 188      bool deleteWhenTriggered;
 189      std::function<void()> handler;
 190  private:
 191      struct event* ev;
 192  };
 193  
 194  #endif // LIMENKA_HTTPSERVER_H
 195