in modules/filters/mod_include.c [3388:3792]
static apr_status_t send_parsed_content(ap_filter_t *f, apr_bucket_brigade *bb)
{
include_ctx_t *ctx = f->ctx;
struct ssi_internal_ctx *intern = ctx->intern;
request_rec *r = f->r;
apr_bucket *b = APR_BRIGADE_FIRST(bb);
apr_bucket_brigade *pass_bb;
apr_status_t rv = APR_SUCCESS;
char *magic; /* magic pointer for sentinel use */
/* fast exit */
if (APR_BRIGADE_EMPTY(bb)) {
return APR_SUCCESS;
}
/* we may crash, since already cleaned up; hand over the responsibility
* to the next filter;-)
*/
if (intern->seen_eos) {
return ap_pass_brigade(f->next, bb);
}
/* All stuff passed along has to be put into that brigade */
pass_bb = apr_brigade_create(ctx->pool, f->c->bucket_alloc);
/* initialization for this loop */
intern->bytes_read = 0;
intern->error = 0;
ctx->flush_now = 0;
/* loop over the current bucket brigade */
while (b != APR_BRIGADE_SENTINEL(bb)) {
const char *data = NULL;
apr_size_t len, index, release;
apr_bucket *newb = NULL;
char **store = &magic;
apr_size_t *store_len = NULL;
/* handle meta buckets before reading any data */
if (APR_BUCKET_IS_METADATA(b)) {
newb = APR_BUCKET_NEXT(b);
APR_BUCKET_REMOVE(b);
if (APR_BUCKET_IS_EOS(b)) {
intern->seen_eos = 1;
/* Hit end of stream, time for cleanup ... But wait!
* Perhaps we're not ready yet. We may have to loop one or
* two times again to finish our work. In that case, we
* just re-insert the EOS bucket to allow for an extra loop.
*
* PARSE_EXECUTE means, we've hit a directive just before the
* EOS, which is now waiting for execution.
*
* PARSE_DIRECTIVE_POSTTAIL means, we've hit a directive with
* no argument and no space between directive and end_seq
* just before the EOS. (consider <!--#printenv--> as last
* or only string within the stream). This state, however,
* just cleans up and turns itself to PARSE_EXECUTE, which
* will be passed through within the next (and actually
* last) round.
*/
if (PARSE_EXECUTE == intern->state ||
PARSE_DIRECTIVE_POSTTAIL == intern->state) {
APR_BUCKET_INSERT_BEFORE(newb, b);
}
else {
break; /* END OF STREAM */
}
}
else {
APR_BRIGADE_INSERT_TAIL(pass_bb, b);
if (APR_BUCKET_IS_FLUSH(b)) {
ctx->flush_now = 1;
}
b = newb;
continue;
}
}
/* enough is enough ... */
if (ctx->flush_now ||
intern->bytes_read > AP_MIN_BYTES_TO_WRITE) {
if (!APR_BRIGADE_EMPTY(pass_bb)) {
rv = ap_pass_brigade(f->next, pass_bb);
if (rv != APR_SUCCESS) {
apr_brigade_destroy(pass_bb);
return rv;
}
}
ctx->flush_now = 0;
intern->bytes_read = 0;
}
/* read the current bucket data */
len = 0;
if (!intern->seen_eos) {
if (intern->bytes_read > 0) {
rv = apr_bucket_read(b, &data, &len, APR_NONBLOCK_READ);
if (APR_STATUS_IS_EAGAIN(rv)) {
ctx->flush_now = 1;
continue;
}
}
if (!len || rv != APR_SUCCESS) {
rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
}
if (rv != APR_SUCCESS) {
apr_brigade_destroy(pass_bb);
return rv;
}
intern->bytes_read += len;
}
/* zero length bucket, fetch next one */
if (!len && !intern->seen_eos) {
b = APR_BUCKET_NEXT(b);
continue;
}
/*
* it's actually a data containing bucket, start/continue parsing
*/
switch (intern->state) {
/* no current tag; search for start sequence */
case PARSE_PRE_HEAD:
index = find_start_sequence(ctx, data, len);
if (index < len) {
apr_bucket_split(b, index);
}
newb = APR_BUCKET_NEXT(b);
if (ctx->flags & SSI_FLAG_PRINTING) {
APR_BUCKET_REMOVE(b);
APR_BRIGADE_INSERT_TAIL(pass_bb, b);
}
else {
apr_bucket_delete(b);
}
if (index < len) {
/* now delete the start_seq stuff from the remaining bucket */
if (PARSE_DIRECTIVE == intern->state) { /* full match */
apr_bucket_split(newb, intern->start_seq_pat->pattern_len);
ctx->flush_now = 1; /* pass pre-tag stuff */
}
b = APR_BUCKET_NEXT(newb);
apr_bucket_delete(newb);
}
else {
b = newb;
}
break;
/* we're currently looking for the end of the start sequence */
case PARSE_HEAD:
index = find_partial_start_sequence(ctx, data, len, &release);
/* check if we mismatched earlier and have to release some chars */
if (release && (ctx->flags & SSI_FLAG_PRINTING)) {
char *to_release = apr_pmemdup(ctx->pool, intern->start_seq, release);
newb = apr_bucket_pool_create(to_release, release, ctx->pool,
f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pass_bb, newb);
}
if (index) { /* any match */
/* now delete the start_seq stuff from the remaining bucket */
if (PARSE_DIRECTIVE == intern->state) { /* final match */
apr_bucket_split(b, index);
ctx->flush_now = 1; /* pass pre-tag stuff */
}
newb = APR_BUCKET_NEXT(b);
apr_bucket_delete(b);
b = newb;
}
break;
/* we're currently grabbing the directive name */
case PARSE_DIRECTIVE:
case PARSE_DIRECTIVE_POSTNAME:
case PARSE_DIRECTIVE_TAIL:
case PARSE_DIRECTIVE_POSTTAIL:
index = find_directive(ctx, data, len, &store, &store_len);
if (index) {
apr_bucket_split(b, index);
newb = APR_BUCKET_NEXT(b);
}
if (store) {
if (index) {
APR_BUCKET_REMOVE(b);
apr_bucket_setaside(b, r->pool);
APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
b = newb;
}
/* time for cleanup? */
if (store != &magic) {
apr_brigade_pflatten(intern->tmp_bb, store, store_len,
ctx->dpool);
apr_brigade_cleanup(intern->tmp_bb);
}
}
else if (index) {
apr_bucket_delete(b);
b = newb;
}
break;
/* skip WS and find out what comes next (arg or end_seq) */
case PARSE_PRE_ARG:
index = find_arg_or_tail(ctx, data, len);
if (index) { /* skipped whitespaces */
if (index < len) {
apr_bucket_split(b, index);
}
newb = APR_BUCKET_NEXT(b);
apr_bucket_delete(b);
b = newb;
}
break;
/* currently parsing name[=val] */
case PARSE_ARG:
case PARSE_ARG_NAME:
case PARSE_ARG_POSTNAME:
case PARSE_ARG_EQ:
case PARSE_ARG_PREVAL:
case PARSE_ARG_VAL:
case PARSE_ARG_VAL_ESC:
case PARSE_ARG_POSTVAL:
index = find_argument(ctx, data, len, &store, &store_len);
if (index) {
apr_bucket_split(b, index);
newb = APR_BUCKET_NEXT(b);
}
if (store) {
if (index) {
APR_BUCKET_REMOVE(b);
apr_bucket_setaside(b, r->pool);
APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
b = newb;
}
/* time for cleanup? */
if (store != &magic) {
apr_brigade_pflatten(intern->tmp_bb, store, store_len,
ctx->dpool);
apr_brigade_cleanup(intern->tmp_bb);
}
}
else if (index) {
apr_bucket_delete(b);
b = newb;
}
break;
/* try to match end_seq at current pos. */
case PARSE_TAIL:
case PARSE_TAIL_SEQ:
index = find_tail(ctx, data, len);
switch (intern->state) {
case PARSE_EXECUTE: /* full match */
apr_bucket_split(b, index);
newb = APR_BUCKET_NEXT(b);
apr_bucket_delete(b);
b = newb;
break;
case PARSE_ARG: /* no match */
/* PARSE_ARG must reparse at the beginning */
APR_BRIGADE_PREPEND(bb, intern->tmp_bb);
b = APR_BRIGADE_FIRST(bb);
break;
default: /* partial match */
newb = APR_BUCKET_NEXT(b);
APR_BUCKET_REMOVE(b);
apr_bucket_setaside(b, r->pool);
APR_BRIGADE_INSERT_TAIL(intern->tmp_bb, b);
b = newb;
break;
}
break;
/* now execute the parsed directive, cleanup the space and
* start again with PARSE_PRE_HEAD
*/
case PARSE_EXECUTE:
/* if there was an error, it was already logged; just stop here */
if (intern->error) {
if (ctx->flags & SSI_FLAG_PRINTING) {
SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
intern->error = 0;
}
}
else {
include_handler_fn_t *handle_func;
handle_func =
(include_handler_fn_t *)apr_hash_get(include_handlers, intern->directive,
intern->directive_len);
if (handle_func) {
DEBUG_INIT(ctx, f, pass_bb);
rv = handle_func(ctx, f, pass_bb);
if (rv != APR_SUCCESS) {
apr_brigade_destroy(pass_bb);
return rv;
}
}
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01371)
"unknown directive \"%s\" in parsed doc %s",
apr_pstrmemdup(r->pool, intern->directive,
intern->directive_len),
r->filename);
if (ctx->flags & SSI_FLAG_PRINTING) {
SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
}
}
}
/* cleanup */
apr_pool_clear(ctx->dpool);
apr_brigade_cleanup(intern->tmp_bb);
/* Oooof. Done here, start next round */
intern->state = PARSE_PRE_HEAD;
break;
} /* switch(ctx->state) */
} /* while (brigade) */
/* End of stream. Final cleanup */
if (intern->seen_eos) {
if (PARSE_HEAD == intern->state) {
if (ctx->flags & SSI_FLAG_PRINTING) {
char *to_release = apr_pmemdup(ctx->pool, intern->start_seq,
intern->parse_pos);
APR_BRIGADE_INSERT_TAIL(pass_bb,
apr_bucket_pool_create(to_release,
intern->parse_pos, ctx->pool,
f->c->bucket_alloc));
}
}
else if (PARSE_PRE_HEAD != intern->state) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01372)
"SSI directive was not properly finished at the end "
"of parsed document %s", r->filename);
if (ctx->flags & SSI_FLAG_PRINTING) {
SSI_CREATE_ERROR_BUCKET(ctx, f, pass_bb);
}
}
if (!(ctx->flags & SSI_FLAG_PRINTING)) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01373)
"missing closing endif directive in parsed document"
" %s", r->filename);
}
/* cleanup our temporary memory */
apr_brigade_destroy(intern->tmp_bb);
apr_pool_destroy(ctx->dpool);
/* don't forget to finally insert the EOS bucket */
APR_BRIGADE_INSERT_TAIL(pass_bb, b);
}
/* if something's left over, pass it along */
if (!APR_BRIGADE_EMPTY(pass_bb)) {
rv = ap_pass_brigade(f->next, pass_bb);
}
else {
rv = APR_SUCCESS;
apr_brigade_destroy(pass_bb);
}
return rv;
}