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 2013/10/27 22:36:40 UTC

[15/50] [abbrv] initial atscppapi commit

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/HttpVersion.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/HttpVersion.cc b/lib/atscppapi/src/HttpVersion.cc
new file mode 100644
index 0000000..7a1e3d5
--- /dev/null
+++ b/lib/atscppapi/src/HttpVersion.cc
@@ -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.
+ */
+
+/**
+ * @file HttpVersion.cc
+ */
+
+#include "atscppapi/HttpVersion.h"
+
+const std::string atscppapi::HTTP_VERSION_STRINGS[] = { std::string("UNKNOWN"),
+                                                        std::string("HTTP/0.9"),
+                                                        std::string("HTTP/1.0"),
+                                                        std::string("HTTP/1.1") };
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/InitializableValue.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/InitializableValue.cc b/lib/atscppapi/src/InitializableValue.cc
new file mode 100644
index 0000000..a0d87af
--- /dev/null
+++ b/lib/atscppapi/src/InitializableValue.cc
@@ -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.
+ */
+
+/**
+ * @file InitializableValue.cc
+ */
+
+#include "InitializableValue.h"
+
+#ifdef DISABLE_TRANSACTION_DATA_CACHING
+bool atscppapi::transaction_data_caching_enabled = false;
+#else
+bool atscppapi::transaction_data_caching_enabled = true;
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Logger.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Logger.cc b/lib/atscppapi/src/Logger.cc
new file mode 100644
index 0000000..a7c18d5
--- /dev/null
+++ b/lib/atscppapi/src/Logger.cc
@@ -0,0 +1,213 @@
+/**
+  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 Logger.cc
+ * @warning log rolling doesn't work correctly in 3.2.x see:
+ *   https://issues.apache.org/jira/browse/TS-1813
+ *   Apply the patch in TS-1813 to correct log rolling in 3.2.x
+ */
+
+#include "atscppapi/Logger.h"
+#include <cstdarg>
+#include <vector>
+#include <cstdio>
+#include <string>
+#include <cstring>
+#include <ts/ts.h>
+#include "atscppapi/noncopyable.h"
+#include "logging_internal.h"
+
+using std::vector;
+using std::string;
+
+using atscppapi::Logger;
+
+/**
+ * @private
+ */
+struct atscppapi::LoggerState: noncopyable  {
+  std::string filename_;
+  bool add_timestamp_;
+  bool rename_file_;
+  volatile Logger::LogLevel level_;
+  bool rolling_enabled_;
+  int rolling_interval_seconds_;
+  TSTextLogObject text_log_obj_;
+  bool initialized_;
+
+  LoggerState() : add_timestamp_(false), rename_file_(false), level_(Logger::LOG_LEVEL_NO_LOG), rolling_enabled_(false),
+                  rolling_interval_seconds_(-1), text_log_obj_(NULL), initialized_(false) { };
+  ~LoggerState() { };
+};
+
+namespace {
+// Since the TSTextLog API doesn't support override the log file sizes (I will add this to TS api at some point)
+// we will use the roll size specified by default in records.config.
+const int ROLL_ON_TIME = 1; // See RollingEnabledValues in LogConfig.h
+}
+
+
+/*
+ * These have default values specified for add_timestamp and rename_file in Logger.h
+ */
+Logger::Logger() {
+  state_ = new LoggerState();
+}
+
+Logger::~Logger() {
+  if (state_->initialized_ && state_->text_log_obj_) {
+    TSTextLogObjectDestroy(state_->text_log_obj_);
+  }
+
+  delete state_;
+}
+
+/*
+ * These have default values specified for rolling_enabled and rolling_interval_seconds in Logger.h
+ */
+bool Logger::init(const string &file, bool add_timestamp, bool rename_file, LogLevel level, bool rolling_enabled, int rolling_interval_seconds) {
+  if (state_->initialized_) {
+    LOG_ERROR("Attempt to reinitialize a logger named '%s' that's already been initialized to '%s'.", file.c_str(), state_->filename_.c_str());
+    return false;
+  }
+  state_->filename_ = file;
+  state_->add_timestamp_ = add_timestamp;
+  state_->rename_file_ = rename_file;
+  state_->level_ = level;
+  state_->rolling_enabled_ = rolling_enabled;
+  state_->rolling_interval_seconds_ = rolling_interval_seconds;
+  state_->initialized_ = true; // set this to true always - we are not providing re-init() after a failed init()
+
+  int mode = 0;
+  if (state_->add_timestamp_) {
+    mode |= TS_LOG_MODE_ADD_TIMESTAMP;
+  }
+
+  if (!state_->rename_file_) {
+    mode |= TS_LOG_MODE_DO_NOT_RENAME;
+  }
+
+  TSReturnCode result = TSTextLogObjectCreate(state_->filename_.c_str(), mode, &state_->text_log_obj_);
+
+  if (result == TS_SUCCESS) {
+    TSTextLogObjectRollingEnabledSet(state_->text_log_obj_, state_->rolling_enabled_ ? ROLL_ON_TIME : 0);
+    TSTextLogObjectRollingIntervalSecSet(state_->text_log_obj_, state_->rolling_interval_seconds_);
+    LOG_DEBUG("Initialized log [%s]", state_->filename_.c_str());
+  } else {
+    state_->level_ = LOG_LEVEL_NO_LOG;
+    LOG_ERROR("Failed to initialize for log [%s]", state_->filename_.c_str());
+  }
+
+  return result == TS_SUCCESS;
+}
+
+void Logger::setLogLevel(Logger::LogLevel level) {
+  if (state_->initialized_) {
+    state_->level_ = level;
+    LOG_DEBUG("Set log level to %d for log [%s]", level, state_->filename_.c_str());
+  }
+}
+
+Logger::LogLevel Logger::getLogLevel() const {
+  if (!state_->initialized_) {
+    LOG_ERROR("Not initialized");
+  }
+  return state_->level_;
+}
+
+void Logger::setRollingIntervalSeconds(int seconds) {
+  if (state_->initialized_) {
+    state_->rolling_interval_seconds_ = seconds;
+    TSTextLogObjectRollingIntervalSecSet(state_->text_log_obj_, seconds);
+    LOG_DEBUG("Set rolling interval for log [%s] to %d seconds", state_->filename_.c_str(), seconds);
+  } else {
+    LOG_ERROR("Not initialized!");
+  }
+}
+
+int Logger::getRollingIntervalSeconds() const {
+  if (!state_->initialized_) {
+    LOG_ERROR("Not initialized");
+  }
+  return state_->rolling_interval_seconds_;
+}
+
+void Logger::setRollingEnabled(bool enabled) {
+  if (state_->initialized_) {
+    state_->rolling_enabled_ = enabled;
+    TSTextLogObjectRollingEnabledSet(state_->text_log_obj_, enabled ? ROLL_ON_TIME : 0);
+    LOG_DEBUG("Rolling for log [%s] is now %s", state_->filename_.c_str(), (enabled ? "true" : "false"));
+  } else {
+    LOG_ERROR("Not initialized!");
+  }
+}
+
+bool Logger::isRollingEnabled() const {
+  if (!state_->initialized_) {
+    LOG_ERROR("Not initialized!");
+  }
+  return state_->rolling_enabled_;
+}
+
+void Logger::flush() {
+  if (state_->initialized_) {
+    TSTextLogObjectFlush(state_->text_log_obj_);
+  } else {
+    LOG_ERROR("Not initialized!");
+  }
+}
+
+namespace {
+const int DEFAULT_BUFFER_SIZE_FOR_VARARGS = 8*1024;
+
+#define TS_TEXT_LOG_OBJECT_WRITE(level) \
+    char buffer[DEFAULT_BUFFER_SIZE_FOR_VARARGS]; \
+    int n; \
+    va_list ap; \
+    while (true) { \
+     va_start(ap, fmt); \
+     n = vsnprintf (&buffer[0], sizeof(buffer), fmt, ap); \
+     va_end(ap); \
+     if (n > -1 && n < sizeof(buffer)) { \
+       LOG_DEBUG("logging a " level " to '%s' with length %d", state_->filename_.c_str(), n); \
+       TSTextLogObjectWrite(state_->text_log_obj_, const_cast<char*>("[" level "] %s"), buffer); \
+     } else { \
+       LOG_ERROR("Unable to log " level " message to '%s' due to size exceeding %d bytes.", state_->filename_.c_str(), sizeof(buffer)); \
+     } \
+     return; \
+    }
+
+} /* end anonymous namespace */
+
+void Logger::logDebug(const char *fmt, ...) {
+  if (state_->level_ <= LOG_LEVEL_DEBUG) {
+    TS_TEXT_LOG_OBJECT_WRITE("DEBUG");
+  }
+}
+
+void Logger::logInfo(const char *fmt, ...) {
+  if (state_->level_ <= LOG_LEVEL_INFO) {
+    TS_TEXT_LOG_OBJECT_WRITE("INFO");
+  }
+}
+
+void Logger::logError(const char *fmt, ...) {
+  if (state_->level_ <= LOG_LEVEL_ERROR) {
+    TS_TEXT_LOG_OBJECT_WRITE("ERROR");
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Makefile.am b/lib/atscppapi/src/Makefile.am
new file mode 100644
index 0000000..52085d0
--- /dev/null
+++ b/lib/atscppapi/src/Makefile.am
@@ -0,0 +1,77 @@
+#
+#  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_CXXFLAGS = -Iinclude
+
+# build the library
+lib_LTLIBRARIES = libatscppapi.la
+libatscppapi_la_LDFLAGS=-lz -lpthread -lrt -version-info @TS_LIBTOOL_VERSION@
+
+libatscppapi_la_SOURCES = GlobalPlugin.cc \
+			  Plugin.cc \
+			  utils.cc \
+			  utils_internal.cc \
+			  Transaction.cc \
+			  TransactionPlugin.cc \
+			  Headers.cc \
+			  Request.cc \
+			  CaseInsensitiveStringComparator.cc \
+			  ClientRequest.cc \
+			  Url.cc \
+			  HttpVersion.cc \
+			  HttpMethod.cc \
+			  InitializableValue.cc \
+			  Response.cc \
+			  TransformationPlugin.cc \
+			  Logger.cc \
+			  Stat.cc \
+			  AsyncHttpFetch.cc \
+			  RemapPlugin.cc \
+			  GzipDeflateTransformation.cc \
+			  GzipInflateTransformation.cc \
+			  AsyncTimer.cc
+
+library_includedir=$(includedir)/atscppapi
+base_include_folder = include/atscppapi/
+
+library_include_HEADERS = $(base_include_folder)/GlobalPlugin.h \
+			  $(base_include_folder)/Plugin.h \
+			  $(base_include_folder)/PluginInit.h \
+			  $(base_include_folder)/Transaction.h \
+			  $(base_include_folder)/TransactionPlugin.h \
+			  $(base_include_folder)/HttpMethod.h \
+			  $(base_include_folder)/HttpStatus.h \
+			  $(base_include_folder)/HttpVersion.h \
+			  $(base_include_folder)/Headers.h \
+			  $(base_include_folder)/Request.h \
+			  $(base_include_folder)/CaseInsensitiveStringComparator.h \
+			  $(base_include_folder)/ClientRequest.h \
+	 		  $(base_include_folder)/Url.h \
+		 	  $(base_include_folder)/Response.h \
+			  $(base_include_folder)/utils.h \
+			  $(base_include_folder)/TransformationPlugin.h \
+			  $(base_include_folder)/Logger.h \
+			  $(base_include_folder)/noncopyable.h \
+			  $(base_include_folder)/Stat.h \
+			  $(base_include_folder)/Mutex.h \
+			  $(base_include_folder)/RemapPlugin.h \
+			  $(base_include_folder)/shared_ptr.h \
+			  $(base_include_folder)/Async.h \
+			  $(base_include_folder)/AsyncHttpFetch.h \
+			  $(base_include_folder)/GzipDeflateTransformation.h \
+			  $(base_include_folder)/GzipInflateTransformation.h \
+			  $(base_include_folder)/AsyncTimer.h
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Plugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Plugin.cc b/lib/atscppapi/src/Plugin.cc
new file mode 100644
index 0000000..dd8d603
--- /dev/null
+++ b/lib/atscppapi/src/Plugin.cc
@@ -0,0 +1,31 @@
+/**
+  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 Plugin.cc
+ */
+#include "atscppapi/Plugin.h"
+
+const std::string atscppapi::HOOK_TYPE_STRINGS[] = { std::string("HOOK_READ_REQUEST_HEADERS_PRE_REMAP"),
+                                                     std::string("HOOK_READ_REQUEST_HEADERS_POST_REMAP"),
+                                                     std::string("HOOK_SEND_REQUEST_HEADERS"),
+                                                     std::string("HOOK_READ_RESPONSE_HEADERS"),
+                                                     std::string("HOOK_SEND_RESPONSE_HEADERS"),
+                                                     std::string("HOOK_OS_DNS")
+                                                      };
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/RemapPlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/RemapPlugin.cc b/lib/atscppapi/src/RemapPlugin.cc
new file mode 100644
index 0000000..9ff07b6
--- /dev/null
+++ b/lib/atscppapi/src/RemapPlugin.cc
@@ -0,0 +1,66 @@
+/**
+  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 RemapPlugin.cc
+ */
+
+#include "atscppapi/RemapPlugin.h"
+#include "logging_internal.h"
+#include "utils_internal.h"
+#include <assert.h>
+#include <ts/remap.h>
+
+using namespace atscppapi;
+
+TSRemapStatus TSRemapDoRemap(void* ih, TSHttpTxn rh, TSRemapRequestInfo* rri) {
+  RemapPlugin *remap_plugin = static_cast<RemapPlugin *>(ih);
+  Url map_from_url(rri->requestBufp, rri->mapFromUrl), map_to_url(rri->requestBufp, rri->mapToUrl);
+  Transaction &transaction = utils::internal::getTransaction(rh);
+  bool redirect = false;
+  RemapPlugin::Result result = remap_plugin->doRemap(map_from_url, map_to_url, transaction, redirect);
+  rri->redirect = redirect ? 1 : 0;
+  switch (result) {
+  case RemapPlugin::RESULT_ERROR:
+    return TSREMAP_ERROR;
+  case RemapPlugin::RESULT_NO_REMAP:
+    return TSREMAP_NO_REMAP;
+  case RemapPlugin::RESULT_DID_REMAP:
+    return TSREMAP_DID_REMAP;
+  case RemapPlugin::RESULT_NO_REMAP_STOP:
+    return TSREMAP_NO_REMAP_STOP;
+  case RemapPlugin::RESULT_DID_REMAP_STOP:
+    return TSREMAP_DID_REMAP_STOP;
+  default:
+    assert(!"Unhandled result");
+    return TSREMAP_ERROR;
+  }
+}
+
+void TSRemapDeleteInstance(void *ih) {
+  RemapPlugin *remap_plugin = static_cast<RemapPlugin *>(ih);
+  delete remap_plugin;
+}
+
+TSReturnCode TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size) {
+  return TS_SUCCESS;
+}
+
+RemapPlugin::RemapPlugin(void **instance_handle) {
+  *instance_handle = static_cast<void *>(this);
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Request.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Request.cc b/lib/atscppapi/src/Request.cc
new file mode 100644
index 0000000..b7cb4c2
--- /dev/null
+++ b/lib/atscppapi/src/Request.cc
@@ -0,0 +1,167 @@
+/**
+  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 Request.cc
+ */
+
+#include "atscppapi/Request.h"
+#include <ts/ts.h>
+#include "atscppapi/noncopyable.h"
+#include "InitializableValue.h"
+#include "utils_internal.h"
+#include "logging_internal.h"
+
+using namespace atscppapi;
+using std::string;
+
+/**
+ * @private
+ */
+struct atscppapi::RequestState: noncopyable  {
+  TSMBuffer hdr_buf_;
+  TSMLoc hdr_loc_;
+  TSMLoc url_loc_;
+  Url url_;
+  Headers headers_;
+  InitializableValue<HttpMethod> method_;
+  InitializableValue<HttpVersion> version_;
+  bool destroy_buf_;
+  RequestState() : hdr_buf_(NULL), hdr_loc_(NULL), url_loc_(NULL), method_(HTTP_METHOD_UNKNOWN, false),
+                   version_(HTTP_VERSION_UNKNOWN, false), destroy_buf_(false) { }
+};
+
+Request::Request() {
+  state_ = new RequestState();
+}
+
+Request::Request(void *hdr_buf, void *hdr_loc) {
+  state_ = new RequestState();
+  init(hdr_buf, hdr_loc);
+  LOG_DEBUG("Initialized request object %p with hdr_buf=%p and hdr_loc=%p", this, hdr_buf, hdr_loc);
+}
+
+Request::Request(const string &url_str, HttpMethod method, HttpVersion version) {
+  state_ = new RequestState();
+  state_->method_.setValue(method);
+  state_->version_.setValue(version);
+  state_->destroy_buf_ = true;
+  state_->hdr_buf_ = TSMBufferCreate();
+  state_->headers_.initDetached();
+  if (TSUrlCreate(state_->hdr_buf_, &state_->url_loc_) == TS_SUCCESS) {
+    const char *url_str_start = url_str.c_str();
+    const char *url_str_end = url_str_start + url_str.size();
+    if (TSUrlParse(state_->hdr_buf_, state_->url_loc_, &url_str_start, url_str_end) != TS_PARSE_DONE) {
+      LOG_ERROR("[%s] does not represent a valid url", url_str.c_str());
+    }
+    else {
+      state_->url_.init(state_->hdr_buf_, state_->url_loc_);
+    }
+  }
+  else {
+    state_->url_loc_ = NULL;
+    LOG_ERROR("Could not create URL field; hdr_buf %p", state_->hdr_buf_); 
+  }
+}
+
+void Request::init(void *hdr_buf, void *hdr_loc) {
+  if (state_->hdr_buf_ || state_->hdr_loc_) {
+    LOG_ERROR("Reinitialization; (hdr_buf, hdr_loc) current(%p, %p), attempted(%p, %p)", state_->hdr_buf_,
+              state_->hdr_loc_, hdr_buf, hdr_loc);
+    return;
+  }
+  state_->hdr_buf_ = static_cast<TSMBuffer>(hdr_buf);
+  state_->hdr_loc_ = static_cast<TSMLoc>(hdr_loc);
+  state_->headers_.init(state_->hdr_buf_, state_->hdr_loc_);
+  state_->url_loc_ = NULL;
+  TSHttpHdrUrlGet(state_->hdr_buf_, state_->hdr_loc_, &state_->url_loc_);
+  if (!state_->url_loc_) {
+    LOG_ERROR("TSHttpHdrUrlGet returned a null url loc, hdr_buf=%p, hdr_loc=%p", state_->hdr_buf_, state_->hdr_loc_);
+  }
+  else {
+    state_->url_.init(state_->hdr_buf_, state_->url_loc_);
+    LOG_DEBUG("Initialized url");
+  }
+}
+
+HttpMethod Request::getMethod() const {
+  if (!state_->method_.isInitialized() && state_->hdr_buf_ && state_->hdr_loc_) {
+    int method_len;
+    const char *method_str = TSHttpHdrMethodGet(state_->hdr_buf_, state_->hdr_loc_, &method_len);
+    if (method_str && method_len) {
+      if (method_str == TS_HTTP_METHOD_GET) {
+        state_->method_ = HTTP_METHOD_GET;
+      } else if (method_str == TS_HTTP_METHOD_POST) {
+        state_->method_ = HTTP_METHOD_POST;
+      } else if (method_str == TS_HTTP_METHOD_HEAD) {
+        state_->method_ = HTTP_METHOD_HEAD;
+      } else if (method_str == TS_HTTP_METHOD_CONNECT) {
+        state_->method_ = HTTP_METHOD_CONNECT;
+      } else if (method_str == TS_HTTP_METHOD_DELETE) {
+        state_->method_ = HTTP_METHOD_DELETE;
+      } else if (method_str == TS_HTTP_METHOD_ICP_QUERY) {
+        state_->method_ = HTTP_METHOD_ICP_QUERY;
+      } else if (method_str == TS_HTTP_METHOD_OPTIONS) {
+        state_->method_ = HTTP_METHOD_OPTIONS;
+      } else if (method_str == TS_HTTP_METHOD_PURGE) {
+        state_->method_ = HTTP_METHOD_PURGE;
+      } else if (method_str == TS_HTTP_METHOD_PUT) {
+        state_->method_ = HTTP_METHOD_PUT;
+      } else if (method_str == TS_HTTP_METHOD_TRACE) {
+        state_->method_ = HTTP_METHOD_TRACE;
+      }
+    } else {
+      LOG_ERROR("TSHttpHdrMethodGet returned null string or it was zero length, hdr_buf=%p, hdr_loc=%p, method str=%p, method_len=%d",
+          state_->hdr_buf_, state_->hdr_loc_, method_str, method_len);
+    }
+  }
+  return state_->method_;
+}
+
+Url &Request::getUrl() {
+  return state_->url_;
+}
+
+atscppapi::HttpVersion Request::getVersion() const {
+  if (!state_->version_.isInitialized() && state_->hdr_buf_ && state_->hdr_loc_) {
+    state_->version_ = utils::internal::getHttpVersion(state_->hdr_buf_, state_->hdr_loc_);
+    LOG_DEBUG("Initializing request version=%d [%s] on hdr_buf=%p, hdr_loc=%p",
+        state_->version_.getValue(), HTTP_VERSION_STRINGS[state_->version_.getValue()].c_str(), state_->hdr_buf_, state_->hdr_loc_);
+  }
+  return state_->version_;
+}
+
+atscppapi::Headers &Request::getHeaders() const {
+  return state_->headers_;
+}
+
+Request::~Request() {
+  if (state_->url_loc_) {
+    if (state_->destroy_buf_) {
+      // usually, hdr_loc is the parent of url_loc, but we created this url_loc "directly" in hdr_buf, 
+      // so we use null as parent loc in this case
+      TSMLoc null_parent_loc = NULL;
+      TSHandleMLocRelease(state_->hdr_buf_, null_parent_loc, state_->url_loc_);
+      TSMBufferDestroy(state_->hdr_buf_);
+    } else {
+      LOG_DEBUG("Destroying request object on hdr_buf=%p, hdr_loc=%p, url_loc=%p", state_->hdr_buf_,
+                state_->hdr_loc_, state_->url_loc_);
+      TSHandleMLocRelease(state_->hdr_buf_, state_->hdr_loc_, state_->url_loc_);
+    }
+  }
+  delete state_;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Response.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Response.cc b/lib/atscppapi/src/Response.cc
new file mode 100644
index 0000000..999aad6
--- /dev/null
+++ b/lib/atscppapi/src/Response.cc
@@ -0,0 +1,126 @@
+/**
+  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 Response.cc
+ */
+#include "atscppapi/Response.h"
+#include "InitializableValue.h"
+#include "atscppapi/noncopyable.h"
+#include "utils_internal.h"
+#include "logging_internal.h"
+
+using namespace atscppapi;
+using std::string;
+
+namespace atscppapi {
+
+/**
+ * @private
+ */
+struct ResponseState: noncopyable {
+  TSMBuffer hdr_buf_;
+  TSMLoc hdr_loc_;
+  InitializableValue<HttpVersion> version_;
+  InitializableValue<HttpStatus> status_code_;
+  InitializableValue<string> reason_phrase_;
+  Headers headers_;
+  ResponseState() : hdr_buf_(NULL), hdr_loc_(NULL), version_(HTTP_VERSION_UNKNOWN, false), status_code_(HTTP_STATUS_UNKNOWN, false) { }
+};
+
+}
+
+Response::Response() {
+  state_ = new ResponseState();
+  state_->headers_.setType(Headers::TYPE_RESPONSE);
+}
+
+void Response::init(void *hdr_buf, void *hdr_loc) {
+  state_->hdr_buf_ = static_cast<TSMBuffer>(hdr_buf);
+  state_->hdr_loc_ = static_cast<TSMLoc>(hdr_loc);
+  state_->headers_.init(state_->hdr_buf_, state_->hdr_loc_);
+  LOG_DEBUG("Initializing response %p with hdr_buf=%p and hdr_loc=%p", this, state_->hdr_buf_, state_->hdr_loc_);
+}
+
+HttpVersion Response::getVersion() const {
+  if (state_->version_.isInitialized()) {
+    return state_->version_;
+  }
+  if (state_->hdr_buf_ && state_->hdr_loc_) {
+    state_->version_ = utils::internal::getHttpVersion(state_->hdr_buf_, state_->hdr_loc_);
+    LOG_DEBUG("Initializing response version to %d [%s] with hdr_buf=%p and hdr_loc=%p",
+        state_->version_.getValue(), HTTP_VERSION_STRINGS[state_->version_.getValue()].c_str(), state_->hdr_buf_, state_->hdr_loc_);
+    return state_->version_;
+  }
+  return HTTP_VERSION_UNKNOWN;
+}
+
+HttpStatus Response::getStatusCode() const {
+  if (state_->status_code_.isInitialized()) {
+    return state_->status_code_;
+  }
+  if (state_->hdr_buf_ && state_->hdr_loc_) {
+    state_->status_code_ = static_cast<HttpStatus>(TSHttpHdrStatusGet(state_->hdr_buf_, state_->hdr_loc_));
+    LOG_DEBUG("Initializing response status code to %d with hdr_buf=%p and hdr_loc=%p",
+        state_->status_code_.getValue(), state_->hdr_buf_, state_->hdr_loc_);
+    return state_->status_code_;
+  }
+
+  return HTTP_STATUS_UNKNOWN;
+}
+
+void Response::setStatusCode(HttpStatus code) {
+  if (state_->hdr_buf_ && state_->hdr_loc_) {
+    TSHttpHdrStatusSet(state_->hdr_buf_, state_->hdr_loc_, static_cast<TSHttpStatus>(code));
+    state_->status_code_ = code;
+    LOG_DEBUG("Changing response status code to %d with hdr_buf=%p and hdr_loc=%p",
+        state_->status_code_.getValue(), state_->hdr_buf_, state_->hdr_loc_);
+  }
+}
+
+const string &Response::getReasonPhrase() const {
+  if (!state_->reason_phrase_.isInitialized() && state_->hdr_buf_ && state_->hdr_loc_) {
+    int length;
+    const char *str = TSHttpHdrReasonGet(state_->hdr_buf_, state_->hdr_loc_, &length);
+    if (str && length) {
+      state_->reason_phrase_.getValueRef().assign(str, length);
+      LOG_DEBUG("Initializing response reason phrase to '%s' with hdr_buf=%p and hdr_loc=%p",
+          state_->reason_phrase_.getValueRef().c_str(), state_->hdr_buf_, state_->hdr_loc_);
+    } else {
+      LOG_ERROR("TSHttpHdrReasonGet returned null string or zero length. str=%p, length=%d, hdr_buf=%p, hdr_loc=%p",
+          str, length, state_->hdr_buf_, state_->hdr_loc_);
+    }
+  }
+  return state_->reason_phrase_; // if not initialized, we will just return an empty string
+}
+
+void Response::setReasonPhrase(const string &phrase) {
+  if (state_->hdr_buf_ && state_->hdr_loc_) {
+    TSHttpHdrReasonSet(state_->hdr_buf_, state_->hdr_loc_, phrase.c_str(), phrase.length());
+    state_->reason_phrase_ = phrase;
+    LOG_DEBUG("Changing response reason phrase to '%s' with hdr_buf=%p and hdr_loc=%p",
+        phrase.c_str(), state_->hdr_buf_, state_->hdr_loc_);
+  }
+}
+
+Headers &Response::getHeaders() const {
+  return state_->headers_;  // if not initialized, we will just return an empty object
+}
+
+Response::~Response() {
+  delete state_;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Stat.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Stat.cc b/lib/atscppapi/src/Stat.cc
new file mode 100644
index 0000000..b10082b
--- /dev/null
+++ b/lib/atscppapi/src/Stat.cc
@@ -0,0 +1,96 @@
+/**
+  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 Stat.cc
+ */
+
+#include "atscppapi/Stat.h"
+#include <string>
+#include <stdint.h>
+#include <ts/ts.h>
+#include "logging_internal.h"
+
+using namespace atscppapi;
+using std::string;
+
+Stat::Stat() : stat_id_(TS_ERROR) {
+// ATS Guarantees that stat ids will always be > 0. So we can use stat_id_ > 0 to
+// verify that this stat has been properly initialized.
+}
+
+Stat::~Stat() {
+// we really dont have any cleanup since ATS doesn't expose a method to destroy stats
+}
+
+bool Stat::init(string name, Stat::SyncType type, bool persistent) {
+  // TS_RECORDDATATYPE_INT is the only type currently supported
+  // so that's why this api doesn't expose other types, TSStatSync is equivalent to StatSyncType
+  stat_id_ = TSStatCreate(name.c_str(), TS_RECORDDATATYPE_INT, persistent ?  TS_STAT_PERSISTENT : TS_STAT_NON_PERSISTENT, static_cast<TSStatSync>(type));
+  if (stat_id_ != TS_ERROR) {
+    LOG_DEBUG("Created new stat named '%s' with stat_id = %d", name.c_str(), stat_id_);
+  } else {
+    LOG_ERROR("Unable to create stat named '%s'.", name.c_str());
+  }
+
+  if (stat_id_ == TS_ERROR) {
+    return false;
+  }
+
+  if (!persistent) {
+    set(0);
+  }
+
+  return true;
+}
+
+void Stat::set(int64_t value) {
+  if (stat_id_ == TS_ERROR) {
+    return;
+  }
+
+  TSStatIntSet(stat_id_, value);
+}
+
+int64_t Stat::get() const {
+  if (stat_id_ == TS_ERROR) {
+    return 0;
+  }
+
+  return TSStatIntGet(stat_id_);
+}
+
+void Stat::increment(int64_t amount) {
+  if (stat_id_ == TS_ERROR) {
+    return;
+  }
+
+  TSStatIntIncrement(stat_id_, amount);
+}
+
+void Stat::decrement(int64_t amount) {
+  if (stat_id_ == TS_ERROR) {
+    return;
+  }
+
+  TSStatIntDecrement(stat_id_, amount);
+}
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Transaction.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Transaction.cc b/lib/atscppapi/src/Transaction.cc
new file mode 100644
index 0000000..e3be20f
--- /dev/null
+++ b/lib/atscppapi/src/Transaction.cc
@@ -0,0 +1,300 @@
+/**
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+/**
+ * @file Transaction.cc
+ */
+
+#include "atscppapi/Transaction.h"
+#include <cstdlib>
+#include <cstring>
+#include <map>
+#include <string>
+#include <ts/ts.h>
+#include "atscppapi/shared_ptr.h"
+#include "logging_internal.h"
+#include "utils_internal.h"
+#include "InitializableValue.h"
+#include "atscppapi/noncopyable.h"
+
+using std::map;
+using std::string;
+using namespace atscppapi;
+
+/**
+ * @private
+ */
+struct atscppapi::TransactionState: noncopyable {
+  TSHttpTxn txn_;
+  std::list<TransactionPlugin *> plugins_;
+  TSMBuffer client_request_hdr_buf_;
+  TSMLoc client_request_hdr_loc_;
+  ClientRequest client_request_;
+  TSMBuffer server_request_hdr_buf_;
+  TSMLoc server_request_hdr_loc_;
+  Request server_request_;
+  TSMBuffer server_response_hdr_buf_;
+  TSMLoc server_response_hdr_loc_;
+  Response server_response_;
+  TSMBuffer client_response_hdr_buf_;
+  TSMLoc client_response_hdr_loc_;
+  Response client_response_;
+  map<string, shared_ptr<Transaction::ContextValue> > context_values_;
+
+  TransactionState(TSHttpTxn txn, TSMBuffer client_request_hdr_buf, TSMLoc client_request_hdr_loc)
+    : txn_(txn), client_request_hdr_buf_(client_request_hdr_buf), client_request_hdr_loc_(client_request_hdr_loc),
+      client_request_(txn, client_request_hdr_buf, client_request_hdr_loc),
+      server_request_hdr_buf_(NULL), server_request_hdr_loc_(NULL),
+      server_response_hdr_buf_(NULL), server_response_hdr_loc_(NULL),
+      client_response_hdr_buf_(NULL), client_response_hdr_loc_(NULL)
+  { };
+};
+
+Transaction::Transaction(void *raw_txn) {
+  TSHttpTxn txn = static_cast<TSHttpTxn>(raw_txn);
+  TSMBuffer hdr_buf;
+  TSMLoc hdr_loc;
+  TSHttpTxnClientReqGet(txn, &hdr_buf, &hdr_loc);
+  if (!hdr_buf || !hdr_loc) {
+    LOG_ERROR("TSHttpTxnClientReqGet tshttptxn=%p returned a null hdr_buf=%p or hdr_loc=%p.", txn, hdr_buf, hdr_loc);
+  }
+
+  state_ = new TransactionState(txn, hdr_buf, hdr_loc);
+  LOG_DEBUG("Transaction tshttptxn=%p constructing Transaction object %p, client req hdr_buf=%p, client req hdr_loc=%p",
+      txn, this, hdr_buf, hdr_loc);
+}
+
+Transaction::~Transaction() {
+  LOG_DEBUG("Transaction tshttptxn=%p destroying Transaction object %p", state_->txn_, this);
+  static const TSMLoc NULL_PARENT_LOC = NULL;
+  TSHandleMLocRelease(state_->client_request_hdr_buf_, NULL_PARENT_LOC, state_->client_request_hdr_loc_);
+  if (state_->server_request_hdr_buf_ && state_->server_request_hdr_loc_) {
+    LOG_DEBUG("Releasing server request");
+    TSHandleMLocRelease(state_->server_request_hdr_buf_, NULL_PARENT_LOC, state_->server_request_hdr_loc_);
+  }
+  if (state_->server_response_hdr_buf_ && state_->server_response_hdr_loc_) {
+    LOG_DEBUG("Releasing server response");
+    TSHandleMLocRelease(state_->server_response_hdr_buf_, NULL_PARENT_LOC, state_->server_response_hdr_loc_);
+  }
+  if (state_->client_response_hdr_buf_ && state_->client_response_hdr_loc_) {
+    LOG_DEBUG("Releasing client response");
+    TSHandleMLocRelease(state_->client_response_hdr_buf_, NULL_PARENT_LOC, state_->client_response_hdr_loc_);
+  }
+  delete state_;
+}
+
+void Transaction::resume() {
+  TSHttpTxnReenable(state_->txn_, static_cast<TSEvent>(TS_EVENT_HTTP_CONTINUE));
+}
+
+void Transaction::error() {
+  LOG_DEBUG("Transaction tshttptxn=%p reenabling to error state", state_->txn_);
+  TSHttpTxnReenable(state_->txn_, static_cast<TSEvent>(TS_EVENT_HTTP_ERROR));
+}
+
+void Transaction::error(const std::string &page) {
+  setErrorBody(page);
+  error(); // finally, reenable with HTTP_ERROR
+}
+
+void Transaction::setErrorBody(const std::string &page) {
+  LOG_DEBUG("Transaction tshttptxn=%p setting error body page: %s", state_->txn_, page.c_str());
+  char *res_bdy = static_cast<char*>(TSmalloc(page.length() + 1));
+  strncpy(res_bdy, page.c_str(), page.length());
+  res_bdy[page.length()] = '\0';
+
+  std::string str_content_type = "text/html";
+  char *content_type = static_cast<char*>(TSmalloc(str_content_type.length() + 1));
+  strncpy(content_type, str_content_type.c_str(), str_content_type.length());
+  content_type[str_content_type.length()] = '\0';
+
+  TSHttpTxnErrorBodySet(state_->txn_, res_bdy, page.length(), content_type);
+}
+
+bool Transaction::isInternalRequest() const {
+  return TSHttpIsInternalRequest(state_->txn_) == TS_SUCCESS;
+}
+
+void *Transaction::getAtsHandle() const {
+  return static_cast<void *>(state_->txn_);
+}
+
+const std::list<atscppapi::TransactionPlugin *> &Transaction::getPlugins() const {
+  return state_->plugins_;
+}
+
+void Transaction::addPlugin(TransactionPlugin *plugin) {
+  LOG_DEBUG("Transaction tshttptxn=%p registering new TransactionPlugin %p.", state_->txn_, plugin);
+  state_->plugins_.push_back(plugin);
+}
+
+shared_ptr<Transaction::ContextValue> Transaction::getContextValue(const std::string &key) {
+  shared_ptr<Transaction::ContextValue> return_context_value;
+  map<string, shared_ptr<Transaction::ContextValue> >::iterator iter = state_->context_values_.find(key);
+  if (iter != state_->context_values_.end()) {
+    return_context_value = iter->second;
+  }
+
+  return return_context_value;
+}
+
+void Transaction::setContextValue(const std::string &key, shared_ptr<Transaction::ContextValue> value) {
+  state_->context_values_[key] = value;
+}
+
+ClientRequest &Transaction::getClientRequest() {
+  return state_->client_request_;
+}
+
+Request &Transaction::getServerRequest() {
+  return state_->server_request_;
+}
+
+Response &Transaction::getServerResponse() {
+  return state_->server_response_;
+}
+
+Response &Transaction::getClientResponse() {
+  return state_->client_response_;
+}
+
+string Transaction::getEffectiveUrl() {
+	string ret_val;
+	int length = 0;
+	char *buf = TSHttpTxnEffectiveUrlStringGet(state_->txn_, &length);
+	if (buf && length) {
+		ret_val.assign(buf, length);
+	}
+
+	if (buf)
+		TSfree(buf);
+
+	return ret_val;
+}
+
+bool Transaction::setCacheUrl(const string &cache_url) {
+	TSReturnCode res = TSCacheUrlSet(state_->txn_, cache_url.c_str(), cache_url.length());
+    return (res == TS_SUCCESS);
+}
+
+const sockaddr *Transaction::getIncomingAddress() const {
+  return TSHttpTxnIncomingAddrGet(state_->txn_);
+}
+
+const sockaddr *Transaction::getClientAddress() const {
+  return TSHttpTxnClientAddrGet(state_->txn_);
+}
+
+const sockaddr *Transaction::getNextHopAddress() const {
+  return TSHttpTxnNextHopAddrGet(state_->txn_);
+}
+
+const sockaddr *Transaction::getServerAddress() const {
+  return TSHttpTxnServerAddrGet(state_->txn_);
+}
+
+bool Transaction::setServerAddress(const sockaddr *sockaddress) {
+  return TSHttpTxnServerAddrSet(state_->txn_,sockaddress) == TS_SUCCESS;
+}
+
+bool Transaction::setIncomingPort(uint16_t port) {
+  TSHttpTxnClientIncomingPortSet(state_->txn_, port);
+  return true; // In reality TSHttpTxnClientIncomingPortSet should return SUCCESS or ERROR.
+}
+
+void Transaction::setTimeout(Transaction::TimeoutType type, int time_ms) {
+  switch (type) {
+    case TIMEOUT_DNS:
+      TSHttpTxnDNSTimeoutSet(state_->txn_, time_ms);
+      break;
+    case TIMEOUT_CONNECT:
+      TSHttpTxnConnectTimeoutSet(state_->txn_, time_ms);
+      break;
+    case TIMEOUT_NO_ACTIVITY:
+      TSHttpTxnNoActivityTimeoutSet(state_->txn_, time_ms);
+      break;
+    case TIMEOUT_ACTIVE:
+      TSHttpTxnActiveTimeoutSet(state_->txn_, time_ms);
+      break;
+    default:
+      break;
+  }
+}
+
+namespace {
+
+/**
+ * initializeHandles is a convinience functor that takes a pointer to a TS Function that
+ * will return the TSMBuffer and TSMLoc for a given server request/response or client/request response
+ *
+ * @param constructor takes a function pointer of type GetterFunction
+ * @param txn a TSHttpTxn
+ * @param hdr_buf the address where the hdr buf will be stored
+ * @param hdr_loc the address where the mem loc will be storeds
+ * @param name name of the entity - used for logging
+ */
+class initializeHandles {
+public:
+  typedef TSReturnCode (*GetterFunction)(TSHttpTxn, TSMBuffer *, TSMLoc *);
+  initializeHandles(GetterFunction getter) : getter_(getter) { }
+  bool operator()(TSHttpTxn txn, TSMBuffer &hdr_buf, TSMLoc &hdr_loc, const char *handles_name) {
+    if (!hdr_buf && !hdr_loc) {
+      if (getter_(txn, &hdr_buf, &hdr_loc) == TS_SUCCESS) {
+        return true;
+      }
+      else {
+        LOG_ERROR("Could not get %s", handles_name);
+      }
+    }
+    else {
+      LOG_ERROR("%s already initialized", handles_name);
+    }
+    return false;
+  }
+private:
+  GetterFunction getter_;
+};
+
+} // anonymous namespace
+
+void Transaction::initServerRequest() {
+  static initializeHandles initializeServerRequestHandles(TSHttpTxnServerReqGet);
+  if (initializeServerRequestHandles(state_->txn_, state_->server_request_hdr_buf_,
+                                     state_->server_request_hdr_loc_, "server request")) {
+    LOG_DEBUG("Initializing server request");
+    state_->server_request_.init(state_->server_request_hdr_buf_, state_->server_request_hdr_loc_);
+  }
+}
+
+void Transaction::initServerResponse() {
+  static initializeHandles initializeServerResponseHandles(TSHttpTxnServerRespGet);
+  if (initializeServerResponseHandles(state_->txn_, state_->server_response_hdr_buf_,
+                                      state_->server_response_hdr_loc_, "server response")) {
+    LOG_DEBUG("Initializing server response");
+    state_->server_response_.init(state_->server_response_hdr_buf_, state_->server_response_hdr_loc_);
+  }
+}
+
+void Transaction::initClientResponse() {
+  static initializeHandles initializeClientResponseHandles(TSHttpTxnClientRespGet);
+  if (initializeClientResponseHandles(state_->txn_, state_->client_response_hdr_buf_,
+                                      state_->client_response_hdr_loc_, "client response")) {
+    LOG_DEBUG("Initializing client response");
+    state_->client_response_.init(state_->client_response_hdr_buf_, state_->client_response_hdr_loc_);
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/TransactionPlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/TransactionPlugin.cc b/lib/atscppapi/src/TransactionPlugin.cc
new file mode 100644
index 0000000..52af478
--- /dev/null
+++ b/lib/atscppapi/src/TransactionPlugin.cc
@@ -0,0 +1,83 @@
+/**
+  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 TransactionPlugin.cc
+ */
+
+#include "atscppapi/TransactionPlugin.h"
+#include <ts/ts.h>
+#include <cstddef>
+#include <cassert>
+#include "atscppapi/Mutex.h"
+#include "atscppapi/shared_ptr.h"
+#include "utils_internal.h"
+#include "atscppapi/noncopyable.h"
+#include "logging_internal.h"
+
+using namespace atscppapi;
+using atscppapi::TransactionPlugin;
+
+/**
+ * @private
+ */
+struct atscppapi::TransactionPluginState: noncopyable {
+  TSCont cont_;
+  TSHttpTxn ats_txn_handle_;
+  shared_ptr<Mutex> mutex_;
+  TransactionPluginState(TSHttpTxn ats_txn_handle) : ats_txn_handle_(ats_txn_handle),
+                                                     mutex_(new Mutex(Mutex::TYPE_RECURSIVE)) { }
+};
+
+namespace {
+
+static int handleTransactionPluginEvents(TSCont cont, TSEvent event, void *edata) {
+  TSHttpTxn txn = static_cast<TSHttpTxn>(edata);
+  TransactionPlugin *plugin = static_cast<TransactionPlugin *>(TSContDataGet(cont));
+  LOG_DEBUG("cont=%p, event=%d, tshttptxn=%p, plugin=%p", cont, event, edata, plugin);
+  atscppapi::utils::internal::invokePluginForEvent(plugin, txn, event);
+  return 0;
+}
+
+} /* anonymous namespace */
+
+TransactionPlugin::TransactionPlugin(Transaction &transaction) {
+  state_ = new TransactionPluginState(static_cast<TSHttpTxn>(transaction.getAtsHandle()));
+  TSMutex mutex = NULL;
+  state_->cont_ = TSContCreate(handleTransactionPluginEvents, mutex);
+  TSContDataSet(state_->cont_, static_cast<void *>(this));
+  LOG_DEBUG("Creating new TransactionPlugin=%p tshttptxn=%p, cont=%p", this, state_->ats_txn_handle_,
+            state_->cont_);
+}
+
+shared_ptr<Mutex> TransactionPlugin::getMutex() {
+  return state_->mutex_;
+}
+
+TransactionPlugin::~TransactionPlugin() {
+  LOG_DEBUG("Destroying TransactionPlugin=%p", this);
+  TSContDestroy(state_->cont_);
+  delete state_;
+}
+
+void TransactionPlugin::registerHook(Plugin::HookType hook_type) {
+  LOG_DEBUG("TransactionPlugin=%p tshttptxn=%p registering hook_type=%d [%s]", this, state_->ats_txn_handle_,
+            hook_type, HOOK_TYPE_STRINGS[hook_type].c_str());
+  TSHttpHookID hook_id = atscppapi::utils::internal::convertInternalHookToTsHook(hook_type);
+  TSHttpTxnHookAdd(state_->ats_txn_handle_, hook_id, state_->cont_);
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/TransformationPlugin.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/TransformationPlugin.cc b/lib/atscppapi/src/TransformationPlugin.cc
new file mode 100644
index 0000000..2fcb899
--- /dev/null
+++ b/lib/atscppapi/src/TransformationPlugin.cc
@@ -0,0 +1,319 @@
+/**
+  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 TransformationPlugin.cc
+ */
+
+#include "atscppapi/TransformationPlugin.h"
+
+#include <ts/ts.h>
+#include <cstddef>
+#include "utils_internal.h"
+#include "logging_internal.h"
+#include "atscppapi/noncopyable.h"
+
+#ifndef INT64_MAX
+#define INT64_MAX (9223372036854775807LL)
+#endif
+
+using namespace atscppapi;
+using atscppapi::TransformationPlugin;
+
+/**
+ * @private
+ */
+struct atscppapi::TransformationPluginState: noncopyable {
+  TSVConn vconn_;
+  Transaction &transaction_;
+  TransformationPlugin &transformation_plugin_;
+  TransformationPlugin::Type type_;
+  TSVIO output_vio_; // this gets initialized on an output().
+  TSHttpTxn txn_;
+  TSIOBuffer output_buffer_;
+  TSIOBufferReader output_buffer_reader_;
+  int64_t bytes_written_;
+
+  // We can only send a single WRITE_COMPLETE even though
+  // we may receive an immediate event after we've sent a
+  // write complete, so we'll keep track of whether or not we've
+  // sent the input end our write complte.
+  bool input_complete_dispatched_;
+
+  TransformationPluginState(atscppapi::Transaction &transaction, TransformationPlugin &transformation_plugin,
+      TransformationPlugin::Type type, TSHttpTxn txn)
+    : vconn_(NULL), transaction_(transaction), transformation_plugin_(transformation_plugin), type_(type),
+      output_vio_(NULL), txn_(txn), output_buffer_(NULL), output_buffer_reader_(NULL), bytes_written_(0),
+      input_complete_dispatched_(false) {
+    output_buffer_ = TSIOBufferCreate();
+    output_buffer_reader_ = TSIOBufferReaderAlloc(output_buffer_);
+  };
+
+  ~TransformationPluginState() {
+    if (output_buffer_reader_) {
+      TSIOBufferReaderFree(output_buffer_reader_);
+      output_buffer_reader_ = NULL;
+    }
+
+    if (output_buffer_) {
+      TSIOBufferDestroy(output_buffer_);
+      output_buffer_ = NULL;
+    }
+  }
+};
+
+namespace {
+
+void cleanupTransformation(TSCont contp) {
+  LOG_DEBUG("Destroying transformation contp=%p", contp);
+  TSContDataSet(contp, reinterpret_cast<void *>(0xDEADDEAD));
+  TSContDestroy(contp);
+}
+
+int handleTransformationPluginRead(TSCont contp, TransformationPluginState *state) {
+  // Traffic Server naming is quite confusing, in this context the write_vio
+  // is actually the vio we read from.
+  TSVIO write_vio = TSVConnWriteVIOGet(contp);
+  if (write_vio) {
+    int64_t to_read = TSVIONTodoGet(write_vio);
+    LOG_DEBUG("Transformation contp=%p write_vio=%p, to_read=%d", contp, write_vio, to_read);
+
+    if (to_read > 0) {
+      /*
+       * The amount of data left to read needs to be truncated by
+       * the amount of data actually in the read buffer.
+       **/
+      int64_t avail = TSIOBufferReaderAvail(TSVIOReaderGet(write_vio));
+      LOG_DEBUG("Transformation contp=%p write_vio=%p, to_read=%d, buffer reader avail=%d", contp, write_vio, to_read, avail);
+
+      if (to_read > avail) {
+        to_read = avail;
+        LOG_DEBUG("Transformation contp=%p write_vio=%p, to read > avail, fixing to_read to be equal to avail. to_read=%d, buffer reader avail=%d", contp, write_vio, to_read, avail);
+      }
+
+      if (to_read > 0) {
+        /* Create a buffer and a buffer reader */
+        TSIOBuffer input_buffer = TSIOBufferCreate();
+        TSIOBufferReader input_reader = TSIOBufferReaderAlloc(input_buffer);
+
+        /* Copy the data from the read buffer to the input buffer. */
+        TSIOBufferCopy(input_buffer, TSVIOReaderGet(write_vio), to_read, 0);
+
+        /* Tell the read buffer that we have read the data and are no
+         longer interested in it. */
+        TSIOBufferReaderConsume(TSVIOReaderGet(write_vio), to_read);
+
+        /* Modify the read VIO to reflect how much data we've completed. */
+        TSVIONDoneSet(write_vio, TSVIONDoneGet(write_vio) + to_read);
+
+        std::string in_data = utils::internal::consumeFromTSIOBufferReader(input_reader);
+        LOG_DEBUG("Transformation contp=%p write_vio=%p consumed %d bytes from bufferreader", contp, write_vio, in_data.length());
+
+        /* Clean up the buffer and reader */
+        TSIOBufferReaderFree(input_reader);
+        TSIOBufferDestroy(input_buffer);
+
+        /* Now call the client to tell them about data */
+        if (in_data.length() > 0) {
+           state->transformation_plugin_.consume(in_data);
+        }
+      }
+
+      /* now that we've finished reading we will check if there is anything left to read. */
+      TSCont vio_cont = TSVIOContGet(write_vio); // for some reason this can occasionally be null
+
+      if (TSVIONTodoGet(write_vio) > 0) {
+        LOG_DEBUG("Transformation contp=%p write_vio=%p, vio_cont=%p still has bytes left to process, todo > 0.", contp, write_vio, vio_cont);
+
+        if (to_read > 0) {
+          TSVIOReenable(write_vio);
+
+          /* Call back the read VIO continuation to let it know that we are ready for more data. */
+          if (vio_cont) {
+            TSContCall(vio_cont, static_cast<TSEvent>(TS_EVENT_VCONN_WRITE_READY), write_vio);
+          }
+        }
+      } else {
+        LOG_DEBUG("Transformation contp=%p write_vio=%p, vio_cont=%p has no bytes left to process, will send WRITE_COMPLETE.", contp, write_vio, vio_cont);
+
+        /* Call back the write VIO continuation to let it know that we have completed the write operation. */
+        if (!state->input_complete_dispatched_) {
+         state->transformation_plugin_.handleInputComplete();
+         state->input_complete_dispatched_ = true;
+         if (vio_cont) {
+           TSContCall(vio_cont, static_cast<TSEvent>(TS_EVENT_VCONN_WRITE_COMPLETE), write_vio);
+         }
+        }
+      }
+    } else {
+      TSCont vio_cont = TSVIOContGet(write_vio); // for some reason this can occasionally be null?
+      LOG_DEBUG("Transformation contp=%p write_vio=%p, vio_cont=%p has no bytes left to process.", contp, write_vio, vio_cont);
+
+      /* Call back the write VIO continuation to let it know that we have completed the write operation. */
+      if (!state->input_complete_dispatched_) {
+       state->transformation_plugin_.handleInputComplete();
+       state->input_complete_dispatched_ = true;
+       if (vio_cont) {
+         TSContCall(vio_cont, static_cast<TSEvent>(TS_EVENT_VCONN_WRITE_COMPLETE), write_vio);
+       }
+      }
+    }
+  } else {
+    LOG_ERROR("Transformation contp=%p write_vio=%p was NULL!", contp, write_vio);
+  }
+  return 0;
+}
+
+int handleTransformationPluginEvents(TSCont contp, TSEvent event, void *edata) {
+  TransformationPluginState *state = static_cast<TransformationPluginState *>(TSContDataGet(contp));
+  LOG_DEBUG("Transformation contp=%p event=%d edata=%p tshttptxn=%p", contp, event, edata, state->txn_);
+
+  // The first thing you always do is check if the VConn is closed.
+  int connection_closed = TSVConnClosedGet(state->vconn_);
+  if (connection_closed) {
+    LOG_DEBUG("Transformation contp=%p tshttptxn=%p is closed connection_closed=%d ", contp, state->txn_, connection_closed);
+    // we'll do the cleanupTransformation in the TransformationPlugin destructor.
+    return 0;
+  }
+
+  if (event == TS_EVENT_VCONN_WRITE_COMPLETE) {
+    TSVConn output_vconn = TSTransformOutputVConnGet(state->vconn_);
+    LOG_DEBUG("Transformation contp=%p tshttptxn=%p received WRITE_COMPLETE, shutting down outputvconn=%p ", contp, state->txn_, output_vconn);
+    TSVConnShutdown(output_vconn, 0, 1);  // The other end is done reading our output
+    return 0;
+  } else if (event == TS_EVENT_ERROR) {
+    TSVIO write_vio;
+    /* Get the write VIO for the write operation that was
+     performed on ourself. This VIO contains the continuation of
+     our parent transformation. */
+    write_vio = TSVConnWriteVIOGet(state->vconn_);
+    TSCont vio_cont = TSVIOContGet(write_vio);
+    LOG_ERROR("Transformation contp=%p tshttptxn=%p received EVENT_ERROR forwarding to write_vio=%p viocont=%p", contp, state->txn_, write_vio, vio_cont);
+    if (vio_cont) {
+      TSContCall(vio_cont, TS_EVENT_ERROR, write_vio);
+    }
+    return 0;
+  }
+
+  // All other events includign WRITE_READY will just attempt to transform more data.
+  return handleTransformationPluginRead(state->vconn_, state);
+}
+
+} /* anonymous namespace */
+
+TransformationPlugin::TransformationPlugin(Transaction &transaction, TransformationPlugin::Type type)
+  : TransactionPlugin(transaction) {
+  state_ = new TransformationPluginState(transaction, *this, type, static_cast<TSHttpTxn>(transaction.getAtsHandle()));
+  state_->vconn_ = TSTransformCreate(handleTransformationPluginEvents, state_->txn_);
+  TSContDataSet(state_->vconn_, static_cast<void *>(state_)); // edata in a TransformationHandler is NOT a TSHttpTxn.
+  LOG_DEBUG("Creating TransformationPlugin=%p (vconn)contp=%p tshttptxn=%p transformation_type=%d", this, state_->vconn_, state_->txn_, type);
+  TSHttpTxnHookAdd(state_->txn_, utils::internal::convertInternalTransformationTypeToTsHook(type), state_->vconn_);
+}
+
+TransformationPlugin::~TransformationPlugin() {
+  LOG_DEBUG("Destroying TransformationPlugin=%p", this);
+  cleanupTransformation(state_->vconn_);
+  delete state_;
+}
+
+size_t TransformationPlugin::produce(const std::string &data) {
+  LOG_DEBUG("TransformationPlugin=%p tshttptxn=%p producing output with length=%d", this, state_->txn_, data.length());
+  int64_t write_length = static_cast<int64_t>(data.length());
+  if (!write_length) {
+    return 0;
+  }
+
+  if (!state_->output_vio_) {
+    TSVConn output_vconn = TSTransformOutputVConnGet(state_->vconn_);
+    LOG_DEBUG("TransformationPlugin=%p tshttptxn=%p will issue a TSVConnWrite, output_vconn=%p.", this, state_->txn_, output_vconn);
+    if (output_vconn) {
+      // If you're confused about the following reference the traffic server transformation docs.
+      // You always write INT64_MAX, this basically says you're not sure how much data you're going to write
+      state_->output_vio_ = TSVConnWrite(output_vconn, state_->vconn_, state_->output_buffer_reader_, INT64_MAX);
+    } else {
+      LOG_ERROR("TransformationPlugin=%p tshttptxn=%p output_vconn=%p cannot issue TSVConnWrite due to null output vconn.",
+          this, state_->txn_, output_vconn);
+      return 0;
+    }
+
+    if (!state_->output_vio_) {
+      LOG_ERROR("TransformationPlugin=%p tshttptxn=%p state_->output_vio=%p, TSVConnWrite failed.",
+          this, state_->txn_, state_->output_vio_);
+      return 0;
+    }
+  }
+
+  // Finally we can copy this data into the output_buffer
+  int64_t bytes_written = TSIOBufferWrite(state_->output_buffer_, data.c_str(), write_length);
+  state_->bytes_written_ += bytes_written; // So we can set BytesDone on outputComplete().
+  LOG_DEBUG("TransformationPlugin=%p tshttptxn=%p write to TSIOBuffer %d bytes total bytes written %d", this, state_->txn_, bytes_written, state_->bytes_written_);
+
+  // Sanity Checks
+  if (bytes_written != write_length) {
+    LOG_ERROR("TransformationPlugin=%p tshttptxn=%p bytes written < expected. bytes_written=%d write_length=%d", this, state_->txn_, bytes_written, write_length);
+  }
+
+  int connection_closed = TSVConnClosedGet(state_->vconn_);
+  LOG_DEBUG("TransformationPlugin=%p tshttptxn=%p vconn=%p connection_closed=%d", this, state_->txn_, state_->vconn_, connection_closed);
+
+  if (!connection_closed) {
+    TSVIOReenable(state_->output_vio_); // Wake up the downstream vio
+  } else {
+    LOG_ERROR("TransformationPlugin=%p tshttptxn=%p output_vio=%p connection_closed=%d : Couldn't reenable output vio (connection closed).", this, state_->txn_, state_->output_vio_, connection_closed);
+  }
+
+  return static_cast<size_t>(bytes_written);
+}
+
+size_t TransformationPlugin::setOutputComplete() {
+  int connection_closed = TSVConnClosedGet(state_->vconn_);
+  LOG_DEBUG("OutputComplete TransformationPlugin=%p tshttptxn=%p vconn=%p connection_closed=%d, total bytes written=%d", this, state_->txn_, state_->vconn_, connection_closed,state_->bytes_written_);
+
+  if (!connection_closed && !state_->output_vio_) {
+      LOG_DEBUG("TransformationPlugin=%p tshttptxn=%p output complete without writing any data, initiating write of 0 bytes.", this, state_->txn_);
+
+      // We're done without ever outputting anything, to correctly
+      // clean up we'll initiate a write then immeidately set it to 0 bytes done.
+      state_->output_vio_ = TSVConnWrite(TSTransformOutputVConnGet(state_->vconn_), state_->vconn_, state_->output_buffer_reader_, 0);
+
+      if (state_->output_vio_) {
+        TSVIONDoneSet(state_->output_vio_, 0);
+        TSVIOReenable(state_->output_vio_); // Wake up the downstream vio
+      } else {
+        LOG_ERROR("TransformationPlugin=%p tshttptxn=%p unable to reenable output_vio=%p because VConnWrite failed.", this, state_->txn_, state_->output_vio_);
+      }
+
+      return 0;
+  }
+
+  if (!connection_closed) {
+    // So there is a possible race condition here, if we wake up a dead
+    // VIO it can cause a segfault, so we must check that the VCONN is not dead.
+    int connection_closed = TSVConnClosedGet(state_->vconn_);
+    if (!connection_closed) {
+      TSVIONBytesSet(state_->output_vio_, state_->bytes_written_);
+      TSVIOReenable(state_->output_vio_); // Wake up the downstream vio
+    } else {
+      LOG_ERROR("TransformationPlugin=%p tshttptxn=%p unable to reenable output_vio=%p connection was closed=%d.", this, state_->txn_, state_->output_vio_, connection_closed);
+    }
+  } else {
+    LOG_ERROR("TransformationPlugin=%p tshttptxn=%p unable to reenable output_vio=%p connection was closed=%d.", this, state_->txn_, state_->output_vio_, connection_closed);
+  }
+
+  return state_->bytes_written_;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/Url.cc
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/Url.cc b/lib/atscppapi/src/Url.cc
new file mode 100644
index 0000000..2dce493
--- /dev/null
+++ b/lib/atscppapi/src/Url.cc
@@ -0,0 +1,218 @@
+/**
+  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 Url.cc
+ */
+#include "atscppapi/Url.h"
+#include <ts/ts.h>
+#include "atscppapi/noncopyable.h"
+#include "InitializableValue.h"
+#include "logging_internal.h"
+
+using namespace atscppapi;
+using std::string;
+
+/**
+ * @private
+ */
+struct atscppapi::UrlState: noncopyable {
+  TSMBuffer hdr_buf_;
+  TSMLoc url_loc_;
+  InitializableValue<string> url_string_;
+  InitializableValue<string> path_;
+  InitializableValue<string> query_;
+  InitializableValue<string> host_;
+  InitializableValue<string> scheme_;
+  InitializableValue<uint16_t> port_;
+  UrlState(TSMBuffer hdr_buf, TSMLoc url_loc) :
+      hdr_buf_(hdr_buf), url_loc_(url_loc) {
+  }
+};
+
+Url::Url() {
+  state_ = new UrlState(static_cast<TSMBuffer>(NULL), static_cast<TSMLoc>(NULL));
+}
+
+Url::Url(void *hdr_buf, void *url_loc) {
+  state_ = new UrlState(static_cast<TSMBuffer>(hdr_buf), static_cast<TSMLoc>(url_loc));
+}
+
+void Url::init(void *hdr_buf, void *url_loc) {
+  state_->hdr_buf_ = static_cast<TSMBuffer>(hdr_buf);
+  state_->url_loc_ = static_cast<TSMLoc>(url_loc);
+}
+
+Url::~Url() {
+  delete state_;
+}
+
+bool inline Url::isInitialized() const {
+  return state_->hdr_buf_ && state_->url_loc_;
+}
+
+void Url::reset() {
+  state_->url_string_.setInitialized(false);
+  state_->path_.setInitialized(false);
+  state_->query_.setInitialized(false);
+  state_->host_.setInitialized(false);
+  state_->scheme_.setInitialized(false);
+  state_->port_.setInitialized(false);
+}
+
+const std::string &Url::getUrlString() const {
+  if (isInitialized() && !state_->url_string_.isInitialized()) {
+    int length;
+    char *memptr = TSUrlStringGet(state_->hdr_buf_, state_->url_loc_, &length);
+    if (memptr && length) {
+      state_->url_string_ = std::string(memptr, length);
+      TSfree(memptr);
+      LOG_DEBUG("Got URL [%s]", state_->url_string_.getValue().c_str());
+    } else {
+      LOG_ERROR("Got null/zero-length URL string; hdr_buf %p, url_loc %p, ptr %p, length %d", state_->hdr_buf_,
+                state_->url_loc_, memptr, length);
+    }
+  }
+  return state_->url_string_;
+}
+
+const std::string &Url::getPath() const {
+  if (isInitialized() && !state_->path_.isInitialized()) {
+    int length;
+    const char *memptr = TSUrlPathGet(state_->hdr_buf_, state_->url_loc_, &length);
+    if (memptr && length) {
+      state_->path_ = std::string(memptr, length);
+    }
+    LOG_DEBUG("Using path [%s]", state_->path_.getValue().c_str());
+  }
+  return state_->path_;
+}
+
+const std::string &Url::getQuery() const {
+  if (isInitialized() && !state_->query_.isInitialized()) {
+    int length;
+    const char *memptr = TSUrlHttpQueryGet(state_->hdr_buf_, state_->url_loc_, &length);
+    if (memptr && length) {
+      state_->query_ = std::string(memptr, length);
+    }
+    LOG_DEBUG("Using query [%s]", state_->query_.getValue().c_str());
+  }
+  return state_->query_;
+}
+
+const std::string &Url::getScheme() const {
+  if (isInitialized() && !state_->scheme_.isInitialized()) {
+    int length;
+    const char *memptr = TSUrlSchemeGet(state_->hdr_buf_, state_->url_loc_, &length);
+    if (memptr && length) {
+      state_->scheme_ = std::string(memptr, length);
+    }
+    LOG_DEBUG("Using scheme [%s]", state_->scheme_.getValue().c_str());
+  }
+  return state_->scheme_;
+}
+
+const std::string &Url::getHost() const {
+  if (isInitialized() && !state_->host_.isInitialized()) {
+    int length;
+    const char *memptr = TSUrlHostGet(state_->hdr_buf_, state_->url_loc_, &length);
+    if (memptr && length) {
+      state_->host_ = std::string(memptr, length);
+    }
+    LOG_DEBUG("Using host [%s]", state_->host_.getValue().c_str());
+  }
+  return state_->host_;
+}
+
+uint16_t Url::getPort() const {
+  if (isInitialized() && !state_->port_.isInitialized()) {
+    state_->port_ = TSUrlPortGet(state_->hdr_buf_, state_->url_loc_);
+    LOG_DEBUG("Got port %d", state_->port_.getValue());
+  }
+  return state_->port_;
+}
+
+void Url::setPath(const std::string &path) {
+  if (!isInitialized()) {
+    LOG_ERROR("Not initialized");
+    return;
+  }
+  state_->url_string_.setInitialized(false);
+  if (TSUrlPathSet(state_->hdr_buf_, state_->url_loc_, path.c_str(), path.length()) == TS_SUCCESS) {
+    state_->path_ = path;
+    LOG_DEBUG("Set path to [%s]", path.c_str());
+  } else {
+    LOG_ERROR("Could not set path; hdr_buf %p, url_loc %p", state_->hdr_buf_, state_->url_loc_);
+  }
+}
+
+void Url::setQuery(const std::string &query) {
+  if (!isInitialized()) {
+    LOG_ERROR("Not initialized");
+    return;
+  }
+  state_->url_string_.setInitialized(false);
+  if (TSUrlHttpQuerySet(state_->hdr_buf_, state_->url_loc_, query.c_str(), query.length()) == TS_SUCCESS) {
+    state_->query_ = query;
+    LOG_DEBUG("Set query to [%s]", query.c_str());
+  } else {
+    LOG_ERROR("Could not set query; hdr_buf %p, url_loc %p", state_->hdr_buf_, state_->url_loc_);
+  }
+}
+
+void Url::setScheme(const std::string &scheme) {
+  if (!isInitialized()) {
+    LOG_ERROR("Not initialized");
+    return;
+  }
+  state_->url_string_.setInitialized(false);
+  if (TSUrlSchemeSet(state_->hdr_buf_, state_->url_loc_, scheme.c_str(), scheme.length()) == TS_SUCCESS) {
+    state_->scheme_ = scheme;
+    LOG_DEBUG("Set scheme to [%s]", scheme.c_str());
+  } else {
+    LOG_ERROR("Could not set scheme; hdr_buf %p, url_loc %p", state_->hdr_buf_, state_->url_loc_);
+  }
+}
+
+void Url::setHost(const std::string &host) {
+  if (!isInitialized()) {
+    LOG_ERROR("Not initialized");
+    return;
+  }
+  state_->url_string_.setInitialized(false);
+  if (TSUrlHostSet(state_->hdr_buf_, state_->url_loc_, host.c_str(), host.length()) == TS_SUCCESS) {
+    state_->host_ = host;
+    LOG_DEBUG("Set host to [%s]", host.c_str());
+  } else {
+    LOG_ERROR("Could not set host; hdr_buf %p, url_loc %p", state_->hdr_buf_, state_->url_loc_);
+  }
+}
+
+void Url::setPort(const uint16_t port) {
+  if (!isInitialized()) {
+    LOG_ERROR("Not initialized");
+    return;
+  }
+  state_->url_string_.setInitialized(false);
+  if (TSUrlPortSet(state_->hdr_buf_, state_->url_loc_, port) == TS_SUCCESS) {
+    state_->port_ = port;
+    LOG_DEBUG("Set port to %d", port);
+  } else {
+    LOG_ERROR("Could not set port; hdr_buf %p, url_loc %p", state_->hdr_buf_, state_->url_loc_);
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/include/InitializableValue.h
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/include/InitializableValue.h b/lib/atscppapi/src/include/InitializableValue.h
new file mode 100644
index 0000000..8d20320
--- /dev/null
+++ b/lib/atscppapi/src/include/InitializableValue.h
@@ -0,0 +1,90 @@
+/**
+  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 InitializableValue.h
+ */
+
+#pragma once
+#ifndef ATSCPPAPI_INITIALIZABLEVALUE_H_
+#define ATSCPPAPI_INITIALIZABLEVALUE_H_
+
+namespace atscppapi {
+
+// cannot be static as InitializableValue is a template and a static
+// member of that would be instantiated once for every type the
+// template is instantiated
+extern bool transaction_data_caching_enabled;
+
+/**
+ * @private
+ */
+template <typename Type> class InitializableValue {
+public:
+  InitializableValue() : initialized_(false) { }
+  explicit InitializableValue(Type value, bool initialized = true) : value_(value), initialized_(initialized) { }
+
+  inline void setValue(const Type &value) {
+    value_ = value;
+    initialized_ = true;
+  }
+
+  inline bool isInitialized() const {
+#ifdef DISABLE_TRANSACTION_DATA_CACHING
+    return false;
+#endif
+    return transaction_data_caching_enabled && initialized_;
+  }
+
+  inline Type &getValueRef() {
+    return value_;
+  }
+
+  inline Type getValue() {
+    return value_;
+  }
+
+  inline const Type &getValueRef() const {
+    return value_;
+  }
+
+  inline void setInitialized(bool initialized = true) {
+    initialized_ = initialized;
+  }
+
+  inline operator Type&() {
+    return value_;
+  }
+
+  inline operator const Type&() const {
+    return value_;
+  }
+
+  inline InitializableValue<Type> &operator=(const Type& value) {
+    setValue(value);
+    return *this;
+  }
+
+private:
+  Type value_;
+  bool initialized_;
+};
+
+}
+
+#endif /* ATSCPPAPI_INITIALIZABLEVALUE_H_ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/include/atscppapi/Async.h
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/include/atscppapi/Async.h b/lib/atscppapi/src/include/atscppapi/Async.h
new file mode 100644
index 0000000..39a24d0
--- /dev/null
+++ b/lib/atscppapi/src/include/atscppapi/Async.h
@@ -0,0 +1,187 @@
+/**
+  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 Async.h
+ * @brief Provides constructs to perform async operations.
+ */
+
+#pragma once
+#ifndef ATSCPPAPI_ASYNC_H_
+#define ATSCPPAPI_ASYNC_H_
+#include <list>
+#include <atscppapi/Mutex.h>
+#include <atscppapi/noncopyable.h>
+#include <atscppapi/shared_ptr.h>
+
+namespace atscppapi {
+
+/**
+ * @private
+ *
+ * @brief This class represents the interface of a dispatch controller. A dispatch controller
+ * is used to dispatch an event to a receiver. This interface exists so that the types in this
+ * header file can be defined.
+ */
+class AsyncDispatchControllerBase : noncopyable {
+public:
+  /**
+   * Dispatches an async event to a receiver.
+   *
+   * @return True if the receiver was still alive.
+   */
+  virtual bool dispatch() = 0;
+  virtual ~AsyncDispatchControllerBase() { }
+};
+
+/**
+ * @brief AsyncProvider is the interface that providers of async operations must implement. 
+ * The system allows decoupling of the lifetime/scope of provider and receiver objects. The 
+ * receiver object might have expired before the async operation is complete and the system
+ * handles this case. Because of this decoupling, it is the responsibility of the provider
+ * to manage it's expiration - self-destruct on completion is a good option.
+ */
+class AsyncProvider {
+public:
+  /**
+   * This method is invoked when the async operation is requested. This call should be used
+   * to just start the async operation and *not* block this thread.
+   *
+   * @param dispatch_controller provides a way to dispatch an "async complete" event to the
+   *                            requester.
+   */
+  virtual void run(shared_ptr<AsyncDispatchControllerBase> dispatch_controller) = 0;
+  virtual ~AsyncProvider() { }
+};
+
+/**
+ * @private
+ *
+ * @brief Dispatch controller implementation. When invoking the receiver, it verifies that the
+ * receiver is still alive, locks the mutex and then invokes handleAsyncComplete().
+ */
+template<typename AsyncEventReceiverType, typename AsyncProviderType>
+class AsyncDispatchController : public AsyncDispatchControllerBase {
+public:
+  bool dispatch() {
+    bool ret = false;
+    ScopedSharedMutexLock scopedLock(dispatch_mutex_);
+    if (event_receiver_) {
+      event_receiver_->handleAsyncComplete(static_cast<AsyncProviderType &>(*provider_));
+      ret = true;
+    }
+    return ret;
+  }
+
+  /**
+   * Constructor
+   *
+   * @param event_receiver The async complete event will be dispatched to this receiver.
+   * @param provider Async operation provider that is passed to the receiver on dispatch.
+   * @param mutex Mutex of the receiver that is locked during the dispatch
+   */
+  AsyncDispatchController(AsyncEventReceiverType *event_receiver, AsyncProviderType *provider, shared_ptr<Mutex> mutex) :
+    event_receiver_(event_receiver), dispatch_mutex_(mutex), provider_(provider) {
+  }
+
+  virtual ~AsyncDispatchController() { }
+public:
+  AsyncEventReceiverType *event_receiver_;
+  shared_ptr<Mutex> dispatch_mutex_;
+private:
+  AsyncProviderType *provider_;
+};
+
+/**
+ * @private
+ * 
+ * @brief A promise is used to let the dispatch controller know if the receiver is still
+ * alive to receive the async complete dispatch. When the receiver dies, this promise is
+ * broken and it automatically updates the dispatch controller.
+ */
+template<typename AsyncEventReceiverType, typename AsyncProviderType>
+class AsyncReceiverPromise : noncopyable {
+public:
+  AsyncReceiverPromise(shared_ptr<AsyncDispatchController<AsyncEventReceiverType, AsyncProviderType> > dispatch_controller) :
+    dispatch_controller_(dispatch_controller) { }
+
+  ~AsyncReceiverPromise() {
+    ScopedSharedMutexLock scopedLock(dispatch_controller_->dispatch_mutex_);
+    dispatch_controller_->event_receiver_ = NULL;
+  }
+protected:
+  shared_ptr<AsyncDispatchController<AsyncEventReceiverType, AsyncProviderType> > dispatch_controller_;
+};
+
+/**
+ * @brief AsyncReceiver is the interface that receivers of async operations must implement. It is
+ * templated on the type of the async operation provider.
+ */
+template<typename AsyncProviderType>
+class AsyncReceiver : noncopyable {
+public:
+  /**
+   * This method is invoked when the async operation is completed. The
+   * mutex provided during the creation of the async operation will be
+   * automatically locked during the invocation of this method.
+   *
+   * @param provider A reference to the provider which completed the async operation.
+   */
+  virtual void handleAsyncComplete(AsyncProviderType &provider) = 0;
+  virtual ~AsyncReceiver() { }
+protected:
+  AsyncReceiver() { }
+  friend class Async;
+private:
+  mutable std::list<shared_ptr<AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType> > > receiver_promises_;
+};
+
+/**
+ * @brief This class provides a method to create an async operation.
+ */
+class Async : noncopyable {
+public:
+  /**
+   * This method sets up the dispatch controller to link the async operation provider and 
+   * receiver and then initiates the operation by invoking the provider. 
+   *
+   * @param event_receiver The receiver of the async complete dispatch.
+   * @param provider The provider of the async operation.
+   * @param mutex The mutex that is locked during the dispatch of the async event complete.
+   *              One will be created if nothing is passed in. Transaction plugins should use 
+   *              TransactionPlugin::getMutex() here and global plugins can pass an appropriate
+   *              or NULL mutex.
+   */
+  template<typename AsyncProviderType>
+  static void execute(AsyncReceiver<AsyncProviderType> *event_receiver, AsyncProviderType *provider, shared_ptr<Mutex> mutex) {
+    if (!mutex.get()) {
+      mutex.reset(new Mutex(Mutex::TYPE_RECURSIVE));
+    }
+    shared_ptr<AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType > > dispatcher(
+      new AsyncDispatchController<AsyncReceiver<AsyncProviderType>, AsyncProviderType >(event_receiver, provider, mutex));
+    shared_ptr<AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType > > receiver_promise(
+      new AsyncReceiverPromise<AsyncReceiver<AsyncProviderType>, AsyncProviderType >(dispatcher));
+    event_receiver->receiver_promises_.push_back(receiver_promise); // now if the event receiver dies, we're safe.
+    provider->run(dispatcher);
+  }
+};
+
+}
+
+
+#endif /* ATSCPPAPI_ASYNC_H_ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/include/atscppapi/AsyncHttpFetch.h
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/include/atscppapi/AsyncHttpFetch.h b/lib/atscppapi/src/include/atscppapi/AsyncHttpFetch.h
new file mode 100644
index 0000000..cb05ca6
--- /dev/null
+++ b/lib/atscppapi/src/include/atscppapi/AsyncHttpFetch.h
@@ -0,0 +1,102 @@
+/**
+  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.h
+ */
+
+#pragma once
+#ifndef ATSCPPAPI_ASYNCHTTPFETCH_H_
+#define ATSCPPAPI_ASYNCHTTPFETCH_H_
+
+#include <string>
+#include <atscppapi/shared_ptr.h>
+#include <atscppapi/Async.h>
+#include <atscppapi/Request.h>
+#include <atscppapi/Response.h>
+
+namespace atscppapi {
+
+// forward declarations
+class AsyncHttpFetchState;
+namespace utils { class internal; }
+
+/**
+ * @brief This class provides an implementation of AsyncProvider that
+ * makes HTTP requests asynchronously. This provider automatically
+ * self-destructs after the completion of the request.
+ *
+ * See example async_http_fetch for sample usage.
+ */
+class AsyncHttpFetch : public AsyncProvider {
+public:
+  AsyncHttpFetch(const std::string &url_str, HttpMethod http_method = HTTP_METHOD_GET);
+
+  /**
+   * Used to manipulate the headers of the request to be made.
+   *
+   * @return A reference to mutable headers.
+   */
+  Headers &getRequestHeaders();
+
+  enum Result { RESULT_SUCCESS = 10000, RESULT_TIMEOUT, RESULT_FAILURE };
+
+  /**
+   * Used to extract the response after request completion. 
+   *
+   * @return Result of the operation
+   */
+  Result getResult() const;
+
+  /**
+   * @return Non-mutable reference to the request URL.
+   */
+  const Url &getRequestUrl() const;
+
+  /**
+   * Used to extract the response after request completion. 
+   *
+   * @return Non-mutable reference to the response.
+   */
+  const Response &getResponse() const;
+
+  /**
+   * Used to extract the body of the response after request completion. On
+   * unsuccessful completion, values (NULL, 0) are set.
+   *
+   * @param body Output argument; will point to the body
+   * @param body_size Output argument; will contain the size of the body 
+   * 
+   */
+  void getResponseBody(const void *&body, size_t &body_size) const;
+
+  virtual ~AsyncHttpFetch();
+
+  /**
+   * Starts a HTTP fetch of the Request contained.
+   */  
+  virtual void run(shared_ptr<AsyncDispatchControllerBase> dispatch_controller);
+
+private:
+  AsyncHttpFetchState *state_;
+  friend class utils::internal;
+};
+
+} /* atscppapi */
+
+#endif /* ATSCPPAPI_ASYNCHTTPFETCH_H_ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/include/atscppapi/AsyncTimer.h
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/include/atscppapi/AsyncTimer.h b/lib/atscppapi/src/include/atscppapi/AsyncTimer.h
new file mode 100644
index 0000000..b076eda
--- /dev/null
+++ b/lib/atscppapi/src/include/atscppapi/AsyncTimer.h
@@ -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 AsyncTimer.h
+ */
+
+#pragma once
+#ifndef ATSCPPAPI_ASYNCTIMER_H_
+#define ATSCPPAPI_ASYNCTIMER_H_
+
+#include <string>
+#include <atscppapi/shared_ptr.h>
+#include <atscppapi/Async.h>
+#include <atscppapi/Request.h>
+#include <atscppapi/Response.h>
+
+namespace atscppapi {
+
+// forward declarations
+class AsyncTimerState;
+
+/**
+ * @brief This class provides an implementation of AsyncProvider that
+ * acts as a timer. It sends events at the set frequency. Calling the
+ * destructor will stop the events. A one-off timer just sends one
+ * event. Calling the destructor before this event will cancel the timer.
+ * 
+ * For either type, user must delete the timer.
+ *
+ * See example async_timer for sample usage.
+ */
+class AsyncTimer : public AsyncProvider {
+public:
+
+  enum Type { TYPE_ONE_OFF = 0, TYPE_PERIODIC };
+
+  /**
+   * Constructor.
+   * 
+   * @param type A one-off timer fires only once and a periodic timer fires periodically.
+   * @param period_in_ms The receiver will receive an event every this many milliseconds.
+   * @param initial_period_in_ms The first event will arrive after this many milliseconds. Subsequent
+   *                             events will have "regular" cadence. This is useful if the timer is
+   *                             set for a long period of time (1hr etc.), but an initial event is
+   *                             required. Value of 0 (default) indicates no initial event is desired.
+   */
+  AsyncTimer(Type type, int period_in_ms, int initial_period_in_ms = 0);
+
+  ~AsyncTimer();
+
+  /**
+   * Starts the timer.
+   */  
+  void run(shared_ptr<AsyncDispatchControllerBase> dispatch_controller);
+
+private:
+  AsyncTimerState *state_;
+};
+
+} /* atscppapi */
+
+#endif /* ATSCPPAPI_ASYNCTIMER_H_ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/7b2ea5f8/lib/atscppapi/src/include/atscppapi/CaseInsensitiveStringComparator.h
----------------------------------------------------------------------
diff --git a/lib/atscppapi/src/include/atscppapi/CaseInsensitiveStringComparator.h b/lib/atscppapi/src/include/atscppapi/CaseInsensitiveStringComparator.h
new file mode 100644
index 0000000..a47462a
--- /dev/null
+++ b/lib/atscppapi/src/include/atscppapi/CaseInsensitiveStringComparator.h
@@ -0,0 +1,51 @@
+/**
+  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.h
+ * @brief A case insensitive comparator that can be used with STL containers.
+ */
+
+#pragma once
+#ifndef ATSCPPAPI_CASE_INSENSITIVE_STRING_COMPARATOR_H_
+#define ATSCPPAPI_CASE_INSENSITIVE_STRING_COMPARATOR_H_
+
+#include <string>
+
+namespace atscppapi {
+
+/**
+ * @brief A case insensitive comparator that can be used with standard library containers.
+ *
+ * The primary use for this class is to make all Headers case insensitive.
+ */
+class CaseInsensitiveStringComparator {
+public:
+  /**
+   * @return true if lhs is lexicographically "less-than" rhs; meant for use in std::map or other standard library containers.
+   */
+  bool operator()(const std::string &lhs, const std::string &rhs) const;
+
+  /**
+   * @return numerical value of lexicographical comparison a la strcmp
+   */
+  int compare(const std::string &lhs, const std::string &rhs) const;
+};
+
+}
+
+#endif