Well, thing is that Keep-Alive and Transfer-Encoding go hand-in-hand
when you add compression to the HTTP response.  With something like gzip
compression the web server doesn't know the the Content-Length of the
stream so the *only* way to achieve Keep-Alive without a Content-Length
(AFAIK) is to do chunked Transfer-Encoding.

Our patch does just as I describe above, allowing for a Keep-Alive
session to persist with compression by add a chunked Transfer-Encoding.
This make a HUGE performance increase over highly latent lines
(China/India -> United States).

Attached and below.  Let me know if this is considered large and I
should move this to a bugzilla issue.  Only took us about 30 minutes to
apply it.  Again, this is against 1.2.21!


--- native/common/jk_util.c     2007/03/19 12:35:35
+++ native/common/jk_util.c     2007/03/20 14:44:58
@@ -1706,6 +1706,9 @@
     s->route = NULL;
     s->retries = JK_RETRIES;
     s->add_log_items = NULL;
+
+       s->flush = NULL;
+       s->flush_packets = JK_FALSE;
 }
 
 #ifdef _MT_CODE_PTHREAD
--- native/iis/jk_isapi_plugin.c        2007/03/19 12:35:35
+++ native/iis/jk_isapi_plugin.c        2007/03/20 14:44:58
@@ -24,6 +24,9 @@
  * Version:     $Revision: 511888 $
*
 
************************************************************************
***/
 
+// Updated with patch from 
+//
http://mail-archives.apache.org/mod_mbox/tomcat-dev/200506.mbox/raw/%3C4
[EMAIL PROTECTED]/2
+
 // This define is needed to include wincrypt,h, needed to get client
certificates
 #define _WIN32_WINNT 0x0400
 
@@ -89,6 +92,17 @@
 #define URI_SELECT_PARSED_VERB      ("parsed")
 #define URI_SELECT_UNPARSED_VERB    ("unparsed")
 #define URI_SELECT_ESCAPED_VERB     ("escaped")
+
+#define ENABLE_CHUNKED_ENCODING_TAG ("enable_chunked_encoding")
+
+/* Headers used in negotiating chunked encoding */
+#define TRANSFER_ENCODING_HEADER_COMPLETE ("Transfer-Encoding:
chunked")
+#define TRANSFER_ENCODING_HEADER_NAME ("Transfer-Encoding")
+#define CONTENT_LENGTH_HEADER_NAME ("Content-Length")
+#define CONNECTION_HEADER_NAME ("Connection")
+#define CONNECTION_CLOSE_VALUE ("Close")
+
+
 #define URI_REWRITE_TAG             ("rewrite_rule_file")
 #define SHM_SIZE_TAG                ("shm_size")
 #define WORKER_MOUNT_RELOAD_TAG     ("worker_mount_reload")
@@ -146,6 +160,11 @@
         (place) = def;                                      \
   } } while(0)
 
+/* HTTP protocol CRLF */
+static char CRLF[3] = { (char)13, (char)10, '\0' };
+/* Whether chunked encoding has been enabled in the settings */
+static int chunked_encoding_enabled = JK_FALSE;
+
 static char ini_file_name[MAX_PATH];
 static int using_ini_file = JK_FALSE;
 static int is_inited = JK_FALSE;
@@ -189,6 +208,8 @@
     int request_started;
     unsigned int bytes_read_so_far;
     LPEXTENSION_CONTROL_BLOCK lpEcb;
+       
+       int chunk_content; /* Whether we're responding with
Transfer-Encoding: chunked content */
 };
 
 typedef struct isapi_log_data_t isapi_log_data_t;
@@ -209,6 +230,8 @@
 
 static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned
int l);
 
+static int JK_METHOD flush_response(jk_ws_service_t *s);
+
 static int init_ws_service(isapi_private_data_t * private_data,
                            jk_ws_service_t *s, char **worker_name);
 
@@ -533,11 +556,15 @@
     if (s && s->ws_private) {
         int rv = JK_TRUE;
         isapi_private_data_t *p = s->ws_private;
+               //int keep_alive = JK_FALSE;        /* Whether the
downstream or us can supply content length */
+
         if (!p->request_started) {
             HSE_SEND_HEADER_EX_INFO hi;
+                       HSE_SEND_HEADER_EX_INFO send_header_ex_info;
             char *status_str;
             char *headers_str = NULL;
-            BOOL keep_alive = FALSE;
+            //BOOL keep_alive = FALSE;
+                       int keep_alive = JK_FALSE;          /* Whether
the downstream or us can supply content length */
             p->request_started = JK_TRUE;
 
             /*
@@ -555,32 +582,113 @@
              * Create response headers string
              */
             if (num_of_headers) {
+                               int chunked_ok = JK_FALSE;          /*
Whether the downstream response allows chunking */
+                               int http11_request = JK_FALSE;  /*
Whether the client is HTTP/1.1 */
+
                 size_t i, len_of_headers;
+
+                /* Check if we've got an HTTP/1.1 response */
+                jk_log(logger, JK_LOG_DEBUG, 
+                    "jk_ws_service_t::start_response, Request uses
protocol %s\n", s->protocol);
+
+                if (strcasecmp(s->protocol, "HTTP/1.1")==0) {
+                    http11_request = JK_TRUE;
+                    chunked_ok = JK_TRUE;      /* Chunking only on
HTTP/1.1 */
+                }
+
                 for (i = 0, len_of_headers = 0; i < num_of_headers;
i++) {
                     len_of_headers += strlen(header_names[i]);
                     len_of_headers += strlen(header_values[i]);
                     len_of_headers += 4;   /* extra for colon, space
and crlf */
                 }
 
-                len_of_headers += 3;       /* crlf and terminating null
char */
+                               /* Provide room in the buffer for the
Transfer-Encoding header if we use it. */
+                len_of_headers +=
strlen(TRANSFER_ENCODING_HEADER_COMPLETE) + 2;
+
+                               len_of_headers += 3;       /* crlf and
terminating null char */
                 headers_str = (char *)malloc(len_of_headers);
                 headers_str[0] = '\0';
 
                 for (i = 0; i < num_of_headers; i++) {
+                    /* Check the downstream response to see whether 
+                    it's appropriate the chunk the response content
+                    and whether it supports keeping the connection open
*/
+                    if(strcasecmp(TRANSFER_ENCODING_HEADER_NAME,
header_names[i])==0) {
+                        keep_alive = http11_request;
+                        chunked_ok = JK_FALSE;
+                    }
+                    else if(strcasecmp(CONTENT_LENGTH_HEADER_NAME,
header_names[i])==0) {
+                        keep_alive = http11_request;
+                        chunked_ok = JK_FALSE;
+                    } 
+                    else if((strcasecmp(CONNECTION_HEADER_NAME,
header_names[i])==0)
+                            && (strcasecmp(CONNECTION_CLOSE_VALUE,
header_values[i])==0)) {
+                        keep_alive = JK_FALSE;
+                        chunked_ok = JK_FALSE;
+                    }
+               
                     StringCbCat(headers_str, len_of_headers,
header_names[i]);
                     StringCbCat(headers_str, len_of_headers, ": ");
                     StringCbCat(headers_str, len_of_headers,
header_values[i]);
-                    StringCbCat(headers_str, len_of_headers, crlf);
+                    StringCbCat(headers_str, len_of_headers, CRLF);
+                }
+                //StringCbCat(headers_str, len_of_headers, crlf);
+
+                /* Check if we can send chunked content */
+                if (chunked_encoding_enabled && chunked_ok) {
+                    jk_log(logger, JK_LOG_DEBUG, 
+                        "jk_ws_service_t::start_response, using
Transfer-Encoding: chunked\n");
+
+                    /** We will supply the transfer-encoding to allow
IIS to keep the connection open */
+                    keep_alive = JK_TRUE;
+
+                    p->chunk_content = JK_TRUE;
+                    /* Indicate to the client that the content will be
chunked
+                    - We've already reserved space for this */
+                                       StringCbCat(headers_str,
len_of_headers, TRANSFER_ENCODING_HEADER_COMPLETE);
+                               StringCbCat(headers_str, len_of_headers,
CRLF);
+                } 
+                else {
+                    p->chunk_content = JK_FALSE;
                 }
-                StringCbCat(headers_str, len_of_headers, crlf);
-                hi.pszHeader = headers_str;
-                hi.cchHeader = (DWORD)strlen(headers_str);
+                               
+                               StringCbCat(headers_str, len_of_headers,
CRLF);
+                //strcat(headers_str, CRLF);
+
+                //hi.pszHeader = headers_str;
+                //hi.cchHeader = (DWORD)strlen(headers_str);
             }
             else {
-                hi.pszHeader = crlf;
+                hi.pszHeader = CRLF;
                 hi.cchHeader = 2;
             }
-            hi.fKeepConn = keep_alive;
+
+            /** Fill in the response */
+            send_header_ex_info.pszStatus = status_str;
+            send_header_ex_info.pszHeader = headers_str;
+            send_header_ex_info.cchStatus = strlen(status_str);
+            send_header_ex_info.cchHeader = strlen(headers_str);
+
+            /* 
+             * Using the extended form of the API means we have to get
this right,
+             * i.e. IIS won't keep connections open if there's a
Content-Length and close them if there isn't.
+             */
+            jk_log(logger, JK_LOG_DEBUG, 
+                "jk_ws_service_t::start_response, keep_alive = %d\n",
keep_alive);
+
+            send_header_ex_info.fKeepConn = keep_alive;
+
+            if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, 
+
HSE_REQ_SEND_RESPONSE_HEADER_EX,
+                                                 &send_header_ex_info,
+                                                 NULL,
+                                                 NULL)) {
+                jk_log(logger, JK_LOG_ERROR, 
+                    "HSE_REQ_SEND_RESPONSE_HEADER failed");
+                               rv = JK_FALSE;
+                       }
+
+            /*hi.fKeepConn = keep_alive;
             if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
 
HSE_REQ_SEND_RESPONSE_HEADER_EX,
                                                  &hi,
@@ -588,7 +696,8 @@
                 jk_log(logger, JK_LOG_ERROR,
                        "HSE_REQ_SEND_RESPONSE_HEADER_EX failed");
                 rv = JK_FALSE;
-            }
+            }*/
+
             if (headers_str)
                 free(headers_str);
             if (status_str)
@@ -658,6 +767,39 @@
     return JK_FALSE;
 }
 
+/*
+* Writes a buffer to the ISAPI response.
+*/
+static int isapi_write_client(isapi_private_data_t *p, const char *buf,
unsigned write_length)
+{
+    unsigned written = 0;           
+    DWORD try_to_write = 0;
+
+    JK_TRACE_ENTER(logger);
+
+    while (written < write_length) {
+        try_to_write = (write_length - written);
+
+        if (!p->lpEcb->WriteClient(p->lpEcb->ConnID, 
+                                   buf + written, &try_to_write, 0)) {
+            jk_log(logger, JK_LOG_ERROR,
+                    "WriteClient failed with %08x", GetLastError());
+            JK_TRACE_EXIT(logger);
+            return JK_FALSE;
+        }
+        written += try_to_write;
+    }
+    JK_TRACE_EXIT(logger);
+    return JK_TRUE;
+}
+
+/* 
+* Write content to the response.
+* If chunked encoding has been enabled and the client supports it 
+*(and it's appropriate for the response), then this will write a
+* single "Transfer-Encoding: chunked" chunk
+* 
+*/
 static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned
int l)
 {
     JK_TRACE_ENTER(logger);
@@ -666,23 +808,56 @@
         isapi_private_data_t *p = s->ws_private;
 
         if (l) {
-            unsigned int written = 0;
+            //unsigned int written = 0;
             char *buf = (char *)b;
 
             if (!p->request_started) {
                 start_response(s, 200, NULL, NULL, NULL, 0);
             }
-
-            while (written < l) {
+            /*while (written < l) {
                 DWORD try_to_write = l - written;
                 if (!p->lpEcb->WriteClient(p->lpEcb->ConnID,
                                            buf + written,
&try_to_write, 0)) {
                     jk_log(logger, JK_LOG_ERROR,
                            "WriteClient failed with %08x",
GetLastError());
                     JK_TRACE_EXIT(logger);
+                    return JK_FALSE;*/
+                       
+                       /* Chunk header */
+            if (p->chunk_content) {
+                char chunk_header[sizeof(unsigned)*2+3]; /* Hex of
chunk length + CRLF + term. */
+                jk_log(logger, JK_LOG_DEBUG, 
+                    "Using chunked encoding - writing chunk header\n");
+
+                /* Construct chunk header : HEX CRLF*/
+                sprintf(chunk_header, "%X%s", l, CRLF);
+
+                if (!isapi_write_client(p, chunk_header,
strlen(chunk_header))) {
+                    jk_log(logger, JK_LOG_ERROR, 
+                        "WriteClient for chunk header failed\n");
+                    JK_TRACE_EXIT(logger);
                     return JK_FALSE;
                 }
-                written += try_to_write;
+            }
+
+            /* Write chunk body */
+            if (!isapi_write_client(p, buf, l)) {
+                jk_log(logger, JK_LOG_ERROR, 
+                    "WriteClient for body chunk failed\n");
+                JK_TRACE_EXIT(logger);
+                return JK_FALSE;
+            }
+
+            /* Write chunk trailer */
+            if (p->chunk_content) {
+                jk_log(logger, JK_LOG_DEBUG, 
+                    "Using chunked encoding - writing chunk
trailer\n");
+
+                if (!isapi_write_client(p, CRLF, strlen(CRLF))) {
+                    jk_log(logger, JK_LOG_ERROR, 
+                        "WriteClient for chunk trailer failed\n");
+                }
+            //    written += try_to_write;
             }
         }
 
@@ -696,6 +871,37 @@
     return JK_FALSE;
 }
 
+
+/**
+* In the case of a Transfer-Encoding: chunked response, this will write
the terminator chunk.
+*/
+static int JK_METHOD flush_response(jk_ws_service_t *s)
+{
+    JK_TRACE_ENTER(logger);
+ 
+    if (s && s->ws_private) {
+        isapi_private_data_t *p = s->ws_private;
+
+        /* Write last chunk + terminator */
+        if (p->chunk_content) {
+            static char CHUNKED_ENCODING_TRAILER[6] = { '0', (char)13,
(char)10, (char)13, (char)10, '\0' };
+
+            jk_log(logger, JK_LOG_DEBUG, 
+                "Terminating chunk encoded response.\n");
+
+            if (!isapi_write_client(p, CHUNKED_ENCODING_TRAILER,
strlen(CHUNKED_ENCODING_TRAILER))) {
+                jk_log(logger, JK_LOG_ERROR, 
+                    "WriteClient for chunk response terminator
failed\n");
+                JK_TRACE_EXIT(logger);
+                return JK_FALSE;
+            }
+        }
+       }    
+
+       JK_TRACE_EXIT(logger);
+    return JK_TRUE;
+}
+
 BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
 {
     BOOL rv = TRUE;
@@ -1087,6 +1293,7 @@
         private_data.request_started = JK_FALSE;
         private_data.bytes_read_so_far = 0;
         private_data.lpEcb = lpEcb;
+               private_data.chunk_content = JK_FALSE;
 
         s.ws_private = &private_data;
         s.pool = &private_data.p;
@@ -1270,6 +1477,7 @@
         jk_log(logger, JK_LOG_DEBUG, "Using rewrite rule file %s.",
                rewrite_rule_file);
         jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.",
uri_select_option);
+               jk_log(logger, JK_LOG_DEBUG, "Using chunked encoding?
%d.\n", chunked_encoding_enabled);
     }
 
     if (rewrite_rule_file[0] && jk_map_alloc(&rewrite_map)) {
@@ -1377,7 +1585,20 @@
         else {
             src = &hkey;
         }
+
+               chunked_encoding_enabled = jk_map_get_bool(map,
ENABLE_CHUNKED_ENCODING_TAG, JK_FALSE);
     }
+
+       ok = ok && get_config_parameter(src,
ENABLE_CHUNKED_ENCODING_TAG, tmpbuf, sizeof(tmpbuf));
+
+       if (ok) {
+               if (strcasecmp(tmpbuf, "true") == 0 ||
+                       *tmpbuf == 'Y' || *tmpbuf == 'y' || *tmpbuf ==
'1') {
+                               chunked_encoding_enabled = JK_TRUE;
+               }
+
+       }
+
     ok = ok && get_config_parameter(src, JK_LOG_FILE_TAG, log_file,
sizeof(log_file));
     if (get_config_parameter(src, JK_LOG_LEVEL_TAG, tmpbuf,
sizeof(tmpbuf))) {
         log_level = jk_parse_log_level(tmpbuf);
@@ -1395,6 +1616,8 @@
             ok = JK_FALSE;
         }
     }
+       
+
     shm_config_size = get_config_int(src, SHM_SIZE_TAG,
JK_SHM_DEF_SIZE);
     worker_mount_reload = get_config_int(src, WORKER_MOUNT_RELOAD_TAG,
JK_URIMAP_DEF_RELOAD);
     strip_session = get_config_bool(src, STRIP_SESSION_TAG, JK_FALSE);
@@ -1508,9 +1731,11 @@
     /* Yes we do want to reuse AJP connections */
     s->disable_reuse = JK_FALSE;
 
-    s->flush = NULL;
-    s->flush_packets = JK_FALSE;
+    //s->flush = NULL;
     s->flush_header = JK_FALSE;
+    /* We want to flush at end of content to terminate chunked encoding
*/
+    s->flush = flush_response;
+    s->flush_packets = JK_FALSE;   
 
     /* Clear RECO status */
     s->reco_status = RECO_NONE;


--
-bk
 
-----Original Message-----
From: Mladen Turk [mailto:[EMAIL PROTECTED] 
Sent: Tuesday, March 20, 2007 10:20 PM
To: Tomcat Users List
Subject: Re: JK ISAPI Filter and Keep-Alive/Chunked Encoding (a.k.a. Is
1.2.21 broken?)

Brandon Knitter wrote:
> I tried this, but still there was no chunked encoding in version
1.2.22.

You we speaking about Keep-Alive, not TE chunked.
> 
> We have patched 1.2.21 to allow for enabling of chunked encoding in
the
> ISAPI filter if you configure it to do so (registry entry).  Would
this
> be of any interest?  It's a rather small patch if you think it could
be
> rolled into 1.2.22 or 1.2.23.
> 

Sure, if it's not large, just post it here, and if it is,
open a bugzilla issue.

Regards,
Mladen.

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may 
contain
confidential information.  Any unauthorized review, use, disclosure or 
distribution
is prohibited.  If you are not the intended recipient, please contact the 
sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
--- native/common/jk_util.c     2007/03/19 12:35:35
+++ native/common/jk_util.c     2007/03/20 14:44:58
@@ -1706,6 +1706,9 @@
     s->route = NULL;
     s->retries = JK_RETRIES;
     s->add_log_items = NULL;
+
+       s->flush = NULL;
+       s->flush_packets = JK_FALSE;
 }
 
 #ifdef _MT_CODE_PTHREAD
--- native/iis/jk_isapi_plugin.c        2007/03/19 12:35:35
+++ native/iis/jk_isapi_plugin.c        2007/03/20 14:44:58
@@ -24,6 +24,9 @@
  * Version:     $Revision: 511888 $                                        *
  ***************************************************************************/
 
+// Updated with patch from 
+// http://mail-archives.apache.org/mod_mbox/tomcat-dev/200506.mbox/raw/[EMAIL 
PROTECTED]/2
+
 // This define is needed to include wincrypt,h, needed to get client 
certificates
 #define _WIN32_WINNT 0x0400
 
@@ -89,6 +92,17 @@
 #define URI_SELECT_PARSED_VERB      ("parsed")
 #define URI_SELECT_UNPARSED_VERB    ("unparsed")
 #define URI_SELECT_ESCAPED_VERB     ("escaped")
+
+#define ENABLE_CHUNKED_ENCODING_TAG ("enable_chunked_encoding")
+
+/* Headers used in negotiating chunked encoding */
+#define TRANSFER_ENCODING_HEADER_COMPLETE ("Transfer-Encoding: chunked")
+#define TRANSFER_ENCODING_HEADER_NAME ("Transfer-Encoding")
+#define CONTENT_LENGTH_HEADER_NAME ("Content-Length")
+#define CONNECTION_HEADER_NAME ("Connection")
+#define CONNECTION_CLOSE_VALUE ("Close")
+
+
 #define URI_REWRITE_TAG             ("rewrite_rule_file")
 #define SHM_SIZE_TAG                ("shm_size")
 #define WORKER_MOUNT_RELOAD_TAG     ("worker_mount_reload")
@@ -146,6 +160,11 @@
         (place) = def;                                      \
   } } while(0)
 
+/* HTTP protocol CRLF */
+static char CRLF[3] = { (char)13, (char)10, '\0' };
+/* Whether chunked encoding has been enabled in the settings */
+static int chunked_encoding_enabled = JK_FALSE;
+
 static char ini_file_name[MAX_PATH];
 static int using_ini_file = JK_FALSE;
 static int is_inited = JK_FALSE;
@@ -189,6 +208,8 @@
     int request_started;
     unsigned int bytes_read_so_far;
     LPEXTENSION_CONTROL_BLOCK lpEcb;
+       
+       int chunk_content; /* Whether we're responding with Transfer-Encoding: 
chunked content */
 };
 
 typedef struct isapi_log_data_t isapi_log_data_t;
@@ -209,6 +230,8 @@
 
 static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l);
 
+static int JK_METHOD flush_response(jk_ws_service_t *s);
+
 static int init_ws_service(isapi_private_data_t * private_data,
                            jk_ws_service_t *s, char **worker_name);
 
@@ -533,11 +556,15 @@
     if (s && s->ws_private) {
         int rv = JK_TRUE;
         isapi_private_data_t *p = s->ws_private;
+               //int keep_alive = JK_FALSE;        /* Whether the downstream 
or us can supply content length */
+
         if (!p->request_started) {
             HSE_SEND_HEADER_EX_INFO hi;
+                       HSE_SEND_HEADER_EX_INFO send_header_ex_info;
             char *status_str;
             char *headers_str = NULL;
-            BOOL keep_alive = FALSE;
+            //BOOL keep_alive = FALSE;
+                       int keep_alive = JK_FALSE;          /* Whether the 
downstream or us can supply content length */
             p->request_started = JK_TRUE;
 
             /*
@@ -555,32 +582,113 @@
              * Create response headers string
              */
             if (num_of_headers) {
+                               int chunked_ok = JK_FALSE;          /* Whether 
the downstream response allows chunking */
+                               int http11_request = JK_FALSE;  /* Whether the 
client is HTTP/1.1 */
+
                 size_t i, len_of_headers;
+
+                /* Check if we've got an HTTP/1.1 response */
+                jk_log(logger, JK_LOG_DEBUG, 
+                    "jk_ws_service_t::start_response, Request uses protocol 
%s\n", s->protocol);
+
+                if (strcasecmp(s->protocol, "HTTP/1.1")==0) {
+                    http11_request = JK_TRUE;
+                    chunked_ok = JK_TRUE;      /* Chunking only on HTTP/1.1 */
+                }
+
                 for (i = 0, len_of_headers = 0; i < num_of_headers; i++) {
                     len_of_headers += strlen(header_names[i]);
                     len_of_headers += strlen(header_values[i]);
                     len_of_headers += 4;   /* extra for colon, space and crlf 
*/
                 }
 
-                len_of_headers += 3;       /* crlf and terminating null char */
+                               /* Provide room in the buffer for the 
Transfer-Encoding header if we use it. */
+                len_of_headers += strlen(TRANSFER_ENCODING_HEADER_COMPLETE) + 
2;
+
+                               len_of_headers += 3;       /* crlf and 
terminating null char */
                 headers_str = (char *)malloc(len_of_headers);
                 headers_str[0] = '\0';
 
                 for (i = 0; i < num_of_headers; i++) {
+                    /* Check the downstream response to see whether 
+                    it's appropriate the chunk the response content
+                    and whether it supports keeping the connection open */
+                    if(strcasecmp(TRANSFER_ENCODING_HEADER_NAME, 
header_names[i])==0) {
+                        keep_alive = http11_request;
+                        chunked_ok = JK_FALSE;
+                    }
+                    else if(strcasecmp(CONTENT_LENGTH_HEADER_NAME, 
header_names[i])==0) {
+                        keep_alive = http11_request;
+                        chunked_ok = JK_FALSE;
+                    } 
+                    else if((strcasecmp(CONNECTION_HEADER_NAME, 
header_names[i])==0)
+                            && (strcasecmp(CONNECTION_CLOSE_VALUE, 
header_values[i])==0)) {
+                        keep_alive = JK_FALSE;
+                        chunked_ok = JK_FALSE;
+                    }
+               
                     StringCbCat(headers_str, len_of_headers, header_names[i]);
                     StringCbCat(headers_str, len_of_headers, ": ");
                     StringCbCat(headers_str, len_of_headers, header_values[i]);
-                    StringCbCat(headers_str, len_of_headers, crlf);
+                    StringCbCat(headers_str, len_of_headers, CRLF);
+                }
+                //StringCbCat(headers_str, len_of_headers, crlf);
+
+                /* Check if we can send chunked content */
+                if (chunked_encoding_enabled && chunked_ok) {
+                    jk_log(logger, JK_LOG_DEBUG, 
+                        "jk_ws_service_t::start_response, using 
Transfer-Encoding: chunked\n");
+
+                    /** We will supply the transfer-encoding to allow IIS to 
keep the connection open */
+                    keep_alive = JK_TRUE;
+
+                    p->chunk_content = JK_TRUE;
+                    /* Indicate to the client that the content will be chunked
+                    - We've already reserved space for this */
+                                       StringCbCat(headers_str, 
len_of_headers, TRANSFER_ENCODING_HEADER_COMPLETE);
+                               StringCbCat(headers_str, len_of_headers, CRLF);
+                } 
+                else {
+                    p->chunk_content = JK_FALSE;
                 }
-                StringCbCat(headers_str, len_of_headers, crlf);
-                hi.pszHeader = headers_str;
-                hi.cchHeader = (DWORD)strlen(headers_str);
+                               
+                               StringCbCat(headers_str, len_of_headers, CRLF);
+                //strcat(headers_str, CRLF);
+
+                //hi.pszHeader = headers_str;
+                //hi.cchHeader = (DWORD)strlen(headers_str);
             }
             else {
-                hi.pszHeader = crlf;
+                hi.pszHeader = CRLF;
                 hi.cchHeader = 2;
             }
-            hi.fKeepConn = keep_alive;
+
+            /** Fill in the response */
+            send_header_ex_info.pszStatus = status_str;
+            send_header_ex_info.pszHeader = headers_str;
+            send_header_ex_info.cchStatus = strlen(status_str);
+            send_header_ex_info.cchHeader = strlen(headers_str);
+
+            /* 
+             * Using the extended form of the API means we have to get this 
right,
+             * i.e. IIS won't keep connections open if there's a 
Content-Length and close them if there isn't.
+             */
+            jk_log(logger, JK_LOG_DEBUG, 
+                "jk_ws_service_t::start_response, keep_alive = %d\n", 
keep_alive);
+
+            send_header_ex_info.fKeepConn = keep_alive;
+
+            if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, 
+                                                 
HSE_REQ_SEND_RESPONSE_HEADER_EX,
+                                                 &send_header_ex_info,
+                                                 NULL,
+                                                 NULL)) {
+                jk_log(logger, JK_LOG_ERROR, 
+                    "HSE_REQ_SEND_RESPONSE_HEADER failed");
+                               rv = JK_FALSE;
+                       }
+
+            /*hi.fKeepConn = keep_alive;
             if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
                                                  
HSE_REQ_SEND_RESPONSE_HEADER_EX,
                                                  &hi,
@@ -588,7 +696,8 @@
                 jk_log(logger, JK_LOG_ERROR,
                        "HSE_REQ_SEND_RESPONSE_HEADER_EX failed");
                 rv = JK_FALSE;
-            }
+            }*/
+
             if (headers_str)
                 free(headers_str);
             if (status_str)
@@ -658,6 +767,39 @@
     return JK_FALSE;
 }
 
+/*
+* Writes a buffer to the ISAPI response.
+*/
+static int isapi_write_client(isapi_private_data_t *p, const char *buf, 
unsigned write_length)
+{
+    unsigned written = 0;           
+    DWORD try_to_write = 0;
+
+    JK_TRACE_ENTER(logger);
+
+    while (written < write_length) {
+        try_to_write = (write_length - written);
+
+        if (!p->lpEcb->WriteClient(p->lpEcb->ConnID, 
+                                   buf + written, &try_to_write, 0)) {
+            jk_log(logger, JK_LOG_ERROR,
+                    "WriteClient failed with %08x", GetLastError());
+            JK_TRACE_EXIT(logger);
+            return JK_FALSE;
+        }
+        written += try_to_write;
+    }
+    JK_TRACE_EXIT(logger);
+    return JK_TRUE;
+}
+
+/* 
+* Write content to the response.
+* If chunked encoding has been enabled and the client supports it 
+*(and it's appropriate for the response), then this will write a
+* single "Transfer-Encoding: chunked" chunk
+* 
+*/
 static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l)
 {
     JK_TRACE_ENTER(logger);
@@ -666,23 +808,56 @@
         isapi_private_data_t *p = s->ws_private;
 
         if (l) {
-            unsigned int written = 0;
+            //unsigned int written = 0;
             char *buf = (char *)b;
 
             if (!p->request_started) {
                 start_response(s, 200, NULL, NULL, NULL, 0);
             }
-
-            while (written < l) {
+            /*while (written < l) {
                 DWORD try_to_write = l - written;
                 if (!p->lpEcb->WriteClient(p->lpEcb->ConnID,
                                            buf + written, &try_to_write, 0)) {
                     jk_log(logger, JK_LOG_ERROR,
                            "WriteClient failed with %08x", GetLastError());
                     JK_TRACE_EXIT(logger);
+                    return JK_FALSE;*/
+                       
+                       /* Chunk header */
+            if (p->chunk_content) {
+                char chunk_header[sizeof(unsigned)*2+3]; /* Hex of chunk 
length + CRLF + term. */
+                jk_log(logger, JK_LOG_DEBUG, 
+                    "Using chunked encoding - writing chunk header\n");
+
+                /* Construct chunk header : HEX CRLF*/
+                sprintf(chunk_header, "%X%s", l, CRLF);
+
+                if (!isapi_write_client(p, chunk_header, 
strlen(chunk_header))) {
+                    jk_log(logger, JK_LOG_ERROR, 
+                        "WriteClient for chunk header failed\n");
+                    JK_TRACE_EXIT(logger);
                     return JK_FALSE;
                 }
-                written += try_to_write;
+            }
+
+            /* Write chunk body */
+            if (!isapi_write_client(p, buf, l)) {
+                jk_log(logger, JK_LOG_ERROR, 
+                    "WriteClient for body chunk failed\n");
+                JK_TRACE_EXIT(logger);
+                return JK_FALSE;
+            }
+
+            /* Write chunk trailer */
+            if (p->chunk_content) {
+                jk_log(logger, JK_LOG_DEBUG, 
+                    "Using chunked encoding - writing chunk trailer\n");
+
+                if (!isapi_write_client(p, CRLF, strlen(CRLF))) {
+                    jk_log(logger, JK_LOG_ERROR, 
+                        "WriteClient for chunk trailer failed\n");
+                }
+            //    written += try_to_write;
             }
         }
 
@@ -696,6 +871,37 @@
     return JK_FALSE;
 }
 
+
+/**
+* In the case of a Transfer-Encoding: chunked response, this will write the 
terminator chunk.
+*/
+static int JK_METHOD flush_response(jk_ws_service_t *s)
+{
+    JK_TRACE_ENTER(logger);
+ 
+    if (s && s->ws_private) {
+        isapi_private_data_t *p = s->ws_private;
+
+        /* Write last chunk + terminator */
+        if (p->chunk_content) {
+            static char CHUNKED_ENCODING_TRAILER[6] = { '0', (char)13, 
(char)10, (char)13, (char)10, '\0' };
+
+            jk_log(logger, JK_LOG_DEBUG, 
+                "Terminating chunk encoded response.\n");
+
+            if (!isapi_write_client(p, CHUNKED_ENCODING_TRAILER, 
strlen(CHUNKED_ENCODING_TRAILER))) {
+                jk_log(logger, JK_LOG_ERROR, 
+                    "WriteClient for chunk response terminator failed\n");
+                JK_TRACE_EXIT(logger);
+                return JK_FALSE;
+            }
+        }
+       }    
+
+       JK_TRACE_EXIT(logger);
+    return JK_TRUE;
+}
+
 BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
 {
     BOOL rv = TRUE;
@@ -1087,6 +1293,7 @@
         private_data.request_started = JK_FALSE;
         private_data.bytes_read_so_far = 0;
         private_data.lpEcb = lpEcb;
+               private_data.chunk_content = JK_FALSE;
 
         s.ws_private = &private_data;
         s.pool = &private_data.p;
@@ -1270,6 +1477,7 @@
         jk_log(logger, JK_LOG_DEBUG, "Using rewrite rule file %s.",
                rewrite_rule_file);
         jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", 
uri_select_option);
+               jk_log(logger, JK_LOG_DEBUG, "Using chunked encoding? %d.\n", 
chunked_encoding_enabled);
     }
 
     if (rewrite_rule_file[0] && jk_map_alloc(&rewrite_map)) {
@@ -1377,7 +1585,20 @@
         else {
             src = &hkey;
         }
+
+               chunked_encoding_enabled = jk_map_get_bool(map, 
ENABLE_CHUNKED_ENCODING_TAG, JK_FALSE);
     }
+
+       ok = ok && get_config_parameter(src, ENABLE_CHUNKED_ENCODING_TAG, 
tmpbuf, sizeof(tmpbuf));
+
+       if (ok) {
+               if (strcasecmp(tmpbuf, "true") == 0 ||
+                       *tmpbuf == 'Y' || *tmpbuf == 'y' || *tmpbuf == '1') {
+                               chunked_encoding_enabled = JK_TRUE;
+               }
+
+       }
+
     ok = ok && get_config_parameter(src, JK_LOG_FILE_TAG, log_file, 
sizeof(log_file));
     if (get_config_parameter(src, JK_LOG_LEVEL_TAG, tmpbuf, sizeof(tmpbuf))) {
         log_level = jk_parse_log_level(tmpbuf);
@@ -1395,6 +1616,8 @@
             ok = JK_FALSE;
         }
     }
+       
+
     shm_config_size = get_config_int(src, SHM_SIZE_TAG, JK_SHM_DEF_SIZE);
     worker_mount_reload = get_config_int(src, WORKER_MOUNT_RELOAD_TAG, 
JK_URIMAP_DEF_RELOAD);
     strip_session = get_config_bool(src, STRIP_SESSION_TAG, JK_FALSE);
@@ -1508,9 +1731,11 @@
     /* Yes we do want to reuse AJP connections */
     s->disable_reuse = JK_FALSE;
 
-    s->flush = NULL;
-    s->flush_packets = JK_FALSE;
+    //s->flush = NULL;
     s->flush_header = JK_FALSE;
+    /* We want to flush at end of content to terminate chunked encoding */
+    s->flush = flush_response;
+    s->flush_packets = JK_FALSE;   
 
     /* Clear RECO status */
     s->reco_status = RECO_NONE;
---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to