1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /* bnull-transform.c: an example program that illustrates a buffered
25 * null transform.
26 *
27 *
28 *
29 * Usage:
30 * bnull-transform.so
31 *
32 *
33 */
34
35 /* set tab stops to four. */
36
37 #include <stdio.h>
38
39 #include "ts/ts.h"
40 #include "tscore/ink_defs.h"
41
42 #define PLUGIN_NAME "bnull_transform"
43
44 #define TS_NULL_MUTEX NULL
45 #define STATE_BUFFER_DATA 0
46 #define STATE_OUTPUT_DATA 1
47
48 typedef struct {
49 int state;
50 TSVIO output_vio;
51 TSIOBuffer output_buffer;
52 TSIOBufferReader output_reader;
53 } MyData;
54
55 static MyData *
my_data_alloc()56 my_data_alloc()
57 {
58 MyData *data;
59
60 data = (MyData *)TSmalloc(sizeof(MyData));
61 data->state = STATE_BUFFER_DATA;
62 data->output_vio = NULL;
63 data->output_buffer = NULL;
64 data->output_reader = NULL;
65
66 return data;
67 }
68
69 static void
my_data_destroy(MyData * data)70 my_data_destroy(MyData *data)
71 {
72 if (data) {
73 if (data->output_buffer) {
74 TSIOBufferDestroy(data->output_buffer);
75 }
76 TSfree(data);
77 }
78 }
79
80 static int
handle_buffering(TSCont contp,MyData * data)81 handle_buffering(TSCont contp, MyData *data)
82 {
83 TSVIO write_vio;
84 int64_t towrite;
85
86 /* Get the write VIO for the write operation that was performed on
87 ourself. This VIO contains the buffer that we are to read from
88 as well as the continuation we are to call when the buffer is
89 empty. */
90 write_vio = TSVConnWriteVIOGet(contp);
91
92 /* Create the output buffer and its associated reader */
93 if (!data->output_buffer) {
94 data->output_buffer = TSIOBufferCreate();
95 TSAssert(data->output_buffer);
96 data->output_reader = TSIOBufferReaderAlloc(data->output_buffer);
97 TSAssert(data->output_reader);
98 }
99
100 /* We also check to see if the write VIO's buffer is non-NULL. A
101 NULL buffer indicates that the write operation has been
102 shutdown and that the continuation does not want us to send any
103 more WRITE_READY or WRITE_COMPLETE events. For this buffered
104 transformation that means we're done buffering data. */
105
106 if (!TSVIOBufferGet(write_vio)) {
107 data->state = STATE_OUTPUT_DATA;
108 return 0;
109 }
110
111 /* Determine how much data we have left to read. For this bnull
112 transform plugin this is also the amount of data we have left
113 to write to the output connection. */
114
115 towrite = TSVIONTodoGet(write_vio);
116 if (towrite > 0) {
117 /* The amount of data left to read needs to be truncated by
118 the amount of data actually in the read buffer. */
119
120 int64_t avail = TSIOBufferReaderAvail(TSVIOReaderGet(write_vio));
121 if (towrite > avail) {
122 towrite = avail;
123 }
124
125 if (towrite > 0) {
126 /* Copy the data from the read buffer to the input buffer. */
127 TSIOBufferCopy(data->output_buffer, TSVIOReaderGet(write_vio), towrite, 0);
128
129 /* Tell the read buffer that we have read the data and are no
130 longer interested in it. */
131 TSIOBufferReaderConsume(TSVIOReaderGet(write_vio), towrite);
132
133 /* Modify the write VIO to reflect how much data we've
134 completed. */
135 TSVIONDoneSet(write_vio, TSVIONDoneGet(write_vio) + towrite);
136 }
137 }
138
139 /* Now we check the write VIO to see if there is data left to read. */
140 if (TSVIONTodoGet(write_vio) > 0) {
141 if (towrite > 0) {
142 /* Call back the write VIO continuation to let it know that we
143 are ready for more data. */
144 TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_READY, write_vio);
145 }
146 } else {
147 data->state = STATE_OUTPUT_DATA;
148
149 /* Call back the write VIO continuation to let it know that we
150 have completed the write operation. */
151 TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_COMPLETE, write_vio);
152 }
153
154 return 1;
155 }
156
157 static int
handle_output(TSCont contp,MyData * data)158 handle_output(TSCont contp, MyData *data)
159 {
160 /* Check to see if we need to initiate the output operation. */
161 if (!data->output_vio) {
162 TSVConn output_conn;
163
164 /* Get the output connection where we'll write data to. */
165 output_conn = TSTransformOutputVConnGet(contp);
166
167 data->output_vio = TSVConnWrite(output_conn, contp, data->output_reader, TSIOBufferReaderAvail(data->output_reader));
168
169 TSAssert(data->output_vio);
170 }
171 return 1;
172 }
173
174 static void
handle_transform(TSCont contp)175 handle_transform(TSCont contp)
176 {
177 MyData *data;
178 int done;
179
180 /* Get our data structure for this operation. The private data
181 structure contains the output VIO and output buffer. If the
182 private data structure pointer is NULL, then we'll create it
183 and initialize its internals. */
184
185 data = (MyData *)TSContDataGet(contp);
186 if (!data) {
187 data = my_data_alloc();
188 TSContDataSet(contp, (void *)data);
189 }
190
191 do {
192 switch (data->state) {
193 case STATE_BUFFER_DATA:
194 done = handle_buffering(contp, data);
195 break;
196 case STATE_OUTPUT_DATA:
197 done = handle_output(contp, data);
198 break;
199 default:
200 done = 1;
201 break;
202 }
203 } while (!done);
204 }
205
206 static int
bnull_transform(TSCont contp,TSEvent event,void * edata ATS_UNUSED)207 bnull_transform(TSCont contp, TSEvent event, void *edata ATS_UNUSED)
208 {
209 /* Check to see if the transformation has been closed by a
210 call to TSVConnClose. */
211
212 if (TSVConnClosedGet(contp)) {
213 my_data_destroy((MyData *)TSContDataGet(contp));
214 TSContDestroy(contp);
215 } else {
216 switch (event) {
217 case TS_EVENT_ERROR: {
218 TSVIO write_vio;
219
220 /* Get the write VIO for the write operation that was
221 performed on ourself. This VIO contains the continuation of
222 our parent transformation. */
223 write_vio = TSVConnWriteVIOGet(contp);
224
225 /* Call back the write VIO continuation to let it know that we
226 have completed the write operation. */
227 TSContCall(TSVIOContGet(write_vio), TS_EVENT_ERROR, write_vio);
228 break;
229 }
230
231 case TS_EVENT_VCONN_WRITE_COMPLETE:
232 /* When our output connection says that it has finished
233 reading all the data we've written to it then we should
234 shutdown the write portion of its connection to
235 indicate that we don't want to hear about it anymore. */
236
237 TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
238 break;
239
240 case TS_EVENT_VCONN_WRITE_READY:
241 default:
242 /* If we get a WRITE_READY event or any other type of event
243 (sent, perhaps, because we were reenabled) then we'll attempt
244 to transform more data. */
245 handle_transform(contp);
246 break;
247 }
248 }
249
250 return 0;
251 }
252
253 static int
transformable(TSHttpTxn txnp)254 transformable(TSHttpTxn txnp)
255 {
256 TSMBuffer bufp;
257 TSMLoc hdr_loc;
258 TSHttpStatus resp_status;
259 int retv = 0;
260
261 /* We are only interested in transforming "200 OK" responses. */
262
263 if (TS_SUCCESS == TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc)) {
264 resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
265 retv = (resp_status == TS_HTTP_STATUS_OK);
266 TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
267 }
268
269 return retv;
270 }
271
272 static void
transform_add(TSHttpTxn txnp)273 transform_add(TSHttpTxn txnp)
274 {
275 TSVConn connp;
276
277 connp = TSTransformCreate(bnull_transform, txnp);
278 TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp);
279 return;
280 }
281
282 static int
transform_plugin(TSCont contp ATS_UNUSED,TSEvent event,void * edata)283 transform_plugin(TSCont contp ATS_UNUSED, TSEvent event, void *edata)
284 {
285 TSHttpTxn txnp = (TSHttpTxn)edata;
286
287 switch (event) {
288 case TS_EVENT_HTTP_READ_RESPONSE_HDR:
289 if (transformable(txnp)) {
290 transform_add(txnp);
291 }
292 TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
293 return 0;
294 default:
295 break;
296 }
297
298 return 0;
299 }
300
301 void
TSPluginInit(int argc ATS_UNUSED,const char * argv[]ATS_UNUSED)302 TSPluginInit(int argc ATS_UNUSED, const char *argv[] ATS_UNUSED)
303 {
304 TSPluginRegistrationInfo info;
305 TSMutex mutex = TS_NULL_MUTEX;
306
307 info.plugin_name = PLUGIN_NAME;
308 info.vendor_name = "Apache Software Foundation";
309 info.support_email = "dev@trafficserver.apache.org";
310
311 if (TSPluginRegister(&info) != TS_SUCCESS) {
312 TSError("[%s] Plugin registration failed", PLUGIN_NAME);
313
314 goto Lerror;
315 }
316
317 /* This is call we could use if we need to protect global data */
318 /* TSReleaseAssert ((mutex = TSMutexCreate()) != TS_NULL_MUTEX); */
319
320 TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, TSContCreate(transform_plugin, mutex));
321 return;
322
323 Lerror:
324 TSError("[%s] Plugin disabled", PLUGIN_NAME);
325 }
326