1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the
8 * License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <iostream>
21 #include <sstream>
22 #include <cstdio>
23 #include <cstring>
24 #include "ts/ts.h"
25 #include "ts/remap.h"
26
27 #include "money_trace.h"
28
29 /**
30 * Allocate transaction data structure.
31 */
32 static struct txndata *
allocTransactionData()33 allocTransactionData()
34 {
35 LOG_DEBUG("allocating transaction state data.");
36 struct txndata *txn_data = static_cast<struct txndata *>(TSmalloc(sizeof(struct txndata)));
37 txn_data->client_request_mt_header = nullptr;
38 txn_data->new_span_mt_header = nullptr;
39 return txn_data;
40 }
41
42 /**
43 * free any previously allocated transaction data.
44 */
45 static void
freeTransactionData(struct txndata * txn_data)46 freeTransactionData(struct txndata *txn_data)
47 {
48 LOG_DEBUG("de-allocating transaction state data.");
49
50 if (txn_data != nullptr) {
51 LOG_DEBUG("freeing transaction data.");
52 TSfree(txn_data->client_request_mt_header);
53 TSfree(txn_data->new_span_mt_header);
54 TSfree(txn_data);
55 }
56 }
57
58 /**
59 * The TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE event callback.
60 *
61 * If there is a cache hit only schedule a TS_HTTP_SEND_RESPONSE_HDR_HOOK
62 * continuation to send back the money trace header in the response to the
63 * client.
64 *
65 * If there is a cache miss, a new money trace header is created and a
66 * TS_HTTP_SEND_REQUES_HDR_HOOK continuation is scheduled to add the
67 * new money trace header to the parent request.
68 */
69 static void
mt_cache_lookup_check(TSCont contp,TSHttpTxn txnp,struct txndata * txn_data)70 mt_cache_lookup_check(TSCont contp, TSHttpTxn txnp, struct txndata *txn_data)
71 {
72 MT generator;
73 int cache_result = 0;
74 char *new_mt_header;
75
76 if (TS_SUCCESS != TSHttpTxnCacheLookupStatusGet(txnp, &cache_result)) {
77 LOG_ERROR("Unable to get cache status.");
78 } else {
79 switch (cache_result) {
80 case TS_CACHE_LOOKUP_MISS:
81 case TS_CACHE_LOOKUP_SKIPPED:
82 new_mt_header = const_cast<char *>(generator.moneyTraceHdr(txn_data->client_request_mt_header));
83 if (new_mt_header != nullptr) {
84 LOG_DEBUG("cache miss, built a new money trace header: %s.", new_mt_header);
85 txn_data->new_span_mt_header = new_mt_header;
86 } else {
87 LOG_DEBUG("failed to build a new money trace header.");
88 }
89 TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_REQUEST_HDR_HOOK, contp);
90
91 // fall through to the default as we always need to send the original
92 // money trace header received from the client back to the client in the
93 // response
94 // fallthrough
95
96 default:
97 TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
98 break;
99 }
100 }
101 }
102
103 /**
104 * remap entry point, called to check for the existence of a money trace
105 * header. If there is one, schedule the continuation to call back and
106 * process on TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK and TS_HTTP_TXN_CLOSE_HOOK.
107 */
108 static void
mt_check_request_header(TSHttpTxn txnp)109 mt_check_request_header(TSHttpTxn txnp)
110 {
111 int length = 0;
112 struct txndata *txn_data = nullptr;
113 TSMBuffer bufp;
114 TSMLoc hdr_loc = nullptr, field_loc = nullptr;
115 TSCont contp;
116
117 // check for a money trace header. If there is one, schedule appropriate continuations.
118 if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc)) {
119 field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, MIME_FIELD_MONEY_TRACE, MIME_LEN_MONEY_TRACE);
120 if (TS_NULL_MLOC != field_loc) {
121 const char *hdr_value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, 0, &length);
122 if (!hdr_value || length <= 0) {
123 LOG_DEBUG("ignoring, corrupt money trace header.");
124 } else {
125 if (nullptr == (contp = TSContCreate(transaction_handler, nullptr))) {
126 LOG_ERROR("failed to create the transaction handler continuation");
127 } else {
128 txn_data = allocTransactionData();
129 txn_data->client_request_mt_header = TSstrndup(hdr_value, length);
130 txn_data->client_request_mt_header[length] = '\0'; // workaround for bug in core.
131 LOG_DEBUG("found money trace header: %s, length: %d", txn_data->client_request_mt_header, length);
132 TSContDataSet(contp, txn_data);
133 TSHttpTxnHookAdd(txnp, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
134 TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, contp);
135 }
136 }
137 } else {
138 LOG_DEBUG("no money trace header was found in the request.");
139 }
140 } else {
141 LOG_DEBUG("failed to retrieve the client request.");
142 }
143 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
144 }
145
146 /**
147 * The TS_EVENT_HTTP_SEND_RESPONSE_HDR callback.
148 *
149 * Adds the money trace header received in the client request to the
150 * client response headers.
151 */
152 static void
mt_send_client_response(TSHttpTxn txnp,struct txndata * txn_data)153 mt_send_client_response(TSHttpTxn txnp, struct txndata *txn_data)
154 {
155 TSMBuffer bufp;
156 TSMLoc hdr_loc = nullptr, field_loc = nullptr;
157
158 if (txn_data->client_request_mt_header == nullptr) {
159 LOG_DEBUG("no client request header to return.");
160 return;
161 }
162
163 // send back the money trace header received in the request
164 // back in the response to the client.
165 if (TS_SUCCESS != TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc)) {
166 LOG_DEBUG("could not get the server response headers.");
167 return;
168 } else {
169 if (TS_SUCCESS ==
170 TSMimeHdrFieldCreateNamed(bufp, hdr_loc, MIME_FIELD_MONEY_TRACE, strlen(MIME_FIELD_MONEY_TRACE), &field_loc)) {
171 if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, txn_data->client_request_mt_header,
172 strlen(txn_data->client_request_mt_header))) {
173 LOG_DEBUG("response header added: %s: %s", MIME_FIELD_MONEY_TRACE, txn_data->client_request_mt_header);
174 TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
175 }
176 } else {
177 LOG_DEBUG("failed to create money trace response header.");
178 }
179 }
180 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
181
182 return;
183 }
184
185 /**
186 * The TS_EVENT_HTTP_SEND_REQUEST_HDR callback.
187 *
188 * When a parent request is made, this function adds the new
189 * money trace header to the parent request headers.
190 */
191 static void
mt_send_server_request(TSHttpTxn txnp,struct txndata * txn_data)192 mt_send_server_request(TSHttpTxn txnp, struct txndata *txn_data)
193 {
194 TSMBuffer bufp;
195 TSMLoc hdr_loc = nullptr, field_loc = nullptr;
196
197 if (txn_data->new_span_mt_header == nullptr) {
198 LOG_DEBUG("there is no new mt request header to send.");
199 return;
200 }
201
202 if (TS_SUCCESS == TSHttpTxnServerReqGet(txnp, &bufp, &hdr_loc)) {
203 field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, MIME_FIELD_MONEY_TRACE, MIME_LEN_MONEY_TRACE);
204 if (TS_NULL_MLOC != field_loc) {
205 if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, txn_data->new_span_mt_header,
206 strlen(txn_data->new_span_mt_header))) {
207 LOG_DEBUG("server request header updated: %s: %s", MIME_FIELD_MONEY_TRACE, txn_data->new_span_mt_header);
208 }
209 } else {
210 LOG_DEBUG("unable to retrieve the money trace header location from the server request headers.");
211 return;
212 }
213 }
214 TSHandleMLocRelease(bufp, hdr_loc, field_loc);
215
216 return;
217 }
218
219 /**
220 * Remap initialization.
221 */
222 TSReturnCode
TSRemapInit(TSRemapInterface * api_info,char * errbuf,int errbuf_size)223 TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
224 {
225 if (!api_info) {
226 strncpy(errbuf, "[tsremap_init] - Invalid TSRemapInterface argument", errbuf_size - 1);
227 return TS_ERROR;
228 }
229
230 if (api_info->tsremap_version < TSREMAP_VERSION) {
231 snprintf(errbuf, errbuf_size, "[TSRemapInit] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
232 (api_info->tsremap_version & 0xffff));
233 return TS_ERROR;
234 }
235
236 LOG_DEBUG("cache_range_requests remap is successfully initialized.");
237
238 return TS_SUCCESS;
239 }
240
241 /**
242 * not used, one instance per remap and no parameters are used.
243 */
244 TSReturnCode
TSRemapNewInstance(int argc,char * argv[],void ** ih,char *,int)245 TSRemapNewInstance(int argc, char *argv[], void **ih, char * /*errbuf */, int /* errbuf_size */)
246 {
247 return TS_SUCCESS;
248 }
249
250 /**
251 * not used, one instance per remap
252 */
253 void
TSRemapDeleteInstance(void * ih)254 TSRemapDeleteInstance(void *ih)
255 {
256 LOG_DEBUG("no op");
257 }
258
259 /**
260 * Remap entry point.
261 */
262 TSRemapStatus
TSRemapDoRemap(void * ih,TSHttpTxn txnp,TSRemapRequestInfo *)263 TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo * /* rri */)
264 {
265 mt_check_request_header(txnp);
266 return TSREMAP_NO_REMAP;
267 }
268
269 /**
270 * Transaction event handler.
271 */
272 static int
transaction_handler(TSCont contp,TSEvent event,void * edata)273 transaction_handler(TSCont contp, TSEvent event, void *edata)
274 {
275 TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
276 struct txndata *txn_data = static_cast<struct txndata *>(TSContDataGet(contp));
277
278 switch (event) {
279 case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
280 LOG_DEBUG("transaction cache lookup complete.");
281 mt_cache_lookup_check(contp, txnp, txn_data);
282 break;
283 case TS_EVENT_HTTP_SEND_REQUEST_HDR:
284 LOG_DEBUG("updating send request headers.");
285 mt_send_server_request(txnp, txn_data);
286 break;
287 case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
288 LOG_DEBUG("updating send response headers.");
289 mt_send_client_response(txnp, txn_data);
290 break;
291 case TS_EVENT_HTTP_TXN_CLOSE:
292 LOG_DEBUG("handling transaction close.");
293 freeTransactionData(txn_data);
294 TSContDestroy(contp);
295 break;
296 default:
297 TSAssert(!"Unexpected event");
298 break;
299 }
300 TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
301
302 return TS_SUCCESS;
303 }
304
305 const char *
moneyTraceHdr(const char * mt_request_hdr)306 MT::moneyTraceHdr(const char *mt_request_hdr)
307 {
308 char copy[8192] = {'\0'};
309 char *toks[3], *p = nullptr, *saveptr = nullptr;
310 std::ostringstream temp_str;
311 std::string mt_header_str;
312 int numtoks = 0;
313
314 if (mt_request_hdr == nullptr) {
315 LOG_DEBUG("an empty header was passed in.");
316 return nullptr;
317 } else {
318 strncpy(copy, mt_request_hdr, 8191);
319 }
320
321 // parse the money header.
322 p = strtok_r(copy, ";", &saveptr);
323 if (p != nullptr) {
324 toks[numtoks++] = p;
325 // copy the traceid
326 } else {
327 LOG_DEBUG("failed to parse the money_trace_header: %s", mt_request_hdr);
328 return nullptr;
329 }
330 do {
331 p = strtok_r(nullptr, ";", &saveptr);
332 if (p != nullptr) {
333 toks[numtoks++] = p;
334 }
335 } while (p != nullptr && numtoks < 3);
336
337 if (numtoks != 3 || toks[0] == nullptr || toks[1] == nullptr || toks[2] == nullptr) {
338 LOG_DEBUG("failed to parse the money_trace_header: %s", mt_request_hdr);
339 return nullptr;
340 }
341
342 if (strncmp(toks[0], "trace-id", strlen("trace-id")) == 0 && strncmp(toks[2], "span-id", strlen("span-id")) == 0 &&
343 (p = strchr(toks[2], '=')) != nullptr) {
344 p++;
345 if (strncmp("0x", p, 2) == 0) {
346 temp_str << toks[0] << ";parent-id=" << p << ";span-id=0x" << std::hex << spanId() << std::ends;
347 } else {
348 temp_str << toks[0] << ";parent-id=" << p << ";span-id=" << spanId() << std::ends;
349 }
350 } else {
351 LOG_DEBUG("invalid money_trace_header: %s", mt_request_hdr);
352 return nullptr;
353 }
354
355 mt_header_str = temp_str.str();
356
357 return TSstrndup(mt_header_str.c_str(), mt_header_str.length());
358 }
359