2008-08-06 18:46:42 +00:00
|
|
|
|
|
|
|
#include "base.h"
|
2008-08-13 00:18:35 +00:00
|
|
|
#include "utils.h"
|
2008-08-15 15:17:04 +00:00
|
|
|
#include "plugin_core.h"
|
2008-08-06 18:46:42 +00:00
|
|
|
|
|
|
|
void response_init(response *resp) {
|
|
|
|
resp->headers = http_headers_new();
|
|
|
|
resp->http_status = 0;
|
|
|
|
resp->transfer_encoding = HTTP_TRANSFER_ENCODING_IDENTITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
void response_reset(response *resp) {
|
2008-09-29 13:46:58 +00:00
|
|
|
http_headers_reset(resp->headers);
|
2008-08-06 18:46:42 +00:00
|
|
|
resp->http_status = 0;
|
|
|
|
resp->transfer_encoding = HTTP_TRANSFER_ENCODING_IDENTITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
void response_clear(response *resp) {
|
2008-09-29 13:46:58 +00:00
|
|
|
http_headers_free(resp->headers);
|
2008-08-06 18:46:42 +00:00
|
|
|
resp->http_status = 0;
|
|
|
|
resp->transfer_encoding = HTTP_TRANSFER_ENCODING_IDENTITY;
|
|
|
|
}
|
|
|
|
|
2008-09-08 00:20:55 +00:00
|
|
|
void response_send_headers(connection *con) {
|
2008-08-06 22:26:17 +00:00
|
|
|
GString *head = g_string_sized_new(8*1024);
|
2008-08-06 18:46:42 +00:00
|
|
|
|
|
|
|
if (con->response.http_status < 100 || con->response.http_status > 999) {
|
|
|
|
con->response.http_status = 500;
|
|
|
|
con->content_handler = NULL;
|
|
|
|
chunkqueue_reset(con->out);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 == con->out->length && con->content_handler == NULL
|
|
|
|
&& con->response.http_status >= 400 && con->response.http_status < 600) {
|
2008-08-13 00:18:35 +00:00
|
|
|
|
2008-10-02 15:38:57 +00:00
|
|
|
/*chunkqueue_append_mem(con->out, CONST_STR_LEN("Custom error\r\n"));*/
|
|
|
|
response_send_error_page(con);
|
2008-08-06 18:46:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (con->content_handler == NULL) {
|
|
|
|
con->out->is_closed = TRUE;
|
|
|
|
}
|
|
|
|
|
2008-08-06 22:26:17 +00:00
|
|
|
if ((con->response.http_status >= 100 && con->response.http_status < 200) ||
|
|
|
|
con->response.http_status == 204 ||
|
|
|
|
con->response.http_status == 205 ||
|
|
|
|
con->response.http_status == 304) {
|
|
|
|
/* They never have a content-body/length */
|
|
|
|
chunkqueue_reset(con->out);
|
|
|
|
con->out->is_closed = TRUE;
|
|
|
|
} else if (con->out->is_closed) {
|
2008-09-08 00:20:55 +00:00
|
|
|
g_string_printf(con->wrk->tmp_str, "%"L_GOFFSET_FORMAT, con->out->length);
|
|
|
|
http_header_overwrite(con->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(con->wrk->tmp_str));
|
2008-08-06 22:26:17 +00:00
|
|
|
} else if (con->keep_alive && con->request.http_version == HTTP_VERSION_1_1) {
|
2008-08-13 19:50:07 +00:00
|
|
|
if (!(con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED)) {
|
|
|
|
con->response.transfer_encoding |= HTTP_TRANSFER_ENCODING_CHUNKED;
|
|
|
|
http_header_append(con->response.headers, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked"));
|
|
|
|
}
|
2008-08-06 22:26:17 +00:00
|
|
|
} else {
|
|
|
|
/* Unknown content length, no chunked encoding */
|
|
|
|
con->keep_alive = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (con->request.http_method == HTTP_METHOD_HEAD) {
|
|
|
|
/* content-length is set, but no body */
|
|
|
|
chunkqueue_reset(con->out);
|
|
|
|
con->out->is_closed = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Status line */
|
|
|
|
if (con->request.http_version == HTTP_VERSION_1_1) {
|
|
|
|
g_string_append_len(head, CONST_STR_LEN("HTTP/1.1 "));
|
|
|
|
if (!con->keep_alive)
|
|
|
|
http_header_overwrite(con->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)
|
|
|
|
http_header_overwrite(con->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive"));
|
|
|
|
}
|
2008-10-02 15:38:57 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
guint len;
|
|
|
|
gchar status_str[4];
|
|
|
|
gchar *str = http_status_string(con->response.http_status, &len);
|
|
|
|
http_status_to_str(con->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"));
|
|
|
|
}
|
2008-08-06 18:46:42 +00:00
|
|
|
|
2008-08-06 22:26:17 +00:00
|
|
|
/* Append headers */
|
|
|
|
{
|
|
|
|
http_header *header;
|
2008-09-09 14:38:40 +00:00
|
|
|
GList *iter;
|
2008-08-06 22:26:17 +00:00
|
|
|
gboolean have_date = FALSE, have_server = FALSE;
|
|
|
|
|
2008-09-09 14:38:40 +00:00
|
|
|
for (iter = g_queue_peek_head_link(&con->response.headers->entries); iter; iter = g_list_next(iter)) {
|
|
|
|
header = (http_header*) iter->data;
|
|
|
|
g_string_append_len(head, GSTR_LEN(header->data));
|
2008-09-24 20:07:00 +00:00
|
|
|
g_string_append_len(head, CONST_STR_LEN("\r\n"));
|
2008-09-09 14:38:40 +00:00
|
|
|
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;
|
2008-08-06 22:26:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!have_date) {
|
2008-09-08 00:20:55 +00:00
|
|
|
GString *d = worker_current_timestamp(con->wrk);
|
2008-08-06 22:26:17 +00:00
|
|
|
/* 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) {
|
2008-09-26 14:11:08 +00:00
|
|
|
GString *tag = CORE_OPTION(CORE_OPTION_SERVER_TAG).string;
|
2008-08-15 15:17:04 +00:00
|
|
|
|
2008-08-15 18:38:20 +00:00
|
|
|
if (tag->len) {
|
2008-08-15 15:17:04 +00:00
|
|
|
g_string_append_len(head, CONST_STR_LEN("Server: "));
|
2008-08-15 18:38:20 +00:00
|
|
|
g_string_append_len(head, GSTR_LEN(tag));
|
2008-08-15 15:17:04 +00:00
|
|
|
g_string_append_len(head, CONST_STR_LEN("\r\n"));
|
|
|
|
}
|
2008-08-06 22:26:17 +00:00
|
|
|
}
|
|
|
|
}
|
2008-08-06 18:46:42 +00:00
|
|
|
|
|
|
|
g_string_append_len(head, CONST_STR_LEN("\r\n"));
|
|
|
|
chunkqueue_append_string(con->raw_out, head);
|
|
|
|
}
|
2008-10-02 15:38:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
void response_send_error_page(connection *con) {
|
|
|
|
gchar status_str[3];
|
|
|
|
guint len;
|
|
|
|
gchar *str;
|
|
|
|
|
|
|
|
chunkqueue_append_mem(con->out, CONST_STR_LEN(
|
|
|
|
"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
|
|
|
|
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
|
|
|
|
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
|
|
|
|
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
|
|
|
|
" <head>\n"
|
|
|
|
" <title>"
|
|
|
|
));
|
|
|
|
|
|
|
|
http_status_to_str(con->response.http_status, status_str);
|
|
|
|
|
|
|
|
chunkqueue_append_mem(con->out, status_str, 3);
|
|
|
|
chunkqueue_append_mem(con->out, CONST_STR_LEN(" - "));
|
|
|
|
str = http_status_string(con->response.http_status, &len);
|
|
|
|
chunkqueue_append_mem(con->out, str, len);
|
|
|
|
|
|
|
|
chunkqueue_append_mem(con->out, CONST_STR_LEN(
|
|
|
|
"</title>\n"
|
|
|
|
" </head>\n"
|
|
|
|
" <body>\n"
|
|
|
|
" <h1>"
|
|
|
|
));
|
|
|
|
|
|
|
|
chunkqueue_append_mem(con->out, status_str, 3);
|
|
|
|
chunkqueue_append_mem(con->out, CONST_STR_LEN(" - "));
|
|
|
|
chunkqueue_append_mem(con->out, str, len);
|
|
|
|
|
|
|
|
chunkqueue_append_mem(con->out, CONST_STR_LEN(
|
|
|
|
"</h1>\n"
|
|
|
|
" </body>\n"
|
|
|
|
"</html>\n"
|
|
|
|
));
|
|
|
|
}
|