]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
1 | // Copyright The OpenTelemetry Authors |
2 | // SPDX-License-Identifier: Apache-2.0 | |
3 | ||
4 | #pragma once | |
5 | ||
6 | #include <functional> | |
7 | #include <list> | |
8 | #include <map> | |
9 | ||
10 | #include "socket_tools.h" | |
11 | ||
12 | #ifdef HAVE_HTTP_DEBUG | |
13 | # ifdef LOG_TRACE | |
14 | # undef LOG_TRACE | |
15 | # define LOG_TRACE(x, ...) printf(x "\n", __VA_ARGS__) | |
16 | # endif | |
17 | #endif | |
18 | ||
19 | #ifndef HTTP_SERVER_NS | |
20 | # define HTTP_SERVER_NS testing | |
21 | #endif | |
22 | ||
23 | namespace HTTP_SERVER_NS | |
24 | { | |
25 | ||
26 | constexpr const char *CONTENT_TYPE = "Content-Type"; | |
27 | constexpr const char *CONTENT_TYPE_TEXT = "text/plain"; | |
28 | constexpr const char *CONTENT_TYPE_BIN = "application/octet-stream"; | |
29 | ||
30 | struct HttpRequest | |
31 | { | |
32 | std::string client; | |
33 | std::string method; | |
34 | std::string uri; | |
35 | std::string protocol; | |
36 | std::map<std::string, std::string> headers; | |
37 | std::string content; | |
38 | }; | |
39 | ||
40 | struct HttpResponse | |
41 | { | |
42 | int code; | |
43 | std::string message; | |
44 | std::map<std::string, std::string> headers; | |
45 | std::string body; | |
46 | }; | |
47 | ||
48 | using CallbackFunction = std::function<int(HttpRequest const &request, HttpResponse &response)>; | |
49 | ||
50 | class HttpRequestCallback | |
51 | { | |
52 | protected: | |
53 | CallbackFunction callback = nullptr; | |
54 | ||
55 | public: | |
56 | HttpRequestCallback(){}; | |
57 | ||
58 | HttpRequestCallback &operator=(HttpRequestCallback other) | |
59 | { | |
60 | callback = other.callback; | |
61 | return *this; | |
62 | }; | |
63 | ||
64 | HttpRequestCallback(CallbackFunction func) : callback(func){}; | |
65 | ||
66 | HttpRequestCallback &operator=(CallbackFunction func) | |
67 | { | |
68 | callback = func; | |
69 | return (*this); | |
70 | } | |
71 | ||
72 | virtual int onHttpRequest(HttpRequest const &request, HttpResponse &response) | |
73 | { | |
74 | if (callback != nullptr) | |
75 | { | |
76 | return callback(request, response); | |
77 | } | |
78 | return 0; | |
79 | }; | |
80 | }; | |
81 | ||
82 | // Simple HTTP server | |
83 | // Goals: | |
84 | // - Support enough of HTTP to be used as a mock | |
85 | // - Be flexible to allow creating various test scenarios | |
86 | // Out of scope: | |
87 | // - Performance | |
88 | // - Full support of RFC 7230-7237 | |
89 | class HttpServer : private SocketTools::Reactor::SocketCallback | |
90 | { | |
91 | protected: | |
92 | struct Connection | |
93 | { | |
94 | SocketTools::Socket socket; | |
95 | std::string receiveBuffer; | |
96 | std::string sendBuffer; | |
97 | enum | |
98 | { | |
99 | Idle, | |
100 | ReceivingHeaders, | |
101 | Sending100Continue, | |
102 | ReceivingBody, | |
103 | Processing, | |
104 | SendingHeaders, | |
105 | SendingBody, | |
106 | Closing | |
107 | } state; | |
108 | size_t contentLength; | |
109 | bool keepalive; | |
110 | HttpRequest request; | |
111 | HttpResponse response; | |
112 | }; | |
113 | ||
114 | std::string m_serverHost; | |
115 | bool allowKeepalive{true}; | |
116 | SocketTools::Reactor m_reactor; | |
117 | std::list<SocketTools::Socket> m_listeningSockets; | |
118 | ||
119 | class HttpRequestHandler : public std::pair<std::string, HttpRequestCallback *> | |
120 | { | |
121 | public: | |
122 | HttpRequestHandler(std::string key, HttpRequestCallback *value) | |
123 | { | |
124 | first = key; | |
125 | second = value; | |
126 | }; | |
127 | ||
128 | HttpRequestHandler() : std::pair<std::string, HttpRequestCallback *>() | |
129 | { | |
130 | first = ""; | |
131 | second = nullptr; | |
132 | }; | |
133 | ||
134 | HttpRequestHandler &operator=(std::pair<std::string, HttpRequestCallback *> other) | |
135 | { | |
136 | first = other.first; | |
137 | second = other.second; | |
138 | return (*this); | |
139 | }; | |
140 | ||
141 | HttpRequestHandler &operator=(HttpRequestCallback &cb) | |
142 | { | |
143 | second = &cb; | |
144 | return (*this); | |
145 | }; | |
146 | ||
147 | HttpRequestHandler &operator=(HttpRequestCallback *cb) | |
148 | { | |
149 | second = cb; | |
150 | return (*this); | |
151 | }; | |
152 | }; | |
153 | ||
154 | std::list<HttpRequestHandler> m_handlers; | |
155 | ||
156 | std::map<SocketTools::Socket, Connection> m_connections; | |
157 | size_t m_maxRequestHeadersSize, m_maxRequestContentSize; | |
158 | ||
159 | public: | |
160 | void setKeepalive(bool keepAlive) { allowKeepalive = keepAlive; } | |
161 | ||
162 | HttpServer() | |
163 | : m_serverHost("unnamed"), | |
164 | allowKeepalive(true), | |
165 | m_reactor(*this), | |
166 | m_maxRequestHeadersSize(8192), | |
167 | m_maxRequestContentSize(2 * 1024 * 1024){}; | |
168 | ||
169 | HttpServer(std::string serverHost, int port = 30000) : HttpServer() | |
170 | { | |
171 | std::ostringstream os; | |
172 | os << serverHost << ":" << port; | |
173 | setServerName(os.str()); | |
174 | addListeningPort(port); | |
175 | }; | |
176 | ||
177 | ~HttpServer() | |
178 | { | |
179 | for (auto &sock : m_listeningSockets) | |
180 | { | |
181 | sock.close(); | |
182 | } | |
183 | } | |
184 | ||
185 | void setRequestLimits(size_t maxRequestHeadersSize, size_t maxRequestContentSize) | |
186 | { | |
187 | m_maxRequestHeadersSize = maxRequestHeadersSize; | |
188 | m_maxRequestContentSize = maxRequestContentSize; | |
189 | } | |
190 | ||
191 | void setServerName(std::string const &name) { m_serverHost = name; } | |
192 | ||
193 | int addListeningPort(int port) | |
194 | { | |
195 | SocketTools::Socket socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
196 | socket.setNonBlocking(); | |
197 | socket.setReuseAddr(); | |
198 | ||
199 | SocketTools::SocketAddr addr(0, port); | |
200 | socket.bind(addr); | |
201 | socket.getsockname(addr); | |
202 | ||
203 | socket.listen(10); | |
204 | m_listeningSockets.push_back(socket); | |
205 | m_reactor.addSocket(socket, SocketTools::Reactor::Acceptable); | |
206 | LOG_INFO("HttpServer: Listening on %s", addr.toString().c_str()); | |
207 | ||
208 | return addr.port(); | |
209 | } | |
210 | ||
211 | HttpRequestHandler &addHandler(const std::string &root, HttpRequestCallback &handler) | |
212 | { | |
213 | // No thread-safety here! | |
214 | m_handlers.push_back({root, &handler}); | |
215 | LOG_INFO("HttpServer: Added handler for %s", root.c_str()); | |
216 | return m_handlers.back(); | |
217 | } | |
218 | ||
219 | HttpRequestHandler &operator[](const std::string &root) | |
220 | { | |
221 | // No thread-safety here! | |
222 | m_handlers.push_back({root, nullptr}); | |
223 | LOG_INFO("HttpServer: Added handler for %s", root.c_str()); | |
224 | return m_handlers.back(); | |
225 | } | |
226 | ||
227 | HttpServer &operator+=(std::pair<const std::string &, HttpRequestCallback &> other) | |
228 | { | |
229 | LOG_INFO("HttpServer: Added handler for %s", other.first.c_str()); | |
230 | m_handlers.push_back(HttpRequestHandler(other.first, &other.second)); | |
231 | return (*this); | |
232 | }; | |
233 | ||
234 | void start() { m_reactor.start(); } | |
235 | ||
236 | void stop() { m_reactor.stop(); } | |
237 | ||
238 | protected: | |
239 | virtual void onSocketAcceptable(SocketTools::Socket socket) override | |
240 | { | |
241 | LOG_TRACE("HttpServer: accepting socket fd=0x%llx", socket.m_sock); | |
242 | assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) != | |
243 | m_listeningSockets.end()); | |
244 | ||
245 | SocketTools::Socket csocket; | |
246 | SocketTools::SocketAddr caddr; | |
247 | if (socket.accept(csocket, caddr)) | |
248 | { | |
249 | csocket.setNonBlocking(); | |
250 | Connection &conn = m_connections[csocket]; | |
251 | conn.socket = csocket; | |
252 | conn.state = Connection::Idle; | |
253 | conn.request.client = caddr.toString(); | |
254 | m_reactor.addSocket(csocket, SocketTools::Reactor::Readable | SocketTools::Reactor::Closed); | |
255 | LOG_TRACE("HttpServer: [%s] accepted", conn.request.client.c_str()); | |
256 | } | |
257 | } | |
258 | ||
259 | virtual void onSocketReadable(SocketTools::Socket socket) override | |
260 | { | |
261 | LOG_TRACE("HttpServer: reading socket fd=0x%llx", socket.m_sock); | |
262 | // No thread-safety here! | |
263 | assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == | |
264 | m_listeningSockets.end()); | |
265 | ||
266 | // No thread-safety here! | |
267 | auto connIt = m_connections.find(socket); | |
268 | if (connIt == m_connections.end()) | |
269 | { | |
270 | return; | |
271 | } | |
272 | Connection &conn = connIt->second; | |
273 | ||
274 | char buffer[2048] = {0}; | |
275 | int received = socket.recv(buffer, sizeof(buffer)); | |
276 | LOG_TRACE("HttpServer: [%s] received %d", conn.request.client.c_str(), received); | |
277 | if (received <= 0) | |
278 | { | |
279 | handleConnectionClosed(conn); | |
280 | return; | |
281 | } | |
282 | conn.receiveBuffer.append(buffer, buffer + received); | |
283 | ||
284 | handleConnection(conn); | |
285 | } | |
286 | ||
287 | virtual void onSocketWritable(SocketTools::Socket socket) override | |
288 | { | |
289 | LOG_TRACE("HttpServer: writing socket fd=0x%llx", socket.m_sock); | |
290 | ||
291 | // No thread-safety here! | |
292 | assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == | |
293 | m_listeningSockets.end()); | |
294 | ||
295 | // No thread-safety here! | |
296 | auto connIt = m_connections.find(socket); | |
297 | if (connIt == m_connections.end()) | |
298 | { | |
299 | return; | |
300 | } | |
301 | Connection &conn = connIt->second; | |
302 | ||
303 | if (!sendMore(conn)) | |
304 | { | |
305 | handleConnection(conn); | |
306 | } | |
307 | } | |
308 | ||
309 | virtual void onSocketClosed(SocketTools::Socket socket) override | |
310 | { | |
311 | LOG_TRACE("HttpServer: closing socket fd=0x%llx", socket.m_sock); | |
312 | assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == | |
313 | m_listeningSockets.end()); | |
314 | ||
315 | auto connIt = m_connections.find(socket); | |
316 | if (connIt == m_connections.end()) | |
317 | { | |
318 | return; | |
319 | } | |
320 | Connection &conn = connIt->second; | |
321 | ||
322 | handleConnectionClosed(conn); | |
323 | } | |
324 | ||
325 | bool sendMore(Connection &conn) | |
326 | { | |
327 | if (conn.sendBuffer.empty()) | |
328 | { | |
329 | return false; | |
330 | } | |
331 | ||
332 | int sent = conn.socket.send(conn.sendBuffer.data(), static_cast<int>(conn.sendBuffer.size())); | |
333 | LOG_TRACE("HttpServer: [%s] sent %d", conn.request.client.c_str(), sent); | |
334 | if (sent < 0 && conn.socket.error() != SocketTools::Socket::ErrorWouldBlock) | |
335 | { | |
336 | return true; | |
337 | } | |
338 | conn.sendBuffer.erase(0, sent); | |
339 | ||
340 | if (!conn.sendBuffer.empty()) | |
341 | { | |
342 | m_reactor.addSocket(conn.socket, | |
343 | SocketTools::Reactor::Writable | SocketTools::Reactor::Closed); | |
344 | return true; | |
345 | } | |
346 | ||
347 | return false; | |
348 | } | |
349 | ||
350 | protected: | |
351 | void handleConnectionClosed(Connection &conn) | |
352 | { | |
353 | LOG_TRACE("HttpServer: [%s] closed", conn.request.client.c_str()); | |
354 | if (conn.state != Connection::Idle && conn.state != Connection::Closing) | |
355 | { | |
356 | LOG_WARN("HttpServer: [%s] connection closed unexpectedly", conn.request.client.c_str()); | |
357 | } | |
358 | m_reactor.removeSocket(conn.socket); | |
359 | auto connIt = m_connections.find(conn.socket); | |
360 | conn.socket.close(); | |
361 | m_connections.erase(connIt); | |
362 | } | |
363 | ||
364 | void handleConnection(Connection &conn) | |
365 | { | |
366 | for (;;) | |
367 | { | |
368 | if (conn.state == Connection::Idle) | |
369 | { | |
370 | conn.response.code = 0; | |
371 | conn.state = Connection::ReceivingHeaders; | |
372 | LOG_TRACE("HttpServer: [%s] receiving headers", conn.request.client.c_str()); | |
373 | } | |
374 | ||
375 | if (conn.state == Connection::ReceivingHeaders) | |
376 | { | |
377 | bool lfOnly = false; | |
378 | size_t ofs = conn.receiveBuffer.find("\r\n\r\n"); | |
379 | if (ofs == std::string::npos) | |
380 | { | |
381 | lfOnly = true; | |
382 | ofs = conn.receiveBuffer.find("\n\n"); | |
383 | } | |
384 | size_t headersLen = (ofs != std::string::npos) ? ofs : conn.receiveBuffer.length(); | |
385 | if (headersLen > m_maxRequestHeadersSize) | |
386 | { | |
387 | LOG_WARN("HttpServer: [%s] headers too long - %u", conn.request.client.c_str(), | |
388 | static_cast<unsigned>(headersLen)); | |
389 | conn.response.code = 431; // Request Header Fields Too Large | |
390 | conn.keepalive = false; | |
391 | conn.state = Connection::Processing; | |
392 | continue; | |
393 | } | |
394 | if (ofs == std::string::npos) | |
395 | { | |
396 | return; | |
397 | } | |
398 | ||
399 | if (!parseHeaders(conn)) | |
400 | { | |
401 | LOG_WARN("HttpServer: [%s] invalid headers", conn.request.client.c_str()); | |
402 | conn.response.code = 400; // Bad Request | |
403 | conn.keepalive = false; | |
404 | conn.state = Connection::Processing; | |
405 | continue; | |
406 | } | |
407 | LOG_INFO("HttpServer: [%s] %s %s %s", conn.request.client.c_str(), | |
408 | conn.request.method.c_str(), conn.request.uri.c_str(), | |
409 | conn.request.protocol.c_str()); | |
410 | conn.receiveBuffer.erase(0, ofs + (lfOnly ? 2 : 4)); | |
411 | ||
412 | conn.keepalive = (conn.request.protocol == "HTTP/1.1"); | |
413 | auto const connection = conn.request.headers.find("Connection"); | |
414 | if (connection != conn.request.headers.end()) | |
415 | { | |
416 | if (equalsLowercased(connection->second, "keep-alive")) | |
417 | { | |
418 | conn.keepalive = true; | |
419 | } | |
420 | else if (equalsLowercased(connection->second, "close")) | |
421 | { | |
422 | conn.keepalive = false; | |
423 | } | |
424 | } | |
425 | ||
426 | auto const contentLength = conn.request.headers.find("Content-Length"); | |
427 | if (contentLength != conn.request.headers.end()) | |
428 | { | |
429 | conn.contentLength = atoi(contentLength->second.c_str()); | |
430 | } | |
431 | else | |
432 | { | |
433 | conn.contentLength = 0; | |
434 | } | |
435 | if (conn.contentLength > m_maxRequestContentSize) | |
436 | { | |
437 | LOG_WARN("HttpServer: [%s] content too long - %u", conn.request.client.c_str(), | |
438 | static_cast<unsigned>(conn.contentLength)); | |
439 | conn.response.code = 413; // Payload Too Large | |
440 | conn.keepalive = false; | |
441 | conn.state = Connection::Processing; | |
442 | continue; | |
443 | } | |
444 | ||
445 | auto const expect = conn.request.headers.find("Expect"); | |
446 | if (expect != conn.request.headers.end() && conn.request.protocol == "HTTP/1.1") | |
447 | { | |
448 | if (!equalsLowercased(expect->second, "100-continue")) | |
449 | { | |
450 | LOG_WARN("HttpServer: [%s] unknown expectation - %s", conn.request.client.c_str(), | |
451 | expect->second.c_str()); | |
452 | conn.response.code = 417; // Expectation Failed | |
453 | conn.keepalive = false; | |
454 | conn.state = Connection::Processing; | |
455 | continue; | |
456 | } | |
457 | conn.sendBuffer = "HTTP/1.1 100 Continue\r\n\r\n"; | |
458 | conn.state = Connection::Sending100Continue; | |
459 | LOG_TRACE("HttpServer: [%s] sending \"100 Continue\"", conn.request.client.c_str()); | |
460 | continue; | |
461 | } | |
462 | ||
463 | conn.state = Connection::ReceivingBody; | |
464 | LOG_TRACE("HttpServer: [%s] receiving body", conn.request.client.c_str()); | |
465 | } | |
466 | ||
467 | if (conn.state == Connection::Sending100Continue) | |
468 | { | |
469 | if (sendMore(conn)) | |
470 | { | |
471 | return; | |
472 | } | |
473 | ||
474 | conn.state = Connection::ReceivingBody; | |
475 | LOG_TRACE("HttpServer: [%s] receiving body", conn.request.client.c_str()); | |
476 | } | |
477 | ||
478 | if (conn.state == Connection::ReceivingBody) | |
479 | { | |
480 | if (conn.receiveBuffer.length() < conn.contentLength) | |
481 | { | |
482 | return; | |
483 | } | |
484 | ||
485 | if (conn.receiveBuffer.length() == conn.contentLength) | |
486 | { | |
487 | conn.request.content = std::move(conn.receiveBuffer); | |
488 | conn.receiveBuffer.clear(); | |
489 | } | |
490 | else | |
491 | { | |
492 | conn.request.content.assign(conn.receiveBuffer, 0, conn.contentLength); | |
493 | conn.receiveBuffer.erase(0, conn.contentLength); | |
494 | } | |
495 | ||
496 | conn.state = Connection::Processing; | |
497 | LOG_TRACE("HttpServer: [%s] processing request", conn.request.client.c_str()); | |
498 | } | |
499 | ||
500 | if (conn.state == Connection::Processing) | |
501 | { | |
502 | processRequest(conn); | |
503 | ||
504 | std::ostringstream os; | |
505 | os << conn.request.protocol << ' ' << conn.response.code << ' ' << conn.response.message | |
506 | << "\r\n"; | |
507 | for (auto const &header : conn.response.headers) | |
508 | { | |
509 | os << header.first << ": " << header.second << "\r\n"; | |
510 | } | |
511 | os << "\r\n"; | |
512 | ||
513 | conn.sendBuffer = os.str(); | |
514 | conn.state = Connection::SendingHeaders; | |
515 | LOG_TRACE("HttpServer: [%s] sending headers", conn.request.client.c_str()); | |
516 | } | |
517 | ||
518 | if (conn.state == Connection::SendingHeaders) | |
519 | { | |
520 | if (sendMore(conn)) | |
521 | { | |
522 | return; | |
523 | } | |
524 | ||
525 | conn.sendBuffer = std::move(conn.response.body); | |
526 | conn.state = Connection::SendingBody; | |
527 | LOG_TRACE("HttpServer: [%s] sending body", conn.request.client.c_str()); | |
528 | } | |
529 | ||
530 | if (conn.state == Connection::SendingBody) | |
531 | { | |
532 | if (sendMore(conn)) | |
533 | { | |
534 | return; | |
535 | } | |
536 | ||
537 | conn.keepalive &= allowKeepalive; | |
538 | ||
539 | if (conn.keepalive) | |
540 | { | |
541 | m_reactor.addSocket(conn.socket, | |
542 | SocketTools::Reactor::Readable | SocketTools::Reactor::Closed); | |
543 | conn.state = Connection::Idle; | |
544 | LOG_TRACE("HttpServer: [%s] idle (keep-alive)", conn.request.client.c_str()); | |
545 | if (conn.receiveBuffer.empty()) | |
546 | { | |
547 | return; | |
548 | } | |
549 | } | |
550 | else | |
551 | { | |
552 | conn.socket.shutdown(SocketTools::Socket::ShutdownSend); | |
553 | m_reactor.addSocket(conn.socket, SocketTools::Reactor::Closed); | |
554 | conn.state = Connection::Closing; | |
555 | LOG_TRACE("HttpServer: [%s] closing", conn.request.client.c_str()); | |
556 | } | |
557 | } | |
558 | ||
559 | if (conn.state == Connection::Closing) | |
560 | { | |
561 | return; | |
562 | } | |
563 | } | |
564 | } | |
565 | ||
566 | bool parseHeaders(Connection &conn) | |
567 | { | |
568 | // Method | |
569 | char const *begin = conn.receiveBuffer.c_str(); | |
570 | char const *ptr = begin; | |
571 | while (*ptr && *ptr != ' ' && *ptr != '\r' && *ptr != '\n') | |
572 | { | |
573 | ptr++; | |
574 | } | |
575 | if (*ptr != ' ') | |
576 | { | |
577 | return false; | |
578 | } | |
579 | conn.request.method.assign(begin, ptr); | |
580 | while (*ptr == ' ') | |
581 | { | |
582 | ptr++; | |
583 | } | |
584 | ||
585 | // URI | |
586 | begin = ptr; | |
587 | while (*ptr && *ptr != ' ' && *ptr != '\r' && *ptr != '\n') | |
588 | { | |
589 | ptr++; | |
590 | } | |
591 | if (*ptr != ' ') | |
592 | { | |
593 | return false; | |
594 | } | |
595 | conn.request.uri.assign(begin, ptr); | |
596 | while (*ptr == ' ') | |
597 | { | |
598 | ptr++; | |
599 | } | |
600 | ||
601 | // Protocol | |
602 | begin = ptr; | |
603 | while (*ptr && *ptr != ' ' && *ptr != '\r' && *ptr != '\n') | |
604 | { | |
605 | ptr++; | |
606 | } | |
607 | if (*ptr != '\r' && *ptr != '\n') | |
608 | { | |
609 | return false; | |
610 | } | |
611 | conn.request.protocol.assign(begin, ptr); | |
612 | if (*ptr == '\r') | |
613 | { | |
614 | ptr++; | |
615 | } | |
616 | if (*ptr != '\n') | |
617 | { | |
618 | return false; | |
619 | } | |
620 | ptr++; | |
621 | ||
622 | // Headers | |
623 | conn.request.headers.clear(); | |
624 | while (*ptr != '\r' && *ptr != '\n') | |
625 | { | |
626 | // Name | |
627 | begin = ptr; | |
628 | while (*ptr && *ptr != ':' && *ptr != ' ' && *ptr != '\r' && *ptr != '\n') | |
629 | { | |
630 | ptr++; | |
631 | } | |
632 | if (*ptr != ':') | |
633 | { | |
634 | return false; | |
635 | } | |
636 | std::string name = normalizeHeaderName(begin, ptr); | |
637 | ptr++; | |
638 | while (*ptr == ' ') | |
639 | { | |
640 | ptr++; | |
641 | } | |
642 | ||
643 | // Value | |
644 | begin = ptr; | |
645 | while (*ptr && *ptr != '\r' && *ptr != '\n') | |
646 | { | |
647 | ptr++; | |
648 | } | |
649 | conn.request.headers[name] = std::string(begin, ptr); | |
650 | if (*ptr == '\r') | |
651 | { | |
652 | ptr++; | |
653 | } | |
654 | if (*ptr != '\n') | |
655 | { | |
656 | return false; | |
657 | } | |
658 | ptr++; | |
659 | } | |
660 | ||
661 | if (*ptr == '\r') | |
662 | { | |
663 | ptr++; | |
664 | } | |
665 | if (*ptr != '\n') | |
666 | { | |
667 | return false; | |
668 | } | |
669 | ptr++; | |
670 | ||
671 | return true; | |
672 | } | |
673 | ||
674 | static bool equalsLowercased(std::string const &str, char const *mask) | |
675 | { | |
676 | char const *ptr = str.c_str(); | |
677 | while (*ptr && *mask && ::tolower(*ptr) == *mask) | |
678 | { | |
679 | ptr++; | |
680 | mask++; | |
681 | } | |
682 | return !*ptr && !*mask; | |
683 | } | |
684 | ||
685 | static std::string normalizeHeaderName(char const *begin, char const *end) | |
686 | { | |
687 | std::string result(begin, end); | |
688 | bool first = true; | |
689 | for (char &ch : result) | |
690 | { | |
691 | if (first) | |
692 | { | |
693 | ch = static_cast<char>(::toupper(ch)); | |
694 | first = false; | |
695 | } | |
696 | else if (ch == '-') | |
697 | { | |
698 | first = true; | |
699 | } | |
700 | else | |
701 | { | |
702 | ch = static_cast<char>(::tolower(ch)); | |
703 | } | |
704 | } | |
705 | return result; | |
706 | } | |
707 | ||
708 | void processRequest(Connection &conn) | |
709 | { | |
710 | conn.response.message.clear(); | |
711 | conn.response.headers.clear(); | |
712 | conn.response.body.clear(); | |
713 | ||
714 | if (conn.response.code == 0) | |
715 | { | |
716 | conn.response.code = 404; // Not Found | |
717 | for (auto &handler : m_handlers) | |
718 | { | |
719 | if (conn.request.uri.length() >= handler.first.length() && | |
720 | strncmp(conn.request.uri.c_str(), handler.first.c_str(), handler.first.length()) == 0) | |
721 | { | |
722 | LOG_TRACE("HttpServer: [%s] using handler for %s", conn.request.client.c_str(), | |
723 | handler.first.c_str()); | |
724 | // auto callback = handler.second; // Bazel gets mad at this unused | |
725 | // var, uncomment when using | |
726 | int result = handler.second->onHttpRequest(conn.request, conn.response); | |
727 | if (result != 0) | |
728 | { | |
729 | conn.response.code = result; | |
730 | break; | |
731 | } | |
732 | } | |
733 | } | |
734 | ||
735 | if (conn.response.code == -1) | |
736 | { | |
737 | LOG_TRACE("HttpServer: [%s] closing by request", conn.request.client.c_str()); | |
738 | handleConnectionClosed(conn); | |
739 | } | |
740 | } | |
741 | ||
742 | if (conn.response.message.empty()) | |
743 | { | |
744 | conn.response.message = getDefaultResponseMessage(conn.response.code); | |
745 | } | |
746 | ||
747 | conn.response.headers["Host"] = m_serverHost; | |
748 | conn.response.headers["Connection"] = (conn.keepalive ? "keep-alive" : "close"); | |
749 | conn.response.headers["Date"] = formatTimestamp(time(nullptr)); | |
750 | conn.response.headers["Content-Length"] = std::to_string(conn.response.body.size()); | |
751 | } | |
752 | ||
753 | static std::string formatTimestamp(time_t time) | |
754 | { | |
755 | tm tm; | |
756 | #ifdef _WIN32 | |
757 | gmtime_s(&tm, &time); | |
758 | #else | |
759 | gmtime_r(&time, &tm); | |
760 | #endif | |
761 | char buf[32]; | |
762 | strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm); | |
763 | return buf; | |
764 | } | |
765 | ||
766 | public: | |
767 | static char const *getDefaultResponseMessage(int code) | |
768 | { | |
769 | switch (code) | |
770 | { | |
771 | // *INDENT-OFF* | |
772 | case 100: | |
773 | return "Continue"; | |
774 | case 101: | |
775 | return "Switching Protocols"; | |
776 | case 200: | |
777 | return "OK"; | |
778 | case 201: | |
779 | return "Created"; | |
780 | case 202: | |
781 | return "Accepted"; | |
782 | case 203: | |
783 | return "Non-Authoritative Information"; | |
784 | case 204: | |
785 | return "No Content"; | |
786 | case 205: | |
787 | return "Reset Content"; | |
788 | case 206: | |
789 | return "Partial Content"; | |
790 | case 300: | |
791 | return "Multiple Choices"; | |
792 | case 301: | |
793 | return "Moved Permanently"; | |
794 | case 302: | |
795 | return "Found"; | |
796 | case 303: | |
797 | return "See Other"; | |
798 | case 304: | |
799 | return "Not Modified"; | |
800 | case 305: | |
801 | return "Use Proxy"; | |
802 | case 306: | |
803 | return "Switch Proxy"; | |
804 | case 307: | |
805 | return "Temporary Redirect"; | |
806 | case 308: | |
807 | return "Permanent Redirect"; | |
808 | case 400: | |
809 | return "Bad Request"; | |
810 | case 401: | |
811 | return "Unauthorized"; | |
812 | case 402: | |
813 | return "Payment Required"; | |
814 | case 403: | |
815 | return "Forbidden"; | |
816 | case 404: | |
817 | return "Not Found"; | |
818 | case 405: | |
819 | return "Method Not Allowed"; | |
820 | case 406: | |
821 | return "Not Acceptable"; | |
822 | case 407: | |
823 | return "Proxy Authentication Required"; | |
824 | case 408: | |
825 | return "Request Timeout"; | |
826 | case 409: | |
827 | return "Conflict"; | |
828 | case 410: | |
829 | return "Gone"; | |
830 | case 411: | |
831 | return "Length Required"; | |
832 | case 412: | |
833 | return "Precondition Failed"; | |
834 | case 413: | |
835 | return "Payload Too Large"; | |
836 | case 414: | |
837 | return "URI Too Long"; | |
838 | case 415: | |
839 | return "Unsupported Media Type"; | |
840 | case 416: | |
841 | return "Range Not Satisfiable"; | |
842 | case 417: | |
843 | return "Expectation Failed"; | |
844 | case 421: | |
845 | return "Misdirected Request"; | |
846 | case 426: | |
847 | return "Upgrade Required"; | |
848 | case 428: | |
849 | return "Precondition Required"; | |
850 | case 429: | |
851 | return "Too Many Requests"; | |
852 | case 431: | |
853 | return "Request Header Fields Too Large"; | |
854 | case 500: | |
855 | return "Internal Server Error"; | |
856 | case 501: | |
857 | return "Not Implemented"; | |
858 | case 502: | |
859 | return "Bad Gateway"; | |
860 | case 503: | |
861 | return "Service Unavailable"; | |
862 | case 504: | |
863 | return "Gateway Timeout"; | |
864 | case 505: | |
865 | return "HTTP Version Not Supported"; | |
866 | case 506: | |
867 | return "Variant Also Negotiates"; | |
868 | case 510: | |
869 | return "Not Extended"; | |
870 | case 511: | |
871 | return "Network Authentication Required"; | |
872 | default: | |
873 | return "???"; | |
874 | // *INDENT-ON* | |
875 | } | |
876 | } | |
877 | }; | |
878 | ||
879 | } // namespace HTTP_SERVER_NS |