httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ruediger Pluem <rpl...@apache.org>
Subject Re: svn commit: r1133582 - in /httpd/httpd/trunk: CHANGES docs/manual/filter.xml docs/manual/mod/mod_data.xml docs/manual/mod/mod_data.xml.meta docs/manual/new_features_2_4.xml modules/filters/config.m4 modules/filters/mod_data.c
Date Thu, 09 Jun 2011 07:16:50 GMT


On 06/09/2011 12:13 AM, minfrin@apache.org wrote:
> Author: minfrin
> Date: Wed Jun  8 22:13:21 2011
> New Revision: 1133582
> 
> URL: http://svn.apache.org/viewvc?rev=1133582&view=rev
> Log:
> mod_data: Introduce a filter to support RFC2397 data URLs.
> 
> Added:
>     httpd/httpd/trunk/docs/manual/mod/mod_data.xml
>     httpd/httpd/trunk/docs/manual/mod/mod_data.xml.meta
>     httpd/httpd/trunk/modules/filters/mod_data.c
> Modified:
>     httpd/httpd/trunk/CHANGES
>     httpd/httpd/trunk/docs/manual/filter.xml
>     httpd/httpd/trunk/docs/manual/new_features_2_4.xml
>     httpd/httpd/trunk/modules/filters/config.m4
> 
>
> Added: httpd/httpd/trunk/modules/filters/mod_data.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/filters/mod_data.c?rev=1133582&view=auto
> ==============================================================================
> --- httpd/httpd/trunk/modules/filters/mod_data.c (added)
> +++ httpd/httpd/trunk/modules/filters/mod_data.c Wed Jun  8 22:13:21 2011
> @@ -0,0 +1,237 @@
> +/* Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements.  See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You under the Apache License, Version 2.0
> + * (the "License"); you may not use this file except in compliance with
> + * the License.  You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +/*
> + * mod_data.c --- Turn the response into an rfc2397 data URL, suitable for
> + *                displaying as inline content on a page.
> + */
> +
> +#include "apr.h"
> +#include "apr_strings.h"
> +#include "apr_buckets.h"
> +#include "apr_base64.h"
> +#include "apr_lib.h"
> +
> +#include "ap_config.h"
> +#include "util_filter.h"
> +#include "httpd.h"
> +#include "http_config.h"
> +#include "http_log.h"
> +#include "http_request.h"
> +#include "http_protocol.h"
> +
> +#define DATA_FILTER "DATA"
> +
> +module AP_MODULE_DECLARE_DATA data_module;
> +
> +typedef struct data_ctx
> +{
> +    unsigned char overflow[3];
> +    int count;
> +    apr_bucket_brigade *bb;
> +} data_ctx;
> +
> +/**
> + * Create a data URL as follows:
> + *
> + * data:[<mime-type>;][charset=<charset>;]base64,<payload>
> + *
> + * Where:
> + *
> + * mime-type: The mime type of the original response.
> + * charset: The optional character set corresponding to the mime type.
> + * payload: A base64 version of the response body.
> + *
> + * The content type of the response is set to text/plain.
> + *
> + * The Content-Length header, if present, is updated with the new content
> + * length based on the increase in size expected from the base64 conversion.
> + * If the Content-Length header is too large to fit into an int, we remove
> + * the Content-Length header instead.
> + */
> +static apr_status_t data_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
> +{
> +    apr_bucket *e;
> +    request_rec *r = f->r;
> +    data_ctx *ctx = f->ctx;
> +    apr_status_t rv = APR_SUCCESS;
> +
> +    /* first time in? create a context */
> +    if (!ctx) {
> +        char *type;
> +        char *charset;
> +        char *end;
> +        const char *content_length;
> +
> +        /* base64-ing won't work on subrequests, it would be nice if
> +         * it did. Within subrequests, we have no EOS to check for,
> +         * so we don't know when to flush the tail to the network.
> +         */
> +        if (!ap_is_initial_req(f->r)) {
> +            ap_remove_output_filter(f);
> +            return ap_pass_brigade(f->next, bb);
> +        }
> +
> +        ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
> +        ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
> +
> +        type = apr_pstrdup(r->pool, r->content_type);
> +        if (type) {
> +            charset = strchr(type, ' ');
> +            if (charset) {
> +                *charset++ = 0;
> +                end = strchr(charset, ' ');
> +                if (end) {
> +                    *end++ = 0;
> +                }
> +            }
> +        }
> +
> +        apr_brigade_printf(ctx->bb, NULL, NULL, "data:%s%s;base64,",
> +                type ? type : "", charset ? charset : "");
> +
> +        content_length = apr_table_get(r->headers_out, "Content-Length");
> +        if (content_length) {
> +            apr_off_t len, clen;
> +            apr_brigade_length(ctx->bb, 1, &len);
> +            clen = apr_atoi64(content_length);
> +            if (clen >= 0 && clen < APR_INT32_MAX) {
> +                ap_set_content_length(r, len + apr_base64_encode_len(clen) - 1);
> +            }
> +            else {
> +                apr_table_unset(r->headers_out, "Content-Length");
> +            }
> +        }
> +
> +        ap_set_content_type(r, "text/plain");
> +
> +    }
> +
> +    /* Do nothing if asked to filter nothing. */
> +    if (APR_BRIGADE_EMPTY(bb)) {
> +        return ap_pass_brigade(f->next, bb);
> +    }
> +
> +    while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(bb)) {
> +        const char *data;
> +        apr_size_t size;
> +        apr_size_t tail;
> +        apr_size_t len;
> +        /* buffer big enough for 8000 encoded bytes (6000 raw bytes) and terminator
*/
> +        char buffer[APR_BUCKET_BUFF_SIZE + 1];
> +        char encoded[((sizeof(ctx->overflow)) / 3) * 4 + 1];
> +
> +        /* make sure we don't read more than 6000 bytes at a time */
> +        apr_brigade_partition(bb, (APR_BUCKET_BUFF_SIZE / 4 * 3), &e);

Shouldn't we move this below the the checking for the metadata bucket?
Why partitioning again, when we only move a metadata bucket between the brigades?

> +
> +        e = APR_BRIGADE_FIRST(bb);
> +
> +        /* EOS means we are done. */
> +        if (APR_BUCKET_IS_EOS(e)) {
> +
> +            /* write away the tail */
> +            if (ctx->count) {
> +                len = apr_base64_encode_binary(encoded, ctx->overflow,
> +                        ctx->count);
> +                apr_brigade_write(ctx->bb, NULL, NULL, encoded, len - 1);
> +                ctx->count = 0;
> +            }
> +
> +            /* pass the EOS across */
> +            APR_BUCKET_REMOVE(e);
> +            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
> +
> +            /* pass what we have down the chain */
> +            ap_remove_output_filter(f);
> +            rv = ap_pass_brigade(f->next, ctx->bb);
> +            continue;
> +        }
> +
> +        /* metadata buckets are preserved as is */
> +        if (APR_BUCKET_IS_METADATA(e)) {
> +            /*
> +             * Remove meta data bucket from old brigade and insert into the
> +             * new.
> +             */
> +            APR_BUCKET_REMOVE(e);
> +            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
> +            continue;

Why do we ignore FLUSH buckets here? Shouldn't we handle them similar to EOS except for the
removal of
the filter?

> +        }
> +
> +        /* size will never be more than 6000 bytes */
> +        if (APR_SUCCESS == (rv = apr_bucket_read(e, &data, &size,
> +                APR_BLOCK_READ))) {
> +
> +            /* fill up and write out our overflow buffer if partially used */
> +            while (size && ctx->count && ctx->count < sizeof(ctx->overflow))
{
> +                ctx->overflow[ctx->count++] = *data++;
> +                size--;
> +            }
> +            if (ctx->count == sizeof(ctx->overflow)) {
> +                len = apr_base64_encode_binary(encoded, ctx->overflow,
> +                        sizeof(ctx->overflow));
> +                apr_brigade_write(ctx->bb, NULL, NULL, encoded, len - 1);
> +                ctx->count = 0;
> +            }
> +
> +            /* write the main base64 chunk */
> +            tail = size % sizeof(ctx->overflow);
> +            size -= tail;
> +            if (size) {
> +                len = apr_base64_encode_binary(buffer,
> +                        (const unsigned char *) data, size);
> +                apr_brigade_write(ctx->bb, NULL, NULL, buffer, len - 1);
> +            }
> +
> +            /* save away any tail in the overflow buffer */
> +            if (tail) {
> +                memcpy(ctx->overflow, data + size, tail);
> +                ctx->count += tail;
> +            }
> +
> +            apr_bucket_delete(e);
> +
> +            /* pass what we have down the chain */
> +            rv = ap_pass_brigade(f->next, ctx->bb);
> +            if (rv) {
> +                /* should break out of the loop, since our write to the client 
> +                 * failed in some way. */
> +                continue;
> +            }
> +
> +        }
> +
> +    }

Metadata buckets from the origin brigade that come after an EOS bucket are copied to ctx->bb
but
silently dropped. Shouldn't we have a

if ((APR_SUCCESS == rv) && (!APR_BRIGADE_EMPTY(ctx-bb))) {
    rv = ap_pass_brigade(f->next, ctx->bb);
}

here for safety reasons?

> +
> +    return rv;
> +
> +}
> +

Regards

RĂ¼diger


Mime
View raw message