| /** |
| 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 "atscppapi/Transaction.h" |
| #include <cstdlib> |
| #include <cstring> |
| #include <map> |
| #include <string> |
| #include <ts/ts.h> |
| #include "atscppapi/shared_ptr.h" |
| #include "logging_internal.h" |
| #include "utils_internal.h" |
| #include "atscppapi/noncopyable.h" |
| |
| using std::map; |
| using std::string; |
| using namespace atscppapi; |
| |
| /** |
| * @private |
| */ |
| struct atscppapi::TransactionState: noncopyable { |
| TSHttpTxn txn_; |
| 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_; |
| map<string, shared_ptr<Transaction::ContextValue> > context_values_; |
| |
| TransactionState(TSHttpTxn txn, TSMBuffer client_request_hdr_buf, TSMLoc client_request_hdr_loc) |
| : txn_(txn), 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_(NULL), server_request_hdr_loc_(NULL), |
| server_response_hdr_buf_(NULL), server_response_hdr_loc_(NULL), |
| client_response_hdr_buf_(NULL), client_response_hdr_loc_(NULL) |
| { }; |
| }; |
| |
| Transaction::Transaction(void *raw_txn) { |
| TSHttpTxn txn = static_cast<TSHttpTxn>(raw_txn); |
| TSMBuffer hdr_buf; |
| TSMLoc hdr_loc; |
| 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_ = new 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); |
| static const TSMLoc NULL_PARENT_LOC = NULL; |
| TSHandleMLocRelease(state_->client_request_hdr_buf_, NULL_PARENT_LOC, state_->client_request_hdr_loc_); |
| if (state_->server_request_hdr_buf_ && state_->server_request_hdr_loc_) { |
| LOG_DEBUG("Releasing server request"); |
| TSHandleMLocRelease(state_->server_request_hdr_buf_, NULL_PARENT_LOC, state_->server_request_hdr_loc_); |
| } |
| if (state_->server_response_hdr_buf_ && state_->server_response_hdr_loc_) { |
| LOG_DEBUG("Releasing server response"); |
| TSHandleMLocRelease(state_->server_response_hdr_buf_, NULL_PARENT_LOC, state_->server_response_hdr_loc_); |
| } |
| if (state_->client_response_hdr_buf_ && state_->client_response_hdr_loc_) { |
| LOG_DEBUG("Releasing client response"); |
| TSHandleMLocRelease(state_->client_response_hdr_buf_, NULL_PARENT_LOC, state_->client_response_hdr_loc_); |
| } |
| delete state_; |
| } |
| |
| 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: %s", state_->txn_, page.c_str()); |
| char *res_bdy = static_cast<char*>(TSmalloc(page.length() + 1)); |
| strncpy(res_bdy, page.c_str(), page.length()); |
| res_bdy[page.length()] = '\0'; |
| |
| std::string str_content_type = "text/html"; |
| char *content_type = static_cast<char*>(TSmalloc(str_content_type.length() + 1)); |
| strncpy(content_type, str_content_type.c_str(), str_content_type.length()); |
| content_type[str_content_type.length()] = '\0'; |
| |
| TSHttpTxnErrorBodySet(state_->txn_, res_bdy, page.length(), content_type); |
| } |
| |
| bool Transaction::isInternalRequest() const { |
| return TSHttpIsInternalRequest(state_->txn_) == TS_SUCCESS; |
| } |
| |
| 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); |
| } |
| |
| shared_ptr<Transaction::ContextValue> Transaction::getContextValue(const std::string &key) { |
| shared_ptr<Transaction::ContextValue> return_context_value; |
| map<string, 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, shared_ptr<Transaction::ContextValue> value) { |
| state_->context_values_[key] = value; |
| } |
| |
| ClientRequest &Transaction::getClientRequest() { |
| return state_->client_request_; |
| } |
| |
| Request &Transaction::getServerRequest() { |
| return state_->server_request_; |
| } |
| |
| Response &Transaction::getServerResponse() { |
| return state_->server_response_; |
| } |
| |
| Response &Transaction::getClientResponse() { |
| return state_->client_response_; |
| } |
| |
| 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); |
| } |
| |
| 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; |
| } |
| } |
| |
| namespace { |
| |
| /** |
| * initializeHandles is a convinience 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 storeds |
| * @param name name of the entity - used for logging |
| */ |
| class initializeHandles { |
| public: |
| typedef TSReturnCode (*GetterFunction)(TSHttpTxn, TSMBuffer *, TSMLoc *); |
| initializeHandles(GetterFunction getter) : getter_(getter) { } |
| bool operator()(TSHttpTxn txn, TSMBuffer &hdr_buf, TSMLoc &hdr_loc, const char *handles_name) { |
| if (!hdr_buf && !hdr_loc) { |
| if (getter_(txn, &hdr_buf, &hdr_loc) == TS_SUCCESS) { |
| return true; |
| } |
| else { |
| LOG_ERROR("Could not get %s", handles_name); |
| } |
| } |
| else { |
| LOG_ERROR("%s already initialized", handles_name); |
| } |
| return false; |
| } |
| private: |
| GetterFunction getter_; |
| }; |
| |
| } // anonymous namespace |
| |
| void Transaction::initServerRequest() { |
| static initializeHandles initializeServerRequestHandles(TSHttpTxnServerReqGet); |
| if (initializeServerRequestHandles(state_->txn_, state_->server_request_hdr_buf_, |
| state_->server_request_hdr_loc_, "server request")) { |
| LOG_DEBUG("Initializing server request"); |
| state_->server_request_.init(state_->server_request_hdr_buf_, state_->server_request_hdr_loc_); |
| } |
| } |
| |
| void Transaction::initServerResponse() { |
| static initializeHandles initializeServerResponseHandles(TSHttpTxnServerRespGet); |
| if (initializeServerResponseHandles(state_->txn_, state_->server_response_hdr_buf_, |
| state_->server_response_hdr_loc_, "server response")) { |
| LOG_DEBUG("Initializing server response"); |
| state_->server_response_.init(state_->server_response_hdr_buf_, state_->server_response_hdr_loc_); |
| } |
| } |
| |
| void Transaction::initClientResponse() { |
| static initializeHandles initializeClientResponseHandles(TSHttpTxnClientRespGet); |
| if (initializeClientResponseHandles(state_->txn_, state_->client_response_hdr_buf_, |
| state_->client_response_hdr_loc_, "client response")) { |
| LOG_DEBUG("Initializing client response"); |
| state_->client_response_.init(state_->client_response_hdr_buf_, state_->client_response_hdr_loc_); |
| } |
| } |