Hi Graham,

On Tue, Dec 9, 2014 at 5:50 PM, Graham Leggett <minf...@sharp.fm> wrote:
> On 01 Dec 2014, at 7:45 PM, Yann Ylavic <ylavic....@gmail.com> wrote:
>
>> OK, the other way around then (I thought osize was the block size), so
>> it can read more bytes than asked to.
>> This may also be unexpected by the caller...
>>
>> At least for the nonblocking case, I think we should return what we
>> have so far (if any).
>
> Something like this?

Regarding the input filter wrt nonblocking, I'd rather do it this way
(modifications inline) :

+static apr_status_t crypto_in_filter(ap_filter_t *f, apr_bucket_brigade *bb,
+        ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
+{
+    apr_bucket *e, *after;
+    apr_status_t rv = APR_SUCCESS;
+    crypto_ctx *ctx = f->ctx;
+
+    if (!ctx->tmp) {
+        ctx->tmp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+    }
+
+    /* just get out of the way of things we don't want. */
+    if (mode != AP_MODE_READBYTES) {
+        return ap_get_brigade(f->next, bb, mode, block, readbytes);
+    }
+
+    /* if our buffer is empty, read off the network until the buffer is full */
+    if (APR_BRIGADE_EMPTY(ctx->bb)) {
+        ctx->remaining = ctx->osize;
+        ctx->written = 0;
+
+        while (!ctx->seen_eos && ctx->remaining > 0) {
+            const char *data;
+            apr_size_t size = 0;
+
+            rv = ap_get_brigade(f->next, ctx->tmp, mode, block,
ctx->remaining);
+
+            /* if an error was received, bail out now. If the error is
+             * EAGAIN and we have not yet seen an EOS, we will definitely
+             * be called again, at which point we will send our buffered
+             * data. Instead of sending EAGAIN, some filters return an
+             * empty brigade instead when data is not yet available. In
+             * this case, we drop through and pass buffered data, if any.
+             */
+            if (!APR_STATUS_IS_EAGAIN(rv) && APR_SUCCESS != rv) {
+                return rv;
+            }

Here:
            if (APR_STATUS_IS_EAGAIN(rv) || APR_BRIGADE_EMPTY(ctx->tmp)) {
                if (APR_BRIGADE_EMPTY(ctx->bb)) {
                    return rv:
                }
                break;
            }
            if (APR_SUCCESS != rv) {
                return rv;
            }

+
+            for (e = APR_BRIGADE_FIRST(ctx->tmp);
+                    e != APR_BRIGADE_SENTINEL(ctx->tmp);
+                    e = APR_BUCKET_NEXT(e)) {
+
+                /* if we see an EOS, we are done */
+                if (APR_BUCKET_IS_EOS(e)) {
+
+                    /* handle any leftovers */
+                    do_crypto(f, NULL, 0, 1);
+                    apr_brigade_write(ctx->bb, NULL, NULL,
+                            (const char *) ctx->out, ctx->written);
+
+                    APR_BUCKET_REMOVE(e);
+                    APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+                    ctx->seen_eos = 1;
+                    break;
+                }
+
+                /* flush buckets clear the buffer */
+                if (APR_BUCKET_IS_FLUSH(e)) {
+                    APR_BUCKET_REMOVE(e);
+                    APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+                    break;
+                }
+
+                /* pass metadata buckets through */
+                if (APR_BUCKET_IS_METADATA(e)) {
+                    APR_BUCKET_REMOVE(e);
+                    APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
+                    continue;
+                }
+
+                /* read the bucket in, pack it into the buffer */
+                rv = apr_bucket_read(e, &data, &size, block);
+                if (APR_SUCCESS == rv || APR_STATUS_IS_EAGAIN(rv)) {

And here instead :
                rv = apr_bucket_read(e, &data, &size, APR_BLOCK_READ);
                if (APR_SUCCESS == rv) {
since nothing should block (likely not a socket or pipe bucket).
Otherwise we'd have to keep this bucket to restart from it on the next call...

+
+                    do_crypto(f, (unsigned char *) data, size, 0);
+                    if (!ctx->remaining || APR_STATUS_IS_EAGAIN(rv)) {
+                        apr_brigade_write(ctx->bb, NULL, NULL,
+                                (const char *) ctx->out, ctx->written);
+                    }
+
+                    apr_bucket_delete(e);
+                }
+                else {
+                    return rv;
+                }
+
+            }
+        }
+    }
+
+    /* give the caller the data they asked for from the buffer */
+    apr_brigade_partition(ctx->bb, readbytes, &after);
+    e = APR_BRIGADE_FIRST(ctx->bb);
+    while (e != after) {
+        if (APR_BUCKET_IS_EOS(e)) {
+            /* last bucket read, step out of the way */
+            ap_remove_input_filter(f);
+        }
+        APR_BUCKET_REMOVE(e);
+        APR_BRIGADE_INSERT_TAIL(bb, e);
+        e = APR_BRIGADE_FIRST(ctx->bb);
+    }
+
+    /* clear the content length */
+    if (!ctx->clength) {
+        ctx->clength = 1;
+        apr_table_unset(f->r->headers_in, "Content-Length");
+    }
+
+    return rv;
+}

Regards,
Yann.

>
> Regards,
> Graham
> —

Reply via email to