RFC: mod_ssl output buffering

2010-11-05 Thread Joe Orton
mod_ssl's output buffering has been bothering me for a while.

1) it buffers the encrypted output stream (to some extent) coupled with 
regular use of FLUSH buckets.  This seems redundant/inefficient; the 
core output filter should be doing this kind of thing optimally already.

2) it does /not/ do any buffering of the plaintext input brigades.  This 
leads to some pathological inefficiency as discussed here in the past:

http://marc.info/?l=apache-httpd-devm=125070085316400w=2

The one justification I know for (1) is in the ssl_engine_io.c comment:

/* the first two SSL_writes (of 1024 and 261 bytes)
 * need to be in the same packet (vec[0].iov_base)
 */

but I see no evidence this will not happen if the core output filter is 
allowed to operate as normal.  Maybe that was not true in the psat.

So I'd propose to remove (1), which is simple and add (2), which is not 
so simple.  I've hacked up (yet another) coalescing filter for (2) 
which works to coalesce chunked brigades as expected; proof of concept 
attached.  Would welcome some discussion before I work on this 
further...

Regards, Joe
Index: ssl_engine_io.c
===
--- ssl_engine_io.c (revision 1031534)
+++ ssl_engine_io.c (working copy)
@@ -102,7 +102,6 @@
 BIO*pbioWrite;
 ap_filter_t*pInputFilter;
 ap_filter_t*pOutputFilter;
-intnobuffer; /* non-zero to prevent buffering */
 SSLConnRec *config;
 } ssl_filter_ctx_t;
 
@@ -110,9 +109,6 @@
 ssl_filter_ctx_t *filter_ctx;
 conn_rec *c;
 apr_bucket_brigade *bb;/* Brigade used as a buffer. */
-apr_size_t length; /* Number of bytes stored in -bb brigade. */
-char buffer[AP_IOBUFSIZE]; /* Fixed-size buffer */
-apr_size_t blen;   /* Number of bytes of -buffer used. */
 apr_status_t rc;
 } bio_filter_out_ctx_t;
 
@@ -124,36 +120,13 @@
 outctx-filter_ctx = filter_ctx;
 outctx-c = c;
 outctx-bb = apr_brigade_create(c-pool, c-bucket_alloc);
-outctx-blen = 0;
-outctx-length = 0;
 
 return outctx;
 }
 
-static int bio_filter_out_flush(BIO *bio)
+/* Pass the output filter brigade up the filter stack. */
+static int bio_filter_out_pass(bio_filter_out_ctx_t *outctx)
 {
-bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio-ptr);
-apr_bucket *e;
-
-if (!(outctx-blen || outctx-length)) {
-outctx-rc = APR_SUCCESS;
-return 1;
-}
-
-if (outctx-blen) {
-e = apr_bucket_transient_create(outctx-buffer, outctx-blen,
-outctx-bb-bucket_alloc);
-/* we filled this buffer first so add it to the
- * head of the brigade
- */
-APR_BRIGADE_INSERT_HEAD(outctx-bb, e);
-outctx-blen = 0;
-}
-
-outctx-length = 0;
-e = apr_bucket_flush_create(outctx-bb-bucket_alloc);
-APR_BRIGADE_INSERT_TAIL(outctx-bb, e);
-
 outctx-rc = ap_pass_brigade(outctx-filter_ctx-pOutputFilter-next,
  outctx-bb);
 /* Fail if the connection was reset: */
@@ -163,6 +136,17 @@
 return (outctx-rc == APR_SUCCESS) ? 1 : -1;
 }
 
+static int bio_filter_out_flush(BIO *bio)
+{
+bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio-ptr);
+apr_bucket *e;
+
+e = apr_bucket_flush_create(outctx-bb-bucket_alloc);
+APR_BRIGADE_INSERT_TAIL(outctx-bb, e);
+
+return bio_filter_out_pass(outctx);
+}
+
 static int bio_filter_create(BIO *bio)
 {
 bio-shutdown = 1;
@@ -194,7 +178,8 @@
 static int bio_filter_out_write(BIO *bio, const char *in, int inl)
 {
 bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio-ptr);
-
+apr_bucket *e;
+
 /* Abort early if the client has initiated a renegotiation. */
 if (outctx-filter_ctx-config-reneg_state == RENEG_ABORT) {
 outctx-rc = APR_ECONNABORTED;
@@ -207,77 +192,48 @@
  */
 BIO_clear_retry_flags(bio);
 
-if (!outctx-length  (inl + outctx-blen  sizeof(outctx-buffer)) 
-!outctx-filter_ctx-nobuffer) {
-/* the first two SSL_writes (of 1024 and 261 bytes)
- * need to be in the same packet (vec[0].iov_base)
- */
-/* XXX: could use apr_brigade_write() to make code look cleaner
- * but this way we avoid the malloc(APR_BUCKET_BUFF_SIZE)
- * and free() of it later
- */
-memcpy(outctx-buffer[outctx-blen], in, inl);
-outctx-blen += inl;
+/* pass along the encrypted data
+ * need to flush since we're using SSL's malloc-ed buffer
+ * which will be overwritten once we leave here
+ */
+e = apr_bucket_transient_create(in, inl, outctx-bb-bucket_alloc);
+APR_BRIGADE_INSERT_TAIL(outctx-bb, e);
+
+if (bio_filter_out_pass(outctx)  0) {
+return -1;
 }
-else {
-/* pass along the encrypted data
- * need to flush since we're using SSL's 

Re: RFC: mod_ssl output buffering

2010-11-05 Thread Rainer Jung
Interesting stuff. I patched trunk and ran the test suite (using OpenSSL 
0.9.8o).


All tests pass for MPMs prefork, worker,event on Solaris 10 (but maybe 
you knew that already).


I didn't really check the intended functionality though.

Compilation showed two trivial warnings:

ssl_engine_io.c: In function `flush_coalesced_data':
ssl_engine_io.c:1348: warning: long unsigned int format, apr_size_t arg 
(arg 8)

ssl_engine_io.c: In function `ssl_io_filter_coalesce':
ssl_engine_io.c:1422: warning: long unsigned int format, apr_size_t arg 
(arg 8)


Both are about trace logging, using %lu for ctx-bytes resp. len. Using 
APR_SIZE_T_FMT instead of lu fixes those.


There are some strange lines in the switch which is part of 
bio_filter_out_ctrl() (the part starting with PENDING; note the 
break in the previous line):


 227 case BIO_CTRL_GET_CLOSE:
 228 ret = (long)bio-shutdown;
 229 break;
 230   case BIO_CTRL_SET_CLOSE:
 231 bio-shutdown = (int)num;
 232 break;
 233 /* _PENDING is interpreted as pending to read - since this
 234  * is a *write* buffer, return zero. */
 235 ret = 0L;
 236 break;
...

Finally: I wasn't able to find eeeko, only eeko [1] and eeek [2]. Though 
there is eeeko! [3].


Have a nice weekend

Rainer

[1] http://www.urbandictionary.com/define.php?term=eeko
[2] http://www.urbandictionary.com/define.php?term=eeek
[3] http://eeeko.com/