| /** | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you under the Apache License, Version 2.0 (the | |
| "License"); you may not use this file except in compliance | |
| with the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| /** | |
| * @file Transaction.cc | |
| */ | |
| #include <memory> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <map> | |
| #include <string> | |
| #include <utility> | |
| #include "tscpp/api/Transaction.h" | |
| #include "tscore/ink_memory.h" | |
| #include "logging_internal.h" | |
| #include "utils_internal.h" | |
| #include "tscpp/api/noncopyable.h" | |
| using std::map; | |
| using std::string; | |
| using namespace atscppapi; | |
| /** | |
| * @private | |
| */ | |
| struct atscppapi::TransactionState : noncopyable { | |
| TSHttpTxn txn_; | |
| TSEvent event_; ///< Current event being dispatched. | |
| std::list<TransactionPlugin *> plugins_; | |
| TSMBuffer client_request_hdr_buf_; | |
| TSMLoc client_request_hdr_loc_; | |
| ClientRequest client_request_; | |
| TSMBuffer server_request_hdr_buf_; | |
| TSMLoc server_request_hdr_loc_; | |
| Request server_request_; | |
| TSMBuffer server_response_hdr_buf_; | |
| TSMLoc server_response_hdr_loc_; | |
| Response server_response_; | |
| TSMBuffer client_response_hdr_buf_; | |
| TSMLoc client_response_hdr_loc_; | |
| Response client_response_; | |
| TSMBuffer cached_response_hdr_buf_; | |
| TSMLoc cached_response_hdr_loc_; | |
| Response cached_response_; | |
| TSMBuffer cached_request_hdr_buf_; | |
| TSMLoc cached_request_hdr_loc_; | |
| Request cached_request_; | |
| map<string, std::shared_ptr<Transaction::ContextValue>> context_values_; | |
| TransactionState(TSHttpTxn txn, TSMBuffer client_request_hdr_buf, TSMLoc client_request_hdr_loc) | |
| : txn_(txn), | |
| event_(TS_EVENT_NONE), | |
| client_request_hdr_buf_(client_request_hdr_buf), | |
| client_request_hdr_loc_(client_request_hdr_loc), | |
| client_request_(txn, client_request_hdr_buf, client_request_hdr_loc), | |
| server_request_hdr_buf_(nullptr), | |
| server_request_hdr_loc_(nullptr), | |
| server_response_hdr_buf_(nullptr), | |
| server_response_hdr_loc_(nullptr), | |
| client_response_hdr_buf_(nullptr), | |
| client_response_hdr_loc_(nullptr), | |
| cached_response_hdr_buf_(nullptr), | |
| cached_response_hdr_loc_(nullptr), | |
| cached_request_hdr_buf_(nullptr), | |
| cached_request_hdr_loc_(nullptr){}; | |
| }; | |
| Transaction::Transaction(void *raw_txn) | |
| { | |
| TSHttpTxn txn = static_cast<TSHttpTxn>(raw_txn); | |
| TSMBuffer hdr_buf; | |
| TSMLoc hdr_loc; | |
| (void)TSHttpTxnClientReqGet(txn, &hdr_buf, &hdr_loc); | |
| if (!hdr_buf || !hdr_loc) { | |
| LOG_ERROR("TSHttpTxnClientReqGet tshttptxn=%p returned a null hdr_buf=%p or hdr_loc=%p.", txn, hdr_buf, hdr_loc); | |
| } | |
| state_ = std::make_unique<TransactionState>(txn, hdr_buf, hdr_loc); | |
| LOG_DEBUG("Transaction tshttptxn=%p constructing Transaction object %p, client req hdr_buf=%p, client req hdr_loc=%p", txn, this, | |
| hdr_buf, hdr_loc); | |
| } | |
| Transaction::~Transaction() | |
| { | |
| LOG_DEBUG("Transaction tshttptxn=%p destroying Transaction object %p", state_->txn_, this); | |
| } | |
| void | |
| Transaction::setEvent(TSEvent event) | |
| { | |
| state_->event_ = event; | |
| } | |
| bool | |
| Transaction::configIntSet(TSOverridableConfigKey conf, int value) | |
| { | |
| return TS_SUCCESS == TSHttpTxnConfigIntSet(state_->txn_, conf, static_cast<TSMgmtInt>(value)); | |
| } | |
| bool | |
| Transaction::configIntGet(TSOverridableConfigKey conf, int *value) | |
| { | |
| return TS_SUCCESS == TSHttpTxnConfigIntGet(state_->txn_, conf, reinterpret_cast<TSMgmtInt *>(value)); | |
| } | |
| bool | |
| Transaction::configFloatSet(TSOverridableConfigKey conf, float value) | |
| { | |
| return TS_SUCCESS == TSHttpTxnConfigFloatSet(state_->txn_, conf, static_cast<TSMgmtFloat>(value)); | |
| } | |
| bool | |
| Transaction::configFloatGet(TSOverridableConfigKey conf, float *value) | |
| { | |
| return TS_SUCCESS == TSHttpTxnConfigFloatGet(state_->txn_, conf, value); | |
| } | |
| bool | |
| Transaction::configStringSet(TSOverridableConfigKey conf, std::string const &value) | |
| { | |
| return TS_SUCCESS == TSHttpTxnConfigStringSet(state_->txn_, conf, const_cast<TSMgmtString>(value.data()), value.length()); | |
| } | |
| bool | |
| Transaction::configStringGet(TSOverridableConfigKey conf, std::string &value) | |
| { | |
| const char *svalue; | |
| int length; | |
| bool zret = TS_SUCCESS == TSHttpTxnConfigStringGet(state_->txn_, conf, &svalue, &length); | |
| if (zret) { | |
| value.assign(svalue, length); | |
| } else { | |
| value.clear(); | |
| } | |
| return zret; | |
| } | |
| bool | |
| Transaction::configFind(std::string const &name, TSOverridableConfigKey *conf, TSRecordDataType *type) | |
| { | |
| return TS_SUCCESS == TSHttpTxnConfigFind(name.data(), name.length(), conf, type); | |
| } | |
| void | |
| Transaction::resume() | |
| { | |
| TSHttpTxnReenable(state_->txn_, static_cast<TSEvent>(TS_EVENT_HTTP_CONTINUE)); | |
| } | |
| void | |
| Transaction::error() | |
| { | |
| LOG_DEBUG("Transaction tshttptxn=%p reenabling to error state", state_->txn_); | |
| TSHttpTxnReenable(state_->txn_, static_cast<TSEvent>(TS_EVENT_HTTP_ERROR)); | |
| } | |
| void | |
| Transaction::error(const std::string &page) | |
| { | |
| setErrorBody(page); | |
| error(); // finally, reenable with HTTP_ERROR | |
| } | |
| void | |
| Transaction::setErrorBody(const std::string &page) | |
| { | |
| LOG_DEBUG("Transaction tshttptxn=%p setting error body page length: %lu", state_->txn_, page.length()); | |
| char *body = static_cast<char *>(TSmalloc(page.length())); | |
| memcpy(body, page.data(), page.length()); | |
| TSHttpTxnErrorBodySet(state_->txn_, body, page.length(), nullptr); // Default to text/html | |
| } | |
| void | |
| Transaction::setErrorBody(const std::string &page, const std::string &mimetype) | |
| { | |
| LOG_DEBUG("Transaction tshttptxn=%p setting error body page length: %lu", state_->txn_, page.length()); | |
| char *body = static_cast<char *>(TSmalloc(page.length())); | |
| memcpy(body, page.data(), page.length()); | |
| TSHttpTxnErrorBodySet(state_->txn_, body, page.length(), TSstrdup(mimetype.c_str())); | |
| } | |
| void | |
| Transaction::setStatusCode(HttpStatus code) | |
| { | |
| LOG_DEBUG("Transaction tshttptxn=%p setting status code: %d", state_->txn_, code); | |
| TSHttpTxnStatusSet(state_->txn_, static_cast<TSHttpStatus>(code), "tscpp"); | |
| } | |
| bool | |
| Transaction::isInternalRequest() const | |
| { | |
| return (0 != TSHttpTxnIsInternal(state_->txn_)); | |
| } | |
| void * | |
| Transaction::getAtsHandle() const | |
| { | |
| return static_cast<void *>(state_->txn_); | |
| } | |
| const std::list<atscppapi::TransactionPlugin *> & | |
| Transaction::getPlugins() const | |
| { | |
| return state_->plugins_; | |
| } | |
| void | |
| Transaction::addPlugin(TransactionPlugin *plugin) | |
| { | |
| LOG_DEBUG("Transaction tshttptxn=%p registering new TransactionPlugin %p.", state_->txn_, plugin); | |
| state_->plugins_.push_back(plugin); | |
| } | |
| std::shared_ptr<Transaction::ContextValue> | |
| Transaction::getContextValue(const std::string &key) | |
| { | |
| std::shared_ptr<Transaction::ContextValue> return_context_value; | |
| map<string, std::shared_ptr<Transaction::ContextValue>>::iterator iter = state_->context_values_.find(key); | |
| if (iter != state_->context_values_.end()) { | |
| return_context_value = iter->second; | |
| } | |
| return return_context_value; | |
| } | |
| void | |
| Transaction::setContextValue(const std::string &key, std::shared_ptr<Transaction::ContextValue> value) | |
| { | |
| state_->context_values_[key] = std::move(value); | |
| } | |
| ClientRequest & | |
| Transaction::getClientRequest() | |
| { | |
| return state_->client_request_; | |
| ; | |
| } | |
| string | |
| Transaction::getEffectiveUrl() | |
| { | |
| string ret_val; | |
| int length = 0; | |
| char *buf = TSHttpTxnEffectiveUrlStringGet(state_->txn_, &length); | |
| if (buf && length) { | |
| ret_val.assign(buf, length); | |
| } | |
| if (buf) { | |
| TSfree(buf); | |
| } | |
| return ret_val; | |
| } | |
| bool | |
| Transaction::setCacheUrl(const string &cache_url) | |
| { | |
| TSReturnCode res = TSCacheUrlSet(state_->txn_, cache_url.c_str(), cache_url.length()); | |
| return (res == TS_SUCCESS); | |
| } | |
| void | |
| Transaction::setSkipRemapping(int flag) | |
| { | |
| TSHttpTxnCntlSet(state_->txn_, TS_HTTP_CNTL_SKIP_REMAPPING, flag); | |
| } | |
| const sockaddr * | |
| Transaction::getIncomingAddress() const | |
| { | |
| return TSHttpTxnIncomingAddrGet(state_->txn_); | |
| } | |
| const sockaddr * | |
| Transaction::getClientAddress() const | |
| { | |
| return TSHttpTxnClientAddrGet(state_->txn_); | |
| } | |
| const sockaddr * | |
| Transaction::getNextHopAddress() const | |
| { | |
| return TSHttpTxnNextHopAddrGet(state_->txn_); | |
| } | |
| const sockaddr * | |
| Transaction::getServerAddress() const | |
| { | |
| return TSHttpTxnServerAddrGet(state_->txn_); | |
| } | |
| bool | |
| Transaction::setServerAddress(const sockaddr *sockaddress) | |
| { | |
| return TSHttpTxnServerAddrSet(state_->txn_, sockaddress) == TS_SUCCESS; | |
| } | |
| bool | |
| Transaction::setIncomingPort(uint16_t port) | |
| { | |
| TSHttpTxnClientIncomingPortSet(state_->txn_, port); | |
| return true; // In reality TSHttpTxnClientIncomingPortSet should return SUCCESS or ERROR. | |
| } | |
| /* | |
| * Note: The following methods cannot be attached to a Response | |
| * object because that would require the Response object to | |
| * know that it's a server or client response because of the | |
| * TS C api which is TSHttpTxnServerRespBodyBytesGet. | |
| */ | |
| size_t | |
| Transaction::getServerResponseBodySize() | |
| { | |
| return static_cast<size_t>(TSHttpTxnServerRespBodyBytesGet(state_->txn_)); | |
| } | |
| size_t | |
| Transaction::getServerResponseHeaderSize() | |
| { | |
| return static_cast<size_t>(TSHttpTxnServerRespHdrBytesGet(state_->txn_)); | |
| } | |
| size_t | |
| Transaction::getClientResponseBodySize() | |
| { | |
| return static_cast<size_t>(TSHttpTxnClientRespBodyBytesGet(state_->txn_)); | |
| } | |
| size_t | |
| Transaction::getClientResponseHeaderSize() | |
| { | |
| return static_cast<size_t>(TSHttpTxnClientRespHdrBytesGet(state_->txn_)); | |
| } | |
| void | |
| Transaction::setTimeout(Transaction::TimeoutType type, int time_ms) | |
| { | |
| switch (type) { | |
| case TIMEOUT_DNS: | |
| TSHttpTxnDNSTimeoutSet(state_->txn_, time_ms); | |
| break; | |
| case TIMEOUT_CONNECT: | |
| TSHttpTxnConnectTimeoutSet(state_->txn_, time_ms); | |
| break; | |
| case TIMEOUT_NO_ACTIVITY: | |
| TSHttpTxnNoActivityTimeoutSet(state_->txn_, time_ms); | |
| break; | |
| case TIMEOUT_ACTIVE: | |
| TSHttpTxnActiveTimeoutSet(state_->txn_, time_ms); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| Transaction::CacheStatus | |
| Transaction::getCacheStatus() | |
| { | |
| int obj_status = TS_ERROR; | |
| if (TSHttpTxnCacheLookupStatusGet(state_->txn_, &obj_status) == TS_ERROR) { | |
| return CACHE_LOOKUP_NONE; | |
| } | |
| switch (obj_status) { | |
| case TS_CACHE_LOOKUP_MISS: | |
| return CACHE_LOOKUP_MISS; | |
| case TS_CACHE_LOOKUP_HIT_STALE: | |
| return CACHE_LOOKUP_HIT_STALE; | |
| case TS_CACHE_LOOKUP_HIT_FRESH: | |
| return CACHE_LOOKUP_HIT_FRESH; | |
| case TS_CACHE_LOOKUP_SKIPPED: | |
| return CACHE_LOOKUP_SKIPPED; | |
| default: | |
| return CACHE_LOOKUP_NONE; | |
| } | |
| } | |
| void | |
| Transaction::redirectTo(std::string const &url) | |
| { | |
| // Must re-alloc the string locally because ownership is transferred to the transaction. | |
| char *const buffer = static_cast<char *>(TSmalloc(url.size() + 1)); | |
| memcpy(buffer, url.c_str(), url.size()); | |
| buffer[url.size()] = '\0'; | |
| TSHttpTxnRedirectUrlSet(state_->txn_, buffer, url.size()); | |
| } | |
| namespace | |
| { | |
| /** | |
| * initializeHandles is a convenience functor that takes a pointer to a TS Function that | |
| * will return the TSMBuffer and TSMLoc for a given server request/response or client/request response | |
| * | |
| * @param constructor takes a function pointer of type GetterFunction | |
| * @param txn a TSHttpTxn | |
| * @param hdr_buf the address where the hdr buf will be stored | |
| * @param hdr_loc the address where the mem loc will be stored | |
| * @param name name of the entity - used for logging | |
| */ | |
| class initializeHandles | |
| { | |
| public: | |
| using GetterFunction = TSReturnCode (*)(TSHttpTxn, TSMBuffer *, TSMLoc *); | |
| initializeHandles(GetterFunction getter) : getter_(getter) {} | |
| bool | |
| operator()(TSHttpTxn txn, TSMBuffer &hdr_buf, TSMLoc &hdr_loc, const char *handles_name) | |
| { | |
| hdr_buf = nullptr; | |
| hdr_loc = nullptr; | |
| if (getter_(txn, &hdr_buf, &hdr_loc) == TS_SUCCESS) { | |
| return true; | |
| } else { | |
| LOG_ERROR("Could not get %s", handles_name); | |
| } | |
| return false; | |
| } | |
| private: | |
| GetterFunction getter_; | |
| }; | |
| } // anonymous namespace | |
| Request & | |
| Transaction::getServerRequest() | |
| { | |
| static initializeHandles initializeServerRequestHandles(TSHttpTxnServerReqGet); | |
| if (nullptr == state_->server_request_hdr_buf_) { | |
| initializeServerRequestHandles(state_->txn_, state_->server_request_hdr_buf_, state_->server_request_hdr_loc_, | |
| "server request"); | |
| LOG_DEBUG("Initializing server request, event %d", state_->event_); | |
| state_->server_request_.init(state_->server_request_hdr_buf_, state_->server_request_hdr_loc_); | |
| } | |
| return state_->server_request_; | |
| } | |
| Response & | |
| Transaction::getServerResponse() | |
| { | |
| static initializeHandles initializeServerResponseHandles(TSHttpTxnServerRespGet); | |
| if (nullptr == state_->server_response_hdr_buf_) { | |
| initializeServerResponseHandles(state_->txn_, state_->server_response_hdr_buf_, state_->server_response_hdr_loc_, | |
| "server response"); | |
| LOG_DEBUG("Initializing server response, event %d", state_->event_); | |
| state_->server_response_.init(state_->server_response_hdr_buf_, state_->server_response_hdr_loc_); | |
| } | |
| return state_->server_response_; | |
| } | |
| Response & | |
| Transaction::getClientResponse() | |
| { | |
| static initializeHandles initializeClientResponseHandles(TSHttpTxnClientRespGet); | |
| if (nullptr == state_->client_response_hdr_buf_) { | |
| initializeClientResponseHandles(state_->txn_, state_->client_response_hdr_buf_, state_->client_response_hdr_loc_, | |
| "client response"); | |
| LOG_DEBUG("Initializing client response, event %d", state_->event_); | |
| state_->client_response_.init(state_->client_response_hdr_buf_, state_->client_response_hdr_loc_); | |
| } | |
| return state_->client_response_; | |
| } | |
| Request & | |
| Transaction::getCachedRequest() | |
| { | |
| static initializeHandles initializeCachedRequestHandles(TSHttpTxnCachedReqGet); | |
| if (state_->event_ == TS_EVENT_HTTP_TXN_CLOSE) { | |
| // CachedRequest is destroyed in tunnel_handler_cache_read | |
| state_->cached_request_.reset(); | |
| LOG_DEBUG("Reset cached request, event %d", state_->event_); | |
| } else { | |
| if (nullptr == state_->cached_request_hdr_buf_) { | |
| initializeCachedRequestHandles(state_->txn_, state_->cached_request_hdr_buf_, state_->cached_request_hdr_loc_, | |
| "cached request"); | |
| LOG_DEBUG("Initializing cached request, event %d", state_->event_); | |
| state_->cached_request_.init(state_->cached_request_hdr_buf_, state_->cached_request_hdr_loc_); | |
| } | |
| } | |
| return state_->cached_request_; | |
| } | |
| Response & | |
| Transaction::getCachedResponse() | |
| { | |
| static initializeHandles initializeCachedResponseHandles(TSHttpTxnCachedRespGet); | |
| if (nullptr == state_->cached_response_hdr_buf_) { | |
| initializeCachedResponseHandles(state_->txn_, state_->cached_response_hdr_buf_, state_->cached_response_hdr_loc_, | |
| "cached response"); | |
| LOG_DEBUG("Initializing cached response, event %d", state_->event_); | |
| state_->cached_response_.init(state_->cached_response_hdr_buf_, state_->cached_response_hdr_loc_); | |
| } | |
| return state_->cached_response_; | |
| } | |
| void | |
| Transaction::resetHandles() | |
| { | |
| state_->cached_request_hdr_buf_ = nullptr; | |
| state_->cached_request_hdr_loc_ = nullptr; | |
| state_->cached_response_hdr_buf_ = nullptr; | |
| state_->cached_response_hdr_loc_ = nullptr; | |
| state_->client_response_hdr_buf_ = nullptr; | |
| state_->client_response_hdr_loc_ = nullptr; | |
| state_->server_request_hdr_buf_ = nullptr; | |
| state_->server_request_hdr_loc_ = nullptr; | |
| state_->server_response_hdr_buf_ = nullptr; | |
| state_->server_response_hdr_loc_ = nullptr; | |
| } |