Table of Contents
The new protocol APIs enable you to extend Traffic Server to be a web proxy for any protocol. This chapter describes new protocol APIs and the plugins that support new protocols. It also provides a detailed review of code for a sample Protocol plugin that supports a very simple artificial HTTP-like protocol.
This chapter contains the following sections:
Gives the state diagram and header structure of the artificial protocol. Describes what the supporting plugin has to do.
In-depth explanation of the Protocol plugin: begins with overall architecture, describes how to write continuations as state machines, and finishes with a walk-through of Protocol plugin code as it processes a transaction.
The sample protocol enables a client to ask a server for a file.
Clients send requests to a specific Traffic Server port (specified in
plugin.config
). The requests look like the following:
server_name file_name\n\n
Using the Protocol plugin, Traffic Server can accept these requests, parse them, and act as a proxy cache (i.e., requests the file from the origin server on the client’s behalf and stores copies of response messages in cache). The Protocol plugin is a state machine that flows through the states illustrated in Figure 6.1, “Sample Protocol State Diagram”. This figure illustrates the steps that Traffic Server and the Protocol plugin go through in order to support the sample protocol.
In more specific terms, Traffic Server and the Protocol plugin must:
listen for and accept client connections (on the accept port
specified in plugin.config
)
read incoming client requests
look up the requested content in the Traffic Server cache
serve content from cache if the request is a cache hit (this simple example does not do freshness checking)
open a connection to the origin server if the request is a
cache miss (on the server port specified in plugin.config
)
forward the request to the origin server
receive the origin server response
cache the response and send it on to the client
To see how the Protocol plugin works, you need to understand some broader concepts. This section assumes you're familiar with the concepts of continuation, Traffic Server’s asynchronous event model, and basic Traffic Server plugin structure. If you are not familiar with these concepts, then reference Getting Started and How to Create Traffic Server Plugins.
The Protocol plugin creates a static continuation that is an “accept” state machine - that is, a state machine whose job is to accept client connections on the appropriate port. When Traffic Server accepts a net connection from a client on that port, the accept state machine is activated. It then creates a new continuation: a transaction state machine. The accept state machine creates one transaction state machine for each transaction (where a transaction consists of a client request and Traffic Server’s response). Each transaction state machine lives until the transaction completes; then it is destroyed. If the client’s request for content is a cache miss, then a transaction state machine might need to open a connection to the origin server. This is illustrated in Figure 6.2, “Protocol Plugin Overview”.
The first steps for writing the Protocol plugin are now clear:
in INKPluginInit
, you must create a continuation that listens for net
connections on the client port specified in plugin.config
(this continuation is the accept
state machine).
Below is a summary of the continuations implemented for the Protocol plugin:
An accept state machine that listens for client connections, and then creates transaction state machines whenever Traffic Server accepts a new client connection. The accept state machine lives as long as Traffic Server is running.
Transaction state machines that read client requests, process them, and are subsequently destroyed when the transaction is finished.
Implementing the rest of the Protocol plugin
requires that you understand the flow of events during the
course of a transaction. Unlike HTTP transaction plugins, this plugin
must read data from network connections and then read/write data to the
Traffic Server cache. This means that its continuations do not receive
HTTP state machine events; they receive events from Traffic Server’s
processor subsystems. For example: the accept state machine is activated by an
INK_EVENT_NET_ACCEPT
event from Traffic Server’s Net
Processor; the handler function for the accept state machine must be
able to handle that event.
The transaction state machines are activated when the client connection receives incoming request data. The Net Processor notifies the transaction state machine of incoming data. The transaction state machine reads the data; when finished, it initiates a cache lookup of the requested file. When the cache lookup completes, the transaction state machine is activated by the Traffic Server Cache Processor.
If the transaction state machine needs to open a connection to the origin server to fetch content (in the case of a cache miss), then the transaction state machine initiates a DNS lookup of the server name. The transaction state machine is activated by a DNS lookup event from the Traffic Server Host Database Processor.
If the transaction has to connect to the origin server, then the transaction state machine initiates a net connection and waits for an event from Net Processor.
The flow of events is illustrated in Figure 6.3, “Protocol Plugin Flow of Events”. The thin straight lines show Net Processor event flow, the thin dashed lines are Host DB event flow, and the thick dashed lines are Cache event flow.
Notice that this flow of events is independent of the Protocol plugin's design (i.e., whether you build “accept” and “transaction”
state machines). Any plugin that supports network connections
uses the net vconnection interfaces (INKNetAccept
,
INKNetConnect
) and thus receives events from Net
Processor. Any plugin that performs cache lookups or cache writes uses
INKCacheRead
, INKCacheWrite
,
INKVConnRead
, and INKVConnWrite
and thus
receives events from Cache Processor and the Traffic Server event
system; similarly, any plugin that does DNS lookups receives events
from the Host DB Processor.
The transaction state machines (TSMs) in the Protocol plugin must do the following:
Keep track of the state of the transaction
Handle the events received (based on the state of the transaction and the event received)
Update the state of the transaction as it changes
Below is one way you can implement TSMs (details on how the Protocol plugin does this appear in the next section):
Create a data structure for transactions that contains all
of the state data you need to keep track of. In the Protocol
plugin this is a struct, Txn_SM
.
When you create the TSM’s continuation, initialize data of
type Txn_SM
. Initialize the data to the
initial state of a transaction (in this case, a net connection has
just been accepted). Associate this data to the TSM continuation
using INKContDataSet
.
Write state handler functions that handle the expected events for each state.
Write the handler for the TSM. Its job is to receive events,
examine the current state, and execute the appropriate state
handler function. In the Protocol plugin, the handler is
main_handler
. main_handler
calls the
state handler functions to handle each state.
The flow of execution is illustrated in Figure 6.4, “How Transaction State Machines are Implemented in the Protocol Plugin”.
The handler for the TSM, (called main_handler
in the Protocol plugin) receives events from the TSM.
main_handler
examines the state of the
transaction—in particular, it examines the current handler.
main_handler
calls the
current_handler
(which is one of the state handler
functions), and then passes the current event to current_handler
.
In Figure 6.4 below, the
current handler is called state2_handler
.
The current_handler
handles the event and
updates the data.
In Figure 6.4 below, the state is changed
from state2
to state3
(and the current
handler is changed from state2_handler
to
state3_handler
).
The next time
main_handler
receives an event, it will be processed
by state3_handler
.
state2_handler
arranges the next callback of
the TSM. Typically, it gives Traffic Server additional work to do
(such as writing a file to cache)so that it can progress to the
next state.
The TSM (main_handler
) then waits for the
next event to arrive from Traffic Server.
The implementation above is diagrammed below in “How Transaction State Machines are Implemented in the Protocol Plugin”. Additional details are provided in the next section, which provides a walk through the processing of a typical transaction.
The code is contained in the following files:
Protocol.c
and
Protocol.h
Accept.c
and
Accept.h
TxnSM.c
and
TxnSM.h
Below is a step-by-step walk-through of the code.
The INKPluginInit
function is in
Protocol.c
. It checks the validity of the
plugin.confi
g entries (there must be two: a client accept port and
a server port) and runs an initialization routine, init
.
The init
function (in
Protocol.c
) creates the plugin’s log file
using INKTextLogObjectCreate
.
The init
function creates the accept
state machine using AcceptCreate
. The code for
AcceptCreate
is in
Accept.c
.
The accept state machine, like the transaction state
machine, keeps track of its state via a data structure. This data
structure, Accept, is defined in Accept.h
. In
AcceptCreate
, state data is associated with the new
accept state machine using INKContDataSet
.
The init
function arranges the callback
of the accept state machine when there is a network connection
by using INKNetAccept
.
The handler for the accept state machine is
accept_event
in
Accept.c
. When Traffic Server’s Net Processor
sends INK_EVENT_NET_ACCEPT
to the accept state
machine, accept_event
creates a transaction
state machine (txn_sm
) by calling
TxnSMCreate
.
Notice that
accept_event
creates a mutex for the
transaction state machine, as each transaction state machine has its
own mutex.
The TxnSMCreate
function is in
TxnSM.c
. The first thing it does is
initialize the transaction’s data, which is of type
TxnSM
(defined in TxnSM.h
).
Notice that the current handler (q_current_handler
)
is set to state_start
.
TxnSMCreate
then creates a transaction
state machine using INKContCreate
. The handler for
the transaction state machine is main_handler
, which is in TxnSM.c
When accept_event
receives
INK_EVENT_NET_ACCEPT
, it calls the transaction state
machine (INKContCall
(txn_sm, 0,
NULL);
). The event passed to
main_handler
is 0
(INK_EVENT_NONE
).
The first thing main_handler
does is
examine the current txn_sm
state by calling
INKContDataGet
. The state is
state_start
.
main_handler
then invokes the handler for
state_start
by using the function pointer TxnSMHandler
(defined in
TxnSM.h
).
The state_start
handler function (in
TxnSM.c
) is handed an event (at this stage,
the event is INK_EVENT_NET_ACCEPT
) and a client
vconnection.
state_start
checks to see if
this client vconnection is closed; if it is not, then
state_start
attempts to read data from the
client vconnection into an INKIOBuffer
(state_start
is handling the event it
receives).
state_start
changes the current handler
to state_interface_with_client
(i.e., it updates the
state of the transaction to the next state).
state_start
initiates a read of the
client vconnection (arranges for Traffic Server to send
INK_EVENT_VCONN_READ_READY
events to the TSM) by
calling INKVConnRead
.
state_interface_with_client
is
activated by the next event from Traffic Server. It checks for
errors and examines the read VIO for the read operation initiated
by INKVConnRead
.
If the read VIO is the client_read_VIO
(which
we are expecting at this stage in the transaction), then
state_interface_with_client
updates the state
to state_read_request_from_client
state_read_request_from_client
handles
actual INK_EVENT_READ_READY
events and reads the
client request.
state_read_request_from_client
parses
the client request.
state_read_request_from_client
updates
the state to the next state,
state_handle_cache_lookup
.
state_read_request_from_client
arranges
for Traffic Server to call back the TSM with the next set of
events (initiating the cache lookup) by calling
INKCacheRead
.
When the INKCacheRead
sends the TSM either
INK_EVENT_OPEN_READ
(a cache hit) or
INK_EVENT_OPEN_READ_FAILED
(a cache miss),
main_handler
calls
state_handle_cache_lookup
.