I've patched pound myself, here's the diff with respect to Pound 2.6. The patch does the following:

If the backend unexpectedly closes the connection during transfer of the content associated with a request from client to backend, and the backend sent a response before closing the connection, the following patch will make pound forward that response from the backend to the client. The previous behavior was that pound would just send "500 Internal Server Error" to the client.

$ diff -u http.c http-patched.c
--- http.c      2011-12-28 14:57:45.000000000 +0100
+++ http-patched.c      2012-12-17 17:29:00.706549200 +0100
@@ -487,7 +487,7 @@
 void
 do_http(thr_arg *arg)
 {
- int cl_11, be_11, res, chunked, n, sock, no_cont, skip, conn_closed, force_10, sock_proto, is_rpc; + int cl_11, be_11, res, chunked, n, sock, no_cont, skip, conn_closed, force_10, sock_proto, is_rpc, be_err_closed;
     LISTENER            *lstn;
     SERVICE             *svc;
     BACKEND             *backend, *cur_backend, *old_backend;
@@ -592,6 +592,7 @@
     BIO_set_buffer_size(cl, MAXBUF);
     cl = BIO_push(bb, cl);

+    be_err_closed = 0;
     for(cl_11 = be_11 = 0;;) {
         res_bytes = L0;
         is_rpc = -1;
@@ -1106,26 +1107,22 @@
         if(cl_11 && chunked) {
/* had Transfer-encoding: chunked so read/write all the chunks (HTTP/1.1 only) */ if(copy_chunks(cl, be, NULL, cur_backend->be_type, lstn->max_req)) {
+                be_err_closed = 1;
                 str_be(buf, MAXBUF - 1, cur_backend);
                 end_req = cur_time();
                 addr2str(caddr, MAXBUF - 1, &from_host, 1);
- logmsg(LOG_NOTICE, "(%lx) e500 for %s copy_chunks to %s/%s (%.3f sec)", + logmsg(LOG_NOTICE, "(%lx) error for %s copy_chunks to %s/%s (%.3f sec)", pthread_self(), caddr, buf, request, (end_req - start_req) / 1000000.0);
-                err_reply(cl, h500, lstn->err500);
-                clean_all();
-                return;
             }
         } else if(cont > L0 && is_rpc != 1) {
/* had Content-length, so do raw reads/writes for the length */
             if(copy_bin(cl, be, cont, NULL, cur_backend->be_type)) {
+                be_err_closed = 1;
                 str_be(buf, MAXBUF - 1, cur_backend);
                 end_req = cur_time();
                 addr2str(caddr, MAXBUF - 1, &from_host, 1);
- logmsg(LOG_NOTICE, "(%lx) e500 for %s error copy client cont to %s/%s: %s (%.3f sec)", + logmsg(LOG_NOTICE, "(%lx) error for %s copy client cont to %s/%s: %s (%.3f sec)", pthread_self(), caddr, buf, request, strerror(errno), (end_req - start_req) / 1000000.0);
-                err_reply(cl, h500, lstn->err500);
-                clean_all();
-                return;
             }
         } else if(cont > 0L && is_readable(cl, lstn->to)) {
             char one;
@@ -1156,11 +1153,12 @@
                     if(errno)
logmsg(LOG_NOTICE, "(%lx) error write request pending: %s",
                             pthread_self(), strerror(errno));
-                    clean_all();
-                    pthread_exit(NULL);
+                    be_err_closed = 1;
+                    break;
                 }
             }
-            BIO_flush(be);
+            if (!be_err_closed)
+                BIO_flush(be);

             /*
              * find the socket BIO in the chain
@@ -1174,7 +1172,7 @@
             /*
              * copy till EOF
              */
-            while((res = BIO_read(cl_unbuf, buf, MAXBUF)) > 0) {
+ while(!be_err_closed && (res = BIO_read(cl_unbuf, buf, MAXBUF)) > 0) {
                 if((res_bytes += res) > cont) {
logmsg(LOG_NOTICE, "(%lx) error copy request body: max. RPC length exceeded",
                         pthread_self());
@@ -1185,8 +1183,7 @@
                     if(errno)
logmsg(LOG_NOTICE, "(%lx) error copy request body: %s",
                             pthread_self(), strerror(errno));
-                    clean_all();
-                    pthread_exit(NULL);
+                    be_err_closed = 1;
                 } else {
                     BIO_flush(be);
                 }
@@ -1194,15 +1191,13 @@
         }

         /* flush to the back-end */
-        if(cur_backend->be_type == 0 && BIO_flush(be) != 1) {
+ if(!be_err_closed && cur_backend->be_type == 0 && BIO_flush(be) != 1) {
             str_be(buf, MAXBUF - 1, cur_backend);
             end_req = cur_time();
             addr2str(caddr, MAXBUF - 1, &from_host, 1);
logmsg(LOG_NOTICE, "(%lx) e500 for %s error flush to %s/%s: %s (%.3f sec)", pthread_self(), caddr, buf, request, strerror(errno), (end_req - start_req) / 1000000.0);
-            err_reply(cl, h500, lstn->err500);
-            clean_all();
-            return;
+            be_err_closed = 1;
         }

         /*
@@ -1253,7 +1248,7 @@
u_name[0]? u_name: "-", req_time, request, cur_backend->be_type, referer, u_agent);
                 break;
             }
-            if(!cl_11 || conn_closed || force_10)
+            if(be_err_closed || !cl_11 || conn_closed || force_10)
                 break;
             continue;
         } else if(is_rpc == 1) {
@@ -1541,13 +1536,18 @@
         }
         /*
          * Stop processing if:
+         *  - backend closed the connection during transfer of the content
+ * (in this case we have still made an attempt to forward any response + * from the backend to the client, but now we have to stop because
+         *    of the closed connection)
+         *      or
          *  - client is not HTTP/1.1
          *      or
          *  - we had a "Connection: closed" header
          *      or
          *  - this is an SSL connection and we had a NoHTTPS11 directive
          */
-        if(!cl_11 || conn_closed || force_10)
+        if(be_err_closed || !cl_11 || conn_closed || force_10)
             break;
     }



On 12/11/2012 05:50 PM, Bastiaan Stougie wrote:
Hi,

We are considering to use pound as reverse proxy. During tests, we have encountered the following issue:

- a client does a PUT request, with non-zero content-length
- pound forwards the PUT request and starts to forward the associated content - the server that pound forwards to reads and evaluates the request, without reading the content yet - the server that pound forwards to notices a problem with the request and responds, then closes the connection without ever reading the content associated with the PUT request - pound notices that it can not forward the content (EPIPE), and immediately responds to the client with "500 Internal Server Error"

In other words, on error, the response from the the server that pound forwards to never reaches the client, and the client needs more information than "500 Internal Server Error" to be able to take appropriate action.

The PUT content can be very big (gigabytes), so reading it completely and then responding would take the server considerable time and resources, and would cost much bandwidth.

Would it be possible to have pound attempt to forward the response back to the client after the forward of the content to the server has failed?

Thank you,

Bastiaan Stougie



--
To unsubscribe send an email with subject unsubscribe to [email protected].
Please contact [email protected] for questions.

Reply via email to