1 /**
2   @file
3   @brief Plugin to verify the ordering of session and transaction start and
4   close hooks is correct. Keeps track of statistics about the number of
5   hooks tracked that are caught and of the number of errors encountered.
6 
7   @section license License
8 
9   Licensed to the Apache Software Foundation (ASF) under one
10   or more contributor license agreements.  See the NOTICE file
11   distributed with this work for additional information
12   regarding copyright ownership.  The ASF licenses this file
13   to you under the Apache License, Version 2.0 (the
14   "License"); you may not use this file except in compliance
15   with the License.  You may obtain a copy of the License at
16 
17       http://www.apache.org/licenses/LICENSE-2.0
18 
19   Unless required by applicable law or agreed to in writing, software
20   distributed under the License is distributed on an "AS IS" BASIS,
21   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22   See the License for the specific language governing permissions and
23   limitations under the License.
24 */
25 
26 #include <stdlib.h> // for abort
27 #include <ts/ts.h>  // for debug
28 
29 // debug messages viewable by setting 'proxy.config.diags.debug.tags'
30 // in 'records.config'
31 
32 // debug messages during one-time initialization
33 static const char DEBUG_TAG_INIT[] = "continuations_verify.init";
34 static const char DEBUG_TAG_MSG[]  = "continuations_verify.msg";
35 static const char DEBUG_TAG_HOOK[] = "continuations_verify.hook";
36 
37 // plugin registration info
38 static char plugin_name[]   = "continuations_verify";
39 static char vendor_name[]   = "apache";
40 static char support_email[] = "shinrich@apache.org";
41 
42 // Statistics provided by the plugin
43 static int stat_ssn_close_1 = 0; // number of TS_HTTP_SSN_CLOSE hooks caught by the first continuation
44 static int stat_ssn_close_2 = 0; // number of TS_HTTP_SSN_CLOSE hooks caught by the second continuation
45 static int stat_txn_close_1 = 0; // number of TS_HTTP_TXN_CLOSE hooks caught by the first continuation
46 static int stat_txn_close_2 = 0; // number of TS_HTTP_TXN_CLOSE hooks caught by the second continuation
47 static int stat_test_done   = 0; // Incremented when receiving a traffic_ctl message
48 
49 static int
handle_msg(TSCont contp,TSEvent event,void * edata)50 handle_msg(TSCont contp, TSEvent event, void *edata)
51 {
52   if (event == TS_EVENT_LIFECYCLE_MSG) { // External trigger, such as traffic_ctl
53     TSDebug(DEBUG_TAG_MSG, "event TS_EVENT_LIFECYCLE_MSG");
54     // Send to a ET net thread just to be sure.
55     // Turns out the msg is sent to a task thread, but task
56     // threads do not get their thread local copy of the stats
57     // merged in.  So externally, test.done was stuck at 0 without
58     // the Schedule to a NET thread
59     TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
60   } else {
61     TSDebug(DEBUG_TAG_MSG, "event %d", event);
62     TSStatIntIncrement(stat_test_done, 1);
63   }
64   return 0;
65 }
66 
67 /**
68     This function is called on every request and logs session and transaction
69     start and close events. It is used upon initialization to install the hooks
70     to the corresponding events. Return value is irrelevant.
71 */
72 static int
handle_order_1(TSCont contp,TSEvent event,void * edata)73 handle_order_1(TSCont contp, TSEvent event, void *edata)
74 {
75   TSHttpSsn ssnp; // session data
76   TSHttpTxn txnp; // transaction data
77 
78   TSDebug(DEBUG_TAG_HOOK, "order_1 event %d", event);
79 
80   // Find the event that happened
81   switch (event) {
82   case TS_EVENT_HTTP_TXN_CLOSE: // End of transaction
83     txnp = reinterpret_cast<TSHttpTxn>(edata);
84 
85     TSStatIntIncrement(stat_txn_close_1, 1);
86     TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
87     break;
88   case TS_EVENT_HTTP_SSN_CLOSE: // End of session
89     ssnp = reinterpret_cast<TSHttpSsn>(edata);
90 
91     TSStatIntIncrement(stat_ssn_close_1, 1);
92     TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE);
93     break;
94   // Just release the lock for all other states and do nothing
95   default:
96     break;
97   }
98 
99   return 0;
100 }
101 
102 static int
handle_order_2(TSCont contp,TSEvent event,void * edata)103 handle_order_2(TSCont contp, TSEvent event, void *edata)
104 {
105   TSHttpSsn ssnp; // session data
106   TSHttpTxn txnp; // transaction data
107 
108   TSDebug(DEBUG_TAG_HOOK, "order_2 event %d", event);
109 
110   // Find the event that happened
111   switch (event) {
112   case TS_EVENT_HTTP_TXN_CLOSE: // End of transaction
113     txnp = reinterpret_cast<TSHttpTxn>(edata);
114 
115     TSStatIntIncrement(stat_txn_close_2, 1);
116     TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
117     break;
118   case TS_EVENT_HTTP_SSN_CLOSE: // End of session
119     ssnp = reinterpret_cast<TSHttpSsn>(edata);
120 
121     TSStatIntIncrement(stat_ssn_close_2, 1);
122     TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE);
123     break;
124   // Just release the lock for all other states and do nothing
125   default:
126     break;
127   }
128 
129   return 0;
130 }
131 
132 /**
133     Entry point for the plugin.
134         - Attaches global hooks for session start and close.
135         - Attaches global hooks for transaction start and close.
136         - Attaches lifecycle hook for communication through traffic_ctl
137         - Initializes all statistics as described in the README
138 */
139 void
TSPluginInit(int argc,const char * argv[])140 TSPluginInit(int argc, const char *argv[])
141 {
142   TSDebug(DEBUG_TAG_INIT, "initializing plugin");
143 
144   TSPluginRegistrationInfo info;
145 
146   info.plugin_name   = plugin_name;
147   info.vendor_name   = vendor_name;
148   info.support_email = support_email;
149 
150 #if (TS_VERSION_MAJOR < 3)
151   if (TSPluginRegister(TS_SDK_VERSION_2_0, &info) != TS_SUCCESS) {
152 #elif (TS_VERSION_MAJOR < 6)
153   if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
154 #else
155   if (TSPluginRegister(&info) != TS_SUCCESS) {
156 #endif
157     TSError("[%s] Plugin registration failed. \n", plugin_name);
158   }
159 
160   TSCont contp_1 = TSContCreate(handle_order_1, TSMutexCreate());
161   TSCont contp_2 = TSContCreate(handle_order_2, TSMutexCreate());
162   TSCont contp   = TSContCreate(handle_msg, TSMutexCreate());
163 
164   if (contp_1 == nullptr || contp_2 == nullptr || contp == nullptr) {
165     // Continuation initialization failed. Unrecoverable, report and exit.
166     TSError("[%s] could not create continuation", plugin_name);
167     abort();
168   } else {
169     // Continuation initialization succeeded.
170 
171     stat_txn_close_1 =
172       TSStatCreate("continuations_verify.txn.close.1", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM);
173     stat_ssn_close_1 =
174       TSStatCreate("continuations_verify.ssn.close.1", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM);
175     stat_txn_close_2 =
176       TSStatCreate("continuations_verify.txn.close.2", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM);
177     stat_ssn_close_2 =
178       TSStatCreate("continuations_verify.ssn.close.2", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM);
179     stat_test_done =
180       TSStatCreate("continuations_verify.test.done", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM);
181 
182     // Add all hooks.
183     TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, contp_1);
184     TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, contp_1);
185 
186     TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, contp_2);
187     TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, contp_2);
188 
189     // Respond to a traffic_ctl message
190     TSLifecycleHookAdd(TS_LIFECYCLE_MSG_HOOK, contp);
191   }
192 }
193