Let server_set_cookie() stay silent when the cookie isn't modified
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index 282523c..7d3b723 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.399 2012/10/21 12:53:33 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.416 2012/11/24 14:01:25 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -951,6 +951,7 @@ static int server_response_is_complete(struct client_state *csp,
        */
       csp->expected_content_length = 0;
       content_length_known = TRUE;
+      csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET;
    }
 
    if (csp->http->status == 204 || csp->http->status == 304)
@@ -960,6 +961,7 @@ static int server_response_is_complete(struct client_state *csp,
        */
       csp->expected_content_length = 0;
       content_length_known = TRUE;
+      csp->flags |= CSP_FLAG_SERVER_CONTENT_LENGTH_SET;
    }
 
    return (content_length_known && ((0 == csp->expected_content_length)
@@ -1064,11 +1066,9 @@ void save_connection_destination(jb_socket sfd,
  *               we do.
  *
  *               Data that doesn't belong to the current request is
- *               thrown away to let the client retry on a clean socket.
- *
- *               XXX: This is a hack until we can deal with multiple
- *                    pipelined requests at the same time.
- *
+ *               either thrown away to let the client retry on a clean
+ *               socket, or stashed to be dealt with after the current
+ *               request is served.
  *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
@@ -1109,7 +1109,8 @@ static void verify_request_length(struct client_state *csp)
    }
 
    if (!(csp->flags & CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ)
-    && ((csp->client_iob->cur[0] != '\0') || (csp->expected_client_content_length != 0)))
+      && ((csp->client_iob->cur < csp->client_iob->eod)
+         || (csp->expected_client_content_length != 0)))
    {
       if (strcmpic(csp->http->gpc, "GET")
          && strcmpic(csp->http->gpc, "HEAD")
@@ -1119,9 +1120,7 @@ static void verify_request_length(struct client_state *csp)
       {
          /* XXX: this is an incomplete hack */
          csp->flags &= ~CSP_FLAG_CLIENT_REQUEST_COMPLETELY_READ;
-         csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED;
-         log_error(LOG_LEVEL_CONNECT,
-            "There might be a request body. The connection will not be kept alive.");
+         log_error(LOG_LEVEL_CONNECT, "There better be a request body.");
       }
       else
       {
@@ -1134,8 +1133,7 @@ static void verify_request_length(struct client_state *csp)
                "Possible pipeline attempt detected. The connection will not "
                "be kept alive and we will only serve the first request.");
             /* Nuke the pipelined requests from orbit, just to be sure. */
-            csp->client_iob->buf[0] = '\0';
-            csp->client_iob->eod = csp->client_iob->cur = csp->client_iob->buf;
+            clear_iob(csp->client_iob);
          }
          else
          {
@@ -1144,7 +1142,7 @@ static void verify_request_length(struct client_state *csp)
              * it once we're done serving the current request.
              */
             csp->flags |= CSP_FLAG_PIPELINED_REQUEST_WAITING;
-            assert(csp->client_iob->eod > csp->client_iob->cur);
+            assert(csp->client_iob->eod >= csp->client_iob->cur);
             log_error(LOG_LEVEL_CONNECT, "Complete client request followed by "
                "%d bytes of pipelined data received.",
                (int)(csp->client_iob->eod - csp->client_iob->cur));
@@ -1183,7 +1181,7 @@ static void mark_server_socket_tainted(struct client_state *csp)
     * actually been reused.
     */
    if ((csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE)
-      && !(csp->flags |= CSP_FLAG_SERVER_SOCKET_TAINTED))
+      && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED))
    {
       log_error(LOG_LEVEL_CONNECT,
          "Marking the server socket %d tainted.",
@@ -1302,6 +1300,9 @@ static jb_err receive_client_request(struct client_state *csp)
    struct list header_list;
    struct list *headers = &header_list;
 
+   /* We don't care if the arriving data is a valid HTTP request or not. */
+   csp->requests_received_total++;
+
    http = csp->http;
 
    memset(buf, 0, sizeof(buf));
@@ -1696,10 +1697,20 @@ static void chat(struct client_state *csp)
    {
       if (csp->server_connection.sfd != JB_INVALID_SOCKET)
       {
-         log_error(LOG_LEVEL_CONNECT,
-            "Closing server socket %u. Opened for %s.",
-            csp->server_connection.sfd, csp->server_connection.host);
-         close_socket(csp->server_connection.sfd);
+#ifdef FEATURE_CONNECTION_SHARING
+         if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
+         {
+            remember_connection(&csp->server_connection);
+         }
+         else
+#endif /* def FEATURE_CONNECTION_SHARING */
+         {
+            log_error(LOG_LEVEL_CONNECT,
+               "Closing server socket %d connected to %s. Total requests: %u.",
+               csp->server_connection.sfd, csp->server_connection.host,
+               csp->server_connection.requests_sent_total);
+            close_socket(csp->server_connection.sfd);
+         }
          mark_connection_closed(&csp->server_connection);
       }
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
@@ -1793,7 +1804,7 @@ static void chat(struct client_state *csp)
       {
          return;
       }
-      IOB_RESET(csp->client_iob);
+      clear_iob(csp->client_iob);
    }
 
    log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport);
@@ -1809,7 +1820,9 @@ static void chat(struct client_state *csp)
 
    server_body = 0;
 
+#ifdef FEATURE_CONNECTION_KEEP_ALIVE
    watch_client_socket = 0 == (csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING);
+#endif
 
    for (;;)
    {
@@ -2510,7 +2523,7 @@ static void prepare_csp_for_next_request(struct client_state *csp)
    csp->expected_content_length = 0;
    csp->expected_client_content_length = 0;
    list_remove_all(csp->headers);
-   IOB_RESET(csp->iob);
+   clear_iob(csp->iob);
    freez(csp->error_message);
    free_http_request(csp->http);
    destroy_list(csp->headers);
@@ -2556,7 +2569,7 @@ static void prepare_csp_for_next_request(struct client_state *csp)
        * Freeing the buffer itself isn't technically necessary,
        * but makes debugging more convenient.
        */
-      IOB_RESET(csp->client_iob);
+      clear_iob(csp->client_iob);
    }
 }
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
@@ -2582,12 +2595,12 @@ void serve(struct client_state *csp)
 static void serve(struct client_state *csp)
 #endif /* def AMIGA */
 {
+   int config_file_change_detected = 0; /* Only used for debugging */
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
 #ifdef FEATURE_CONNECTION_SHARING
    static int monitor_thread_running = 0;
 #endif /* def FEATURE_CONNECTION_SHARING */
    int continue_chatting = 0;
-   int config_file_change_detected = 0; /* Only used for debugging */
 
    log_error(LOG_LEVEL_CONNECT, "Accepted connection from %s on socket %d",
       csp->ip_addr_str, csp->cfd);
@@ -2618,9 +2631,9 @@ static void serve(struct client_state *csp)
          & RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE)
          && !(csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED)
          && (csp->cfd != JB_INVALID_SOCKET)
-         && ((csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)
-             || (csp->config->feature_flags &
-                RUNTIME_FEATURE_CONNECTION_SHARING));
+         && (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE)
+         && ((csp->flags & CSP_FLAG_SERVER_CONTENT_LENGTH_SET)
+            || (csp->flags & CSP_FLAG_CHUNKED));
 
       if (!(csp->flags & CSP_FLAG_CRUNCHED)
          && (csp->server_connection.sfd != JB_INVALID_SOCKET))
@@ -2635,14 +2648,13 @@ static void serve(struct client_state *csp)
             || !(latency < csp->server_connection.keep_alive_timeout))
          {
             log_error(LOG_LEVEL_CONNECT,
-               "Closing connection on server socket %d to %s: "
-               "keep-alive %u, tainted: %u, socket alive %u. %s timeout: %u.",
+               "Closing server socket %d connected to %s. "
+               "Keep-alive %u. Tainted: %u. Socket alive %u. Timeout: %u.",
                csp->server_connection.sfd, csp->server_connection.host,
                0 != (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE),
                0 != (csp->flags & CSP_FLAG_SERVER_SOCKET_TAINTED),
                socket_is_still_alive(csp->server_connection.sfd),
-               ((csp->flags & CSP_FLAG_SERVER_KEEP_ALIVE_TIMEOUT_SET) ?
-               "Specified" : "Assumed"), csp->server_connection.keep_alive_timeout);
+               csp->server_connection.keep_alive_timeout);
 #ifdef FEATURE_CONNECTION_SHARING
             if (csp->config->feature_flags & RUNTIME_FEATURE_CONNECTION_SHARING)
             {
@@ -2665,9 +2677,9 @@ static void serve(struct client_state *csp)
          if (((csp->flags & CSP_FLAG_PIPELINED_REQUEST_WAITING) != 0)
             && socket_is_still_alive(csp->cfd))
          {
-            log_error(LOG_LEVEL_CONNECT, "A client request has been "
+            log_error(LOG_LEVEL_CONNECT, "Client request %d has been "
                "pipelined on socket %d and the socket is still alive.",
-               csp->cfd);
+               csp->requests_received_total+1, csp->cfd);
             prepare_csp_for_next_request(csp);
             continue;
          }
@@ -2694,7 +2706,8 @@ static void serve(struct client_state *csp)
             && socket_is_still_alive(csp->cfd))
          {
             log_error(LOG_LEVEL_CONNECT,
-               "Client request arrived in time on socket %d.", csp->cfd);
+               "Client request %u arrived in time on socket %d.",
+               csp->requests_received_total+1, csp->cfd);
             prepare_csp_for_next_request(csp);
          }
          else
@@ -2733,8 +2746,8 @@ static void serve(struct client_state *csp)
       else if (csp->server_connection.sfd != JB_INVALID_SOCKET)
       {
          log_error(LOG_LEVEL_CONNECT,
-            "Closing server socket %d connected to %s. Keep-alive %u. "
-            "Tainted: %u. Socket alive %u. Timeout: %u. "
+            "Closing server socket %d connected to %s. Keep-alive: %u. "
+            "Tainted: %u. Socket alive: %u. Timeout: %u. "
             "Configuration file change detected: %u",
             csp->server_connection.sfd, csp->server_connection.host,
             0 != (csp->flags & CSP_FLAG_SERVER_CONNECTION_KEEP_ALIVE),
@@ -2767,11 +2780,11 @@ static void serve(struct client_state *csp)
    if (csp->cfd != JB_INVALID_SOCKET)
    {
       log_error(LOG_LEVEL_CONNECT, "Closing client socket %d. "
-         "Keep-alive: %u, Socket alive: %u. Data available: %u. "
-         "Configuration file change detected: %u.",
+         "Keep-alive: %u. Socket alive: %u. Data available: %u. "
+         "Configuration file change detected: %u. Requests received: %u.",
          csp->cfd, 0 != (csp->flags & CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE),
          socket_is_still_alive(csp->cfd), data_is_available(csp->cfd, 0),
-         config_file_change_detected);
+         config_file_change_detected, csp->requests_received_total);
       drain_and_close_socket(csp->cfd);
    }
 
@@ -2817,7 +2830,7 @@ static int32 server_thread(void *data)
 static void usage(const char *myname)
 {
    printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n"
-          "Usage: %s "
+          "Usage: %s [--config-test] "
 #if defined(unix)
           "[--chroot] "
 #endif /* defined(unix) */
@@ -3007,6 +3020,7 @@ int main(int argc, char **argv)
 #endif
 {
    int argc_pos = 0;
+   int do_config_test = 0;
    unsigned int random_seed;
 #ifdef unix
    struct passwd *pw = NULL;
@@ -3137,6 +3151,11 @@ int main(int argc, char **argv)
       }
 #endif /* defined(unix) */
 
+      else if (strcmp(argv[argc_pos], "--config-test") == 0)
+      {
+         do_config_test = 1;
+      }
+
       else if (argc_pos + 1 != argc)
       {
          /*
@@ -3240,6 +3259,10 @@ int main(int argc, char **argv)
 # endif /* def _WIN_CONSOLE */
 #endif /* def _WIN32 */
 
+   if (do_config_test)
+   {
+      exit(NULL == load_config());
+   }
 
    /* Initialize the CGI subsystem */
    cgi_init_error_messages();