You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by br...@apache.org on 2013/10/18 23:51:11 UTC
[4/5] initial atscppapi commit
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") };
+