Please note that this function is copied directly from some private code. The ((template_context *)f->ctx) variable is a standard filter context variable that is typecast to what I know it is, which contains an apr_bucket, etc (and my variable that includes the output of the "include"). The structure (so you can know what type the variables are) will be at the bottom of this document. The function :
static apr_status_t mod_template_include_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
for (((template_context *)f->ctx)->include_bucket = APR_BRIGADE_FIRST(bb);
((template_context *)f->ctx)->include_bucket != APR_BRIGADE_SENTINEL(bb);
((template_context *)f->ctx)->include_bucket = APR_BUCKET_NEXT(((template_context *)f->ctx)->include_bucket)) { if (!APR_BUCKET_IS_EOS(((template_context *)f->ctx)->include_bucket)) {
if (apr_bucket_read(((template_context *)f->ctx)->include_bucket,&(((template_context *)f->ctx)->include_data),&(((template_context *)f->ctx)->include_length),APR_BLOCK_READ) == APR_SUCCESS) {
if (((template_context *)f->ctx)->include_data_real == NULL) {
((template_context *)f->ctx)->include_data_real = apr_palloc(f->r->pool,((template_context *)f->ctx)->include_length+1);
strncpy(((template_context *)f->ctx)->include_data_real,((template_context *)f->ctx)->include_data,((template_context *)f->ctx)->include_length);
((template_context *)f->ctx)->include_data_real[((template_context *)f->ctx)->include_length] = '\0';
} else {
((template_context *)f->ctx)->include_data_tmp = ((template_context *)f->ctx)->include_data_real;
((template_context *)f->ctx)->include_data_real = apr_palloc(f->r->pool,((template_context *)f->ctx)->include_length+strlen(((template_context *)f->ctx)->include_data_tmp) + 1);
strcpy(((template_context *)f->ctx)->include_data_real,((template_context *)f->ctx)->include_data_tmp);
strncat(((template_context *)f->ctx)->include_data_real,((template_context *)f->ctx)->include_data,((template_context *)f->ctx)->include_length);
((template_context *)f->ctx)->include_data_real[((template_context *)f->ctx)->include_length+strlen(((template_context *)f->ctx)->include_data_tmp)] = '\0';
}
APR_BUCKET_REMOVE(((template_context *)f->ctx)->include_bucket);
}
}
};
apr_brigade_destroy(bb);
return APR_SUCCESS;
//return apr_brigade_create(f->r->pool,f->c->bucket_alloc);
}
The next step is to actually call a sub request, assign the filter to it, and run it. First, I set up the request doing this :
((template_context *)f->ctx)->include_filter_rec = apr_palloc(f->r->pool,sizeof(ap_filter_rec_t));
memset(((template_context *)f->ctx)->include_filter_rec,0,sizeof(ap_filter_rec_t));
((template_context *)f->ctx)->include_filter_rec->name = "TEMPLATE-INCLUDE-WRAPPER";
((template_context *)f->ctx)->include_filter_rec->filter_func.out_func = &mod_template_include_output_filter;
((template_context *)f->ctx)->include_filter_rec->next = NULL;
((template_context *)f->ctx)->include_filter_rec->ftype = AP_FTYPE_RESOURCE;
((template_context *)f->ctx)->include_filter = apr_palloc(f->r->pool,sizeof(ap_filter_t));
((template_context *)f->ctx)->include_filter->frec = ((template_context *)f->ctx)->include_filter_rec;
((template_context *)f->ctx)->include_filter->ctx = (template_context *)f->ctx;
((template_context *)f->ctx)->include_filter->next = NULL;
((template_context *)f->ctx)->include_filter->r = f->r;
((template_context *)f->ctx)->include_filter->c = f->r->connection;
Next, I run the request using :
/* now, run the subrequest */
((template_context *)f->ctx)->include_data = NULL;
((template_context *)f->ctx)->include_data_real = NULL;
((template_context *)f->ctx)->include_r = ap_sub_req_lookup_uri(uri,f->r,((template_context *)f->ctx)->include_filter);
if ((((template_context *)f->ctx)->include_r != NULL) && (((template_context *)f->ctx)->include_r->status == HTTP_OK)) {
((template_context *)f->ctx)->include_int = ap_run_sub_req(((template_context *)f->ctx)->include_r);
}
if (((template_context *)f->ctx)->include_r != NULL) {
ap_destroy_sub_req(((template_context *)f->ctx)->include_r);
}
Now, just to be sure, check the results - if there was an error, you may not want that in the document. For example :
/* did we have an error of sorts? */
if (((template_context *)f->ctx)->include_data_real == NULL) {
return NULL;
}
Then you can create a new bucket (with a filter) or just respond with the handler using the fresh content :
new_bucket = apr_bucket_pool_create(((template_context *)f->ctx)->include_data_real,strlen(((template_context *)f->ctx)->include_data_real),f->r->pool,f->c->bucket_alloc);
Now, for those that have waited patiently, the following is the structure definition of my context :
typedef struct template_context {
char *title;
char *head;
const char *include_data;
char *include_data_tmp;
char *include_data_real;
apr_bucket *include_bucket;
// apr_off_t include_length;
apr_ssize_t include_length;
ap_filter_rec_t *include_filter_rec;
ap_filter_t *include_filter;
int include_int;
request_rec *include_r;
const char *header;
const char *trailer;
const char *bucket_data;
apr_file_t *f_header;
apr_file_t *f_trailer;
apr_file_t *file_tmp;
apr_finfo_t sb;
char *buffer;
char *char_tmp;
int get_tag_length;
int int_tmp;
int content_length;
char *tag_open;
char *tag_close;
int flags;
struct content_type_list *types;
const apr_strmatch_pattern *strmatch;
const char *match;
apr_bucket_brigade *brigade;
apr_bucket *trailer_bucket;
apr_size_t bucket_length;
apr_bucket *current_bucket;
apr_bucket *tmp_bucket;
apr_bucket *new_bucket;
apr_time_t time_tmp;
template_conf *config;
} template_context;
Yes, it will be a little obvious that my template wrapping module is fairly complex, but that is okay - it does a great deal more than just wrapping. It adjusts modified dates/times based on the template, it uses includes, finding additional components (dynamic menu building, etc). And all done on the fly. It was a very fun project to build!
No comments:
Post a Comment