]> granicus.if.org Git - libevent/commitdiff
Fix the default HTTP error template
authorFelix Nawothnig <felix.nawothnig@googlemail.com>
Wed, 26 May 2010 16:50:59 +0000 (12:50 -0400)
committerNick Mathewson <nickm@torproject.org>
Wed, 26 May 2010 17:43:01 +0000 (13:43 -0400)
The current template...

<HTML><HEAD><TITLE>%s</TITLE>
</HEAD><BODY>
<H1>Method Not Implemented</H1>
Invalid method in request<P>
</BODY></HTML>

is highly confusing. The given title is easily overlooked and the
hard-coded content is just plain wrong in most cases (I really read
this as "the server did not understand the requested HTTP method)

This patch changes the template to include the error reason in the
body as well as in the header, and to infer the proper reason from
the status code whenever the reason argument is NULL.

This patch also removes a redundant evhttp_add_header from
evhttp_send_error; evhttp_send_page already adds a "Connection:
close" header.

http.c
include/event2/http.h

diff --git a/http.c b/http.c
index c4fb09c525164709aa89af9055abb7ed227b2338..71850bb8800226874a37214b12a6c246f0c9126c 100644 (file)
--- a/http.c
+++ b/http.c
@@ -171,6 +171,7 @@ static void evhttp_read_header(struct evhttp_connection *evcon,
     struct evhttp_request *req);
 static int evhttp_add_header_internal(struct evkeyvalq *headers,
     const char *key, const char *value);
+static const char *evhttp_response_phrase_internal(int code);
 
 /* callbacks for bufferevent */
 static void evhttp_read_cb(struct bufferevent *, void *);
@@ -2030,11 +2031,11 @@ evhttp_send_done(struct evhttp_connection *evcon, void *arg)
 void
 evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
 {
+
 #define ERR_FORMAT "<HTML><HEAD>\n" \
            "<TITLE>%d %s</TITLE>\n" \
            "</HEAD><BODY>\n" \
-           "<H1>Method Not Implemented</H1>\n" \
-           "Invalid method in request<P>\n" \
+           "<H1>%s</H1>\n" \
            "</BODY></HTML>\n"
 
        struct evbuffer *buf = evbuffer_new();
@@ -2043,13 +2044,13 @@ evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
                evhttp_connection_free(req->evcon);
                return;
        }
-
-       /* close the connection on error */
-       evhttp_add_header(req->output_headers, "Connection", "close");
+       if (reason == NULL) {
+               reason = evhttp_response_phrase_internal(error);
+       }
 
        evhttp_response_code(req, error, reason);
 
-       evbuffer_add_printf(buf, ERR_FORMAT, error, reason);
+       evbuffer_add_printf(buf, ERR_FORMAT, error, reason, reason);
 
        evhttp_send_page(req, buf);
 
@@ -2168,6 +2169,96 @@ evhttp_send_reply_end(struct evhttp_request *req)
        }
 }
 
+static const char *informational_phrases[] = {
+       /* 100 */ "Continue",
+       /* 101 */ "Switching Protocols"
+};
+
+static const char *success_phrases[] = {
+       /* 200 */ "OK",
+       /* 201 */ "Created",
+       /* 202 */ "Accepted",
+       /* 203 */ "Non-Authoritative Information",
+       /* 204 */ "No Content",
+       /* 205 */ "Reset Content",
+       /* 206 */ "Partial Content"
+};
+
+static const char *redirection_phrases[] = {
+       /* 300 */ "Multiple Choices",
+       /* 301 */ "Moved Permanently",
+       /* 302 */ "Found",
+       /* 303 */ "See Other",
+       /* 304 */ "Not Modified",
+       /* 305 */ "Use Proxy",
+       /* 307 */ "Temporary Redirect"
+};
+
+static const char *client_error_phrases[] = {
+       /* 400 */ "Bad Request",
+       /* 401 */ "Unauthorized",
+       /* 402 */ "Payment Required",
+       /* 403 */ "Forbidden",
+       /* 404 */ "Not Found",
+       /* 405 */ "Method Not Allowed",
+       /* 406 */ "Not Acceptable",
+       /* 407 */ "Proxy Authentication Required",
+       /* 408 */ "Request Time-out",
+       /* 409 */ "Conflict",
+       /* 410 */ "Gone",
+       /* 411 */ "Length Required",
+       /* 412 */ "Precondition Failed",
+       /* 413 */ "Request Entity Too Large",
+       /* 414 */ "Request-URI Too Large",
+       /* 415 */ "Unsupported Media Type",
+       /* 416 */ "Requested range not satisfiable",
+       /* 417 */ "Expectation Failed"
+};
+
+static const char *server_error_phrases[] = {
+       /* 500 */ "Internal Server Error",
+       /* 501 */ "Not Implemented",
+       /* 502 */ "Bad Gateway",
+       /* 503 */ "Service Unavailable",
+       /* 504 */ "Gateway Time-out",
+       /* 505 */ "HTTP Version not supported"
+};
+
+struct response_class {
+       const char *name;
+       size_t num_responses;
+       const char **responses;
+};
+
+#ifndef MEMBERSOF
+#define MEMBERSOF(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+static const struct response_class response_classes[] = {
+       /* 1xx */ { "Informational", MEMBERSOF(informational_phrases), informational_phrases },
+       /* 2xx */ { "Success", MEMBERSOF(success_phrases), success_phrases },
+       /* 3xx */ { "Redirection", MEMBERSOF(redirection_phrases), redirection_phrases },
+       /* 4xx */ { "Client Error", MEMBERSOF(client_error_phrases), client_error_phrases },
+       /* 5xx */ { "Server Error", MEMBERSOF(server_error_phrases), server_error_phrases }
+};
+
+static const char *
+evhttp_response_phrase_internal(int code)
+{
+       int klass = code / 100 - 1;
+       int subcode = code % 100;
+
+       /* Unknown class - can't do any better here */
+       if (klass < 0 || klass >= MEMBERSOF(response_classes))
+               return "Unknown Status Class";
+
+       /* Unknown sub-code, return class name at least */
+       if (subcode >= response_classes[klass].num_responses)
+               return response_classes[klass].name;
+
+       return response_classes[klass].responses[subcode];
+}
+
 void
 evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
 {
@@ -2175,6 +2266,8 @@ evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
        req->response_code = code;
        if (req->response_code_line != NULL)
                mm_free(req->response_code_line);
+       if (reason == NULL)
+               reason = evhttp_response_phrase_internal(code);
        req->response_code_line = mm_strdup(reason);
 }
 
index b4176bf5a56b01f87685a9b7d23656844759b004..cab068a5e9110c220c0bf793df3cea41706fd54c 100644 (file)
@@ -249,7 +249,8 @@ void evhttp_set_timeout(struct evhttp *http, int timeout_in_secs);
  *
  * @param req a request object
  * @param error the HTTP error code
- * @param reason a brief explanation of the error
+ * @param reason a brief explanation of the error.  If this is NULL, we'll
+ *    just use the standard meaning of the error code.
  */
 void evhttp_send_error(struct evhttp_request *req, int error,
     const char *reason);