Apache HTTPD
mod_sed.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
3 * Use is subject to license terms.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0.
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 * or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "httpd.h"
19#include "http_config.h"
20#include "http_log.h"
21#include "apr_strings.h"
22#include "apr_general.h"
23#include "util_filter.h"
24#include "apr_buckets.h"
25#include "http_request.h"
26#include "libsed.h"
27
28static const char *sed_filter_name = "Sed";
29#define MODSED_OUTBUF_SIZE 8000
30#define MAX_TRANSIENT_BUCKETS 50
31
37
43
44/* Context for filter invocation for single HTTP request */
58
59module AP_MODULE_DECLARE_DATA sed_module;
60
61/* This function will be call back from libsed functions if there is any error
62 * happened during execution of sed scripts
63 */
64static apr_status_t log_sed_errf(void *data, const char *error)
65{
67 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02998) "%s", error);
68 return APR_SUCCESS;
69}
70
71/* This function will be call back from libsed functions if there is any
72 * compilation error.
73 */
74static apr_status_t sed_compile_errf(void *data, const char *error)
75{
78 return APR_SUCCESS;
79}
80
81/* clear the temporary pool (used for transient buckets)
82 */
84{
85 apr_pool_clear(ctx->tpool);
86 ctx->outbuf = NULL;
87 ctx->curoutbuf = NULL;
88 ctx->numbuckets = 0;
89}
90
91/* alloc_outbuf
92 * allocate output buffer
93 */
95{
96 ctx->outbuf = apr_palloc(ctx->tpool, ctx->bufsize + 1);
97 ctx->curoutbuf = ctx->outbuf;
98}
99
100/* append_bucket
101 * Allocate a new bucket from buf and sz and append to ctx->bb
102 */
104{
106 apr_bucket *b;
107 if (ctx->tpool == ctx->r->pool) {
108 /* We are not using transient bucket */
109 b = apr_bucket_pool_create(buf, sz, ctx->r->pool,
110 ctx->r->connection->bucket_alloc);
112 }
113 else {
114 /* We are using transient bucket */
116 ctx->r->connection->bucket_alloc);
118 ctx->numbuckets++;
119 if (ctx->numbuckets >= MAX_TRANSIENT_BUCKETS) {
120 b = apr_bucket_flush_create(ctx->r->connection->bucket_alloc);
122 status = ap_pass_brigade(ctx->f->next, ctx->bb);
125 }
126 }
127 return status;
128}
129
130/*
131 * flush_output_buffer
132 * Flush the output data (stored in ctx->outbuf)
133 */
135{
136 apr_size_t size = ctx->curoutbuf - ctx->outbuf;
137 char *out;
139 if ((ctx->outbuf == NULL) || (size <=0))
140 return status;
141 out = apr_pmemdup(ctx->tpool, ctx->outbuf, size);
143 ctx->curoutbuf = ctx->outbuf;
144 return status;
145}
146
147/* This is a call back function. When libsed wants to generate the output,
148 * this function will be invoked.
149 */
151{
152 /* dummy is basically filter context. Context is passed during invocation
153 * of sed_eval_buffer
154 */
158 if (ctx->outbuf == NULL) {
160 }
161 remainbytes = ctx->bufsize - (ctx->curoutbuf - ctx->outbuf);
162 if (sz >= remainbytes) {
163 if (remainbytes > 0) {
164 memcpy(ctx->curoutbuf, buf, remainbytes);
165 buf += remainbytes;
166 sz -= remainbytes;
167 ctx->curoutbuf += remainbytes;
168 }
169 /* buffer is now full */
170 status = append_bucket(ctx, ctx->outbuf, ctx->bufsize);
171 if (status == APR_SUCCESS) {
172 /* if size is bigger than the allocated buffer directly add to output
173 * brigade */
174 if (sz >= ctx->bufsize) {
175 char* newbuf = apr_pmemdup(ctx->tpool, buf, sz);
177 if (status == APR_SUCCESS) {
178 /* old buffer is now used so allocate new buffer */
180 }
181 else {
183 }
184 }
185 else {
186 /* old buffer is now used so allocate new buffer */
188 memcpy(ctx->curoutbuf, buf, sz);
189 ctx->curoutbuf += sz;
190 }
191 }
192 else {
194 }
195 }
196 else {
197 memcpy(ctx->curoutbuf, buf, sz);
198 ctx->curoutbuf += sz;
199 }
200 return status;
201}
202
203/* Compile a sed expression. Compiled context is saved in sed_cfg->sed_cmds.
204 * Memory required for compilation context is allocated from cmd->pool.
205 */
207 cmd_parms *cmd,
208 const char *expr)
209{
211
212 if (!sed_cfg->sed_cmds) {
213 sed_commands_t *sed_cmds;
214 sed_cmds = apr_pcalloc(cmd->pool, sizeof(sed_commands_t));
216 cmd->pool);
217 if (status != APR_SUCCESS) {
218 sed_destroy_commands(sed_cmds);
219 return status;
220 }
221 sed_cfg->sed_cmds = sed_cmds;
222 }
223 status = sed_compile_string(sed_cfg->sed_cmds, expr);
224 if (status != APR_SUCCESS) {
225 sed_destroy_commands(sed_cfg->sed_cmds);
226 sed_cfg->sed_cmds = NULL;
227 }
228 return status;
229}
230
231/* sed eval cleanup function */
233{
234 sed_eval_t *eval = (sed_eval_t *) data;
235 sed_destroy_eval(eval);
236 return APR_SUCCESS;
237}
238
239/* Initialize sed filter context. If successful then context is set in f->ctx
240 */
242{
245 request_rec *r = f->r;
246 /* Create the context. Call sed_init_eval. libsed will generated
247 * output by calling sed_write_output and generates any error by
248 * invoking log_sed_errf.
249 */
250 ctx = apr_pcalloc(r->pool, sizeof(sed_filter_ctxt));
251 ctx->r = r;
252 ctx->bb = NULL;
253 ctx->numbuckets = 0;
254 ctx->f = f;
255 status = sed_init_eval(&ctx->eval, sed_cfg->sed_cmds, log_sed_errf,
257 if (status != APR_SUCCESS) {
258 return status;
259 }
262 ctx->bufsize = MODSED_OUTBUF_SIZE;
263 if (usetpool) {
264 apr_pool_create(&(ctx->tpool), r->pool);
265 apr_pool_tag(ctx->tpool, "sed_tpool");
266 }
267 else {
268 ctx->tpool = r->pool;
269 }
271 f->ctx = ctx;
272 return APR_SUCCESS;
273}
274
275/* Entry function for Sed output filter */
278{
279 apr_bucket *b;
281 sed_config *cfg = ap_get_module_config(f->r->per_dir_config,
282 &sed_module);
283 sed_filter_ctxt *ctx = f->ctx;
285
286 if ((sed_cfg == NULL) || (sed_cfg->sed_cmds == NULL)) {
287 /* No sed expressions */
289 return ap_pass_brigade(f->next, bb);
290 }
291
292 if (ctx == NULL) {
293
295 /* no need to run sed filter for Head requests */
297 return ap_pass_brigade(f->next, bb);
298 }
299
301 if (status != APR_SUCCESS)
302 return status;
303 ctx = f->ctx;
304 apr_table_unset(f->r->headers_out, "Content-Length");
305
306 ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
307 }
308
309 /* Here is the main logic. Iterate through all the buckets, read the
310 * content of the bucket, call sed_eval_buffer on the data.
311 * sed_eval_buffer will read the data line by line, run filters on each
312 * line. sed_eval_buffer will generates the output by calling
313 * sed_write_output which will add the output to ctx->bb. At the end of
314 * the loop, ctx->bb is passed to the next filter in chain. At the end of
315 * the data, if new line is not found then sed_eval_buffer will store the
316 * data in its own buffer.
317 *
318 * Once eos bucket is found then sed_finalize_eval will flush the rest of
319 * the data. If there is no new line in last line of data, new line is
320 * appended (that is a solaris sed behavior). libsed's internal memory for
321 * evaluation is allocated on request's pool so it will be cleared once
322 * request is over.
323 *
324 * If flush bucket is found then append the flush bucket to ctx->bb
325 * and pass it to next filter. There may be some data which will still be
326 * in sed's internal buffer which can't be flushed until new line
327 * character is arrived.
328 */
329 while (!APR_BRIGADE_EMPTY(bb)) {
330 b = APR_BRIGADE_FIRST(bb);
331 if (APR_BUCKET_IS_EOS(b)) {
332 /* Now clean up the internal sed buffer */
333 sed_finalize_eval(&ctx->eval, ctx);
335 if (status != APR_SUCCESS) {
336 break;
337 }
338 /* Move the eos bucket to ctx->bb brigade */
341 }
342 else if (APR_BUCKET_IS_FLUSH(b)) {
344 if (status != APR_SUCCESS) {
345 break;
346 }
347 /* Move the flush bucket to ctx->bb brigade */
350 }
351 else {
353 const char *buf = NULL;
354 apr_size_t bytes = 0;
355
357 if (status == APR_SUCCESS) {
358 status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
359 }
360 if (status != APR_SUCCESS) {
361 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10394) "error evaluating sed on output");
362 break;
363 }
364 }
366 }
367 }
368 if (status == APR_SUCCESS) {
370 }
371 if (!APR_BRIGADE_EMPTY(ctx->bb)) {
372 if (status == APR_SUCCESS) {
373 status = ap_pass_brigade(f->next, ctx->bb);
374 }
376 }
378 return status;
379}
380
381/* Entry function for Sed input filter */
387{
388 sed_config *cfg = ap_get_module_config(f->r->per_dir_config,
389 &sed_module);
390 sed_filter_ctxt *ctx = f->ctx;
392 apr_bucket_brigade *bbinp;
394
395 if (mode != AP_MODE_READBYTES) {
396 return ap_get_brigade(f->next, bb, mode, block, readbytes);
397 }
398
399 if ((sed_cfg == NULL) || (sed_cfg->sed_cmds == NULL)) {
400 /* No sed expression */
401 return ap_get_brigade(f->next, bb, mode, block, readbytes);
402 }
403
404 if (!ctx) {
405 if (!ap_is_initial_req(f->r)) {
407 /* XXX : Should we filter the sub requests too */
408 return ap_get_brigade(f->next, bb, mode, block, readbytes);
409 }
411 if (status != APR_SUCCESS)
412 return status;
413 ctx = f->ctx;
414 ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
415 ctx->bbinp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
416 }
417
418 bbinp = ctx->bbinp;
419
420 /* Here is the logic :
421 * Read the readbytes data from next level fiter into bbinp. Loop through
422 * the buckets in bbinp and read the data from buckets and invoke
423 * sed_eval_buffer on the data. libsed will generate its output using
424 * sed_write_output which will add data in ctx->bb. Do it until it have
425 * at least one bucket in ctx->bb. At the end of data eos bucket
426 * should be there.
427 *
428 * Once eos bucket is seen, then invoke sed_finalize_eval to clear the
429 * output. If the last byte of data is not a new line character then sed
430 * will add a new line to the data that is default sed behaviour. Note
431 * that using this filter with POST data, caller may not expect this
432 * behaviour.
433 *
434 * If next level fiter generate the flush bucket, we can't do much about
435 * it. If we want to return the flush bucket in brigade bb (to the caller)
436 * the question is where to add it?
437 */
438 while (APR_BRIGADE_EMPTY(ctx->bb)) {
439 apr_bucket *b;
440
441 /* read the bytes from next level filter */
442 apr_brigade_cleanup(bbinp);
443 status = ap_get_brigade(f->next, bbinp, mode, block, readbytes);
444 if (status != APR_SUCCESS) {
445 return status;
446 }
447 for (b = APR_BRIGADE_FIRST(bbinp); b != APR_BRIGADE_SENTINEL(bbinp);
448 b = APR_BUCKET_NEXT(b)) {
449 const char *buf = NULL;
451
452 if (APR_BUCKET_IS_EOS(b)) {
453 /* eos bucket. Clear the internal sed buffers */
454 sed_finalize_eval(&ctx->eval, ctx);
458 break;
459 }
460 else if (APR_BUCKET_IS_FLUSH(b)) {
461 /* What should we do with flush bucket */
462 continue;
463 }
465 == APR_SUCCESS) {
466 status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
467 if (status != APR_SUCCESS) {
468 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, f->r, APLOGNO(10395) "error evaluating sed on input");
469 return status;
470 }
472 }
473 }
474 }
475
476 if (!APR_BRIGADE_EMPTY(ctx->bb)) {
477 apr_bucket *b = NULL;
478
480 APR_BRIGADE_CONCAT(bb, ctx->bb);
481 }
482 else {
483 APR_BRIGADE_CONCAT(bb, ctx->bb);
484 apr_brigade_split_ex(bb, b, ctx->bb);
485 }
486 }
487 return APR_SUCCESS;
488}
489
490static const char *sed_add_expr(cmd_parms *cmd, void *cfg, const char *arg)
491{
492 int offset = (int) (long) cmd->info;
494 (sed_expr_config *) (((char *) cfg) + offset);
496 return apr_psprintf(cmd->temp_pool,
497 "Failed to compile sed expression. %s",
498 sed_cfg->last_error);
499 }
500 return NULL;
501}
502
503static void *create_sed_dir_config(apr_pool_t *p, char *s)
504{
505 sed_config *cfg = apr_pcalloc(p, sizeof(sed_config));
506 return cfg;
507}
508
509static const command_rec sed_filter_cmds[] = {
510 AP_INIT_TAKE1("OutputSed", sed_add_expr,
511 (void *) APR_OFFSETOF(sed_config, output),
513 "Sed regular expression for Response"),
514 AP_INIT_TAKE1("InputSed", sed_add_expr,
515 (void *) APR_OFFSETOF(sed_config, input),
517 "Sed regular expression for Request"),
518 {NULL}
519};
520
528
531 create_sed_dir_config, /* dir config creater */
532 NULL, /* dir merger --- default is to override */
533 NULL, /* server config */
534 NULL, /* merge server config */
535 sed_filter_cmds, /* command table */
536 register_hooks /* register hooks */
537};
APR-UTIL Buckets/Bucket Brigades.
APR Miscellaneous library routines.
APR Strings library.
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
#define AP_DECLARE_MODULE(foo)
request_rec * r
void ap_remove_input_filter(ap_filter_t *f)
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
ap_filter_rec_t * ap_register_output_filter(const char *name, ap_out_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
apr_status_t ap_filter_rec_t * ap_register_input_filter(const char *name, ap_in_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
void ap_remove_output_filter(ap_filter_t *f)
@ AP_FTYPE_RESOURCE
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
const unsigned char * buf
Definition util_md5.h:50
int ap_is_initial_req(request_rec *r)
Definition request.c:2567
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
#define APR_INCOMPLETE
Definition apr_errno.h:452
apr_file_t * f
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BUCKET_REMOVE(e)
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define APR_BUCKET_NEXT(e)
apr_read_type_e
Definition apr_buckets.h:57
#define APR_BRIGADE_CONCAT(a, b)
#define APR_BRIGADE_EMPTY(b)
#define APR_BRIGADE_SENTINEL(b)
#define apr_bucket_delete(e)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
#define apr_bucket_read(e, str, len, block)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
#define ACCESS_CONF
#define STANDARD20_MODULE_STUFF
apr_size_t size
const char * input
Definition apr_cstr.h:93
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
apr_seek_where_t apr_off_t * offset
void * data
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const void apr_size_t bytes
Definition apr_random.h:91
const char * s
Definition apr_strings.h:95
apr_cmdtype_e cmd
int int status
Apache Configuration.
Apache Logging library.
Apache Request library.
HTTP Daemon routines.
apr_status_t sed_init_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data, sed_write_fn_t *writefn, apr_pool_t *p)
Definition sed1.c:294
void sed_destroy_eval(sed_eval_t *eval)
Definition sed1.c:367
apr_status_t sed_finalize_eval(sed_eval_t *eval, void *f)
Definition sed1.c:480
apr_status_t sed_init_commands(sed_commands_t *commands, sed_err_fn_t *errfn, void *data, apr_pool_t *p)
Definition sed0.c:59
apr_status_t sed_compile_string(sed_commands_t *commands, const char *s)
Definition sed0.c:99
void sed_destroy_commands(sed_commands_t *commands)
Definition sed0.c:92
apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz, void *fout)
Definition sed1.c:406
apr_pool_t * p
Definition md_event.c:32
static apr_file_t * out
Definition mod_info.c:85
static void alloc_outbuf(sed_filter_ctxt *ctx)
Definition mod_sed.c:94
static apr_status_t sed_request_filter(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
Definition mod_sed.c:382
static apr_status_t append_bucket(sed_filter_ctxt *ctx, char *buf, apr_size_t sz)
Definition mod_sed.c:103
static apr_status_t sed_write_output(void *dummy, char *buf, apr_size_t sz)
Definition mod_sed.c:150
static apr_status_t compile_sed_expr(sed_expr_config *sed_cfg, cmd_parms *cmd, const char *expr)
Definition mod_sed.c:206
static const char * sed_add_expr(cmd_parms *cmd, void *cfg, const char *arg)
Definition mod_sed.c:490
#define MODSED_OUTBUF_SIZE
Definition mod_sed.c:29
static void register_hooks(apr_pool_t *p)
Definition mod_sed.c:521
static apr_status_t sed_response_filter(ap_filter_t *f, apr_bucket_brigade *bb)
Definition mod_sed.c:276
#define MAX_TRANSIENT_BUCKETS
Definition mod_sed.c:30
static apr_status_t flush_output_buffer(sed_filter_ctxt *ctx)
Definition mod_sed.c:134
static void clear_ctxpool(sed_filter_ctxt *ctx)
Definition mod_sed.c:83
static apr_status_t sed_compile_errf(void *data, const char *error)
Definition mod_sed.c:74
static const char * sed_filter_name
Definition mod_sed.c:28
static apr_status_t sed_eval_cleanup(void *data)
Definition mod_sed.c:232
static apr_status_t log_sed_errf(void *data, const char *error)
Definition mod_sed.c:64
static apr_status_t init_context(ap_filter_t *f, sed_expr_config *sed_cfg, int usetpool)
Definition mod_sed.c:241
static void * create_sed_dir_config(apr_pool_t *p, char *s)
Definition mod_sed.c:503
static const command_rec sed_filter_cmds[]
Definition mod_sed.c:509
return NULL
Definition mod_so.c:359
The representation of a filter chain.
apr_pool_t * pool
A structure that represents the current request.
Definition httpd.h:845
apr_pool_t * pool
Definition httpd.h:847
apr_pool_t * pool
Definition libsed.h:103
sed_expr_config output
Definition mod_sed.c:40
sed_expr_config input
Definition mod_sed.c:41
sed_commands_t * sed_cmds
Definition mod_sed.c:34
const char * last_error
Definition mod_sed.c:35
ap_filter_t * f
Definition mod_sed.c:48
apr_bucket_brigade * bbinp
Definition mod_sed.c:51
char * outbuf
Definition mod_sed.c:52
sed_eval_t eval
Definition mod_sed.c:47
apr_size_t bufsize
Definition mod_sed.c:54
apr_bucket_brigade * bb
Definition mod_sed.c:50
apr_pool_t * tpool
Definition mod_sed.c:55
char * curoutbuf
Definition mod_sed.c:53
request_rec * r
Definition mod_sed.c:49
Apache filter library.
ap_input_mode_t
input filtering modes
Definition util_filter.h:41
@ AP_MODE_READBYTES
Definition util_filter.h:43
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray