Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: 1d6ff06b29
Fetching contributors…

Cannot retrieve contributors at this time

2977 lines (2455 sloc) 72.466 kb
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Cherokee
*
* Authors:
* Alvaro Lopez Ortega <alvaro@alobbs.com>
*
* Copyright (C) 2001-2011 Alvaro Lopez Ortega
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "common-internal.h"
#include "connection.h"
#include "connection-protected.h"
#include "bogotime.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "util.h"
#include "list.h"
#include "http.h"
#include "handler.h"
#include "thread.h"
#include "handler_error.h"
#include "buffer.h"
#include "config_entry.h"
#include "server-protected.h"
#include "access.h"
#include "virtual_server.h"
#include "socket.h"
#include "header.h"
#include "header_op.h"
#include "header-protected.h"
#include "iocache.h"
#include "dtm.h"
#include "flcache.h"
#define ENTRIES "core,connection"
ret_t
cherokee_connection_new (cherokee_connection_t **conn)
{
CHEROKEE_NEW_STRUCT(n, connection);
INIT_LIST_HEAD (&n->list_node);
n->error_code = http_ok;
n->phase = phase_reading_header;
n->auth_type = http_auth_nothing;
n->req_auth_type = http_auth_nothing;
n->upgrade = http_upgrade_nothing;
n->options = conn_op_nothing;
n->handler = NULL;
n->encoder = NULL;
n->encoder_new_func = NULL;
n->encoder_props = NULL;
n->logger_ref = NULL;
n->keepalive = 0;
n->range_start = -1;
n->range_end = -1;
n->vserver = NULL;
n->arguments = NULL;
n->mmaped = NULL;
n->mmaped_len = 0;
n->io_entry_ref = NULL;
n->thread = NULL;
n->rx = 0;
n->tx = 0;
n->rx_partial = 0;
n->tx_partial = 0;
n->traffic_next = 0;
n->validator = NULL;
n->timeout = -1;
n->timeout_lapse = -1;
n->timeout_header = NULL;
n->polling_fd = -1;
n->polling_multiple = false;
n->polling_mode = FDPOLL_MODE_NONE;
n->expiration = cherokee_expiration_none;
n->expiration_time = 0;
n->expiration_prop = cherokee_expiration_prop_none;
n->respins = 0;
n->limit_rate = false;
n->limit_bps = 0;
n->limit_blocked_until = 0;
cherokee_buffer_init (&n->buffer);
cherokee_buffer_init (&n->header_buffer);
cherokee_buffer_init (&n->incoming_header);
cherokee_buffer_init (&n->encoder_buffer);
cherokee_buffer_init (&n->logger_real_ip);
cherokee_buffer_init (&n->local_directory);
cherokee_buffer_init (&n->web_directory);
cherokee_buffer_init (&n->effective_directory);
cherokee_buffer_init (&n->userdir);
cherokee_buffer_init (&n->request);
cherokee_buffer_init (&n->pathinfo);
cherokee_buffer_init (&n->redirect);
cherokee_buffer_init (&n->host);
cherokee_buffer_init (&n->host_port);
cherokee_buffer_init (&n->self_trace);
n->error_internal_code = http_unset;
cherokee_buffer_init (&n->error_internal_url);
cherokee_buffer_init (&n->error_internal_qs);
cherokee_buffer_init (&n->query_string);
cherokee_buffer_init (&n->request_original);
cherokee_buffer_init (&n->query_string_original);
cherokee_socket_init (&n->socket);
cherokee_header_init (&n->header, header_type_request);
cherokee_post_init (&n->post);
memset (n->regex_ovector, 0, OVECTOR_LEN * sizeof(int));
n->regex_ovecsize = 0;
memset (n->regex_host_ovector, 0, OVECTOR_LEN * sizeof(int));
n->regex_host_ovecsize = 0;
n->chunked_encoding = false;
n->chunked_sent = 0;
n->chunked_last_package = false;
cherokee_buffer_init (&n->chunked_len);
cherokee_config_entry_ref_init (&n->config_entry);
cherokee_flcache_conn_init (&n->flcache);
*conn = n;
return ret_ok;
}
ret_t
cherokee_connection_free (cherokee_connection_t *conn)
{
cherokee_header_mrproper (&conn->header);
cherokee_socket_mrproper (&conn->socket);
if (conn->handler != NULL) {
cherokee_handler_free (conn->handler);
conn->handler = NULL;
}
if (conn->encoder != NULL) {
cherokee_encoder_free (conn->encoder);
conn->encoder = NULL;
}
cherokee_post_mrproper (&conn->post);
cherokee_buffer_mrproper (&conn->request);
cherokee_buffer_mrproper (&conn->request_original);
cherokee_buffer_mrproper (&conn->query_string_original);
cherokee_buffer_mrproper (&conn->logger_real_ip);
cherokee_buffer_mrproper (&conn->pathinfo);
cherokee_buffer_mrproper (&conn->buffer);
cherokee_buffer_mrproper (&conn->header_buffer);
cherokee_buffer_mrproper (&conn->incoming_header);
cherokee_buffer_mrproper (&conn->query_string);
cherokee_buffer_mrproper (&conn->encoder_buffer);
cherokee_buffer_mrproper (&conn->local_directory);
cherokee_buffer_mrproper (&conn->web_directory);
cherokee_buffer_mrproper (&conn->effective_directory);
cherokee_buffer_mrproper (&conn->userdir);
cherokee_buffer_mrproper (&conn->redirect);
cherokee_buffer_mrproper (&conn->host);
cherokee_buffer_mrproper (&conn->host_port);
cherokee_buffer_mrproper (&conn->self_trace);
cherokee_buffer_mrproper (&conn->chunked_len);
cherokee_buffer_mrproper (&conn->error_internal_url);
cherokee_buffer_mrproper (&conn->error_internal_qs);
if (conn->validator != NULL) {
cherokee_validator_free (conn->validator);
conn->validator = NULL;
}
if (conn->arguments != NULL) {
cherokee_avl_free (conn->arguments,
(cherokee_func_free_t) cherokee_buffer_free);
conn->arguments = NULL;
}
if (conn->polling_fd != -1) {
cherokee_fd_close (conn->polling_fd);
conn->polling_fd = -1;
conn->polling_mode = FDPOLL_MODE_NONE;
}
free (conn);
return ret_ok;
}
ret_t
cherokee_connection_clean (cherokee_connection_t *conn)
{
size_t crlf_len;
uint32_t header_len;
cherokee_server_t *srv = CONN_SRV(conn);
/* I/O cache entry reference
*/
if (conn->io_entry_ref != NULL) {
cherokee_iocache_entry_unref (&conn->io_entry_ref);
conn->io_entry_ref = NULL;
}
/* Front-line cache
*/
cherokee_flcache_conn_clean (&conn->flcache);
/* TCP cork
*/
if (conn->options & conn_op_tcp_cork) {
cherokee_connection_set_cork (conn, false);
BIT_UNSET (conn->options, conn_op_tcp_cork);
}
/* POST track
*/
if (srv && srv->post_track) {
srv->post_track->func_unregister (srv->post_track, &conn->post);
}
/* Free handlers before cleaning the conn properties. Its
* free() function might require to read some of them.
*/
if (conn->handler != NULL) {
cherokee_handler_free (conn->handler);
conn->handler = NULL;
}
if (conn->encoder != NULL) {
cherokee_encoder_free (conn->encoder);
conn->encoder = NULL;
}
conn->encoder_new_func = NULL;
conn->encoder_props = NULL;
if (conn->polling_fd != -1) {
cherokee_fd_close (conn->polling_fd);
conn->polling_fd = -1;
}
if (conn->validator != NULL) {
cherokee_validator_free (conn->validator);
conn->validator = NULL;
}
conn->phase = phase_reading_header;
conn->auth_type = http_auth_nothing;
conn->req_auth_type = http_auth_nothing;
conn->upgrade = http_upgrade_nothing;
conn->options = conn_op_nothing;
conn->error_code = http_ok;
conn->range_start = -1;
conn->range_end = -1;
conn->logger_ref = NULL;
conn->mmaped = NULL;
conn->mmaped_len = 0;
conn->rx = 0;
conn->tx = 0;
conn->rx_partial = 0;
conn->tx_partial = 0;
conn->traffic_next = 0;
conn->polling_multiple = false;
conn->polling_mode = FDPOLL_MODE_NONE;
conn->expiration = cherokee_expiration_none;
conn->expiration_time = 0;
conn->expiration_prop = cherokee_expiration_prop_none;
conn->chunked_encoding = false;
conn->chunked_sent = 0;
conn->chunked_last_package = false;
conn->respins = 0;
conn->limit_rate = false;
conn->limit_bps = 0;
conn->limit_blocked_until = 0;
memset (conn->regex_ovector, 0, OVECTOR_LEN * sizeof(int));
conn->regex_ovecsize = 0;
memset (conn->regex_host_ovector, 0, OVECTOR_LEN * sizeof(int));
conn->regex_host_ovecsize = 0;
cherokee_post_clean (&conn->post);
cherokee_buffer_mrproper (&conn->encoder_buffer);
cherokee_buffer_clean (&conn->request);
cherokee_buffer_clean (&conn->request_original);
cherokee_buffer_clean (&conn->query_string_original);
cherokee_buffer_clean (&conn->logger_real_ip);
cherokee_buffer_clean (&conn->pathinfo);
cherokee_buffer_clean (&conn->local_directory);
cherokee_buffer_clean (&conn->web_directory);
cherokee_buffer_clean (&conn->effective_directory);
cherokee_buffer_clean (&conn->userdir);
cherokee_buffer_clean (&conn->redirect);
cherokee_buffer_clean (&conn->host);
cherokee_buffer_clean (&conn->host_port);
cherokee_buffer_clean (&conn->query_string);
cherokee_buffer_clean (&conn->self_trace);
cherokee_buffer_clean (&conn->chunked_len);
conn->error_internal_code = http_unset;
cherokee_buffer_clean (&conn->error_internal_url);
cherokee_buffer_clean (&conn->error_internal_qs);
cherokee_config_entry_ref_clean (&conn->config_entry);
if (conn->arguments != NULL) {
cherokee_avl_free (conn->arguments,
(cherokee_func_free_t) cherokee_buffer_free);
conn->arguments = NULL;
}
/* Drop out the last incoming header
*/
cherokee_header_get_length (&conn->header, &header_len);
cherokee_header_clean (&conn->header);
cherokee_buffer_clean (&conn->buffer);
cherokee_buffer_clean (&conn->header_buffer);
/* Skip trailing CRLF (which may be sent by some HTTP clients)
* only if the number of CRLFs is within the predefine count
* limit otherwise ignore trailing CRLFs so that they will be
* handled in next request. This may avoid a subsequent real
* move_to_begin of the contents left in the buffer.
*/
crlf_len = cherokee_buffer_cnt_spn (&conn->incoming_header, header_len, CRLF);
header_len += (crlf_len <= MAX_HEADER_CRLF) ? crlf_len : 0;
cherokee_buffer_move_to_begin (&conn->incoming_header, header_len);
/* If the connection has incoming headers to be processed,
* then increment the pending counter from the thread
*/
if (! cherokee_buffer_is_empty (&conn->incoming_header)) {
CONN_THREAD(conn)->pending_conns_num++;
}
TRACE (ENTRIES, "conn %p, has headers %d\n", conn,
!cherokee_buffer_is_empty (&conn->incoming_header));
return ret_ok;
}
ret_t
cherokee_connection_clean_close (cherokee_connection_t *conn)
{
/* Close and clean the socket
*/
cherokee_socket_close (&conn->socket);
cherokee_socket_clean (&conn->socket);
/* Make sure the connection Keep-Alive is disabled
*/
conn->keepalive = 0;
cherokee_buffer_clean (&conn->incoming_header);
/* Clean the connection object
*/
cherokee_connection_clean (conn);
return ret_ok;
}
ret_t
cherokee_connection_setup_error_handler (cherokee_connection_t *conn)
{
ret_t ret;
cherokee_server_t *srv;
cherokee_virtual_server_t *vsrv;
cherokee_config_entry_t *entry;
/* NOTE ABOUT THIS FUNCTION: It ought to be call only from
* cherokee/thread.c. Right after calling the function it must
* call continue (becase conn->error_code might have changed).
*/
srv = CONN_SRV(conn);
vsrv = CONN_VSRV(conn);
entry = vsrv->error_handler;
TRACE(ENTRIES, "Setting up error handler: %d (respins=%d)\n",
conn->error_code, conn->respins);
/* It has a common handler. It has to be freed.
*/
if (conn->handler != NULL) {
cherokee_handler_free (conn->handler);
conn->handler = NULL;
}
/* The rid of the encoder as well.
*/
if (conn->encoder_new_func) {
conn->encoder_new_func = NULL;
conn->encoder_props = NULL;
}
if (conn->encoder != NULL) {
cherokee_encoder_free (conn->encoder);
conn->encoder = NULL;
}
/* Loop detection: Set the default error handler and exit.
*/
conn->respins += 1;
if (conn->respins > RESPINS_MAX) {
goto safe;
}
/* Create a new error handler
*/
if ((entry != NULL) && (entry->handler_new_func != NULL)) {
ret = entry->handler_new_func ((void **) &conn->handler, conn, entry->handler_properties);
switch (ret) {
case ret_ok:
/* At this point, two different things might happen:
* - It has got a common handler like handler_redir
* - It has got an error handler like handler_error
*/
conn->phase = phase_init;
TRACE(ENTRIES, "Error handler set. Phase is '%s' now.\n", "init");
goto out;
case ret_eagain:
/* It's an internal error redirection
*/
cherokee_buffer_clean (&conn->pathinfo);
cherokee_buffer_clean (&conn->query_string);
cherokee_buffer_clean (&conn->web_directory);
cherokee_buffer_clean (&conn->local_directory);
conn->error_code = http_ok;
conn->phase = phase_setup_connection;
TRACE(ENTRIES, "Internal redir. Switching to phase 'setup_connection', respins=%d, request=%s\n", conn->respins, conn->request.buf);
goto clean;
default:
TRACE(ENTRIES, "Could not set the error handler. Relying on %s\n", "the default error handler");
break;
}
}
safe:
/* If something was wrong, try with the default error handler
*/
ret = cherokee_handler_error_new (&conn->handler, conn, NULL);
if (unlikely (ret != ret_ok))
return ret_error;
TRACE(ENTRIES, "Default Error handler set. Phase is '%s' now.\n", "init");
conn->phase = phase_init;
out:
#ifdef TRACE_ENABLED
{
const char *name = NULL;
cherokee_module_get_name (MODULE(conn->handler), &name);
TRACE(ENTRIES, "New handler '%s'\n", name ? name : "unknown");
}
#endif
/* Let's keep alive 3xx, 404, and 410 responses.
*/
if (http_type_500 (conn->error_code)) {
conn->keepalive = 0;
} else if (! http_type_300 (conn->error_code) && /* 3xx */
(conn->error_code != http_gone) && /* 410 */
(conn->error_code != http_not_found)) /* 404 */
{
conn->keepalive = 0;
}
ret = ret_ok;
clean:
/* Nothing should be mmaped any longer
*/
if (conn->io_entry_ref != NULL) {
cherokee_iocache_entry_unref (&conn->io_entry_ref);
conn->io_entry_ref = NULL;
}
conn->mmaped = NULL;
conn->mmaped_len = 0;
cherokee_flcache_conn_clean (&conn->flcache);
/* It does not matter whether the previous handler wanted to
* use Chunked encoding.
*/
conn->chunked_encoding = false;
return ret;
}
ret_t
cherokee_connection_setup_hsts_handler (cherokee_connection_t *conn)
{
ret_t ret;
/* Redirect to:
* "https://" + host + request + query_string
*/
cherokee_buffer_clean (&conn->redirect);
cherokee_buffer_add_str (&conn->redirect, "https://");
cherokee_connection_build_host_port_string (conn, &conn->redirect);
cherokee_buffer_add_buffer (&conn->redirect, &conn->request);
if (conn->query_string.len > 0) {
cherokee_buffer_add_char (&conn->redirect, '?');
cherokee_buffer_add_buffer (&conn->redirect, &conn->query_string);
}
/* 301 response: Move Permanetly
*/
conn->error_code = http_moved_permanently;
/* Instance the handler object
*/
ret = cherokee_handler_error_new (&conn->handler, conn, NULL);
if (unlikely (ret != ret_ok)) {
return ret_error;
}
TRACE (ENTRIES, "HSTS redirection handler set. Phase is '%s' now.\n", "init");
conn->phase = phase_init;
return ret_ok;
}
static void
build_response_header_authentication (cherokee_connection_t *conn, cherokee_buffer_t *buffer)
{
ret_t ret;
/* Basic Authenticatiom
* Eg: WWW-Authenticate: Basic realm=""
*/
if (conn->auth_type & http_auth_basic) {
cherokee_buffer_add_str (buffer, "WWW-Authenticate: Basic realm=\"");
cherokee_buffer_add_buffer (buffer, conn->config_entry.auth_realm);
cherokee_buffer_add_str (buffer, "\""CRLF);
}
/* Digest Authentication, Eg:
* WWW-Authenticate: Digest realm="", qop="auth,auth-int",
* nonce="", opaque=""
*/
if (conn->auth_type & http_auth_digest) {
cherokee_thread_t *thread = CONN_THREAD(conn);
cherokee_buffer_t *new_nonce = THREAD_TMP_BUF1(thread);
/* Realm
*/
cherokee_buffer_add_str (buffer, "WWW-Authenticate: Digest realm=\"");
cherokee_buffer_add_buffer (buffer, conn->config_entry.auth_realm);
cherokee_buffer_add_str (buffer, "\", ");
/* Nonce:
* "nonce=\"%s\", "
*/
ret = ret_not_found;
if ((conn->validator != NULL) &&
(! cherokee_buffer_is_empty (&conn->validator->nonce)))
{
ret = cherokee_nonce_table_check (CONN_SRV(conn)->nonces,
&conn->validator->nonce);
}
cherokee_buffer_add_str (buffer, "nonce=\"");
if (ret != ret_ok) {
cherokee_buffer_clean (new_nonce);
cherokee_nonce_table_generate (CONN_SRV(conn)->nonces, conn, new_nonce);
cherokee_buffer_add_buffer (buffer, new_nonce);
} else {
cherokee_buffer_add_buffer (buffer, &conn->validator->nonce);
}
cherokee_buffer_add_str (buffer, "\", ");
/* Quality of protection: auth, auth-int, auth-conf
* Algorithm: MD5
*/
cherokee_buffer_add_str (buffer, "qop=\"auth\", algorithm=\"MD5\""CRLF);
}
}
ret_t
cherokee_connection_read_post (cherokee_connection_t *conn)
{
/* Shortcut
*/
if (conn->handler->read_post == NULL) {
return ret_ok;
}
return cherokee_handler_read_post (conn->handler);
}
void
cherokee_connection_add_expiration_header (cherokee_connection_t *conn,
cherokee_buffer_t *buffer,
cherokee_boolean_t use_maxage)
{
time_t exp_time;
struct tm exp_tm;
char bufstr[DTM_SIZE_GMTTM_STR + 2];
size_t szlen = 0;
cherokee_boolean_t first_prop = true;
/* Expires, and Cache-Control: max-age
*/
switch (conn->expiration) {
case cherokee_expiration_epoch:
cherokee_buffer_add_str (buffer, "Expires: Tue, 01 Jan 1970 00:00:01 GMT" CRLF);
if (conn->expiration_prop != cherokee_expiration_prop_none) {
cherokee_buffer_add_str (buffer, "Cache-Control: ");
}
break;
case cherokee_expiration_max:
cherokee_buffer_add_str (buffer, "Expires: Thu, 31 Dec 2037 23:55:55 GMT" CRLF);
cherokee_buffer_add_str (buffer, "Cache-Control: max-age=315360000");
first_prop = false;
break;
case cherokee_expiration_time:
exp_time = (cherokee_bogonow_now + conn->expiration_time);
cherokee_gmtime (&exp_time, &exp_tm);
szlen = cherokee_dtm_gmttm2str (bufstr, sizeof(bufstr), &exp_tm);
cherokee_buffer_add_str (buffer, "Expires: ");
cherokee_buffer_add (buffer, bufstr, szlen);
cherokee_buffer_add_str (buffer, CRLF);
if (use_maxage) {
cherokee_buffer_add_str (buffer, "Cache-Control: max-age=");
cherokee_buffer_add_long10 (buffer, conn->expiration_time);
first_prop = false;
} else if (conn->expiration_prop) {
cherokee_buffer_add_str (buffer, "Cache-Control: ");
}
break;
default:
SHOULDNT_HAPPEN;
}
/* No properties shortcut
*/
if (conn->expiration_prop == cherokee_expiration_prop_none) {
if (! first_prop)
cherokee_buffer_add_str (buffer, CRLF);
return;
}
/* Caching related properties
*/
#define handle_comma \
do { \
if (first_prop) { \
first_prop = false; \
} else { \
cherokee_buffer_add_str (buffer, ", "); \
} \
} while (false)
if (conn->expiration_prop & cherokee_expiration_prop_public) {
handle_comma;
cherokee_buffer_add_str (buffer, "public");
} else if (conn->expiration_prop & cherokee_expiration_prop_private) {
handle_comma;
cherokee_buffer_add_str (buffer, "private");
} else if (conn->expiration_prop & cherokee_expiration_prop_no_cache) {
handle_comma;
cherokee_buffer_add_str (buffer, "no-cache");
}
if (conn->expiration_prop & cherokee_expiration_prop_no_store) {
handle_comma;
cherokee_buffer_add_str (buffer, "no-store");
}
if (conn->expiration_prop & cherokee_expiration_prop_no_transform) {
handle_comma;
cherokee_buffer_add_str (buffer, "no-transform");
}
if (conn->expiration_prop & cherokee_expiration_prop_must_revalidate) {
handle_comma;
cherokee_buffer_add_str (buffer, "must-revalidate");
}
if (conn->expiration_prop & cherokee_expiration_prop_proxy_revalidate) {
handle_comma;
cherokee_buffer_add_str (buffer, "proxy-revalidate");
}
#undef handle_comma
cherokee_buffer_add_str (buffer, CRLF);
}
static void
build_response_header (cherokee_connection_t *conn,
cherokee_buffer_t *buffer)
{
cherokee_buffer_t *tmp1 = THREAD_TMP_BUF1(CONN_THREAD(conn));
/* Build the response header.
*/
cherokee_buffer_clean (buffer);
/* Add protocol string
*/
switch (conn->header.version) {
case http_version_10:
cherokee_buffer_add_str (buffer, "HTTP/1.0 ");
break;
case http_version_11:
default:
cherokee_buffer_add_str (buffer, "HTTP/1.1 ");
break;
}
/* HTTP response code
*/
if (unlikely (conn->error_internal_code != http_unset)) {
cherokee_http_code_copy (conn->error_internal_code, buffer);
} else {
cherokee_http_code_copy (conn->error_code, buffer);
}
cherokee_buffer_add_str (buffer, CRLF);
/* Add the "Connection:" header
*/
if (conn->upgrade != http_upgrade_nothing) {
cherokee_buffer_add_str (buffer, "Connection: Upgrade"CRLF);
} else if (conn->keepalive > 1) {
if (conn->header.version < http_version_11) {
cherokee_buffer_add_str (buffer, "Connection: Keep-Alive"CRLF);
cherokee_buffer_add_buffer (buffer, conn->timeout_header);
cherokee_buffer_add_str (buffer, ", max=");
cherokee_buffer_add_ulong10 (buffer, conn->keepalive);
cherokee_buffer_add_str (buffer, CRLF);
}
} else {
cherokee_buffer_add_str (buffer, "Connection: close"CRLF);
}
/* Chunked transfer
*/
if (conn->chunked_encoding) {
cherokee_buffer_add_str (buffer, "Transfer-Encoding: chunked" CRLF);
}
/* Exit now if the handler already added all the headers
*/
if ((conn->handler != NULL) &&
(HANDLER_SUPPORTS (conn->handler, hsupport_full_headers)))
return;
/* Date
*/
cherokee_buffer_add_str (buffer, "Date: ");
cherokee_buffer_add_buffer (buffer, &cherokee_bogonow_strgmt);
cherokee_buffer_add_str (buffer, CRLF);
/* Add the Server header
*/
if (&CONN_BIND(conn)->server_string.len > 0) {
cherokee_buffer_add_str (buffer, "Server: ");
cherokee_buffer_add_buffer (buffer, &CONN_BIND(conn)->server_string);
cherokee_buffer_add_str (buffer, CRLF);
}
/* Authentication
*/
if (conn->config_entry.auth_realm != NULL) {
if ((conn->error_code == http_unauthorized) ||
(conn->error_internal_code == http_unauthorized))
{
build_response_header_authentication (conn, buffer);
}
}
/* Redirected connections
*/
if (conn->redirect.len >= 1) {
cherokee_buffer_add_str (buffer, "Location: ");
cherokee_buffer_add_buffer (buffer, &conn->redirect);
cherokee_buffer_add_str (buffer, CRLF);
}
/* Encoder headers
*/
if (conn->encoder) {
/* Front-line Cache (in) */
if (conn->flcache.mode == flcache_mode_in) {
cherokee_buffer_clean (tmp1);
cherokee_encoder_add_headers (conn->encoder, tmp1);
cherokee_buffer_add_buffer (buffer, tmp1);
cherokee_buffer_add_buffer (&conn->flcache.header, tmp1);
}
/* Straight */
else {
cherokee_encoder_add_headers (conn->encoder, buffer);
}
}
}
static void
build_response_header_final (cherokee_connection_t *conn,
cherokee_buffer_t *buffer)
{
cherokee_buffer_t *tmp1 = THREAD_TMP_BUF1(CONN_THREAD(conn));
/* Expiration
*/
if (conn->expiration != cherokee_expiration_none) {
/* Add expiration headers if Front-Line Cache is not
* enabled. If it were, the header was already stored
* in the response file.
*/
if (conn->flcache.mode == flcache_mode_undef) {
cherokee_connection_add_expiration_header (conn, buffer, true);
} else if (conn->flcache.mode == flcache_mode_in) {
cherokee_header_del_entry (buffer, "Cache-Control", 13);
cherokee_connection_add_expiration_header (conn, buffer, true);
}
}
/* Headers ops:
* This must be the last operation
*/
if (conn->config_entry.header_ops) {
cherokee_header_op_render (conn->config_entry.header_ops, &conn->buffer);
}
}
ret_t
cherokee_connection_build_header (cherokee_connection_t *conn)
{
ret_t ret;
/* Front-line cache
*/
if (conn->flcache.mode == flcache_mode_out) {
ret = cherokee_flcache_conn_send_header (&conn->flcache, conn);
if (ret != ret_ok) {
return ret;
}
goto out;
}
/* Try to get the headers from the handler
*/
ret = cherokee_handler_add_headers (conn->handler, &conn->header_buffer);
if (unlikely (ret != ret_ok)) {
switch (ret) {
case ret_eof:
case ret_error:
case ret_eagain:
return ret;
default:
RET_UNKNOWN(ret);
return ret_error;
}
}
/* Front-line Cache (in): Handler headers
*/
if (conn->flcache.mode == flcache_mode_in) {
cherokee_buffer_add_buffer (&conn->flcache.header, &conn->header_buffer);
/* Add X-Cache miss
*/
cherokee_buffer_add_str (&conn->header_buffer, "X-Cache: MISS from ");
cherokee_connection_build_host_port_string (conn, &conn->header_buffer);
cherokee_buffer_add_str (&conn->header_buffer, CRLF);
}
/* Replies with no body cannot use chunked encoding
*/
if ((! http_code_with_body (conn->error_code)) ||
(! http_method_with_body (conn->header.method)))
{
conn->chunked_encoding = false;
}
/* If the connection is using Keep-Alive, it must either know
* the length or use chunked encoding. Otherwise, Keep-Alive
* has to be turned off.
*/
if (conn->keepalive != 0) {
if (! HANDLER_SUPPORTS (conn->handler, hsupport_length)) {
if (! conn->chunked_encoding) {
conn->keepalive = 0;
}
} else {
conn->chunked_encoding = false;
}
}
/* Instance an encoder if needed. Special-case: the proxy
* handler might have instanced a encoded at this stage.
*/
if ((conn->encoder == NULL) &&
(conn->encoder_new_func))
{
/* Internally checks conn_op_cant_encoder */
cherokee_connection_instance_encoder (conn);
}
out:
/* Add the server headers (phase 1)
*/
build_response_header (conn, &conn->buffer);
/* Add handler headers
*/
cherokee_buffer_add_buffer (&conn->buffer, &conn->header_buffer);
/* Add the server headers (phase 2)
*/
build_response_header_final (conn, &conn->buffer);
/* EOH
*/
cherokee_buffer_add_str (&conn->buffer, CRLF);
TRACE(ENTRIES, "Replying:\n%s", conn->buffer.buf);
return ret_ok;
}
ret_t
cherokee_connection_send_header_and_mmaped (cherokee_connection_t *conn)
{
size_t re = 0;
ret_t ret;
int16_t nvec = 1;
struct iovec bufs[2];
/* 1.- Special case: There is not header to send
* because it has been sent by writev() (see below)
*/
if (cherokee_buffer_is_empty (&conn->buffer)) {
ret = cherokee_socket_write (&conn->socket, conn->mmaped, conn->mmaped_len, &re);
if (unlikely (ret != ret_ok) ) {
switch (ret) {
case ret_eof:
case ret_eagain:
return ret;
case ret_error:
conn->keepalive = 0;
return ret;
default:
conn->keepalive = 0;
RET_UNKNOWN(ret);
return ret_error;
}
}
cherokee_connection_tx_add (conn, re);
/* NOTE: conn->mmaped is a ptr. to void
* so we have to apply ptr. math carefully.
*/
conn->mmaped = (void *) ( ((char *)conn->mmaped) + re );
conn->mmaped_len -= (off_t) re;
return (conn->mmaped_len > 0) ? ret_eagain : ret_ok;
}
/* 2.- There are header and mmaped content to send
*/
bufs[0].iov_base = conn->buffer.buf;
bufs[0].iov_len = conn->buffer.len;
if (likely (conn->mmaped_len > 0)) {
bufs[1].iov_base = conn->mmaped;
bufs[1].iov_len = conn->mmaped_len;
nvec = 2;
}
ret = cherokee_socket_writev (&conn->socket, bufs, nvec, &re);
if (unlikely (ret != ret_ok)) {
switch (ret) {
case ret_eof:
case ret_eagain:
return ret;
case ret_error:
conn->keepalive = 0;
return ret_error;
default:
RET_UNKNOWN(ret);
return ret_error;
}
}
/* Add to the connection traffic counter
*/
cherokee_connection_tx_add (conn, re);
/* writev() may not have sent all headers data.
*/
if (unlikely (re < (size_t) conn->buffer.len)) {
/* Partial header data sent.
*/
cherokee_buffer_move_to_begin (&conn->buffer, re);
return ret_eagain;
}
/* OK, all headers have been sent,
* subtract from amount sent and clean header buffer.
*/
re -= (size_t) conn->buffer.len;
cherokee_buffer_clean (&conn->buffer);
/* NOTE: conn->mmaped is a ptr. to void
* so we have to apply ptr. math carefully.
*/
conn->mmaped = (void *) ( ((char *)conn->mmaped) + re );
conn->mmaped_len -= (off_t) re;
return (conn->mmaped_len > 0) ? ret_eagain : ret_ok;
}
void
cherokee_connection_rx_add (cherokee_connection_t *conn, ssize_t rx)
{
if (likely (rx > 0)) {
conn->rx += rx;
conn->rx_partial += rx;
}
}
void
cherokee_connection_tx_add (cherokee_connection_t *conn, ssize_t tx)
{
cuint_t to_sleep;
/* Count it
*/
conn->tx += tx;
conn->tx_partial += tx;
/* Traffic shaping
*/
if (conn->limit_rate) {
to_sleep = (tx * 1000) / conn->limit_bps;
/* It's meant to sleep for a while */
conn->limit_blocked_until = cherokee_bogonow_msec + to_sleep;
}
}
ret_t
cherokee_connection_recv (cherokee_connection_t *conn,
cherokee_buffer_t *buffer,
off_t to_read,
off_t *len)
{
ret_t ret;
size_t cnt_read = 0;
ret = cherokee_socket_bufread (&conn->socket, buffer, to_read, &cnt_read);
switch (ret) {
case ret_ok:
cherokee_connection_rx_add (conn, cnt_read);
*len = cnt_read;
return ret_ok;
case ret_eof:
case ret_error:
return ret;
case ret_eagain:
if (cherokee_socket_pending_read (&conn->socket)) {
CONN_THREAD(conn)->pending_read_num += 1;
}
if (cnt_read > 0) {
cherokee_connection_rx_add (conn, cnt_read);
*len = cnt_read;
return ret_ok;
}
return ret_eagain;
default:
RET_UNKNOWN(ret);
return ret_error;
}
/* NOTREACHED */
}
ret_t
cherokee_connection_reading_check (cherokee_connection_t *conn)
{
/* Check for too long headers
*/
if (conn->incoming_header.len > MAX_HEADER_LEN) {
conn->error_code = http_request_entity_too_large;
return ret_error;
}
return ret_ok;
}
ret_t
cherokee_connection_set_cork (cherokee_connection_t *conn,
cherokee_boolean_t enable)
{
ret_t ret;
ret = cherokee_socket_set_cork (&conn->socket, enable);
if (unlikely (ret != ret_ok)) {
return ret;
}
if (enable) {
BIT_SET (conn->options, conn_op_tcp_cork);
} else {
BIT_UNSET (conn->options, conn_op_tcp_cork);
}
return ret_ok;
}
ret_t
cherokee_connection_send_header (cherokee_connection_t *conn)
{
ret_t ret;
size_t sent = 0;
if (cherokee_buffer_is_empty (&conn->buffer))
return ret_ok;
/* Send the buffer content
*/
ret = cherokee_socket_bufwrite (&conn->socket, &conn->buffer, &sent);
if (unlikely(ret != ret_ok)) return ret;
/* Add to the connection traffic counter
*/
cherokee_connection_tx_add (conn, sent);
/* Drop out the sent data
*/
if (sent == conn->buffer.len) {
cherokee_buffer_clean (&conn->buffer);
return ret_ok;
}
/* There is still some data waiting to be sent
*/
cherokee_buffer_move_to_begin (&conn->buffer, sent);
return ret_eagain;
}
ret_t
cherokee_connection_send (cherokee_connection_t *conn)
{
ret_t ret;
size_t to_send;
size_t sent = 0;
/* EAGAIN handle of chunked responses over SSL. Due a libssl
* restriction, the cryptor will NOT report ret_ok until the
* full content is sent (the buffer can not be touched while
* it is being sent). We generate the plain chunk in-memory in
* order to let this mechanism handle the I/O events.
*/
if ((conn->chunked_encoding) &&
(conn->socket.is_tls == TLS))
{
if (! (conn->options & conn_op_chunked_formatted)) {
cherokee_buffer_prepend_buf (&conn->buffer, &conn->chunked_len);
cherokee_buffer_add_str (&conn->buffer, CRLF);
if (conn->chunked_last_package) {
cherokee_buffer_add_str (&conn->buffer, "0" CRLF CRLF);
}
BIT_SET (conn->options, conn_op_chunked_formatted);
}
}
/* Use writev() to send the chunk-begin mark
*/
else if (conn->chunked_encoding)
{
struct iovec tmp[3];
/* Build the data vector */
tmp[0].iov_base = conn->chunked_len.buf;
tmp[0].iov_len = conn->chunked_len.len;
tmp[1].iov_base = conn->buffer.buf;
tmp[1].iov_len = conn->buffer.len;
/* Traffic shaping */
if ((conn->limit_bps > 0) &&
(conn->limit_bps < conn->buffer.len))
{
tmp[1].iov_len = conn->limit_bps;
}
/* Trailer */
if (conn->chunked_last_package) {
tmp[2].iov_base = CRLF "0" CRLF CRLF;
tmp[2].iov_len = 7;
} else {
tmp[2].iov_base = CRLF;
tmp[2].iov_len = 2;
}
cherokee_iovec_skip_sent (tmp, 3,
conn->chunks, &conn->chunksn,
conn->chunked_sent);
ret = cherokee_socket_writev (&conn->socket, conn->chunks, conn->chunksn, &sent);
switch (ret) {
case ret_ok:
break;
case ret_eof:
case ret_eagain:
return ret;
case ret_error:
conn->keepalive = 0;
return ret_error;
default:
RET_UNKNOWN(ret);
return ret_error;
}
conn->chunked_sent += sent;
/* Clean the information buffer only when everything
* has been sent.
*/
ret = cherokee_iovec_was_sent (tmp, 3, conn->chunked_sent);
if (ret == ret_ok) {
cherokee_buffer_clean (&conn->chunked_len);
cherokee_buffer_clean (&conn->buffer);
ret = ret_ok;
} else {
ret = ret_eagain;
}
goto out;
}
/* Send the buffer content
*/
to_send = conn->buffer.len;
if ((conn->limit_bps > 0) &&
(conn->limit_bps < to_send))
{
to_send = conn->limit_bps;
}
ret = cherokee_socket_write (&conn->socket, conn->buffer.buf, to_send, &sent);
if (unlikely(ret != ret_ok))
return ret;
/* Drop out the sent info
*/
if (sent == conn->buffer.len) {
BIT_UNSET (conn->options, conn_op_chunked_formatted);
cherokee_buffer_clean (&conn->buffer);
ret = ret_ok;
} else if (sent != 0) {
cherokee_buffer_move_to_begin (&conn->buffer, sent);
ret = ret_eagain;
}
out:
/* Add to the connection traffic counter
*/
cherokee_connection_tx_add (conn, sent);
/* If this connection has a handler without Content-Length support
* it has to count the bytes sent
*/
if ((conn->handler) &&
(! HANDLER_SUPPORTS (conn->handler, hsupport_length))) {
conn->range_end += sent;
}
return ret;
}
int
cherokee_connection_should_include_length (cherokee_connection_t *conn)
{
if (conn->encoder_new_func) {
return false;
}
if (conn->encoder) {
return false;
}
return true;
}
ret_t
cherokee_connection_instance_encoder (cherokee_connection_t *conn)
{
ret_t ret;
char *name = NULL;
/* Ensure that the content can be encoded
*/
if (conn->options & conn_op_cant_encoder)
return ret_deny;
if ((! http_code_with_body (conn->error_code)) ||
(! http_method_with_body (conn->header.method)))
return ret_deny;
if (! http_type_200 (conn->error_code))
return ret_deny;
/* Instance and initialize the encoder
*/
ret = conn->encoder_new_func ((void **)&conn->encoder, conn->encoder_props);
if (unlikely (ret != ret_ok)) {
ret = ret_error;
goto error;
}
ret = cherokee_encoder_init (conn->encoder, conn);
switch (ret) {
case ret_ok:
break;
case ret_deny:
/* Refuses to work. Eg: GZip for IE */
goto error;
default:
ret = ret_error;
goto error;
}
/* Update Front-Line cache
*/
if (conn->flcache.mode == flcache_mode_in) {
ret = cherokee_module_get_name (MODULE(conn->encoder), &name);
if (likely ((ret == ret_ok) || (name != NULL))) {
cherokee_buffer_add (&conn->flcache.avl_node_ref->content_encoding, name, strlen(name));
TRACE (ENTRIES, "New entry encoded entry '%s' - %s\n", conn->request.buf, name);
}
}
cherokee_buffer_clean (&conn->encoder_buffer);
return ret_ok;
error:
if (conn->encoder) {
cherokee_encoder_free (conn->encoder);
conn->encoder = NULL;
}
return ret;
}
ret_t
cherokee_connection_shutdown_wr (cherokee_connection_t *conn)
{
ret_t ret;
/* Turn TCP-cork off
*/
if (conn->options & conn_op_tcp_cork) {
cherokee_socket_flush (&conn->socket);
cherokee_connection_set_cork (conn, false);
}
/* At this point, we don't want to follow the TLS protocol
* any longer.
*/
conn->socket.is_tls = non_TLS;
/* Shut down the socket for write, which will send a FIN to
* the peer. If shutdown fails then the socket is unusable.
*/
ret = cherokee_socket_shutdown (&conn->socket, SHUT_WR);
if (unlikely (ret != ret_ok)) {
TRACE (ENTRIES, "Could not shutdown (%d, SHUT_WR)\n", conn->socket.socket);
return ret_error;
}
TRACE (ENTRIES, "Shutdown (%d, SHUT_WR): successful\n", conn->socket.socket);
return ret_ok;
}
ret_t
cherokee_connection_linger_read (cherokee_connection_t *conn)
{
ret_t ret;
size_t cnt_read = 0;
int retries = 2;
cherokee_thread_t *thread = CONN_THREAD(conn);
cherokee_buffer_t *tmp1 = THREAD_TMP_BUF1(thread);
TRACE(ENTRIES",linger", "Linger read, socket status %d\n", conn->socket.status);
while (true) {
/* Read from the socket to nowhere
*/
ret = cherokee_socket_read (&conn->socket, tmp1->buf, tmp1->size, &cnt_read);
switch (ret) {
case ret_eof:
TRACE (ENTRIES",linger", "%s\n", "EOF");
return ret_eof;
case ret_error:
TRACE (ENTRIES",linger", "%s\n", "error");
return ret_error;
case ret_eagain:
TRACE (ENTRIES",linger", "read %u, eagain\n", cnt_read);
return ret_eagain;
case ret_ok:
TRACE (ENTRIES",linger", "%u bytes tossed away\n", cnt_read);
retries--;
if (cnt_read == tmp1->size && retries > 0)
continue;
return ret_eof;
default:
RET_UNKNOWN(ret);
return ret_error;
}
/* NOTREACHED */
}
}
ret_t
cherokee_connection_step (cherokee_connection_t *conn)
{
ret_t ret;
ret_t step_ret = ret_ok;
/* Need to 'read' from handler ?
*/
if (conn->buffer.len > 0) {
return ret_ok;
} else if (unlikely (conn->options & conn_op_got_eof)) {
return ret_eof;
}
/* Front-line cache: Serve content
*/
if (conn->flcache.mode == flcache_mode_out) {
ret = cherokee_flcache_conn_send_body (&conn->flcache, conn);
switch (ret) {
case ret_ok:
return ret_ok;
case ret_eof:
case ret_eof_have_data:
BIT_SET (conn->options, conn_op_got_eof);
return ret;
case ret_error:
case ret_eagain:
case ret_ok_and_sent:
return ret;
default:
RET_UNKNOWN(ret);
return ret;
}
}
/* Do a step in the handler
*/
return_if_fail (conn->handler != NULL, ret_error);
step_ret = cherokee_handler_step (conn->handler, &conn->buffer);
switch (step_ret) {
case ret_ok:
break;
case ret_eof:
case ret_eof_have_data:
BIT_SET (conn->options, conn_op_got_eof);
break;
case ret_error:
case ret_eagain:
case ret_ok_and_sent:
return step_ret;
default:
RET_UNKNOWN(step_ret);
return step_ret;
}
/* Return now if no encoding is needed.
*/
if (conn->encoder != NULL) {
/* Encode handler output.
*/
switch (step_ret) {
case ret_eof:
case ret_eof_have_data:
ret = cherokee_encoder_flush (conn->encoder, &conn->buffer, &conn->encoder_buffer);
step_ret = (conn->encoder_buffer.len == 0) ? ret_eof : ret_eof_have_data;
TRACE (ENTRIES",encoder", "Flush: in=(len %d, size %d) >> out=(len %d, size %d)\n",
conn->buffer.len, conn->buffer.size,
conn->encoder_buffer.len, conn->encoder_buffer.size);
break;
default:
ret = cherokee_encoder_encode (conn->encoder, &conn->buffer, &conn->encoder_buffer);
TRACE (ENTRIES",encoder", "Encode: in=(len %d, size %d) >> out=(len %d, size %d)\n",
conn->buffer.len, conn->buffer.size,
conn->encoder_buffer.len, conn->encoder_buffer.size);
break;
}
if (ret < ret_ok)
return ret;
/* Swap buffers
*/
cherokee_buffer_swap_buffers (&conn->buffer, &conn->encoder_buffer);
cherokee_buffer_clean (&conn->encoder_buffer);
}
/* Front-line cache: Store content
*/
if ((conn->flcache.mode == flcache_mode_in) &&
(! cherokee_buffer_is_empty (&conn->buffer)))
{
cherokee_flcache_conn_write_body (&conn->flcache, conn);
}
/* Chunked encoding header
*/
if (conn->chunked_encoding) {
/* On EOF: Turn off chunked and send the trailer
*/
if (step_ret == ret_eof) {
conn->chunked_encoding = false;
cherokee_buffer_add_str (&conn->buffer, "0" CRLF CRLF);
return ret_eof_have_data;
}
/* On last package: point cherokee_connection_send
* about it. It will add the trailer.
*/
if (step_ret == ret_eof_have_data)
{
conn->chunked_last_package = true;
}
/* Build the Chunked package header:
* len(str(hex(4294967295))+"\r\n\0") = 13
*/
cherokee_buffer_clean (&conn->chunked_len);
cherokee_buffer_ensure_size (&conn->chunked_len, 13);
cherokee_buffer_add_ulong16 (&conn->chunked_len, conn->buffer.len);
cherokee_buffer_add_str (&conn->chunked_len, CRLF);
conn->chunked_sent = 0;
}
return step_ret;
}
static ret_t
get_host (cherokee_connection_t *conn,
char *ptr,
int size)
{
ret_t ret;
char *i;
char *colon = NULL;
char *end = ptr + size;
/* Sanity check
*/
if (unlikely (size <= 0)) {
return ret_error;
}
/* Look up for a colon character
*/
for (i=end-1; i>=ptr; i--) {
if (*i == ':') {
colon = i;
break;
}
}
if (unlikely ((colon != NULL) &&
((colon == ptr) || (end - colon <= 0))))
{
return ret_error;
}
/* Copy the host and port
*/
if (colon) {
ret = cherokee_buffer_add (&conn->host_port, colon+1, end-(colon+1));
if (unlikely (ret != ret_ok))
return ret;
ret = cherokee_buffer_add (&conn->host, ptr, colon - ptr);
if (unlikely (ret != ret_ok))
return ret;
} else {
ret = cherokee_buffer_add (&conn->host, ptr, size);
if (unlikely (ret != ret_ok))
return ret;
}
/* Security check: Hostname shouldn't start with a dot
*/
if ((conn->host.len >= 1) && (*conn->host.buf == '.')) {
return ret_error;
}
/* RFC-1034: Dot ending host names
*/
while ((cherokee_buffer_end_char (&conn->host) == '.') &&
(! cherokee_buffer_is_empty (&conn->host)))
{
cherokee_buffer_drop_ending (&conn->host, 1);
}
return ret_ok;
}
static ret_t
get_encoding (cherokee_connection_t *conn,
char *ptr,
cherokee_avl_t *encoders_accepted)
{
ret_t ret;
char tmp;
char *i1;
char *i2;
char *end;
cherokee_encoder_props_t *props = NULL;
/* ptr = Header at the "Accept-Encoding" position
*/
end = strchr (ptr, CHR_CR);
if (end == NULL) {
return ret_error;
}
*end = '\0'; /* (1) */
i1 = ptr;
do {
while (*i1 == ' ')
i1++;
i2 = strchr (i1, ',');
if (!i2) {
i2 = strchr (i1, ';');
if (!i2) {
i2 = end;
}
}
/* Get the encoder configuration entry
*/
tmp = *i2; /* (2) */
*i2 = '\0';
ret = cherokee_avl_get_ptr (encoders_accepted, i1, (void **)&props);
*i2 = tmp; /* (2') */
if ((ret == ret_ok) && (props != NULL)) {
if (props->perms == cherokee_encoder_allow) {
/* Use encoder
*/
conn->encoder_new_func = props->instance_func;
conn->encoder_props = props;
break;
} else if (props->perms == cherokee_encoder_forbid) {
/* Explicitly forbidden
*/
conn->encoder_new_func = NULL;
conn->encoder_props = NULL;
break;
} else if (props->perms == cherokee_encoder_unset) {
/* Let's it be
*/
break;
}
SHOULDNT_HAPPEN;
return ret_error;
}
/* Next iteration
*/
if (i2 < end) {
i1 = i2+1;
}
} while (i2 < end);
*end = CHR_CR; /* (1') */
return ret_ok;
}
static ret_t
get_authorization (cherokee_connection_t *conn,
cherokee_http_auth_t type,
cherokee_validator_t *validator,
char *ptr,
int ptr_len)
{
ret_t ret;
char *end, *end2;
cuint_t pre_len = 0;
/* It checks that the authentication send by the client is compliant
* with the configuration of the server. It does not check if the
* kind of validator is suitable in this case.
*/
if (equal_str (ptr, "Basic ")) {
/* Check the authentication type
*/
if (!(type & http_auth_basic))
return ret_error;
conn->req_auth_type = http_auth_basic;
pre_len = 6;
} else if (equal_str (ptr, "Digest ")) {
/* Check the authentication type
*/
if (!(type & http_auth_digest))
return ret_error;
conn->req_auth_type = http_auth_digest;
pre_len = 7;
}
/* Skip end of line
*/
end = strchr (ptr, CHR_CR);
end2 = strchr (ptr, CHR_LF);
end = cherokee_min_str (end, end2);
if (end == NULL)
return ret_error;
ptr_len -= (ptr + ptr_len) - end;
/* Skip "Basic " or "Digest "
*/
ptr += pre_len;
ptr_len -= pre_len;
/* Parse the request
*/
switch (conn->req_auth_type) {
case http_auth_basic:
ret = cherokee_validator_parse_basic (validator, ptr, ptr_len);
if (ret != ret_ok) return ret;
break;
case http_auth_digest:
ret = cherokee_validator_parse_digest (validator, ptr, ptr_len);
if (ret != ret_ok) return ret;
/* Check nonce value
*/
if (cherokee_buffer_is_empty(&validator->nonce))
return ret_error;
break;
default:
LOG_ERROR_S (CHEROKEE_ERROR_CONNECTION_AUTH);
return ret_error;
}
return ret_ok;
}
int
cherokee_connection_is_userdir (cherokee_connection_t *conn)
{
return ((conn->request.len > 3) &&
(conn->request.buf[1] == '~'));
}
ret_t
cherokee_connection_set_custom_droot (cherokee_connection_t *conn,
cherokee_config_entry_t *entry)
{
/* Shortcut
*/
if ((entry->document_root == NULL) ||
(entry->document_root->len <= 0))
return ret_ok;
/* Have a special DocumentRoot
*/
BIT_SET (conn->options, conn_op_document_root);
cherokee_buffer_clean (&conn->local_directory);
cherokee_buffer_add_buffer (&conn->local_directory, entry->document_root);
/* It has to drop the webdir from the request:
*
* Directory /thing {
* DocumentRoot /usr/share/this/rocks
* }
*
* on petition: http://server/thing/cherokee
* should read: /usr/share/this/rocks/cherokee
*/
if (cherokee_buffer_is_empty (&conn->request_original)) {
cherokee_buffer_add_buffer (&conn->request_original, &conn->request);
if (! cherokee_buffer_is_empty (&conn->query_string)) {
cherokee_buffer_add_buffer (&conn->query_string_original, &conn->query_string);
}
}
if (conn->web_directory.len > 1) {
cherokee_buffer_move_to_begin (&conn->request, conn->web_directory.len);
}
if ((conn->request.len >= 2) && (strncmp(conn->request.buf, "//", 2) == 0)) {
cherokee_buffer_move_to_begin (&conn->request, 1);
}
TRACE(ENTRIES, "Set Custom Local Directory: '%s'\n", conn->local_directory.buf);
return ret_ok;
}
ret_t
cherokee_connection_build_local_directory (cherokee_connection_t *conn,
cherokee_virtual_server_t *vsrv)
{
ret_t ret;
/* Enhanced Virtual-Hosting
*/
if (vsrv->evhost != NULL) {
TRACE(ENTRIES, "About to evaluate EVHost. Now Document root is: '%s'\n",
conn->local_directory.buf ? conn->local_directory.buf : "");
ret = EVHOST(vsrv->evhost)->func_document_root (vsrv->evhost, conn);
if (ret == ret_ok)
goto ok;
/* Fall to use the default document root
*/
cherokee_buffer_clean (&conn->local_directory);
}
/* Regular request
*/
ret = cherokee_buffer_add_buffer (&conn->local_directory, &vsrv->root);
if (unlikely (ret != ret_ok)) {
goto error;
}
ok:
TRACE(ENTRIES, "Set Local Directory: '%s'\n", conn->local_directory.buf);
return ret_ok;
error:
LOG_ERROR_S (CHEROKEE_ERROR_CONNECTION_LOCAL_DIR);
return ret_error;
}
ret_t
cherokee_connection_build_local_directory_userdir (cherokee_connection_t *conn,
cherokee_virtual_server_t *vsrv)
{
ret_t ret;
struct passwd pwd;
char tmp[1024];
/* Default: it is inside the UserDir in home
*/
ret = cherokee_getpwnam (conn->userdir.buf, &pwd, tmp, sizeof(tmp));
if ((ret != ret_ok) || (pwd.pw_dir == NULL)) {
conn->error_code = http_not_found;
return ret_error;
}
/* Build the local_directory:
*/
cherokee_buffer_add (&conn->local_directory, pwd.pw_dir, strlen(pwd.pw_dir));
cherokee_buffer_add_char (&conn->local_directory, '/');
cherokee_buffer_add_buffer (&conn->local_directory, &vsrv->userdir);
return ret_ok;
}
static ret_t
get_range (cherokee_connection_t *conn, char *ptr, int ptr_len)
{
cuint_t num_len = 0;
CHEROKEE_TEMP(tmp, ptr_len+1);
/* Read the start position
*/
while ((ptr[num_len] != '-') &&
(ptr[num_len] != '\0') &&
(num_len < tmp_size-1)) {
tmp[num_len] = ptr[num_len];
num_len++;
}
tmp[num_len] = '\0';
if (num_len != 0) {
conn->range_start = strtoll (tmp, (char **)NULL, 10);
if (conn->range_start < 0) {
return ret_error;
}
}
/* Advance the pointer
*/
ptr += num_len;
if (*ptr != '-') {
return ret_error;
}
ptr++;
/* Maybe there're an ending position
*/
if ((*ptr != '\0') && (*ptr != CHR_CR) && (*ptr != CHR_LF)) {
num_len = 0;
/* Read the end
*/
while ((ptr[num_len] >= '0') && (ptr[num_len] <= '9') && (num_len < tmp_size-1)) {
tmp[num_len] = ptr[num_len];
num_len++;
}
tmp[num_len] = '\0';
conn->range_end = strtoll (tmp, (char **)NULL, 10);
if (conn->range_end < 0){
return ret_error;
}
}
/* Sanity check: switched range
*/
if ((conn->range_start != -1) && (conn->range_end != -1)) {
if (conn->range_start > conn->range_end) {
conn->error_code = http_range_not_satisfiable;
return ret_error;
}
}
return ret_ok;
}
static ret_t
parse_userdir (cherokee_connection_t *conn)
{
char *begin;
char *end_username;
/* Find user name ending:
*/
begin = &conn->request.buf[2];
end_username = strchr (begin, '/');
if (end_username == NULL) {
/* It has to be redirected
*
* from http://www.alobbs.com/~alo
* to http://www.alobbs.com/~alo/
*/
cherokee_buffer_add_buffer (&conn->redirect, &conn->request);
cherokee_buffer_add_char (&conn->redirect, '/');
conn->error_code = http_moved_permanently;
return ret_error;
}
/* Sanity check:
* The username has to be at least a char long
*/
if ((end_username - begin) <= 0) {
conn->error_code = http_bad_request;
return ret_error;
}
/* Get the user home directory
*/
cherokee_buffer_add (&conn->userdir, begin, end_username - begin);
/* Drop username from the request
*/
cherokee_buffer_move_to_begin (&conn->request, (end_username - conn->request.buf));
return ret_ok;
}
ret_t
cherokee_connection_get_request (cherokee_connection_t *conn)
{
ret_t ret;
char *host;
cuint_t host_len;
cherokee_http_t error_code = http_bad_request;
cherokee_boolean_t read_post = false;
/* Header parsing
*/
ret = cherokee_header_parse (&conn->header, &conn->incoming_header, &error_code);
if (unlikely (ret < ret_ok)) {
goto error;
}
/* Check is request body is present
*/
if (http_method_with_input (conn->header.method)) {
read_post = true;
}
else if (http_method_with_optional_input (conn->header.method)) {
ret = cherokee_header_has_known (&conn->header, header_content_length);
if (ret == ret_ok) {
read_post = true;
} else {
ret = cherokee_header_has_known (&conn->header, header_transfer_encoding);
if (ret == ret_ok) {
read_post = true;
}
}
}
/* Read POST
*/
if (read_post)
{
ret = cherokee_post_read_header (&conn->post, conn);
if (unlikely (ret != ret_ok)) {
return ret;
}
}
/* Copy the request and query string
*/
ret = cherokee_header_copy_request (&conn->header, &conn->request);
if (unlikely (ret < ret_ok))
goto error;
ret = cherokee_header_copy_query_string (&conn->header, &conn->query_string);
if (unlikely (ret < ret_ok))
goto error;
/* "OPTIONS *" special case
*/
if (unlikely ((conn->header.method == http_options) &&
(cherokee_buffer_cmp_str (&conn->request, "*") == 0)))
{
cherokee_buffer_add_buffer (&conn->request_original, &conn->request);
cherokee_buffer_clean (&conn->request);
cherokee_buffer_add_str (&conn->request, "/");
}
/* Look for starting '/' in the request
*/
if (unlikely (conn->request.buf[0] != '/')) {
goto error;
}
#ifdef _WIN32
/* Prevent back-slashes in the request on Windows
*/
TRACE (ENTRIES, "Win32 req before: %s\n", conn->request.buf);
cherokee_buffer_swap_chars (&conn->request, '\\', '/');
TRACE (ENTRIES, "Win32 req after: %s\n", conn->request.buf);
#endif
/* Short the path. It transforms the request:
* /dir1/dir2/../file in /dir1/file
*/
cherokee_path_short (&conn->request);
/* Look for "//"
*/
cherokee_buffer_remove_dups (&conn->request, '/');
/* Read the Host header
*/
ret = cherokee_header_get_known (&conn->header, header_host, &host, &host_len);
switch (ret) {
case ret_error:
case ret_not_found:
if (conn->header.version == http_version_11) {
/* It is needed in HTTP/1.1
*/
TRACE(ENTRIES, "conn %p, HTTP/1.1 with no Host header entry\n", conn);
goto error;
}
break;
case ret_ok:
ret = get_host (conn, host, host_len);
if (unlikely(ret < ret_ok)) goto error;
/* Set the virtual server reference
*/
ret = cherokee_server_get_vserver (CONN_SRV(conn), &conn->host, conn,
(cherokee_virtual_server_t **)&conn->vserver);
if (unlikely (ret != ret_ok)) {
LOG_ERROR (CHEROKEE_ERROR_CONNECTION_GET_VSERVER, conn->host.buf);
return ret_error;
}
break;
default:
RET_UNKNOWN(ret);
return ret_error;
}
/* Userdir requests
*/
if ((!cherokee_buffer_is_empty (&CONN_VSRV(conn)->userdir)) &&
(cherokee_connection_is_userdir (conn)))
{
ret = parse_userdir (conn);
if (ret != ret_ok) return ret;
}
/* Check Upload limit
*/
if ((CONN_VSRV(conn)->post_max_len > 0) &&
(conn->post.len > CONN_VSRV(conn)->post_max_len))
{
conn->error_code = http_request_entity_too_large;
return ret_error;
}
conn->error_code = http_ok;
return ret_ok;
error:
conn->error_code = error_code;
/* Since the request could not be parsed, the connection is
* about to be closed. Log the error now before it's too late.
*/
if (CONN_VSRV(conn)->logger) {
cherokee_logger_write_access (CONN_VSRV(conn)->logger, conn);
}
return ret_error;
}
ret_t
cherokee_connection_check_authentication (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry)
{
ret_t ret;
char *ptr;
cuint_t len;
/* Return, there is nothing to do here
*/
if (config_entry->validator_new_func == NULL)
return ret_ok;
/* Look for authentication in the headers:
* It's done on demand because the directory maybe don't have protection
*/
ret = cherokee_header_get_known (&conn->header, header_authorization, &ptr, &len);
if (ret != ret_ok) {
goto unauthorized;
}
/* Create the validator object
*/
ret = config_entry->validator_new_func ((void **) &conn->validator,
config_entry->validator_properties);
if (ret != ret_ok) {
goto error;
}
/* Read the header information
*/
ret = get_authorization (conn, config_entry->authentication, conn->validator, ptr, len);
if (ret != ret_ok) {
goto unauthorized;
}
/* Check if the user is in the list
*/
if (config_entry->users != NULL) {
void *foo;
if (cherokee_buffer_is_empty (&conn->validator->user)) {
goto unauthorized;
}
ret = cherokee_avl_get (config_entry->users, &conn->validator->user, &foo);
if (ret != ret_ok) {
goto unauthorized;
}
}
/* Check if the validator is suitable
*/
if ((conn->validator->support & conn->req_auth_type) == 0) {
goto error;
}
/* Check the login/password
*/
ret = cherokee_validator_check (conn->validator, conn);
if (ret != ret_ok) {
goto unauthorized;
}
return ret_ok;
unauthorized:
conn->keepalive = 0;
conn->error_code = http_unauthorized;
return ret_error;
error:
conn->keepalive = 0;
conn->error_code = http_internal_error;
return ret_error;
}
ret_t
cherokee_connection_check_ip_validation (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry)
{
ret_t ret;
if (config_entry->access == NULL) {
return ret_ok;
}
ret = cherokee_access_ip_match (config_entry->access, &conn->socket);
if (ret == ret_ok) {
return ret_ok;
}
conn->error_code = http_access_denied;
return ret_error;
}
ret_t
cherokee_connection_check_only_secure (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry)
{
if (config_entry->only_secure == false) {
/* No Only-Secure connection..
*/
return ret_ok;
}
if (conn->socket.is_tls == TLS) {
/* It is secure
*/
return ret_ok;
}
conn->error_code = http_upgrade_required;
conn->upgrade = http_upgrade_tls10;
return ret_error;
}
ret_t
cherokee_connection_check_http_method (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry)
{
if (config_entry->handler_methods & conn->header.method)
return ret_ok;
if (config_entry->handler_methods == http_options)
return ret_ok;
/* Set the error
*/
conn->error_code = http_method_not_allowed;
/* If the HTTP method was not detected, set a safe one
*/
if (conn->header.method == http_unknown) {
conn->header.method = http_get;
}
return ret_error;
}
ret_t
cherokee_connection_create_handler (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry)
{
ret_t ret;
return_if_fail (config_entry->handler_new_func != NULL, ret_error);
/* Create and assign a handler object
*/
ret = (config_entry->handler_new_func) ((void **)&conn->handler, conn, config_entry->handler_properties);
switch (ret) {
case ret_ok:
case ret_eof:
case ret_eagain:
/* Trace the name of the new handler
*/
#ifdef TRACE_ENABLED
if ((conn->handler != NULL) &&
(cherokee_trace_is_tracing()))
{
char *name = NULL;
ret = cherokee_module_get_name (MODULE (conn->handler), &name);
if ((ret == ret_ok) && (name != NULL)) {
TRACE(ENTRIES",handler", "Instanced handler: '%s'\n", name);
}
}
#endif
return ret;
default:
if ((conn->handler == NULL) &&
(conn->error_code == http_ok))
conn->error_code = http_internal_error;
return ret_error;
}
return ret_ok;
}
void
cherokee_connection_set_keepalive (cherokee_connection_t *conn)
{
ret_t ret;
char *ptr;
cuint_t ptr_len;
cherokee_server_t *srv = CONN_SRV(conn);
cherokee_thread_t *thread = CONN_THREAD(conn);
/* Check whether server allows keep-alive
*/
if (srv->keepalive == false)
goto denied;
/* Check the Max concurrent Keep-Alive limit on this thread
*/
if (thread->conns_num >= thread->conns_keepalive_max)
goto denied;
/* Does the virtual server support Keep-Alive?
*/
if (CONN_VSRV(conn)->keepalive == false)
goto denied;
/* Set Keep-alive according with the 'Connection' header
* HTTP 1.1 uses Keep-Alive by default: rfc2616 sec8.1.2
*/
ret = cherokee_header_get_known (&conn->header, header_connection, &ptr, &ptr_len);
if (ret == ret_ok) {
if (conn->header.version == http_version_11) {
if (strncasestrn (ptr, ptr_len, "close", 5) == NULL)
goto granted;
else
goto denied;
} else {
if (strncasestrn (ptr, ptr_len, "keep-alive", 10) != NULL)
goto granted;
else
goto denied;
}
return;
}
if (conn->header.version == http_version_11)
goto granted;
if (conn->flcache.mode == flcache_mode_out)
goto granted;
denied:
TRACE (ENTRIES, "Keep-alive %s\n", "denied");
conn->keepalive = 0;
return;
granted:
if (conn->keepalive == 0)
conn->keepalive = CONN_SRV(conn)->keepalive_max;
TRACE (ENTRIES, "Keep-alive %d\n", conn->keepalive);
}
ret_t
cherokee_connection_set_rate (cherokee_connection_t *conn,
cherokee_config_entry_t *entry)
{
if (entry->limit_bps <= 0)
return ret_ok;
conn->limit_rate = true;
conn->limit_bps = entry->limit_bps;
return ret_ok;
}
void
cherokee_connection_set_chunked_encoding (cherokee_connection_t *conn)
{
/* Conditions to activate Chunked-Encodiog:
* - The server configuration allows to use it
* - It's a keep-alive connection
* - It's a HTTP/1.1 connection
* - It's not serving content from the Front-line cache
*/
conn->chunked_encoding = ((CONN_SRV(conn)->chunked_encoding) &&
(conn->keepalive > 0) &&
(conn->header.version == http_version_11) &&
(conn->flcache.mode != flcache_mode_out));
}
ret_t
cherokee_connection_parse_range (cherokee_connection_t *conn)
{
ret_t ret;
char *ptr;
cuint_t ptr_len;
/* Look for "Range:"
*/
if (HANDLER_SUPPORTS (conn->handler, hsupport_range)) {
ret = cherokee_header_get_known (&conn->header, header_range, &ptr, &ptr_len);
if (ret == ret_ok) {
if (strncmp (ptr, "bytes=", 6) == 0) {
ret = get_range (conn, ptr+6, ptr_len-6);
if (ret < ret_ok) {
conn->error_code = http_range_not_satisfiable;
return ret;
}
}
}
}
return ret_ok;
}
ret_t
cherokee_connection_create_encoder (cherokee_connection_t *conn,
cherokee_avl_t *encoders_accepted)
{
ret_t ret;
char *ptr;
cuint_t ptr_len;
/* No encoders are accepted
*/
if ((encoders_accepted == NULL) ||
(cherokee_avl_is_empty (AVL_GENERIC(encoders_accepted))))
{
return ret_ok;
}
/* Keepalive (Content-Length) connections cannot use encoders
* because the transferred information length would change.
*/
if ((conn->keepalive) &&
(! conn->chunked_encoding) &&
HANDLER_SUPPORTS (conn->handler, hsupport_length))
{
return ret_ok;
}
/* Process the "Accept-Encoding" header
*/
ret = cherokee_header_get_known (&conn->header, header_accept_encoding, &ptr, &ptr_len);
if (ret == ret_ok) {
ret = get_encoding (conn, ptr, encoders_accepted);
if (ret < ret_ok) {
return ret;
}
}
return ret_ok;
}
ret_t
cherokee_connection_parse_args (cherokee_connection_t *conn)
{
ret_t ret;
/* Parse arguments only once
*/
if (conn->arguments != NULL) {
return ret_ok;
}
/* Build a new table
*/
ret = cherokee_avl_new (&conn->arguments);
if (unlikely(ret != ret_ok)) {
return ret;
}
/* Parse the header
*/
ret = cherokee_parse_query_string (&conn->query_string, conn->arguments);
if (unlikely(ret != ret_ok)) {
return ret;
}
return ret_ok;
}
ret_t
cherokee_connection_open_request (cherokee_connection_t *conn)
{
TRACE (ENTRIES, "web_directory='%s' request='%s' local_directory='%s'\n",
conn->web_directory.buf,
conn->request.buf,
conn->local_directory.buf);
/* Ensure the space for headers and I/O buffer
*/
cherokee_buffer_ensure_size (&conn->header_buffer, 384);
cherokee_buffer_ensure_size (&conn->buffer, DEFAULT_READ_SIZE+1);
/* Init the connection handler object
*/
return cherokee_handler_init (conn->handler);
}
ret_t
cherokee_connection_log (cherokee_connection_t *conn)
{
/* Check whether if needs to log now of not
*/
if (conn->logger_ref == NULL) {
return ret_ok;
}
/* Log it
*/
return cherokee_logger_write_access (conn->logger_ref, conn);
}
ret_t
cherokee_connection_update_vhost_traffic (cherokee_connection_t *conn)
{
if (CONN_VSRV(conn)->collector == NULL) {
return ret_ok;
}
cherokee_collector_vsrv_count (CONN_VSRV(conn)->collector,
conn->rx_partial,
conn->tx_partial);
/* Update the time for the next update
*/
conn->traffic_next = cherokee_bogonow_now + DEFAULT_TRAFFIC_UPDATE;
/* Reset partial counters
*/
conn->rx_partial = 0;
conn->tx_partial = 0;
return ret_ok;
}
ret_t
cherokee_connection_clean_for_respin (cherokee_connection_t *conn)
{
TRACE(ENTRIES, "Clean for respin: conn=%p\n", conn);
conn->respins += 1;
if (conn->respins > RESPINS_MAX) {
TRACE(ENTRIES, "Internal redirection limit (%d) exceeded\n", RESPINS_MAX);
conn->error_code = http_internal_error;
return ret_error;
}
if (cherokee_connection_use_webdir(conn)) {
cherokee_buffer_prepend_buf (&conn->request, &conn->web_directory);
cherokee_buffer_clean (&conn->local_directory);
BIT_UNSET (conn->options, conn_op_document_root);
}
cherokee_buffer_clean (&conn->web_directory);
TRACE_CONN(conn);
return ret_ok;
}
ret_t
cherokee_connection_clean_error_headers (cherokee_connection_t *conn)
{
char *begin;
char *end;
if (cherokee_buffer_is_empty (&conn->header_buffer))
return ret_ok;
begin = strncasestrn_s (conn->header_buffer.buf,
conn->header_buffer.len,
"Content-Length: ");
if (begin != NULL) {
end = strchr (begin+16, CHR_CR);
if (end == NULL)
return ret_error;
if (end[1] == CHR_LF)
end++;
cherokee_buffer_remove_chunk (&conn->header_buffer,
begin - conn->header_buffer.buf,
(end-begin)+1);
}
return ret_ok;
}
int
cherokee_connection_use_webdir (cherokee_connection_t *conn)
{
if (! (conn->options & conn_op_document_root))
return 0;
if (cherokee_buffer_is_empty (&conn->web_directory))
return 0;
return 1;
}
#ifdef TRACE_ENABLED
char *
cherokee_connection_print (cherokee_connection_t *conn)
{
const char *phase;
cherokee_buffer_t *buf = &conn->self_trace;
/* Shortcut: Don't render if not tracing
*/
if (! cherokee_trace_is_tracing()) {
return (char *)"";
}
/* Render
*/
cherokee_buffer_clean (buf);
if (conn == NULL) {
cherokee_buffer_add_str (buf, "Connection is NULL\n");
return buf->buf;
}
phase = cherokee_connection_get_phase_str (conn);
cherokee_buffer_add_va (buf, "Connection %p info\n", conn);
#define print_buf(title,name) \
cherokee_buffer_add_va (buf, "\t| %s: '%s' (%d)\n", title, \
(name)->buf ? (name)->buf : "", \
(name)->len);
#define print_cbuf(title,name) \
print_buf(title, &conn->name)
#define print_cint(title,name) \
cherokee_buffer_add_va (buf, "\t| %s: %d\n", title, conn->name);
#define print_str(title,name) \
cherokee_buffer_add_va (buf, "\t| %s: %s\n", title, name);
#define print_add(str) \
cherokee_buffer_add_str (buf, str)
print_cbuf (" Request", request);
print_cbuf ("Request Original", request_original);
print_cbuf (" Web Directory", web_directory);
print_cbuf (" Local Directory", local_directory);
print_cbuf (" Pathinfo", pathinfo);
print_cbuf (" User Dir", userdir);
print_cbuf (" Query string", query_string);
print_cbuf ("Query str. Orig.", query_string_original);
print_cbuf (" Host", host);
print_cbuf (" Redirect", redirect);
print_cint (" Redirect num", respins);
print_cint (" Keepalive", keepalive);
print_cint ("Chunked-Encoding", chunked_encoding);
print_str (" Phase", phase);
print_cint (" Range start", range_start);
print_cint (" Range end", range_end);
print_cint (" Rate", limit_rate);
if (conn->limit_rate) {
print_cint (" Rate BPS", limit_rate);
}
/* Options bit fields
*/
print_add ("\t| Option bits:");
if (conn->options & conn_op_root_index)
print_add (" root_index");
if (conn->options & conn_op_tcp_cork)
print_add (" tcp_cork");
if (conn->options & conn_op_document_root)
print_add (" document_root");
if (conn->options & conn_op_was_polling)
print_add (" was_polling");
if (conn->options & conn_op_cant_encoder)
print_add (" cant_encoder");
if (conn->options & conn_op_got_eof)
print_add (" got_eof");
print_add ("\n");
#undef print_buf
#undef print_cbuf
#undef print_cint
#undef print_str
#undef print_add
cherokee_buffer_add_str (buf, "\t\\_\n");
return buf->buf;
}
#endif
const char *
cherokee_connection_get_phase_str (cherokee_connection_t *conn)
{
switch (conn->phase) {
case phase_nothing: return "Nothing";
case phase_tls_handshake: return "TLS handshake";
case phase_reading_header: return "Reading header";
case phase_processing_header: return "Processing header";
case phase_reading_post: return "Reading POST";
case phase_setup_connection: return "Setting up connection";
case phase_init: return "Init connection";
case phase_add_headers: return "Add headers";
case phase_send_headers: return "Send headers";
case phase_stepping: return "Stepping";
case phase_shutdown: return "Shutdown connection";
case phase_lingering: return "Lingering close";
default:
SHOULDNT_HAPPEN;
}
return NULL;
}
ret_t
cherokee_connection_set_redirect (cherokee_connection_t *conn, cherokee_buffer_t *address)
{
cuint_t len;
/* Build the redirection address
*/
cherokee_buffer_clean (&conn->redirect);
len = conn->web_directory.len +
address->len +
conn->userdir.len +
conn->host.len +
sizeof(":65535") +
sizeof("https://") + 4;
cherokee_buffer_ensure_size (&conn->redirect, len);
if (! cherokee_buffer_is_empty (&conn->host)) {
if (conn->socket.is_tls == TLS)
cherokee_buffer_add_str (&conn->redirect, "https://");
else
cherokee_buffer_add_str (&conn->redirect, "http://");
cherokee_buffer_add_buffer (&conn->redirect, &conn->host);
if (conn->host_port.len > 0) {
cherokee_buffer_add_str (&conn->redirect, ":");
cherokee_buffer_add_buffer (&conn->redirect, &conn->host_port);
}
}
if (! cherokee_buffer_is_empty (&conn->userdir)) {
cherokee_buffer_add_str (&conn->redirect, "/~");
cherokee_buffer_add_buffer (&conn->redirect, &conn->userdir);
}
/* In case the connection has a custom Document Root directory,
* it must add the web equivalent directory to the path (web_directory).
*/
if (cherokee_connection_use_webdir (conn)) {
cherokee_buffer_add_buffer (&conn->redirect, &conn->web_directory);
}
cherokee_buffer_add_buffer (&conn->redirect, address);
return ret_ok;
}
ret_t
cherokee_connection_sleep (cherokee_connection_t *conn,
cherokee_msec_t msecs)
{
conn->limit_blocked_until = cherokee_bogonow_msec + msecs;
return ret_ok;
}
void
cherokee_connection_update_timeout (cherokee_connection_t *conn)
{
if (conn->timeout_lapse == -1) {
TRACE (ENTRIES",timeout", "conn (%p, %s): Timeout = now + %d secs\n",
conn, cherokee_connection_get_phase_str (conn), CONN_SRV(conn)->timeout);
conn->timeout = cherokee_bogonow_now + CONN_SRV(conn)->timeout;
return;
}
TRACE (ENTRIES",timeout", "conn (%p, %s): Timeout = now + %d secs\n",
conn, cherokee_connection_get_phase_str (conn), conn->timeout_lapse);
conn->timeout = cherokee_bogonow_now + conn->timeout_lapse;
}
ret_t
cherokee_connection_build_host_port_string (cherokee_connection_t *conn,
cherokee_buffer_t *buf)
{
/* 1st choice: Request host */
if (! cherokee_buffer_is_empty (&conn->host)) {
cherokee_buffer_add_buffer (buf, &conn->host);
}
/* 2nd choice: Bound IP */
else if ((conn->bind != NULL) &&
(! cherokee_buffer_is_empty (&conn->bind->ip)))
{
cherokee_buffer_add_buffer (buf, &conn->bind->ip);
}
/* 3rd choice: Bound IP, rendered address */
else if ((conn->bind != NULL) &&
(! cherokee_buffer_is_empty (&conn->bind->server_address)))
{
cherokee_buffer_add_buffer (buf, &conn->bind->server_address);
}
/* Port
*/
if ((conn->bind != NULL) &&
(! http_port_is_standard (conn->bind->port, conn->socket.is_tls)))
{
cherokee_buffer_add_char (buf, ':');
cherokee_buffer_add_ulong10 (buf, conn->bind->port);
}
return ret_ok;
}
Jump to Line
Something went wrong with that request. Please try again.