1.. Licensed to the Apache Software Foundation (ASF) under one
2   or more contributor license agreements.  See the NOTICE file
3   distributed with this work for additional information
4   regarding copyright ownership.  The ASF licenses this file
5   to you under the Apache License, Version 2.0 (the
6   "License"); you may not use this file except in compliance
7   with the License.  You may obtain a copy of the License at
8
9   http://www.apache.org/licenses/LICENSE-2.0
10
11   Unless required by applicable law or agreed to in writing,
12   software distributed under the License is distributed on an
13   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14   KIND, either express or implied.  See the License for the
15   specific language governing permissions and limitations
16   under the License.
17
18.. include:: ../../../common.defs
19
20.. _developer-plugins-io-transformations:
21
22Transformations
23***************
24
25Vconnection Implementer's View
26==============================
27
28A VConnection implementer writes only transformations. All other
29VConnections (net VConnections and cache VConnections) are implemented
30in iocore. As mentioned earlier, a given vconnection can have a maximum
31of one read operation and one write operation being performed on it. The
32vconnection user gets information about the operation being performed by
33examining the VIO returned by a call to :c:func:`TSVConnRead` or
34:c:func:`TSVConnWrite`. The implementer, in turn, gets a handle on the VIO
35operation by examining the VIO returned by :c:func:`TSVConnReadVIOGet` or
36:c:func:`TSVConnWriteVIOGet` (recall that every vconnection created through
37the Traffic Server API has an associated read VIO and write VIO, even if
38it only supports reading or writing).
39
40For example, the null transform plugin's transformation examines the
41input VIO by calling:
42
43.. code-block:: c
44
45     input_vio = TSVConnWriteVIOGet (contp);
46
47where ``contp`` is the transformation.
48
49A vconnection is a continuation. This means it has a handler function
50that is run when an event is sent to it, or more accurately, when an
51event that was sent to it is received. It is the handler function's job
52to examine the event, the current state of its read VIO and write VIO,
53and any other internal state the vconnection might have and potentially
54make some progress on the IO operations.
55
56It is common for the handler function for all vconnections to look
57similar. Their basic form looks something like the code fragment below:
58
59.. code-block:: c
60
61    int
62    vconnection_handler (TSCont contp, TSEvent event, void *edata)
63    {
64    if (TSVConnClosedGet (contp)) {
65            /* Destroy any vconnection specific data here. */
66            TSContDestroy (contp);
67            return 0;
68       } else {
69            /* Handle the incoming event */
70       }
71    }
72
73This code fragment basically shows that many vconnections simply want to
74destroy themselves when they are closed. However, the situation might
75also require the vconnection to do some cleanup processing - which is
76why :c:func:`TSVConnClose` does not simply just destroy the vconnection.
77
78Vconnections are state machines that are animated by the events they
79receive. An event is sent to the vconnection whenever an
80:c:func:`TSVConnRead`, :c:func:`TSVConnWrite`, :c:func:`TSVConnClose`,
81:c:func:`TSVConnShutdown`, or :c:func:`TSVIOReenable` call is performed.
82:c:func:`TSVIOReenable` indirectly references the vconnection through a
83back-pointer in the VIO structure to the vconnection. The vconnection
84itself only knows which call was performed by examining its state and
85the state of its VIOs. For example, when :c:func:`TSVConnClose` is called, the
86vconnection is sent an immediate event (``TS_EVENT_IMMEDIATE``). For
87every event the vconnection receives, it needs to check its closed flag
88to see if it has been closed. Similarly, when :c:func:`TSVIOReenable` is
89called, the vconnection is sent an immediate event. For every event the
90vconnection receives, it must check its VIOs to see if the buffers have
91been modified to a state in which it can continue processing one of its
92operations.
93
94Finally, a vconnection is likely the user of other vconnections. It also
95receives events as the user of these other vconnections. When it
96receives such an event, like ``TS_EVENT_VCONN_WRITE_READY``, it might
97just enable another vconnection that's writing into the buffer used by
98the vconnection reading from it. The above description is merely
99intended to give the overall idea for what a vconnection needs to do.
100
101Transformation VConnection
102--------------------------
103
104A :ref:`transformation <transformations>` is
105a specific type of vconnection. It supports a subset of the vconnection
106functionality that enables one or more transformations to be chained
107together. A transformation sits as a bottleneck between an input data
108source and an output data sink, which enables it to view and modify all
109the data passing through it. Alternatively, some transformations simply
110scan the data and pass it on. A common transformation is one that
111compresses data in some manner.
112
113A transformation can modify either the data stream being sent *to* an
114HTTP client (e.g. the document) or the data stream being sent *from* an
115HTTP client (e.g. post data). To do this, the transformation should hook
116on to one of the following hooks:
117
118-  ``TS_HTTP_REQUEST_TRANSFORM_HOOK``
119
120-  ``TS_HTTP_RESPONSE_TRANSFORM_HOOK``
121
122Note that because a transformation is intimately associated with a given
123transaction, it is only possible to add the hook to the transaction
124hooks - not to the global or session hooks. Transformations reside in a
125chain, so their ordering is quite easily determined: transformations
126that add themselves to the chain are simply appended to it.
127
128Data is passed in to the transformation by initiating a vconnection
129write operation on the transformation. As a consequence of this design,
130a transformation must support the vconnection write operation. In other
131words, your transformation must expect an upstream vconnection to write
132data to it. The transformation has to read the data, consume it, and
133tell the upstream vconnection it is finished by sending it an
134``TS_EVENT_WRITE_COMPLETE`` event. Transformations cannot send the
135``TS_EVENT_VCONN_WRITE_COMPLETE`` event to the upstream vconnection
136unless they are finished consuming all incoming data. If
137``TS_EVENT_VCONN_WRITE_COMPLETE`` is sent prematurely, then certain
138internal Traffic Server data structures will not be deallocated, thereby
139causing a memory leak.
140
141Here's how to make sure that all incoming data is consumed:
142
143-  After reading or copying data, make sure that you consume the data
144   and increase the value of ndone for the input VIO, as in the
145   following example taken from ``null_transform.c``:
146
147   .. code-block:: c
148
149       TSIOBufferCopy (TSVIOBufferGet (data->output_vio),
150       TSVIOReaderGet (input_vio), towrite, 0);
151       /* Tell the read buffer that we have read the data and are no longer interested in it. */
152       TSIOBufferReaderConsume (TSVIOReaderGet (input_vio), towrite);
153       /* Modify the input VIO to reflect how much has been read.*/
154       TSVIONDoneSet (input_vio, TSVIONDoneGet (input_vio) + towrite);
155
156-  Before sending ``TS_EVENT_VCONN_WRITE_COMPLETE``, your transformation
157   should check the number of bytes remaining in the upstream
158   vconnection's write VIO (input VIO) using the function
159   ``TSVIONTodoGet`` (``input_vio``). This value should go to zero when
160   all of the upstream data is consumed
161   (``TSVIONTodoGet = nbytes - ndone``). Do not send
162   ``TS_EVENT_VCONN_WRITE_COMPLETE`` events if :c:func:`TSVIONTodoGet` is
163   greater than zero.
164-  The transformation passes data out of itself by using the output
165   vconnection retrieved by :c:func:`TSTransformOutputVConnGet`. Immediately
166   before Traffic Server initiates the write operation (which inputs
167   data into the transformation), it sets the output vconnection either
168   to the next transformation in the chain of transformations or to a
169   special terminating transformation (if it's the last transformation
170   in the chain). Since the transformation is handed ownership of the
171   output vconnection, it must close it at some point in order for it to
172   be deallocated.
173-  All of the transformations in a transformation chain share the
174   transaction's mutex. This small restriction (enforced by
175   :c:func:`TSTransformCreate`) removes many of the locking complications of
176   implementing general vconnections. For example, a transformation does
177   not have to grab its write VIO mutex before accessing its write VIO
178   because it knows it already holds the mutex.
179
180The transformation functions are:
181
182  - :c:func:`TSTransformCreate`
183  - :c:func:`TSTransformOutputVConnGet`
184