The buffered null transform,
bnull-transform.c
, reads the response content into
a buffer and then writes the full buffer out to the client. Many
examples of transformations, such as compression, require you to gather
the full response content in order to perform the transformation.
The buffered null transform uses a state variable to keep track of when it is (a) reading data into the buffer and (b) writing the data from the buffer to the downstream vconnection.
The following is a step-by-step walk through the buffered null transform:
Gets a handle to HTTP transactions.
void INKPluginInit (int argc, const char *argv[]) { INKHttpHookAdd (INK_HTTP_READ_RESPONSE_HDR_HOOK, INKContCreate (transform_plugin, NULL)); }
With this INKPluginInit
routine, the
plugin is called back every time Traffic Server reads a response
header.
Checks to see if the transaction response is transformable.
static int transform_plugin (INKCont contp, INKEvent event, void *edata) { INKHttpTxn txnp = (INKHttpTxn) edata; switch (event) { case INK_EVENT_HTTP_READ_RESPONSE_HDR: if (transformable (txnp)) { transform_add (txnp);}
The default behavior for transformations is to cache the transformed content (you also can tell Traffic Server to cache untransformed content, if you want). Therefore, only responses received directly from an origin server need to be transformed. Objects served from the cache are already transformed. To determine whether the response is from the origin server, the routine transformable checks the response header for the “200 OK” server response.
{ INKMBuffer bufp; INKMLoc hdr_loc; INKHttpStatus resp_status; INKHttpTxnServerRespGet (txnp, &bufp, &hdr_loc); if(INK_HTTP_STATUS_OK== (resp_status=INKHttpHdrStatusGet(bufp,hdr_loc))) { return 1; } else { return 0; } }
If the response is transformable, then the plugin creates a transformation vconnection that gets called back when the response data is ready to be transformed (as it is streaming from the origin server).
static void transform_add (INKHttpTxn txnp) { INKVConn connp; connp = INKTransformCreate (bnull_transform, txnp); INKHttpTxnHookAdd (txnp, INK_HTTP_RESPONSE_TRANSFORM_HOOK, connp); }
The previous code fragment shows that the handler function for
the transformation vconnection is
bnull_transform
.
The bnull_transform
function has to
handle ERROR
, WRITE_COMPLETE
,
WRITE_READY
, and IMMEDIATE
events. If the
transform is just beginning, the event received is probably
IMMEDIATE
. The bnull_transform
function calls handle_transform
to handle
WRITE_READY
and IMMEDIATE
.
The handle_transform
function examines
the data parameter for the continuation passed to it (the
continuation passed to handle_transform
is the
transformation vconnection). The data structure keeps track of two
states: copying the data into the buffer
(STATE_BUFFER_DATA
) and writing the contents of the
buffer to the output vconnection
(STATE_OUTPUT_DATA
).
Get a handle to the input VIO (see the
handle_buffering
function).
input_vio = INKVConnWriteVIOGet (contp);
This is so that the transformation can get information about the upstream vconnection’s write operation to the input buffer.
Copy data from the input buffer to the output buffer. See the
handle_buffering
function for the following
code fragment:
INKIOBufferCopy (data->output_buffer, INKVIOReaderGet (write_vio), towrite, 0);
Tell the input buffer that the transformation has read the
data. See the handle_buffering
function for the
following code fragment:
INKIOBufferReaderConsume (INKVIOReaderGet (write_vio), towrite);
Modify the input VIO to tell it how much data has been read
(increase the value of ndone
). See the handle_buffering
function for the following
code fragment:
INKVIONDoneSet (write_vio, INKVIONDoneGet (write_vio) + towrite); }
If there is more data left to read (if ndone <
nbytes
), then the handle_buffering
function
wakes up the upstream vconnection by sending it
WRITE_READY
:
if (INKVIONTodoGet (write_vio) > 0) { if (towrite > 0) { INKContCall (INKVIOContGet (write_vio), INK_EVENT_VCONN_WRITE_READY, write_vio); } } else {
The process of passing data through the transformation is
illustrated in the following diagram. The transformation sends
WRITE_READY
events when it needs more data; when
data is available, the upstream vconnection reenables the
transformation with an IMMEDIATE
event.
When the data is read into the output buffer, the
handle_buffering
function sets the state of the
transformation’s data structure to STATE_OUTPUT_DATA
and calls the upstream vconnection back with the
WRITE_COMPLETE
event.
data->state = STATE_OUTPUT_DATA; INKContCall (INKVIOContGet (write_vio), INK_EVENT_VCONN_WRITE_COMPLETE, write_vio);
The upstream vconnection will probably shut down the write
operation when it receives the WRITE_COMPLETE
event.
The handler function of the transformation,
bnull_transform
, will receive an
IMMEDIATE
event and call the
handle_transform
function. This time, the state
is STATE_OUTPUT_DATA
, so
handle_transform
calls
handle_output
.
The handle_output
function gets a handle
to the output vconnection:
output_conn = INKTransformOutputVConnGet (contp);
The handle_output function writes the buffer to the output vconnection:
data->output_vio = INKVConnWrite (output_conn, contp, data->output_reader, INKIOBufferReaderAvail (data->output_reader) );
The following diagram illustrates the write to the output vconnection: