]> git.proxmox.com Git - ceph.git/blame - ceph/src/jaegertracing/opentelemetry-cpp/ext/include/opentelemetry/ext/http/server/http_server.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / jaegertracing / opentelemetry-cpp / ext / include / opentelemetry / ext / http / server / http_server.h
CommitLineData
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
23namespace HTTP_SERVER_NS
24{
25
26constexpr const char *CONTENT_TYPE = "Content-Type";
27constexpr const char *CONTENT_TYPE_TEXT = "text/plain";
28constexpr const char *CONTENT_TYPE_BIN = "application/octet-stream";
29
30struct 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
40struct HttpResponse
41{
42 int code;
43 std::string message;
44 std::map<std::string, std::string> headers;
45 std::string body;
46};
47
48using CallbackFunction = std::function<int(HttpRequest const &request, HttpResponse &response)>;
49
50class HttpRequestCallback
51{
52protected:
53 CallbackFunction callback = nullptr;
54
55public:
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
89class HttpServer : private SocketTools::Reactor::SocketCallback
90{
91protected:
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
159public:
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
238protected:
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
350protected:
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
766public:
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