trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bc...@apache.org
Subject svn commit: r1199388 [2/5] - in /trafficserver/plugins/trunk/esi: ./ fetcher/ lib/ test/ test_helper/
Date Tue, 08 Nov 2011 19:07:22 GMT
Added: trafficserver/plugins/trunk/esi/lib/EsiProcessor.cc
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/EsiProcessor.cc?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/EsiProcessor.cc (added)
+++ trafficserver/plugins/trunk/esi/lib/EsiProcessor.cc Tue Nov  8 19:07:20 2011
@@ -0,0 +1,577 @@
+#include "EsiProcessor.h"
+#include "Stats.h"
+#include "FailureInfo.h"
+#include <ctype.h>
+
+using std::string;
+using namespace EsiLib;
+extern pthread_key_t threadKey;
+// this needs to be a fixed address as only the address is used for comparision
+const char *EsiProcessor::INCLUDE_DATA_ID_ATTR = reinterpret_cast<const char *>(0xbeadface);
+
+#define FAILURE_INFO_TAG "plugin_esi_failureInfo"
+
+
+EsiProcessor::EsiProcessor(const char *debug_tag, const char *parser_debug_tag,
+                           const char *expression_debug_tag,
+                           ComponentBase::Debug debug_func, ComponentBase::Error error_func,
+                           HttpDataFetcher &fetcher, Variables &variables,
+                           const HandlerManager &handler_mgr)
+  : ComponentBase(debug_tag, debug_func, error_func),
+    _curr_state(STOPPED),
+    _parser(parser_debug_tag, debug_func, error_func),
+    _n_prescanned_nodes(0),
+    _fetcher(fetcher), _esi_vars(variables),
+    _expression(expression_debug_tag, debug_func, error_func, _esi_vars), _n_try_blocks_processed(0),
+    _handler_manager(handler_mgr) {
+}
+
+bool
+EsiProcessor::start() {
+  if (_curr_state != STOPPED) {
+    _debugLog(_debug_tag.c_str(), "[%s] Implicit call to stop()", __FUNCTION__);
+    stop();
+  }
+  _curr_state = PARSING;
+  return true;
+}
+
+bool
+EsiProcessor::addParseData(const char *data, int data_len) {
+  if (_curr_state == ERRORED) {
+    return false;
+  }
+  if (_curr_state == STOPPED) {
+    _debugLog(_debug_tag.c_str(), "[%s] Implicit call to start()", __FUNCTION__);
+    start();
+  } else if (_curr_state != PARSING) {
+    _debugLog(_debug_tag.c_str(), "[%s] Can only parse in parse stage", __FUNCTION__);
+    return false;
+  }
+  
+  if (!_parser.parseChunk(data, _node_list, data_len)) {
+    _errorLog("[%s] Failed to parse chunk; Stopping processor...", __FUNCTION__);
+    error();
+    Stats::increment(Stats::N_PARSE_ERRS);
+    return false;
+  }
+  if (!_preprocess(_node_list, _n_prescanned_nodes)) {
+    _errorLog("[%s] Failed to preprocess parsed nodes; Stopping processor...", __FUNCTION__);
+    error();
+    return false;
+  }
+  return true;
+}
+
+bool
+EsiProcessor::completeParse(const char *data /* = 0 */, int data_len /* = -1 */) {
+  if (_curr_state == ERRORED) {
+    return false;
+  }
+  if (_curr_state == STOPPED) {
+    _debugLog(_debug_tag.c_str(), "[%s] Implicit call to start()", __FUNCTION__);
+    start();
+  } else if (_curr_state != PARSING) {
+    _debugLog(_debug_tag.c_str(), "[%s] Can only parse in parse stage", __FUNCTION__);
+    return false;
+  }
+
+  if (!_parser.completeParse(_node_list, data, data_len)) {
+    _errorLog("[%s] Couldn't parse ESI document", __FUNCTION__);
+    error();
+    Stats::increment(Stats::N_PARSE_ERRS);
+    return false;
+  }
+  return _handleParseComplete();
+}
+
+bool
+EsiProcessor::usePackedNodeList(const char *data, int data_len) {
+  if (_curr_state != STOPPED) {
+    _errorLog("[%s] Cannot use packed node list whilst processing other data", __FUNCTION__);
+    return false;
+  }
+  start();
+  if (!_node_list.unpack(data, data_len)) {
+    _errorLog("[%s] Could not unpack node list from provided data!", __FUNCTION__);
+    error();
+    return false;
+  }
+  return _handleParseComplete();
+}
+
+bool
+EsiProcessor::_handleParseComplete() {
+  if (_curr_state != PARSING) {
+    _debugLog(_debug_tag.c_str(), "[%s] Cannot handle parse complete in state %d", __FUNCTION__, _curr_state);
+    return false;
+  }
+  if (!_preprocess(_node_list, _n_prescanned_nodes)) {
+    _errorLog("[%s] Failed to preprocess parsed nodes; Stopping processor...", __FUNCTION__);
+    error();
+    return false;
+  }
+  for (IncludeHandlerMap::iterator map_iter = _include_handlers.begin();
+       map_iter != _include_handlers.end(); ++map_iter) {
+    map_iter->second->handleParseComplete();
+  }
+
+  _debugLog(_debug_tag.c_str(), "[%s] Parsed ESI document with %d nodes", __FUNCTION__, _node_list.size());
+  _curr_state = WAITING_TO_PROCESS;
+  
+  return true;
+}
+
+bool
+EsiProcessor::_getIncludeData(const DocNode &node, const char **content_ptr /* = 0 */,
+                              int *content_len_ptr /* = 0 */) {
+  if (node.type == DocNode::TYPE_INCLUDE) {
+    const Attribute &url = node.attr_list.front();
+    string raw_url(url.value, url.value_len);
+    StringHash::iterator iter = _include_urls.find(raw_url);
+    if (iter == _include_urls.end()) {
+      _errorLog("[%s] Data not requested for URL [%.*s]; no data to include",
+                __FUNCTION__, url.value_len, url.value);
+      return false;
+    }
+    const string &processed_url = iter->second;
+    bool result;
+    if (content_ptr && content_len_ptr) {
+      result = _fetcher.getContent(processed_url, *content_ptr, *content_len_ptr);
+    } else {
+      result = (_fetcher.getRequestStatus(processed_url) == STATUS_DATA_AVAILABLE);
+    }
+    if (!result) {
+      _errorLog("[%s] Couldn't get content for URL [%.*s]", 
+                __FUNCTION__, processed_url.size(), processed_url.data());
+      Stats::increment(Stats::N_INCLUDE_ERRS);
+      return false;
+    }
+    _debugLog(_debug_tag.c_str(), "[%s] Got content successfully for URL [%.*s]", __FUNCTION__, 
+              processed_url.size(), processed_url.data());
+    return true;
+  } else if (node.type == DocNode::TYPE_SPECIAL_INCLUDE) {
+    AttributeList::const_iterator attr_iter;
+    for (attr_iter = node.attr_list.begin(); attr_iter != node.attr_list.end(); ++attr_iter) {
+      if (attr_iter->name == INCLUDE_DATA_ID_ATTR) {
+        break;
+      }
+    }
+    int include_data_id = attr_iter->value_len;
+    SpecialIncludeHandler *handler = 
+      reinterpret_cast<SpecialIncludeHandler *>(const_cast<char *>(attr_iter->value));
+    bool result;
+    if (content_ptr && content_len_ptr) {
+      result = handler->getData(include_data_id, *content_ptr, *content_len_ptr);
+    } else {
+      result = (handler->getIncludeStatus(include_data_id) == STATUS_DATA_AVAILABLE);
+    }
+    if (!result) {
+      _errorLog("[%s] Couldn't get content for special include with id %d", __FUNCTION__, include_data_id);
+      Stats::increment(Stats::N_SPCL_INCLUDE_ERRS);
+      return false;
+    }
+    _debugLog(_debug_tag.c_str(), "[%s] Successfully got content for special include with id %d",
+              __FUNCTION__, include_data_id);
+    return true;
+  }
+  _errorLog("[%s] Cannot get include data for node of type %s",
+            __FUNCTION__, DocNode::type_names_[node.type]);
+  return false;
+}
+
+EsiProcessor::ReturnCode
+EsiProcessor::process(const char *&data, int &data_len) {
+  if (_curr_state == ERRORED) {
+    return FAILURE;
+  }
+  if (_curr_state != WAITING_TO_PROCESS) {
+    _errorLog("[%s] Processor has to finish parsing via completeParse() before process() call", __FUNCTION__);
+    return FAILURE;
+  }
+  DocNodeList::iterator node_iter,iter;
+  bool attempt_succeeded;
+  std::vector<std::string> attemptUrls;
+  TryBlockList::iterator try_iter = _try_blocks.begin();
+  for (int i = 0; i < _n_try_blocks_processed; ++i, ++try_iter);
+  for (; _n_try_blocks_processed < static_cast<int>(_try_blocks.size()); ++try_iter) {
+    ++_n_try_blocks_processed;
+    attempt_succeeded = true;
+    for (node_iter = try_iter->attempt_nodes.begin(); node_iter != try_iter->attempt_nodes.end(); ++node_iter) {
+      if ((node_iter->type == DocNode::TYPE_INCLUDE) ||
+          (node_iter->type == DocNode::TYPE_SPECIAL_INCLUDE)) {
+          const Attribute &url = (*node_iter).attr_list.front();              
+          string raw_url(url.value, url.value_len);
+          attemptUrls.push_back(_expression.expand(raw_url));
+        if (!_getIncludeData(*node_iter)) {
+          attempt_succeeded = false;
+          break;
+        }
+      }
+    }
+          
+    /* FAILURE CACHE */
+    FailureData* data=static_cast<FailureData*>(pthread_getspecific(threadKey));
+    _debugLog("plugin_esi_failureInfo","[%s]Fetched data related to thread specfic %p",__FUNCTION__,data);
+    
+    for (iter=try_iter->attempt_nodes.begin(); iter != try_iter->attempt_nodes.end(); ++iter) {
+      if ((iter->type == DocNode::TYPE_INCLUDE) || iter->type == DocNode::TYPE_SPECIAL_INCLUDE)
+      {
+          if(!attempt_succeeded && iter==node_iter)
+              continue;
+          const Attribute &url = (*iter).attr_list.front();              
+          string raw_url(url.value, url.value_len);
+          attemptUrls.push_back(_expression.expand(raw_url));
+      }
+    }
+   
+    if(attemptUrls.size()>0 && data)
+    { 
+        FailureData::iterator it =data->find(attemptUrls[0]);
+        FailureInfo* info;
+    
+        if(it == data->end())
+        {
+            _debugLog("plugin_esi_failureInfo","[%s]Inserting object for the attempt URLS",__FUNCTION__);
+            info=new FailureInfo(FAILURE_INFO_TAG,_debugLog,_errorLog);
+            for(int i=0;i<static_cast<int>(attemptUrls.size());i++)
+            {
+                _debugLog("plugin_esi_failureInfo", "[%s] Urls [%.*s]",__FUNCTION__,attemptUrls[i].size(),attemptUrls[i].data());
+                (*data)[attemptUrls[i]]=info;
+            }
+    
+            info->registerSuccFail(attempt_succeeded);
+
+        } else {
+            info=it->second;
+            //Should be registered only if attemp was made
+            //and it failed
+            if(_reqAdded)
+                info->registerSuccFail(attempt_succeeded);   
+    
+        }
+    }
+    if (attempt_succeeded) {
+      _debugLog(_debug_tag.c_str(), "[%s] attempt section succeded; using attempt section", __FUNCTION__);
+      _node_list.splice(try_iter->pos, try_iter->attempt_nodes);
+    } else {
+      _debugLog(_debug_tag.c_str(), "[%s] attempt section errored; trying except section", __FUNCTION__); 
+      int n_prescanned_nodes = 0;
+      if (!_preprocess(try_iter->except_nodes, n_prescanned_nodes)) {
+        _errorLog("[%s] Failed to preprocess except nodes", __FUNCTION__);
+        stop();
+        return FAILURE;
+      }
+      _node_list.splice(try_iter->pos, try_iter->except_nodes);
+      if (_fetcher.getNumPendingRequests()) { 
+        _debugLog(_debug_tag.c_str(), "[%s] New fetch requests were triggered by except block; "
+                  "Returning NEED_MORE_DATA...", __FUNCTION__);
+        return NEED_MORE_DATA;
+      }
+    }
+  }
+  _curr_state = PROCESSED;
+  for (node_iter = _node_list.begin(); node_iter != _node_list.end(); ++node_iter) {
+    DocNode &doc_node = *node_iter; // handy reference
+    _debugLog(_debug_tag.c_str(), "[%s] Processing ESI node [%s] with data of size %d starting with [%.5s...]", 
+              __FUNCTION__, DocNode::type_names_[doc_node.type], doc_node.data_len,
+              (doc_node.data_len ? doc_node.data : "(null)"));
+    if (doc_node.type == DocNode::TYPE_PRE) {
+      // just copy the data
+      _output_data.append(doc_node.data, doc_node.data_len);
+    } else if (!_processEsiNode(node_iter)) {
+      _errorLog("[%s] Failed to process ESI node [%.*s]", __FUNCTION__, doc_node.data_len, doc_node.data);
+      stop();
+      return FAILURE;
+    }
+  }
+  _addFooterData();
+  data = _output_data.c_str();
+  data_len = _output_data.size();
+  _debugLog(_debug_tag.c_str(), "[%s] ESI processed document of size %d starting with [%.10s]",
+            __FUNCTION__, data_len, (data_len ? data : "(null)"));
+  return SUCCESS;
+}
+
+void
+EsiProcessor::stop() {
+  _output_data.clear();
+  _node_list.clear();
+  _include_urls.clear();
+  _try_blocks.clear();
+  _n_prescanned_nodes = 0;
+  _n_try_blocks_processed = 0;
+  for (IncludeHandlerMap::iterator map_iter = _include_handlers.begin();
+       map_iter != _include_handlers.end(); ++map_iter) {
+    delete map_iter->second;
+  }
+  _include_handlers.clear();
+  _curr_state = STOPPED;
+}
+
+EsiProcessor::~EsiProcessor() {
+  if (_curr_state != STOPPED) {
+    stop();
+  }
+}
+
+bool
+EsiProcessor::_processEsiNode(const DocNodeList::iterator &iter) {
+  bool retval;
+  const DocNode &node = *iter;
+  if ((node.type == DocNode::TYPE_INCLUDE) || (node.type == DocNode::TYPE_SPECIAL_INCLUDE)) {
+    const char *content;
+    int content_len;
+    if (retval = _getIncludeData(node, &content, &content_len)) {
+      _output_data.append(content, content_len);
+    }
+  } else if ((node.type == DocNode::TYPE_COMMENT) || (node.type == DocNode::TYPE_REMOVE) ||
+             (node.type == DocNode::TYPE_TRY) || (node.type == DocNode::TYPE_CHOOSE) ||
+             (node.type == DocNode::TYPE_HTML_COMMENT)) {
+    // choose, try and html-comment would've been dealt with earlier
+    _debugLog(_debug_tag.c_str(), "[%s] No-op for [%s] node", __FUNCTION__, DocNode::type_names_[node.type]);
+    retval = true;
+  } else if (node.type == DocNode::TYPE_VARS) {
+    retval = _handleVars(node.data, node.data_len);
+  } else {
+    _errorLog("[%s] Unknown ESI Doc node type %d", __FUNCTION__, node.type);
+    retval = false;
+  }
+  if (retval) {
+    _debugLog(_debug_tag.c_str(), "[%s] Processed ESI [%s] node", __FUNCTION__, DocNode::type_names_[node.type]);
+  } else {
+    _errorLog("[%s] Failed to process ESI doc node of type %d", __FUNCTION__, node.type);
+  }
+  return retval;
+}
+
+inline bool
+EsiProcessor::_isWhitespace(const char *data, int data_len) {
+  for (int i = 0; i < data_len; ++i) {
+    if (!isspace(data[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool
+EsiProcessor::_handleChoose(DocNodeList::iterator &curr_node) {
+  DocNodeList::iterator iter, otherwise_node, winning_node, end_node;
+  end_node = curr_node->child_nodes.end();
+  otherwise_node = end_node;
+  for (iter = curr_node->child_nodes.begin(); iter != end_node; ++iter) {
+    if (iter->type == DocNode::TYPE_OTHERWISE) {
+      otherwise_node = iter;
+      break;
+    }
+  }
+  winning_node = end_node;
+  for (iter = curr_node->child_nodes.begin(); iter != end_node; ++iter) {
+    if (iter->type == DocNode::TYPE_WHEN) {
+      const Attribute &test_expr = iter->attr_list.front();
+      if (_expression.evaluate(test_expr.value, test_expr.value_len)) {
+        winning_node = iter;
+        break;
+      }
+    }
+  }
+  if (winning_node == end_node) {
+    _debugLog(_debug_tag.c_str(), "[%s] All when nodes failed to evaluate to true", __FUNCTION__);
+    if (otherwise_node != end_node) {
+      _debugLog(_debug_tag.c_str(), "[%s] Using otherwise node...", __FUNCTION__);
+      winning_node = otherwise_node;
+    } else {
+      _debugLog(_debug_tag.c_str(), "[%s] No otherwise node, nothing to do...", __FUNCTION__);
+      return true;
+    }
+  }
+  // splice() inserts elements *before* given position, but we need to
+  // insert new nodes after the choose node for them to be seen by
+  // preprocess(); hence...
+  DocNodeList::iterator next_node = curr_node;
+  ++next_node;
+  _node_list.splice(next_node, winning_node->child_nodes);
+  return true;
+}
+
+bool
+EsiProcessor::_handleTry(DocNodeList::iterator &curr_node) {
+  DocNodeList::iterator iter, end_node = curr_node->child_nodes.end();
+  DocNodeList::iterator attempt_node = end_node, except_node = end_node;
+  for (iter = curr_node->child_nodes.begin(); iter != end_node; ++iter) {
+    if (iter->type == DocNode::TYPE_ATTEMPT) {
+        
+          attempt_node = iter;
+    } else if (iter->type == DocNode::TYPE_EXCEPT) {
+      except_node = iter;
+    }
+  }
+  TryBlock try_info(attempt_node->child_nodes, except_node->child_nodes, curr_node);
+  int n_prescanned_nodes = 0;
+  if (!_preprocess(try_info.attempt_nodes, n_prescanned_nodes)) {
+    _errorLog("[%s] Couldn't preprocess attempt node of try block", __FUNCTION__);
+    return false;
+  }
+  _try_blocks.push_back(try_info);
+  return true;
+}
+
+bool
+EsiProcessor::_handleVars(const char *str, int str_len) {
+  const string &str_value = _expression.expand(str, str_len);
+  _debugLog(_debug_tag.c_str(), "[%s] Vars expression [%.*s] expanded to [%.*s]",
+            __FUNCTION__, str_len, str, str_value.size(), str_value.data());
+  _output_data += str_value;
+  return true;
+}
+
+bool
+EsiProcessor::_handleHtmlComment(const DocNodeList::iterator &curr_node) {
+  DocNodeList inner_nodes;
+  if (!_parser.parse(inner_nodes, curr_node->data, curr_node->data_len)) {
+    _errorLog("[%s] Couldn't parse html comment node content", __FUNCTION__);
+    Stats::increment(Stats::N_PARSE_ERRS);
+    return false;
+  }
+  _debugLog(_debug_tag.c_str(), "[%s] parsed %d inner nodes from html comment node", __FUNCTION__, inner_nodes.size());
+  DocNodeList::iterator next_node = curr_node;
+  ++next_node;
+  _node_list.splice(next_node, inner_nodes); // insert after curr node for preprocessing
+  return true;
+}
+
+bool
+EsiProcessor::_preprocess(DocNodeList &node_list, int &n_prescanned_nodes) {
+  DocNodeList::iterator list_iter = node_list.begin();
+  StringHash::iterator hash_iter;
+  string raw_url;
+  
+  // skip previously examined nodes
+  for (int i = 0; i < n_prescanned_nodes; ++i, ++list_iter);
+  
+  for (; list_iter != node_list.end(); ++list_iter, ++n_prescanned_nodes) {
+    if (list_iter->type == DocNode::TYPE_CHOOSE) {
+      if (!_handleChoose(list_iter)) {
+        _errorLog("[%s] Failed to preprocess choose node", __FUNCTION__);
+        return false;
+      } 
+      _debugLog(_debug_tag.c_str(), "[%s] handled choose node successfully", __FUNCTION__);
+    } else if (list_iter->type == DocNode::TYPE_TRY) {
+      if (!_handleTry(list_iter)) {
+        _errorLog("[%s] Failed to preprocess try node", __FUNCTION__);
+        return false;
+      }
+      _debugLog(_debug_tag.c_str(), "[%s] handled try node successfully", __FUNCTION__);
+    } else if (list_iter->type == DocNode::TYPE_HTML_COMMENT) {
+      if (!_handleHtmlComment(list_iter)) {
+        _errorLog("[%s] Failed to preprocess try node", __FUNCTION__);
+        return false;
+      }
+    } else if (list_iter->type == DocNode::TYPE_INCLUDE) {
+      Stats::increment(Stats::N_INCLUDES);
+      const Attribute &src = list_iter->attr_list.front();
+      raw_url.assign(src.value, src.value_len);
+      _debugLog(_debug_tag.c_str(), "[%s] Adding fetch request for url [%.*s]", 
+                __FUNCTION__, raw_url.size(), raw_url.data());
+      hash_iter = _include_urls.find(raw_url);
+      if (hash_iter != _include_urls.end()) { // we have already processed this URL
+        _debugLog(_debug_tag.c_str(), "[%s] URL [%.*s] already processed",
+                  __FUNCTION__, raw_url.size(), raw_url.data());
+        continue;
+      }
+      const string &expanded_url = _expression.expand(raw_url);
+      if (!expanded_url.size()) {
+        _errorLog("[%s] Couldn't expand raw URL [%.*s]", __FUNCTION__, raw_url.size(), raw_url.data());
+        Stats::increment(Stats::N_INCLUDE_ERRS);
+        continue;
+      }
+
+      bool fetch=true;
+      FailureData* threadData;
+      /* FAILURE CACHE */
+      if((threadData=static_cast<FailureData*>(pthread_getspecific(threadKey))) == NULL){
+          threadData=new FailureData();  
+          if(pthread_setspecific(threadKey,threadData))
+          {
+              INKError("[%s] Unable to set the key", __FUNCTION__);
+              abort();
+          }
+          _debugLog("plugin_esi_failureInfo", "[%s] Data is set for this thread [threadData]%p [threadID]%u [%.*s]", __FUNCTION__,threadData,pthread_self(),expanded_url.size(),expanded_url.data());
+      }else {
+       
+          _debugLog("plugin_esi_failureInfo", "[%s] URL request [%.*s] %u",
+                  __FUNCTION__, expanded_url.size(), expanded_url.data(),pthread_self());
+      
+          FailureData::iterator it =threadData->find(expanded_url);
+          FailureInfo* info;
+
+          if(it != threadData->end()) {
+              info=it->second; 
+              fetch=_reqAdded=info->isAttemptReq();
+              _debugLog(_debug_tag.c_str(),"[%s] Fetch result is %d",__FUNCTION__,fetch);
+        }
+      }
+     
+      if(fetch){
+          if (!_fetcher.addFetchRequest(expanded_url)) {
+            _errorLog("[%s] Couldn't add fetch request for URL [%.*s]", 
+                  __FUNCTION__, raw_url.size(), raw_url.data());
+            Stats::increment(Stats::N_INCLUDE_ERRS);
+            continue;
+          }
+          _include_urls.insert(StringHash::value_type(raw_url, expanded_url));
+      }
+      else{
+          _debugLog("plugin_esi_failureInfo","[%s] Not adding fetch request for [%.*s]",__FUNCTION__,expanded_url.size(),expanded_url.data());
+          continue;
+      }
+    } else if (list_iter->type == DocNode::TYPE_SPECIAL_INCLUDE) {
+      Stats::increment(Stats::N_SPCL_INCLUDES);
+      const Attribute &handler_attr = list_iter->attr_list.front();
+      string handler_id(handler_attr.value, handler_attr.value_len);
+      SpecialIncludeHandler *handler;
+      IncludeHandlerMap::const_iterator map_iter = _include_handlers.find(handler_id);
+      if (map_iter == _include_handlers.end()) {
+        handler = _handler_manager.getHandler(_esi_vars, _expression, _fetcher, handler_id);
+        if (!handler) {
+          _errorLog("[%s] Couldn't create handler with id [%s]", __FUNCTION__, handler_id.c_str());
+          Stats::increment(Stats::N_SPCL_INCLUDE_ERRS);
+          return false;
+        }
+        _include_handlers.insert(IncludeHandlerMap::value_type(handler_id, handler));
+        _debugLog(_debug_tag.c_str(), "[%s] Created new special include handler object for id [%s]",
+                  __FUNCTION__, handler_id.c_str());
+      } else {
+        handler = map_iter->second;
+      }
+      int special_data_id = handler->handleInclude(list_iter->data, list_iter->data_len);
+      if (special_data_id == -1) {
+        _errorLog("[%s] Include handler [%s] couldn't process include with data [%.*s]",
+                  __FUNCTION__, handler_id.c_str(), list_iter->data_len, list_iter->data);
+        Stats::increment(Stats::N_SPCL_INCLUDE_ERRS);
+        return false;
+      }
+      // overloading this structure's members
+      // handler will be in value and include id will be in value_len of the structure
+      list_iter->attr_list.push_back(Attribute(INCLUDE_DATA_ID_ATTR, 0,
+                                               reinterpret_cast<const char *>(handler),
+                                               special_data_id));
+      _debugLog(_debug_tag.c_str(), "[%s] Got id %d for special include at node %d from handler [%s]",
+                __FUNCTION__, special_data_id, n_prescanned_nodes + 1, handler_id.c_str());
+    }
+  }
+  return true;
+}
+
+void
+EsiProcessor::_addFooterData() {
+  const char *footer;
+  int footer_len;
+  for (IncludeHandlerMap::iterator iter = _include_handlers.begin(); iter != _include_handlers.end(); ++iter) {
+    iter->second->getFooter(footer, footer_len);
+    if (footer_len > 0) {
+      _output_data.append(footer, footer_len);
+    }
+  }
+}

Added: trafficserver/plugins/trunk/esi/lib/EsiProcessor.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/EsiProcessor.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/EsiProcessor.h (added)
+++ trafficserver/plugins/trunk/esi/lib/EsiProcessor.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,134 @@
+#ifndef _ESI_PROCESSOR_H
+#define _ESI_PROCESSOR_H
+
+#include <string>
+#include <map>
+#include<pthread.h>
+#include "ComponentBase.h"
+#include "StringHash.h"
+#include "DocNode.h"
+#include "EsiParser.h"
+#include "HttpDataFetcher.h"
+#include "Variables.h"
+#include "Expression.h"
+#include "SpecialIncludeHandler.h"
+#include "HandlerManager.h"
+
+extern pthread_key_t key;
+class EsiProcessor : private EsiLib::ComponentBase
+{
+
+public:
+
+  EsiProcessor(const char *debug_tag, const char *parser_debug_tag, const char *expression_debug_tag,
+               EsiLib::ComponentBase::Debug debug_func, EsiLib::ComponentBase::Error error_func,
+               HttpDataFetcher &fetcher, EsiLib::Variables &variables,
+               const EsiLib::HandlerManager &handler_mgr);
+  
+  /** Initializes the processor with the context of the request to be processed */
+  bool start();
+
+  /** Adds data to be parsed */
+  bool addParseData(const char *data, int data_len = -1);
+
+  /** convenient alternative to method above */ 
+  bool addParseData(const std::string &data) {
+    return addParseData(data.data(), data.size());
+  }
+  
+  /** Tells processor to wrap-up parsing; a final or the only piece of
+   * data can be optionally provided */
+  bool completeParse(const char *data = 0, int data_len = -1);
+  
+  /** convenient alternative to method above */ 
+  bool completeParse(const std::string &data) {
+    return completeParse(data.data(), data.size());
+  }
+  
+  enum ReturnCode { FAILURE, SUCCESS, NEED_MORE_DATA };
+  
+  /** Processes the currently parsed ESI document and returns processed 
+   * data in supplied out-parameters. Should be called when fetcher has 
+   * finished pulling in all data. 
+   * 
+   * try/attempt/except construct can generate new fetch requests
+   * during processing. Only in such cases is NEED_MORE_DATA returned;
+   * else FAILURE/SUCCESS is returned. */
+  ReturnCode process(const char *&data, int &data_len);
+
+  /** returns packed version of document currently being processed */
+  void packNodeList(std::string &buffer, bool retain_buffer_data) {
+    return _node_list.pack(buffer, retain_buffer_data);
+  }
+  
+  /** Unpacks previously parsed and packed ESI node list from given
+   * buffer and preps for process(); Unpacked document will point to
+   * data in argument (i.e., caller space) */
+  bool usePackedNodeList(const char *data, int data_len);
+
+  /** convenient alternative to method above */
+  bool usePackedNodeList(const std::string &data) {
+    return usePackedNodeList(data.data(), data.size());
+  }
+
+  /** Clears state from current request */
+  void stop(); 
+
+  virtual ~EsiProcessor();
+
+private:
+  
+  enum EXEC_STATE { STOPPED, PARSING, WAITING_TO_PROCESS, PROCESSED, ERRORED };
+  EXEC_STATE _curr_state;
+
+  std::string _output_data;
+
+  EsiParser _parser;
+  EsiLib::DocNodeList _node_list;
+  int _n_prescanned_nodes;
+
+  HttpDataFetcher &_fetcher;
+  EsiLib::StringHash _include_urls;
+
+  bool _reqAdded;
+  
+  bool _processEsiNode(const EsiLib::DocNodeList::iterator &iter);
+  bool _handleParseComplete();
+  bool _getIncludeData(const EsiLib::DocNode &node, const char **content_ptr = 0, int *content_len_ptr = 0);
+  bool _handleVars(const char *str, int str_len);
+  bool _handleChoose(EsiLib::DocNodeList::iterator &curr_node);
+  bool _handleTry(EsiLib::DocNodeList::iterator &curr_node);
+  bool _handleHtmlComment(const EsiLib::DocNodeList::iterator &curr_node);
+  bool _preprocess(EsiLib::DocNodeList &node_list, int &n_prescanned_nodes);
+  inline bool _isWhitespace(const char *data, int data_len);
+  void _addFooterData();
+
+  EsiLib::Variables &_esi_vars;
+  EsiLib::Expression _expression;
+
+  struct TryBlock {
+    EsiLib::DocNodeList &attempt_nodes;
+    EsiLib::DocNodeList &except_nodes;
+    EsiLib::DocNodeList::iterator pos;
+    TryBlock(EsiLib::DocNodeList &att, EsiLib::DocNodeList &exc, EsiLib::DocNodeList::iterator p) 
+      : attempt_nodes(att), except_nodes(exc), pos(p) { };
+  };
+  typedef std::list<TryBlock> TryBlockList;
+  TryBlockList _try_blocks;
+  int _n_try_blocks_processed;
+
+  const EsiLib::HandlerManager &_handler_manager;
+
+  static const char *INCLUDE_DATA_ID_ATTR; 
+
+  typedef std::map<std::string, EsiLib::SpecialIncludeHandler *> IncludeHandlerMap;
+  IncludeHandlerMap _include_handlers;
+
+  void error() {
+    stop();
+    _curr_state = ERRORED;
+  }
+};
+
+
+#endif // _ESI_PROCESSOR_H

Added: trafficserver/plugins/trunk/esi/lib/Expression.cc
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Expression.cc?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Expression.cc (added)
+++ trafficserver/plugins/trunk/esi/lib/Expression.cc Tue Nov  8 19:07:20 2011
@@ -0,0 +1,232 @@
+#include "Expression.h"
+#include "Utils.h"
+
+using std::string;
+using namespace EsiLib;
+
+const string Expression::EMPTY_STRING("");
+const string Expression::TRUE_STRING("true");
+const Expression::OperatorString Expression::OPERATOR_STRINGS[N_OPERATORS] = { 
+  Expression::OperatorString("==", 2),
+  Expression::OperatorString("!=", 2),
+  Expression::OperatorString("<=", 2),
+  Expression::OperatorString(">=", 2),
+  Expression::OperatorString("<", 1),
+  Expression::OperatorString(">", 1),
+  Expression::OperatorString("!", 1),
+  Expression::OperatorString("|", 1),
+  Expression::OperatorString("&", 1) 
+};
+
+Expression::Expression(const char *debug_tag, ComponentBase::Debug debug_func, 
+                             ComponentBase::Error error_func, Variables &variables) 
+  : ComponentBase(debug_tag, debug_func, error_func), _variables(variables), _value("") {
+}
+  
+inline bool
+Expression::_stripQuotes(const char *&expr, int &expr_len) const {
+  char quote_char = 0;
+  if (expr[0] == '\'') {
+    quote_char = '\'';
+  } else if (expr[0] == '"') {
+    quote_char = '"';
+  }
+  if (quote_char) {
+    if (expr[expr_len - 1] != quote_char) {
+      _errorLog("[%s] Unterminated quote in expression [%.*s]", __FUNCTION__, expr_len, expr);
+      return false;
+    }
+    expr_len -= 2;
+    ++expr;
+  }
+  return true;
+}
+
+const string &
+Expression::expand(const char *expr, int expr_len /* = -1 */) {
+  int var_start_index = -1, var_size;
+  Utils::trimWhiteSpace(expr, expr_len);
+  if (!expr_len) {
+    _debugLog(_debug_tag.c_str(), "[%s] Returning empty string for empty expression", __FUNCTION__);
+    goto lFail;
+  }
+  if (!_stripQuotes(expr, expr_len)) {
+    goto lFail;
+  }
+  _value.clear();
+  bool last_variable_expanded;
+  for (int i = 0; i < expr_len; ++i) {
+    if ((expr[i] == '$') && ((expr_len - i) >= 3) && (expr[i + 1] == '(')) {
+      if (var_start_index != -1) {
+        _debugLog(_debug_tag.c_str(), "[%s] Cannot have nested variables in expression [%.*s]",
+                  __FUNCTION__, expr_len, expr);
+        goto lFail;
+      }
+      var_start_index = i + 2; // skip the '$('
+      ++i; // skip past '$'; for loop's incrementor will skip past '('
+    } else if (((expr[i] == ')') || (expr[i] == '|')) && (var_start_index != -1)) {
+      last_variable_expanded = false;
+      var_size = i - var_start_index;
+      if (var_size) {
+        const string &var_value = _variables.getValue(expr + var_start_index, var_size);
+        _debugLog(_debug_tag.c_str(), "[%s] Got value [%.*s] for variable [%.*s]",
+                  __FUNCTION__, var_value.size(), var_value.data(), var_size, expr + var_start_index);
+        last_variable_expanded = (var_value.size() > 0);
+        _value += var_value;
+      } else {
+        _debugLog(_debug_tag.c_str(), "[%s] Parsing out empty variable", __FUNCTION__);
+      }
+      if (expr[i] == '|') {
+        int default_value_start = ++i;
+        while (i < expr_len) {
+          if (expr[i] == ')') {
+            break;
+          }
+          ++i;
+        }
+        if (i == expr_len) {
+          _debugLog(_debug_tag.c_str(), "[%s] Expression [%.*s] has unterminated variable (with default value)",
+                    __FUNCTION__, expr_len, expr);
+          goto lFail;
+        }
+        const char *default_value = expr + default_value_start;
+        int default_value_len = i - default_value_start;
+        if (!_stripQuotes(default_value, default_value_len)) {
+          goto lFail;
+        }
+        if (!last_variable_expanded) {
+          _debugLog(_debug_tag.c_str(), "[%s] Using default value [%.*s] as variable expanded to empty string",
+                    __FUNCTION__, default_value_len, default_value);
+          _value.append(default_value, default_value_len);
+        }
+      }
+      var_start_index = -1;
+    } else if (var_start_index == -1) {
+      _value += expr[i];
+    }
+  }
+  if (var_start_index != -1) {
+    _debugLog(_debug_tag.c_str(), "[%s] Returning empty string for expression with unterminated variable [%.*s]",
+              __FUNCTION__, expr_len - var_start_index, expr + var_start_index);
+    goto lFail;
+  }
+  _debugLog(_debug_tag.c_str(), "[%s] Returning final expanded expression [%.*s]",
+            __FUNCTION__, _value.size(), _value.data());
+  return _value;
+
+lFail:
+  return EMPTY_STRING;
+}
+
+inline int
+Expression::_findOperator(const char *expr, int expr_len, Operator &op) const {
+  string expr_str(expr, expr_len);
+  size_t sep;
+  for (int i = 0; i < N_OPERATORS; ++i) {
+    const OperatorString &op_str = OPERATOR_STRINGS[i];
+    sep = (op_str.str_len == 1) ? expr_str.find(op_str.str[0]) : expr_str.find(op_str.str);
+    if (sep < expr_str.size()) {
+      op = static_cast<Operator>(i);
+      return static_cast<int>(sep);
+    }
+  }
+  return -1;
+}
+
+inline bool
+Expression::_evalSimpleExpr(const char *expr, int expr_len) {
+  const string &lhs = expand(expr, expr_len);
+  _debugLog(_debug_tag.c_str(), "[%s] simple expression [%.*s] evaluated to [%.*s]",
+            __FUNCTION__, expr_len, expr, lhs.size(), lhs.data());
+  double val;
+  return _convert(lhs, val) ? val : !lhs.empty();
+}
+
+
+bool
+Expression::evaluate(const char *expr, int expr_len /* = -1 */) {
+  Utils::trimWhiteSpace(expr, expr_len);
+  if (!expr_len) {
+    _debugLog(_debug_tag.c_str(), "[%s] Returning false for empty expression", __FUNCTION__);
+    return false;
+  }
+  Operator op;
+  const char *subexpr;
+  int subexpr_len;
+  string lhs, rhs;
+  bool retval = false;
+  int sep = _findOperator(expr, expr_len, op);
+
+  if (sep == -1) {
+    retval = _evalSimpleExpr(expr, expr_len);
+  } else if (_isBinaryOperator(op)) {
+    subexpr = expr;
+    subexpr_len = sep;
+    lhs = expand(subexpr, subexpr_len);
+    _debugLog(_debug_tag.c_str(), "[%s] LHS [%.*s] expanded to [%.*s]",
+              __FUNCTION__, subexpr_len, subexpr, lhs.size(), lhs.data());
+    subexpr = expr + sep + OPERATOR_STRINGS[op].str_len;
+    subexpr_len = expr_len - subexpr_len - OPERATOR_STRINGS[op].str_len;
+    rhs = expand(subexpr, subexpr_len);
+    _debugLog(_debug_tag.c_str(), "[%s] RHS [%.*s] expanded to [%.*s]",
+              __FUNCTION__, subexpr_len, subexpr, rhs.size(), rhs.data());
+    double lhs_numerical, rhs_numerical;
+    bool are_numerical = _convert(lhs, lhs_numerical);
+    are_numerical = are_numerical ? _convert(rhs, rhs_numerical) : false;
+    switch (op) {
+    case OP_EQ:
+      retval = are_numerical ? (lhs_numerical == rhs_numerical) : (lhs == rhs);
+      break;
+    case OP_NEQ:
+      retval = are_numerical ? (lhs_numerical != rhs_numerical) : (lhs != rhs);
+      break;
+    case OP_OR:
+      retval = are_numerical ? (lhs_numerical || rhs_numerical) : (lhs.size() || rhs.size());
+      break;
+    case OP_AND:
+      retval = are_numerical ? (lhs_numerical && rhs_numerical) : (lhs.size() && rhs.size());
+      break;
+    default:
+      if (lhs.empty() || rhs.empty()) {
+        // one of the sides expanded to nothing; invalid comparison
+        _debugLog(_debug_tag.c_str(), "[%s] LHS/RHS empty. Cannot evaluate comparisons", __FUNCTION__);
+        retval = false;
+      } else {
+        switch (op) {
+        case OP_LT:
+          retval = are_numerical ? (lhs_numerical < rhs_numerical) : (lhs < rhs);
+          break;
+        case OP_GT:
+          retval = are_numerical ? (lhs_numerical > rhs_numerical) : (lhs > rhs);
+          break;
+        case OP_LTEQ:
+          retval = are_numerical ? (lhs_numerical <= rhs_numerical) : (lhs <= rhs);
+          break;
+        case OP_GTEQ:
+          retval = are_numerical ? (lhs_numerical >= rhs_numerical) : (lhs >= rhs);
+          break;
+        default:
+          _debugLog(_debug_tag.c_str(), "[%s] Unknown operator in expression [%.*s]; returning false",
+                    __FUNCTION__, expr_len, expr);
+        }
+      }
+      break;
+    }
+  } else if (op == OP_NOT) {
+    if (sep == 0) {
+      subexpr = expr + 1;
+      subexpr_len = expr_len - 1;
+      retval = !_evalSimpleExpr(expr + 1, expr_len - 1);
+    } else {
+      _debugLog(_debug_tag.c_str(), "[%s] Unary negation not preceding literal in expression [%.*s]; assuming true",
+                __FUNCTION__, expr_len, expr);
+      retval = true;
+    }
+  } else {
+    _debugLog(_debug_tag.c_str(), "[%s] Unknown operator in expression [%.*s]; returning false",
+              __FUNCTION__, expr_len, expr);
+  }
+  _debugLog(_debug_tag.c_str(), "[%s] Returning [%s] for expression [%.*s]",
+            __FUNCTION__, (retval ? "true" : "false"), expr_len, expr);
+  return retval;
+}

Added: trafficserver/plugins/trunk/esi/lib/Expression.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Expression.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Expression.h (added)
+++ trafficserver/plugins/trunk/esi/lib/Expression.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,84 @@
+#ifndef _ESI_EXPRESSION_H
+
+#define _ESI_EXPRESSION_H
+
+#include <string>
+#include <stdlib.h>
+
+#include "ComponentBase.h"
+#include "Variables.h"
+
+namespace EsiLib { 
+
+class Expression : private ComponentBase {
+
+public:
+
+  Expression(const char *debug_tag, ComponentBase::Debug debug_func, ComponentBase::Error error_func,
+             Variables &variables);
+  
+  /** substitutes variables (if any) in given expression */
+  const std::string &expand(const char *expr, int expr_len = -1);
+
+  /** convenient alternative for method above */
+  const std::string &expand(const std::string &expr) {
+    return expand(expr.data(), expr.size());
+  }
+
+  /** evaluates boolean value of given expression */
+  bool evaluate(const char *expr, int expr_len = -1);
+
+  /** convenient alternative for method above */
+  bool evaluate(const std::string &expr) {
+    return evaluate(expr.data(), expr.size());
+  }
+
+  virtual ~Expression() { };
+
+private:
+
+  static const std::string EMPTY_STRING;
+  static const std::string TRUE_STRING;
+
+  Variables &_variables;
+  std::string _value;
+
+  // these are arranged in parse priority format indices correspond to op strings array 
+  enum Operator { OP_EQ, OP_NEQ, OP_LTEQ, OP_GTEQ, OP_LT, OP_GT, OP_NOT, OP_OR, OP_AND, N_OPERATORS };
+
+  struct OperatorString {
+    const char *str;
+    int str_len;
+    OperatorString(const char *s = 0, int s_len = -1) : str(s), str_len(s_len) { };
+  };
+  
+  static const OperatorString OPERATOR_STRINGS[N_OPERATORS];
+
+  inline void _trimWhiteSpace(const char *&expr, int &expr_len) const;
+  
+  inline bool _stripQuotes(const char *&expr, int &expr_len) const;
+  
+  inline int _findOperator(const char *expr, int expr_len, Operator &op) const;
+  
+  inline bool _isBinaryOperator(Operator &op) const {
+    return ((op == OP_EQ) || (op == OP_NEQ) || (op == OP_LT) || (op == OP_GT) ||
+            (op == OP_LTEQ) || (op == OP_GTEQ) || (op == OP_OR) || (op == OP_AND));
+  }
+
+  inline bool _convert(const std::string &str, double &value) {
+    size_t str_size = str.size();
+    if (str_size) {
+      char *endp;
+      const char *str_ptr = str.c_str();
+      value = strtod(str_ptr, &endp);
+      return (static_cast<unsigned int>(endp - str_ptr) == str_size);
+    }
+    return false;
+  }
+
+  inline bool _evalSimpleExpr(const char *expr, int expr_len);
+};
+
+};
+
+#endif // _ESI_EXPRESSION_H

Added: trafficserver/plugins/trunk/esi/lib/FailureInfo.cc
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/FailureInfo.cc?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/FailureInfo.cc (added)
+++ trafficserver/plugins/trunk/esi/lib/FailureInfo.cc Tue Nov  8 19:07:20 2011
@@ -0,0 +1,85 @@
+#include "FailureInfo.h"
+
+
+static int LOWER_CUT_OFF=300;
+static int HIGHER_CUT_OFF=1000;
+
+void FailureInfo::registerSuccFail(bool isSuccess)
+{
+    struct timeval currTime,result;
+    gettimeofday(&currTime,NULL);
+    timersub(&currTime,&_start,&result);
+
+    if( ( result.tv_sec*1000000+result.tv_usec )   > (WINDOW_SIZE*1000) )
+    {
+        _windowMarker=++_windowMarker%_totalSlots;
+      
+        if(_windowMarker==_totalSlots-1)
+        {
+            ++_windowsPassed;
+            double avg=0;
+            for(size_t i=0;i<_totalSlots;i++)
+            {
+                if(_statistics[i].first >0)
+                {
+                    avg+=_statistics[i].first/(_statistics[i].first+_statistics[i].second);
+                }
+            }
+            _avgOverWindow+=avg/_windowsPassed;
+            _debugLog(_debug_tag.c_str(),"[%s] current average over window is %lf",__FUNCTION__,_avgOverWindow);
+        }
+    
+        gettimeofday(&_start,NULL);
+    }
+
+   if(isSuccess)
+   {
+       _statistics[_windowMarker].second++;
+   } 
+   
+   else
+   {
+       _statistics[_windowMarker].first++;
+   }
+}
+    
+bool FailureInfo::isAttemptReq()
+{
+    double avg=0;
+    for(size_t i=0;i<_totalSlots;i++)
+    {
+        if(_statistics[i].first >0)
+        {
+            avg+=_statistics[i].first/(_statistics[i].first+_statistics[i].second);
+            
+        }
+    }
+    
+    if(avg) { 
+        //Average it out for time being
+        avg=avg/_totalSlots;
+        double prob;
+
+        if(avg*1000<LOWER_CUT_OFF) {
+            prob=avg;
+
+        } else {
+            double mapFactor=( ( (avg*1000-LOWER_CUT_OFF)*(avg*1000-LOWER_CUT_OFF) ) / (HIGHER_CUT_OFF-LOWER_CUT_OFF ) )+LOWER_CUT_OFF;
+            prob=mapFactor/1000;
+        }
+        
+        if(static_cast<int>(prob))
+            prob=_avgOverWindow;
+        
+        _debugLog(_debug_tag.c_str(),"[%s] Calculated probability is %lf",__FUNCTION__,prob);
+        int decision=rand()%100;
+
+        if(decision<prob*100) {
+            _debugLog(_debug_tag.c_str(),"[%s] fetch request will not be added for an attempt request",__FUNCTION__);
+            return (_requestMade=false);
+        }
+    }
+        
+    _debugLog(_debug_tag.c_str(),"[%s] fetch request will be added for an attempt request",__FUNCTION__);
+    return true;
+}

Added: trafficserver/plugins/trunk/esi/lib/FailureInfo.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/FailureInfo.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/FailureInfo.h (added)
+++ trafficserver/plugins/trunk/esi/lib/FailureInfo.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,78 @@
+#ifndef FAILURE_INFO_H
+#define FAILURE_INFO_H
+#include <time.h>
+#include <vector>
+#include <map>
+#include <sys/time.h>
+#include <string>
+#include <pthread.h>
+#include "ComponentBase.h"
+using namespace std;
+
+typedef std::vector <std::pair <double , double > > FailureToSuccess;
+typedef std::map<std::string,class FailureInfo*> FailureData;
+
+static const int WINDOW_SIZE=200;
+static const int TOTAL_DURATION=2000;
+
+class FailureInfo : private EsiLib::ComponentBase
+{
+public:
+
+    FailureInfo(const char* debug_tag,ComponentBase::Debug debug_func,ComponentBase::Error error_func)
+            :ComponentBase(debug_tag,debug_func,error_func),_windowsPassed(0),_avgOverWindow(0),_requestMade(true)
+    {
+        _totalSlots=TOTAL_DURATION/WINDOW_SIZE;
+        _windowMarker=0;
+        for(size_t i=0;i<_totalSlots;i++)
+            _statistics.push_back(make_pair(0,0));
+        _debugLog(_debug_tag.c_str(),"FailureInfo Ctor:inserting URL object into the statistics map [FailureInfo object]%p",this);
+    };
+
+    ~FailureInfo(){}
+
+    /* Fills the statistics vector depending
+     * upon the position of the window marker
+     */
+    void registerSuccFail(bool isSuccess);
+
+    /*
+     * Decides if an attempt shud be made
+     * for the attempt tag or except tag
+     * depending upon the statistics
+     */
+    bool isAttemptReq();
+
+private:
+    /*
+     * Keeps track of failures of attempt
+     * vs success
+     */
+    FailureToSuccess _statistics;
+
+    /* Slot on which to register success/failures
+     * Changes as soon as time has passed windowSize
+     */
+    size_t _windowMarker;
+
+    /* Number of slots to be filled over time */
+    size_t _totalSlots;
+
+    /* Start time for the window slots */
+    struct timeval _start;
+
+    /* Keep track of the number of windows filled prev*/
+    size_t _windowsPassed;
+    
+    /*Used as a deciding factor between attempt/except
+     * incase prob is complete truth
+     */
+    double _avgOverWindow;
+    
+public:
+    /*Was a reqeust made*/
+    bool _requestMade;
+
+};
+
+#endif

Added: trafficserver/plugins/trunk/esi/lib/HandlerManager.cc
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/HandlerManager.cc?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/HandlerManager.cc (added)
+++ trafficserver/plugins/trunk/esi/lib/HandlerManager.cc Tue Nov  8 19:07:20 2011
@@ -0,0 +1,69 @@
+#include <dlfcn.h>
+
+#include "HandlerManager.h"
+
+using std::map;
+using std::string;
+using namespace EsiLib;
+
+#define CLASS_NAME "HandlerManager"
+
+const char *const HandlerManager::FACTORY_FUNCTION_NAME = "createSpecialIncludeHandler";
+
+void
+HandlerManager::loadObjects(const Utils::KeyValueMap &handlers) {
+
+  ModuleHandleMap::iterator path_map_iter;
+
+  for (Utils::KeyValueMap::const_iterator id_map_iter = handlers.begin();
+       id_map_iter != handlers.end(); ++id_map_iter) {
+
+    const string &id = id_map_iter->first;
+    const string &path = id_map_iter->second;
+
+    path_map_iter = _path_to_module_map.find(path);
+
+    if (path_map_iter != _path_to_module_map.end()) {
+      // we have already loaded this object; just point id to original handle
+      _id_to_function_map.insert(FunctionHandleMap::value_type(id, (path_map_iter->second).function));
+    } else {
+      // no, we have to load this object
+      void *obj_handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
+      if (!obj_handle) {
+        _errorLog("[%s::%s] Could not load module [%s]. Error [%s]",
+                  CLASS_NAME, __FUNCTION__, path.c_str(), dlerror());
+      } else {
+        SpecialIncludeHandlerCreator func_handle =
+          (SpecialIncludeHandlerCreator)(dlsym(obj_handle, FACTORY_FUNCTION_NAME));
+        if (!func_handle) {
+          _errorLog("[%s::%s] Could not find factory function [%s] in module [%s]. Error [%s]",
+                    CLASS_NAME, __FUNCTION__, FACTORY_FUNCTION_NAME, path.c_str(), dlerror());
+          dlclose(obj_handle);
+        } else {
+          _id_to_function_map.insert(FunctionHandleMap::value_type(id, func_handle));
+          _path_to_module_map.insert(ModuleHandleMap::value_type(path, ModuleHandles(obj_handle, func_handle)));
+          _debugLog(_debug_tag.c_str(), "[%s] Loaded handler module [%s]", __FUNCTION__, path.c_str());
+        }
+      }
+    }
+  }
+}
+
+SpecialIncludeHandler *
+HandlerManager::getHandler(Variables &esi_vars, Expression &esi_expr, HttpDataFetcher &fetcher,
+                           const std::string &id) const {
+  FunctionHandleMap::const_iterator iter = _id_to_function_map.find(id);
+  if (iter == _id_to_function_map.end()) {
+    _errorLog("[%s::%s] handler id [%s] does not map to any loaded object",
+              CLASS_NAME, __FUNCTION__, id.c_str());
+    return 0;
+  }
+  return (*(iter->second))(esi_vars, esi_expr, fetcher, id);
+}
+
+HandlerManager::~HandlerManager() {
+  for (ModuleHandleMap::iterator map_iter = _path_to_module_map.begin();
+       map_iter != _path_to_module_map.end(); ++map_iter) {
+    dlclose((map_iter->second).object);
+  }
+}

Added: trafficserver/plugins/trunk/esi/lib/HandlerManager.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/HandlerManager.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/HandlerManager.h (added)
+++ trafficserver/plugins/trunk/esi/lib/HandlerManager.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,52 @@
+#ifndef _HANDLER_MANAGER_H
+
+#define _HANDLER_MANAGER_H
+
+#include <string>
+#include <map>
+
+#include "ComponentBase.h"
+#include "Utils.h"
+#include "SpecialIncludeHandler.h"
+#include "IncludeHandlerFactory.h"
+#include "Variables.h"
+#include "HttpDataFetcher.h"
+
+namespace EsiLib {
+
+class HandlerManager : protected ComponentBase {
+
+public:
+
+  HandlerManager(const char *debug_tag, Debug debug_func, Error error_func) :
+    ComponentBase(debug_tag, debug_func, error_func) {
+  };
+
+  void loadObjects(const Utils::KeyValueMap &handlers);
+
+  SpecialIncludeHandler *getHandler(Variables &esi_vars, Expression &esi_expr,
+                                    HttpDataFetcher &http_fetcher, const std::string &id) const;
+
+  ~HandlerManager();
+
+private:
+
+  typedef std::map<std::string, SpecialIncludeHandlerCreator> FunctionHandleMap;
+
+  struct ModuleHandles {
+    void *object;
+    SpecialIncludeHandlerCreator function;
+    ModuleHandles(void *o = 0, SpecialIncludeHandlerCreator f = 0) : object(o), function(f) { };
+  };
+
+  typedef std::map<std::string, ModuleHandles> ModuleHandleMap;
+
+  FunctionHandleMap _id_to_function_map;
+  ModuleHandleMap _path_to_module_map;
+
+  static const char *const FACTORY_FUNCTION_NAME;
+};
+
+};
+
+#endif

Added: trafficserver/plugins/trunk/esi/lib/HttpHeader.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/HttpHeader.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/HttpHeader.h (added)
+++ trafficserver/plugins/trunk/esi/lib/HttpHeader.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,22 @@
+#ifndef _ESI_HTTP_HEADER_H
+
+#define _ESI_HTTP_HEADER_H
+
+#include <list>
+
+namespace EsiLib {
+
+struct HttpHeader {
+  const char *name;
+  int name_len;
+  const char *value;
+  int value_len;
+  HttpHeader(const char *n = 0, int n_len = -1, const char *v = 0, int v_len = -1) 
+    : name(n), name_len(n_len), value(v), value_len(v_len) { };
+};
+
+typedef std::list<HttpHeader> HttpHeaderList;
+
+};
+
+#endif // _ESI_HTTP_HEADER_H

Added: trafficserver/plugins/trunk/esi/lib/IncludeHandlerFactory.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/IncludeHandlerFactory.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/IncludeHandlerFactory.h (added)
+++ trafficserver/plugins/trunk/esi/lib/IncludeHandlerFactory.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,31 @@
+#ifndef _INCLUDE_HANDLER_FACTORY_H
+
+#define _INCLUDE_HANDLER_FACTORY_H
+
+#include <string>
+#include "SpecialIncludeHandler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EsiLib::SpecialIncludeHandler *createSpecialIncludeHandler(EsiLib::Variables &esi_vars,
+                                                           EsiLib::Expression &esi_expr,
+                                                           HttpDataFetcher &fetcher,
+                                                           const std::string &id);
+  
+#ifdef __cplusplus
+}
+#endif
+
+namespace EsiLib {
+
+typedef SpecialIncludeHandler *(*SpecialIncludeHandlerCreator)(Variables &esi_vars,
+                                                               Expression &esi_expr,
+                                                               HttpDataFetcher &fetcher,
+                                                               const std::string &id);
+
+};
+
+#endif
+

Added: trafficserver/plugins/trunk/esi/lib/SpecialIncludeHandler.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/SpecialIncludeHandler.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/SpecialIncludeHandler.h (added)
+++ trafficserver/plugins/trunk/esi/lib/SpecialIncludeHandler.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,49 @@
+#ifndef _ESI_SPECIAL_INCLUDE_HANDLER
+
+#define _ESI_SPECIAL_INCLUDE_HANDLER
+
+#include "HttpDataFetcher.h"
+#include "Variables.h"
+#include "Expression.h"
+
+namespace EsiLib {
+
+class SpecialIncludeHandler {
+
+public:
+
+  SpecialIncludeHandler(Variables &esi_vars,
+                        Expression &esi_expr, HttpDataFetcher &http_fetcher)
+    : _esi_vars(esi_vars), _esi_expr(esi_expr), _http_fetcher(http_fetcher) {
+  }
+
+  virtual int handleInclude(const char *data, int data_len) = 0;
+
+  virtual void handleParseComplete() = 0;
+
+  /** trivial implementation */
+  virtual DataStatus getIncludeStatus(int include_id) {
+    const char *data;
+    int data_len;
+    return getData(include_id, data, data_len) ? STATUS_DATA_AVAILABLE : STATUS_ERROR;
+  }
+
+  virtual bool getData(int include_id, const char *&data, int &data_len) = 0;
+
+  virtual void getFooter(const char *&footer, int &footer_len) {
+    footer_len = 0;
+  }
+
+  virtual ~SpecialIncludeHandler() { };
+
+protected:
+
+  Variables &_esi_vars;
+  Expression &_esi_expr;
+  HttpDataFetcher &_http_fetcher;
+
+};
+
+};
+
+#endif

Added: trafficserver/plugins/trunk/esi/lib/Stats.cc
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Stats.cc?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Stats.cc (added)
+++ trafficserver/plugins/trunk/esi/lib/Stats.cc Tue Nov  8 19:07:20 2011
@@ -0,0 +1,36 @@
+#include "Stats.h"
+
+using namespace EsiLib;
+
+const char *Stats::STAT_NAMES[Stats::MAX_STAT_ENUM] = { 
+  "esi.n_os_docs",
+  "esi.n_cache_docs",
+  "esi.n_parse_errs",
+  "esi.n_includes",
+  "esi.n_include_errs",
+  "esi.n_spcl_includes",
+  "esi.n_spcl_include_errs"
+};
+
+static uint32_t g_stat_indices[Stats::MAX_STAT_ENUM];
+static StatSystem *g_system = 0;
+
+void Stats::init(StatSystem *system) {
+  g_system = system;
+  if (g_system) {
+    for (int i = 0; i < Stats::MAX_STAT_ENUM; ++i) {
+      if (!g_system->create(Stats::STAT_NAMES[i], &g_stat_indices[i])) {
+        Utils::ERROR_LOG("[%s] Unable to create stat [%s]", __FUNCTION__, Stats::STAT_NAMES[i]);
+      }
+    }
+  }
+}
+
+void Stats::increment(Stats::STAT st, int step /* = 1 */) {
+  if (g_system) {
+    if (!g_system->increment(g_stat_indices[st], step)) {
+      Utils::ERROR_LOG("[%s] Unable to increment stat [%s] by step [%d]", __FUNCTION__, step,
+                       Stats::STAT_NAMES[st]);
+    }
+  }
+}

Added: trafficserver/plugins/trunk/esi/lib/Stats.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Stats.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Stats.h (added)
+++ trafficserver/plugins/trunk/esi/lib/Stats.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,40 @@
+#ifndef _ESI_STATS_H
+
+#define _ESI_STATS_H
+
+#include "Utils.h"
+#include <InkAPI.h>
+
+namespace EsiLib {
+
+/** interface that stat systems should implement */
+class StatSystem {
+public:
+  virtual bool create(const char *name, uint32_t *handle) = 0;
+  virtual bool increment(uint32_t handle, int step = 1) = 0;
+  virtual ~StatSystem() { };
+};
+
+namespace Stats {
+
+enum STAT { N_OS_DOCS = 0,
+            N_CACHE_DOCS = 1,
+            N_PARSE_ERRS = 2,
+            N_INCLUDES = 3,
+            N_INCLUDE_ERRS = 4,
+            N_SPCL_INCLUDES = 5,
+            N_SPCL_INCLUDE_ERRS = 6,
+            MAX_STAT_ENUM = 7 };
+
+extern const char *STAT_NAMES[MAX_STAT_ENUM];
+
+void init(StatSystem *system);
+
+void increment(STAT st, int step = 1);
+
+};
+
+};
+              
+
+#endif

Added: trafficserver/plugins/trunk/esi/lib/StringHash.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/StringHash.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/StringHash.h (added)
+++ trafficserver/plugins/trunk/esi/lib/StringHash.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,24 @@
+#ifndef _STRING_HASH_H
+
+#define _STRING_HASH_H
+
+#include <string>
+#include <ext/hash_map>
+
+namespace EsiLib {
+
+struct StringHasher {
+  inline size_t operator ()(const std::string &str) const {
+    return __gnu_cxx::hash<const char *>()(str.c_str());
+  };
+};
+
+typedef __gnu_cxx::hash_map<std::string, std::string, StringHasher> StringHash;
+
+template<typename T>
+class StringKeyHash : public __gnu_cxx::hash_map<std::string, T, StringHasher> {
+};
+
+};
+
+#endif // _STRING_HASH_H

Added: trafficserver/plugins/trunk/esi/lib/Utils.cc
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Utils.cc?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Utils.cc (added)
+++ trafficserver/plugins/trunk/esi/lib/Utils.cc Tue Nov  8 19:07:20 2011
@@ -0,0 +1,172 @@
+#include "Utils.h"
+
+#include <sstream>
+
+using namespace EsiLib;
+
+ComponentBase::Debug Utils::DEBUG_LOG(0);
+ComponentBase::Error Utils::ERROR_LOG(0);
+
+#define DEBUG_TAG "EsiUtils"
+
+using std::string;
+
+void
+Utils::init(ComponentBase::Debug debug_func, ComponentBase::Error error_func) {
+  DEBUG_LOG = debug_func;
+  ERROR_LOG = error_func;
+}
+
+bool
+Utils::getAttribute(const string &data, const string &attr, size_t curr_pos, size_t end_pos,
+                    Attribute &attr_info,
+                    size_t *term_pos /* = 0 */, char terminator /* = 0 */) {
+  size_t attr_start = data.find(attr, curr_pos);
+  if (attr_start >= end_pos) {
+    ERROR_LOG("[%s] Tag has no [%.*s] attribute", __FUNCTION__, attr.size(), attr.data());
+    return false;
+  }
+  curr_pos = attr_start + attr.size();
+  bool equals_found = false;
+  for (; curr_pos < end_pos; ++curr_pos) {
+    if (data[curr_pos] == ' ') {
+      continue;
+    } else {
+      if (data[curr_pos] == '=') {
+        equals_found = true;
+      }
+      break;
+    }
+  }
+  if (!equals_found) {
+    ERROR_LOG("[%s] Attribute [%.*s] has no value", __FUNCTION__, attr.size(), attr.data());
+    return false;
+  }
+  ++curr_pos;
+  if (curr_pos == end_pos) {
+    ERROR_LOG("[%s] No space for value after [%.*s] attribute", __FUNCTION__, attr.size(), attr.data());
+    return false;
+  }
+  bool in_quoted_part = false;
+  bool quoted = false;
+  size_t i;
+  for (i = curr_pos; i < end_pos; ++i) {
+    if (data[i] == '"') {
+      quoted = true;
+      in_quoted_part = !in_quoted_part;
+    }
+    else if (data[i] == ' ') {
+      if (!in_quoted_part) {
+        break;
+      }
+    } else if (terminator && !in_quoted_part && (data[i] == terminator)) {
+      break;
+    }
+  }
+  const char *data_start_ptr = data.data();
+  if (in_quoted_part) {
+    ERROR_LOG("[%s] Unterminated quote in value for attribute [%.*s] starting at [%.10s]",
+              __FUNCTION__, attr.size(), attr.data(), data_start_ptr + curr_pos);
+    return false;
+  }
+  if (terminator && term_pos) {
+    *term_pos = data.find(terminator, i);
+    if (*term_pos >= end_pos) {
+      ERROR_LOG("[%s] Unterminated attribute [%.*s]", __FUNCTION__, attr.size(), attr.data());
+      return false;
+    }
+  }
+  attr_info.name = data_start_ptr + attr_start;
+  attr_info.name_len = attr.size();
+  attr_info.value = data_start_ptr + curr_pos;
+  attr_info.value_len = i - curr_pos;
+  if (quoted) {
+    ++attr_info.value;
+    attr_info.value_len -= 2;
+  }
+  return true;
+}
+
+void
+Utils::parseKeyValueConfig(const std::list<string> &lines, KeyValueMap &kvMap) {
+  string key, value;
+  std::istringstream iss;
+  for (std::list<string>::const_iterator list_iter = lines.begin(); list_iter != lines.end(); ++list_iter) {
+    const string &conf_line = *list_iter; // handy reference
+    if (!conf_line.size() || (conf_line[0] == '#')) {
+      continue;
+    }
+    iss.clear();
+    iss.str(conf_line);
+    if (iss.good()) {
+      iss >> key;
+      iss >> value;
+      if (key.size() && value.size()) {
+        kvMap.insert(KeyValueMap::value_type(key, value));
+        DEBUG_LOG(DEBUG_TAG, "[%s] Read value [%s] for key [%s]", __FUNCTION__, value.c_str(), key.c_str());
+      }
+    }
+    key.clear();
+    value.clear();
+  }
+}
+
+void
+Utils::parseAttributes(const char *data, int data_len, AttributeList &attr_list,
+                       const char *pair_separators /* = " " */) {
+  attr_list.clear();
+  if (!data || (data_len <= 0)) {
+    return;
+  }
+  char separator_lookup[256] = { 0 };
+  int i;
+  for (i = 0; pair_separators[i]; ++i) {
+    separator_lookup[static_cast<unsigned int>(pair_separators[i])] = 1;
+  }
+  Attribute attr;
+  bool inside_quotes = false, end_of_attribute;
+  bool escape_on = false;
+  for (i = 0; (i < data_len) && ((isspace(data[i]) || separator_lookup[static_cast<unsigned int>(data[i])]));
+       ++i);
+  attr.name = data + i;
+  attr.value = 0;
+  for (; i <= data_len; ++i) {
+    end_of_attribute = false;
+    if (i == data_len) {
+      end_of_attribute = true;
+    } else if (separator_lookup[static_cast<unsigned int>(data[i])] && !inside_quotes) {
+      end_of_attribute = true;
+    } // else ignore separator when in quotes
+    if (end_of_attribute) {
+      if (!inside_quotes) {
+        if (attr.value > attr.name) {
+          attr.value_len = data + i - attr.value;
+          trimWhiteSpace(attr.name, attr.name_len);
+          trimWhiteSpace(attr.value, attr.value_len);
+          if (attr.value[0] == '"') {
+            ++attr.value;
+            attr.value_len -= 2;
+          }
+          if (attr.name_len && attr.value_len) {
+            DEBUG_LOG(DEBUG_TAG, "[%s] Added attribute with name [%.*s] and value [%.*s]",
+                      __FUNCTION__, attr.name_len, attr.name, attr.value_len, attr.value);
+            attr_list.push_back(attr);
+          } // else ignore empty name/value
+        } // else ignore attribute with no value
+      } // else ignore variable with unterminated quotes
+      for(; (i < data_len) && ((isspace(data[i]) || separator_lookup[static_cast<unsigned int>(data[i])]));
+          ++i);
+      attr.name = data + i;
+      attr.value = 0;
+      inside_quotes = false;
+    } else if (data[i] == '"') {
+      if (!escape_on) { 
+        inside_quotes = !inside_quotes;
+      }
+    } else if ((data[i] == '=') && !attr.value && !inside_quotes) {
+      attr.value = data + i + 1;
+      attr.name_len = data + i - attr.name;
+    }
+    escape_on = (data[i] == '\\') ? true : false;
+  }
+}

Added: trafficserver/plugins/trunk/esi/lib/Utils.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Utils.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Utils.h (added)
+++ trafficserver/plugins/trunk/esi/lib/Utils.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,94 @@
+#ifndef _ESI_UTILS_H 
+
+#define _ESI_UTILS_H
+
+#include "DocNode.h"
+#include "ComponentBase.h"
+
+#include <string>
+#include <map>
+#include <list>
+#include <ctype.h>
+
+namespace EsiLib {
+
+namespace Utils {
+
+  extern ComponentBase::Debug DEBUG_LOG;
+
+  extern ComponentBase::Error ERROR_LOG;
+
+  void init(ComponentBase::Debug debug_func, ComponentBase::Error error_func);
+
+  // looks for the given attribute in given data; a terminator can
+  // also be specified apart from the end_pos; attr_info will point
+  // to data inside data string
+  bool getAttribute(const std::string &data, const std::string &attr, size_t curr_pos, size_t end_pos,
+                    Attribute &attr_info, size_t *term_pos = 0, char terminator = 0);
+  
+  // less specialized version of method above
+  inline bool getAttribute(const std::string &data, const char *attr, Attribute &attr_info) {
+    return getAttribute(data, std::string(attr), 0, data.size(), attr_info);
+  }
+
+  // trims leading and trailing white space; input arguments
+  // will be modified to reflect trimmed data
+  inline void trimWhiteSpace(const char *&data, int &data_len) {
+    if (!data) {
+      data_len = 0;
+    } else {
+      if (data_len == -1) {
+        data_len = strlen(data);
+      }
+      int i, j;
+      for (i = 0; ((i < data_len) && isspace(data[i])); ++i);
+      for (j = (data_len - 1); ((j > i) && isspace(data[j])); --j);
+      data += i;
+      data_len = j - i + 1;
+    }
+  }
+
+  // does case insensitive comparison
+  inline bool areEqual(const char *str1, int str1_len, const char *str2, int str2_len) {
+    if (str1_len == str2_len) {
+      return (strncasecmp(str1, str2, str1_len) == 0);
+    }
+    return false;
+  }
+
+  inline bool areEqual(const char *str1, int str1_len, const std::string &str2) {
+    return areEqual(str1, str1_len, str2.data(), static_cast<int>(str2.size()));
+  }
+
+  // parses a string of name=value attributes separated by any character in pair_separators;
+  void parseAttributes(const char *data, int data_len, AttributeList &attr_list,
+                       const char *pair_separators = " ");
+
+  inline void parseAttributes(const std::string &data, AttributeList &attr_list,
+                              const char *pair_separators = " ") {
+    parseAttributes(data.data(), data.size(), attr_list, pair_separators);
+  }
+
+  typedef std::map<std::string, std::string> KeyValueMap;
+
+  // parses given lines (assumes <key><whitespace><value> format) and
+  // stores them in supplied map; Lines beginning with '#' are ignored
+  void parseKeyValueConfig(const std::list<std::string> &lines, KeyValueMap &kvMap);
+  
+  inline std::string unescape(const char *str, int len = -1) {
+    std::string retval("");
+    if (str) { 
+      for (int i = 0; (((len == -1) && (str[i] != '\0')) || (i < len)); ++i) {
+        if (str[i] != '\\') {
+          retval += str[i];
+        }
+      }
+    }
+    return retval;
+  }
+
+};
+
+};
+
+#endif

Added: trafficserver/plugins/trunk/esi/lib/Variables.cc
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Variables.cc?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Variables.cc (added)
+++ trafficserver/plugins/trunk/esi/lib/Variables.cc Tue Nov  8 19:07:20 2011
@@ -0,0 +1,404 @@
+#include "Variables.h"
+#include "Attribute.h"
+#include "Utils.h"
+
+#include <errno.h>
+
+using std::list;
+using std::pair;
+using std::string;
+using namespace EsiLib;
+
+const string Variables::EMPTY_STRING("");
+const string Variables::TRUE_STRING("true");
+const string Variables::VENDOR_STRING("vendor");
+const string Variables::VERSION_STRING("version");
+const string Variables::PLATFORM_STRING("platform");
+const string Variables::SIMPLE_HEADERS[] = { string("HOST"),
+                                             string("REFERER"),
+                                             string("") };
+
+const string Variables::SPECIAL_HEADERS[] = { string("ACCEPT-LANGUAGE"),
+                                              string("COOKIE"),
+                                              string("USER-AGENT"),
+                                              string("QUERY_STRING"),
+                                              string("") };
+
+const string Variables::NORM_SIMPLE_HEADERS[] = { string("HTTP_HOST"),
+                                                  string("HTTP_REFERER"),
+                                                  string("") };
+
+const string Variables::NORM_SPECIAL_HEADERS[] = { string("HTTP_ACCEPT_LANGUAGE"),
+                                                   string("HTTP_COOKIE"),
+                                                   string("HTTP_USER_AGENT"),
+                                                   string("QUERY_STRING"),
+                                                   string("") };
+
+inline string &
+Variables::_toUpperCase(string &str) const {
+  for (size_t i = 0; i < str.size(); ++i) {
+    if ((str[i] >= 'a') && (str[i] <= 'z')) {
+      str[i] = 'A' + (str[i] - 'a');
+    }
+  }
+  return str;
+}
+
+inline int 
+Variables::_searchHeaders(const string headers[], const char *name, int name_len) const {
+  int curr_header_size;
+  for (int i = 0; (curr_header_size = static_cast<int>(headers[i].size())); ++i) {
+    if ((name_len == curr_header_size) && 
+        (strncasecmp(headers[i].data(), name, curr_header_size) == 0)) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+inline void
+Variables::_insert(StringHash &hash, const std::string &key, const std::string &value) {
+  std::pair<StringHash::iterator, bool> result = hash.insert(StringHash::value_type(key, value));
+  if (!result.second) {
+    result.first->second = value;
+  }
+}
+
+void
+Variables::populate(const HttpHeader &header) {
+  if (header.name && header.name_len && header.value && header.value_len) {
+
+    // doing this because we can't change our const input arg
+    int name_len = (header.name_len == -1) ? strlen(header.name) : header.name_len;
+    int value_len = (header.value_len == -1) ? strlen(header.value) : header.value_len;
+
+    // we need to save the cookie string to build the jar from
+    if ((name_len == 6) && (strncasecmp(header.name, "Cookie", 6) == 0)) {
+      _releaseCookieJar();
+      if (_cookie_str.size()) {
+        _cookie_str.append(", ");
+      }
+      _cookie_str.append(header.value, value_len);
+    }
+
+    if (_headers_parsed) {
+      _parseHeader(header.name, name_len, header.value, value_len);
+    } else {
+      int match_index = _searchHeaders(SIMPLE_HEADERS, header.name, name_len);
+      if (match_index != -1) {
+        _cached_simple_headers[match_index].push_back(string(header.value, value_len));
+      } else {
+        match_index = _searchHeaders(SPECIAL_HEADERS, header.name, name_len);
+        if (match_index != -1) {
+          _cached_special_headers[match_index].push_back(string(header.value, value_len));
+        } else {
+          _debugLog(_debug_tag.c_str(), "[%s] Not retaining header [%.*s]", __FUNCTION__, name_len,
+                    header.name);
+        }
+      }
+    }
+  }
+}
+
+inline void
+Variables::_parseSimpleHeader(SimpleHeader hdr, const string &value) {
+  _debugLog(_debug_tag.c_str(), "[%s] Inserting value for simple header [%s]",
+            __FUNCTION__, SIMPLE_HEADERS[hdr].c_str());
+  _simple_data[NORM_SIMPLE_HEADERS[hdr]] = value;
+}
+
+inline void
+Variables::_parseSimpleHeader(SimpleHeader hdr, const char *value, int value_len) {
+  _parseSimpleHeader(hdr, string(value, value_len));
+}
+
+void
+Variables::_parseSpecialHeader(SpecialHeader hdr, const char *value, int value_len) {
+  switch (hdr) {
+  case HTTP_ACCEPT_LANGUAGE:
+    _parseAcceptLangString(value, value_len);
+    break;
+  case HTTP_COOKIE:
+    _parseCookieString(value, value_len);
+    break;
+  case HTTP_USER_AGENT:
+    _parseUserAgentString(value, value_len);
+    break;
+  default:
+    _debugLog(_debug_tag.c_str(), "[%s] Skipping unrecognized header", __FUNCTION__);
+    break;
+  }
+}
+
+void
+Variables::_parseHeader(const char *name, int name_len, const char *value, int value_len) {
+  int match_index = _searchHeaders(SIMPLE_HEADERS, name, name_len);
+  if (match_index != -1) {
+    _parseSimpleHeader(static_cast<SimpleHeader>(match_index), value, value_len);
+  } else {
+    match_index = _searchHeaders(SPECIAL_HEADERS, name, name_len);
+    if (match_index != -1) {
+      _parseSpecialHeader(static_cast<SpecialHeader>(match_index), value, value_len);
+    } else {
+      _debugLog(_debug_tag.c_str(), "[%s] Unrecognized header [%.*s]", __FUNCTION__, value_len, value);
+    }
+  }
+}
+
+void
+Variables::_parseQueryString(const char *query_string, int query_string_len) {
+  _insert(_simple_data, string("QUERY_STRING"), string(query_string, query_string_len));
+  AttributeList attr_list;
+  Utils::parseAttributes(query_string, query_string_len, attr_list, "&");
+  for (AttributeList::iterator iter = attr_list.begin(); iter != attr_list.end(); ++iter) {
+    _debugLog(_debug_tag.c_str(), "[%s] Inserting query string variable [%.*s] with value [%.*s]",
+              __FUNCTION__, iter->name_len, iter->name, iter->value_len, iter->value);
+    _insert(_dict_data[QUERY_STRING], string(iter->name, iter->name_len),
+            string(iter->value, iter->value_len));
+  }
+}
+
+void
+Variables::_parseCachedHeaders() {
+  _debugLog(_debug_tag.c_str(), "[%s] Parsing headers", __FUNCTION__);
+  for (int i = 0; i < N_SIMPLE_HEADERS; ++i) {
+    for (HeaderValueList::iterator value_iter = _cached_simple_headers[i].begin();
+         value_iter != _cached_simple_headers[i].end(); ++value_iter) {
+      _parseSimpleHeader(static_cast<SimpleHeader>(i), *value_iter);
+    }
+  }
+  for (int i = 0; i < N_SPECIAL_HEADERS; ++i) {
+    for (HeaderValueList::iterator value_iter = _cached_special_headers[i].begin();
+         value_iter != _cached_special_headers[i].end(); ++value_iter) {
+      _parseSpecialHeader(static_cast<SpecialHeader>(i), value_iter->data(), value_iter->size());
+    }
+  }
+}
+
+const std::string &
+Variables::getValue(const string &name) const {
+  if (!_headers_parsed || !_query_string_parsed) {
+    // we need to do this because we want to
+    // 1) present const getValue() to clients
+    // 2) parse lazily (only on demand)
+    Variables &non_const_self = const_cast<Variables &>(*this);
+    if (!_headers_parsed) {
+      non_const_self._parseCachedHeaders();
+      non_const_self._headers_parsed = true;
+    }
+    if (!_query_string_parsed) {
+      int query_string_size = static_cast<int>(_query_string.size());
+      if (query_string_size) {
+        non_const_self._parseQueryString(_query_string.data(), query_string_size);
+        non_const_self._query_string_parsed = true;
+      }
+    }
+  }
+  string search_key(name);
+  _toUpperCase(search_key);
+  StringHash::const_iterator iter = _simple_data.find(search_key);
+  if (iter != _simple_data.end()) {
+    _debugLog(_debug_tag.c_str(), "[%s] Found value [%.*s] for variable [%.*s] in simple data", 
+              __FUNCTION__, iter->second.size(), iter->second.data(), name.size(), name.data());
+    return iter->second;
+  }
+  const char *header;
+  int header_len;
+  const char *attr;
+  int attr_len;
+  if (!_parseDictVariable(name, header, header_len, attr, attr_len)) {
+    _debugLog(_debug_tag.c_str(), "[%s] Unmatched simple variable [%.*s] not in dict variable form",
+              __FUNCTION__, name.size(), name.data());
+    return EMPTY_STRING;
+  }
+  int dict_index = _searchHeaders(NORM_SPECIAL_HEADERS, header, header_len); // ignore the HTTP_ prefix
+  if (dict_index == -1) {
+    _debugLog(_debug_tag.c_str(), "[%s] Dict variable [%.*s] refers to unknown dictionary",
+              __FUNCTION__, name.size(), name.data());
+    return EMPTY_STRING;
+  }
+
+  // change variable name to use only the attribute field
+  search_key.assign(attr, attr_len);
+
+  iter = _dict_data[dict_index].find(search_key);
+
+  if (dict_index == HTTP_ACCEPT_LANGUAGE) {
+    _debugLog(_debug_tag.c_str(), "[%s] Returning boolean literal for lang variable [%.*s]",
+              __FUNCTION__, search_key.size(), search_key.data());
+    return (iter == _dict_data[dict_index].end()) ? EMPTY_STRING : TRUE_STRING;
+  }
+  
+  if (iter != _dict_data[dict_index].end()) {
+    _debugLog(_debug_tag.c_str(), "[%s] Found variable [%.*s] in %s dictionary with value [%.*s]",
+              __FUNCTION__, search_key.size(), search_key.data(), NORM_SPECIAL_HEADERS[dict_index].c_str(), 
+              iter->second.size(), iter->second.data());
+    return iter->second;
+  }
+
+  size_t cookie_part_divider = (dict_index == HTTP_COOKIE) ? search_key.find(';') : search_key.size();
+  if (cookie_part_divider && (cookie_part_divider < (search_key.size() - 1))) {
+    _debugLog(_debug_tag.c_str(), "[%s] Cookie variable [%s] refers to sub cookie", 
+              __FUNCTION__, search_key.c_str());
+    return _getSubCookieValue(search_key, cookie_part_divider);
+  }
+  
+  _debugLog(_debug_tag.c_str(), "[%s] Found no value for dict variable [%s]", __FUNCTION__, name.c_str());
+  return EMPTY_STRING;
+}
+
+const string &
+Variables::_getSubCookieValue(const string &cookie_str, size_t cookie_part_divider) const {
+  if (!_cookie_jar_created) {
+    if (_cookie_str.size()) {
+      Variables &non_const_self = const_cast<Variables &>(*this); // same reasoning as in getValue()
+      // TODO - code was here
+      non_const_self._cookie_jar_created = true;
+    } else {
+      _debugLog(_debug_tag.c_str(), "[%s] Cookie string empty; nothing to construct jar from", __FUNCTION__);
+    }
+  }
+  if (_cookie_jar_created) {
+    // we need to do this as we are going to manipulate the 'divider'
+    // character, and we don't need to create a copy of the string for
+    // that; hence this shortcut
+    string &non_const_cookie_str = const_cast<string &>(cookie_str);
+    
+    non_const_cookie_str[cookie_part_divider] = '\0'; // make sure cookie name is NULL terminated
+    const char *cookie_name = non_const_cookie_str.data(); /* above NULL will take effect */
+    const char *part_name = 
+      non_const_cookie_str.c_str() /* NULL terminate the part */ + cookie_part_divider + 1;
+    bool user_name = (part_name[0] == 'u') && (part_name[1] == '\0');
+    if (user_name) {
+      part_name = "l";
+    }
+    // TODO - code was here
+    const char *sub_cookie_value = NULL;
+    non_const_cookie_str[cookie_part_divider] = ';'; // restore before returning
+    if (!sub_cookie_value) {
+      _debugLog(_debug_tag.c_str(), "[%s] Could not find value for part [%s] of cookie [%.*s]", __FUNCTION__,
+                part_name, cookie_part_divider, cookie_name);
+      return EMPTY_STRING;
+    } else {
+      // we need to do this as have to return a string reference
+      string &retval = const_cast<Variables &>(*this)._cached_sub_cookie_value;
+
+      if (user_name) {
+        char unscrambled_login[256];
+        // TODO - code was here
+        _debugLog(_debug_tag.c_str(), "[%s] Unscrambled login name to [%s]", __FUNCTION__, unscrambled_login);
+        retval.assign(unscrambled_login);
+      } else {
+        _debugLog(_debug_tag.c_str(), "[%s] Got value [%s] for cookie name [%.*s] and part [%s]",
+                  __FUNCTION__, sub_cookie_value, cookie_part_divider, cookie_name, part_name);
+        retval.assign(sub_cookie_value);
+      }
+      return retval;
+    }
+  } else {
+    _errorLog("[%s] Cookie jar not available; Returning empty string", __FUNCTION__);
+    return EMPTY_STRING;
+  }
+}
+
+void
+Variables::clear() {
+  _simple_data.clear();
+  for (int i = 0; i < N_SPECIAL_HEADERS; ++i) {
+    _dict_data[i].clear();
+    _cached_special_headers[i].clear();
+  }
+  for (int i = 0; i < N_SIMPLE_HEADERS; ++i) {
+    _cached_simple_headers[i].clear();
+  }
+  _query_string.clear();
+  _headers_parsed = _query_string_parsed = false;
+  _cookie_str.clear();
+  _releaseCookieJar();
+}
+
+void
+Variables::_parseCookieString(const char *str, int str_len) {
+  AttributeList cookies;
+  Utils::parseAttributes(str, str_len, cookies, ";,");
+  for (AttributeList::iterator iter = cookies.begin(); iter != cookies.end(); ++iter) {
+    _insert(_dict_data[HTTP_COOKIE], string(iter->name, iter->name_len), string(iter->value, iter->value_len));
+    _debugLog(_debug_tag.c_str(), "[%s] Inserted cookie with name [%.*s] and value [%.*s]", __FUNCTION__,
+              iter->name_len, iter->name, iter->value_len, iter->value);
+  }
+}
+
+void
+Variables::_parseUserAgentString(const char *str, int str_len) {
+  string user_agent_str(str, str_len); // need NULL-terminated version
+  // TODO - code was here
+  char version_buf[64];
+  // TODO - code was here
+  _insert(_dict_data[HTTP_USER_AGENT], VERSION_STRING, version_buf);
+}
+
+void
+Variables::_parseAcceptLangString(const char *str, int str_len) {
+  int i;
+  for(i = 0; (i < str_len) && ((isspace(str[i]) || str[i] == ',')); ++i);
+  const char *lang = str + i;
+  int lang_len;
+  for (; i <= str_len; ++i) {
+    if ((i == str_len) || (str[i] == ',')) {
+      lang_len = str + i - lang;
+      for (; lang_len && isspace(lang[lang_len - 1]); --lang_len);
+      if (lang_len) {
+        _insert(_dict_data[HTTP_ACCEPT_LANGUAGE], string(lang, lang_len), EMPTY_STRING);
+        _debugLog(_debug_tag.c_str(), "[%s] Added language [%.*s]", __FUNCTION__, lang_len, lang);
+      }
+      for(; (i < str_len) && ((isspace(str[i]) || str[i] == ',')); ++i);
+      lang = str + i;
+    }
+  }
+}
+
+bool
+Variables::_parseDictVariable(const std::string &variable, const char *&header, int &header_len,
+                              const char *&attr, int &attr_len) const {
+  const char *var_ptr = variable.data();
+  int var_size = variable.size();
+  if ((var_size <= 4) || (variable[var_size - 1] != '}')) {
+    return false;
+  }
+  int paranth_index = -1;
+  for (int i = 0; i < (var_size - 1); ++i) {
+    if (variable[i] == '{') {
+      if (paranth_index != -1) {
+        _debugLog(_debug_tag.c_str(), "[%s] Cannot have multiple paranthesis in dict variable [%.*s]",
+                  __FUNCTION__, var_size, var_ptr);
+        return false;
+      }
+      paranth_index = i;
+    }
+    if (variable[i] == '}') {
+      _debugLog(_debug_tag.c_str(), "[%s] Cannot have multiple paranthesis in dict variable [%.*s]",
+                __FUNCTION__, var_size, var_ptr);
+      return false;
+    }
+  }
+  if (paranth_index == -1) {
+    _debugLog(_debug_tag.c_str(), "[%s] Could not find opening paranthesis in variable [%.*s]",
+              __FUNCTION__, var_size, var_ptr);
+    return false;
+  }
+  if (paranth_index == 0) {
+    _debugLog(_debug_tag.c_str(), "[%s] Dict variable has no dict name [%.*s]",
+              __FUNCTION__, var_size, var_ptr);
+    return false;
+  }
+  if (paranth_index == (var_size - 2)) {
+    _debugLog(_debug_tag.c_str(), "[%s] Dict variable has no attribute name [%.*s]",
+              __FUNCTION__, var_size, var_ptr);
+    return false;
+  }
+  header = var_ptr;
+  header_len = paranth_index;
+  attr = var_ptr + paranth_index + 1;
+  attr_len = var_size - header_len - 2;
+  return true;
+}

Added: trafficserver/plugins/trunk/esi/lib/Variables.h
URL: http://svn.apache.org/viewvc/trafficserver/plugins/trunk/esi/lib/Variables.h?rev=1199388&view=auto
==============================================================================
--- trafficserver/plugins/trunk/esi/lib/Variables.h (added)
+++ trafficserver/plugins/trunk/esi/lib/Variables.h Tue Nov  8 19:07:20 2011
@@ -0,0 +1,132 @@
+#ifndef _ESI_VARIABLES_H
+
+#define _ESI_VARIABLES_H
+
+#include <list>
+#include <boost/noncopyable.hpp>
+
+#include "ComponentBase.h"
+#include "StringHash.h"
+#include "HttpHeader.h"
+
+namespace EsiLib {
+
+class Variables : private ComponentBase, boost::noncopyable {
+
+public:
+
+  Variables(const char *debug_tag, ComponentBase::Debug debug_func, ComponentBase::Error error_func)
+    : ComponentBase(debug_tag, debug_func, error_func), _headers_parsed(false), _query_string(""),
+      _query_string_parsed(false), _cookie_jar_created(false) { };
+  
+  /** currently 'host', 'referer', 'accept-language', 'cookie' and 'user-agent' headers are parsed */
+  void populate(const HttpHeader &header);
+
+  void populate(const HttpHeaderList &headers) {
+    for (HttpHeaderList::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) {
+      populate(*iter);
+    }
+  };
+
+  void populate(const char *query_string, int query_string_len = -1) {
+    if (query_string && (query_string_len != 0)) {
+      if (query_string_len == -1) {
+        query_string_len = strlen(query_string);
+      }
+      if (_query_string_parsed) {
+        _parseQueryString(query_string, query_string_len);
+      } else {
+        _query_string.assign(query_string, query_string_len);
+      }
+    }
+  }
+
+  /** returns value of specified variable; empty string returned for unknown variable; key
+   * has to be prefixed with 'http_' string for all variable names except 'query_string' */
+  const std::string &getValue(const std::string &name) const;
+
+  /** convenient alternative for method above */
+  const std::string &getValue(const char *name, int name_len = -1) const {
+    if (!name) {
+      return EMPTY_STRING;
+    }
+    std::string var_name;
+    if (name_len == -1) {
+      var_name.assign(name);
+    } else {
+      var_name.assign(name, name_len);
+    }
+    return getValue(var_name);
+  }
+
+  void clear();
+
+  virtual ~Variables() { _releaseCookieJar(); };
+
+private:
+
+  static const std::string EMPTY_STRING;
+  static const std::string TRUE_STRING;
+  static const std::string VENDOR_STRING;
+  static const std::string VERSION_STRING;
+  static const std::string PLATFORM_STRING;
+
+  enum SimpleHeader { HTTP_HOST = 0, HTTP_REFERER = 1 };
+  static const std::string SIMPLE_HEADERS[];  // indices should map to enum values above
+
+  enum SpecialHeader { HTTP_ACCEPT_LANGUAGE = 0, HTTP_COOKIE = 1, HTTP_USER_AGENT = 2, QUERY_STRING = 3 };
+  static const std::string SPECIAL_HEADERS[];  // indices should map to enum values above
+
+  // normalized versions of the headers above; indices should correspond correctly
+  static const std::string NORM_SIMPLE_HEADERS[];
+  static const std::string NORM_SPECIAL_HEADERS[]; // indices should again map to enum values
+
+  static const int N_SIMPLE_HEADERS = HTTP_REFERER + 1;
+  static const int N_SPECIAL_HEADERS = QUERY_STRING + 1;
+
+  StringHash _simple_data;
+  StringHash _dict_data[N_SPECIAL_HEADERS];
+
+  inline std::string &_toUpperCase(std::string &str) const;
+  inline int _searchHeaders(const std::string headers[], const char *name, int name_len) const;
+  bool _parseDictVariable(const std::string &variable, const char *&header, int &header_len,
+                                 const char *&attr, int &attr_len) const;
+  void _parseCookieString(const char *str, int str_len);
+  void _parseUserAgentString(const char *str, int str_len);
+  void _parseAcceptLangString(const char *str, int str_len);
+  inline void _parseSimpleHeader(SimpleHeader hdr, const std::string &value);
+  inline void _parseSimpleHeader(SimpleHeader hdr, const char *value, int value_len);
+  void _parseSpecialHeader(SpecialHeader hdr, const char *value, int value_len);
+  void _parseCachedHeaders();
+
+  inline void _insert(StringHash &hash, const std::string &key, const std::string &value);
+
+  typedef std::list<std::string> HeaderValueList;
+  HeaderValueList _cached_simple_headers[N_SIMPLE_HEADERS];
+  HeaderValueList _cached_special_headers[N_SPECIAL_HEADERS];
+  
+  std::string _cookie_str;
+  bool _headers_parsed;
+  std::string _query_string;
+  bool _query_string_parsed;
+
+  void _parseHeader(const char *name, int name_len, const char *value, int value_len);
+  void _parseQueryString(const char *query_string, int query_string_len);
+  
+  // TODO - code was here
+  bool _cookie_jar_created;
+
+  inline void _releaseCookieJar() {
+    if (_cookie_jar_created) {
+      _cookie_jar_created = false;
+    }
+  }
+
+  std::string _cached_sub_cookie_value;
+  const std::string &_getSubCookieValue(const std::string &cookie_str, size_t cookie_part_divider) const;
+
+};
+
+};
+
+#endif // _ESI_VARIABLES_H



Mime
View raw message