This section provides a step-by-step description of what the null transform plugin does, along with sections of code that apply. For context, you can find each code snippet in the complete source code. Some of the error checking details are left out - to give the description a step-by-step flow, only the highlights of the transform are included.
Below is an overview of the null transform plugin:
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 can also 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 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.
static int transformable (INKHttpTxn txnp) { 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 (null_transform, txnp); INKHttpTxnHookAdd (txnp, INK_HTTP_RESPONSE_TRANSFORM_HOOK, connp); }
The previous code fragment shows that the handler function for
the transformation vconnection is
null_transform
.
Get a handle to the output vconnection (that receives data from the tranformation).
output_conn = INKTransformOutputVConnGet (con
Get a handle to the input VIO. (See the
handle_transform
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.
Initiate a write to the output vconnection of the specified
number of bytes. When the write is initiated, the transformation
expects to receive WRITE_READY
,
WRITE_COMPLETE
, or ERROR
events from the
output vconnection.
See the handle_transform
function for the
following code fragment:
data->output_vio = INKVConnWrite (output_conn, contp, data->output_reader, INKVIONBytesGet (input_vio));
Copy data from the input buffer to the output buffer. See the
handle_transform
function for the following code
fragment:
INKIOBufferCopy (INKVIOBufferGet (data->output_vio), INKVIOReaderGet (input_vio), towrite, 0);
Tell the input buffer that the transformation has read the
data. See the handle_transform
function for the
following code fragment:
INKIOBufferReaderConsume (INKVIOReaderGet (input_vio), towrite);
Modify the input VIO to tell it how much data has been read
(increase the value of ndone
). See the handle_transform
function for the following code fragment:
INKVIONDoneSet (input_vio, INKVIONDoneGet (input_vio) + towrite);
If there is more data left to read (if ndone <
nbytes
), then the handle_transform
function wakes up
the downstream vconnection with a reenable and wakes up the upstream
vconnection by sending it WRITE_READY
:
if (INKVIONTodoGet (input_vio) > 0) { if (towrite > 0) { INKVIOReenable (data->output_vio); INKContCall (INKVIOContGet (input_vio), INK_EVENT_VCONN_WRITE_READY, input_vio); } } else {
The process of passing data through the transformation is
illustrated in the following diagram. The downstream vconnections
send WRITE_READY
events when they need more data;
when data is available, the upstream vconnections reenable the
downstream vconnections. In this instance, the INKVIOReenable
function sends
INK_EVENT_IMMEDIATE
.
If the handle_transform
function finds there
is no more data to read, then it sets nbytes
to ndone
on the output
(downstream) VIO and wakes up the output vconnection with a
reenable. It then triggers the end of the write operation from the
upstream vconnection by sending the upstream vconnection a WRITE_COMPLETE
event.
} else { INKVIONBytesSet (data->output_vio, INKVIONDoneGet (input_vio)); INKVIOReenable (data->output_vio); INKContCall (INKVIOContGet (input_vio), INK_EVENT_VCONN_WRITE_COMPLETE, input_vio); }
When the upstream vconnection receives the
WRITE_COMPLETE
event, it will probably shut down the
write operation.
Similarly, when the downstream vconnection has consumed all of
the data, it sends the transformation a WRITE_COMPLETE
event. The transformation handles this event with a shut down (the
transformation shuts down the write operation to the downstream
vconnection). See the null_plugin
function for the
following code fragment:
case INK_EVENT_VCONN_WRITE_COMPLETE: INKVConnShutdown (INKTransformOutputVConnGet (contp), 0, 1 break;
The following diagram illustrates the flow of events: