Fix the default HTTP error template

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.
This commit is contained in:
Felix Nawothnig 2010-05-26 12:50:59 -04:00 committed by Nick Mathewson
parent 3689bd2d49
commit 06bd05634d
2 changed files with 101 additions and 7 deletions

105
http.c
View File

@ -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);
}

View 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);