From: Christian Hofstaedtler Date: Sat, 5 Oct 2013 17:16:17 +0000 (+0200) Subject: ws: rework exception/error handling X-Git-Tag: rec-3.6.0-rc1~416^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=331969453f1d2f39dbd8980fd82e08ccb7803f10;p=pdns ws: rework exception/error handling HTTP-level errors now cause HttpExceptions, and generate corresponding HTTP status replies. Those replies can be served as HTML, JSON or plain text, depending on what the client wanted. Also, this gets rid of the old "class Exception", and duplicate catch statements, because SessionException is a PDNSException already. --- diff --git a/pdns/session.cc b/pdns/session.cc index 0e338d35a..716be607d 100644 --- a/pdns/session.cc +++ b/pdns/session.cc @@ -291,7 +291,7 @@ Server::Server(int port, const string &localaddress) s = socket(d_local.sin4.sin_family ,SOCK_STREAM,0); if(s < 0) - throw Exception(string("socket: ")+strerror(errno)); + throw SessionException(string("socket: ")+strerror(errno)); Utility::setCloseOnExec(s); diff --git a/pdns/session.hh b/pdns/session.hh index 998876017..c2902520a 100644 --- a/pdns/session.hh +++ b/pdns/session.hh @@ -103,12 +103,4 @@ private: int s; }; -class Exception -{ -public: - Exception(){reason="Unspecified";}; - Exception(string r){reason=r;}; - string reason; -}; - #endif /* SESSION_HH */ diff --git a/pdns/webserver.cc b/pdns/webserver.cc index 79a6cd314..8e0d8f693 100644 --- a/pdns/webserver.cc +++ b/pdns/webserver.cc @@ -25,6 +25,7 @@ #include #include "dns.hh" #include "base64.hh" +#include "json.hh" mapWebServer::d_functions; @@ -47,16 +48,19 @@ void WebServer::setCaller(void *that) } void *WebServer::serveConnection(void *p) -{ +try { pthread_detach(pthread_self()); Session *client=static_cast(p); + bool want_html=false; + bool want_json=false; + try { string line; client->setTimeout(5); client->getLine(line); stripLine(line); if(line.empty()) - throw Exception("Invalid web request"); + throw HttpBadRequestException(); // L<<"page: "< parts; @@ -110,27 +114,44 @@ void *WebServer::serveConnection(void *p) client->getLine(line); stripLine(line); - // L<cparts; stringtok(cparts,plain,":"); - // L<putLine("HTTP/1.1 401 OK\n"); - client->putLine("WWW-Authenticate: Basic realm=\"PowerDNS\"\n"); - - client->putLine("Connection: close\n"); - client->putLine("Content-Type: text/html; charset=utf-8\n\n"); - client->putLine("Please enter a valid password!\n"); - client->close(); - delete client; - return 0; - } + if(!d_password.empty() && !authOK) + throw HttpUnauthorizedException(); HandlerFunction *fptr; if(d_functions.count(baseUrl) && (fptr=d_functions[baseUrl])) { @@ -163,44 +175,45 @@ void *WebServer::serveConnection(void *p) client->putLine(ret); } else { - client->putLine("HTTP/1.1 404 Not found\n"); - client->putLine("Connection: close\n"); - client->putLine("Content-Type: text/html; charset=utf-8\n\n"); - // FIXME: CSS problem? - client->putLine("

Did not find file '"+baseUrl+"'\n"); + throw HttpNotFoundException(); } - - client->close(); - delete client; - client=0; - return 0; } - catch(SessionTimeoutException &e) { - // L<close(); - delete client; - client=0; + catch(HttpException &e) { + client->putLine(e.statusLine()); + client->putLine("Connection: close\n"); + client->putLine(e.headers()); + if(want_html) { + client->putLine("Content-Type: text/html; charset=utf-8\n\n"); + client->putLine("" + e.what() + "

" + e.what() + "

"); + } else if (want_json) { + client->putLine("Content-Type: application/json\n\n"); + client->putLine(returnJSONError(e.what())); + } else { + client->putLine("Content-Type: text/plain; charset=utf-8\n\n"); + client->putLine(e.what()); + } } + + client->close(); + delete client; + client=0; + return 0; } +catch(SessionTimeoutException &e) { + // L<(d_status_code) + " " + d_reason_phrase + "\n"; + } + + virtual std::string headers() const { + return ""; + } + + virtual std::string what() const { + return d_reason_phrase; + } + +private: + int d_status_code; + std::string d_reason_phrase; +}; + +class HttpBadRequestException : public HttpException { +public: + HttpBadRequestException() : HttpException(400, "Bad Request") { }; +}; + +class HttpUnauthorizedException : public HttpException { +public: + HttpUnauthorizedException() : HttpException(401, "Unauthorized") { }; + + std::string headers() const { + return "WWW-Authenticate: Basic realm=\"PowerDNS\"\n"; + } +}; + +class HttpNotFoundException : public HttpException { +public: + HttpNotFoundException() : HttpException(404, "Not Found") { }; +}; + +class HttpMethodNotAllowedException : public HttpException { +public: + HttpMethodNotAllowedException() : HttpException(405, "Method Not Allowed") { }; +}; + class WebServer { public: diff --git a/pdns/ws.cc b/pdns/ws.cc index d7ea5cebb..4b17211f8 100644 --- a/pdns/ws.cc +++ b/pdns/ws.cc @@ -273,7 +273,7 @@ static int intFromJson(const Value& val) { } else if (val.IsString()) { return atoi(val.GetString()); } else { - throw Exception("Value not an Integer"); + throw PDNSException("Value not an Integer"); } } @@ -429,9 +429,8 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v return returnJSONObject(object); } else if(command == "pdns-control") { - // TODO: turn this into a 405 if(method!="POST") - return returnJSONError("pdns-control requires a POST"); + throw HttpMethodNotAllowedException(); // cout<<"post: "<(post.c_str()).HasParseError()) @@ -560,8 +559,7 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v map success; // empty success object return returnJSONObject(success); } else { - // TODO: turn this into a 405 - return returnJSONError("Method not allowed"); + throw HttpMethodNotAllowedException(); } } else if(command=="log-grep") {