You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by os...@apache.org on 2014/08/04 15:57:50 UTC

[3/3] git commit: ats_speed: PageSpeed optimization plugin

ats_speed: PageSpeed optimization plugin

Pull in the plugin code from https://github.com/We-Amp/ats_speed.
This adds the plugin as-is, and doesn't wire up to automake yet.

As this plugin needs to download external libraries to
build, a custom make file is added.

See README.md for setting this up.

For more information see
https://developers.google.com/speed/pagespeed/optimization and
http://www.atsspeed.com/

Plugin is donated by We-Amp, ip cleared via:
http://incubator.apache.org/ip-clearance/ats-ats_speed.html
Jira ticket: https://issues.apache.org/jira/browse/TS-2926


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/083abd4f
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/083abd4f
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/083abd4f

Branch: refs/heads/master
Commit: 083abd4ff89994682aab406dee18df891a136518
Parents: fab2025
Author: Otto van der Schaaf <os...@we-amp.com>
Authored: Wed Jul 9 22:01:10 2014 +0200
Committer: Otto van der Schaaf <os...@we-amp.com>
Committed: Wed Jul 9 22:27:00 2014 +0200

----------------------------------------------------------------------
 plugins/experimental/ats_speed/.gitignore       |   15 +
 plugins/experimental/ats_speed/LICENSE          |  202 ++++
 plugins/experimental/ats_speed/Makefile         |   66 ++
 .../experimental/ats_speed/Makefile.psol_source |   33 +
 plugins/experimental/ats_speed/README.md        |   52 +
 .../experimental/ats_speed/ats_base_fetch.cc    |  135 +++
 plugins/experimental/ats_speed/ats_base_fetch.h |   80 ++
 .../ats_speed/ats_beacon_intercept.cc           |  350 ++++++
 .../ats_speed/ats_beacon_intercept.h            |   23 +
 plugins/experimental/ats_speed/ats_config.cc    |  196 ++++
 plugins/experimental/ats_speed/ats_config.h     |   82 ++
 .../experimental/ats_speed/ats_demo_filter.cc   |   82 ++
 .../experimental/ats_speed/ats_demo_filter.h    |   57 +
 .../experimental/ats_speed/ats_header_utils.cc  |   88 ++
 .../experimental/ats_speed/ats_header_utils.h   |   33 +
 .../ats_speed/ats_log_message_handler.cc        |   93 ++
 .../ats_speed/ats_log_message_handler.h         |   28 +
 .../ats_speed/ats_message_handler.cc            |  107 ++
 .../ats_speed/ats_message_handler.h             |   68 ++
 .../ats_speed/ats_process_context.cc            |   76 ++
 .../ats_speed/ats_process_context.h             |   49 +
 .../ats_speed/ats_resource_intercept.cc         |  376 ++++++
 .../ats_speed/ats_resource_intercept.h          |   21 +
 .../ats_speed/ats_rewrite_driver_factory.cc     |  178 +++
 .../ats_speed/ats_rewrite_driver_factory.h      |  102 ++
 .../ats_speed/ats_rewrite_options.cc            |  256 +++++
 .../ats_speed/ats_rewrite_options.h             |   95 ++
 .../ats_speed/ats_server_context.cc             |   38 +
 .../experimental/ats_speed/ats_server_context.h |   48 +
 plugins/experimental/ats_speed/ats_speed.cc     | 1083 ++++++++++++++++++
 plugins/experimental/ats_speed/ats_speed.h      |   94 ++
 .../experimental/ats_speed/ats_thread_system.h  |   43 +
 plugins/experimental/ats_speed/ethread.patch    |   13 +
 plugins/experimental/ats_speed/gzip/Makefile    |    9 +
 plugins/experimental/ats_speed/gzip/README      |    4 +
 .../ats_speed/gzip/configuration.cc             |  264 +++++
 .../experimental/ats_speed/gzip/configuration.h |   84 ++
 .../experimental/ats_speed/gzip/debug_macros.h  |   59 +
 plugins/experimental/ats_speed/gzip/gzip.cc     |  826 +++++++++++++
 plugins/experimental/ats_speed/gzip/gzip.config |    6 +
 plugins/experimental/ats_speed/gzip/misc.cc     |  197 ++++
 plugins/experimental/ats_speed/gzip/misc.h      |   84 ++
 .../ats_speed/scripts/prepare_psol.sh           |   95 ++
 43 files changed, 5890 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/.gitignore
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/.gitignore b/plugins/experimental/ats_speed/.gitignore
new file mode 100644
index 0000000..a12b1d2
--- /dev/null
+++ b/plugins/experimental/ats_speed/.gitignore
@@ -0,0 +1,15 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.gz
+*~
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/LICENSE
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/LICENSE b/plugins/experimental/ats_speed/LICENSE
new file mode 100644
index 0000000..e06d208
--- /dev/null
+++ b/plugins/experimental/ats_speed/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/Makefile
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/Makefile b/plugins/experimental/ats_speed/Makefile
new file mode 100644
index 0000000..a2f5de7
--- /dev/null
+++ b/plugins/experimental/ats_speed/Makefile
@@ -0,0 +1,66 @@
+SHELL := /bin/bash
+TSXS?=tsxs
+# Specify BUILDTYPE=Debug for a debug build
+BUILDTYPE?=Release
+MOD_PAGESPEED_DIR=$(shell pwd)/psol/include/
+PAGESPEED_OUT=$(shell pwd)/psol/lib/$(BUILDTYPE)/linux/x64/
+
+
+os_name=unknown_os
+arch_name=ia32
+uname_os=$(shell uname)
+uname_arch=$(shell uname -m)
+
+ifeq ($(uname_os),Linux)
+  os_name=linux
+endif
+
+ifeq ($(uname_arch), x86_64)
+  arch_name=x64
+endif
+ifeq ($(uname_arch), amd64)
+  arch_name=x64
+endif
+
+INC =-I$(MOD_PAGESPEED_DIR)\
+ -I$(MOD_PAGESPEED_DIR)third_party/chromium/src/\
+ -I$(MOD_PAGESPEED_DIR)third_party/google-sparsehash/src\
+ -I$(MOD_PAGESPEED_DIR)third_party/google-sparsehash/gen/arch/$(os_name)/$(arch_name)/include\
+ -I$(MOD_PAGESPEED_DIR)third_party/protobuf/src\
+ -I$(MOD_PAGESPEED_DIR)third_party/re2/src\
+ -I$(MOD_PAGESPEED_DIR)third_party/out/$(BUILDTYPE)/obj/gen\
+ -I$(MOD_PAGESPEED_DIR)third_party/apr/src/include/\
+ -I$(MOD_PAGESPEED_DIR)third_party/aprutil/src/include/\
+ -I$(MOD_PAGESPEED_DIR)third_party/apr/gen/arch/$(os_name)/$(arch_name)/include/\
+ -I$(MOD_PAGESPEED_DIR)third_party/aprutil/gen/arch/$(os_name)/$(arch_name)/include/\
+ -I$(MOD_PAGESPEED_DIR)out/$(BUILDTYPE)/obj/gen\
+ -I$(MOD_PAGESPEED_DIR)out/$(BUILDTYPE)/obj/gen/protoc_out/instaweb
+
+PSOL_LIBS = $(PAGESPEED_OUT)pagespeed_automatic.a $(PAGESPEED_OUT)libserf.a $(PAGESPEED_OUT)libaprutil.a $(PAGESPEED_OUT)libapr.a
+
+%.so: psol %.cc
+# https://github.com/pagespeed/ngx_pagespeed/issues/433: it would be nice to have -Wall -Werror, only suppressing when needed.
+	g++ $(INC) -shared -o ats_speed.so -g -pipe -O3 -fpic $(MOD_PAGESPEED_DIR)/out/$(BUILDTYPE)/obj/gen/data2c_out/instaweb/net/instaweb/apache/install/mod_pagespeed_example/*.cc $(MOD_PAGESPEED_DIR)/net/instaweb/system/*.cc  *.cc -lstdc++ -lstdc++  -lpthread $(PSOL_LIBS) -lrt
+
+all: psol gzip/gzip.so ats_speed.so
+
+1.7.30.4.tar.gz:
+	wget --no-check-certificate https://dl.google.com/dl/page-speed/psol/1.7.30.4.tar.gz
+
+psol/: 1.7.30.4.tar.gz
+	tar -xzvf 1.7.30.4.tar.gz
+
+gzip/gzip.so:
+	cd gzip && make
+
+install: all
+	$(TSXS) -i -o ats_speed.so
+	cd gzip && make install
+
+cleanpsol:
+	rm -rf psol/
+	rm *.gz
+
+clean:
+	rm -f *.lo *.so *.o
+	rm -f gzip/*.lo gzip/*.so gzip/*.o

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/Makefile.psol_source
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/Makefile.psol_source b/plugins/experimental/ats_speed/Makefile.psol_source
new file mode 100755
index 0000000..8428d3a
--- /dev/null
+++ b/plugins/experimental/ats_speed/Makefile.psol_source
@@ -0,0 +1,33 @@
+TSXS?=tsxs
+BUILDTYPE=Release
+MOD_PAGESPEED_DIR=$(HOME)/code/google/mod_pagespeed/src/
+PAGESPEED_OUT=$(MOD_PAGESPEED_DIR)out/$(BUILDTYPE)/
+
+INC =-I$(MOD_PAGESPEED_DIR)\
+ -I$(MOD_PAGESPEED_DIR)third_party/chromium/src/\
+ -I$(MOD_PAGESPEED_DIR)third_party/google-sparsehash/src\
+ -I$(MOD_PAGESPEED_DIR)third_party/google-sparsehash/gen/arch/linux/x64/include\
+ -I$(MOD_PAGESPEED_DIR)third_party/protobuf/src\
+ -I$(MOD_PAGESPEED_DIR)third_party/re2/src\
+ -I$(MOD_PAGESPEED_DIR)third_party/out/$(BUILDTYPE)/obj/gen\
+ -I$(MOD_PAGESPEED_DIR)third_party/apr/src/include/\
+ -I$(MOD_PAGESPEED_DIR)third_party/aprutil/src/include/\
+ -I$(MOD_PAGESPEED_DIR)third_party/apr/gen/arch/linux/x64/include/\
+ -I$(MOD_PAGESPEED_DIR)third_party/aprutil/gen/arch/linux/x64/include/\
+ -I$(PAGESPEED_OUT)obj/gen/\
+ -I$(PAGESPEED_OUT)obj/gen/protoc_out/instaweb/
+
+PSOL_LIBS = $(MOD_PAGESPEED_DIR)net/instaweb/automatic/pagespeed_automatic.a
+
+%.so: %.cc
+	g++ $(INC) -shared -o ats_speed.so -g -pipe -Wall -Werror -O3 -fpic *.cc -lstdc++  -lpthread -lrt $(PSOL_LIBS)
+
+all: gzip/gzip.so ats_speed.so
+
+install: all
+	$(TSXS) -i -o ats_speed.so 
+	cp gzip/gzip.so ./
+	$(TSXS) -i -o zip.so 
+
+clean:
+	rm -f *.lo *.so *.o

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/README.md
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/README.md b/plugins/experimental/ats_speed/README.md
new file mode 100644
index 0000000..ae5db92
--- /dev/null
+++ b/plugins/experimental/ats_speed/README.md
@@ -0,0 +1,52 @@
+![ScreenShot](http://www.atsspeed.com/images/xATSSPEED_logo_plusshout_728x91.png.pagespeed.ic.8mRpu2PXS0.png
+)
+
+Apache Traffic Server web content optimization plugin powered by Google PageSpeed
+
+http://www.atsspeed.com/
+
+To build, a simple 'make' should work. Use 'sudo make install' to install.
+Optionally, patching ATS with ethread.patch helps with eliminating latency that 
+sometimes gets induced when synchronising ATS's and PSOL's thread pools.
+
+After that, update ATS's plugin.config with:
+```
+ats_speed.so                                                                                 
+gzip.so /usr/local/etc/trafficserver/gzip.config  
+````
+gzip.so also is build with ats_speed, as it currently is a slightly
+modified version of the official one from the ATS repository.
+
+There are some hard-coded things in the plugin, these directories should exist:
+- /tmp/ps_log/ to exist
+- /tmp/ats_ps/ to exist
+
+Configuration files go into `/usr/local/etc/trafficserver/psol.`
+That folder is monitored, and changes to files in there are picked
+up immediately. A sample configuration:
+
+```
+# [host]
+[192.168.185.185]
+# Force traffic server to cache all origin responses
+override_expiry
+pagespeed FlushHtml on
+pagespeed RewriteLevel CoreFilters
+pagespeed EnableFilters rewrite_domains,trim_urls
+pagespeed MapRewriteDomain http://192.168.185.185 http://www.foo.com
+pagespeed MapOriginDomain http://192.168.185.185 http://www.foo.com
+pagespeed EnableFilters prioritize_critical_css,move_css_to_head,move_css_above_scripts
+pagespeed EnableFilters fallback_rewrite_css_urls,insert_img_dimensions,lazyload_images,local_storage_cache
+pagespeed EnableFilters prioritize_critical_css,rewrite_css
+pagespeed EnableFilters combine_javascript,combine_css
+```
+
+It also expects this in records.config from ATS to function:
+`CONFIG proxy.config.url_remap.pristine_host_hdr INT 0`
+
+You can view debug output of the plugin using `traffic_server -T ".*speed.*"`
+
+The current state compiles against PSOL 1.7.30.4-beta.
+Please note the this plugin will generate asserts when build against
+the debug version of mps (option->Merge from a different thread).
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_base_fetch.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_base_fetch.cc b/plugins/experimental/ats_speed/ats_base_fetch.cc
new file mode 100644
index 0000000..7788d8f
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_base_fetch.cc
@@ -0,0 +1,135 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+
+#include "ats_base_fetch.h"
+
+#include <ts/ts.h>
+
+#include "ats_server_context.h"
+
+#include "net/instaweb/util/public/string_util.h"
+#include "net/instaweb/util/public/string_writer.h"
+#include "net/instaweb/util/public/google_message_handler.h"
+
+
+using namespace net_instaweb;
+
+// TODO(oschaaf): rename is_resource_fetch -> write_raw_response_headers
+AtsBaseFetch::AtsBaseFetch(AtsServerContext* server_context,
+                           const net_instaweb::RequestContextPtr& request_ctx,
+                           TSVIO downstream_vio, TSIOBuffer downstream_buffer, bool is_resource_fetch) :
+  AsyncFetch(request_ctx),
+  server_context_(server_context),
+  done_called_(false),
+  last_buf_sent_(false),
+  references_(2),
+  downstream_vio_(downstream_vio),
+  downstream_buffer_(downstream_buffer),
+  is_resource_fetch_(is_resource_fetch),
+  downstream_length_(0),
+  txn_mutex_(TSVIOMutexGet(downstream_vio)) {
+  buffer_.reserve(1024 * 32);
+}
+
+AtsBaseFetch::~AtsBaseFetch() {
+  CHECK(references_ == 0);
+}
+
+// Should be called from the event loop,
+// and thus with the txn mutex held by ATS
+void AtsBaseFetch::Release() {
+  DecrefAndDeleteIfUnreferenced();
+}
+
+void AtsBaseFetch::Lock(){
+  TSMutexLock(txn_mutex_);
+}
+
+void AtsBaseFetch::Unlock() {
+  TSMutexUnlock(txn_mutex_);
+}
+
+bool AtsBaseFetch::HandleWrite(const StringPiece& sp, net_instaweb::MessageHandler* handler) {
+  ForwardData(sp, false, false);
+  return true;
+}
+
+bool AtsBaseFetch::HandleFlush( net_instaweb::MessageHandler* handler ) {
+  ForwardData("", true, false);
+  return true;
+}
+
+void AtsBaseFetch::HandleHeadersComplete() {
+  // oschaaf: ATS will currently send its response headers
+  // earlier than this will fire. So this has become a no-op.
+  // This implies that we can't support convert_meta_tags
+  TSDebug("ats-speed", "HeadersComplete()!");
+  // For resource fetches, we need to output the headers in raw HTTP format.
+  if (is_resource_fetch_) {
+    GoogleMessageHandler mh;
+    GoogleString s;
+    StringWriter string_writer(&s);
+    response_headers()->Add("Connection", "Close");
+    response_headers()->WriteAsHttp(&string_writer, &mh);
+    ForwardData(StringPiece(s.data(),s.size()), true, false);
+  }
+}
+
+void AtsBaseFetch::ForwardData(const StringPiece& sp, bool reenable, bool last) {
+  TSIOBufferBlock downstream_blkp;
+  char *downstream_buffer;
+  int64_t downstream_length;
+  int64_t to_write = sp.size();
+
+  Lock();
+  if (references_ == 2) {
+    while (to_write > 0) {
+      downstream_blkp = TSIOBufferStart(downstream_buffer_);
+      downstream_buffer = TSIOBufferBlockWriteStart(downstream_blkp, &downstream_length);
+      int64_t bytes_written = to_write > downstream_length ? downstream_length : to_write;
+      memcpy(downstream_buffer, sp.data() + (sp.size() - to_write), bytes_written);
+      to_write -= bytes_written;
+      downstream_length_ += bytes_written;
+      TSIOBufferProduce(downstream_buffer_, bytes_written);
+    }
+    CHECK(to_write == 0) << "to_write failure";
+    if (last) {
+      TSVIONBytesSet(downstream_vio_, downstream_length_);
+    }
+    if (reenable) { 
+      TSVIOReenable(downstream_vio_);
+    }
+  }
+  Unlock();
+}
+
+void AtsBaseFetch::HandleDone(bool success) {
+  CHECK(!done_called_);
+  CHECK(downstream_vio_);
+  TSDebug("ats-speed", "Done()!");
+
+  Lock();
+  done_called_ = true;
+  ForwardData("", true, true);
+  DecrefAndDeleteIfUnreferenced();
+  Unlock();
+}
+
+void AtsBaseFetch::DecrefAndDeleteIfUnreferenced() {
+  if (__sync_add_and_fetch(&references_, -1) == 0) {
+    delete this;
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_base_fetch.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_base_fetch.h b/plugins/experimental/ats_speed/ats_base_fetch.h
new file mode 100644
index 0000000..4d5f88a
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_base_fetch.h
@@ -0,0 +1,80 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#ifndef ATS_BASE_FETCH_H_
+#define ATS_BASE_FETCH_H_
+
+#include <string>
+
+#include <ts/ts.h>
+
+#include "ats_speed.h"
+
+#include "net/instaweb/http/public/async_fetch.h"
+#include "net/instaweb/http/public/headers.h"
+#include "net/instaweb/util/public/string.h"
+
+
+namespace net_instaweb {
+
+class AtsServerContext;
+class AbstractMutex;
+
+class AtsBaseFetch : public net_instaweb::AsyncFetch {
+
+public:
+  // TODO(oschaaf): change this to take the downstream buffer and vio
+  // instead of AtsData*. Also, make the bytes send a property
+  // of the fetch itself instead of tracking it on data.
+  // Doing so, would allow us to share this with the server intercept
+  // code for resources.
+  AtsBaseFetch(AtsServerContext* server_context,
+               const net_instaweb::RequestContextPtr& request_ctx,
+               TSVIO downstream_vio,
+               TSIOBuffer downstream_buffer,
+               bool is_resource_fetch);
+  
+  virtual ~AtsBaseFetch();
+  void Release();
+private:
+  virtual bool HandleWrite(const StringPiece& sp, net_instaweb::MessageHandler* handler);
+  virtual bool HandleFlush( net_instaweb::MessageHandler* handler);
+  virtual void HandleHeadersComplete();
+  virtual void HandleDone(bool success);
+  void Lock();
+  void Unlock();
+  void DecrefAndDeleteIfUnreferenced();
+  void ForwardData(const StringPiece& sp, bool reenable, bool last);
+  GoogleString buffer_;
+  AtsServerContext* server_context_;
+  bool done_called_;
+  bool last_buf_sent_;
+
+  // How many active references there are to this fetch. Starts at two,
+  // decremented once when Done() is called and once when Release() is called.
+  int references_;
+  TSVIO downstream_vio_;
+  TSIOBuffer downstream_buffer_;
+  bool is_resource_fetch_;
+  int64_t downstream_length_;
+
+  // We don't own this mutex
+  TSMutex txn_mutex_;
+};
+
+} /* ats_pagespeed */
+
+
+#endif /* ATS_BASE_FETCH_H_ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_beacon_intercept.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_beacon_intercept.cc b/plugins/experimental/ats_speed/ats_beacon_intercept.cc
new file mode 100644
index 0000000..175e218
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_beacon_intercept.cc
@@ -0,0 +1,350 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+// 'inspired' by the esi intercept code
+
+#include "ats_beacon_intercept.h"
+#include "ats_speed.h"
+#include "ats_server_context.h"
+
+#include "net/instaweb/system/public/system_request_context.h"
+
+#include <string>
+#include <limits.h>
+#include <strings.h>
+#include <stdio.h>
+
+using std::string;
+using namespace net_instaweb;
+
+#define DEBUG_TAG "ats_speed_beacon"
+
+struct InterceptCtx {
+  TSVConn net_vc;
+  TSCont contp;
+
+  struct IoHandle {
+    TSVIO vio;
+    TSIOBuffer buffer;
+    TSIOBufferReader reader;
+    IoHandle()
+      : vio(0), buffer(0), reader(0) { };
+    ~IoHandle() {
+      if (reader) {
+        TSIOBufferReaderFree(reader);
+      }
+      if (buffer) {
+        TSIOBufferDestroy(buffer);
+      }
+    };
+  };
+
+  IoHandle input;
+  IoHandle output;
+
+  TSHttpParser http_parser;
+  string body;
+  int req_content_len;
+  TSMBuffer req_hdr_bufp;
+  TSMLoc req_hdr_loc;
+  bool req_hdr_parsed;
+  bool initialized;
+  TransformCtx* request_context;
+  InterceptCtx(TSCont cont) 
+    : net_vc(0), contp(cont), input(), output(), body(""), req_content_len(0), req_hdr_bufp(0), req_hdr_loc(0),
+      req_hdr_parsed(false), initialized(false) {
+    http_parser = TSHttpParserCreate();
+  }
+
+  bool init(TSVConn vconn);
+
+  void setupWrite();
+
+  ~InterceptCtx() {
+    TSDebug(DEBUG_TAG, "[%s] Destroying continuation data", __FUNCTION__);
+    TSHttpParserDestroy(http_parser); 
+    if (req_hdr_loc) {
+      TSHandleMLocRelease(req_hdr_bufp, TS_NULL_MLOC, req_hdr_loc);
+    }
+    if (req_hdr_bufp) {
+      TSMBufferDestroy(req_hdr_bufp);
+    }
+    if (request_context) {
+      ats_ctx_destroy(request_context);
+      request_context = NULL;
+    }
+  };
+};
+
+bool
+InterceptCtx::init(TSVConn vconn)
+{
+  if (initialized) {
+    TSError("[%s] InterceptCtx already initialized!", __FUNCTION__);
+    return false;
+  }
+  
+  net_vc = vconn;
+
+  input.buffer = TSIOBufferCreate();
+  input.reader = TSIOBufferReaderAlloc(input.buffer);
+  input.vio = TSVConnRead(net_vc, contp, input.buffer, INT_MAX);
+
+  req_hdr_bufp = TSMBufferCreate();
+  req_hdr_loc = TSHttpHdrCreate(req_hdr_bufp);
+  TSHttpHdrTypeSet(req_hdr_bufp, req_hdr_loc, TS_HTTP_TYPE_REQUEST);
+
+  initialized = true;
+  TSDebug(DEBUG_TAG, "[%s] InterceptCtx initialized!", __FUNCTION__);
+  return true;
+}
+
+void
+InterceptCtx::setupWrite() {
+  TSAssert(output.buffer == 0);
+  output.buffer = TSIOBufferCreate();
+  output.reader = TSIOBufferReaderAlloc(output.buffer);
+  output.vio = TSVConnWrite(net_vc, contp, output.reader, INT_MAX);
+}
+
+// Parses out query params from the request.
+void ps_query_params_handler(StringPiece unparsed_uri, StringPiece* data) {
+  stringpiece_ssize_type question_mark_index = unparsed_uri.find("?");
+  if (question_mark_index == StringPiece::npos) {
+    *data = "";
+  } else {
+    *data = unparsed_uri.substr(
+        question_mark_index+1, unparsed_uri.size() - (question_mark_index+1));
+  }
+}
+
+static bool
+handleRead(InterceptCtx *cont_data, bool &read_complete) {
+  int avail = TSIOBufferReaderAvail(cont_data->input.reader);
+  if (avail == TS_ERROR) {
+    TSError("[%s] Error while getting number of bytes available", __FUNCTION__);
+    return false;
+  }
+  
+  TSDebug(DEBUG_TAG, "[%s] Parsed header, avail: %d", __FUNCTION__, avail);
+
+  int consumed = 0;
+  if (avail > 0) {
+    int64_t data_len;
+    const char *data;
+    TSIOBufferBlock block = TSIOBufferReaderStart(cont_data->input.reader);
+    while (block != NULL) {
+      data = TSIOBufferBlockReadStart(block, cont_data->input.reader, &data_len);
+      if (!cont_data->req_hdr_parsed) {
+        const char *endptr = data + data_len;
+        if (TSHttpHdrParseReq(cont_data->http_parser, cont_data->req_hdr_bufp, cont_data->req_hdr_loc,
+                               &data, endptr) == TS_PARSE_DONE) {
+          TSDebug(DEBUG_TAG, "[%s] Parsed header", __FUNCTION__);
+          TSMLoc content_len_loc = TSMimeHdrFieldFind(cont_data->req_hdr_bufp, cont_data->req_hdr_loc,
+                                                        TS_MIME_FIELD_CONTENT_LENGTH, -1);
+          
+          /*if (!content_len_loc) {
+            TSError("[%s] Error while searching content length header [%s]",
+                     __FUNCTION__, TS_MIME_FIELD_CONTENT_LENGTH);
+            return false;
+          }
+          if (!content_len_loc) {
+            TSError("[%s] request doesn't contain content length header [%s]",
+                     __FUNCTION__, TS_MIME_FIELD_CONTENT_TYPE);
+            return false;
+            }*/
+          if (!content_len_loc) {
+            cont_data->req_content_len = 0;
+          } else {
+            cont_data->req_content_len = TSMimeHdrFieldValueIntGet(cont_data->req_hdr_bufp, cont_data->req_hdr_loc,
+                                         content_len_loc, 0);
+            TSHandleMLocRelease(cont_data->req_hdr_bufp, cont_data->req_hdr_loc, content_len_loc);
+          }
+          TSDebug(DEBUG_TAG, "[%s] Got content length as %d", __FUNCTION__, cont_data->req_content_len);
+          if (cont_data->req_content_len < 0) {
+            TSError("[%s] Invalid content length [%d]", __FUNCTION__, cont_data->req_content_len);
+            return false;
+          }
+          if (endptr - data) {
+            TSDebug(DEBUG_TAG, "[%s] Appending %ld bytes to body", __FUNCTION__, static_cast<long int>(endptr - data));
+            cont_data->body.append(data, endptr - data);
+          }
+          cont_data->req_hdr_parsed = true;
+        }
+      } else {
+        //TSDebug(DEBUG_TAG, "[%s] Appending %" PRId64" bytes to body", __FUNCTION__, data_len);
+        cont_data->body.append(data, data_len);
+      }
+      consumed += data_len;
+      block = TSIOBufferBlockNext(block);
+    }
+  }
+  
+  TSIOBufferReaderConsume(cont_data->input.reader, consumed);
+
+  TSDebug(DEBUG_TAG, "[%s] Consumed %d bytes from input vio, avail: %d", __FUNCTION__, consumed, avail);
+  
+  // Modify the input VIO to reflect how much data we've completed.
+  TSVIONDoneSet(cont_data->input.vio, TSVIONDoneGet(cont_data->input.vio) + consumed);
+
+  if (static_cast<int>(cont_data->body.size()) == cont_data->req_content_len) {
+    TSDebug(DEBUG_TAG, "[%s] Completely read body of size %d", __FUNCTION__, cont_data->req_content_len);
+    read_complete = true;
+  } else {
+    read_complete = false;
+    TSDebug(DEBUG_TAG, "[%s] Reenabling input vio as %ld bytes still need to be read",
+             __FUNCTION__, static_cast<long int>(cont_data->req_content_len - cont_data->body.size()));
+    TSVIOReenable(cont_data->input.vio);
+  }
+  return true;
+}
+
+static bool
+processRequest(InterceptCtx *cont_data) {
+  string reply_header("HTTP/1.1 204 No Content\r\n");
+  int body_size = static_cast<int>(cont_data->body.size());
+  if (cont_data->req_content_len != body_size) {
+    TSError("[%s] Read only %d bytes of body; expecting %d bytes", __FUNCTION__, body_size,
+             cont_data->req_content_len);
+  }
+
+  char buf[64];
+  //snprintf(buf, 64, "%s: %d\r\n\r\n", TS_MIME_FIELD_CONTENT_LENGTH, body_size);
+  snprintf(buf, 64, "%s: %d\r\n\r\n", TS_MIME_FIELD_CONTENT_LENGTH, 0);
+  reply_header.append(buf);
+  reply_header.append("Cache-Control: max-age=0, no-cache");
+  //TSError("[%s] reply header: \n%s", __FUNCTION__, reply_header.data());
+
+  StringPiece query_param_beacon_data;
+  ps_query_params_handler(cont_data->request_context->url_string->c_str(), &query_param_beacon_data);
+  
+  GoogleString beacon_data = net_instaweb::StrCat(
+      query_param_beacon_data, "&", cont_data->body);
+  ServerContext* server_context = cont_data->request_context->server_context;
+  
+  SystemRequestContext* system_request_context = 
+      new SystemRequestContext(server_context->thread_system()->NewMutex(),
+                               server_context->timer(),
+                               80,
+                               "127.0.0.1");
+  
+  if (!server_context->HandleBeacon(
+          beacon_data,
+            cont_data->request_context->user_agent->c_str(),
+          net_instaweb::RequestContextPtr(system_request_context))) {
+    TSError("Beacon handling failure!");
+  } else {
+    //TSDebug(DEBUG_TAG,  "Beacon post data processed OK: [%s]", beacon_data.c_str());
+  }
+  
+  cont_data->setupWrite();
+  if (TSIOBufferWrite(cont_data->output.buffer, reply_header.data(), reply_header.size()) == TS_ERROR) {
+    TSError("[%s] Error while writing reply header", __FUNCTION__);
+    return false;
+  }
+  /*
+  if (TSIOBufferWrite(cont_data->output.buffer, cont_data->body.data(), body_size) == TS_ERROR) {
+    TSError("[%s] Error while writing content", __FUNCTION__);
+    return false;
+    }*/
+  int total_bytes_written = reply_header.size() + body_size;
+  TSDebug(DEBUG_TAG, "[%s] Wrote reply of size %d", __FUNCTION__, total_bytes_written);
+  TSVIONBytesSet(cont_data->output.vio, total_bytes_written);
+  
+  TSVIOReenable(cont_data->output.vio);
+  return true;
+}
+
+static int
+txn_intercept(TSCont contp, TSEvent event, void *edata) {
+    TSDebug(DEBUG_TAG, "[%s] Received event: %d", __FUNCTION__, (int)event);
+
+  InterceptCtx *cont_data = static_cast<InterceptCtx *>(TSContDataGet(contp));
+  bool read_complete = false;
+  bool shutdown = false;
+  switch (event) {
+  case TS_EVENT_NET_ACCEPT:
+    TSDebug(DEBUG_TAG, "[%s] Received net accept event", __FUNCTION__);
+    TSAssert(cont_data->initialized == false);
+    if (!cont_data->init(static_cast<TSVConn>(edata))) {
+      TSError("[%s] Could not initialize continuation data!", __FUNCTION__);
+      return 1;
+    }
+    break;
+  case TS_EVENT_VCONN_READ_READY:
+    TSDebug(DEBUG_TAG, "[%s] Received read ready event", __FUNCTION__);
+    if (!handleRead(cont_data, read_complete)) {
+      TSError("[%s] Error while reading from input vio", __FUNCTION__);
+      //return 0;
+      read_complete = true;
+    }
+    break;
+  case TS_EVENT_VCONN_READ_COMPLETE:
+  case TS_EVENT_VCONN_EOS:
+    // intentional fall-through
+    TSDebug(DEBUG_TAG, "[%s] Received read complete/eos event %d", __FUNCTION__, event);
+    read_complete = true;
+    break;
+  case TS_EVENT_VCONN_WRITE_READY:
+    TSDebug(DEBUG_TAG, "[%s] Received write ready event", __FUNCTION__);
+    break;
+  case TS_EVENT_VCONN_WRITE_COMPLETE:
+    TSDebug(DEBUG_TAG, "[%s] Received write complete event", __FUNCTION__);
+    shutdown = true;
+    break;
+  case TS_EVENT_ERROR:
+    // todo: do some error handling here
+    TSDebug(DEBUG_TAG, "[%s] Received error event; going to shutdown, event: %d", __FUNCTION__, event);    
+    TSError("[%s] Received error event; going to shutdown, event: %d", __FUNCTION__, event);
+    shutdown = true;
+    break;
+  default:
+    break;
+  }
+
+  if (read_complete) {
+    if (!processRequest(cont_data)) {
+      TSError("[%s] Failed to process process", __FUNCTION__);
+    } else {
+      TSDebug(DEBUG_TAG, "[%s] Processed request successfully", __FUNCTION__);
+    }
+  }
+
+  if (shutdown) {
+    TSDebug(DEBUG_TAG, "[%s] Completed request processing. Shutting down...", __FUNCTION__);
+    if (cont_data->net_vc) {
+      TSVConnClose(cont_data->net_vc);
+    }
+    delete cont_data;
+    TSContDestroy(contp);
+  }
+
+  return 1;
+}
+
+bool
+hook_beacon_intercept(TSHttpTxn txnp) {
+  TSCont contp = TSContCreate(txn_intercept, TSMutexCreate());
+  if (!contp) {
+    TSError("[%s] Could not create intercept request", __FUNCTION__);
+    return false;
+  }
+  InterceptCtx *cont_data = new InterceptCtx(contp);
+  cont_data->request_context = get_transaction_context(txnp);
+  TSContDataSet(contp, cont_data);
+  TSHttpTxnIntercept(contp, txnp);
+  TSDebug(DEBUG_TAG, "[%s] Setup server intercept successfully", __FUNCTION__);
+  return true;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_beacon_intercept.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_beacon_intercept.h b/plugins/experimental/ats_speed/ats_beacon_intercept.h
new file mode 100644
index 0000000..21a7ea0
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_beacon_intercept.h
@@ -0,0 +1,23 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#ifndef _ATS_BEACON_INTERCEPT_H
+#define _ATS_BEACON_INTERCEPT_H
+
+#include "ts/ts.h"
+
+bool hook_beacon_intercept(TSHttpTxn txnp);
+
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_config.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_config.cc b/plugins/experimental/ats_speed/ats_config.cc
new file mode 100644
index 0000000..c9dd73d
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_config.cc
@@ -0,0 +1,196 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#include "ats_config.h"
+
+#include <ts/ts.h>
+#include <fstream>
+
+#include "net/instaweb/util/public/string_util.h"
+
+#include "ats_message_handler.h"
+#include "ats_rewrite_options.h"
+
+namespace net_instaweb {
+
+using namespace std;
+
+
+
+void ltrim_if(string& s, int (* fp) (int)) {
+  for (size_t i = 0; i < s.size();) {
+    if (fp(s[i])) {
+      s.erase(i,1);
+    } else  {
+      break;
+    }
+  }
+}
+
+void rtrim_if(string& s, int (* fp) (int)) {
+  for (ssize_t i = (ssize_t)s.size() - 1; i >= 0; i--) {
+    if (fp(s[i])) {
+      s.erase(i,1);
+    } else  {
+      break;
+    }
+  }
+}
+
+void trim_if(string& s, int (* fp) (int)) {
+  ltrim_if(s, fp);
+  rtrim_if(s, fp);
+}
+
+vector<string> tokenize(const string &s, int (* fp) (int)) {
+  vector<string> r;
+  string tmp;
+
+  for (size_t i = 0; i < s.size(); i++) {
+    if ( fp(s[i]) ) {
+      if ( tmp.size()  ) {
+        r.push_back(tmp);
+        tmp = "";
+      }
+    } else {
+      tmp += s[i];
+    }
+  }
+
+  if ( tmp.size()  ) {
+    r.push_back(tmp);
+  }
+
+  return r;
+}
+
+AtsConfig::AtsConfig(AtsThreadSystem* thread_system)
+      : thread_system_(thread_system) {
+  AddHostConfig(new AtsHostConfig(GoogleString("(XXXXXX)"), new AtsRewriteOptions(thread_system_)));
+}
+
+AtsConfig::~AtsConfig() {
+  for (size_t i = 0; i < host_configurations_.size(); i++) {
+    delete host_configurations_[i];
+    host_configurations_.clear();
+  }
+}
+
+void AtsConfig::AddHostConfig(AtsHostConfig* hc){
+  host_configurations_.push_back(hc);
+}
+
+AtsHostConfig::~AtsHostConfig() {
+  if (options_ != NULL) {
+    delete options_;
+    options_ = NULL;
+  }
+}
+
+AtsHostConfig * AtsConfig::Find(const char * host, int host_length) {
+  AtsHostConfig * host_configuration = host_configurations_[0];
+
+  std::string shost(host, host_length);
+
+  for (size_t i = 1; i < host_configurations_.size(); i++ ) {
+    if (host_configurations_[i]->host() == shost){
+      host_configuration = host_configurations_[i];
+      break;
+    }
+  }
+
+  return host_configuration;
+}
+
+bool AtsConfig::Parse(const char * path ) {
+  string pathstring(path);
+
+  // If we have a path and it's not an absolute path, make it relative to the
+  // configuration directory.
+  if (!pathstring.empty() && pathstring[0] != '/') {
+    pathstring.assign(TSConfigDirGet());
+    pathstring.append("/");
+    pathstring.append(path);
+  }
+
+  trim_if(pathstring, isspace);
+
+  AtsHostConfig* current_host_configuration = host_configurations_[0];
+
+  if (pathstring.empty())  {
+    TSError("Empty path passed in AtsConfig::Parse");
+    return false;
+  }
+
+  path = pathstring.c_str();
+  std::ifstream f;
+
+  size_t lineno = 0;
+
+  f.open(path, std::ios::in);
+
+  if (!f.is_open()) {
+    TSError("could not open file [%s], skip",path);
+    return false;
+  }
+
+
+  while (!f.eof()) {
+    std::string line;
+    getline(f, line);
+    ++lineno;
+
+    trim_if(line, isspace);
+    if (line.size() == 0) {
+      continue;
+    }
+    if (line[0] == '#') {
+      continue;
+    }
+    
+    vector<string> v = tokenize( line, isspace );
+    if (v.size() == 0)
+      continue;
+    GoogleString msg;
+    AtsMessageHandler handler(thread_system_->NewMutex());
+    if (v.size() == 1) {
+      string token = v[0];
+      if ((token[0] == '[') && (token[token.size()-1] == ']')) {
+        GoogleString current_host = token.substr(1, token.size() - 2);
+        current_host_configuration = new AtsHostConfig(current_host, new AtsRewriteOptions(thread_system_));
+        AddHostConfig(current_host_configuration);
+      } else if (StringCaseEqual(token,"override_expiry")) {
+        current_host_configuration->set_override_expiry(true);
+      } else {
+        msg = "unknown single token on a line";
+      }
+    } else {
+      global_settings settings;
+      v.erase (v.begin());
+      const char* err = current_host_configuration->options()->ParseAndSetOptions(v, &handler, settings);
+      if (err) {
+        msg.append(err);
+      }
+    }
+    if (msg.size() > 0) {
+      TSDebug("ats-speed", "Error parsing line [%s]: [%s]", line.c_str(), msg.c_str());
+    }
+  }
+
+  return true;
+}
+
+
+} //  namespace net_instaweb

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_config.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_config.h b/plugins/experimental/ats_speed/ats_config.h
new file mode 100644
index 0000000..bc6e7f8
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_config.h
@@ -0,0 +1,82 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#ifndef ATS_CONFIG_H_
+#define ATS_CONFIG_H_
+
+#include <string>
+#include <vector>
+
+#include <ts/ts.h>
+
+#include "ats_thread_system.h"
+
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+
+
+namespace net_instaweb {
+
+class AtsRewriteOptions;
+
+class AtsHostConfig {
+public:
+  explicit AtsHostConfig(const GoogleString & host, AtsRewriteOptions* options)
+      : host_(host)
+      , options_(options)
+  {
+  }
+  virtual ~AtsHostConfig();
+
+  inline GoogleString host() { return host_; }
+  inline AtsRewriteOptions* options() { return options_; }
+  inline bool override_expiry() { return override_expiry_; }
+  inline void set_override_expiry(bool x) { override_expiry_ = x; }
+private:
+  GoogleString host_;
+  AtsRewriteOptions* options_;
+  bool override_expiry_;
+  DISALLOW_COPY_AND_ASSIGN(AtsHostConfig);
+}; // class AtsHostConfig
+
+class AtsConfig {
+  friend class AtsHostConfig;
+public:
+  explicit AtsConfig(AtsThreadSystem* thread_system);
+  virtual ~AtsConfig();
+  
+  // TODO(oschaaf): destructor??
+  bool Parse(const char * path);
+  AtsHostConfig * Find(const char * host, int host_length);
+  inline AtsHostConfig * GlobalConfiguration() {
+    return host_configurations_[0];
+  }
+  AtsThreadSystem* thread_system() {
+    return thread_system_;
+  }
+
+private:
+  void AddHostConfig(AtsHostConfig* hc);
+
+  std::vector<AtsHostConfig *> host_configurations_;
+  AtsThreadSystem* thread_system_;
+  //todo: destructor. delete owned host configurations
+  DISALLOW_COPY_AND_ASSIGN(AtsConfig);
+}; // class Configuration
+
+
+}  // namespace net_instaweb
+
+#endif  // ATS_CONFIG_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_demo_filter.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_demo_filter.cc b/plugins/experimental/ats_speed/ats_demo_filter.cc
new file mode 100644
index 0000000..18944d5
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_demo_filter.cc
@@ -0,0 +1,82 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#include "net/instaweb/rewriter/public/add_head_filter.h"
+#include "net/instaweb/htmlparse/public/html_parse.h"
+#include "net/instaweb/htmlparse/public/html_element.h"
+#include <string>
+
+#include <ts/ts.h>
+#include "ats_demo_filter.h"
+
+namespace net_instaweb {
+  const char* AtsDemoFilter::kPoweredByHtml =
+      "<div id=\"weamp_poweredby\" style=\"bottom:0; height:30px; left:0;  width:100%;\">"
+      "<div style=\"line-height:30px; margin:0 auto; width:100%; text-align:center; \">"
+      "<a target=\"_blank\" title=\"Google PageSpeed optimization demo brought to you by We-Amp\" href=\"http://www.we-amp.com/\">Google PageSpeed optimization demo by We-Amp</a>"
+      "</div>"
+      "</div>";
+
+
+
+AtsDemoFilter::AtsDemoFilter(HtmlParse* parser, bool banner) :
+    parser_(parser),
+    banner_(banner)
+{
+}
+
+void AtsDemoFilter::StartElement(HtmlElement* element) {
+  if (banner_ && element->keyword() == HtmlName::kBody) {
+    HtmlNode* el = parser_->NewCharactersNode(NULL, AtsDemoFilter::kPoweredByHtml);
+    parser_->InsertNodeBeforeCurrent(el);
+  }
+  
+  if (element->keyword() == HtmlName::kA || element->keyword() == HtmlName::kBase
+      || element->keyword() == HtmlName::kForm|| element->keyword() == HtmlName::kImg
+      || element->keyword() == HtmlName::kLink || element->keyword() == HtmlName::kScript) { 
+    HtmlElement::AttributeList* attributes = element->mutable_attributes();
+    for (HtmlElement::AttributeIterator i(attributes->begin());
+         i != attributes->end(); ++i) {
+      
+      HtmlElement::Attribute& attribute = *i;
+      if (attribute.keyword() == HtmlName::kAction || attribute.keyword() == HtmlName::kHref 
+          || attribute.keyword() == HtmlName::kSrc) {
+        const char * attribute_value = NULL;
+        if ( attribute.DecodedValueOrNull() != NULL ) {
+          attribute_value = attribute.DecodedValueOrNull();
+        } else {
+          attribute_value = attribute.escaped_value();
+        }
+        
+        if ( attribute_value != NULL) {
+          GoogleUrl url( attribute_value );
+          if (url.IsWebValid()) {
+            if (url.Host() == from_domain_) {
+              StringPiece scheme = url.Scheme();
+              StringPiece host = to_domain_.c_str();
+              StringPiece pathAndQuery = url.PathAndLeaf();
+              GoogleString rewritten = StrCat(scheme,"://", host, pathAndQuery);
+              attribute.SetValue(rewritten.c_str());
+              break;
+            }
+          }
+        }
+      }   
+    }      
+  }
+}
+
+
+}  // namespace net_instaweb

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_demo_filter.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_demo_filter.h b/plugins/experimental/ats_speed/ats_demo_filter.h
new file mode 100644
index 0000000..dc5aa78
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_demo_filter.h
@@ -0,0 +1,57 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#ifndef ATS_DEMO_FILTER_H_
+#define ATS_DEMO_FILTER_H_
+
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+#include "net/instaweb/htmlparse/public/empty_html_filter.h"
+#include "net/instaweb/util/public/atom.h"
+#include "net/instaweb/htmlparse/public/html_parse.h"
+#include "net/instaweb/htmlparse/public/html_element.h"
+#include <string>
+
+using base::StringPiece;
+
+namespace net_instaweb {
+
+
+  
+  class AtsDemoFilter : public EmptyHtmlFilter {
+ public:
+    static const char* kPoweredByHtml;
+
+    explicit AtsDemoFilter(HtmlParse* parser, bool banner);
+    virtual void StartElement(HtmlElement* element);
+    virtual const char* Name() const { return "AtsDemo"; }
+    // TODO: move to constructor
+    void set_domains(const StringPiece& to_domain, const StringPiece& from_domain) 
+    { 
+      to_domain.CopyToString(&to_domain_); 
+      from_domain.CopyToString(&from_domain_); 
+    }
+
+  private:
+    std::string to_domain_;
+    std::string from_domain_;
+    HtmlParse* parser_;
+    bool banner_;
+    DISALLOW_COPY_AND_ASSIGN(AtsDemoFilter);
+  };
+
+}  // namespace net_instaweb
+
+#endif  // ATS_DEMO_FILTER_H_

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_header_utils.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_header_utils.cc b/plugins/experimental/ats_speed/ats_header_utils.cc
new file mode 100644
index 0000000..74e46c9
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_header_utils.cc
@@ -0,0 +1,88 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#include "ats_header_utils.h"
+
+GoogleString get_header(TSMBuffer bufp, TSMLoc hdr_loc, const char * header_name)
+{
+  const char * val = NULL;
+  int val_len;
+  TSMLoc field_loc = TSMimeHdrFieldFind( bufp, hdr_loc, header_name, -1);
+
+  if (field_loc) {
+    val = TSMimeHdrFieldValueStringGet (bufp, hdr_loc, field_loc, 0, &val_len);
+    TSHandleMLocRelease(bufp,hdr_loc,field_loc);
+    return GoogleString(val,val_len);
+  }
+
+  return GoogleString("");
+}
+
+void unset_header(TSMBuffer bufp, TSMLoc hdr_loc, const char * header_name)
+{
+  TSMLoc field_loc = TSMimeHdrFieldFind( bufp, hdr_loc, header_name, -1);
+
+  if (field_loc) {
+    TSMimeHdrFieldDestroy(bufp, hdr_loc, field_loc);
+    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+  }
+}
+
+void hide_accept_encoding(TSMBuffer reqp, TSMLoc hdr_loc, const char * hidden_header_name)
+{
+  TSMLoc field = TSMimeHdrFieldFind(reqp, hdr_loc, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
+  while (field) {
+    TSMLoc tmp;
+    tmp = TSMimeHdrFieldNextDup(reqp, hdr_loc, field);
+    TSMimeHdrFieldNameSet(reqp, hdr_loc, field, hidden_header_name, -1);
+    TSHandleMLocRelease(reqp, hdr_loc, field);
+    field = tmp;
+  }
+}
+
+void restore_accept_encoding(TSMBuffer reqp, TSMLoc hdr_loc, const char * hidden_header_name)
+{
+  TSMLoc field = TSMimeHdrFieldFind(reqp, hdr_loc, hidden_header_name, -1);
+
+  while (field) {
+    TSMLoc tmp;
+    tmp = TSMimeHdrFieldNextDup(reqp, hdr_loc, field);
+    TSMimeHdrFieldNameSet(reqp, hdr_loc, field, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
+    TSHandleMLocRelease(reqp, hdr_loc, field);
+    field = tmp;
+  }
+}
+
+void set_header(TSMBuffer bufp, TSMLoc hdr_loc, const char * header_name, const char * header_value)
+{
+  TSMLoc field_loc = TSMimeHdrFieldFind( bufp, hdr_loc, header_name, -1);
+
+  if (field_loc) {
+    TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, header_value, -1);
+  } else {    
+    if ( TSMimeHdrFieldCreate(bufp, hdr_loc, &field_loc) == TS_SUCCESS ) {
+      TSMimeHdrFieldNameSet(bufp, hdr_loc, field_loc, header_name, -1);
+      TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
+      TSMimeHdrFieldValueStringSet(bufp,hdr_loc,field_loc,-1,header_value,-1);
+    } else {
+      TSError("field creation error for field [%s]", header_name);
+      return;
+    }
+  }
+
+  if (field_loc) {
+    TSHandleMLocRelease(bufp,hdr_loc,field_loc);
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_header_utils.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_header_utils.h b/plugins/experimental/ats_speed/ats_header_utils.h
new file mode 100644
index 0000000..7aa6f7d
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_header_utils.h
@@ -0,0 +1,33 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#ifndef ATS_HEADER_UTILS_H
+#define ATS_HEADER_UTILS_H
+
+#include <string>
+
+#include <ts/ts.h>
+
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+
+
+GoogleString get_header(TSMBuffer bufp, TSMLoc hdr_loc, const char * header_name);
+void unset_header(TSMBuffer bufp, TSMLoc hdr_loc, const char * header_name);
+void hide_accept_encoding(TSMBuffer reqp, TSMLoc hdr_loc, const char * hidden_header_name);
+void restore_accept_encoding(TSMBuffer reqp, TSMLoc hdr_loc, const char * hidden_header_name);
+void set_header(TSMBuffer bufp, TSMLoc hdr_loc, const char * header_name, const char * header_value);
+
+#endif //  ATS_HEADER_UTILS_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_log_message_handler.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_log_message_handler.cc b/plugins/experimental/ats_speed/ats_log_message_handler.cc
new file mode 100644
index 0000000..635a841
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_log_message_handler.cc
@@ -0,0 +1,93 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#include "ats_log_message_handler.h"
+
+#include <ts/ts.h>
+
+#include <unistd.h>
+
+#include <limits>
+#include <string>
+
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "net/instaweb/public/version.h"
+#include "net/instaweb/util/public/string_util.h"
+
+// Make sure we don't attempt to use LOG macros here, since doing so
+// would cause us to go into an infinite log loop.
+#undef LOG
+#define LOG USING_LOG_HERE_WOULD_CAUSE_INFINITE_RECURSION
+
+namespace {
+
+bool LogMessageHandler(int severity, const char* file, int line,
+                       size_t message_start, const GoogleString& str) {
+  GoogleString message = str;
+  if (severity == logging::LOG_FATAL) {
+    if (base::debug::BeingDebugged()) {
+      base::debug::BreakDebugger();
+    } else {
+      base::debug::StackTrace trace;
+      std::ostringstream stream;
+      trace.OutputToStream(&stream);
+      message.append(stream.str());
+    }
+  }
+
+  // Trim the newline off the end of the message string.
+  size_t last_msg_character_index = message.length() - 1;
+  if (message[last_msg_character_index] == '\n') {
+    message.resize(last_msg_character_index);
+  }
+
+  TSDebug("ats-speed-vlog", "[%s] %s",
+                net_instaweb::kModPagespeedVersion,
+                message.c_str());
+
+  if (severity == logging::LOG_FATAL) {
+    // Crash the process to generate a dump.
+    base::debug::BreakDebugger();
+  }
+
+  return true;
+}
+
+}  // namespace
+
+
+namespace net_instaweb {
+
+namespace log_message_handler {
+
+
+const int kDebugLogLevel = -2;
+
+void Install() {
+  logging::SetLogMessageHandler(&LogMessageHandler);
+
+  // All VLOG(2) and higher will be displayed as DEBUG logs if the nginx log
+  // level is DEBUG.
+  // TODO(oschaaf): from config
+  //if (log->log_level >= NGX_LOG_DEBUG) {
+    logging::SetMinLogLevel(-2);
+  //}
+}
+
+}  // namespace log_message_handler
+
+}  // namespace net_instaweb

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_log_message_handler.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_log_message_handler.h b/plugins/experimental/ats_speed/ats_log_message_handler.h
new file mode 100644
index 0000000..1a113aa
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_log_message_handler.h
@@ -0,0 +1,28 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#ifndef ATS_LOG_MESSAGE_HANDLER_H_
+#define ATS_LOG_MESSAGE_HANDLER_H_
+
+
+namespace net_instaweb {
+
+  namespace log_message_handler {
+    void Install();
+  }  // namespace log_message_handler
+
+}  // namespace net_instaweb
+
+#endif  // ATS_LOG_MESSAGE_HANDLER_H_

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_message_handler.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_message_handler.cc b/plugins/experimental/ats_speed/ats_message_handler.cc
new file mode 100644
index 0000000..7f21660
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_message_handler.cc
@@ -0,0 +1,107 @@
+// Copyright 2013 Google Inc.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Author: oschaaf@gmail.com (Otto van der Schaaf)
+
+#include "ats_message_handler.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+#include "net/instaweb/util/public/abstract_mutex.h"
+#include "net/instaweb/util/public/debug.h"
+#include "net/instaweb/util/public/shared_circular_buffer.h"
+#include "net/instaweb/util/public/string_util.h"
+#include "net/instaweb/public/version.h"
+#include "pagespeed/kernel/base/posix_timer.h"
+#include "pagespeed/kernel/base/time_util.h"
+   
+
+namespace {
+
+// This will be prefixed to every logged message.
+const char kModuleName[] = "ats_pagespeed";
+
+}  // namespace
+
+namespace net_instaweb {
+
+AtsMessageHandler::AtsMessageHandler(AbstractMutex* mutex)
+    : mutex_(mutex),
+      buffer_(NULL) {
+  SetPidString(static_cast<int64>(getpid()));
+}
+
+
+bool AtsMessageHandler::Dump(Writer* writer) {
+  // Can't dump before SharedCircularBuffer is set up.
+  if (buffer_ == NULL) {
+    return false;
+  }
+  return buffer_->Dump(writer, &handler_);
+}
+
+void AtsMessageHandler::set_buffer(SharedCircularBuffer* buff) {
+  ScopedMutex lock(mutex_.get());
+  buffer_ = buff;
+}
+
+void AtsMessageHandler::MessageVImpl(MessageType type, const char* msg,
+                                     va_list args) {
+  GoogleString formatted_message = Format(msg, args);
+  
+  TSDebug("ats-speed", "[%s %s] %s", kModuleName, kModPagespeedVersion,
+          formatted_message.c_str());
+ 
+  // Prepare a log message for the SharedCircularBuffer only.
+  // Prepend time and severity to message.
+  // Format is [time] [severity] [pid] message.
+  GoogleString message;
+  GoogleString time;
+  PosixTimer timer;
+  if (!ConvertTimeToString(timer.NowMs(), &time)) {
+    time = "?";
+  }
+  
+  StrAppend(&message, "[", time, "] ",
+            "[", MessageTypeToString(type), "] ");
+  StrAppend(&message, pid_string_, " ", formatted_message, "\n");
+  {
+    ScopedMutex lock(mutex_.get());
+    if (buffer_ != NULL) {
+      buffer_->Write(message);
+    }
+  }
+}
+
+void AtsMessageHandler::FileMessageVImpl(MessageType type, const char* file,
+                                         int line, const char* msg,
+                                         va_list args) {
+  GoogleString formatted_message = Format(msg, args);
+  TSDebug("ats-speed", "[%s %s] %s:%d:%s",
+                kModuleName, kModPagespeedVersion, file, line,
+                formatted_message.c_str());
+}
+
+// TODO(sligocki): It'd be nice not to do so much string copying.
+GoogleString AtsMessageHandler::Format(const char* msg, va_list args) {
+  GoogleString buffer;
+
+  // Ignore the name of this routine: it formats with vsnprintf.
+  // See base/stringprintf.cc.
+  StringAppendV(&buffer, msg, args);
+  return buffer;
+}
+
+}  // namespace net_instaweb

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_message_handler.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_message_handler.h b/plugins/experimental/ats_speed/ats_message_handler.h
new file mode 100644
index 0000000..fb40590
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_message_handler.h
@@ -0,0 +1,68 @@
+// Copyright 2013 Google Inc.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Author: oschaaf@gmail.com (Otto van der Schaaf)
+
+#ifndef NGX_MESSAGE_HANDLER_H_
+#define NGX_MESSAGE_HANDLER_H_
+
+#include <ts/ts.h>
+#include <cstdarg>
+
+#include "net/instaweb/util/public/basictypes.h"
+#include "net/instaweb/util/public/google_message_handler.h"
+#include "net/instaweb/util/public/message_handler.h"
+#include "net/instaweb/util/public/scoped_ptr.h"
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+
+namespace net_instaweb {
+
+  class AbstractMutex;
+  class SharedCircularBuffer;
+  class Timer;
+  class Writer;
+
+  class AtsMessageHandler : public GoogleMessageHandler {
+ public:
+    explicit AtsMessageHandler(AbstractMutex* mutex);
+
+    void set_buffer(SharedCircularBuffer* buff);
+
+    void SetPidString(const int64 pid) {
+      pid_string_ = StrCat("[", Integer64ToString(pid), "]");
+    }
+    // Dump contents of SharedCircularBuffer.
+    bool Dump(Writer* writer);
+
+ protected:
+    virtual void MessageVImpl(MessageType type, const char* msg, va_list args);
+
+    virtual void FileMessageVImpl(MessageType type, const char* filename,
+                                  int line, const char* msg, va_list args);
+
+ private:
+    GoogleString Format(const char* msg, va_list args);
+
+    scoped_ptr<AbstractMutex> mutex_;
+    GoogleString pid_string_;
+    GoogleMessageHandler handler_;
+    SharedCircularBuffer* buffer_;
+
+    DISALLOW_COPY_AND_ASSIGN(AtsMessageHandler);
+  };
+
+}  // namespace net_instaweb
+
+#endif  // NGX_MESSAGE_HANDLER_H_

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_process_context.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_process_context.cc b/plugins/experimental/ats_speed/ats_process_context.cc
new file mode 100644
index 0000000..2cc47a7
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_process_context.cc
@@ -0,0 +1,76 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#include "ats_process_context.h"
+
+#include <vector>
+
+#include "ats_rewrite_driver_factory.h"
+#include "ats_server_context.h"
+#include "ats_message_handler.h"
+#include "ats_thread_system.h"
+
+#include "net/instaweb/automatic/public/proxy_fetch.h"
+#include "net/instaweb/util/public/pthread_shared_mem.h"
+
+namespace net_instaweb {
+
+AtsProcessContext::AtsProcessContext() {
+  AtsThreadSystem* ts = new AtsThreadSystem();
+  message_handler_.reset(new AtsMessageHandler(ts->NewMutex()));
+  driver_factory_.reset(new AtsRewriteDriverFactory(ts));
+  server_context_ = driver_factory()->MakeAtsServerContext();
+
+  AtsRewriteOptions* root_options_ = (AtsRewriteOptions*)driver_factory_->default_options();
+  AtsRewriteOptions* server_options = root_options_->Clone();
+  AtsRewriteOptions* options = new AtsRewriteOptions(driver_factory_->thread_system());
+  server_options->Merge(*options);
+  delete options;
+
+  server_context_->global_options()->Merge(*server_options);
+  delete server_options;
+
+  message_handler_->Message(kInfo,"global default options:\r\n[%s]",driver_factory_->default_options()->OptionsToString().c_str());
+  message_handler_->Message(kInfo,"server ctx default options:\r\n[%s]",server_context_->global_options()->OptionsToString().c_str());
+  std::vector<SystemServerContext*> server_contexts;
+  server_contexts.push_back(server_context_);
+  
+  //Statistics* statistics =
+  //    driver_factory_->MakeGlobalSharedMemStatistics(*(SystemRewriteOptions*)server_context_->global_options());
+  GoogleString error_message;
+  int error_index = -1;
+  Statistics* global_statistics = NULL;
+  driver_factory_.get()->PostConfig(
+      server_contexts, &error_message, &error_index, &global_statistics);
+  if (error_index != -1) {
+     server_contexts[error_index]->message_handler()->Message(
+         kError, "ngx_pagespeed is enabled. %s", error_message.c_str());
+     //return NGX_ERROR;
+     CHECK(false);
+  }
+  
+  AtsRewriteDriverFactory::InitStats(global_statistics);
+    
+  driver_factory()->RootInit();
+  driver_factory()->ChildInit();
+
+  proxy_fetch_factory_.reset(new ProxyFetchFactory(server_context_));
+  message_handler_->Message(kInfo, "Process context constructed");
+}
+
+AtsProcessContext::~AtsProcessContext() {
+}
+
+}  // namespace net_instaweb

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/083abd4f/plugins/experimental/ats_speed/ats_process_context.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/ats_process_context.h b/plugins/experimental/ats_speed/ats_process_context.h
new file mode 100644
index 0000000..aae2118
--- /dev/null
+++ b/plugins/experimental/ats_speed/ats_process_context.h
@@ -0,0 +1,49 @@
+// Copyright 2013 We-Amp B.V.
+//
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#ifndef ATS_PROCESS_CONTEXT_H_
+#define ATS_PROCESS_CONTEXT_H_
+
+#include "net/instaweb/util/public/google_message_handler.h"
+#include "net/instaweb/util/public/message_handler.h"
+#include "net/instaweb/util/public/scoped_ptr.h"
+
+namespace net_instaweb {
+
+class AtsRewriteDriverFactory;
+class ProxyFetchFactory;
+class AtsServerContext;
+
+class AtsProcessContext {
+ public:
+  explicit AtsProcessContext();
+  virtual ~AtsProcessContext();
+
+  // TODO(oschaaf): const correctness
+  MessageHandler* message_handler() { return message_handler_.get(); }
+  AtsRewriteDriverFactory* driver_factory() { return driver_factory_.get(); }
+  ProxyFetchFactory* proxy_fetch_factory() { return proxy_fetch_factory_.get(); }
+  AtsServerContext* server_context() { return server_context_; }
+ private:
+  scoped_ptr<MessageHandler> message_handler_;
+  scoped_ptr<AtsRewriteDriverFactory> driver_factory_;
+  scoped_ptr<ProxyFetchFactory> proxy_fetch_factory_;
+  AtsServerContext* server_context_;
+};
+
+
+}  // namespace net_instaweb
+
+#endif // ATS_PROCESS_CONTEXT_H_