This section describes an example NSAPI filter named example-replace, which examines outgoing data and substitutes one string for another. The example 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 */ }