You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2012/06/13 22:18:14 UTC

[7/11] Moved these plugins from the separate git repo

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/esi/test/processor_test.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/test/processor_test.cc b/plugins/experimental/esi/test/processor_test.cc
new file mode 100644
index 0000000..c0147ea
--- /dev/null
+++ b/plugins/experimental/esi/test/processor_test.cc
@@ -0,0 +1,1132 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  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.
+ */
+
+#include <iostream>
+#include <assert.h>
+#include <string>
+
+#include "EsiProcessor.h"
+#include "TestHttpDataFetcher.h"
+#include "print_funcs.h"
+#include "Utils.h"
+#include "HandlerMap.h"
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+using namespace EsiLib;
+
+static const int FETCHER_STATIC_DATA_SIZE = 30;
+
+int main() 
+{
+  Variables esi_vars("vars", &Debug, &Error);
+  HandlerManager handler_mgr("handler_mgr", &Debug, &Error);
+
+  Utils::init(&Debug, &Error);
+
+  {
+    cout << endl << "===================== Test 1) call sequence" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("");
+    const char *output_data;
+    int output_data_len;
+
+    cout << "Negative test - process()ing without completeParse()ing..." << endl;
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::FAILURE);
+    esi_proc.stop();
+
+    cout << "Implicit call to start() #1..." << endl;
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 0);
+    esi_proc.stop();
+
+    cout << "Implicit call to start() #2..." << endl;
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 0);
+    esi_proc.stop();
+
+    cout << "Negative test: calling process() before start()" << endl;
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::FAILURE);
+
+    cout << "Negative test: calling addParseData() after process()" << endl;
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 0);
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == false);
+    esi_proc.stop();
+
+    cout << "Negative test: calling completeParse() after process()" << endl;
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 0);
+    assert(esi_proc.completeParse() == false);
+    esi_proc.stop();
+
+    cout << "Good call sequence with no data" << endl;
+    assert(esi_proc.start() == true);
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 2) Negative test: invalid ESI tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo<esi:blah/>bar");
+    
+    const char *output_data;
+    int output_data_len = 10;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::FAILURE);
+    assert(output_data_len == 10); // should remain unchanged
+  }
+  
+  {
+    cout << endl << "===================== Test 3) comment tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo<esi:comment text=\"bleh\"/>bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 6);
+    assert(strncmp(output_data, "foobar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 4) comment tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:comment text=\"bleh\"/>bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 3);
+    assert(strncmp(output_data, "bar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 5) comment tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo<esi:comment text=\"bleh\"/>");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 3);
+    assert(strncmp(output_data, "foo", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 6) multi-line comment tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo\n<esi:comment text=\"\nbleh\"/>\nbar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 8);
+    assert(strncmp(output_data, "foo\n\nbar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 7) multi-line remove tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo\n<esi:remove><img src=\"http://www.example.com\"></esi:remove>\nbar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 8);
+    assert(strncmp(output_data, "foo\n\nbar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 8) remove and comment tags" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo\n<esi:remove><img src=\"http://www.example.com\"></esi:remove>\nbar"
+                      "foo2\n<esi:comment text=\"bleh\"/>\nbar2");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 18);
+    assert(strncmp(output_data, "foo\n\nbarfoo2\n\nbar2", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 9) multiple remove and comment tags" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo1<esi:remove><img src=\"http://www.example.com\"></esi:remove>bar1\n"
+                      "foo1<esi:comment text=\"bleh\"/>bar1\n"
+                      "foo2<esi:remove><img src=\"http://www.example.com\"></esi:remove>bar2\n"
+                      "foo2<esi:comment text=\"bleh\"/>bar2\n"
+                      "foo3<esi:remove><img src=\"http://www.example.com\"></esi:remove>bar3\n"
+                      "foo3<esi:comment text=\"bleh\"/>bar3\n");
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 54);
+    assert(strncmp(output_data, "foo1bar1\nfoo1bar1\nfoo2bar2\nfoo2bar2\nfoo3bar3\nfoo3bar3\n", 
+                   output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 10) include tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:include src=url1/> bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 8 + 4 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, "foo >>>>> Content for URL [url1] <<<<< bar", output_data_len) == 0);
+  }
+  
+  {
+    cout << endl << "===================== Test 11) include tag with no URL" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:include src=/> bar");
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == false);
+  }
+
+  {
+    cout << endl << "===================== Test 12) include tag with no src" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:include /> bar");
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == false);
+  }
+
+  {
+    cout << endl << "===================== Test 13) multiple include tags" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo1 <esi:include src=url1/> bar1\n"
+                      "foo2 <esi:include src=url2/> bar2\n"
+                      "<esi:include src=\"blah bleh\"/>");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 11 + 4 + FETCHER_STATIC_DATA_SIZE + 11 + 4 + FETCHER_STATIC_DATA_SIZE +
+           9 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, 
+                   "foo1 >>>>> Content for URL [url1] <<<<< bar1\n"
+                   "foo2 >>>>> Content for URL [url2] <<<<< bar2\n"
+                   ">>>>> Content for URL [blah bleh] <<<<<", output_data_len) == 0);
+  }
+  
+  {
+    cout << endl << "===================== Test 14) remove, comment and include tags" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo1 <esi:include src=url1/> bar1\n"
+                      "foo2 <esi:include src=url2/> bar2\n"
+                      "<esi:include src=\"blah bleh\"/>"
+                      "<esi:comment text=\"bleh\"/>"
+                      "<esi:remove> <a href=> </esi:remove>");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 11 + 4 + FETCHER_STATIC_DATA_SIZE + 11 + 4 + FETCHER_STATIC_DATA_SIZE +
+           9 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, 
+                   "foo1 >>>>> Content for URL [url1] <<<<< bar1\n"
+                   "foo2 >>>>> Content for URL [url2] <<<<< bar2\n"
+                   ">>>>> Content for URL [blah bleh] <<<<<", output_data_len) == 0);
+  }
+  
+  {
+    cout << endl << "===================== Test 15) multiple addParseData calls" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    char line1[] = "foo1 <esi:include src=url1/> bar1\n";
+    char line2[] = "foo2 <esi:include src=url2/> bar2\n";
+    char line3[] = "<esi:include src=\"blah bleh\"/>";
+    char line4[] = "<esi:comment text=\"bleh\"/>";
+    char line5[] = "<esi:remove> <a href=>";
+    char line6[] = "</esi:remove>";
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(line1, sizeof(line1) - 1) == true);
+    assert(esi_proc.addParseData(line2, sizeof(line2) - 1) == true);
+    assert(esi_proc.addParseData(line3, sizeof(line3) - 1) == true);
+    assert(esi_proc.addParseData(line4, sizeof(line4) - 1) == true);
+    assert(esi_proc.addParseData(line5, sizeof(line5) - 1) == true);
+    assert(esi_proc.addParseData(line6, 13) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 11 + 4 + FETCHER_STATIC_DATA_SIZE + 11 + 4 + FETCHER_STATIC_DATA_SIZE +
+           9 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, 
+                   "foo1 >>>>> Content for URL [url1] <<<<< bar1\n"
+                   "foo2 >>>>> Content for URL [url2] <<<<< bar2\n"
+                   ">>>>> Content for URL [blah bleh] <<<<<", output_data_len) == 0);
+  }
+  
+  {
+    cout << endl << "===================== Test 16) one-shot parse" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo1 <esi:include src=url1/> bar1\n"
+                      "foo2 <esi:include src=url2/> bar2\n"
+                      "<esi:include src=\"blah bleh\"/>"
+                      "<esi:comment text=\"bleh\"/>"
+                      "<esi:remove> <a href=> </esi:remove>");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.completeParse(input_data.data(), input_data.size()) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 11 + 4 + FETCHER_STATIC_DATA_SIZE + 11 + 4 + FETCHER_STATIC_DATA_SIZE +
+           9 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, 
+                   "foo1 >>>>> Content for URL [url1] <<<<< bar1\n"
+                   "foo2 >>>>> Content for URL [url2] <<<<< bar2\n"
+                   ">>>>> Content for URL [blah bleh] <<<<<", output_data_len) == 0);
+  }
+  
+  {
+    cout << endl << "===================== Test 17) final chunk call" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    char line1[] = "foo1 <esi:include src=url1/> bar1\n";
+    char line2[] = "foo2 <esi:include src=url2/> bar2\n";
+    char line3[] = "<esi:include src=\"blah bleh\"/>";
+    char line4[] = "<esi:comment text=\"bleh\"/>";
+    char line5[] = "<esi:remove> <a href=>";
+    char line6[] = "</esi:remove>";
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(line1, sizeof(line1) - 1) == true);
+    assert(esi_proc.addParseData(line2, sizeof(line2) - 1) == true);
+    assert(esi_proc.addParseData(line3, sizeof(line3) - 1) == true);
+    assert(esi_proc.addParseData(line4, sizeof(line4) - 1) == true);
+    assert(esi_proc.addParseData(line5, sizeof(line5) - 1) == true);
+    assert(esi_proc.completeParse(line6, sizeof(line6) - 1) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 11 + 4 + FETCHER_STATIC_DATA_SIZE + 11 + 4 + FETCHER_STATIC_DATA_SIZE +
+           9 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, 
+                   "foo1 >>>>> Content for URL [url1] <<<<< bar1\n"
+                   "foo2 >>>>> Content for URL [url2] <<<<< bar2\n"
+                   ">>>>> Content for URL [blah bleh] <<<<<", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 18) no length arg" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:include src=url1/> bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 8 + 4 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, "foo >>>>> Content for URL [url1] <<<<< bar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 19) std::string arg" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:include src=url1/> bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 8 + 4 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, "foo >>>>> Content for URL [url1] <<<<< bar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 20) one-shot parse, std::string arg" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo1 <esi:include src=url1/> bar1\n"
+                      "foo2 <esi:include src=url2/> bar2\n"
+                      "<esi:include src=\"blah bleh\"/>"
+                      "<esi:comment text=bleh />"
+                      "<esi:remove> <a href=> </esi:remove>");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.completeParse(input_data) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 11 + 4 + FETCHER_STATIC_DATA_SIZE + 11 + 4 + FETCHER_STATIC_DATA_SIZE +
+           9 + FETCHER_STATIC_DATA_SIZE);
+    assert(strncmp(output_data, 
+                   "foo1 >>>>> Content for URL [url1] <<<<< bar1\n"
+                   "foo2 >>>>> Content for URL [url2] <<<<< bar2\n"
+                   ">>>>> Content for URL [blah bleh] <<<<<", output_data_len) == 0);
+  }
+  
+  {
+    cout << endl << "===================== Test 21) invalidly expanding url" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:include src=$(HTTP_HOST) /> bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::FAILURE);
+    assert(output_data_len == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 22) vars node with simple expression" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:vars>HTTP_HOST</esi:vars> bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 17);
+    assert(strncmp(output_data, "foo HTTP_HOST bar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 23) vars node expression with valid variable" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:vars>$(HTTP_HOST)</esi:vars> bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 8);
+    assert(strncmp(output_data, "foo  bar", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 24) vars node with invalid expression" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:vars>$(HTTP_HOST</esi:vars> bar");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 8);
+    assert(strncmp(output_data, "foo  bar", output_data_len) == 0);
+  }
+ 
+  {
+    cout << endl << "===================== Test 25) choose-when" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:choose>"
+                      "<esi:when test=foo>"
+                      "<esi:include src=foo />"
+                      "</esi:when>"
+                      "<esi:when test=bar>"
+                      "<esi:include src=bar />"
+                      "</esi:when>"
+                      "<esi:otherwise>"
+                      "<esi:include src=otherwise />"
+                      "</esi:otherwise>"
+                      "</esi:choose>");
+    const char *output_data;
+    int output_data_len;
+    
+    assert(esi_proc.completeParse(input_data) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == FETCHER_STATIC_DATA_SIZE + 3);
+    assert(strncmp(output_data, ">>>>> Content for URL [foo] <<<<<", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 26) choose-when" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:choose>"
+                      "<esi:otherwise>"
+                      "<esi:include src=otherwise />"
+                      "</esi:otherwise>"
+                      "</esi:choose>");
+    const char *output_data;
+    int output_data_len;
+    
+    assert(esi_proc.completeParse(input_data) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == FETCHER_STATIC_DATA_SIZE + 9);
+    assert(strncmp(output_data, ">>>>> Content for URL [otherwise] <<<<<", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 27) try block" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:include src=attempt />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:include src=except />"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == FETCHER_STATIC_DATA_SIZE + 7);
+    assert(strncmp(output_data, ">>>>> Content for URL [attempt] <<<<<", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 28) try block" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:include src=attempt />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:include src=except />"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::NEED_MORE_DATA);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::FAILURE);
+    data_fetcher.setReturnData(true);
+    assert(output_data_len == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 29) try block" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:include src=attempt />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:include src=except />"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::NEED_MORE_DATA);
+    data_fetcher.setReturnData(true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == FETCHER_STATIC_DATA_SIZE + 6);
+    assert(strncmp(output_data, ">>>>> Content for URL [except] <<<<<", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 30) try block" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:include src=attempt />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "except"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    data_fetcher.setReturnData(true);
+    assert(output_data_len == 6);
+    assert(strncmp(output_data, "except", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 31) try block" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:include src=pre />"
+                      "foo"
+                      "<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:include src=attempt />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:include src=except />"
+                      "</esi:except>"
+                      "</esi:try>"
+                      "bar");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::NEED_MORE_DATA);
+    data_fetcher.setReturnData(true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == FETCHER_STATIC_DATA_SIZE + 3 + 3 + FETCHER_STATIC_DATA_SIZE + 6 + 3);
+    assert(strncmp(output_data,
+                   ">>>>> Content for URL [pre] <<<<<foo>>>>> Content for URL [except] <<<<<bar",
+                   output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 32) html comment node" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:include src=helloworld />"
+                      "foo"
+                      "<!--esi <esi:vars>blah</esi:vars>-->"
+                      "bar");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == FETCHER_STATIC_DATA_SIZE + 10 + 3 + 4 + 3);
+    assert(strncmp(output_data,
+                   ">>>>> Content for URL [helloworld] <<<<<fooblahbar",
+                   output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 33) invalid html comment node" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:include src=helloworld />"
+                      "foo"
+                      "<!--esi <esi:vars>blah</esi:var>-->"
+                      "bar");
+
+    assert(esi_proc.completeParse(input_data) == false);
+  }
+
+  {
+    cout << endl << "===================== Test 34) choose-when" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:choose>\n\t"
+                      "<esi:when test=foo>"
+                      "\t<esi:include src=foo />"
+                      "</esi:when>\n"
+                      "<esi:when test=bar>"
+                      "<esi:include src=bar />"
+                      "</esi:when>\n"
+                      "<esi:otherwise>"
+                      "<esi:include src=otherwise />"
+                      "</esi:otherwise>\n"
+                      "</esi:choose>");
+    const char *output_data;
+    int output_data_len;
+    
+    assert(esi_proc.completeParse(input_data) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 1 + FETCHER_STATIC_DATA_SIZE + 3);
+    assert(strncmp(output_data, "\t>>>>> Content for URL [foo] <<<<<", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 35) special-include 1" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:special-include handler=stub/>");
+    gHandlerMap.clear();
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(gHandlerMap.size() == 1);
+    assert(gHandlerMap.begin()->first == "stub");
+    StubIncludeHandler *handler = gHandlerMap["stub"];
+    assert(handler->parseCompleteCalled == false);
+    assert(esi_proc.completeParse() == true);
+    assert(handler->parseCompleteCalled == true);
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == StubIncludeHandler::DATA_PREFIX_SIZE + 1);
+    assert(strncmp(output_data, "Special data for include id 1", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 36) special-include 2" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:special-include handler=stub/> <esi:special-include handler=stub/> bar");
+    gHandlerMap.clear();
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(gHandlerMap.size() == 1);
+    assert(gHandlerMap.begin()->first == "stub");
+    StubIncludeHandler *handler = gHandlerMap["stub"];
+    assert(handler->parseCompleteCalled == false);
+    assert(esi_proc.completeParse() == true);
+    assert(handler->parseCompleteCalled == true);
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == (4 + StubIncludeHandler::DATA_PREFIX_SIZE + 1 + 1 +
+                               StubIncludeHandler::DATA_PREFIX_SIZE + 1 + 4));
+    assert(strncmp(output_data, "foo Special data for include id 1 Special data for include id 2 bar",
+                   output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 37) special-include 3" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("foo <esi:special-include handler=ads/> <esi:special-include handler=udb/> bar");
+    gHandlerMap.clear();
+    assert(esi_proc.addParseData(input_data) == true);
+    assert(gHandlerMap.size() == 2);
+    assert(gHandlerMap.find(string("ads")) != gHandlerMap.end());
+    assert(gHandlerMap.find(string("udb")) != gHandlerMap.end());
+    StubIncludeHandler *ads_handler = gHandlerMap["ads"];
+    StubIncludeHandler *udb_handler = gHandlerMap["udb"];
+    assert(ads_handler->parseCompleteCalled == false);
+    assert(udb_handler->parseCompleteCalled == false);
+    assert(esi_proc.completeParse() == true);
+    assert(ads_handler->parseCompleteCalled == true);
+    assert(udb_handler->parseCompleteCalled == true);
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == (4 + StubIncludeHandler::DATA_PREFIX_SIZE + 1 + 1 +
+                               StubIncludeHandler::DATA_PREFIX_SIZE + 1 + 4));
+    assert(strncmp(output_data, "foo Special data for include id 1 Special data for include id 1 bar",
+                   output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 38) special-include negative" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:special-include handler=stub/>");
+    gHandlerMap.clear();
+    StubIncludeHandler::includeResult = false;
+    assert(esi_proc.addParseData(input_data) == false);
+    assert(gHandlerMap.size() == 1); // it'll still be created
+    assert(gHandlerMap.begin()->first == "stub");
+    StubIncludeHandler::includeResult = true;
+  }
+
+  {
+    cout << endl << "===================== Test 39) try block with special include" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == StubIncludeHandler::DATA_PREFIX_SIZE + 1);
+    assert(strncmp(output_data, "Special data for include id 1", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 40) try block with special include" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::NEED_MORE_DATA);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::FAILURE);
+    data_fetcher.setReturnData(true);
+    assert(output_data_len == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 41) try block with special include" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::NEED_MORE_DATA);
+    data_fetcher.setReturnData(true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == StubIncludeHandler::DATA_PREFIX_SIZE + 1);
+    assert(strncmp(output_data, "Special data for include id 2", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 42) special include try block" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "except"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    assert(esi_proc.completeParse(input_data) == true);
+
+     // this is to make the StubHandler return failure
+    data_fetcher.setReturnData(false);
+    
+     // this is to decrement HttpDataFetcher's pending request count - argument content doesn't matter
+    data_fetcher.getContent("blah", output_data, output_data_len);
+
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    data_fetcher.setReturnData(true);
+    assert(output_data_len == 6);
+    assert(strncmp(output_data, "except", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 43) comment tag" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:comment text=\"bleh\"/>");
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 44) using packed node list" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiParser parser("parser", &Debug, &Error);
+    DocNodeList node_list;
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:except>"
+                      "</esi:try>");
+    assert(parser.parse(node_list, input_data) == true);
+
+    string packedNodeList = node_list.pack();
+
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+
+    assert(esi_proc.usePackedNodeList(packedNodeList) == true);
+
+    const char *output_data;
+    int output_data_len;
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::NEED_MORE_DATA);
+    data_fetcher.setReturnData(true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == StubIncludeHandler::DATA_PREFIX_SIZE + 1);
+    assert(strncmp(output_data, "Special data for include id 2", output_data_len) == 0);
+
+    esi_proc.stop();
+    node_list.clear();
+    input_data = 
+      "<esi:choose>\n\t"
+      "<esi:when test=foo>"
+      "\t<esi:include src=foo />"
+      "</esi:when>\n"
+      "<esi:when test=bar>"
+      "<esi:include src=bar />"
+      "</esi:when>\n"
+      "<esi:otherwise>"
+      "<esi:include src=otherwise />"
+      "</esi:otherwise>\n"
+      "</esi:choose>";
+    assert(parser.parse(node_list, input_data) == true);
+    packedNodeList = node_list.pack();
+    assert(esi_proc.usePackedNodeList(packedNodeList) == true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 1 + FETCHER_STATIC_DATA_SIZE + 3);
+    assert(strncmp(output_data, "\t>>>>> Content for URL [foo] <<<<<", output_data_len) == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 45) using packed node list" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:comment text=\"bleh\"/>");
+
+    EsiParser parser("parser", &Debug, &Error);
+    DocNodeList node_list;
+    string input_data2("<esi:try>"
+                       "<esi:attempt>"
+                       "<esi:special-include handler=stub />"
+                       "</esi:attempt>"
+                       "<esi:except>"
+                       "<esi:special-include handler=stub />"
+                       "</esi:except>"
+                       "</esi:try>");
+    assert(parser.parse(node_list, input_data2) == true);
+
+    string packedNodeList = node_list.pack();
+
+    
+    const char *output_data;
+    int output_data_len;
+
+    assert(esi_proc.addParseData(input_data.c_str(), input_data.size()) == true);
+    assert(esi_proc.usePackedNodeList(packedNodeList) == false);
+    assert(esi_proc.completeParse() == true);
+    assert(esi_proc.usePackedNodeList(packedNodeList) == false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == 0);
+  }
+
+  {
+    cout << endl << "===================== Test 46) special include with footer" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:except>"
+                      "</esi:try>");
+
+    const char *output_data;
+    int output_data_len;
+    StubIncludeHandler::FOOTER = "<!--footer-->";
+    StubIncludeHandler::FOOTER_SIZE = strlen(StubIncludeHandler::FOOTER);
+    assert(esi_proc.completeParse(input_data) == true);
+    data_fetcher.setReturnData(false);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::NEED_MORE_DATA);
+    data_fetcher.setReturnData(true);
+    assert(esi_proc.process(output_data, output_data_len) == EsiProcessor::SUCCESS);
+    assert(output_data_len == StubIncludeHandler::DATA_PREFIX_SIZE + 1 + StubIncludeHandler::FOOTER_SIZE);
+    assert(strncmp(output_data, "Special data for include id 2",
+                   output_data_len - StubIncludeHandler::FOOTER_SIZE) == 0);
+    assert(strncmp(output_data + StubIncludeHandler::DATA_PREFIX_SIZE + 1, StubIncludeHandler::FOOTER,
+                   StubIncludeHandler::FOOTER_SIZE) == 0);
+    StubIncludeHandler::FOOTER = 0;
+    StubIncludeHandler::FOOTER_SIZE = 0;
+  }
+
+  {
+    cout << endl << "===================== Test 47) using packed node list" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiParser parser("parser", &Debug, &Error);
+    DocNodeList node_list;
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:except>"
+                      "</esi:try>");
+    assert(parser.parse(node_list, input_data) == true);
+
+    string packedNodeList = node_list.pack();
+
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+
+    assert(esi_proc.usePackedNodeList(0, packedNodeList.size()) == false);
+  }
+
+  {
+    cout << endl << "===================== Test 48) using packed node list" << endl;
+    TestHttpDataFetcher data_fetcher;
+    EsiParser parser("parser", &Debug, &Error);
+    DocNodeList node_list;
+    string input_data("<esi:try>"
+                      "<esi:attempt>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:attempt>"
+                      "<esi:except>"
+                      "<esi:special-include handler=stub />"
+                      "</esi:except>"
+                      "</esi:try>");
+    assert(parser.parse(node_list, input_data) == true);
+
+    string packedNodeList = node_list.pack();
+
+    EsiProcessor esi_proc("processor", "parser", "expression", &Debug, &Error, data_fetcher, esi_vars,
+                          handler_mgr);
+
+    assert(esi_proc.usePackedNodeList(packedNodeList.data(), 0) == false);
+  }
+
+  cout << endl << "All tests passed!" << endl;
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/esi/test/sampleProb.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/test/sampleProb.cc b/plugins/experimental/esi/test/sampleProb.cc
new file mode 100644
index 0000000..15fe859
--- /dev/null
+++ b/plugins/experimental/esi/test/sampleProb.cc
@@ -0,0 +1,228 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  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.
+ */
+
+#include <time.h>
+#include <stdint.h>
+#include <iostream>
+#include <utility>
+#include <vector>
+#include <map>
+#include <sys/time.h>
+using namespace std;
+
+static int windowSize=200;//ms
+static int totalDuration=2000;//seconds
+static int lowerCutOff=300;
+static int higherCutOff=1000;
+
+class FailureInfo
+{
+public:
+
+    FailureInfo()
+    {
+        gettimeofday(&_start,NULL);
+        _totalSlot=totalDuration/windowSize;//INtegral multiple
+        _marker=0;
+        for(int i=0;i<_totalSlot;i++)
+            _passFail.push_back(make_pair(0,0));
+
+        _avgOverWindow=0;
+        _windowPassed=0;
+    };
+
+    ~FailureInfo() 
+    {
+    }
+    
+    //Whenever the window time expires u start filling the count
+    //by taking a mod
+    //so what u get is over a window of 200 ms and 10 rounds
+    //the no of failures
+    //Introduce a variable which will be a function of
+    //failure and which will lead to points in graph 
+    //according to which the probability of serving the 
+    // data from cache or contacting the origin server
+    // will be decided
+    std::vector <std::pair < double,double > > _passFail;
+
+    int _marker;
+
+    int _totalSlot;
+    
+    struct timeval _start;
+
+    double _avgOverWindow;
+
+    int _windowPassed;
+
+};
+
+
+typedef std::map<std::string,class FailureInfo*> FailureData;
+
+void registerSuccFail(string URL,FailureData& data,bool isSuccess)
+{
+    struct timeval currTime,result,startTime;
+    int marker;
+    FailureData::iterator it;
+    it=data.find(URL);
+    vector < pair < double , double > > & passFail=it->second->_passFail;
+    marker=it->second->_marker;
+ 
+ 
+    startTime=it->second->_start;  
+     
+    gettimeofday(&currTime,NULL);
+    
+    timersub(&currTime,&startTime,&result);
+
+
+
+    if( ( result.tv_sec*1000000+result.tv_usec )   > (windowSize*1000) )
+    {
+        marker=++marker%it->second->_totalSlot;
+        if(marker==it->second->_totalSlot-1)
+        {
+            ++it->second->_windowPassed;
+            double avg=0;
+            for(int i=0;i<it->second->_totalSlot;i++)
+            {
+                if(passFail[i].first >0)
+                {
+                    avg+=passFail[i].first/(passFail[i].first+passFail[i].second);
+                }
+            }
+            it->second->_avgOverWindow+=avg/it->second->_windowPassed;
+        }
+        it->second->_marker=marker;
+        gettimeofday(&it->second->_start,NULL);
+    }
+    
+
+   if(isSuccess)
+   {
+       passFail[marker].second++;
+   } 
+   
+   else
+   {
+       passFail[marker].first++;
+   }
+}
+
+    
+bool isAttemptReq(string URL,FailureData& data)
+{
+    FailureData::iterator it;
+    it=data.find(URL);
+    if(it != data.end())
+    {
+        double avg=0;
+        vector < pair < double , double > > & passFail=it->second->_passFail;
+
+        for(int i=0;i<it->second->_totalSlot;i++)
+        {
+            //cout<<"Failure:"<<passFail[i].first<< "Total"<< (passFail[i].first+passFail[i].second )<<endl;
+            if(passFail[i].first >0)
+            {
+                avg+=passFail[i].first/(passFail[i].first+passFail[i].second);
+                //cout<<"Prob of faillure:"<<passFail[i].first/(passFail[i].first+passFail[i].second)<<endl;
+            }
+        }
+ 
+       if(avg)
+       { 
+           avg=avg/it->second->_totalSlot;
+           double prob;
+
+        
+           if(avg*1000<lowerCutOff)
+           {
+               prob=avg;
+           }
+ 
+        
+           else
+           {
+               double mapFactor=( ( (avg*1000-lowerCutOff)*(avg*1000-lowerCutOff) ) / ( higherCutOff-lowerCutOff ) )+lowerCutOff;
+               prob=mapFactor/1000;
+               cout<<prob<<endl;
+           }
+
+           if(prob==1)
+           {
+               prob=it->second->_avgOverWindow;
+               cout<<"Average"<<prob<<endl;
+           }
+        
+           int decision=rand()%100;
+
+      
+           if(decision<prob*100)
+               return false;
+        
+           return true;
+       }
+       return true;
+
+    }
+    else
+    {
+        FailureInfo* info=new FailureInfo();
+        data[URL]=info;
+        return true;
+    }
+}
+
+
+const std::string fetchURL="www.example.com";
+int main(int argc,char**argv)
+{
+    //Simulate the scenario
+    FailureData data;
+    int noOfAttempt=0,noOfExcept=0;
+
+    int count=atoi(argv[1]); 
+    while(count--)
+    {
+        int decision=rand()%100;
+
+        if(isAttemptReq(fetchURL,data))
+        {
+            noOfAttempt++;
+            if(decision>=atoi(argv[2]) && 0)
+                registerSuccFail(fetchURL,data,true);
+    
+            else
+                registerSuccFail(fetchURL,data,false);
+        }
+        else
+            noOfExcept++;
+   
+    }
+
+    cout<<" SERVED FROM ATTEMPT "<<noOfAttempt<<" TOTAL "<<atoi(argv[1])<<endl;
+    cout<<" SERVED FROM EXCEPT "<<noOfExcept<<" TOTAL "<<atoi(argv[1])<<endl;
+       
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/esi/test/utils_test.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/test/utils_test.cc b/plugins/experimental/esi/test/utils_test.cc
new file mode 100644
index 0000000..0fc9ccb
--- /dev/null
+++ b/plugins/experimental/esi/test/utils_test.cc
@@ -0,0 +1,116 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  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.
+ */
+
+#include <iostream>
+#include <assert.h>
+#include <string>
+
+#include "print_funcs.h"
+#include "Utils.h"
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+using namespace EsiLib;
+
+void checkAttributes(const char *check_id, const AttributeList &attr_list, const char *attr_info[]) {
+  cout << check_id << ": checking attributes" << endl;
+  AttributeList::const_iterator iter = attr_list.begin();
+  for (int i = 0; attr_info[i]; i += 2, ++iter) {
+    assert(iter->name_len == static_cast<int>(strlen(attr_info[i])));
+    assert(strncmp(iter->name, attr_info[i], iter->name_len) == 0);
+    assert(iter->value_len == static_cast<int>(strlen(attr_info[i + 1])));
+    assert(strncmp(iter->value, attr_info[i + 1], iter->value_len) == 0);
+  }
+  assert(iter == attr_list.end());
+}
+
+int main() 
+{
+  Utils::init(&Debug, &Error);
+
+  AttributeList attr_list;
+
+  string str1("pos=SKY spaceid=12123");
+  Utils::parseAttributes(str1, attr_list);
+  const char *expected_strs1[] = { "pos", "SKY", "spaceid", "12123", 0 };
+  checkAttributes("test1", attr_list, expected_strs1);
+
+  string str2("  pos=SKY	  spaceid=12123 ");
+  Utils::parseAttributes(str2, attr_list);
+  const char *expected_strs2[] = { "pos", "SKY", "spaceid", "12123", 0 };
+  checkAttributes("test2", attr_list, expected_strs2);
+
+  string str3("  pos=\"SKY\"	  spaceid=12123 ");
+  Utils::parseAttributes(str3, attr_list);
+  const char *expected_strs3[] = { "pos", "SKY", "spaceid", "12123", 0 };
+  checkAttributes("test3", attr_list, expected_strs3);
+
+  string str4("  pos=\" SKY BAR \"	  spaceid=12123 blah=\"foo");
+  Utils::parseAttributes(str4, attr_list);
+  const char *expected_strs4[] = { "pos", " SKY BAR ", "spaceid", "12123", 0 };
+  checkAttributes("test4", attr_list, expected_strs4);
+
+  string str5("a=\"b & xyz\"&c=d&e=f&g=h\"");
+  Utils::parseAttributes(str5, attr_list, "&");
+  const char *expected_strs5[] = { "a", "b & xyz", "c", "d", "e", "f", 0 };
+  checkAttributes("test5", attr_list, expected_strs5);
+
+  string str6("abcd=&");
+  Utils::parseAttributes(str6, attr_list, "&");
+  const char *expected_strs6[] = { 0 };
+  checkAttributes("test6", attr_list, expected_strs6);
+
+  string str7("&& abcd=& key1=val1 &=val2&val3&&");
+  Utils::parseAttributes(str7, attr_list, "&");
+  const char *expected_strs7[] = { "key1", "val1", 0 };
+  checkAttributes("test7", attr_list, expected_strs7);
+  
+  const char *escaped_sequence = "{\\\"site-attribute\\\":\\\"content=no_expandable; ajax_cert_expandable\\\"}";
+  string str8("pos=\"FPM1\" spaceid=96584352 extra_mime=\"");
+  str8.append(escaped_sequence);
+  str8.append("\" foo=bar a=\"b\"");
+  const char *expected_strs8[] = {
+    "pos", "FPM1", "spaceid", "96584352", "extra_mime", escaped_sequence, "foo", "bar", "a", "b", 0
+  };
+  Utils::parseAttributes(str8, attr_list);
+  checkAttributes("test8", attr_list, expected_strs8);
+
+  assert(Utils::unescape(escaped_sequence) ==
+         "{\"site-attribute\":\"content=no_expandable; ajax_cert_expandable\"}");
+  assert(Utils::unescape(0) == "");
+  assert(Utils::unescape("\\", 0) == "");
+  assert(Utils::unescape("\\hello\"", 3) == "he");
+  assert(Utils::unescape("\\hello\"", -3) == "");
+  assert(Utils::unescape("hello") == "hello");
+
+  string str9("n1=v1; n2=v2;, n3=v3, ;n4=v4=extrav4");
+  Utils::parseAttributes(str9, attr_list, ";,");
+  const char *expected_strs9[] = { "n1", "v1", "n2", "v2", "n3", "v3", "n4", "v4=extrav4", 0 };
+  checkAttributes("test9", attr_list, expected_strs9);
+
+  cout << endl << "All tests passed!" << endl;
+  return 0;
+}
+  

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/esi/test/vars_test.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/test/vars_test.cc b/plugins/experimental/esi/test/vars_test.cc
new file mode 100644
index 0000000..d15f953
--- /dev/null
+++ b/plugins/experimental/esi/test/vars_test.cc
@@ -0,0 +1,399 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  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.
+ */
+
+#include <iostream>
+#include <assert.h>
+#include <string>
+#include <stdarg.h>
+
+#include "print_funcs.h"
+#include "Variables.h"
+#include "Expression.h"
+#include "Utils.h"
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+using namespace EsiLib;
+
+void
+addToHeaderList(const char *strings[], HttpHeaderList &headers) {
+  for (int i = 0; strings[i]; i += 2) {
+    if (i % 4 == 0) {
+      headers.push_back(HttpHeader(strings[i], -1, strings[i + 1], -1));
+      headers.push_back(HttpHeader());
+    } else {
+      headers.push_back(HttpHeader(strings[i], strlen(strings[i]),
+                                   strings[i + 1], strlen(strings[i + 1])));
+    }
+  }
+}
+
+string gFakeDebugLog;
+
+void
+fakeDebug(const char *tag, const char *fmt, ...) {
+  static const int LINE_SIZE = 1024;
+  char buf[LINE_SIZE];
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(buf, LINE_SIZE, fmt, ap);
+  printf("Debug (%s): %s\n", tag, buf);
+  va_end(ap);
+  gFakeDebugLog.append(buf);
+}
+
+int main() 
+{
+  Utils::init(&Debug, &Error);
+
+  {
+    cout << endl << "===================== Test 1" << endl;
+    Variables esi_vars("vars_test", &Debug, &Error);
+    const char *strings[] = { "Cookie", "; c1=v1; c2=v2; ;   c3; c4=;    c5=v5  ",
+                              "Host", "example.com",
+                              "Referer", "google.com",
+                              "Blah", "Blah",
+                              "Accept-Language", "en-gb , en-us ,  ,",
+                              "Accept-Language", "ka-in",
+                              0 };
+
+    HttpHeaderList headers;
+    addToHeaderList(strings, headers);
+    esi_vars.populate(headers);
+    esi_vars.populate("a=b&c=d&e=f");
+
+    assert(esi_vars.getValue("HTTP_COOKIE{c1}") == "v1");
+    assert(esi_vars.getValue("HTTP_COOKIE{c2}") == "v2");
+    assert(esi_vars.getValue("HTTP_COOKIE{c3}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c4}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c5}") == "v5");
+    assert(esi_vars.getValue("HTTP_COOKIE{c2}") != "v1");
+    assert(esi_vars.getValue("HTTP_COOKIE{C1}") != "v1");
+    assert(esi_vars.getValue("HTTP_USER_AGENT").size() == 0);
+    assert(esi_vars.getValue("BLAH").size() == 0);
+    assert(esi_vars.getValue("HTTP_HOST") == "example.com");
+    assert(esi_vars.getValue("HTTP_host") == "example.com");
+    assert(esi_vars.getValue("HTTP_REFERER") == "google.com");
+    assert(esi_vars.getValue("HTTP_BLAH").size() == 0);
+    assert(esi_vars.getValue("HTTP_ACCEPT_LANGUAGE{en-gb}") == "true");
+    assert(esi_vars.getValue("HTTP_ACCEPT_LANGUAGE{en-us}") == "true");
+    assert(esi_vars.getValue("HTTP_ACCEPT_LANGUAGE{es-us}") == "");
+    assert(esi_vars.getValue("QUERY_STRING") == "a=b&c=d&e=f");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "b");
+    assert(esi_vars.getValue("QUERY_STRING{e}") == "f");
+    assert(esi_vars.getValue("QUERY_STRING{z}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c1") == "");
+    assert(esi_vars.getValue("HTTP_COOKIEc1") == "");
+    assert(esi_vars.getValue("HTTP_COOKIEc1}") == "");
+    assert(esi_vars.getValue("{c1}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c1{c2}}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c1{c2}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c1c}2}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c1c2}") == "");
+    assert(esi_vars.getValue("{c1c2}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c1}c") == "");
+    esi_vars.populate(HttpHeader("hosT", -1, "localhost", -1));
+    assert(esi_vars.getValue("HTTP_HOST") == "localhost");
+
+    esi_vars.populate(HttpHeader("User-agent", -1, 
+                                 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) "
+                                 "Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)", -1));
+    assert(esi_vars.getValue("HTTP_USER_AGENT{vendor}") == "gecko");
+    assert(esi_vars.getValue("HTTP_USER_AGENT{platform}") == "windows_xp");
+    assert(esi_vars.getValue("HTTP_USER_AGENT{version}") == "1.9");
+    assert(esi_vars.getValue("HTTP_USER_AGENT{blah}") == "");
+
+    assert(esi_vars.getValue("HTTP_ACCEPT_LANGUAGE{ka-in}") == "true");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING") == "");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "");
+    assert(esi_vars.getValue("HTTP_COOKIE{c1}") == "");
+    esi_vars.populate(headers);
+    esi_vars.populate("a=b&c=d&e=f");
+
+    Expression esi_expr("vars_test", &Debug, &Error, esi_vars);
+    assert(esi_expr.expand(0) == "");
+    assert(esi_expr.expand("") == "");
+    assert(esi_expr.expand("blah") == "blah");
+    assert(esi_expr.expand("blah$(HTTP_HOST") == "");
+    assert(esi_expr.expand("blah$A(HTTP_HOST)") == "blah$A(HTTP_HOST)");
+    assert(esi_expr.expand("blah$()") == "blah");
+    assert(esi_expr.expand("blah-$(HTTP_HOST)") == "blah-example.com");
+    assert(esi_expr.expand("blah-$(HTTP_REFERER)") == "blah-google.com");
+    assert(esi_expr.expand("blah-$(HTTP_COOKIE{c1})") == "blah-v1");
+    assert(esi_expr.expand("blah-$(HTTP_COOKIE{c1a})") == "blah-");
+    assert(esi_expr.expand("blah-$(HTTP_COOKIE{c1}$(HTTP_HOST))") == "");
+    assert(esi_expr.expand("blah-$(HTTP_COOKIE{c1})-$(HTTP_HOST)") == "blah-v1-example.com");
+    assert(esi_expr.expand("$()") == "");
+    assert(esi_expr.expand("$(HTTP_COOKIE{c1})$(HTTP_COOKIE{c2})$(HTTP_HOST)") == "v1v2example.com");
+
+    // quotes
+    assert(esi_expr.expand("'blah") == ""); // unterminated quote
+    assert(esi_expr.expand("\"blah") == ""); // unterminated quote
+    assert(esi_expr.expand("'blah'") == "blah");
+    assert(esi_expr.expand("\"blah\"") == "blah");
+    assert(esi_expr.expand("'$(HTTP_COOKIE{c1})'") == "v1");
+    assert(esi_expr.expand("\"$(HTTP_HOST)\"") == "example.com");
+
+    // leading/trailing whitespace
+    assert(esi_expr.expand("   blah  ") == "blah");
+    assert(esi_expr.expand("   $(HTTP_REFERER) $(HTTP_HOST)  ") == "google.com example.com");
+    assert(esi_expr.expand(" ' foo ' ") == " foo ");
+    assert(esi_expr.expand(" ' foo '") == " foo ");
+    assert(esi_expr.expand("bar ") == "bar");
+
+    // evaluate tests
+    assert(esi_expr.evaluate("foo") == true);
+    assert(esi_expr.evaluate("") == false);
+    assert(esi_expr.evaluate("$(HTTP_HOST)") == true);
+    assert(esi_expr.evaluate("$(HTTP_XHOST)") == false);
+    assert(esi_expr.evaluate("foo == foo") == true);
+    assert(esi_expr.evaluate("'foo' == \"foo\"") == true);
+    assert(esi_expr.evaluate("foo == foo1") == false);
+    assert(esi_expr.evaluate("'foo' == \"foo1\"") == false);
+    assert(esi_expr.evaluate("$(HTTP_REFERER) == google.com") == true);
+    assert(esi_expr.evaluate("$(HTTP_HOST)=='example.com'") == true);
+    assert(esi_expr.evaluate("$(HTTP_REFERER) != google.com") == false);
+    assert(esi_expr.evaluate("$(HTTP_HOST)!='example.com'") == false);
+    assert(esi_expr.evaluate("$(HTTP_HOST) == 'facebook.com'") == false);
+    assert(esi_expr.evaluate("!") == true);
+    assert(esi_expr.evaluate("!abc") == false);
+    assert(esi_expr.evaluate("!$(FOO_BAR)") == true);
+    assert(esi_expr.evaluate("!$(HTTP_HOST)") == false);
+    assert(esi_expr.evaluate("abc!abc") == true);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) == 'v1'") == true);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1b}) == 'v1'") == false);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) <= 'v2'") == true);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) < 'v2'") == true);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) >= 'v0'") == true);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) > 'v2'") == false);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) & 'v2'") == true);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{foo}) & $(HTTP_COOKIE{bar})") == false);
+    assert(esi_expr.evaluate("'' | $(HTTP_COOKIE{c1})") == true);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{foo}) | $(HTTP_COOKIE{bar})") == false);
+
+    // default value tests
+    assert(esi_expr.expand("foo|bar") == "foo|bar");
+    assert(esi_expr.expand("$(HTTP_HOST|") == "");
+    assert(esi_expr.expand("$(HTTP_HOST|foo") == "");
+    assert(esi_expr.expand("$(HTTP_HOST|foo)") == "example.com");
+    assert(esi_expr.expand("$(HTTP_XHOST|foo)") == "foo");
+    assert(esi_expr.expand("$(|foo)") == "foo");
+    assert(esi_expr.expand("$(HTTP_ACCEPT_LANGUAGE{en-uk})") == "");
+    assert(esi_expr.expand("$(HTTP_ACCEPT_LANGUAGE{en-uk}|'yes')") == "yes");
+    assert(esi_expr.expand("$(HTTP_ACCEPT_LANGUAGE{en-uk}|'yes with space')") == "yes with space");
+    assert(esi_expr.expand("$(HTTP_ACCEPT_LANGUAGE{en-gb}|'yes')") == "true");
+    assert(esi_expr.expand("$(HTTP_ACCEPT_LANGUAGE{en-gb}|'yes)") == "");
+    assert(esi_expr.expand("$(HTTP_ACCEPT_LANGUAGE{en-uk}|'yes)") == "");
+
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{non-existent}) < 7") == false);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) > $(HTTP_COOKIE{non-existent})") == false);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{non-existent}) <= 7") == false);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) >= $(HTTP_COOKIE{non-existent})") == false);
+
+    // query string tests
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("a");
+    assert(esi_vars.getValue("QUERY_STRING") == "a");
+    assert(esi_vars.getValue("QUERY_STRING{a}").size() == 0);
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("");
+    assert(esi_vars.getValue("QUERY_STRING") == "");
+    assert(esi_vars.getValue("QUERY_STRING{a}").size() == 0);
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("a=b");
+    assert(esi_vars.getValue("QUERY_STRING") == "a=b");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "b");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("a=b&");
+    assert(esi_vars.getValue("QUERY_STRING") == "a=b&");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "b");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("&a=b&");
+    assert(esi_vars.getValue("QUERY_STRING") == "&a=b&");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "b");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("name1=value1&name2=value2&name3=val%32ue");
+    assert(esi_vars.getValue("QUERY_STRING") == "name1=value1&name2=value2&name3=val%32ue");
+    assert(esi_vars.getValue("QUERY_STRING{name1}") == "value1");
+    assert(esi_vars.getValue("QUERY_STRING{name2}") == "value2");
+    assert(esi_vars.getValue("QUERY_STRING{name3}") == "val%32ue");
+    assert(esi_vars.getValue("QUERY_STRING{name4}") == "");
+    assert(esi_vars.getValue("QUERY_STRING{}") == "");
+    assert(esi_vars.getValue("QUERY_STRING{foo}") == "");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("=");
+    assert(esi_vars.getValue("QUERY_STRING") == "=");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("a=&");
+    assert(esi_vars.getValue("QUERY_STRING") == "a=&");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("=b&");
+    assert(esi_vars.getValue("QUERY_STRING") == "=b&");
+    assert(esi_vars.getValue("QUERY_STRING{a}") == "");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("foo=bar&blah=&");
+    assert(esi_vars.getValue("QUERY_STRING") == "foo=bar&blah=&");
+    assert(esi_vars.getValue("QUERY_STRING{foo}") == "bar");
+    assert(esi_vars.getValue("QUERY_STRING{blah}") == "");
+
+    esi_vars.clear();
+    assert(esi_vars.getValue("QUERY_STRING").size() == 0);
+    esi_vars.populate("=blah&foo=bar");
+    assert(esi_vars.getValue("QUERY_STRING") == "=blah&foo=bar");
+    assert(esi_vars.getValue("QUERY_STRING{foo}") == "bar");
+    assert(esi_vars.getValue("QUERY_STRING{blah}") == "");
+  }
+
+  {
+    cout << endl << "===================== Test 2" << endl;
+    gFakeDebugLog.assign("");
+    Variables esi_vars("vars_test", &fakeDebug, &Error);
+
+    esi_vars.populate(HttpHeader("Host", -1, "example.com", -1));
+    esi_vars.populate(HttpHeader("Referer", -1, "google.com", -1));
+    const char *PARSING_DEBUG_MESSAGE = "Parsing headers";
+    assert(gFakeDebugLog.find(PARSING_DEBUG_MESSAGE) >= gFakeDebugLog.size()); // shouldn't have parsed yet
+
+    assert(esi_vars.getValue("HTTP_HOST") == "example.com");
+    size_t str_pos = gFakeDebugLog.find(PARSING_DEBUG_MESSAGE);
+    assert(str_pos < gFakeDebugLog.size()); // should've parsed now
+
+    assert(esi_vars.getValue("HTTP_REFERER") == "google.com");
+    assert(gFakeDebugLog.rfind(PARSING_DEBUG_MESSAGE) == str_pos); // shouldn't have parsed again
+
+    esi_vars.populate(HttpHeader("Host", -1, "localhost", -1));
+    assert(esi_vars.getValue("HTTP_HOST") == "localhost");
+    assert(gFakeDebugLog.rfind(PARSING_DEBUG_MESSAGE) == str_pos); // should not have parsed all headers
+    assert(esi_vars.getValue("HTTP_HOST") == "localhost"); // only this one
+    assert(esi_vars.getValue("HTTP_REFERER") == "google.com");
+
+    esi_vars.clear();
+    esi_vars.populate(HttpHeader("Host", -1, "home", -1));
+    assert(esi_vars.getValue("HTTP_HOST") == "home");
+    assert(gFakeDebugLog.rfind(PARSING_DEBUG_MESSAGE) != str_pos); // should have parsed again
+    assert(esi_vars.getValue("HTTP_REFERER") == "");
+  }
+
+  {
+    cout << endl << "===================== Test 3" << endl;
+    Variables esi_vars("vars_test", &Debug, &Error);
+
+    esi_vars.populate(HttpHeader("Host", -1, "example.com", -1));
+    esi_vars.populate(HttpHeader("Referer", -1, "google.com", -1));
+    esi_vars.populate(HttpHeader("Cookie", -1, "age=21; grade=-5; avg=4.3; t1=\" \"; t2=0.0", -1));
+    esi_vars.populate(HttpHeader("Cookie", -1, "t3=-0; t4=0; t5=6", -1));
+
+    Expression esi_expr("vars_test", &Debug, &Error, esi_vars);
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{age}) >= -9"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{age}) > 9"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{age}) < 22"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{age}) <= 22.1"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{age}) > 100a")); // non-numerical
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{t1})")); // non-numerical
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{grade})"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{grade}) == -5"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{grade}) != -5.1"));
+    assert(esi_expr.evaluate("!$(HTTP_COOKIE{t2})"));
+    assert(esi_expr.evaluate("!$(HTTP_COOKIE{t3})"));
+    assert(esi_expr.evaluate("!$(HTTP_COOKIE{t4})"));
+    assert(esi_expr.evaluate("+4.3 == $(HTTP_COOKIE{avg})"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{grade}) < -0x2"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{t2}) | 1"));
+    assert(!esi_expr.evaluate("$(HTTP_COOKIE{t3}) & 1"));
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{t5}) == 6"));
+
+    string strange_cookie("c1=123");
+    strange_cookie[4] = '\0';
+    esi_vars.populate(HttpHeader("Cookie", -1, strange_cookie.data(), strange_cookie.size()));
+    assert(esi_vars.getValue("HTTP_COOKIE{c1}").size() == 3);
+    assert(esi_vars.getValue("HTTP_COOKIE{c1}")[1] == '\0');
+    assert(esi_expr.evaluate("$(HTTP_COOKIE{c1}) != 1"));
+  }
+
+  {
+    cout << endl << "===================== Test 4" << endl;
+    Variables esi_vars("vars_test", &Debug, &Error);
+    string cookie_str(""); // TODO - might need to 
+    esi_vars.populate(HttpHeader("Cookie", -1, cookie_str.data(), cookie_str.size()));
+
+    assert(esi_vars.getValue("HTTP_COOKIE{FPS}") == "dl");
+    assert(esi_vars.getValue("HTTP_COOKIE{mb}") ==
+           "d=OPsv7rvU4FFaAOoIRi75BBuqdMdbMLFuDwQmk6nKrCgno7L4xuN44zm7QBQJRmQSh8ken6GSVk8-&v=1");
+    assert(esi_vars.getValue("HTTP_COOKIE{Y;u}") == "pmanjesh");
+    assert(esi_vars.getValue("HTTP_COOKIE{Y;l}") == "fc0d94i7/o");
+    assert(esi_vars.getValue("HTTP_COOKIE{C}") == "mg=1");
+    assert(esi_vars.getValue("HTTP_COOKIE{non-existent}") == "");
+
+    assert(esi_vars.getValue("HTTP_COOKIE{Y;intl}") == "us");
+    assert(esi_vars.getValue("HTTP_COOKIE{Y}") ==
+           "v=1&n=fmaptagvuff50&l=fc0d94i7/o&p=m2f0000313000400&r=8j&lg=en-US&intl=us");
+
+    esi_vars.populate(HttpHeader("Host", -1, "www.example.com", -1));
+    assert(esi_vars.getValue("HTTP_COOKIE{F}") == "a=4KvLV9IMvTJnIAqCk25y9Use6hnPALtUf3n78PihlcIqvmzoW."
+           "Ax8UyW8_oxtgFNrrdmooqZmPa7WsX4gE.6sI69wuNwRKrRPFT29h9lhwuxxLz0RuQedVXhJhc323Q-&b=8gQZ");
+    assert(esi_vars.getValue("HTTP_HOST") == "www.example.com");
+
+    esi_vars.populate(HttpHeader("Cookie", -1, "a=b; c=d", -1));
+    assert(esi_vars.getValue("HTTP_COOKIE{Y;intl}") == "us");
+    assert(esi_vars.getValue("HTTP_COOKIE{F}") == "a=4KvLV9IMvTJnIAqCk25y9Use6hnPALtUf3n78PihlcIqvmzoW."
+           "Ax8UyW8_oxtgFNrrdmooqZmPa7WsX4gE.6sI69wuNwRKrRPFT29h9lhwuxxLz0RuQedVXhJhc323Q-&b=8gQZ");
+    assert(esi_vars.getValue("HTTP_COOKIE{a}") == "b");
+    assert(esi_vars.getValue("HTTP_COOKIE{c}") == "d");
+    assert(esi_vars.getValue("HTTP_HOST") == "www.example.com");
+    assert(esi_vars.getValue("HTTP_COOKIE{Y;blah}") == "");
+
+    esi_vars.clear();
+    esi_vars.populate(HttpHeader("Cookie", -1, "Y=junk", -1));
+    assert(esi_vars.getValue("HTTP_COOKIE{Y}") == "junk");
+    assert(esi_vars.getValue("HTTP_COOKIE{Y;intl}") == "");
+  }
+
+  cout << endl << "All tests passed!" << endl;
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/esi/test_helper/print_funcs.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/test_helper/print_funcs.cc b/plugins/experimental/esi/test_helper/print_funcs.cc
new file mode 100644
index 0000000..7acc06f
--- /dev/null
+++ b/plugins/experimental/esi/test_helper/print_funcs.cc
@@ -0,0 +1,47 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  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.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "print_funcs.h"
+
+static const int LINE_SIZE = 1024 * 1024;
+
+void Debug(const char *tag, const char *fmt, ...) {
+  char buf[LINE_SIZE];
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(buf, LINE_SIZE, fmt, ap);
+  printf("Debug (%s): %s\n", tag, buf);
+  va_end(ap);
+}
+
+void Error(const char *fmt, ...) {
+  char buf[LINE_SIZE];
+  va_list ap;
+  va_start(ap, fmt);
+  vsnprintf(buf, LINE_SIZE, fmt, ap);
+  printf("Error: %s\n", buf);
+  va_end(ap);
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/esi/test_helper/print_funcs.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/esi/test_helper/print_funcs.h b/plugins/experimental/esi/test_helper/print_funcs.h
new file mode 100644
index 0000000..81b6383
--- /dev/null
+++ b/plugins/experimental/esi/test_helper/print_funcs.h
@@ -0,0 +1,31 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  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.
+ */
+
+#ifndef _PRINT_FUNCS_H
+#define _PRINT_FUNCS_H
+
+void Debug(const char *tag, const char *fmt, ...);
+
+void Error(const char *fmt, ...);
+
+#endif // _PRINT_FUNCS_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/geoip_acl/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/geoip_acl/README b/plugins/experimental/geoip_acl/README
new file mode 100644
index 0000000..bcb4f0a
--- /dev/null
+++ b/plugins/experimental/geoip_acl/README
@@ -0,0 +1,88 @@
+Apache Traffic Server GeoIP ACLs
+================================
+
+This is a simple ATS plugin for denying (or allowing) requests based on the
+source IP geo-location. Currently only the Maxmind APIs are supported, but
+we'd be happy to other other (open) APIs if you let us know.
+
+
+Building
+========
+
+The build and installation requires a full installation of Apache Traffic
+Server v3.0.0 or later. In particular, the include files must be available,
+and the tsxs build script should be in the path (or modify the Makefile).
+
+    % gmake
+    % sudo gmake install
+
+
+Configuration
+=============
+
+Once installed, there are three primary use cases, which we will discuss in
+details. Note that in all configurations, the first plugin parameter must
+specify what the matches should be applied to. Currently, only one rule set
+is supported, for Country ISO codes. This is specified with a parameter of
+
+   @pparam=country
+
+
+Future additions to this plugin could include other regions, such as city,
+state, continent etc.
+
+The three typical use cases are as follows:
+
+1. Per remap configurations, applicable to the entire remap rule. This is
+useful when you can partition your content so that entire prefix paths
+should be filtered. For example, lets assume that http://example.com/music
+is restricted to US customers only, and everything else is world wide
+accessible. In remap.config, you would have something like
+
+    map http://example.com/music http://music.example.com \
+        @plugin=geoip_acl.so @pparam=country @pparam=allow @pparam=US
+
+    map http://example.com  http://other.example.com
+
+2. If you can not partition the data with a path prefix, you can specify a
+separate regex mapping filter. The remap.config file might then look like
+
+    map http://example.com http://music.example.com \
+        @plugin=geoip_acl.so @pparam=country @pparam=regex::/etc/music.regex
+
+
+where music.regex is a format with PCRE (perl compatible) regular
+expressions, and unique rules for match. E.g.
+
+    .*\.mp3	 allow	US
+    .*\.ogg	 deny	US
+
+
+Note that the default in the case of no matches on the regular expressions
+is to "allow" the request. This can be overriden, see next use case.
+
+
+3. You can also combine 1) and 2), and provide defaults in the remap.config
+configuration, which then applies for the cases where no regular expressions
+matches at all. This would be useful to override the default which is to
+allow all requests that don't match. For example
+
+
+    map http://example.com http://music.example.com \
+        @plugin=geoip_acl.so @pparam=country @pparam=allow @pparam= US \
+        @pparam=regex::/etc/music.regex 
+
+
+This tells the plugin that in the situation where there is no matching
+regular expression, only allow requests originating from the US.
+
+
+Finally, there's one additional parameter option that can be used:
+
+    @pparam=html::/some/path.html
+
+
+This will override the default reponse body for the denied responses with a
+custom piece of HTML. This can be useful to explain to your users why they
+are getting denied access to a particular piece of content. This
+configuration can be used with any of the use cases described above.