This section describes an example NSAPI filter named example-replace, which examines outgoing data and substitutes one string for another. It shows how you can create a filter that intercepts and modifies outgoing data.
To load the filter, add the following line in the Init section of the obj.conf file:
Init fn="load-modules" shlib="<path>/replace.
ext" NativeThread="no"
|
To execute the filter during the request-response process for some object, add the following line to that object in the obj.conf file:
Output fn="insert-filter" type="text/*" filter="example-replace" from="iPlanet" to="Sun ONE" |
The source code for this example is in the replace.c file in the plugins/nsapi/examples subdirectory of the server root directory.
#ifdef XP_WIN32
#define NSAPI_PUBLIC __declspec(dllexport)
#else /* !XP_WIN32 */
#define NSAPI_PUBLIC
#endif /* !XP_WIN32 */
/*
* nsapi.h declares the NSAPI interface.
*/
#include "nsapi.h"
/* -------------------ExampleReplaceData------------------------- */
/*
* ExampleReplaceData will be used to store information between
* filter method invocations. Each instance of the example-replace
* filter will have its own ExampleReplaceData object.
*/
typedef struct ExampleReplaceData ExampleReplaceData;
struct ExampleReplaceData {
char *from; /* the string to replace */
int fromlen; /* length of "from" */
char *to; /* the string to replace "from" with */
int tolen; /* length of "to" */
int matched; /* number of "from" chars matched */
};
/* -------------- example_replace_insert ------------------------ */
/*
* example_replace_insert implements the example-replace filter’s
* insert method. The insert filter method is called before the
* server adds the filter to the filter stack.
*/
#ifdef __cplusplus
extern "C"
#endif
int example_replace_insert(FilterLayer *layer, pblock *pb)
{
const char *from;
const char *to;
ExampleReplaceData *data;
/*
* Look for the string to replace, "from", and the string to
* replace it with, "to". Both values are required.
*/
from = pblock_findval("from", pb);
to = pblock_findval("to", pb);
if (from == NULL || to == NULL || strlen(from) < 1) {
log_error(LOG_MISCONFIG, "example-replace-insert",
layer->context->sn, layer->context->rq,
"missing parameter (need from and to)");
return REQ_ABORTED; /* error preparing for insertion */
}
/*
* Allocate an ExampleReplaceData object that will store
* configuration and state information.
*/
data = (ExampleReplaceData *)MALLOC(sizeof(ExampleReplaceData));
if (data == NULL)
return REQ_ABORTED; /* error preparing for insertion */
/* Initialize the ExampleReplaceData */
data->from = STRDUP(from);
data->fromlen = strlen(from);
data->to = STRDUP(to);
data->tolen = strlen(to);
data->matched = 0;
/* Check for out of memory errors */
if (data->from == NULL || data->to == NULL) {
FREE(data->from);
FREE(data->to);
FREE(data);
return REQ_ABORTED; /* error preparing for insertion */
}
/*
* Store a pointer to the ExampleReplaceData object in the
* FilterLayer. This information can then be accessed from other
* filter methods.
*/
layer->context->data = data;
/* Remove the Content-length: header if we might change the
* body length */
if (data->tolen != data->fromlen) {
pb_param *pp;
pp = pblock_remove("content-length", layer->context->rq->srvhdrs);
if (pp)
param_free(pp);
}
return REQ_PROCEED; /* insert filter */
}
/* -------------- example_replace_remove ------------------------ */
/*
* example_replace_remove implements the example-replace filter’s
* remove method. The remove filter method is called before the
* server removes the filter from the filter stack.
*/
#ifdef __cplusplus
extern "C"
#endif
void example_replace_remove(FilterLayer *layer)
{
ExampleReplaceData *data;
/* Access the ExampleReplaceData we allocated in
example_replace_insert */
data = (ExampleReplaceData *)layer->context->data;
/* Send any partial "from" match */
if (data->matched > 0)
net_write(layer->lower, data->from, data->matched);
/* Destroy the ExampleReplaceData object */
FREE(data->from);
FREE(data->to);
FREE(data);
}
/* -------------- example_replace_write ------------------------- */
/*
* example_replace_write implements the example-replace filter’s
* write method. The write filter method is called when there is data
* to be sent to the client.
*/
#ifdef __cplusplus
extern "C"
#endif
int example_replace_write(FilterLayer *layer, const void *buf, int amount)
{
ExampleReplaceData *data;
const char *buffer;
int consumed;
int i;
int unsent;
int rv;
/* Access the ExampleReplaceData we allocated in
example_replace_insert */
data = (ExampleReplaceData *)layer->context->data;
/* Check for "from" matches in the caller’s buffer */
buffer = (const char *)buf;
consumed = 0;
for (i = 0; i < amount; i++) {
/* Check whether this character matches */
if (buffer[i] == data->from[data->matched]) {
/* Matched a(nother) character */
data->matched++;
/* If we’ve now matched all of "from"... */
if (data->matched == data->fromlen) {
/* Send any data that preceded the match */
unsent = i + 1 - consumed - data->matched;
if (unsent > 0) {
rv = net_write(layer->lower, &buffer[consumed], unsent);
if (rv != unsent)
return IO_ERROR;
}
/* Send "to" in place of "from" */
rv = net_write(layer->lower, data->to, data->tolen);
if (rv != data->tolen)
return IO_ERROR;
/* We’ve handled up to and including buffer[i] */
consumed = i + 1;
/* Start looking for the next "from" match from scratch */
data->matched = 0;
}
} else if (data->matched > 0) {
/* This match didn’t pan out, we need to backtrack */
int j;
int backtrack = data->matched;
data->matched = 0;
/* Check for other potential "from" matches
* preceding buffer[i] */
for (j = 1; j < backtrack; j++) {
/* Check whether this character matches */
if (data->from[j] == data->from[data->matched]) {
/* Matched a(nother) character */
data->matched++;
} else if (data->matched > 0) {
/* This match didn’t pan out, we need to
* backtrack */
j -= data->matched;
data->matched = 0;
}
}
/* If the failed (partial) match begins before the buffer... */
unsent = backtrack - data->matched;
if (unsent > i) {
/* Send the failed (partial) match */
rv = net_write(layer->lower, data->from, unsent);
if (rv != unsent)
return IO_ERROR;
/* We’ve handled up to, but not including,
* buffer[i] */
consumed = i;
}
/* We’re not done with buffer[i] yet */
i--;
}
}
/* Send any data we know won’t be part of a future
* "from" match */
unsent = amount - consumed - data->matched;
if (unsent > 0) {
rv = net_write(layer->lower, &buffer[consumed], unsent);
if (rv != unsent)
return IO_ERROR;
}
return amount;
}
/* ---------------- nsapi_module_init --------------------------- */
/*
* This is the module initialization entry point for this NSAPI
* plugin. The server calls this entry point in response to the
* Init fn="load-modules" line in magnus.conf.
*/
NSAPI_PUBLIC nsapi_module_init(pblock *pb, Session *sn, Request *rq)
{
FilterMethods methods = FILTER_METHODS_INITIALIZER;
const Filter *filter;
/*
* Create the example-replace filter. The example-replace filter
* has order FILTER_CONTENT_TRANSLATION, meaning it transforms
* content (entity body data) from one form to another. The
* example-replace filter implements the write filter method,
* meaning it is interested in outgoing data.
*/
methods.insert = &example_replace_insert;
methods.remove = &example_replace_remove;
methods.write = &example_replace_write;
filter = filter_create("example-replace",
FILTER_CONTENT_TRANSLATION,
&methods);
if (filter == NULL) {
pblock_nvinsert("error", system_errmsg(), pb);
return REQ_ABORTED; /* error initializing plugin */
}
return REQ_PROCEED; /* success */
}