diff --git a/src/reqs.c b/src/reqs.c index 2de43a8..56b9b17 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1394,6 +1394,9 @@ void handle_connection (int fd) close (fd); return; } + + set_readtimeout(connptr->client_fd, config.idletimeout); + set_writetimeout(connptr->client_fd, config.idletimeout); if (check_acl (peer_ipaddr, peer_string, config.access_list) <= 0) { update_stats (STAT_DENIED); @@ -1462,6 +1465,7 @@ void handle_connection (int fd) } goto fail; } + update_reqpeer(request->host); connptr->upstream_proxy = UPSTREAM_HOST (request->host); if (connptr->upstream_proxy != NULL) { @@ -1472,7 +1476,7 @@ void handle_connection (int fd) connptr->server_fd = opensock (request->host, request->port, connptr->server_ip_addr); if (connptr->server_fd < 0) { - indicate_http_error (connptr, 500, "Unable to connect", + indicate_http_error (connptr, 601, "Unable to connect", "detail", PACKAGE_NAME " " "was unable to connect to the remote web server.", @@ -1485,10 +1489,14 @@ void handle_connection (int fd) "file descriptor %d.", request->host, connptr->server_fd); + if (!connptr->connect_method) establish_http_connection (connptr, request); } + set_readtimeout(connptr->server_fd, config.idletimeout); + set_writetimeout(connptr->server_fd, config.idletimeout); + if (process_client_headers (connptr, hashofheaders) < 0) { update_stats (STAT_BADCONN); goto fail; @@ -1510,6 +1518,7 @@ void handle_connection (int fd) } relay_connection (connptr); + update_donepeer(request->host); log_message (LOG_INFO, "Closed connection between local client (fd:%d) " diff --git a/src/sock.c b/src/sock.c index d5d899b..70e8c51 100644 --- a/src/sock.c +++ b/src/sock.c @@ -162,6 +162,34 @@ int socket_blocking (int sock) return fcntl (sock, F_SETFL, flags & ~O_NONBLOCK); } +/* + * set the socket 's read timeout -hanbaga + */ +int set_readtimeout(int sock, int timeout) +{ + assert (sock >= 0); + + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + return setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(struct timeval)); +} + +/* + * set the socket 's write timeout -hanbaga + */ +int set_writetimeout(int sock, int timeout) +{ + assert (sock >= 0); + + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval)); +} + /* * Start listening to a socket. Create a socket with the selected port. * The size of the socket address will be returned to the caller through diff --git a/src/sock.h b/src/sock.h index 5eac10b..b140cf4 100644 --- a/src/sock.h +++ b/src/sock.h @@ -31,6 +31,9 @@ extern int opensock (const char *host, int port, const char *bind_to); extern int listen_sock (uint16_t port, socklen_t * addrlen); +extern int set_readtimeout(int sock, int timeout); +extern int set_writetimeout(int sock, int timeout); + extern int socket_nonblocking (int sock); extern int socket_blocking (int sock); diff --git a/src/stats.c b/src/stats.c index c7b4423..3a6987f 100644 --- a/src/stats.c +++ b/src/stats.c @@ -33,6 +33,13 @@ #include "stats.h" #include "utils.h" #include "conf.h" +#include "hashmap.h" + +struct peer_s { + char host[36]; + int count; + int done; +}; struct stat_s { unsigned long int num_reqs; @@ -40,9 +47,11 @@ struct stat_s { unsigned long int num_open; unsigned long int num_refused; unsigned long int num_denied; + struct peer_s peers[100]; }; static struct stat_s *stats; +static int MAX_ITEM_LEN = 48; /* * Initialize the statistics information to zero. @@ -63,65 +72,86 @@ int showstats (struct conn_s *connptr) { char *message_buffer; - char opens[16], reqs[16], badconns[16], denied[16], refused[16]; - FILE *statfile; - - snprintf (opens, sizeof (opens), "%lu", stats->num_open); - snprintf (reqs, sizeof (reqs), "%lu", stats->num_reqs); - snprintf (badconns, sizeof (badconns), "%lu", stats->num_badcons); - snprintf (denied, sizeof (denied), "%lu", stats->num_denied); - snprintf (refused, sizeof (refused), "%lu", stats->num_refused); - - if (!config.statpage || (!(statfile = fopen (config.statpage, "r")))) { - message_buffer = (char *) safemalloc (MAXBUFFSIZE); - if (!message_buffer) - return -1; - - snprintf - (message_buffer, MAXBUFFSIZE, - "\n" - "\n" - "\n" - "%s version %s run-time statistics\n" - "\n" - "

%s version %s run-time statistics

\n" - "

\n" - "Number of open connections: %lu
\n" - "Number of requests: %lu
\n" - "Number of bad connections: %lu
\n" - "Number of denied connections: %lu
\n" - "Number of refused connections due to high load: %lu\n" - "

\n" - "
\n" - "

Generated by %s version %s.

\n" "\n" - "\n", - PACKAGE, VERSION, PACKAGE, VERSION, - stats->num_open, - stats->num_reqs, - stats->num_badcons, stats->num_denied, - stats->num_refused, PACKAGE, VERSION); - - if (send_http_message (connptr, 200, "OK", - message_buffer) < 0) { - safefree (message_buffer); - return -1; - } + + message_buffer = (char *) safemalloc (MAXBUFFSIZE); + if (!message_buffer) + return -1; + memset(message_buffer, 0, MAXBUFFSIZE); + + char item[MAX_ITEM_LEN]; + int idx = 0; + + for(; idxpeers)/sizeof(struct peer_s); ++idx) + { + if (strlen(stats->peers[idx].host) == 0){ + continue; + } + if (idx >= MAXBUFFSIZE/MAX_ITEM_LEN){ + break; + } + if (idx == 0){ + strcat(message_buffer, "{\"domain\":{"); + } + memset(item, 0, MAX_ITEM_LEN); + snprintf(item, MAX_ITEM_LEN, "\"%s\":[%d, %d],", stats->peers[idx].host, stats->peers[idx].count, stats->peers[idx].done); + strcat(message_buffer, item); + } + + if (strlen(message_buffer) > 0 && strlen(message_buffer) + 2 < MAXBUFFSIZE){ + message_buffer[strlen(message_buffer)-1] = '}'; + strcat(message_buffer, ","); + }else if (strlen(message_buffer) == 0){ + message_buffer[0] = '{'; + } + + do { + memset(item, 0, MAX_ITEM_LEN); + snprintf(item, MAX_ITEM_LEN, "\"%s\":%d,", "connreqs", stats->num_reqs); + if (strlen(message_buffer) + strlen(item) >= MAXBUFFSIZE){ + break; + } + strcat(message_buffer, item); + memset(item, 0, MAX_ITEM_LEN); + snprintf(item, MAX_ITEM_LEN, "\"%s\":%d,", "connbads", stats->num_badcons); + if (strlen(message_buffer) + strlen(item) >= MAXBUFFSIZE){ + break; + } + strcat(message_buffer, item); + memset(item, 0, MAX_ITEM_LEN); + snprintf(item, MAX_ITEM_LEN, "\"%s\":%d,", "connopens", stats->num_open); + if (strlen(message_buffer) + strlen(item) >= MAXBUFFSIZE){ + break; + } + strcat(message_buffer, item); + memset(item, 0, MAX_ITEM_LEN); + snprintf(item, MAX_ITEM_LEN, "\"%s\":%d,", "connrefused", stats->num_refused); + if (strlen(message_buffer) + strlen(item) >= MAXBUFFSIZE){ + break; + } + strcat(message_buffer, item); + memset(item, 0, MAX_ITEM_LEN); + snprintf(item, MAX_ITEM_LEN, "\"%s\":%d", "conndenied", stats->num_denied); + if (strlen(message_buffer) + strlen(item) >= MAXBUFFSIZE){ + break; + } + strcat(message_buffer, item); + } while(0); + + int httpcode = 200; + if (strlen(message_buffer) + 1 < MAXBUFFSIZE){ + strcat(message_buffer, "}"); + }else{ + httpcode = 500; + } + + if (send_http_json_message (connptr, httpcode, "OK", message_buffer) < 0) { safefree (message_buffer); return 0; } - add_error_variable (connptr, "opens", opens); - add_error_variable (connptr, "reqs", reqs); - add_error_variable (connptr, "badconns", badconns); - add_error_variable (connptr, "deniedconns", denied); - add_error_variable (connptr, "refusedconns", refused); - add_standard_vars (connptr); - send_http_headers (connptr, 200, "Statistic requested"); - send_html_file (statfile, connptr); - fclose (statfile); - + memset(stats, 0, sizeof(struct stat_s)); + safefree (message_buffer); return 0; } @@ -154,3 +184,57 @@ int update_stats (status_t update_level) return 0; } + +int update_reqpeer(char* host) +{ + struct peer_s* empty_peer = NULL; + int idx = 0; + + for(; idxpeers)/sizeof(struct peer_s); ++idx) + { + if (strlen(stats->peers[idx].host) == 0 && stats->peers[idx].count == 0 && empty_peer == NULL){ + empty_peer = &stats->peers[idx]; + break; + } + if (0 == strncmp(host, stats->peers[idx].host, 35)){ + stats->peers[idx].count += 1; + log_message(LOG_CONN, host); + return 0; + } + } + + if (empty_peer == NULL){ + return -1; + } + + strncpy(empty_peer->host, host, 35); + empty_peer->count = 1; + return 1; +} + +int update_donepeer(char* host) +{ + struct peer_s* empty_peer = NULL; + int idx = 0; + + for(; idxpeers)/sizeof(struct peer_s); ++idx) + { + if (strlen(stats->peers[idx].host) == 0 && stats->peers[idx].count == 0 && stats->peers[idx].done == 0 && empty_peer == NULL){ + empty_peer = &stats->peers[idx]; + break; + } + if (0 == strncmp(host, stats->peers[idx].host, 35)){ + stats->peers[idx].done += 1; + log_message(LOG_CONN, host); + return 0; + } + } + + if (empty_peer == NULL){ + return -1; + } + + strncpy(empty_peer->host, host, 35); + empty_peer->done = 1; + return 0; +} diff --git a/src/stats.h b/src/stats.h index b1a43c3..5d058d2 100644 --- a/src/stats.h +++ b/src/stats.h @@ -40,5 +40,7 @@ typedef enum { extern void init_stats (void); extern int showstats (struct conn_s *connptr); extern int update_stats (status_t update_level); +extern int update_reqpeer(char* host); +extern int update_donepeer(char* host); #endif diff --git a/src/utils.c b/src/utils.c index ef2e673..acbab40 100644 --- a/src/utils.c +++ b/src/utils.c @@ -58,6 +58,30 @@ send_http_message (struct conn_s *connptr, int http_code, return 0; } +int +send_http_json_message (struct conn_s *connptr, int http_code, + const char *error_title, const char *message) +{ + static const char *headers[] = { + "Server: " PACKAGE "/" VERSION, + "Content-type: application/json", + "Connection: close" + }; + + http_message_t msg; + + msg = http_message_create (http_code, error_title); + if (msg == NULL) + return -1; + + http_message_add_headers (msg, headers, 3); + http_message_set_body (msg, message, strlen (message)); + http_message_send (msg, connptr->client_fd); + http_message_destroy (msg); + + return 0; +} + /* * Safely creates filename and returns the low-level file descriptor. */ diff --git a/src/utils.h b/src/utils.h index e6b0fc4..b68c5d0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -29,6 +29,8 @@ struct conn_s; extern int send_http_message (struct conn_s *connptr, int http_code, const char *error_title, const char *message); +extern int send_http_json_message (struct conn_s *connptr, int http_code, + const char *error_title, const char *message); extern int pidfile_create (const char *path); extern int create_file_safely (const char *filename,