trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bri...@apache.org
Subject [4/5] initial atscppapi commit
Date Fri, 18 Oct 2013 21:51:11 GMT
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/null_transformation_plugin/NullTransformationPlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/null_transformation_plugin/NullTransformationPlugin.cc b/lib/atscppapi/examples/null_transformation_plugin/NullTransformationPlugin.cc
new file mode 100644
index 0000000..b226290
--- /dev/null
+++ b/lib/atscppapi/examples/null_transformation_plugin/NullTransformationPlugin.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013 LinkedIn Corp. All rights reserved.
+ * Licensed 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.
+ *
+ */
+
+#include <iostream>
+#include <atscppapi/GlobalPlugin.h>
+#include <atscppapi/TransactionPlugin.h>
+#include <atscppapi/TransformationPlugin.h>
+#include <atscppapi/PluginInit.h>
+#include <atscppapi/Logger.h>
+
+using namespace atscppapi;
+using std::string;
+
+namespace {
+atscppapi::Logger *log;
+#define TAG "null_transformation"
+}
+
+class NullTransformationPlugin : public TransformationPlugin {
+public:
+  NullTransformationPlugin(Transaction &transaction)
+    : TransformationPlugin(transaction, RESPONSE_TRANSFORMATION) {
+    registerHook(HOOK_SEND_RESPONSE_HEADERS);
+  }
+
+  void handleSendResponseHeaders(Transaction &transaction) {
+    transaction.getClientResponse().getHeaders().set("X-Content-Transformed", "1");
+    transaction.resume();
+  }
+
+  void consume(const string &data) {
+    produce(data);
+  }
+
+  void handleInputComplete() {
+    setOutputComplete();
+  }
+
+  virtual ~NullTransformationPlugin() {
+
+  }
+
+private:
+};
+
+class GlobalHookPlugin : public GlobalPlugin {
+public:
+  GlobalHookPlugin() {
+    registerHook(HOOK_READ_RESPONSE_HEADERS);
+  }
+
+  virtual void handleReadResponseHeaders(Transaction &transaction) {
+    transaction.addPlugin(new NullTransformationPlugin(transaction));
+    transaction.resume();
+  }
+
+};
+
+void TSPluginInit(int argc, const char *argv[]) {
+  TS_DEBUG(TAG, "TSPluginInit");
+  GlobalPlugin *instance = new GlobalHookPlugin();
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/post_buffer/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/post_buffer/Makefile.am b/lib/atscppapi/examples/post_buffer/Makefile.am
new file mode 100644
index 0000000..e893f5e
--- /dev/null
+++ b/lib/atscppapi/examples/post_buffer/Makefile.am
@@ -0,0 +1,29 @@
+#
+#  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.
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+target=PostBuffer.so
+pkglibdir = ${pkglibexecdir}
+pkglib_LTLIBRARIES = PostBuffer.la
+PostBuffer_la_SOURCES = PostBuffer.cc
+PostBuffer_la_LDFLAGS = -module -avoid-version -shared -L$(top_srcdir) -latscppapi
+
+all:
+	ln -sf .libs/$(target)
+
+clean-local:
+	rm -f $(target)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/post_buffer/PostBuffer.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/post_buffer/PostBuffer.cc b/lib/atscppapi/examples/post_buffer/PostBuffer.cc
new file mode 100644
index 0000000..2c3f132
--- /dev/null
+++ b/lib/atscppapi/examples/post_buffer/PostBuffer.cc
@@ -0,0 +1,75 @@
+/**
+  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 <atscppapi/GlobalPlugin.h>
+#include <atscppapi/TransactionPlugin.h>
+#include <atscppapi/TransformationPlugin.h>
+#include <atscppapi/PluginInit.h>
+
+using namespace atscppapi;
+using std::cerr;
+using std::endl;
+using std::string;
+
+class PostBufferTransformationPlugin : public TransformationPlugin {
+public:
+  PostBufferTransformationPlugin(Transaction &transaction)
+    : TransformationPlugin(transaction, REQUEST_TRANSFORMATION), transaction_(transaction) {
+    buffer_.reserve(1024); // not required, this is an optimization to start the buffer at a slightly higher value.
+  }
+
+  void consume(const string &data) {
+    buffer_.append(data);
+  }
+
+  void handleInputComplete() {
+    produce(buffer_);
+    setOutputComplete();
+  }
+
+  virtual ~PostBufferTransformationPlugin() { }
+
+private:
+  Transaction &transaction_;
+  string buffer_;
+};
+
+class GlobalHookPlugin : public GlobalPlugin {
+public:
+  GlobalHookPlugin() {
+    registerHook(HOOK_READ_REQUEST_HEADERS_POST_REMAP);
+  }
+
+  virtual void handleReadRequestHeadersPostRemap(Transaction &transaction) {
+    cerr << "Read Request Headers Post Remap" << endl;
+    cerr << "Path: " << transaction.getClientRequest().getUrl().getPath() << endl;
+    cerr << "Method: " << HTTP_METHOD_STRINGS[transaction.getClientRequest().getMethod()] << endl;
+    if (transaction.getClientRequest().getMethod() == HTTP_METHOD_POST) {
+      transaction.addPlugin(new PostBufferTransformationPlugin(transaction));
+    }
+
+    transaction.resume();
+  }
+};
+
+void TSPluginInit(int argc, const char *argv[]) {
+  GlobalPlugin *instance = new GlobalHookPlugin();
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/remap_plugin/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/remap_plugin/Makefile.am b/lib/atscppapi/examples/remap_plugin/Makefile.am
new file mode 100644
index 0000000..5926f6a
--- /dev/null
+++ b/lib/atscppapi/examples/remap_plugin/Makefile.am
@@ -0,0 +1,30 @@
+#
+#  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.
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+target=RemapPlugin.so
+pkglibdir = ${pkglibexecdir}
+pkglib_LTLIBRARIES = RemapPlugin.la
+RemapPlugin_la_SOURCES = RemapPlugin.cc
+RemapPlugin_la_LDFLAGS = -module -avoid-version -shared -L$(top_srcdir) -latscppapi
+
+all:
+	ln -sf .libs/$(target)
+
+clean-local:
+	rm -f $(target)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/remap_plugin/RemapPlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/remap_plugin/RemapPlugin.cc b/lib/atscppapi/examples/remap_plugin/RemapPlugin.cc
new file mode 100644
index 0000000..8e06cec
--- /dev/null
+++ b/lib/atscppapi/examples/remap_plugin/RemapPlugin.cc
@@ -0,0 +1,86 @@
+/**
+  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 <atscppapi/RemapPlugin.h>
+#include <atscppapi/PluginInit.h>
+#include <atscppapi/Logger.h>
+#include <vector>
+#include <sstream>
+
+using namespace std;
+using namespace atscppapi;
+
+#define LOG_TAG "remapplugin"
+
+class MyRemapPlugin : public RemapPlugin {
+public:
+  MyRemapPlugin(void **instance_handle) : RemapPlugin(instance_handle) { }
+
+  Result doRemap(const Url &map_from_url, const Url &map_to_url, Transaction &transaction, bool &redirect) {
+    Url &request_url = transaction.getClientRequest().getUrl();
+    TS_DEBUG(LOG_TAG, "from URL is [%s], to URL is [%s], request URL is [%s]", map_from_url.getUrlString().c_str(),
+             map_to_url.getUrlString().c_str(), request_url.getUrlString().c_str());
+    const string &query = request_url.getQuery();
+    string query_param_raw;
+    map<string, string> query_params;
+    std::istringstream iss(query);
+    while (std::getline(iss, query_param_raw, '&')) {
+      size_t equals_pos = query_param_raw.find('=');
+      if (equals_pos && (equals_pos < (query_param_raw.size() - 1))) {
+        query_params[string(query_param_raw, 0, equals_pos)] = 
+          string(query_param_raw, equals_pos + 1, query_param_raw.size() - equals_pos - 1);
+      }
+    }
+    if (query_params.count("error")) {
+      return RESULT_ERROR;
+    }
+    const string &remap = query_params["remap"];
+    bool stop = (query_params["stop"] == "true");
+    Result result = stop ? RESULT_NO_REMAP_STOP : RESULT_NO_REMAP;
+    if (remap == "true") {
+      const string &path = query_params["path"];
+      if (path.size()) {
+        request_url.setPath(path);
+      }
+      const string &host = query_params["host"];
+      if (host.size()) {
+        request_url.setHost(host);
+      }
+      const string &port_str = query_params["port"];
+      if (port_str.size()) {
+        uint16_t port;
+        iss.str(port_str);
+        iss >> port;
+        request_url.setPort(port);
+      }
+      if (query_params.count("redirect")) {
+        redirect = true;
+      }
+      result = stop ? RESULT_DID_REMAP_STOP : RESULT_DID_REMAP;
+    }
+    request_url.setQuery("");
+    TS_DEBUG(LOG_TAG, "Request URL is now [%s]", request_url.getUrlString().c_str());
+    return result;
+  }
+};
+
+TsReturnCode TSRemapNewInstance(int argc, char *argv[], void **instance_handle, char *errbuf, int errbuf_size) {
+  MyRemapPlugin *new_remap_plugin = new MyRemapPlugin(instance_handle);
+  return TS_SUCCESS;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/request_cookies/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/request_cookies/Makefile.am b/lib/atscppapi/examples/request_cookies/Makefile.am
new file mode 100644
index 0000000..dd315cb
--- /dev/null
+++ b/lib/atscppapi/examples/request_cookies/Makefile.am
@@ -0,0 +1,30 @@
+#
+#  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.
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+target=RequestCookies.so
+pkglibdir = ${pkglibexecdir}
+pkglib_LTLIBRARIES = RequestCookies.la
+RequestCookies_la_SOURCES = RequestCookies.cc
+RequestCookies_la_LDFLAGS = -module -avoid-version -shared -L$(top_srcdir) -latscppapi
+
+all:
+	ln -sf .libs/$(target)
+
+clean-local:
+	rm -f $(target)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/request_cookies/RequestCookies.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/request_cookies/RequestCookies.cc b/lib/atscppapi/examples/request_cookies/RequestCookies.cc
new file mode 100644
index 0000000..ebdfdc1
--- /dev/null
+++ b/lib/atscppapi/examples/request_cookies/RequestCookies.cc
@@ -0,0 +1,67 @@
+/**
+  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 <atscppapi/PluginInit.h>
+#include <atscppapi/GlobalPlugin.h>
+#include <atscppapi/Logger.h>
+
+using namespace atscppapi;
+using std::string;
+
+#define LOG_TAG "request_cookies"
+
+class MyGlobalPlugin : GlobalPlugin {
+public:
+  MyGlobalPlugin() {
+    GlobalPlugin::registerHook(Plugin::HOOK_READ_REQUEST_HEADERS_PRE_REMAP);
+  }
+private:
+  void handleReadRequestHeadersPreRemap(Transaction &transaction) {
+    Headers &headers = transaction.getClientRequest().getHeaders();
+    TS_DEBUG(LOG_TAG, "Read request");
+    logRequestCookies(headers);
+    headers.addCookie("gen-c1", "gen-v2");
+    TS_DEBUG(LOG_TAG, "Added cookie");
+    logRequestCookies(headers);
+    headers.setCookie("c1", "correctv");
+    TS_DEBUG(LOG_TAG, "Set cookie");
+    logRequestCookies(headers);
+    headers.deleteCookie("gen-c1");
+    TS_DEBUG(LOG_TAG, "Deleted cookie");
+    logRequestCookies(headers);
+    transaction.resume();
+  }
+
+  void logRequestCookies(Headers &headers) {
+    TS_DEBUG(LOG_TAG, "Cookie header is [%s]", headers.getJoinedValues("Cookie").c_str());
+    string map_str;
+    const Headers::RequestCookieMap &cookie_map = headers.getRequestCookies();
+    for (Headers::RequestCookieMap::const_iterator cookie_iter = cookie_map.begin(), cookie_end = cookie_map.end();
+         cookie_iter != cookie_end; ++cookie_iter) {
+      map_str += cookie_iter->first;
+      map_str += ": ";
+      map_str += Headers::getJoinedValues(cookie_iter->second);
+      map_str += "\n";
+    }
+    TS_DEBUG(LOG_TAG, "Cookie map is\n%s", map_str.c_str());
+  }
+};
+
+void TSPluginInit(int argc, const char *argv[]) {
+  new MyGlobalPlugin();
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/serverresponse/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/serverresponse/Makefile.am b/lib/atscppapi/examples/serverresponse/Makefile.am
new file mode 100644
index 0000000..9f8f4c7
--- /dev/null
+++ b/lib/atscppapi/examples/serverresponse/Makefile.am
@@ -0,0 +1,29 @@
+#
+#  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.
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+target=ServerResponse.so
+pkglibdir = ${pkglibexecdir}
+pkglib_LTLIBRARIES = ServerResponse.la
+ServerResponse_la_SOURCES = ServerResponse.cc
+ServerResponse_la_LDFLAGS = -module -avoid-version -shared -L$(top_srcdir) -latscppapi
+
+all:
+	ln -sf .libs/$(target)
+
+clean-local:
+	rm -f $(target)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/serverresponse/ServerResponse.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/serverresponse/ServerResponse.cc b/lib/atscppapi/examples/serverresponse/ServerResponse.cc
new file mode 100644
index 0000000..d1b7a69
--- /dev/null
+++ b/lib/atscppapi/examples/serverresponse/ServerResponse.cc
@@ -0,0 +1,105 @@
+/**
+  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 <atscppapi/GlobalPlugin.h>
+#include <atscppapi/PluginInit.h>
+#include <atscppapi/utils.h>
+
+using namespace atscppapi;
+
+using std::cout;
+using std::endl;
+using std::list;
+using std::string;
+
+class ServerResponsePlugin : public GlobalPlugin {
+public:
+  ServerResponsePlugin() {
+    registerHook(HOOK_SEND_REQUEST_HEADERS);
+    registerHook(HOOK_READ_RESPONSE_HEADERS);
+    registerHook(HOOK_SEND_RESPONSE_HEADERS);
+  }
+
+  void handleSendRequestHeaders(Transaction &transaction) {
+    // Here we can decide to abort the request to the origin (we can do this earlier too)
+    // and just send the user an error page.
+    if(transaction.getClientRequest().getUrl().getQuery().find("error=1") != string::npos) {
+      // Give this user an error page and don't make a request to an origin.
+      cout << "Sending this request an error page" << endl;
+      transaction.error("This is the error response, but the response code is 500."
+                        "In this example no request was made to the orgin.");
+      // HTTP/1.1 500 INKApi Error
+    } else {
+      transaction.resume();
+    }
+    cout << "Server request headers are" << endl;
+    printHeaders(transaction.getServerRequest().getHeaders());
+  }
+
+  void handleReadResponseHeaders(Transaction &transaction) {
+    cout << "Hello from handleReadResponseHeaders!" << endl;
+    cout << "Server response headers are" << endl;
+    Response &server_response = transaction.getServerResponse();
+    cout << "Reason phrase is " << server_response.getReasonPhrase() << endl;
+    printHeaders(server_response.getHeaders());
+    transaction.resume();
+  }
+
+  void handleSendResponseHeaders(Transaction &transaction) {
+    cout << "Hello from handleSendResponseHeaders!" << endl;
+    cout << "Client response headers are" << endl;
+    printHeaders(transaction.getClientResponse().getHeaders());
+
+    //
+    // If the url contains a query parameter redirect=1 we will send the
+    // user to to somewhere else. Obviously this is a silly example
+    // since we should technically detect this long before the origin
+    // request and prevent the origin request in the first place.
+    //
+
+    if(transaction.getClientRequest().getUrl().getQuery().find("redirect=1") != string::npos) {
+      cout << "Sending this guy to google." << endl;
+      transaction.getClientResponse().getHeaders().set("Location", "http://www.google.com");
+      transaction.getClientResponse().setStatusCode(HTTP_STATUS_MOVED_TEMPORARILY);
+      transaction.getClientResponse().setReasonPhrase("Come Back Later");
+      // HTTP/1.1 302 Come Back Later
+    }
+
+    transaction.resume();
+  }
+
+private:
+  void printHeaders(const Headers &headers) {
+    for (Headers::const_iterator header_iter = headers.begin(), header_end = headers.end();
+         header_iter != header_end; ++header_iter) {
+      const string &name = header_iter->first;
+      const list<string> &value_list = header_iter->second;
+      cout << "Header. " << name <<  ": " << endl;
+      for (list<string>::const_iterator value_iter = value_list.begin(), value_end = value_list.end();
+           value_iter != value_end; ++value_iter) {
+        cout << "\t" << *value_iter << endl;
+      }
+    }
+    cout << endl;
+  }
+};
+
+void TSPluginInit(int argc, const char *argv[]) {
+  GlobalPlugin *instance = new ServerResponsePlugin();
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/stat_example/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/stat_example/Makefile.am b/lib/atscppapi/examples/stat_example/Makefile.am
new file mode 100644
index 0000000..3e49f79
--- /dev/null
+++ b/lib/atscppapi/examples/stat_example/Makefile.am
@@ -0,0 +1,30 @@
+#
+#  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.
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+target=StatExample.so
+pkglibdir = ${pkglibexecdir}
+pkglib_LTLIBRARIES = StatExample.la
+StatExample_la_SOURCES = StatExample.cc
+StatExample_la_LDFLAGS = -module -avoid-version -shared -L$(top_srcdir) -latscppapi
+
+all:
+	ln -sf .libs/$(target)
+
+clean-local:
+	rm -f $(target)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/stat_example/StatExample.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/stat_example/StatExample.cc b/lib/atscppapi/examples/stat_example/StatExample.cc
new file mode 100644
index 0000000..49e91a7
--- /dev/null
+++ b/lib/atscppapi/examples/stat_example/StatExample.cc
@@ -0,0 +1,71 @@
+/**
+  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 <atscppapi/GlobalPlugin.h>
+#include <atscppapi/Logger.h>
+#include <atscppapi/Stat.h>
+#include <atscppapi/PluginInit.h>
+#include <cstring>
+
+using namespace atscppapi;
+using std::string;
+
+namespace {
+// This is for the -T tag debugging
+// To view the debug messages ./traffic_server -T "stat_example.*"
+#define TAG "stat_example"
+
+// This will be the actual stat name
+// You can view it using traffic_line -r stat_example
+const string STAT_NAME = "stat_example";
+
+// This is the stat we'll be using, you can view it's value
+// using traffic_line -r stat_example
+Stat stat;
+}
+
+/*
+ * This is a simple plugin that will increment a counter
+ * everytime a request comes in.
+ */
+class GlobalHookPlugin : public GlobalPlugin {
+public:
+  GlobalHookPlugin() {
+    TS_DEBUG(TAG, "Registering a global hook HOOK_READ_REQUEST_HEADERS_POST_REMAP");
+    registerHook(HOOK_READ_REQUEST_HEADERS_POST_REMAP);
+  }
+
+  virtual void handleReadRequestHeadersPostRemap(Transaction &transaction) {
+    TS_DEBUG(TAG, "Received a request, incrementing the counter.");
+    stat.increment();
+    TS_DEBUG(TAG, "Stat '%s' value = %lld", STAT_NAME.c_str(), stat.get());
+    transaction.resume();
+  }
+};
+
+void TSPluginInit(int argc, const char *argv[]) {
+  TS_DEBUG(TAG, "Loaded stat_example plugin");
+
+  // Since this stat is not persistent it will be initialized to 0.
+  stat.init(STAT_NAME, Stat::SYNC_COUNT, true);
+  stat.set(0);
+
+  GlobalPlugin *instance = new GlobalHookPlugin();
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/timeout_example/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/timeout_example/Makefile.am b/lib/atscppapi/examples/timeout_example/Makefile.am
new file mode 100644
index 0000000..7583476
--- /dev/null
+++ b/lib/atscppapi/examples/timeout_example/Makefile.am
@@ -0,0 +1,30 @@
+#
+#  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.
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+target=TimeoutExamplePlugin.so
+pkglibdir = ${pkglibexecdir}
+pkglib_LTLIBRARIES = TimeoutExamplePlugin.la
+TimeoutExamplePlugin_la_SOURCES = TimeoutExamplePlugin.cc
+TimeoutExamplePlugin_la_LDFLAGS = -module -avoid-version -shared -L$(top_srcdir) -latscppapi
+
+all:
+	ln -sf .libs/$(target)
+
+clean-local:
+	rm -f $(target)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/timeout_example/TimeoutExamplePlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/timeout_example/TimeoutExamplePlugin.cc b/lib/atscppapi/examples/timeout_example/TimeoutExamplePlugin.cc
new file mode 100644
index 0000000..fb2fe83
--- /dev/null
+++ b/lib/atscppapi/examples/timeout_example/TimeoutExamplePlugin.cc
@@ -0,0 +1,56 @@
+/**
+  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 <atscppapi/GlobalPlugin.h>
+#include <atscppapi/Logger.h>
+#include <atscppapi/PluginInit.h>
+
+using namespace atscppapi;
+
+#define TAG "timeout_example_plugin"
+
+class TimeoutExamplePlugin : public GlobalPlugin {
+public:
+  TimeoutExamplePlugin() {
+    registerHook(HOOK_READ_REQUEST_HEADERS_PRE_REMAP);
+    registerHook(HOOK_SEND_RESPONSE_HEADERS);
+  }
+
+  virtual void handleSendResponseHeaders(Transaction &transaction) {
+    TS_DEBUG(TAG, "Sending response headers to the client, status=%d", transaction.getClientResponse().getStatusCode());
+    transaction.resume();
+  }
+
+  virtual void handleReadRequestHeadersPreRemap(Transaction &transaction) {
+    TS_DEBUG(TAG, "Setting all timeouts to 1ms, this will likely cause the transaction to receive a 504.");
+    transaction.setTimeout(Transaction::TIMEOUT_CONNECT, 1);
+    transaction.setTimeout(Transaction::TIMEOUT_ACTIVE, 1);
+    transaction.setTimeout(Transaction::TIMEOUT_DNS, 1);
+    transaction.setTimeout(Transaction::TIMEOUT_NO_ACTIVITY, 1);
+    transaction.resume();
+  }
+};
+
+void TSPluginInit(int argc, const char *argv[]) {
+  TS_DEBUG(TAG, "TSPluginInit");
+  GlobalPlugin *instance = new TimeoutExamplePlugin();
+}
+
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/transactionhook/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/transactionhook/Makefile.am b/lib/atscppapi/examples/transactionhook/Makefile.am
new file mode 100644
index 0000000..1c145cd
--- /dev/null
+++ b/lib/atscppapi/examples/transactionhook/Makefile.am
@@ -0,0 +1,30 @@
+#
+#  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.
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+target=TransactionHookPlugin.so
+pkglibdir = ${pkglibexecdir}
+pkglib_LTLIBRARIES = TransactionHookPlugin.la
+TransactionHookPlugin_la_SOURCES = TransactionHookPlugin.cc
+TransactionHookPlugin_la_LDFLAGS = -module -avoid-version -shared -L$(top_srcdir) -latscppapi
+
+all:
+	ln -sf .libs/$(target)
+
+clean-local:
+	rm -f $(target)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/examples/transactionhook/TransactionHookPlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/examples/transactionhook/TransactionHookPlugin.cc b/lib/atscppapi/examples/transactionhook/TransactionHookPlugin.cc
new file mode 100644
index 0000000..b84f300
--- /dev/null
+++ b/lib/atscppapi/examples/transactionhook/TransactionHookPlugin.cc
@@ -0,0 +1,60 @@
+/**
+  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 <atscppapi/GlobalPlugin.h>
+#include <atscppapi/TransactionPlugin.h>
+#include <atscppapi/PluginInit.h>
+
+using namespace atscppapi;
+
+class TransactionHookPlugin : public atscppapi::TransactionPlugin {
+public:
+  TransactionHookPlugin(Transaction &transaction) : TransactionPlugin(transaction) {
+    char_ptr_ = new char[100];
+    TransactionPlugin::registerHook(HOOK_SEND_RESPONSE_HEADERS);
+    std::cout << "Constructed!" << std::endl;
+  }
+  virtual ~TransactionHookPlugin() {
+    delete[] char_ptr_; // cleanup
+    std::cout << "Destroyed!" << std::endl;
+  }
+  void handleSendResponseHeaders(Transaction &transaction) {
+    std::cout << "Send response headers!" << std::endl;
+    transaction.resume();
+  }
+private:
+  char *char_ptr_;
+};
+
+class GlobalHookPlugin : public atscppapi::GlobalPlugin {
+public:
+  GlobalHookPlugin() {
+    GlobalPlugin::registerHook(HOOK_READ_REQUEST_HEADERS_PRE_REMAP);
+  }
+  virtual void handleReadRequestHeadersPreRemap(Transaction &transaction) {
+    std::cout << "Hello from handleReadRequesHeadersPreRemap!" << std::endl;
+    transaction.addPlugin(new TransactionHookPlugin(transaction));
+    transaction.resume();
+  }
+};
+
+void TSPluginInit(int argc, const char *argv[]) {
+  atscppapi::GlobalPlugin *instance = new GlobalHookPlugin();
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/AsyncHttpFetch.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/AsyncHttpFetch.cc b/lib/atscppapi/src/AsyncHttpFetch.cc
new file mode 100644
index 0000000..51ca95d
--- /dev/null
+++ b/lib/atscppapi/src/AsyncHttpFetch.cc
@@ -0,0 +1,170 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file AsyncHttpFetch.cc
+ */
+
+#include "atscppapi/AsyncHttpFetch.h"
+#include <ts/ts.h>
+#include <arpa/inet.h>
+#include "logging_internal.h"
+#include "utils_internal.h"
+
+using namespace atscppapi;
+using std::string;
+
+/**
+ * @private
+ */
+struct atscppapi::AsyncHttpFetchState : noncopyable {
+  Request request_;
+  Response response_;
+  AsyncHttpFetch::Result result_;
+  const void *body_;
+  size_t body_size_;
+  TSMBuffer hdr_buf_;
+  TSMLoc hdr_loc_;
+  shared_ptr<AsyncDispatchControllerBase> dispatch_controller_;
+
+  AsyncHttpFetchState(const string &url_str, HttpMethod http_method)
+    : request_(url_str, http_method, HTTP_VERSION_1_0), result_(AsyncHttpFetch::RESULT_FAILURE), body_(NULL),
+      body_size_(0), hdr_buf_(NULL), hdr_loc_(NULL) { }
+  
+  ~AsyncHttpFetchState() {
+    if (hdr_loc_) {
+      TSMLoc null_parent_loc = NULL;
+      TSHandleMLocRelease(hdr_buf_, null_parent_loc, hdr_loc_);
+    }
+    if (hdr_buf_) {
+      TSMBufferDestroy(hdr_buf_);
+    }
+  }
+};
+
+namespace {
+
+const unsigned int LOCAL_IP_ADDRESS = 0x0100007f;
+const int LOCAL_PORT = 8080;
+
+static int handleFetchEvents(TSCont cont, TSEvent event, void *edata) {
+  LOG_DEBUG("Fetch result returned event = %d, edata = %p", event, edata);
+  AsyncHttpFetch *fetch_provider = static_cast<AsyncHttpFetch *>(TSContDataGet(cont));
+  AsyncHttpFetchState *state = utils::internal::getAsyncHttpFetchState(*fetch_provider);
+  
+  if (event == static_cast<int>(AsyncHttpFetch::RESULT_SUCCESS)) {
+    TSHttpTxn txn = static_cast<TSHttpTxn>(edata);
+    int data_len;
+    const char *data_start = TSFetchRespGet(txn, &data_len);
+    const char *data_end = data_start + data_len;
+    
+    TSHttpParser parser = TSHttpParserCreate();
+    state->hdr_buf_ = TSMBufferCreate();
+    state->hdr_loc_ = TSHttpHdrCreate(state->hdr_buf_);
+    TSHttpHdrTypeSet(state->hdr_buf_, state->hdr_loc_, TS_HTTP_TYPE_RESPONSE);
+    if (TSHttpHdrParseResp(parser, state->hdr_buf_, state->hdr_loc_, &data_start, data_end) == TS_PARSE_DONE) {
+      TSHttpStatus status = TSHttpHdrStatusGet(state->hdr_buf_, state->hdr_loc_);
+      state->body_ = data_start; // data_start will now be pointing to body
+      state->body_size_ = data_end - data_start;
+      utils::internal::initResponse(state->response_, state->hdr_buf_, state->hdr_loc_);
+      LOG_DEBUG("Fetch result had a status code of %d with a body length of %d", status, state->body_size_);
+    } else {
+      LOG_ERROR("Unable to parse response; Request URL [%s]; transaction %p",
+                state->request_.getUrl().getUrlString().c_str(), txn);
+      event = static_cast<TSEvent>(AsyncHttpFetch::RESULT_FAILURE);
+    }
+    TSHttpParserDestroy(parser);
+  }
+  state->result_ = static_cast<AsyncHttpFetch::Result>(event);
+  if (!state->dispatch_controller_->dispatch()) {
+    LOG_DEBUG("Unable to dispatch result from AsyncFetch because promise has died.");
+  }
+
+  delete fetch_provider; // we must always be sure to clean up the provider when we're done with it.
+  TSContDestroy(cont);
+  return 0;
+}
+
+}
+
+AsyncHttpFetch::AsyncHttpFetch(const std::string &url_str, HttpMethod http_method) {
+  LOG_DEBUG("Created new AsyncHttpFetch object %p", this);
+  state_ = new AsyncHttpFetchState(url_str, http_method);
+}
+
+void AsyncHttpFetch::run(shared_ptr<AsyncDispatchControllerBase> sender) {
+  state_->dispatch_controller_ = sender;
+
+  TSCont fetchCont = TSContCreate(handleFetchEvents, TSMutexCreate());
+  TSContDataSet(fetchCont, static_cast<void *>(this)); // Providers have to clean themselves up when they are done.
+
+  TSFetchEvent event_ids;
+  event_ids.success_event_id = RESULT_SUCCESS;
+  event_ids.failure_event_id = RESULT_FAILURE;
+  event_ids.timeout_event_id = RESULT_TIMEOUT;
+
+  struct sockaddr_in addr;
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = LOCAL_IP_ADDRESS;
+  addr.sin_port = LOCAL_PORT;
+
+  string request_str(HTTP_METHOD_STRINGS[state_->request_.getMethod()]);
+  request_str += ' ';
+  request_str += state_->request_.getUrl().getUrlString();
+  request_str += ' ';
+  request_str += HTTP_VERSION_STRINGS[state_->request_.getVersion()];
+  request_str += "\r\n";
+
+  for (Headers::const_iterator iter = state_->request_.getHeaders().begin(),
+         end = state_->request_.getHeaders().end(); iter != end; ++iter) {
+    request_str += iter->first;
+    request_str += ": ";
+    request_str += Headers::getJoinedValues(iter->second);
+    request_str += "\r\n";
+  }
+  request_str += "\r\n";
+
+  LOG_DEBUG("Issing TSFetchUrl with request\n[%s]", request_str.c_str());
+  TSFetchUrl(request_str.c_str(), request_str.size(), reinterpret_cast<struct sockaddr const *>(&addr), fetchCont,
+             AFTER_BODY, event_ids);
+}
+
+Headers &AsyncHttpFetch::getRequestHeaders() {
+  return state_->request_.getHeaders();
+}
+
+AsyncHttpFetch::Result AsyncHttpFetch::getResult() const {
+  return state_->result_;
+}
+
+const Url &AsyncHttpFetch::getRequestUrl() const {
+  return state_->request_.getUrl();
+}
+
+const Response &AsyncHttpFetch::getResponse() const {
+  return state_->response_;
+}
+
+void AsyncHttpFetch::getResponseBody(const void *&body, size_t &body_size) const {
+  body = state_->body_;
+  body_size = state_->body_size_;
+}
+
+AsyncHttpFetch::~AsyncHttpFetch() {
+  delete state_;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/AsyncTimer.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/AsyncTimer.cc b/lib/atscppapi/src/AsyncTimer.cc
new file mode 100644
index 0000000..975af3c
--- /dev/null
+++ b/lib/atscppapi/src/AsyncTimer.cc
@@ -0,0 +1,106 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file AsyncTimer.cc
+ */
+#include "atscppapi/AsyncTimer.h"
+#include <ts/ts.h>
+#include "logging_internal.h"
+
+using namespace atscppapi;
+
+struct atscppapi::AsyncTimerState {
+  TSCont cont_;
+  AsyncTimer::Type type_;
+  int period_in_ms_;
+  int initial_period_in_ms_;
+  TSAction initial_timer_action_;
+  TSAction periodic_timer_action_;
+  AsyncTimer *timer_;
+  shared_ptr<AsyncDispatchControllerBase> dispatch_controller_;
+  AsyncTimerState(AsyncTimer::Type type, int period_in_ms, int initial_period_in_ms, AsyncTimer *timer)
+    : type_(type), period_in_ms_(period_in_ms), initial_period_in_ms_(initial_period_in_ms),
+      initial_timer_action_(NULL), periodic_timer_action_(NULL), timer_(timer) { }
+};
+
+namespace {
+
+int handleTimerEvent(TSCont cont, TSEvent event, void *edata) {
+  AsyncTimerState *state = static_cast<AsyncTimerState *>(TSContDataGet(cont));
+  if (state->initial_timer_action_) {
+    LOG_DEBUG("Received initial timer event.");
+    state->initial_timer_action_ = NULL; // mark it so that it won't be canceled in the destructor
+    if (state->type_ == AsyncTimer::TYPE_PERIODIC) {
+      LOG_DEBUG("Scheduling periodic event now");
+      state->periodic_timer_action_ = TSContScheduleEvery(state->cont_, state->period_in_ms_,
+                                                          TS_THREAD_POOL_DEFAULT);
+    }
+  }
+  if (!state->dispatch_controller_->dispatch()) {
+    LOG_DEBUG("Receiver has died. Destroying timer");
+    delete state->timer_; // auto-destruct only in this case
+  }
+  return 0;
+}
+
+}
+
+AsyncTimer::AsyncTimer(Type type, int period_in_ms, int initial_period_in_ms) {
+  state_ = new AsyncTimerState(type, period_in_ms, initial_period_in_ms, this);
+  TSMutex null_mutex = NULL;
+  state_->cont_ = TSContCreate(handleTimerEvent, null_mutex);
+  TSContDataSet(state_->cont_, static_cast<void *>(state_));
+}
+
+void AsyncTimer::run(shared_ptr<AsyncDispatchControllerBase> dispatch_controller) {
+  int one_off_timeout_in_ms = 0;
+  int regular_timeout_in_ms = 0;
+  if (state_->type_ == AsyncTimer::TYPE_ONE_OFF) {
+    one_off_timeout_in_ms = state_->period_in_ms_;
+  }
+  else {
+    one_off_timeout_in_ms = state_->initial_period_in_ms_;
+    regular_timeout_in_ms = state_->period_in_ms_;
+  }
+  if (one_off_timeout_in_ms) {
+    LOG_DEBUG("Scheduling initial/one-off event");
+    state_->initial_timer_action_ = TSContSchedule(state_->cont_, one_off_timeout_in_ms,
+                                                   TS_THREAD_POOL_DEFAULT);
+  }
+  else if (regular_timeout_in_ms) {
+    LOG_DEBUG("Scheduling regular timer events");
+    state_->periodic_timer_action_ = TSContScheduleEvery(state_->cont_, regular_timeout_in_ms,
+                                                         TS_THREAD_POOL_DEFAULT);
+  }
+  state_->dispatch_controller_ = dispatch_controller;
+}
+
+AsyncTimer::~AsyncTimer() {
+  if (state_->initial_timer_action_) {
+    LOG_DEBUG("Canceling initial timer action");
+    TSActionCancel(state_->initial_timer_action_);
+  }
+  if (state_->periodic_timer_action_) {
+    LOG_DEBUG("Canceling periodic timer action");
+    TSActionCancel(state_->periodic_timer_action_);
+  }
+  LOG_DEBUG("Destroying cont");
+  TSContDestroy(state_->cont_);
+  delete state_;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/CaseInsensitiveStringComparator.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/CaseInsensitiveStringComparator.cc b/lib/atscppapi/src/CaseInsensitiveStringComparator.cc
new file mode 100644
index 0000000..2d8b77b
--- /dev/null
+++ b/lib/atscppapi/src/CaseInsensitiveStringComparator.cc
@@ -0,0 +1,70 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file CaseInsensitiveStringComparator.cc
+ */
+
+#include "atscppapi/CaseInsensitiveStringComparator.h"
+
+namespace {
+  static char NORMALIZED_CHARACTERS[256];
+  static volatile bool normalizer_initialized(false);
+}
+
+using atscppapi::CaseInsensitiveStringComparator;
+using std::string;
+
+bool CaseInsensitiveStringComparator::operator()(const string &lhs, const string &rhs) const {
+  return (compare(lhs, rhs) < 0);
+}
+
+int CaseInsensitiveStringComparator::compare(const string &lhs, const string &rhs) const {
+  if (!normalizer_initialized) {
+    // this initialization is safe to execute in concurrent threads - hence no locking
+    for (int i = 0; i < 256; ++i) {
+      NORMALIZED_CHARACTERS[i] = static_cast<unsigned char>(i);
+    }
+    for (unsigned char i = 'A'; i < 'Z'; ++i) {
+      NORMALIZED_CHARACTERS[i] = 'a' + (i - 'A');
+    }
+    normalizer_initialized = true;
+  }
+  size_t lhs_size = lhs.size();
+  size_t rhs_size = rhs.size();
+  if ((lhs_size > 0) && (rhs_size > 0)) { 
+    size_t num_chars_to_compare = (lhs_size < rhs_size) ? lhs_size : rhs_size;
+    for (size_t i = 0; i < num_chars_to_compare; ++i) {
+      unsigned char normalized_lhs_char = NORMALIZED_CHARACTERS[static_cast<const unsigned char>(lhs[i])]; 
+      unsigned char normalized_rhs_char = NORMALIZED_CHARACTERS[static_cast<const unsigned char>(rhs[i])]; 
+      if (normalized_lhs_char < normalized_rhs_char) {
+        return -1;
+      }
+      if (normalized_lhs_char > normalized_rhs_char) {
+        return 1;
+      }
+    }
+  }
+  if (lhs_size < rhs_size) {
+    return -1;
+  }
+  if (lhs_size > rhs_size) {
+    return 1;
+  }
+  return 0; // both strings are equal
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/ClientRequest.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/ClientRequest.cc b/lib/atscppapi/src/ClientRequest.cc
new file mode 100644
index 0000000..8f2e602
--- /dev/null
+++ b/lib/atscppapi/src/ClientRequest.cc
@@ -0,0 +1,75 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file ClientRequest.cc
+ */
+
+#include "atscppapi/ClientRequest.h"
+#include <cstdlib>
+#include <ts/ts.h>
+#include "atscppapi/noncopyable.h"
+#include "InitializableValue.h"
+#include "logging_internal.h"
+
+using namespace atscppapi;
+
+/**
+ * @private
+ */
+struct atscppapi::ClientRequestState: noncopyable {
+  TSHttpTxn txn_;
+  TSMBuffer pristine_hdr_buf_;
+  TSMLoc pristine_url_loc_;
+  Url pristine_url_;
+  ClientRequestState(TSHttpTxn txn) : txn_(txn), pristine_hdr_buf_(NULL), pristine_url_loc_(NULL) { }
+};
+
+atscppapi::ClientRequest::ClientRequest(void *ats_txn_handle, void *hdr_buf, void *hdr_loc) :
+    Request(hdr_buf, hdr_loc) {
+  state_ = new ClientRequestState(static_cast<TSHttpTxn>(ats_txn_handle));
+}
+
+atscppapi::ClientRequest::~ClientRequest() {
+  if (state_->pristine_url_loc_ && state_->pristine_hdr_buf_) {
+    TSMLoc null_parent_loc = NULL;
+    LOG_DEBUG("Releasing pristine url loc for transaction %p; hdr_buf %p, url_loc %p", state_->txn_,
+              state_->pristine_hdr_buf_, state_->pristine_url_loc_);
+    TSHandleMLocRelease(state_->pristine_hdr_buf_, null_parent_loc, state_->pristine_url_loc_);
+  }
+
+  delete state_;
+}
+
+const Url &atscppapi::ClientRequest::getPristineUrl() const {
+  if (!state_->pristine_url_loc_) {
+    TSHttpTxnPristineUrlGet(state_->txn_, &state_->pristine_hdr_buf_, &state_->pristine_url_loc_);
+
+    if ((state_->pristine_hdr_buf_ != NULL) && (state_->pristine_url_loc_ != NULL)) {
+      state_->pristine_url_.init(state_->pristine_hdr_buf_, state_->pristine_url_loc_);
+      LOG_DEBUG("Pristine URL initialized");
+    } else {
+      LOG_ERROR("Failed to get pristine URL for transaction %p; hdr_buf %p, url_loc %p", state_->txn_,
+                state_->pristine_hdr_buf_, state_->pristine_url_loc_);
+    }
+  } else {
+    LOG_DEBUG("Pristine URL already initialized");
+  }
+
+  return state_->pristine_url_;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/GlobalPlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/GlobalPlugin.cc b/lib/atscppapi/src/GlobalPlugin.cc
new file mode 100644
index 0000000..40cd9e8
--- /dev/null
+++ b/lib/atscppapi/src/GlobalPlugin.cc
@@ -0,0 +1,78 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file GlobalPlugin.cc
+ */
+#include "atscppapi/GlobalPlugin.h"
+#include <ts/ts.h>
+#include <cstddef>
+#include <cassert>
+#include "atscppapi/noncopyable.h"
+#include "utils_internal.h"
+#include "logging_internal.h"
+
+using namespace atscppapi;
+
+/**
+ * @private
+ */
+struct atscppapi::GlobalPluginState : noncopyable {
+  TSCont cont_;
+  bool ignore_internal_transactions_;
+  GlobalPlugin *global_plugin_;
+  GlobalPluginState(GlobalPlugin *global_plugin, bool ignore_internal_transactions)
+    : global_plugin_(global_plugin), ignore_internal_transactions_(ignore_internal_transactions) { }
+};
+
+namespace {
+
+static int handleGlobalPluginEvents(TSCont cont, TSEvent event, void *edata) {
+  TSHttpTxn txn = static_cast<TSHttpTxn>(edata);
+  GlobalPluginState *state = static_cast<GlobalPluginState *>(TSContDataGet(cont));
+  if (state->ignore_internal_transactions_ && (TSHttpIsInternalRequest(txn) == TS_SUCCESS)) {
+    LOG_DEBUG("Ignoring event %d on internal transaction %p for global plugin %p", event, txn,
+              state->global_plugin_);
+    TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+  } else {
+    LOG_DEBUG("Invoking global plugin %p for event %d on transaction %p", state->global_plugin_, event, txn);
+    utils::internal::invokePluginForEvent(state->global_plugin_, txn, event);
+  }
+  return 0;
+}
+
+} /* anonymous namespace */
+
+GlobalPlugin::GlobalPlugin(bool ignore_internal_transactions) {
+  utils::internal::initTransactionManagement();
+  state_ = new GlobalPluginState(this, ignore_internal_transactions);
+  TSMutex mutex = NULL;
+  state_->cont_ = TSContCreate(handleGlobalPluginEvents, mutex);
+  TSContDataSet(state_->cont_, static_cast<void *>(state_));
+}
+
+GlobalPlugin::~GlobalPlugin() {
+  TSContDestroy(state_->cont_);
+  delete state_;
+}
+
+void GlobalPlugin::registerHook(Plugin::HookType hook_type) {
+  TSHttpHookID hook_id = utils::internal::convertInternalHookToTsHook(hook_type);
+  TSHttpHookAdd(hook_id, state_->cont_);
+  LOG_DEBUG("Registered global plugin %p for hook %s", this, HOOK_TYPE_STRINGS[hook_type].c_str());
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/GzipDeflateTransformation.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/GzipDeflateTransformation.cc b/lib/atscppapi/src/GzipDeflateTransformation.cc
new file mode 100644
index 0000000..5d701c8
--- /dev/null
+++ b/lib/atscppapi/src/GzipDeflateTransformation.cc
@@ -0,0 +1,156 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+/**
+ * @file GzipDeflateTransformation.cc
+ */
+
+#include <string>
+#include <cstring>
+#include <vector>
+#include <zlib.h>
+#include "atscppapi/TransformationPlugin.h"
+#include "atscppapi/GzipDeflateTransformation.h"
+#include "logging_internal.h"
+
+using namespace atscppapi::transformations;
+using std::string;
+using std::vector;
+
+namespace {
+const int GZIP_MEM_LEVEL = 8;
+const int WINDOW_BITS = 31; // Always use 31 for gzip.
+const int ONE_KB = 1024;
+}
+
+/**
+ * @private
+ */
+struct atscppapi::transformations::GzipDeflateTransformationState: noncopyable {
+  z_stream z_stream_;
+  bool z_stream_initialized_;
+  int64_t bytes_produced_;
+  TransformationPlugin::Type transformation_type_;
+
+  GzipDeflateTransformationState(TransformationPlugin::Type type) :
+        z_stream_initialized_(false), transformation_type_(type), bytes_produced_(0) {
+
+    memset(&z_stream_, 0, sizeof(z_stream_));
+    int err =
+        deflateInit2(&z_stream_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, WINDOW_BITS , GZIP_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+
+    if (Z_OK != err) {
+      LOG_ERROR("deflateInit2 failed with error code '%d'.", err);
+    } else {
+      z_stream_initialized_ = true;
+    }
+  };
+
+  ~GzipDeflateTransformationState() {
+    if (z_stream_initialized_) {
+      deflateEnd(&z_stream_);
+    }
+  };
+};
+
+
+GzipDeflateTransformation::GzipDeflateTransformation(Transaction &transaction, TransformationPlugin::Type type) : TransformationPlugin(transaction, type) {
+  state_ = new GzipDeflateTransformationState(type);
+}
+
+GzipDeflateTransformation::~GzipDeflateTransformation() {
+  delete state_;
+}
+
+void GzipDeflateTransformation::consume(const string &data) {
+  if (data.size() == 0) {
+    return;
+  }
+
+  if (!state_->z_stream_initialized_) {
+    LOG_ERROR("Unable to deflate output because the z_stream was not initialized.");
+    return;
+  }
+
+  int iteration = 0;
+  state_->z_stream_.data_type = Z_ASCII;
+  state_->z_stream_.next_in = reinterpret_cast<unsigned char *>(const_cast<char *>(data.c_str()));
+  state_->z_stream_.avail_in = data.length();
+
+  // For small payloads the size can actually be greater than the original input
+  // so we'll use twice the original size to avoid needless repeated calls to deflate.
+  unsigned long buffer_size = data.length() < ONE_KB ? 2 * ONE_KB : data.length();
+  vector<unsigned char> buffer(buffer_size);
+
+  do {
+    LOG_DEBUG("Iteration %d: Deflate will compress %d bytes", ++iteration, data.size());
+    state_->z_stream_.avail_out = buffer_size;
+    state_->z_stream_.next_out = &buffer[0];
+
+    int err = deflate(&state_->z_stream_, Z_SYNC_FLUSH);
+    if (Z_OK != err) {
+      LOG_ERROR("Iteration %d: Deflate failed to compress %d bytes with error code '%d'", iteration, data.size(), err);
+      return;
+    }
+
+    int bytes_to_write = buffer_size - state_->z_stream_.avail_out;
+    state_->bytes_produced_ += bytes_to_write;
+
+    LOG_DEBUG("Iteration %d: Deflate compressed %d bytes to %d bytes, producing output...", iteration, data.size(), bytes_to_write);
+    produce(string(reinterpret_cast<char *>(&buffer[0]), static_cast<size_t>(bytes_to_write)));
+  } while (state_->z_stream_.avail_out == 0);
+
+  if (state_->z_stream_.avail_in != 0) {
+    LOG_ERROR("Inflate finished with data still remaining in the buffer of size '%d'", state_->z_stream_.avail_in);
+  }
+}
+
+void GzipDeflateTransformation::handleInputComplete() {
+  // We will flush out anything that's remaining in the gzip buffer
+  int status = Z_OK;
+  int iteration = 0;
+  const int buffer_size = 1024; // 1024 bytes is usually more than enough for the epilouge
+  unsigned char buffer[buffer_size];
+
+  /* Deflate remaining data */
+  do {
+    LOG_DEBUG("Iteration %d: Gzip deflate finalizing.", ++iteration);
+    state_->z_stream_.data_type = Z_ASCII;
+    state_->z_stream_.avail_out = buffer_size;
+    state_->z_stream_.next_out = buffer;
+
+    status = deflate(&state_->z_stream_, Z_FINISH);
+
+    int bytes_to_write = buffer_size - state_->z_stream_.avail_out;
+    state_->bytes_produced_ += bytes_to_write;
+
+    if (status == Z_OK || status == Z_STREAM_END) {
+      LOG_DEBUG("Iteration %d: Gzip deflate finalize had an extra %d bytes to process, status '%d'. Producing output...", iteration, bytes_to_write, status);
+      produce(string(reinterpret_cast<char *>(buffer), static_cast<size_t>(bytes_to_write)));
+    } else if (status != Z_STREAM_END) {
+      LOG_ERROR("Iteration %d: Gzip deflinate finalize produced an error '%d'", iteration, status);
+    }
+  } while (status == Z_OK);
+
+  int64_t bytes_written = setOutputComplete();
+  if (state_->bytes_produced_ != bytes_written) {
+    LOG_ERROR("Gzip bytes produced sanity check failed, deflated bytes = %d != written bytes = %d", state_->bytes_produced_, bytes_written);
+  }
+}
+
+
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/GzipInflateTransformation.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/GzipInflateTransformation.cc b/lib/atscppapi/src/GzipInflateTransformation.cc
new file mode 100644
index 0000000..931e6e8
--- /dev/null
+++ b/lib/atscppapi/src/GzipInflateTransformation.cc
@@ -0,0 +1,130 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file GzipInflateTransformation.cc
+ */
+
+#include <string>
+#include <cstring>
+#include <vector>
+#include <zlib.h>
+#include "atscppapi/TransformationPlugin.h"
+#include "atscppapi/GzipInflateTransformation.h"
+#include "logging_internal.h"
+
+using namespace atscppapi::transformations;
+using std::string;
+using std::vector;
+
+namespace {
+const int WINDOW_BITS = 31; // Always use 31 for gzip.
+unsigned int INFLATE_SCALE_FACTOR = 6;
+}
+
+/**
+ * @private
+ */
+struct atscppapi::transformations::GzipInflateTransformationState: noncopyable {
+  z_stream z_stream_;
+  bool z_stream_initialized_;
+  int64_t bytes_produced_;
+  TransformationPlugin::Type transformation_type_;
+
+  GzipInflateTransformationState(TransformationPlugin::Type type) :
+        z_stream_initialized_(false), transformation_type_(type), bytes_produced_(0) {
+
+    memset(&z_stream_, 0, sizeof(z_stream_));
+
+    int err = inflateInit2(&z_stream_, WINDOW_BITS);
+    if (Z_OK != err) {
+      LOG_ERROR("inflateInit2 failed with error code '%d'.", err);
+    } else {
+      z_stream_initialized_ = true;
+    }
+  };
+
+  ~GzipInflateTransformationState() {
+    if (z_stream_initialized_) {
+      int err = inflateEnd(&z_stream_);
+      if (Z_OK != err && Z_STREAM_END != err) {
+        LOG_ERROR("Unable to inflateEnd(), returned error code '%d'", err);
+      }
+    }
+  };
+};
+
+
+GzipInflateTransformation::GzipInflateTransformation(Transaction &transaction, TransformationPlugin::Type type) : TransformationPlugin(transaction, type) {
+  state_ = new GzipInflateTransformationState(type);
+}
+
+GzipInflateTransformation::~GzipInflateTransformation() {
+  delete state_;
+}
+
+void GzipInflateTransformation::consume(const string &data) {
+  if (data.size() == 0) {
+    return;
+  }
+
+  if (!state_->z_stream_initialized_) {
+    LOG_ERROR("Unable to inflate output because the z_stream was not initialized.");
+    return;
+  }
+
+  int err = Z_OK;
+  int iteration = 0;
+  int inflate_block_size = INFLATE_SCALE_FACTOR * data.size();
+  vector<char> buffer(inflate_block_size);
+
+  // Setup the compressed input
+  state_->z_stream_.next_in = reinterpret_cast<unsigned char *>(const_cast<char *>(data.c_str()));
+  state_->z_stream_.avail_in = data.length();
+
+  // Loop while we have more data to inflate
+  while (state_->z_stream_.avail_in > 0 && err != Z_STREAM_END) {
+    LOG_DEBUG("Iteration %d: Gzip has %d bytes to inflate", ++iteration, state_->z_stream_.avail_in);
+
+    // Setup where the decompressed output will go.
+    state_->z_stream_.next_out = reinterpret_cast<unsigned char *>(&buffer[0]);
+    state_->z_stream_.avail_out = inflate_block_size;
+
+    /* Uncompress */
+    err = inflate(&state_->z_stream_, Z_SYNC_FLUSH);
+
+    if (err != Z_OK && err != Z_STREAM_END) {
+     LOG_ERROR("Iteration %d: Inflate failed with error '%d'", iteration, err);
+     return;
+    }
+
+    LOG_DEBUG("Iteration %d: Gzip inflated a total of %d bytes, producingOutput...", iteration, (inflate_block_size - state_->z_stream_.avail_out));
+    produce(string(&buffer[0], (inflate_block_size - state_->z_stream_.avail_out)));
+    state_->bytes_produced_ += (inflate_block_size - state_->z_stream_.avail_out);
+  }
+}
+
+void GzipInflateTransformation::handleInputComplete() {
+  int64_t bytes_written = setOutputComplete();
+  if (state_->bytes_produced_ != bytes_written) {
+    LOG_ERROR("Gzip bytes produced sanity check failed, inflated bytes = %d != written bytes = %d", state_->bytes_produced_, bytes_written);
+  }
+}
+
+
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Headers.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Headers.cc b/lib/atscppapi/src/Headers.cc
new file mode 100644
index 0000000..c4da326
--- /dev/null
+++ b/lib/atscppapi/src/Headers.cc
@@ -0,0 +1,532 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file Headers.cc
+ */
+#include "atscppapi/Headers.h"
+#include "InitializableValue.h"
+#include "logging_internal.h"
+#include <ts/ts.h>
+#include "atscppapi/noncopyable.h"
+#include <cctype>
+
+using atscppapi::Headers;
+using std::string;
+using std::list;
+using std::pair;
+using std::make_pair;
+using std::ostringstream;
+using std::map;
+
+namespace atscppapi {
+
+namespace {
+  const int APPEND_INDEX = -1;
+  const list<string> EMPTY_VALUE_LIST;
+  const int FIRST_INDEX = 0;
+}
+
+/**
+ * @private
+ */
+struct HeadersState: noncopyable {
+  Headers::Type type_;
+  TSMBuffer hdr_buf_;
+  TSMLoc hdr_loc_;
+  InitializableValue<Headers::NameValuesMap> name_values_map_;
+  bool detached_;
+  InitializableValue<Headers::RequestCookieMap> request_cookies_;
+  InitializableValue<list<Headers::ResponseCookie> > response_cookies_;
+  HeadersState(Headers::Type type) : type_(type), hdr_buf_(NULL), hdr_loc_(NULL), detached_(false) { }
+};
+
+}
+
+Headers::Headers(Type type) {
+  state_ = new HeadersState(type);
+}
+
+Headers::Type Headers::getType() const {
+  return state_->type_;
+}
+
+void Headers::setType(Type type) {
+  state_->type_ = type;
+}
+
+void Headers::init(void *hdr_buf, void *hdr_loc) {
+  if (state_->hdr_buf_ || state_->hdr_loc_ || state_->detached_) {
+    LOG_ERROR("Cannot reinitialize; hdr_buf %p, hdr_loc %p, detached %s", state_->hdr_buf_, state_->hdr_loc_,
+              (state_->detached_ ? "true" : "false"));
+    return;
+  }
+  state_->hdr_buf_ = static_cast<TSMBuffer>(hdr_buf);
+  state_->hdr_loc_ = static_cast<TSMLoc>(hdr_loc);
+}
+
+void Headers::initDetached() {
+  if (state_->hdr_buf_ || state_->hdr_loc_ || state_->detached_) {
+    LOG_ERROR("Cannot reinitialize; hdr_buf %p, hdr_loc %p, detached %s", state_->hdr_buf_, state_->hdr_loc_,
+              (state_->detached_ ? "true" : "false"));
+    return;
+  }
+  state_->detached_ = true;
+  state_->name_values_map_.setInitialized();
+}
+
+namespace {
+
+void extractHeaderFieldValues(TSMBuffer hdr_buf, TSMLoc hdr_loc, TSMLoc field_loc, const string &header_name,
+                              list<string> &value_list) {
+  int num_values = TSMimeHdrFieldValuesCount(hdr_buf, hdr_loc, field_loc);
+  LOG_DEBUG("Got %d values for header [%s]", num_values, header_name.c_str());
+  if (!num_values) {
+    LOG_DEBUG("Header [%s] has no values; Adding empty string", header_name.c_str());
+    value_list.push_back(string());
+    return;
+  }
+  const char *value;
+  int value_len;
+  for (int i = 0; i < num_values; ++i) {
+    value = TSMimeHdrFieldValueStringGet(hdr_buf, hdr_loc, field_loc, i, &value_len);
+    value_list.push_back((value && value_len) ? string(value, value_len) : string());
+    LOG_DEBUG("Added value [%.*s] to header [%s]", value_len, value, header_name.c_str());
+  }
+}
+
+}
+
+bool Headers::checkAndInitHeaders() const {
+  if (state_->name_values_map_.isInitialized()) {
+    return true;
+  } else if ((state_->hdr_buf_ == NULL) || (state_->hdr_loc_ == NULL)) {
+    LOG_ERROR("Failed to initialize! TS header handles not set; hdr_buf %p, hdr_loc %p", state_->hdr_buf_, 
+              state_->hdr_loc_);
+    return false;
+  }
+  state_->name_values_map_.getValueRef().clear();
+  string key;
+  const char *name, *value;
+  int name_len, num_values, value_len;
+  pair<NameValuesMap::iterator, bool> insert_result;
+  TSMLoc field_loc = TSMimeHdrFieldGet(state_->hdr_buf_, state_->hdr_loc_, FIRST_INDEX);
+  while (field_loc) {
+    name = TSMimeHdrFieldNameGet(state_->hdr_buf_, state_->hdr_loc_, field_loc, &name_len);
+    if (name && (name_len > 0)) {
+      key.assign(name, name_len);
+      insert_result = state_->name_values_map_.getValueRef().insert(
+        NameValuesMap::value_type(key, EMPTY_VALUE_LIST));
+      NameValuesMap::iterator &inserted_element = insert_result.first;
+      list<string> &value_list = inserted_element->second;
+      extractHeaderFieldValues(state_->hdr_buf_, state_->hdr_loc_, field_loc, key, value_list);
+    } else {
+      LOG_ERROR("Failed to get name of header; hdr_buf %p, hdr_loc %p", state_->hdr_buf_, state_->hdr_loc_);
+    }
+    TSMLoc next_field_loc = TSMimeHdrFieldNext(state_->hdr_buf_, state_->hdr_loc_, field_loc);
+    TSHandleMLocRelease(state_->hdr_buf_, state_->hdr_loc_, field_loc);
+    field_loc = next_field_loc;
+  }
+  state_->name_values_map_.setInitialized();
+  LOG_DEBUG("Initialized headers map");
+  return true;
+}
+
+Headers::~Headers() {
+  delete state_;
+}
+
+Headers::size_type Headers::erase(const string &k) {
+  if (!checkAndInitHeaders()) {
+    return 0;
+  }
+  if ((state_->type_ == TYPE_REQUEST) && (CaseInsensitiveStringComparator()(k, "Cookie") == 0)) {
+    state_->request_cookies_.getValueRef().clear();
+    state_->request_cookies_.setInitialized(false);
+  } else if ((state_->type_ == TYPE_RESPONSE) && (CaseInsensitiveStringComparator()(k, "Set-Cookie") == 0)) {
+    state_->response_cookies_.getValueRef().clear();
+    state_->response_cookies_.setInitialized(false);
+  }
+  LOG_DEBUG("Erasing header [%s]", k.c_str());
+  return doBasicErase(k);
+}
+
+Headers::size_type Headers::doBasicErase(const string &k) {
+  if (!state_->detached_) {
+    TSMLoc field_loc = TSMimeHdrFieldFind(state_->hdr_buf_, state_->hdr_loc_, k.c_str(), k.length());
+    while (field_loc) {
+      TSMLoc next_field_loc = TSMimeHdrFieldNextDup(state_->hdr_buf_, state_->hdr_loc_, field_loc);
+      TSMimeHdrFieldDestroy(state_->hdr_buf_, state_->hdr_loc_, field_loc);
+      TSHandleMLocRelease(state_->hdr_buf_, state_->hdr_loc_, field_loc);
+      field_loc = next_field_loc;
+    }
+  }
+  return state_->name_values_map_.getValueRef().erase(k);
+}
+
+Headers::const_iterator Headers::set(const pair<string, list<string> > &pair) {
+  erase(pair.first);
+  return append(pair);
+}
+
+
+Headers::const_iterator Headers::set(const string &key, const list<string> &val) {
+  return set(make_pair(key,val));
+}
+
+Headers::const_iterator Headers::set(const string &key, const string &val) {
+  list<string> values;
+  values.push_back(val);
+  return set(make_pair(key,values));
+}
+
+Headers::const_iterator Headers::append(const pair<string, list<string> > &pair) {
+  if (!checkAndInitHeaders()) {
+    return state_->name_values_map_.getValueRef().end();
+  }
+  if ((state_->type_ == TYPE_REQUEST) && (CaseInsensitiveStringComparator()(pair.first, "Cookie") == 0)) {
+    state_->request_cookies_.getValueRef().clear();
+    state_->request_cookies_.setInitialized(false);
+  } else if ((state_->type_ == TYPE_RESPONSE) &&
+             (CaseInsensitiveStringComparator()(pair.first, "Set-Cookie") == 0)) {
+    state_->response_cookies_.getValueRef().clear();
+    state_->response_cookies_.setInitialized(false);
+  }
+  return doBasicAppend(pair);
+}
+
+Headers::const_iterator Headers::doBasicAppend(const pair<string, list<string> > &pair) {
+  const string &header_name = pair.first; // handy references
+  const list<string> &new_values = pair.second;
+
+  std::pair<NameValuesMap::iterator, bool> insert_result;
+  if (state_->detached_) {
+    insert_result = state_->name_values_map_.getValueRef().insert(make_pair(header_name, EMPTY_VALUE_LIST));
+    list<string> &value_list = insert_result.first->second; // existing or newly inserted
+    for (list<string>::const_iterator iter = new_values.begin(), end = new_values.end(); iter != end; ++iter) {
+      value_list.push_back(*iter);
+      LOG_DEBUG("Appended value [%s] to header [%s]", iter->c_str(), header_name.c_str());
+    }
+  } else {
+    TSMLoc field_loc =
+      TSMimeHdrFieldFind(state_->hdr_buf_, state_->hdr_loc_, header_name.c_str(), header_name.length());
+    
+    if (!field_loc) {
+      TSMimeHdrFieldCreate(state_->hdr_buf_, state_->hdr_loc_, &field_loc);
+      TSMimeHdrFieldNameSet(state_->hdr_buf_, state_->hdr_loc_, field_loc, header_name.c_str(), header_name.length());
+      TSMimeHdrFieldAppend(state_->hdr_buf_, state_->hdr_loc_, field_loc);
+      LOG_DEBUG("Created new structure for header [%s]", header_name.c_str());
+    }
+    for(list<string>::const_iterator ii = new_values.begin(); ii != new_values.end(); ++ii) {
+      TSMimeHdrFieldValueStringInsert(state_->hdr_buf_, state_->hdr_loc_, field_loc, APPEND_INDEX, (*ii).c_str(),
+                                      (*ii).length());
+    }
+
+    insert_result = state_->name_values_map_.getValueRef().insert(make_pair(header_name, EMPTY_VALUE_LIST));
+    list<string> &value_list = insert_result.first->second; // existing or newly inserted
+
+    //
+    // Now because TSMimeHdrFieldValueStringInsert will (possibly) parse each value for commas, that is,
+    // if you insert a list of three elements "Foo", "Bar,Baz", "Blah", this would become
+    // four elements in the marshal buffer and we need to update our own map to reflect that.
+    //
+    // Rather than inserting the list<strings> directly into our map, we will actually rebuild it using the
+    // Traffic Server HDR Marshal Buffer so we're 100% consistent with the internal representation.
+    //
+    if (!insert_result.second) {
+      value_list.clear();
+    }
+    extractHeaderFieldValues(state_->hdr_buf_, state_->hdr_loc_, field_loc, header_name, value_list);
+    TSHandleMLocRelease(state_->hdr_buf_, state_->hdr_loc_, field_loc);
+    LOG_DEBUG("Header [%s] has value(s) [%s]", header_name.c_str(), getJoinedValues(value_list).c_str());
+  }
+  return insert_result.first;
+}
+
+string Headers::getJoinedValues(const string &key, char value_delimiter) {
+  checkAndInitHeaders();
+
+  string ret;
+  Headers::NameValuesMap::iterator key_iter = state_->name_values_map_.getValueRef().find(key);
+  if (key_iter == state_->name_values_map_.getValueRef().end()) {
+    LOG_DEBUG("Header [%s] not present", key.c_str());
+    return ret;
+  }
+  return getJoinedValues(key_iter->second);
+}
+
+string Headers::getJoinedValues(const list<string> &values, char delimiter) {
+  string ret;
+  ret.reserve(128);
+  for (list<string>::const_iterator vals_iter = values.begin(), vals_end = values.end(); vals_iter != vals_end;
+       ++vals_iter) {
+    if (!ret.empty()) {
+      ret += delimiter;
+    }
+    ret.append(*vals_iter);
+  }
+  return ret;
+}
+
+Headers::const_iterator Headers::append(const string &key, const list<string> &val) {
+  return append(make_pair(key,val));
+}
+
+Headers::const_iterator Headers::append(const string &key, const string &val) {
+  list<string> values;
+  values.push_back(val);
+  return append(make_pair(key,values));
+}
+
+Headers::const_iterator Headers::begin() const {
+  checkAndInitHeaders();
+  return state_->name_values_map_.getValueRef().begin();
+}
+
+Headers::const_iterator Headers::end() const {
+  return state_->name_values_map_.getValueRef().end();
+}
+
+Headers::const_reverse_iterator Headers::rbegin() const {
+  checkAndInitHeaders();
+  return state_->name_values_map_.getValueRef().rbegin();
+}
+
+Headers::const_reverse_iterator Headers::rend() const {
+  return state_->name_values_map_.getValueRef().rend();
+}
+
+Headers::const_iterator Headers::find(const string &k) const {
+  checkAndInitHeaders();
+  return state_->name_values_map_.getValueRef().find(k);
+}
+
+Headers::size_type Headers::count(const string &key) const {
+  checkAndInitHeaders();
+  return state_->name_values_map_.getValueRef().count(key);
+}
+
+bool Headers::empty() const {
+  checkAndInitHeaders();
+  return state_->name_values_map_.getValueRef().empty();
+}
+
+Headers::size_type Headers::max_size() const {
+  return state_->name_values_map_.getValueRef().max_size();
+}
+
+Headers::size_type Headers::size() const {
+  checkAndInitHeaders();
+  return state_->name_values_map_.getValueRef().size();
+}
+
+namespace {
+
+const list<string> EMPTY_REQUEST_COOKIE_VALUE_LIST;
+
+void stripEnclosingWhitespace(string &token) {
+  size_t token_size = token.size(), i;
+  for (i = 0; (i < token_size) && std::isspace(token[i]); ++i);
+  if (i) {
+    token.erase(0, i);
+    token_size -= i;
+  }
+  // can't use >= 0 here as we size_t will never go negative
+  for (i = token_size; (i > 0) && std::isspace(token[i - 1]); --i);
+  if (token_size - i) {
+    token.resize(i);
+  }
+}
+
+void addCookieToMap(Headers::RequestCookieMap &cookie_map, const string &name, const string &value) {
+  Headers::RequestCookieMap::value_type element_to_insert(name, EMPTY_REQUEST_COOKIE_VALUE_LIST);
+  list<string> &value_list = (cookie_map.insert(element_to_insert)).first->second;
+  if (!value_list.empty()) { // log duplicates
+    LOG_DEBUG("Cookie [%s] has multiple instances", name.c_str());
+  }
+  value_list.push_back(value);
+  LOG_DEBUG("Added cookie [%s] with value [%s]", name.c_str(), value.c_str());
+}
+
+}
+
+const Headers::RequestCookieMap &Headers::getRequestCookies() const {
+  if (state_->type_ != Headers::TYPE_REQUEST) {
+    LOG_ERROR("Object is not of type request. Returning empty map");
+    return state_->request_cookies_;
+  }
+  if (state_->request_cookies_.isInitialized() || !checkAndInitHeaders()) {
+    return state_->request_cookies_;
+  }
+  state_->request_cookies_.setInitialized();
+  const_iterator iter = find("Cookie");
+  if (iter != end()) {
+    const list<string> &cookie_kvs = iter->second; // cookie key-value pairs
+    string name, value;
+    for (list<string>::const_iterator cookie_kv_iter = cookie_kvs.begin(), cookie_kv_end = cookie_kvs.end();
+         cookie_kv_iter != cookie_kv_end; ++cookie_kv_iter) {
+      const string &cookie_kv = *cookie_kv_iter;
+      size_t cookie_kv_size = cookie_kv.size();
+      size_t start_pos, end_pos, length;
+        
+      for (size_t i = 0; i < cookie_kv_size; ) {
+        start_pos = i;
+        for (end_pos = start_pos;
+             (end_pos < cookie_kv_size) && (cookie_kv[end_pos] != '=') && (cookie_kv[end_pos] != ';'); ++end_pos);
+        if ((end_pos == cookie_kv_size) || (cookie_kv[end_pos] == ';')) {
+          LOG_DEBUG("Unexpected end in cookie key value string [%s]", cookie_kv.c_str());
+          return state_->request_cookies_;
+        }
+        length = end_pos - start_pos;
+        name.assign(cookie_kv, start_pos, length);
+        stripEnclosingWhitespace(name);
+        if (name.empty()) {
+          LOG_DEBUG("Empty cookie name in key value string [%s]", cookie_kv.c_str());
+          return state_->request_cookies_;
+        }
+        start_pos = ++end_pos; // value should start here
+        if (start_pos == cookie_kv_size)  {
+          LOG_DEBUG("Cookie [%s] has no value in key value string [%s]", name.c_str(), cookie_kv.c_str());
+          return state_->request_cookies_;
+        }
+        bool within_quotes = false;
+        for (end_pos = start_pos; end_pos < cookie_kv_size; ++end_pos) {
+          if (cookie_kv[end_pos] == '"') {
+            within_quotes = !within_quotes;
+          } else if ((cookie_kv[end_pos] == ';') && !within_quotes) {
+            break;
+          }
+        }
+        length = end_pos - start_pos;
+        value.assign(cookie_kv, start_pos, length);
+        stripEnclosingWhitespace(value);
+        addCookieToMap(state_->request_cookies_, name ,value);
+        i = ++end_pos; // next name should start here
+      }
+    }
+  }
+  return state_->request_cookies_;
+}
+
+const list<Headers::ResponseCookie> &Headers::getResponseCookies() const {
+  if (state_->type_ != Headers::TYPE_RESPONSE) {
+    LOG_ERROR("Object is not of type response. Returning empty list");
+    return state_->response_cookies_;
+  }
+  if (state_->response_cookies_.isInitialized() || !checkAndInitHeaders()) {
+    return state_->response_cookies_;
+  }
+  state_->response_cookies_.setInitialized();
+  // @TODO Parse Set-Cookie headers here
+  return state_->response_cookies_;
+}
+
+bool Headers::addCookie(const string &name, const string &value) {
+  if (state_->type_ != Headers::TYPE_REQUEST) {
+    LOG_ERROR("Cannot add request cookie to response headers");
+    return false;
+  }
+  if (!checkAndInitHeaders()) {
+    return false;
+  }
+  addCookieToMap(state_->request_cookies_, name, value);
+  updateRequestCookieHeaderFromMap();
+  return true;
+}
+
+bool Headers::addCookie(const ResponseCookie &response_cookie) {
+  if (state_->type_ != Headers::TYPE_RESPONSE) {
+    LOG_ERROR("Cannot add response cookie to object not of type response");
+    return false;
+  }
+  if (!checkAndInitHeaders()) {
+    false;
+  }
+  // @TODO Do logic here
+  return true;
+}
+  
+bool Headers::setCookie(const string &name, const string &value) {
+  if (state_->type_ != Headers::TYPE_REQUEST) {
+    LOG_ERROR("Cannot set request cookie to response headers");
+    return false;
+  }
+  if (!checkAndInitHeaders()) {
+    return false;
+  }
+  getRequestCookies();
+  state_->request_cookies_.getValueRef().erase(name);
+  return addCookie(name, value);
+}
+
+bool Headers::setCookie(const ResponseCookie &response_cookie) {
+  if (state_->type_ != Headers::TYPE_RESPONSE) {
+    LOG_ERROR("Cannot set response cookie to request headers");
+    return false;
+  }
+  if (!checkAndInitHeaders()) {
+    return false;
+  }
+  // @TODO Do logic here
+  return true;
+}
+
+bool Headers::deleteCookie(const string &name) {
+  if (!checkAndInitHeaders()) {
+    return false;
+  }
+  if (state_->type_ == TYPE_REQUEST) {
+    getRequestCookies();
+    RequestCookieMap::iterator iter = state_->request_cookies_.getValueRef().find(name);
+    if (iter == state_->request_cookies_.getValueRef().end()) {
+      LOG_DEBUG("Cookie [%s] doesn't exist", name.c_str());
+      return true;
+    }
+    state_->request_cookies_.getValueRef().erase(iter);
+    updateRequestCookieHeaderFromMap();
+    return true;
+  }
+  getResponseCookies();
+  // @TODO Do logic here
+  return true;
+}
+
+void Headers::updateRequestCookieHeaderFromMap() {
+  string cookie_header;
+  for (RequestCookieMap::iterator cookie_iter = state_->request_cookies_.getValueRef().begin(), 
+         cookie_end = state_->request_cookies_.getValueRef().end(); cookie_iter != cookie_end; ++cookie_iter) {
+    for (list<string>::iterator value_iter = cookie_iter->second.begin(), value_end = cookie_iter->second.end();
+         value_iter != value_end; ++value_iter) {
+      cookie_header += cookie_iter->first;
+      cookie_header += '=';
+      cookie_header += *value_iter;
+      cookie_header += "; ";
+    }
+  }
+  if (!cookie_header.empty()) {
+    cookie_header.erase(cookie_header.size() - 2, 2); // erase trailing '; '
+  }
+
+  // we could have called set(), but set() invalidates the cookie map
+  // indirectly by calling append(). But our map is up to date. So we
+  // do put the set() logic here explicitly.
+  doBasicErase("Cookie");
+  list<string> values;
+  values.push_back(cookie_header);
+  doBasicAppend(pair<string, list<string> >("Cookie", values));
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/HttpMethod.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/HttpMethod.cc b/lib/atscppapi/src/HttpMethod.cc
new file mode 100644
index 0000000..c8a6051
--- /dev/null
+++ b/lib/atscppapi/src/HttpMethod.cc
@@ -0,0 +1,36 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file HttpMethod.cc
+ */
+
+#include "atscppapi/HttpMethod.h"
+
+const std::string atscppapi::HTTP_METHOD_STRINGS[] = { std::string("UNKNOWN"),
+                                                       std::string("GET"),
+                                                       std::string("POST"),
+                                                       std::string("HEAD"),
+                                                       std::string("CONNECT"),
+                                                       std::string("DELETE"),
+                                                       std::string("ICP_QUERY"),
+                                                       std::string("OPTIONS"),
+                                                       std::string("PURGE"),
+                                                       std::string("PUT"),
+                                                       std::string("TRACE") };
+


Mime
View raw message