From 85a82af5f51ff606064e14658887f4684e51f629 Mon Sep 17 00:00:00 2001 From: Robert James Kaes Date: Thu, 11 Apr 2002 20:44:15 +0000 Subject: Did a bit of consolidation by moving code into remove_connection_headers() and added the get_content_length() function. The process_server_headers() function was rewritten to remove the Connection header correctly, and also retrieve the Content-Length value. This value is needed in the relay_connection() function since there are some remote machines which do not properly close down the connection once the body has been retrieved. Thanks to James Flemer for finding a test case for this problem. --- src/reqs.c | 143 +++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/reqs.c b/src/reqs.c index 9f25d8c..1e1857f 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1,4 +1,4 @@ -/* $Id: reqs.c,v 1.54 2002-04-09 20:06:24 rjkaes Exp $ +/* $Id: reqs.c,v 1.55 2002-04-11 20:44:15 rjkaes Exp $ * * This is where all the work in tinyproxy is actually done. Incoming * connections have a new thread created for them. The thread then @@ -520,9 +520,16 @@ get_all_headers(int fd, hashmap_t hashofheaders) * header sent via the client (which is stored in data right now.) */ static int -remove_connection_headers(hashmap_t hashofheaders, char* data, ssize_t len) +remove_connection_headers(hashmap_t hashofheaders) { + char *data; char* ptr; + ssize_t len; + + /* Look for the connection header. If it's not found, return. */ + len = hashmap_search(hashofheaders, "connection", (void **)&data); + if (len <= 0) + return 0; /* * Go through the data line and replace any special characters with @@ -548,9 +555,31 @@ remove_connection_headers(hashmap_t hashofheaders, char* data, ssize_t len) ptr++; } + /* Now remove the connection header it self. */ + hashmap_remove(hashofheaders, "connection"); + return 0; } +/* + * If there is a Content-Length header, then return the value; otherwise, return + * a negative number. + */ +static long +get_content_length(hashmap_t hashofheaders) +{ + ssize_t len; + char *data; + long content_length = -1; + + len = hashmap_search(hashofheaders, "content-length", (void **)&data); + if (len > 0) { + content_length = atol(data); + } + + return content_length; +} + /* * Number of buckets to use internally in the hashmap. */ @@ -610,27 +639,16 @@ process_client_headers(struct conn_s *connptr) } /* - * See if there is a "Connection" header. If so, we need to do a bit - * of processing. :) + * See if there is a "Content-Length" header. If so, again we need + * to do a bit of processing. */ - len = hashmap_search(hashofheaders, "connection", (void **)&data); - if (len > 0) { - /* - * Go through the tokens in the connection header and - * remove the headers from the hash. - */ - remove_connection_headers(hashofheaders, data, len); - hashmap_remove(hashofheaders, "connection"); - } + content_length = get_content_length(hashofheaders); /* - * See if there is a "Content-Length" header. If so, again we need - * to do a bit of processing. + * See if there is a "Connection" header. If so, we need to do a bit + * of processing. :) */ - len = hashmap_search(hashofheaders, "content-length", (void **)&data); - if (len > 0) { - content_length = atol(data); - } + remove_connection_headers(hashofheaders); /* * See if there is a "Via" header. If so, again we need to do a bit @@ -712,28 +730,70 @@ process_client_headers(struct conn_s *connptr) static int process_server_headers(struct conn_s *connptr) { - char *header; + char *response_line; + + hashmap_t hashofheaders; + vector_t listofheaders; + char *data, *header; ssize_t len; + int i; - while (1) { - if ((len = readline(connptr->server_fd, &header)) <= 0) { - DEBUG2("Server (file descriptor %d) closed connection.", - connptr->server_fd); - return -1; - } + /* FIXME: Remember to handle a "simple_req" type */ - if (safe_write(connptr->client_fd, header, len) < 0) { - safefree(header); - return -1; - } + /* Get the response line from the remote server. */ + if ((len = readline(connptr->server_fd, &response_line)) <= 0) + return -1; - if (CHECK_CRLF(header, len)) - break; + hashofheaders = hashmap_create(HEADER_BUCKETS); + if (!hashofheaders) { + safefree(response_line); + return -1; + } + + /* + * Get all the headers from the remote server in a big hash + */ + if (get_all_headers(connptr->server_fd, hashofheaders) < 0) { + log_message(LOG_WARNING, "Could not retrieve all the headers from the remote server."); + hashmap_delete(hashofheaders); + safefree(response_line); + return -1; + } - safefree(header); + /* + * If there is a "Content-Length" header, retrieve the information + * from it for later use. + */ + connptr->remote_content_length = get_content_length(hashofheaders); + + /* + * See if there is a connection header. If so, we need to to a bit of + * processing. + */ + remove_connection_headers(hashofheaders); + + /* Send the saved response line first */ + safe_write(connptr->client_fd, response_line, strlen(response_line)); + safefree(response_line); + + /* + * Okay, output all the remaining headers to the client. + */ + listofheaders = hashmap_keys(hashofheaders); + for (i = 0; i < vector_length(listofheaders); ++i) { + len = vector_getentry(listofheaders, i, (void **)&data); + hashmap_search(hashofheaders, data, (void **)&header); + + write_message(connptr->client_fd, + "%s: %s\r\n", + data, header); } + vector_delete(listofheaders); + hashmap_delete(hashofheaders); + + /* Write the final blank line to signify the end of the headers */ + safe_write(connptr->client_fd, "\r\n", 2); - safefree(header); return 0; } @@ -754,6 +814,7 @@ relay_connection(struct conn_s *connptr) int ret; double tdiff; int maxfd = max(connptr->client_fd, connptr->server_fd) + 1; + ssize_t bytes_received; socket_nonblocking(connptr->client_fd); socket_nonblocking(connptr->server_fd); @@ -802,9 +863,14 @@ relay_connection(struct conn_s *connptr) last_access = time(NULL); } - if (FD_ISSET(connptr->server_fd, &rset) - && read_buffer(connptr->server_fd, connptr->sbuffer) < 0) { - break; + if (FD_ISSET(connptr->server_fd, &rset)) { + bytes_received = read_buffer(connptr->server_fd, connptr->sbuffer); + if (bytes_received < 0) + break; + + connptr->remote_content_length -= bytes_received; + if (connptr->remote_content_length == 0) + break; } if (FD_ISSET(connptr->client_fd, &rset) && read_buffer(connptr->client_fd, connptr->cbuffer) < 0) { @@ -1054,6 +1120,9 @@ handle_connection(int fd) relay_proxy: relay_connection(connptr); + log_message(LOG_INFO, "Closed connection between local client (fd:%d) and remote client (fd:%d)", + connptr->client_fd, connptr->server_fd); + /* * All done... close everything and go home... :) */ -- cgit v1.2.3