📄 mod_substitute.c
字号:
/* * Everything to be passed to the next filter goes in * here, our pass brigade. */ ctx->passbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); /* Create our temporary pool only once */ apr_pool_create(&(ctx->tpool), f->r->pool); apr_table_unset(f->r->headers_out, "Content-Length"); } /* * Shortcircuit processing */ if (APR_BRIGADE_EMPTY(bb)) return APR_SUCCESS; /* * Here's the concept: * Read in the data and look for newlines. Once we * find a full "line", add it to our working brigade. * If we've finished reading the brigade and we have * any left over data (not a "full" line), store that * for the next pass. * * Note: anything stored in ctx->linebb for sure does not have * a newline char, so we don't concat that bb with the * new bb, since we would spending time searching for the newline * in data we know it doesn't exist. So instead, we simply scan * our current bb and, if we see a newline, prepend ctx->linebb * to the front of it. This makes the code much less straight- * forward (otherwise we could APR_BRIGADE_CONCAT(ctx->linebb, bb) * and just scan for newlines and not bother with needing to know * when ctx->linebb needs to be reset) but also faster. We'll take * the speed. * * Note: apr_brigade_split_line would be nice here, but we * really can't use it since we need more control and we want * to re-use already read bucket data. * * See mod_include if still confused :) */ while ((b = APR_BRIGADE_FIRST(bb)) && (b != APR_BRIGADE_SENTINEL(bb))) { if (APR_BUCKET_IS_EOS(b)) { /* * if we see the EOS, then we need to pass along everything we * have. But if the ctx->linebb isn't empty, then we need to add * that to the end of what we'll be passing. */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { rv = apr_brigade_pflatten(ctx->linebb, &bflat, &fbytes, ctx->tpool); tmp_b = apr_bucket_transient_create(bflat, fbytes, f->r->connection->bucket_alloc); do_pattmatch(f, tmp_b, ctx->pattbb, ctx->tpool); APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb); } apr_brigade_cleanup(ctx->linebb); APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); } /* * No need to handle FLUSH buckets separately as we call * ap_pass_brigade anyway at the end of the loop. */ else if (APR_BUCKET_IS_METADATA(b)) { APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); } else { /* * We have actual "data" so read in as much as we can and start * scanning and splitting from our read buffer */ rv = apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ); if (rv != APR_SUCCESS || bytes == 0) { APR_BUCKET_REMOVE(b); } else { int num = 0; while (bytes > 0) { nl = memchr(buff, APR_ASCII_LF, bytes); if (nl) { len = (apr_size_t) (nl - buff) + 1; /* split *after* the newline */ apr_bucket_split(b, len); /* * We've likely read more data, so bypass rereading * bucket data and continue scanning through this * buffer */ bytes -= len; buff += len; /* * we need b to be updated for future potential * splitting */ tmp_b = APR_BUCKET_NEXT(b); APR_BUCKET_REMOVE(b); /* * Hey, we found a newline! Don't forget the old * stuff that needs to be added to the front. So we * add the split bucket to the end, flatten the whole * bb, morph the whole shebang into a bucket which is * then added to the tail of the newline bb. */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { APR_BRIGADE_INSERT_TAIL(ctx->linebb, b); rv = apr_brigade_pflatten(ctx->linebb, &bflat, &fbytes, ctx->tpool); b = apr_bucket_transient_create(bflat, fbytes, f->r->connection->bucket_alloc); apr_brigade_cleanup(ctx->linebb); } do_pattmatch(f, b, ctx->pattbb, ctx->tpool); /* * Count how many buckets we have in ctx->passbb * so far. Yes, this is correct we count ctx->passbb * and not ctx->pattbb as we do not reset num on every * iteration. */ for (b = APR_BRIGADE_FIRST(ctx->pattbb); b != APR_BRIGADE_SENTINEL(ctx->pattbb); b = APR_BUCKET_NEXT(b)) { num++; } APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb); /* * If the number of buckets in ctx->passbb reaches an * "insane" level, we consume much memory for all the * buckets as such. So lets flush them down the chain * in this case and thus clear ctx->passbb. This frees * the buckets memory for further processing. * Usually this condition should not become true, but * it is a safety measure for edge cases. */ if (num > AP_MAX_BUCKETS) { b = apr_bucket_flush_create( f->r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); rv = ap_pass_brigade(f->next, ctx->passbb); apr_brigade_cleanup(ctx->passbb); num = 0; apr_pool_clear(ctx->tpool); if (rv != APR_SUCCESS) return rv; } b = tmp_b; } else { /* * no newline in whatever is left of this buffer so * tuck data away and get next bucket */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->linebb, b); bytes = 0; } } } } if (!APR_BRIGADE_EMPTY(ctx->passbb)) { rv = ap_pass_brigade(f->next, ctx->passbb); apr_brigade_cleanup(ctx->passbb); if (rv != APR_SUCCESS) { apr_pool_clear(ctx->tpool); return rv; } } apr_pool_clear(ctx->tpool); } /* Anything left we want to save/setaside for the next go-around */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { /* * Provide ap_save_brigade with an existing empty brigade * (ctx->linesbb) to avoid creating a new one. */ ap_save_brigade(f, &(ctx->linesbb), &(ctx->linebb), f->r->pool); tmp_bb = ctx->linebb; ctx->linebb = ctx->linesbb; ctx->linesbb = tmp_bb; } return APR_SUCCESS;}static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line){ char *from = NULL; char *to = NULL; char *flags = NULL; char *ourline; char delim; subst_pattern_t *nscript; int is_pattern = 0; int ignore_case = 0; int flatten = 1; ap_regex_t *r = NULL; if (apr_tolower(*line) != 's') { return "Bad Substitute format, must be an s/// pattern"; } ourline = apr_pstrdup(cmd->pool, line); delim = *++ourline; if (delim) from = ++ourline; if (from) { if (*ourline != delim) { while (*++ourline && *ourline != delim); } if (*ourline) { *ourline = '\0'; to = ++ourline; } } if (to) { if (*ourline != delim) { while (*++ourline && *ourline != delim); } if (*ourline) { *ourline = '\0'; flags = ++ourline; } } if (!delim || !from || !*from || !to) { return "Bad Substitute format, must be a complete s/// pattern"; } if (flags) { while (*flags) { delim = apr_tolower(*flags); /* re-use */ if (delim == 'i') ignore_case = 1; else if (delim == 'n') is_pattern = 1; else if (delim == 'f') flatten = 1; else if (delim == 'q') flatten = 0; else return "Bad Substitute flag, only s///[infq] are supported"; flags++; } } /* first see if we can compile the regex */ if (!is_pattern) { r = ap_pregcomp(cmd->pool, from, AP_REG_EXTENDED | (ignore_case ? AP_REG_ICASE : 0)); if (!r) return "Substitute could not compile regex"; } nscript = apr_array_push(((subst_dir_conf *) cfg)->patterns); /* init the new entries */ nscript->pattern = NULL; nscript->regexp = NULL; nscript->replacement = NULL; nscript->patlen = 0; if (is_pattern) { nscript->patlen = strlen(from); nscript->pattern = apr_strmatch_precompile(cmd->pool, from, !ignore_case); } else { nscript->regexp = r; } nscript->replacement = to; nscript->replen = strlen(to); nscript->flatten = flatten; return NULL;}#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTHstatic void register_hooks(apr_pool_t *pool){ ap_register_output_filter(substitute_filter_name, substitute_filter, NULL, AP_FTYPE_RESOURCE);}static const command_rec substitute_cmds[] = { AP_INIT_TAKE1("Substitute", set_pattern, NULL, OR_ALL, "Pattern to filter the response content (s/foo/bar/[inf])"), {NULL}};module AP_MODULE_DECLARE_DATA substitute_module = { STANDARD20_MODULE_STUFF, create_substitute_dcfg, /* dir config creater */ merge_substitute_dcfg, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ substitute_cmds, /* command table */ register_hooks /* register hooks */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -