xref: /trafficserver/src/tscpp/api/Transaction.cc (revision 4cfd5a73)
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
7   "License"); you may not use this file except in compliance
8   with the 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, software
13   distributed under the License is distributed on an "AS IS" BASIS,
14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   See the License for the specific language governing permissions and
16   limitations under the License.
17  */
18 
19 /**
20  * @file Transaction.cc
21  */
22 
23 #include <memory>
24 #include <cstdlib>
25 #include <cstring>
26 #include <map>
27 #include <string>
28 #include <utility>
29 
30 #include "tscpp/api/Transaction.h"
31 #include "tscore/ink_memory.h"
32 #include "logging_internal.h"
33 #include "utils_internal.h"
34 #include "tscpp/api/noncopyable.h"
35 
36 using std::map;
37 using std::string;
38 using namespace atscppapi;
39 
40 /**
41  * @private
42  */
43 struct atscppapi::TransactionState : noncopyable {
44   TSHttpTxn txn_;
45   TSEvent event_; ///< Current event being dispatched.
46   std::list<TransactionPlugin *> plugins_;
47   TSMBuffer client_request_hdr_buf_;
48   TSMLoc client_request_hdr_loc_;
49   ClientRequest client_request_;
50   TSMBuffer server_request_hdr_buf_;
51   TSMLoc server_request_hdr_loc_;
52   Request server_request_;
53   TSMBuffer server_response_hdr_buf_;
54   TSMLoc server_response_hdr_loc_;
55   Response server_response_;
56   TSMBuffer client_response_hdr_buf_;
57   TSMLoc client_response_hdr_loc_;
58   Response client_response_;
59   TSMBuffer cached_response_hdr_buf_;
60   TSMLoc cached_response_hdr_loc_;
61   Response cached_response_;
62   TSMBuffer cached_request_hdr_buf_;
63   TSMLoc cached_request_hdr_loc_;
64   Request cached_request_;
65   map<string, std::shared_ptr<Transaction::ContextValue>> context_values_;
66 
TransactionStateatscppapi::TransactionState67   TransactionState(TSHttpTxn txn, TSMBuffer client_request_hdr_buf, TSMLoc client_request_hdr_loc)
68     : txn_(txn),
69       event_(TS_EVENT_NONE),
70       client_request_hdr_buf_(client_request_hdr_buf),
71       client_request_hdr_loc_(client_request_hdr_loc),
72       client_request_(txn, client_request_hdr_buf, client_request_hdr_loc),
73       server_request_hdr_buf_(nullptr),
74       server_request_hdr_loc_(nullptr),
75       server_response_hdr_buf_(nullptr),
76       server_response_hdr_loc_(nullptr),
77       client_response_hdr_buf_(nullptr),
78       client_response_hdr_loc_(nullptr),
79       cached_response_hdr_buf_(nullptr),
80       cached_response_hdr_loc_(nullptr),
81       cached_request_hdr_buf_(nullptr),
82       cached_request_hdr_loc_(nullptr){};
83 };
84 
Transaction(void * raw_txn)85 Transaction::Transaction(void *raw_txn)
86 {
87   TSHttpTxn txn = static_cast<TSHttpTxn>(raw_txn);
88   TSMBuffer hdr_buf;
89   TSMLoc hdr_loc;
90   (void)TSHttpTxnClientReqGet(txn, &hdr_buf, &hdr_loc);
91   if (!hdr_buf || !hdr_loc) {
92     LOG_ERROR("TSHttpTxnClientReqGet tshttptxn=%p returned a null hdr_buf=%p or hdr_loc=%p.", txn, hdr_buf, hdr_loc);
93   }
94 
95   state_ = new TransactionState(txn, hdr_buf, hdr_loc);
96   LOG_DEBUG("Transaction tshttptxn=%p constructing Transaction object %p, client req hdr_buf=%p, client req hdr_loc=%p", txn, this,
97             hdr_buf, hdr_loc);
98 }
99 
~Transaction()100 Transaction::~Transaction()
101 {
102   LOG_DEBUG("Transaction tshttptxn=%p destroying Transaction object %p", state_->txn_, this);
103   delete state_;
104 }
105 
106 void
setEvent(TSEvent event)107 Transaction::setEvent(TSEvent event)
108 {
109   state_->event_ = event;
110 }
111 
112 bool
configIntSet(TSOverridableConfigKey conf,int value)113 Transaction::configIntSet(TSOverridableConfigKey conf, int value)
114 {
115   return TS_SUCCESS == TSHttpTxnConfigIntSet(state_->txn_, conf, static_cast<TSMgmtInt>(value));
116 }
117 bool
configIntGet(TSOverridableConfigKey conf,int * value)118 Transaction::configIntGet(TSOverridableConfigKey conf, int *value)
119 {
120   return TS_SUCCESS == TSHttpTxnConfigIntGet(state_->txn_, conf, reinterpret_cast<TSMgmtInt *>(value));
121 }
122 
123 bool
configFloatSet(TSOverridableConfigKey conf,float value)124 Transaction::configFloatSet(TSOverridableConfigKey conf, float value)
125 {
126   return TS_SUCCESS == TSHttpTxnConfigFloatSet(state_->txn_, conf, static_cast<TSMgmtFloat>(value));
127 }
128 
129 bool
configFloatGet(TSOverridableConfigKey conf,float * value)130 Transaction::configFloatGet(TSOverridableConfigKey conf, float *value)
131 {
132   return TS_SUCCESS == TSHttpTxnConfigFloatGet(state_->txn_, conf, value);
133 }
134 
135 bool
configStringSet(TSOverridableConfigKey conf,std::string const & value)136 Transaction::configStringSet(TSOverridableConfigKey conf, std::string const &value)
137 {
138   return TS_SUCCESS == TSHttpTxnConfigStringSet(state_->txn_, conf, const_cast<TSMgmtString>(value.data()), value.length());
139 }
140 
141 bool
configStringGet(TSOverridableConfigKey conf,std::string & value)142 Transaction::configStringGet(TSOverridableConfigKey conf, std::string &value)
143 {
144   const char *svalue;
145   int length;
146   bool zret = TS_SUCCESS == TSHttpTxnConfigStringGet(state_->txn_, conf, &svalue, &length);
147   if (zret) {
148     value.assign(svalue, length);
149   } else {
150     value.clear();
151   }
152   return zret;
153 }
154 
155 bool
configFind(std::string const & name,TSOverridableConfigKey * conf,TSRecordDataType * type)156 Transaction::configFind(std::string const &name, TSOverridableConfigKey *conf, TSRecordDataType *type)
157 {
158   return TS_SUCCESS == TSHttpTxnConfigFind(name.data(), name.length(), conf, type);
159 }
160 
161 void
resume()162 Transaction::resume()
163 {
164   TSHttpTxnReenable(state_->txn_, static_cast<TSEvent>(TS_EVENT_HTTP_CONTINUE));
165 }
166 
167 void
error()168 Transaction::error()
169 {
170   LOG_DEBUG("Transaction tshttptxn=%p reenabling to error state", state_->txn_);
171   TSHttpTxnReenable(state_->txn_, static_cast<TSEvent>(TS_EVENT_HTTP_ERROR));
172 }
173 
174 void
error(const std::string & page)175 Transaction::error(const std::string &page)
176 {
177   setErrorBody(page);
178   error(); // finally, reenable with HTTP_ERROR
179 }
180 
181 void
setErrorBody(const std::string & page)182 Transaction::setErrorBody(const std::string &page)
183 {
184   LOG_DEBUG("Transaction tshttptxn=%p setting error body page length: %lu", state_->txn_, page.length());
185   char *body = static_cast<char *>(TSmalloc(page.length()));
186   memcpy(body, page.data(), page.length());
187   TSHttpTxnErrorBodySet(state_->txn_, body, page.length(), nullptr); // Default to text/html
188 }
189 
190 void
setErrorBody(const std::string & page,const std::string & mimetype)191 Transaction::setErrorBody(const std::string &page, const std::string &mimetype)
192 {
193   LOG_DEBUG("Transaction tshttptxn=%p setting error body page length: %lu", state_->txn_, page.length());
194   char *body = static_cast<char *>(TSmalloc(page.length()));
195   memcpy(body, page.data(), page.length());
196   TSHttpTxnErrorBodySet(state_->txn_, body, page.length(), TSstrdup(mimetype.c_str()));
197 }
198 
199 void
setStatusCode(HttpStatus code)200 Transaction::setStatusCode(HttpStatus code)
201 {
202   LOG_DEBUG("Transaction tshttptxn=%p setting status code: %d", state_->txn_, code);
203   TSHttpTxnStatusSet(state_->txn_, static_cast<TSHttpStatus>(code));
204 }
205 
206 bool
isInternalRequest() const207 Transaction::isInternalRequest() const
208 {
209   return (0 != TSHttpTxnIsInternal(state_->txn_));
210 }
211 
212 void *
getAtsHandle() const213 Transaction::getAtsHandle() const
214 {
215   return static_cast<void *>(state_->txn_);
216 }
217 
218 const std::list<atscppapi::TransactionPlugin *> &
getPlugins() const219 Transaction::getPlugins() const
220 {
221   return state_->plugins_;
222 }
223 
224 void
addPlugin(TransactionPlugin * plugin)225 Transaction::addPlugin(TransactionPlugin *plugin)
226 {
227   LOG_DEBUG("Transaction tshttptxn=%p registering new TransactionPlugin %p.", state_->txn_, plugin);
228   state_->plugins_.push_back(plugin);
229 }
230 
231 std::shared_ptr<Transaction::ContextValue>
getContextValue(const std::string & key)232 Transaction::getContextValue(const std::string &key)
233 {
234   std::shared_ptr<Transaction::ContextValue> return_context_value;
235   map<string, std::shared_ptr<Transaction::ContextValue>>::iterator iter = state_->context_values_.find(key);
236   if (iter != state_->context_values_.end()) {
237     return_context_value = iter->second;
238   }
239 
240   return return_context_value;
241 }
242 
243 void
setContextValue(const std::string & key,std::shared_ptr<Transaction::ContextValue> value)244 Transaction::setContextValue(const std::string &key, std::shared_ptr<Transaction::ContextValue> value)
245 {
246   state_->context_values_[key] = std::move(value);
247 }
248 
249 ClientRequest &
getClientRequest()250 Transaction::getClientRequest()
251 {
252   return state_->client_request_;
253   ;
254 }
255 
256 string
getEffectiveUrl()257 Transaction::getEffectiveUrl()
258 {
259   string ret_val;
260   int length = 0;
261   char *buf  = TSHttpTxnEffectiveUrlStringGet(state_->txn_, &length);
262   if (buf && length) {
263     ret_val.assign(buf, length);
264   }
265 
266   if (buf) {
267     TSfree(buf);
268   }
269 
270   return ret_val;
271 }
272 
273 bool
setCacheUrl(const string & cache_url)274 Transaction::setCacheUrl(const string &cache_url)
275 {
276   TSReturnCode res = TSCacheUrlSet(state_->txn_, cache_url.c_str(), cache_url.length());
277   return (res == TS_SUCCESS);
278 }
279 
280 void
setSkipRemapping(int flag)281 Transaction::setSkipRemapping(int flag)
282 {
283   TSSkipRemappingSet(state_->txn_, flag);
284 }
285 
286 const sockaddr *
getIncomingAddress() const287 Transaction::getIncomingAddress() const
288 {
289   return TSHttpTxnIncomingAddrGet(state_->txn_);
290 }
291 
292 const sockaddr *
getClientAddress() const293 Transaction::getClientAddress() const
294 {
295   return TSHttpTxnClientAddrGet(state_->txn_);
296 }
297 
298 const sockaddr *
getNextHopAddress() const299 Transaction::getNextHopAddress() const
300 {
301   return TSHttpTxnNextHopAddrGet(state_->txn_);
302 }
303 
304 const sockaddr *
getServerAddress() const305 Transaction::getServerAddress() const
306 {
307   return TSHttpTxnServerAddrGet(state_->txn_);
308 }
309 
310 bool
setServerAddress(const sockaddr * sockaddress)311 Transaction::setServerAddress(const sockaddr *sockaddress)
312 {
313   return TSHttpTxnServerAddrSet(state_->txn_, sockaddress) == TS_SUCCESS;
314 }
315 
316 bool
setIncomingPort(uint16_t port)317 Transaction::setIncomingPort(uint16_t port)
318 {
319   TSHttpTxnClientIncomingPortSet(state_->txn_, port);
320   return true; // In reality TSHttpTxnClientIncomingPortSet should return SUCCESS or ERROR.
321 }
322 
323 /*
324  * Note: The following methods cannot be attached to a Response
325  * object because that would require the Response object to
326  * know that it's a server or client response because of the
327  * TS C api which is TSHttpTxnServerRespBodyBytesGet.
328  */
329 size_t
getServerResponseBodySize()330 Transaction::getServerResponseBodySize()
331 {
332   return static_cast<size_t>(TSHttpTxnServerRespBodyBytesGet(state_->txn_));
333 }
334 
335 size_t
getServerResponseHeaderSize()336 Transaction::getServerResponseHeaderSize()
337 {
338   return static_cast<size_t>(TSHttpTxnServerRespHdrBytesGet(state_->txn_));
339 }
340 
341 size_t
getClientResponseBodySize()342 Transaction::getClientResponseBodySize()
343 {
344   return static_cast<size_t>(TSHttpTxnClientRespBodyBytesGet(state_->txn_));
345 }
346 
347 size_t
getClientResponseHeaderSize()348 Transaction::getClientResponseHeaderSize()
349 {
350   return static_cast<size_t>(TSHttpTxnClientRespHdrBytesGet(state_->txn_));
351 }
352 
353 void
setTimeout(Transaction::TimeoutType type,int time_ms)354 Transaction::setTimeout(Transaction::TimeoutType type, int time_ms)
355 {
356   switch (type) {
357   case TIMEOUT_DNS:
358     TSHttpTxnDNSTimeoutSet(state_->txn_, time_ms);
359     break;
360   case TIMEOUT_CONNECT:
361     TSHttpTxnConnectTimeoutSet(state_->txn_, time_ms);
362     break;
363   case TIMEOUT_NO_ACTIVITY:
364     TSHttpTxnNoActivityTimeoutSet(state_->txn_, time_ms);
365     break;
366   case TIMEOUT_ACTIVE:
367     TSHttpTxnActiveTimeoutSet(state_->txn_, time_ms);
368     break;
369   default:
370     break;
371   }
372 }
373 
374 Transaction::CacheStatus
getCacheStatus()375 Transaction::getCacheStatus()
376 {
377   int obj_status = TS_ERROR;
378 
379   if (TSHttpTxnCacheLookupStatusGet(state_->txn_, &obj_status) == TS_ERROR) {
380     return CACHE_LOOKUP_NONE;
381   }
382 
383   switch (obj_status) {
384   case TS_CACHE_LOOKUP_MISS:
385     return CACHE_LOOKUP_MISS;
386   case TS_CACHE_LOOKUP_HIT_STALE:
387     return CACHE_LOOKUP_HIT_STALE;
388   case TS_CACHE_LOOKUP_HIT_FRESH:
389     return CACHE_LOOKUP_HIT_FRESH;
390   case TS_CACHE_LOOKUP_SKIPPED:
391     return CACHE_LOOKUP_SKIPPED;
392   default:
393     return CACHE_LOOKUP_NONE;
394   }
395 }
396 
397 void
redirectTo(std::string const & url)398 Transaction::redirectTo(std::string const &url)
399 {
400   // Must re-alloc the string locally because ownership is transferred to the transaction.
401   char *const buffer = static_cast<char *>(TSmalloc(url.size() + 1));
402   memcpy(buffer, url.c_str(), url.size());
403   buffer[url.size()] = '\0';
404   TSHttpTxnRedirectUrlSet(state_->txn_, buffer, url.size());
405 }
406 
407 namespace
408 {
409 /**
410  * initializeHandles is a convenience functor that takes a pointer to a TS Function that
411  * will return the TSMBuffer and TSMLoc for a given server request/response or client/request response
412  *
413  * @param constructor takes a function pointer of type GetterFunction
414  * @param txn a TSHttpTxn
415  * @param hdr_buf the address where the hdr buf will be stored
416  * @param hdr_loc the address where the mem loc will be stored
417  * @param name name of the entity - used for logging
418  */
419 class initializeHandles
420 {
421 public:
422   using GetterFunction = TSReturnCode (*)(TSHttpTxn, TSMBuffer *, TSMLoc *);
initializeHandles(GetterFunction getter)423   initializeHandles(GetterFunction getter) : getter_(getter) {}
424   bool
operator ()(TSHttpTxn txn,TSMBuffer & hdr_buf,TSMLoc & hdr_loc,const char * handles_name)425   operator()(TSHttpTxn txn, TSMBuffer &hdr_buf, TSMLoc &hdr_loc, const char *handles_name)
426   {
427     hdr_buf = nullptr;
428     hdr_loc = nullptr;
429     if (getter_(txn, &hdr_buf, &hdr_loc) == TS_SUCCESS) {
430       return true;
431     } else {
432       LOG_ERROR("Could not get %s", handles_name);
433     }
434     return false;
435   }
436 
437 private:
438   GetterFunction getter_;
439 };
440 
441 } // anonymous namespace
442 
443 Request &
getServerRequest()444 Transaction::getServerRequest()
445 {
446   static initializeHandles initializeServerRequestHandles(TSHttpTxnServerReqGet);
447   if (nullptr == state_->server_request_hdr_buf_) {
448     initializeServerRequestHandles(state_->txn_, state_->server_request_hdr_buf_, state_->server_request_hdr_loc_,
449                                    "server request");
450     LOG_DEBUG("Initializing server request, event %d", state_->event_);
451     state_->server_request_.init(state_->server_request_hdr_buf_, state_->server_request_hdr_loc_);
452   }
453   return state_->server_request_;
454 }
455 
456 Response &
getServerResponse()457 Transaction::getServerResponse()
458 {
459   static initializeHandles initializeServerResponseHandles(TSHttpTxnServerRespGet);
460   if (nullptr == state_->server_response_hdr_buf_) {
461     initializeServerResponseHandles(state_->txn_, state_->server_response_hdr_buf_, state_->server_response_hdr_loc_,
462                                     "server response");
463     LOG_DEBUG("Initializing server response, event %d", state_->event_);
464     state_->server_response_.init(state_->server_response_hdr_buf_, state_->server_response_hdr_loc_);
465   }
466   return state_->server_response_;
467 }
468 
469 Response &
getClientResponse()470 Transaction::getClientResponse()
471 {
472   static initializeHandles initializeClientResponseHandles(TSHttpTxnClientRespGet);
473   if (nullptr == state_->client_response_hdr_buf_) {
474     initializeClientResponseHandles(state_->txn_, state_->client_response_hdr_buf_, state_->client_response_hdr_loc_,
475                                     "client response");
476     LOG_DEBUG("Initializing client response, event %d", state_->event_);
477     state_->client_response_.init(state_->client_response_hdr_buf_, state_->client_response_hdr_loc_);
478   }
479   return state_->client_response_;
480 }
481 
482 Request &
getCachedRequest()483 Transaction::getCachedRequest()
484 {
485   static initializeHandles initializeCachedRequestHandles(TSHttpTxnCachedReqGet);
486 
487   if (state_->event_ == TS_EVENT_HTTP_TXN_CLOSE) {
488     // CachedRequest is destroyed in tunnel_handler_cache_read
489     state_->cached_request_.reset();
490     LOG_DEBUG("Reset cached request, event %d", state_->event_);
491   } else {
492     if (nullptr == state_->cached_request_hdr_buf_) {
493       initializeCachedRequestHandles(state_->txn_, state_->cached_request_hdr_buf_, state_->cached_request_hdr_loc_,
494                                      "cached request");
495       LOG_DEBUG("Initializing cached request, event %d", state_->event_);
496       state_->cached_request_.init(state_->cached_request_hdr_buf_, state_->cached_request_hdr_loc_);
497     }
498   }
499   return state_->cached_request_;
500 }
501 
502 Response &
getCachedResponse()503 Transaction::getCachedResponse()
504 {
505   static initializeHandles initializeCachedResponseHandles(TSHttpTxnCachedRespGet);
506   if (nullptr == state_->cached_response_hdr_buf_) {
507     initializeCachedResponseHandles(state_->txn_, state_->cached_response_hdr_buf_, state_->cached_response_hdr_loc_,
508                                     "cached response");
509     LOG_DEBUG("Initializing cached response, event %d", state_->event_);
510     state_->cached_response_.init(state_->cached_response_hdr_buf_, state_->cached_response_hdr_loc_);
511   }
512   return state_->cached_response_;
513 }
514 
515 void
resetHandles()516 Transaction::resetHandles()
517 {
518   state_->cached_request_hdr_buf_  = nullptr;
519   state_->cached_request_hdr_loc_  = nullptr;
520   state_->cached_response_hdr_buf_ = nullptr;
521   state_->cached_response_hdr_loc_ = nullptr;
522 
523   state_->client_response_hdr_buf_ = nullptr;
524   state_->client_response_hdr_loc_ = nullptr;
525 
526   state_->server_request_hdr_buf_  = nullptr;
527   state_->server_request_hdr_loc_  = nullptr;
528   state_->server_response_hdr_buf_ = nullptr;
529   state_->server_response_hdr_loc_ = nullptr;
530 }
531