#include
#include
void li_response_init(liResponse *resp) {
resp->headers = li_http_headers_new();
resp->http_status = 0;
resp->transfer_encoding = LI_HTTP_TRANSFER_ENCODING_IDENTITY;
}
void li_response_reset(liResponse *resp) {
li_http_headers_reset(resp->headers);
resp->http_status = 0;
resp->transfer_encoding = LI_HTTP_TRANSFER_ENCODING_IDENTITY;
}
void li_response_clear(liResponse *resp) {
li_http_headers_free(resp->headers);
resp->http_status = 0;
resp->transfer_encoding = LI_HTTP_TRANSFER_ENCODING_IDENTITY;
}
gboolean li_response_send_headers(liConnection *con) {
GString *head;
liVRequest *vr = con->mainvr;
if (vr->response.http_status < 100 || vr->response.http_status > 999) {
VR_ERROR(vr, "wrong status: %i", vr->response.http_status);
return FALSE;
}
head = g_string_sized_new(8*1024-1);
if (0 == con->out->length && con->mainvr->backend == NULL
&& vr->response.http_status >= 400 && vr->response.http_status < 600) {
/*li_chunkqueue_append_mem(con->out, CONST_STR_LEN("Custom error\r\n"));*/
li_response_send_error_page(con);
}
if (con->mainvr->backend == NULL) {
con->out->is_closed = TRUE;
}
if ((vr->response.http_status >= 100 && vr->response.http_status < 200) ||
vr->response.http_status == 204 ||
vr->response.http_status == 205 ||
vr->response.http_status == 304) {
/* They never have a content-body/length */
li_chunkqueue_reset(con->out);
con->out->is_closed = TRUE;
} else if (con->out->is_closed) {
g_string_printf(con->wrk->tmp_str, "%"L_GOFFSET_FORMAT, con->out->length);
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(con->wrk->tmp_str));
} else if (con->keep_alive && vr->request.http_version == LI_HTTP_VERSION_1_1) {
/* TODO: maybe someone set a content length header? */
if (!(vr->response.transfer_encoding & LI_HTTP_TRANSFER_ENCODING_CHUNKED)) {
vr->response.transfer_encoding |= LI_HTTP_TRANSFER_ENCODING_CHUNKED;
li_http_header_append(vr->response.headers, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked"));
}
} else {
/* Unknown content length, no chunked encoding */
con->keep_alive = FALSE;
}
if (vr->request.http_method == LI_HTTP_METHOD_HEAD) {
/* content-length is set, but no body */
li_chunkqueue_reset(con->out);
con->out->is_closed = TRUE;
}
/* Status line */
if (vr->request.http_version == LI_HTTP_VERSION_1_1) {
g_string_append_len(head, CONST_STR_LEN("HTTP/1.1 "));
if (!con->keep_alive)
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("close"));
} else {
g_string_append_len(head, CONST_STR_LEN("HTTP/1.0 "));
if (con->keep_alive)
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive"));
}
{
guint len;
gchar status_str[4];
gchar *str = li_http_status_string(vr->response.http_status, &len);
li_http_status_to_str(vr->response.http_status, status_str);
status_str[3] = ' ';
g_string_append_len(head, status_str, 4);
g_string_append_len(head, str, len);
g_string_append_len(head, CONST_STR_LEN("\r\n"));
}
/* Append headers */
{
liHttpHeader *header;
GList *iter;
gboolean have_date = FALSE, have_server = FALSE;
for (iter = g_queue_peek_head_link(&vr->response.headers->entries); iter; iter = g_list_next(iter)) {
header = (liHttpHeader*) iter->data;
g_string_append_len(head, GSTR_LEN(header->data));
g_string_append_len(head, CONST_STR_LEN("\r\n"));
if (!have_date && http_header_key_is(header, CONST_STR_LEN("date"))) have_date = TRUE;
if (!have_server && http_header_key_is(header, CONST_STR_LEN("server"))) have_server = TRUE;
}
if (!have_date) {
GString *d = li_worker_current_timestamp(con->wrk, LI_GMTIME, LI_TS_FORMAT_HEADER);
/* HTTP/1.1 requires a Date: header */
g_string_append_len(head, CONST_STR_LEN("Date: "));
g_string_append_len(head, GSTR_LEN(d));
g_string_append_len(head, CONST_STR_LEN("\r\n"));
}
if (!have_server) {
GString *tag = CORE_OPTION(LI_CORE_OPTION_SERVER_TAG).string;
if (tag->len) {
g_string_append_len(head, CONST_STR_LEN("Server: "));
g_string_append_len(head, GSTR_LEN(tag));
g_string_append_len(head, CONST_STR_LEN("\r\n"));
}
}
}
g_string_append_len(head, CONST_STR_LEN("\r\n"));
li_chunkqueue_append_string(con->raw_out, head);
return TRUE;
}
#define SET_LEN_AND_RETURN_STR(x) \
do { \
*len = sizeof(x) - 1; \
return x; \
} while(0)
static gchar *li_response_error_description(guint status_code, guint *len) {
switch (status_code) {
/* 4XX client error */
case 400: SET_LEN_AND_RETURN_STR("Your browser sent a request that this server could not understand.
");
case 401: SET_LEN_AND_RETURN_STR(
"This server could not verify that you are authorized to access the resource requested.
"
"Either you supplied the wrong credentials (e.g. bad password), or your browser doesn't understand how to supply the credentials required.
"
);
case 402: SET_LEN_AND_RETURN_STR("Insert coint.
");
case 403: SET_LEN_AND_RETURN_STR("You don't have permission to access the requested resource.
");
case 404: SET_LEN_AND_RETURN_STR("The requested resource was not found.
Make sure you entered the correct URL in your browser.
\n");
case 405: SET_LEN_AND_RETURN_STR("The requested method is not allowed for this resource.
");
case 406: SET_LEN_AND_RETURN_STR("An appropriate representation of the requested resource could not be found.
");
case 407: SET_LEN_AND_RETURN_STR(
"This server could not verify that you are authorized to access the resource requested.
"
"Either you supplied the wrong credentials (e.g. bad password), or your browser doesn't understand how to supply the credentials required.
"
);
case 408: SET_LEN_AND_RETURN_STR("Timeout waiting for the request.
");
case 409: SET_LEN_AND_RETURN_STR("The request could not be completed due to a conflict with the current state of the requested resource.
");
case 410: SET_LEN_AND_RETURN_STR(
"The requested resource is no longer available on this server and there is no forwarding address.
"
"Please remove all references to this resource.
"
);
case 411: SET_LEN_AND_RETURN_STR("The requested method requires a valid Content-Length header.
");
case 412: SET_LEN_AND_RETURN_STR("The precondition for this request was not met.
");
case 413: SET_LEN_AND_RETURN_STR("Your client sent too much data for this request.
");
case 414: SET_LEN_AND_RETURN_STR("The requested URL's length exceeds the configured limit of this server.
");
case 415: SET_LEN_AND_RETURN_STR("The supplied request data is not in a format acceptable for processing by this resource.
");
case 416: SET_LEN_AND_RETURN_STR("The requested part of this resource was not found.
");
case 417: SET_LEN_AND_RETURN_STR("The expectation given in the Expect request-header field could not be met by this server.
");
case 418: SET_LEN_AND_RETURN_STR("I have come here to brew coffee and kick ass... and I'm all out of coffee.
");
case 421: SET_LEN_AND_RETURN_STR("There are too many connections from your internet address.
");
case 422: SET_LEN_AND_RETURN_STR("The request could not be processed.
");
case 423: SET_LEN_AND_RETURN_STR("The requested resource is currently locked.
");
case 424: SET_LEN_AND_RETURN_STR("The method could not be performed on the resource because the requested action depended on another action and that other action failed.
");
case 426: SET_LEN_AND_RETURN_STR("This resource should be accessed via SSL.
");
/* 5XX server error */
case 500: SET_LEN_AND_RETURN_STR("The server encountered an internal error and was unable to complete your request.
The request method is not supported by this server.
");
case 502: SET_LEN_AND_RETURN_STR("The proxy server received an invalid response from an upstream server.
");
case 503: SET_LEN_AND_RETURN_STR("The server is temporarily unable to service your request due to maintenance downtime or capacity problems.
Please try again later.
");
case 504: SET_LEN_AND_RETURN_STR("The proxy server did not receive a timely response from the upstream server.
");
case 505: SET_LEN_AND_RETURN_STR("The requested http version is not supported by this server.
");
case 506: SET_LEN_AND_RETURN_STR("A variant for the requested resource is itself a negotiable resource.
The request could not be fullfilled because of insufficient storage available on the server.");
case 509: SET_LEN_AND_RETURN_STR("The available bandwidth limit for this resource has been reached.
");
case 510: SET_LEN_AND_RETURN_STR("A mandatory extension policy in the request is not accepted by the server for this resource.
");
/* unknown */
default: SET_LEN_AND_RETURN_STR("");
}
}
void li_response_send_error_page(liConnection *con) {
liVRequest *vr = con->mainvr;
gchar status_code[3];
guint len;
gchar *str;
GString *html;
html = g_string_sized_new(1023);
g_string_append_len(html, CONST_STR_LEN(
"\n"
"\n"
" \n"
" "
));
li_http_status_to_str(con->mainvr->response.http_status, status_code);
g_string_append_len(html, status_code, 3);
g_string_append_len(html, CONST_STR_LEN(" - "));
str = li_http_status_string(con->mainvr->response.http_status, &len);
g_string_append_len(html, str, len);
g_string_append_len(html, CONST_STR_LEN(
"\n"
" \n"
" \n"
" \n"
" \n"
"
Error "
));
g_string_append_len(html, status_code, 3);
g_string_append_len(html, CONST_STR_LEN(" - "));
g_string_append_len(html, str, len);
g_string_append_len(html, CONST_STR_LEN("
\n"));
str = li_response_error_description(con->mainvr->response.http_status, &len);
g_string_append_len(html, str, len);
g_string_append_len(html, CONST_STR_LEN("
"));
g_string_append_len(html, GSTR_LEN(CORE_OPTION(LI_CORE_OPTION_SERVER_TAG).string));
g_string_append_len(html, CONST_STR_LEN(
"
\n"
"
\n"
" \n"
"\n"
));
li_chunkqueue_append_string(con->out, html);
}