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