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/20 22:57:45 UTC

[5/7] ats_pagespeed: rename ats_speed -> ats_pagespeed

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/ats_rewrite_options.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/ats_rewrite_options.h b/plugins/experimental/ats_pagespeed/ats_rewrite_options.h
new file mode 100644
index 0000000..4a39dcd
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/ats_rewrite_options.h
@@ -0,0 +1,103 @@
+/** @file
+
+    A brief file description
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#ifndef ATS_REWRITE_OPTIONS_H_
+#define ATS_REWRITE_OPTIONS_H_
+
+#include <string>
+#include <vector>
+
+#include "net/instaweb/util/public/string.h"
+#include "net/instaweb/util/public/string_util.h"
+#include "net/instaweb/rewriter/public/rewrite_options.h"
+#include "net/instaweb/system/public/system_rewrite_options.h"
+
+
+//#include "ats_configuration.h"
+
+
+namespace net_instaweb {
+
+class ThreadSystem;
+
+struct global_settings {
+  global_settings()
+  : info_urls_local_only(true)
+  , use_native_fetcher(false)
+      , use_per_vhost_statistics(true)
+      , message_buffer_size(1024*128)
+      , shm_cache_size_kb(0)
+      //, rate_limit_background_fetches(true)
+      //, force_caching(false)
+      //, list_outstanding_urls_on_error(false)
+      //, track_original_content_length(false)
+  {
+  }
+  bool info_urls_local_only;
+  bool use_native_fetcher;
+  bool use_per_vhost_statistics;
+  int message_buffer_size;
+  //bool rate_limit_background_fetches;
+  //bool force_caching;
+  //bool list_outstanding_urls_on_error;
+  //bool track_original_content_length;
+  int shm_cache_size_kb;
+};
+
+
+class AtsRewriteOptions : public SystemRewriteOptions {
+ public:
+    // See rewrite_options::Initialize and ::Terminate
+    static void Initialize();
+    static void Terminate();
+
+    AtsRewriteOptions(ThreadSystem* thread_system);
+    virtual ~AtsRewriteOptions() {
+    }
+
+    const char* ParseAndSetOptions(
+        std::vector<std::string> args, MessageHandler* handler, global_settings& global_config);
+
+    virtual AtsRewriteOptions* Clone() const;
+    OptionSettingResult ParseAndSetOptions0(
+        StringPiece directive, GoogleString* msg, MessageHandler* handler);
+
+    virtual OptionSettingResult ParseAndSetOptionFromName1(
+        StringPiece name, StringPiece arg,
+        GoogleString* msg, MessageHandler* handler);
+
+
+ private:
+    bool SetBoolFlag(bool* v, StringPiece arg);
+    static Properties* ats_properties_;
+    static void AddProperties();
+    void Init();
+
+    bool IsDirective(StringPiece config_directive, StringPiece compare_directive);
+
+    DISALLOW_COPY_AND_ASSIGN(AtsRewriteOptions);
+  };
+
+} // namespace net_instaweb
+
+#endif  // ATS_REWRITE_OPTIONS_H_

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/ats_server_context.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/ats_server_context.cc b/plugins/experimental/ats_pagespeed/ats_server_context.cc
new file mode 100644
index 0000000..4fc9eff
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/ats_server_context.cc
@@ -0,0 +1,46 @@
+/** @file
+
+    A brief file description
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#include "ats_server_context.h"
+#include "ats_rewrite_driver_factory.h"
+#include "ats_rewrite_options.h"
+
+#include "net/instaweb/system/public/system_caches.h"
+
+
+using namespace net_instaweb;
+
+AtsRewriteOptions* AtsServerContext::config() {
+  return (AtsRewriteOptions*)global_options();
+}
+
+AtsServerContext::AtsServerContext(AtsRewriteDriverFactory* factory) :
+  // TODO(oschaaf): host/port
+  SystemServerContext(factory, "foo.com" /*hostname*/, 8080/*port*/),
+    initialized_(false),
+    ats_factory_(factory) {
+}
+
+AtsServerContext::~AtsServerContext() {
+
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/ats_server_context.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/ats_server_context.h b/plugins/experimental/ats_pagespeed/ats_server_context.h
new file mode 100644
index 0000000..e156ed3
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/ats_server_context.h
@@ -0,0 +1,56 @@
+/** @file
+
+    A brief file description
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#ifndef ATS_SERVER_CONTEXT_H_
+#define ATS_SERVER_CONTEXT_H_
+
+#include "ats_rewrite_options.h"
+
+#include "net/instaweb/system/public/system_server_context.h"
+#include "net/instaweb/util/public/statistics.h"
+
+namespace net_instaweb {
+
+class AtsRewriteOptions;
+class AtsRewriteDriverFactory;
+
+class AtsServerContext : public net_instaweb::SystemServerContext {
+ public:
+  explicit AtsServerContext(AtsRewriteDriverFactory* factory);
+  virtual ~AtsServerContext();
+
+  virtual bool ProxiesHtml() const {
+    return true;
+  }
+
+  AtsRewriteOptions *config();
+  AtsRewriteDriverFactory *ats_rewrite_driver_factory() { return ats_factory_; }
+
+ private:
+  bool initialized_;
+  AtsRewriteDriverFactory* ats_factory_;
+};
+
+} /* ats_pagespeed */
+
+#endif /* ATS_SERVER_CONTEXT_H_ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/ats_thread_system.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/ats_thread_system.h b/plugins/experimental/ats_pagespeed/ats_thread_system.h
new file mode 100644
index 0000000..e3565a8
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/ats_thread_system.h
@@ -0,0 +1,50 @@
+/** @file
+
+    A brief file description
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#ifndef ATS_THREAD_SYSTEM_H_
+#define ATS_THREAD_SYSTEM_H_
+
+#include <pthread.h>
+
+#include <ts/ts.h>
+#include "net/instaweb/system/public/system_thread_system.h"
+#include "net/instaweb/util/public/thread.h"
+#include "net/instaweb/util/public/thread_system.h"
+#include "net/instaweb/util/public/pthread_rw_lock.h"
+#include "net/instaweb/util/public/condvar.h"
+
+namespace net_instaweb {
+
+class AtsThreadSystem : public net_instaweb::SystemThreadSystem {
+ public:
+  virtual void BeforeThreadRunHook() {
+    TSThreadInit();
+  }
+
+  virtual ~AtsThreadSystem() { }
+};
+
+} // net_instaweb
+
+
+#endif // ATS_THREAD_SYSTEM_H_

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/Makefile
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/Makefile b/plugins/experimental/ats_pagespeed/gzip/Makefile
new file mode 100644
index 0000000..6a02ae2
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/Makefile
@@ -0,0 +1,24 @@
+#  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.
+
+TSXS?=tsxs
+
+all:
+	$(TSXS) -o gzip.so -v -C *.cc
+install: 
+	$(TSXS) -v -i -o gzip.so
+clean:
+	rm -f *.lo *.so

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/README b/plugins/experimental/ats_pagespeed/gzip/README
new file mode 100644
index 0000000..2e74681
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/README
@@ -0,0 +1,4 @@
+This gzip is not compiled, but only here to be able to diff later
+with the official gzip plugin. It is very slightly modified no 
+be able to disable caching of its compressed output through a 
+response header
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/configuration.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/configuration.cc b/plugins/experimental/ats_pagespeed/gzip/configuration.cc
new file mode 100644
index 0000000..b1c499d
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/configuration.cc
@@ -0,0 +1,264 @@
+/** @file
+
+  Transforms content using gzip or deflate
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include "configuration.h"
+#include <fstream>
+#include <algorithm>
+#include <vector>
+#include <fnmatch.h>
+
+namespace Gzip {
+  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;
+  }
+
+  enum ParserState {
+    kParseStart,
+    kParseCompressibleContentType,
+    kParseRemoveAcceptEncoding,
+    kParseEnable,
+    kParseCache,
+    kParseDisallow,
+  };
+
+  void Configuration::AddHostConfiguration(HostConfiguration * hc){
+    host_configurations_.push_back(hc);
+  }
+
+  void HostConfiguration::add_disallow(const std::string & disallow) {
+    disallows_.push_back(disallow);
+  }
+
+  void HostConfiguration::add_compressible_content_type(const std::string & content_type) {
+    compressible_content_types_.push_back(content_type);
+  }
+
+  HostConfiguration * Configuration::Find(const char * host, int host_length) {
+    HostConfiguration * 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 HostConfiguration::IsUrlAllowed(const char * url, int url_len) {
+    string surl(url, url_len);
+
+    for (size_t i = 0; i < disallows_.size(); i++) {
+      if ( fnmatch (disallows_[i].c_str(), surl.c_str(), 0) == 0 ) {
+        info("url [%s] disabled for compression, matched on pattern [%s]",
+            surl.c_str(), disallows_[i].c_str());
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  bool HostConfiguration::ContentTypeIsCompressible(const char * content_type, int content_type_length) {
+    string scontent_type(content_type, content_type_length);
+    bool is_match = false;
+    
+    for (size_t i = 0; i < compressible_content_types_.size(); i++) {
+      const char* match_string = compressible_content_types_[i].c_str();
+      bool exclude = match_string[0] == '!';
+      if (exclude) {
+        match_string++;//skip '!'
+      }
+      if ( fnmatch (match_string, scontent_type.c_str(), 0) == 0 ) {
+        info("compressible content type [%s], matched on pattern [%s]",
+            scontent_type.c_str(), compressible_content_types_[i].c_str());
+        is_match = !exclude;
+      }
+    }
+
+    return is_match;
+  }
+
+  Configuration * Configuration::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);
+
+    Configuration * c = new Configuration();
+    HostConfiguration * current_host_configuration = new HostConfiguration("");
+    c->AddHostConfiguration(current_host_configuration);
+    current_host_configuration->add_compressible_content_type("text/*");
+    current_host_configuration->add_compressible_content_type("application/xml*");
+    current_host_configuration->add_compressible_content_type("*javascript*");
+    current_host_configuration->add_compressible_content_type("image/svg+xml");
+
+    
+    if (pathstring.empty())  {
+      return c;
+    }
+
+    path = pathstring.c_str();
+    info("Parsing file \"%s\"", path);
+    std::ifstream f;
+
+    size_t lineno = 0;
+
+    f.open(path, std::ios::in);
+
+    if (!f.is_open()) {
+      warning("could not open file [%s], skip",path);
+      return c;
+    }
+
+    enum ParserState state = kParseStart;
+
+    while (!f.eof()) {
+      std::string line;
+      getline(f, line);
+      ++lineno;
+
+      trim_if(line, isspace);
+      if (line.size() == 0) {
+        continue;
+      }
+
+      vector<string> v = tokenize( line, isspace );
+
+      for(size_t i = 0; i < v.size(); i++ ) {
+        string token = v[i];
+        trim_if(token, isspace);
+
+        //should not happen
+        if (!token.size()) continue;
+
+        //once a comment is encountered, we are done processing the line
+        if (token[0] == '#') break;
+
+        switch(state) {
+        case kParseStart:
+          if ( (token[0] == '[') && (token[token.size()-1] == ']')){
+            std::string current_host = token.substr(1,token.size()-2);
+            current_host_configuration = new HostConfiguration(current_host);
+            c->AddHostConfiguration(current_host_configuration);
+          } else if (token == "compressible-content-type" ) {
+            state = kParseCompressibleContentType;
+          } else if (token == "remove-accept-encoding" ) {
+            state = kParseRemoveAcceptEncoding;
+          } else if (token == "enabled" ) {
+            state = kParseEnable;
+          } else if (token == "cache" ) {
+            state = kParseCache;
+          } else if (token == "disallow" ) {
+            state = kParseDisallow;
+          }
+          else {
+            warning("failed to interpret \"%s\" at line %zu", token.c_str(), lineno);
+          }
+          break;
+        case kParseCompressibleContentType:
+          current_host_configuration->add_compressible_content_type(token);
+          state = kParseStart;
+          break;
+        case kParseRemoveAcceptEncoding:
+          current_host_configuration->set_remove_accept_encoding(token == "true");
+          state = kParseStart;
+          break;
+        case kParseEnable:
+          current_host_configuration->set_enabled(token == "true");
+          state = kParseStart;
+          break;
+        case kParseCache:
+          current_host_configuration->set_cache(token == "true");
+          state = kParseStart;
+          break;
+        case kParseDisallow:
+          current_host_configuration->add_disallow(token);
+          state = kParseStart;
+          break;
+        }
+      }
+    }
+
+    if (state != kParseStart) {
+      warning("the parser state indicates that data was expected when it reached the end of the file (%d)", state);
+    }
+
+    return c;
+  } //Configuration::Parse
+} //namespace

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/configuration.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/configuration.h b/plugins/experimental/ats_pagespeed/gzip/configuration.h
new file mode 100644
index 0000000..b38cb64
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/configuration.h
@@ -0,0 +1,84 @@
+/** @file
+
+  Transforms content using gzip or deflate
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+
+#ifndef GZIP_CONFIGURATION_H_
+#define GZIP_CONFIGURATION_H_
+
+#include <string>
+#include <vector>
+#include "debug_macros.h"
+
+namespace Gzip  { 
+  class HostConfiguration {
+  public: //todo -> only configuration should be able to construct hostconfig
+    explicit HostConfiguration(const std::string & host)
+      : host_(host)
+      , enabled_(true)
+      , cache_(true)
+      , remove_accept_encoding_(false)
+    {}
+
+    inline bool enabled() { return enabled_; }
+    inline void set_enabled(bool x) { enabled_ = x; } 
+    inline bool cache() { return cache_; }
+    inline void set_cache(bool x) { cache_ = x; } 
+    inline bool remove_accept_encoding() { return remove_accept_encoding_; }
+    inline void set_remove_accept_encoding(bool x) { remove_accept_encoding_ = x; } 
+    inline std::string host() { return host_; }
+    void add_disallow(const std::string & disallow);
+    void add_compressible_content_type(const std::string & content_type);
+    bool IsUrlAllowed(const char * url, int url_len);
+    bool ContentTypeIsCompressible(const char * content_type, int content_type_length);
+
+  private:
+    std::string host_;
+    bool enabled_;
+    bool cache_;
+    bool remove_accept_encoding_;
+    std::vector<std::string> compressible_content_types_;
+    std::vector<std::string> disallows_;
+    DISALLOW_COPY_AND_ASSIGN(HostConfiguration);
+  };//class HostConfiguration
+
+  class Configuration {
+    friend class HostConfiguration;
+  public:
+    static Configuration * Parse(const char * path);
+    HostConfiguration * Find(const char * host, int host_length); 
+    inline HostConfiguration * GlobalConfiguration() {
+      return host_configurations_[0];
+    }
+
+  private:
+    explicit Configuration()  {}
+    void AddHostConfiguration(HostConfiguration * hc);
+
+    std::vector<HostConfiguration *> host_configurations_;
+    //todo: destructor. delete owned host configurations
+    DISALLOW_COPY_AND_ASSIGN(Configuration);
+  }; //class Configuration
+
+}//namespace
+
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/debug_macros.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/debug_macros.h b/plugins/experimental/ats_pagespeed/gzip/debug_macros.h
new file mode 100644
index 0000000..151de31
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/debug_macros.h
@@ -0,0 +1,59 @@
+/** @file
+
+  Transforms content using gzip or deflate
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#ifndef _DBG_MACROS_H
+#define _DBG_MACROS_H
+
+#include <ts/ts.h>
+
+#define TAG "gzip"
+
+#define debug(fmt, args...) do {                                    \
+  TSDebug(TAG, "DEBUG: [%s:%d] [%s] " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+  } while (0)
+
+#define info(fmt, args...) do {                                    \
+  TSDebug(TAG, "INFO: " fmt, ##args ); \
+  } while (0)
+
+#define warning(fmt, args...) do {                                    \
+  TSDebug(TAG, "WARNING: " fmt, ##args ); \
+} while (0)
+
+#define error(fmt, args...) do {                                    \
+  TSError("[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+  TSDebug(TAG, "[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+} while (0)
+
+#define fatal(fmt, args...) do {                                    \
+  TSError("[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+  TSDebug(TAG, "[%s:%d] [%s] ERROR: " fmt, __FILE__, __LINE__, __FUNCTION__ , ##args ); \
+  exit(-1); \
+} while (0)
+
+//FIXME: this one doesn't deserve to be here
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+#endif //_DBG_MACROS_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/gzip.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/gzip.cc b/plugins/experimental/ats_pagespeed/gzip/gzip.cc
new file mode 100644
index 0000000..1397762
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/gzip.cc
@@ -0,0 +1,826 @@
+/** @file
+
+  Transforms content using gzip or deflate
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#include <stdint.h>
+
+#include <string>
+#include <string.h>
+#include <zlib.h>
+#include <ts/ts.h>
+#include "debug_macros.h"
+#include "misc.h"
+#include "configuration.h"
+//#include "ink_defs.h"
+
+using namespace std;
+using namespace Gzip;
+
+//FIXME: custom dictionaries would be nice. configurable/content-type?
+//FIXME: look into autoscaling the compression level based on connection speed
+// a gprs device might benefit from a higher compression ratio, whereas a desktop w. high bandwith
+// might be served better with little or no compression at all
+//FIXME: look into compressing from the task thread pool
+//FIXME: make normalizing accept encoding configurable
+
+// from mod_deflate:
+// ZLIB's compression algorithm uses a
+// 0-9 based scale that GZIP does where '1' is 'Best speed' 
+// and '9' is 'Best compression'. Testing has proved level '6' 
+// to be about the best level to use in an HTTP Server. 
+
+const int ZLIB_COMPRESSION_LEVEL = 6;
+
+int arg_idx_hooked;
+int arg_idx_host_configuration;
+int arg_idx_url_disallowed;
+
+
+const char * global_hidden_header_name;
+Configuration* config = NULL;
+const char *dictionary = NULL;
+
+static GzipData *
+gzip_data_alloc(int compression_type)
+{
+  GzipData *data;
+  int err;
+
+  data = (GzipData *) TSmalloc(sizeof(GzipData));
+  data->downstream_vio = NULL;
+  data->downstream_buffer = NULL;
+  data->downstream_reader = NULL;
+  data->downstream_length = 0;
+  data->state = transform_state_initialized;
+  data->compression_type = compression_type;
+  data->zstrm.next_in = Z_NULL;
+  data->zstrm.avail_in = 0;
+  data->zstrm.total_in = 0;
+  data->zstrm.next_out = Z_NULL;
+  data->zstrm.avail_out = 0;
+  data->zstrm.total_out = 0;
+  data->zstrm.zalloc = gzip_alloc;
+  data->zstrm.zfree = gzip_free;
+  data->zstrm.opaque = (voidpf) 0;
+  data->zstrm.data_type = Z_ASCII;
+
+  int window_bits = (compression_type == COMPRESSION_TYPE_GZIP) ? WINDOW_BITS_GZIP : WINDOW_BITS_DEFLATE;
+
+  err = deflateInit2(&data->zstrm, ZLIB_COMPRESSION_LEVEL, Z_DEFLATED, window_bits, ZLIB_MEMLEVEL, Z_DEFAULT_STRATEGY);
+
+  if (err != Z_OK) {
+    fatal("gzip-transform: ERROR: deflateInit (%d)!", err);
+  }
+
+  if (dictionary) {
+    err = deflateSetDictionary(&data->zstrm, (const Bytef *) dictionary, strlen(dictionary));
+    if (err != Z_OK) {
+      fatal("gzip-transform: ERROR: deflateSetDictionary (%d)!", err);
+    }
+  }
+
+  return data;
+}
+
+
+static void
+gzip_data_destroy(GzipData * data)
+{
+  TSReleaseAssert(data);
+
+  //deflateEnd returnvalue ignore is intentional
+  //it would spew log on every client abort
+  deflateEnd(&data->zstrm);
+
+  if (data->downstream_buffer) {
+    TSIOBufferDestroy(data->downstream_buffer);
+  }
+
+  TSfree(data);
+}
+
+static TSReturnCode
+gzip_content_encoding_header(TSMBuffer bufp, TSMLoc hdr_loc, const int compression_type)
+{
+  TSReturnCode ret;
+  TSMLoc ce_loc;
+
+  // Delete Content-Encoding if present???
+
+  if ((ret = TSMimeHdrFieldCreateNamed(bufp, hdr_loc, "Content-Encoding", sizeof("Content-Encoding") - 1, &ce_loc)) == TS_SUCCESS) {
+    if (compression_type == COMPRESSION_TYPE_DEFLATE) {
+      ret = TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, ce_loc, -1, "deflate", sizeof("deflate") - 1);
+    } else if (compression_type == COMPRESSION_TYPE_GZIP) {
+      ret = TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, ce_loc, -1, "gzip", sizeof("gzip") - 1);
+    }
+    if (ret == TS_SUCCESS) {
+      ret = TSMimeHdrFieldAppend(bufp, hdr_loc, ce_loc);
+    }
+    TSHandleMLocRelease(bufp, hdr_loc, ce_loc);
+  }
+
+  if (ret != TS_SUCCESS) {
+    error("cannot add the Content-Encoding header");
+  }
+
+  return ret;
+}
+
+static TSReturnCode
+gzip_vary_header(TSMBuffer bufp, TSMLoc hdr_loc)
+{
+  TSReturnCode ret;
+  TSMLoc ce_loc;
+
+  ce_loc = TSMimeHdrFieldFind(bufp, hdr_loc, "Vary", sizeof("Vary") - 1);
+  if (ce_loc) {
+    int idx, count, len; 
+    const char *value;
+
+    count = TSMimeHdrFieldValuesCount(bufp, hdr_loc, ce_loc);
+    for(idx=0; idx<count; idx++) {
+      value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, ce_loc, idx, &len);
+      if (len &&
+          strncasecmp("Accept-Encoding", value, len) == 0) {
+        // Bail, Vary: Accept-Encoding already sent from origin
+        TSHandleMLocRelease(bufp, hdr_loc, ce_loc);
+        return TS_SUCCESS;
+      }
+    }
+
+    ret = TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, ce_loc, -1, "Accept-Encoding", sizeof("Accept-Encoding") - 1);
+    TSHandleMLocRelease(bufp, hdr_loc, ce_loc);
+  } else {
+    if ((ret = TSMimeHdrFieldCreateNamed(bufp, hdr_loc, "Vary", sizeof("Vary") - 1, &ce_loc)) == TS_SUCCESS) {
+      if ((ret = TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, ce_loc, -1, "Accept-Encoding", sizeof("Accept-Encoding") - 1)) == TS_SUCCESS) {
+        ret = TSMimeHdrFieldAppend(bufp, hdr_loc, ce_loc);
+      }
+
+      TSHandleMLocRelease(bufp, hdr_loc, ce_loc);
+    }
+  }
+
+  if (ret != TS_SUCCESS) {
+    error("cannot add/update the Vary header");
+  }
+
+  return ret;
+}
+
+//FIXME: the etag alteration isn't proper. it should modify the value inside quotes
+//       specify a very header..
+static TSReturnCode
+gzip_etag_header(TSMBuffer bufp, TSMLoc hdr_loc)
+{
+  TSReturnCode ret = TS_SUCCESS;
+  TSMLoc ce_loc;
+
+  ce_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_ETAG, TS_MIME_LEN_ETAG);
+
+  if (ce_loc) {
+    int changetag = 1;
+    int strl;
+    const char *strv = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, ce_loc, -1, &strl);
+    //do not alter weak etags.
+    //FIXME: consider just making the etag weak for compressed content
+    if (strl >= 2) {
+      if ((strv[0] == 'w' || strv[0] == 'W') && strv[1] == '/') {
+        changetag = 0;
+      }
+      if (changetag) {
+        ret = TSMimeHdrFieldValueAppend(bufp, hdr_loc, ce_loc, 0, "-df", 3);
+      }
+    }
+    TSHandleMLocRelease(bufp, hdr_loc, ce_loc);
+  }
+
+  if (ret != TS_SUCCESS) {
+    error("cannot handle the %s header", TS_MIME_FIELD_ETAG);
+  }
+
+  return ret;
+}
+
+//FIXME: some things are potentially compressible. those responses
+static void
+gzip_transform_init(TSCont contp, GzipData * data)
+{
+  //update the vary, content-encoding, and etag response headers
+  //prepare the downstream for transforming
+
+  TSVConn downstream_conn;
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+
+  data->state = transform_state_output;
+
+  if (TSHttpTxnTransformRespGet(data->txn, &bufp, &hdr_loc) != TS_SUCCESS) {
+    error("Error TSHttpTxnTransformRespGet");
+    return;
+  }
+
+  if (gzip_content_encoding_header(bufp, hdr_loc, data->compression_type) == TS_SUCCESS &&
+      gzip_vary_header(bufp, hdr_loc) == TS_SUCCESS &&
+      gzip_etag_header(bufp, hdr_loc) == TS_SUCCESS) {
+    downstream_conn = TSTransformOutputVConnGet(contp);
+    data->downstream_buffer = TSIOBufferCreate();
+    data->downstream_reader = TSIOBufferReaderAlloc(data->downstream_buffer);
+    data->downstream_vio = TSVConnWrite(downstream_conn, contp, data->downstream_reader, INT64_MAX);
+  }
+
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+}
+
+
+
+static void
+gzip_transform_one(GzipData * data, TSIOBufferReader upstream_reader, int amount)
+{
+  TSIOBufferBlock downstream_blkp;
+  const char *upstream_buffer;
+  char *downstream_buffer;
+  int64_t upstream_length, downstream_length;
+  int err;
+
+  while (amount > 0) {
+    downstream_blkp = TSIOBufferReaderStart(upstream_reader);
+    if (!downstream_blkp) {
+      error("couldn't get from IOBufferBlock");
+      return;
+    }
+
+    upstream_buffer = TSIOBufferBlockReadStart(downstream_blkp, upstream_reader, &upstream_length);
+    if (!upstream_buffer) {
+      error("couldn't get from TSIOBufferBlockReadStart");
+      return;
+    }
+
+    if (upstream_length > amount) {
+      upstream_length = amount;
+    }
+
+    data->zstrm.next_in = (unsigned char *) upstream_buffer;
+    data->zstrm.avail_in = upstream_length;
+
+    while (data->zstrm.avail_in > 0) {
+      downstream_blkp = TSIOBufferStart(data->downstream_buffer);
+      downstream_buffer = TSIOBufferBlockWriteStart(downstream_blkp, &downstream_length);
+
+      data->zstrm.next_out = (unsigned char *) downstream_buffer;
+      data->zstrm.avail_out = downstream_length;
+
+      err = deflate(&data->zstrm, Z_NO_FLUSH);
+
+      if (err != Z_OK)
+        warning("deflate() call failed: %d", err);
+
+      if (downstream_length > data->zstrm.avail_out) {
+        TSIOBufferProduce(data->downstream_buffer, downstream_length - data->zstrm.avail_out);
+        data->downstream_length += (downstream_length - data->zstrm.avail_out);
+      }
+
+      if (data->zstrm.avail_out > 0) {
+        if (data->zstrm.avail_in != 0) {
+          error("gzip-transform: ERROR: avail_in is (%d): should be 0", data->zstrm.avail_in);
+        }
+      }
+    }
+
+    TSIOBufferReaderConsume(upstream_reader, upstream_length);
+    amount -= upstream_length;
+  }
+}
+
+static void
+gzip_transform_finish(GzipData * data)
+{
+  if (data->state == transform_state_output) {
+    TSIOBufferBlock downstream_blkp;
+    char *downstream_buffer;
+    int64_t downstream_length;
+    int err;
+
+    data->state = transform_state_finished;
+
+    for (;;) {
+      downstream_blkp = TSIOBufferStart(data->downstream_buffer);
+
+      downstream_buffer = TSIOBufferBlockWriteStart(downstream_blkp, &downstream_length);
+      data->zstrm.next_out = (unsigned char *) downstream_buffer;
+      data->zstrm.avail_out = downstream_length;
+
+      err = deflate(&data->zstrm, Z_FINISH);
+
+      if (downstream_length > (int64_t) data->zstrm.avail_out) {
+        TSIOBufferProduce(data->downstream_buffer, downstream_length - data->zstrm.avail_out);
+        data->downstream_length += (downstream_length - data->zstrm.avail_out);
+      }
+
+      if (err == Z_OK) {        /* some more data to encode */
+        continue;
+      }
+
+      if (err != Z_STREAM_END) {
+        warning("deflate should report Z_STREAM_END");
+      }
+      break;
+    }
+
+    if (data->downstream_length != (int64_t) (data->zstrm.total_out)) {
+      error("gzip-transform: ERROR: output lengths don't match (%d, %ld)", data->downstream_length,
+            data->zstrm.total_out);
+    }
+
+    gzip_log_ratio(data->zstrm.total_in, data->downstream_length);
+  }
+}
+
+
+static void
+gzip_transform_do(TSCont contp)
+{
+  TSVIO upstream_vio;
+  GzipData *data;
+  int64_t upstream_todo;
+  int64_t upstream_avail;
+  int64_t downstream_bytes_written;
+
+  data = (GzipData*)TSContDataGet(contp);
+  if (data->state == transform_state_initialized) {
+    gzip_transform_init(contp, data);
+  }
+
+  upstream_vio = TSVConnWriteVIOGet(contp);
+  downstream_bytes_written = data->downstream_length;
+
+  if (!TSVIOBufferGet(upstream_vio)) {
+    gzip_transform_finish(data);
+
+    TSVIONBytesSet(data->downstream_vio, data->downstream_length);
+
+    if (data->downstream_length > downstream_bytes_written) {
+      TSVIOReenable(data->downstream_vio);
+    }
+    return;
+  }
+
+  upstream_todo = TSVIONTodoGet(upstream_vio);
+
+  if (upstream_todo > 0) {
+    upstream_avail = TSIOBufferReaderAvail(TSVIOReaderGet(upstream_vio));
+
+    if (upstream_todo > upstream_avail) {
+      upstream_todo = upstream_avail;
+    }
+
+    if (upstream_todo > 0) {
+      gzip_transform_one(data, TSVIOReaderGet(upstream_vio), upstream_todo);
+      TSVIONDoneSet(upstream_vio, TSVIONDoneGet(upstream_vio) + upstream_todo);
+    }
+  }
+
+  if (TSVIONTodoGet(upstream_vio) > 0) {
+    if (upstream_todo > 0) {
+      if (data->downstream_length > downstream_bytes_written) {
+        TSVIOReenable(data->downstream_vio);
+      }
+      TSContCall(TSVIOContGet(upstream_vio), TS_EVENT_VCONN_WRITE_READY, upstream_vio);
+    }
+  } else {
+    gzip_transform_finish(data);
+    TSVIONBytesSet(data->downstream_vio, data->downstream_length);
+
+    if (data->downstream_length > downstream_bytes_written) {
+      TSVIOReenable(data->downstream_vio);
+    }
+
+    TSContCall(TSVIOContGet(upstream_vio), TS_EVENT_VCONN_WRITE_COMPLETE, upstream_vio);
+  }
+}
+
+
+static int
+gzip_transform(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */)
+{
+  if (TSVConnClosedGet(contp)) {
+    gzip_data_destroy((GzipData*)TSContDataGet(contp));
+    TSContDestroy(contp);
+    return 0;
+  } else {
+    switch (event) {
+    case TS_EVENT_ERROR:{
+        debug("gzip_transform: TS_EVENT_ERROR starts");
+        TSVIO upstream_vio = TSVConnWriteVIOGet(contp);
+        TSContCall(TSVIOContGet(upstream_vio), TS_EVENT_ERROR, upstream_vio);
+      }
+      break;
+    case TS_EVENT_VCONN_WRITE_COMPLETE:
+      TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);
+      break;
+    case TS_EVENT_VCONN_WRITE_READY:
+      gzip_transform_do(contp);
+      break;
+    case TS_EVENT_IMMEDIATE:
+      gzip_transform_do(contp);
+      break;
+    default:
+      warning("unknown event [%d]", event);
+      gzip_transform_do(contp);
+      break;
+    }
+  }
+
+  return 0;
+}
+
+
+static int
+gzip_transformable(TSHttpTxn txnp, int server, HostConfiguration * host_configuration, int *compress_type)
+{
+  /* Server response header */
+  TSMBuffer bufp;
+  TSMLoc hdr_loc;
+  TSMLoc field_loc;
+
+  /* Client request header */
+  TSMBuffer cbuf;
+  TSMLoc chdr;
+  TSMLoc cfield;
+
+  const char *value;
+  int nvalues;
+  int i, compression_acceptable, len;
+
+  TSHttpStatus resp_status;
+  if (server) {
+    TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc);
+  } else {
+    TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc);
+  }
+  resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+
+  //conservatively pick some statusses to compress
+  if (!(resp_status == 200 || resp_status == 404 || resp_status == 500)) {
+    info("http response status [%d] is not compressible", resp_status);
+    return 0;
+  }
+
+  TSHttpTxnClientReqGet(txnp, &cbuf, &chdr);
+
+  //the only compressible method is currently GET.
+  int method_length;
+  const char *method = TSHttpHdrMethodGet(cbuf, chdr, &method_length);
+  if (!(method_length == TS_HTTP_LEN_GET && memcmp(method, TS_HTTP_METHOD_GET, TS_HTTP_LEN_GET) == 0)) {
+    debug("method is not GET, not compressible");
+    TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
+    return 0;
+  }
+
+  cfield = TSMimeHdrFieldFind(cbuf, chdr, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
+  if (cfield != TS_NULL_MLOC) {
+    compression_acceptable = 0;
+    nvalues = TSMimeHdrFieldValuesCount(cbuf, chdr, cfield);
+    for (i=0; i<nvalues; i++) {
+      value = TSMimeHdrFieldValueStringGet(cbuf, chdr, cfield, i, &len);
+      if (!value) {
+        continue;
+      }
+
+      if (strncasecmp(value, "deflate", sizeof("deflate") - 1) == 0) {
+        compression_acceptable = 1;
+        *compress_type = COMPRESSION_TYPE_DEFLATE;
+        break;
+      } else if (strncasecmp(value, "gzip", sizeof("gzip") - 1) == 0) {
+        compression_acceptable = 1;
+        *compress_type = COMPRESSION_TYPE_GZIP;
+        break;
+      }
+    }
+
+    TSHandleMLocRelease(cbuf, chdr, cfield);
+    TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
+
+    if (!compression_acceptable) {
+      info("no acceptable encoding found in request header, not compressible");
+      return 0;
+    }
+  } else {
+    info("no acceptable encoding found in request header, not compressible");
+    TSHandleMLocRelease(cbuf, chdr, cfield);
+    TSHandleMLocRelease(cbuf, TS_NULL_MLOC, chdr);
+    return 0;
+  }
+
+  if (server) {
+    TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc);
+  } else {
+    TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc);
+  }
+
+  /* If there already exists a content encoding then we don't want
+     to do anything. */
+  field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_ENCODING, -1);
+  if (field_loc) {
+    info("response is already content encoded, not compressible");
+    TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+    return 0;
+  }
+
+  /* We only want to do gzip compression on documents that have a
+     content type of "text/" or "application/x-javascript". */
+  field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, TS_MIME_FIELD_CONTENT_TYPE, -1);
+  if (!field_loc) {
+    info("no content type header found, not compressible");
+    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+    return 0;
+  }
+
+  value = TSMimeHdrFieldValueStringGet(bufp, hdr_loc, field_loc, 0, &len);
+
+  int rv = host_configuration->ContentTypeIsCompressible(value, len);
+  if (!rv) { 
+    info("content-type [%.*s] not compressible", len, value);
+  }
+  TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+  return rv;
+}
+
+
+static void
+gzip_transform_add(TSHttpTxn txnp, int /* server ATS_UNUSED */, HostConfiguration * hc, int compress_type)
+{
+  int *tmp = (int *) TSHttpTxnArgGet(txnp, arg_idx_hooked);
+  if (tmp) {
+    //happens on cache_stale_hit
+    debug("transform hook already set, bail");
+    return;
+  } else {
+    TSHttpTxnArgSet(txnp, arg_idx_hooked, (void *) &GZIP_ONE);
+    info("adding compression transform");
+  }
+
+  TSHttpTxnUntransformedRespCache(txnp, 1);
+
+  if (!hc->cache()) {
+    debug("@@@@@ Gzip cache disabled");
+    TSHttpTxnTransformedRespCache(txnp, 0);
+  } else {
+    TSMBuffer bufp;
+    TSMLoc hdr_loc, field_loc;
+    int cache = 1;
+    if (TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS || TSHttpTxnCachedRespGet(txnp, &bufp, &hdr_loc) == TS_SUCCESS) {
+      field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, "@gzip_nocache", strlen("@gzip_nocache"));
+      if (field_loc) {
+        cache = 0;
+        debug("@@@@@ Gzip disallows cacheing of transformed response");
+        TSHandleMLocRelease(bufp, hdr_loc, field_loc);
+      } else  {
+        debug("@@@@ Gzip allows cacheing of transformed response");
+      }
+      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+    }
+    
+    TSHttpTxnTransformedRespCache(txnp, cache);
+  }
+
+  TSVConn connp;
+  GzipData *data;
+
+  connp = TSTransformCreate(gzip_transform, txnp);
+  data = gzip_data_alloc(compress_type);
+  data->txn = txnp;
+
+  TSContDataSet(connp, data);
+  TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp);
+}
+
+static int
+cache_transformable(TSHttpTxn txnp)
+{
+  int obj_status;
+
+  if (TSHttpTxnCacheLookupStatusGet(txnp, &obj_status) == TS_ERROR) {
+    warning("Couldn't get cache status of object");
+    return 0;
+  }
+  if (obj_status == TS_CACHE_LOOKUP_HIT_STALE) {
+    info("stale cache hit");
+    return 0;
+  }
+  if (obj_status == TS_CACHE_LOOKUP_HIT_FRESH) {
+    info("fresh cache hit");
+    return 1;
+  }
+
+  return 0;
+}
+
+HostConfiguration * 
+find_host_configuration(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer bufp, TSMLoc locp)
+{
+  TSMLoc fieldp = TSMimeHdrFieldFind(bufp, locp, TS_MIME_FIELD_HOST, TS_MIME_LEN_HOST);
+
+  if (fieldp) {
+    int strl;
+    const char *strv = TSMimeHdrFieldValueStringGet(bufp, locp, fieldp, -1, &strl);
+    TSHandleMLocRelease(bufp, locp, fieldp);
+
+    HostConfiguration * host_configuration = config->Find(strv, strl);   
+    return host_configuration;
+  }
+
+  return config->GlobalConfiguration();
+}
+
+
+static int
+transform_plugin(TSCont /* contp ATS_UNUSED */, TSEvent event, void *edata)
+{
+  TSHttpTxn txnp = (TSHttpTxn) edata;
+  int compress_type = COMPRESSION_TYPE_DEFLATE;
+
+  switch (event) {
+    case TS_EVENT_HTTP_READ_REQUEST_HDR:
+      {
+        TSMBuffer req_buf;
+        TSMLoc req_loc;
+        if (TSHttpTxnClientReqGet(txnp, &req_buf, &req_loc) == TS_SUCCESS) {
+          int url_len;
+          char * url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_len);
+          HostConfiguration * hc = find_host_configuration(txnp, req_buf, req_loc);
+          //we could clone the hosting configuration here, to make it deletable on reload?
+          TSHttpTxnArgSet(txnp, arg_idx_host_configuration, (void *) hc);
+
+          if (!hc->enabled() || !hc->IsUrlAllowed(url, url_len)) {
+            //FIXME: no double negatives
+            TSHttpTxnArgSet(txnp, arg_idx_url_disallowed, (void *) &GZIP_ONE);
+            info("url [%.*s] not allowed", url_len, url);
+          } else {
+            normalize_accept_encoding(txnp, req_buf, req_loc);	
+          }
+          TSfree(url);
+          TSHandleMLocRelease(req_buf, TS_NULL_MLOC, req_loc);
+        }
+        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+      }
+      break;
+
+    case TS_EVENT_HTTP_READ_RESPONSE_HDR:
+      {
+        //os: the accept encoding header needs to be restored..
+        //otherwise the next request won't get a cache hit on this
+        HostConfiguration * hc = (HostConfiguration*)TSHttpTxnArgGet(txnp, arg_idx_host_configuration);
+        if (hc != NULL) { 
+          if (hc->remove_accept_encoding()) {
+            TSMBuffer req_buf;
+            TSMLoc req_loc;
+            if (TSHttpTxnServerReqGet(txnp, &req_buf, &req_loc) == TS_SUCCESS) {
+              restore_accept_encoding(txnp, req_buf, req_loc, global_hidden_header_name);
+              TSHandleMLocRelease(req_buf, TS_NULL_MLOC, req_loc);
+            }
+          }
+
+          int allowed = !TSHttpTxnArgGet(txnp, arg_idx_url_disallowed);
+          if ( allowed && gzip_transformable(txnp, 1, hc, &compress_type)) {
+            gzip_transform_add(txnp, 1, hc, compress_type);
+          }
+        }
+        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+      }
+      break;
+
+    case TS_EVENT_HTTP_SEND_REQUEST_HDR:
+      {
+        HostConfiguration * hc = (HostConfiguration*)TSHttpTxnArgGet(txnp, arg_idx_host_configuration);
+        if (hc!=NULL) {
+          if (hc->remove_accept_encoding()) {
+            TSMBuffer req_buf;
+            TSMLoc req_loc;
+            if (TSHttpTxnServerReqGet(txnp, &req_buf, &req_loc) == TS_SUCCESS) {
+              hide_accept_encoding(txnp, req_buf, req_loc, global_hidden_header_name);
+              TSHandleMLocRelease(req_buf, TS_NULL_MLOC, req_loc);
+            }
+          }
+        }
+        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+      }
+      break;
+
+    case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:
+      {
+        int allowed = !TSHttpTxnArgGet(txnp, arg_idx_url_disallowed);
+        HostConfiguration * hc = (HostConfiguration*)TSHttpTxnArgGet(txnp, arg_idx_host_configuration);
+        if ( hc != NULL ) { 
+          if (allowed && cache_transformable(txnp) && gzip_transformable(txnp, 0, hc, &compress_type)) {
+            gzip_transform_add(txnp, 0, hc, compress_type);
+          }
+        }
+        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+      }
+      break;
+
+    default:
+      fatal("gzip transform unknown event");
+  }
+
+  return 0;
+}
+
+
+static void
+read_configuration(TSCont contp) {
+  const char * path = (const char *)TSContDataGet(contp);
+  Configuration * newconfig = Configuration::Parse(path);
+
+  Configuration * oldconfig =__sync_lock_test_and_set(&config, newconfig);
+  debug("config swapped,old config %p", oldconfig);
+
+  //FIXME: we have leaked.
+  //consider cloning or refcounting the configuration passed to the txn
+  //to make deleting the old configuration possible
+  //if (config != NULL )
+  //  delete config;
+}
+
+static int
+management_update(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */)
+{
+  TSReleaseAssert(event == TS_EVENT_MGMT_UPDATE);
+  info("management update event received");
+  read_configuration(contp);
+  return 0;
+}
+
+
+void
+TSPluginInit(int argc, const char *argv[])
+{
+  string config_path;
+
+  if (argc > 2)  {
+    fatal("the gzip plugin does not accept more than 1 plugin argument");
+  } else if (argc == 2) { 
+    config_path = std::string(argv[1]);
+  }
+
+  info("TSPluginInit %s", argv[0]);
+
+  if (!register_plugin()) {
+    fatal("The gzip plugin failed to register");
+  }
+
+  //if (argc == 2) {
+  //  dictionary = load_dictionary(argv[1]);
+  //}
+
+  if (TSHttpArgIndexReserve("gzip", "for remembering if the hook was set", &arg_idx_hooked) != TS_SUCCESS) {
+    fatal("failed to reserve an argument index");
+  }
+  if (TSHttpArgIndexReserve("gzip", "for storing if compression is applicable", &arg_idx_host_configuration) != TS_SUCCESS) {
+    fatal("failed to reserve an argument index");
+  }
+  if (TSHttpArgIndexReserve("gzip", "for storing if compression is disallowed for this txn", &arg_idx_url_disallowed) != TS_SUCCESS) {
+    fatal("failed to reserve an argument index");
+  }
+
+  global_hidden_header_name = init_hidden_header_name();
+
+  TSCont management_contp = TSContCreate(management_update, NULL);
+  //fixme: never freed. there is no shutdown event?
+  char * p = (char*)TSmalloc(config_path.size()+1);
+  strcpy(p,config_path.c_str());
+  TSContDataSet(management_contp,(void*)p);
+  TSMgmtUpdateRegister(management_contp, TAG);
+  read_configuration(management_contp);
+
+  TSCont transform_contp = TSContCreate(transform_plugin, NULL);
+  TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, transform_contp);
+  TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, transform_contp);
+  TSHttpHookAdd(TS_HTTP_SEND_REQUEST_HDR_HOOK, transform_contp);
+  TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, transform_contp);
+
+  info("loaded");
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/gzip.config
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/gzip.config b/plugins/experimental/ats_pagespeed/gzip/gzip.config
new file mode 100644
index 0000000..81e9fa4
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/gzip.config
@@ -0,0 +1,6 @@
+# Set some global options first
+cache true
+enabled true
+remove-accept-encoding false
+compressible-content-type text/*
+compressible-content-type *javascript*

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/misc.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/misc.cc b/plugins/experimental/ats_pagespeed/gzip/misc.cc
new file mode 100644
index 0000000..0ea6911
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/misc.cc
@@ -0,0 +1,197 @@
+/** @file
+
+  Transforms content using gzip or deflate
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include "ts/ts.h"
+//#include "ink_defs.h"
+#include <stdint.h>
+#include <inttypes.h>
+#include "misc.h"
+#include <string.h>
+#include "debug_macros.h"
+
+voidpf
+gzip_alloc(voidpf /* opaque ATS_UNUSED */, uInt items, uInt size)
+{
+  return (voidpf) TSmalloc(items * size);
+}
+
+void
+gzip_free(voidpf /* opaque ATS_UNUSED */, voidpf address)
+{
+  TSfree(address);
+}
+
+void
+normalize_accept_encoding(TSHttpTxn /* txnp ATS_UNUSED */, TSMBuffer reqp, TSMLoc hdr_loc)
+{
+  TSMLoc field = TSMimeHdrFieldFind(reqp, hdr_loc, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
+  int deflate = 0;
+  int gzip = 0;
+
+  //remove the accept encoding field(s), 
+  //while finding out if gzip or deflate is supported.    
+  while (field) {
+    TSMLoc tmp;
+
+    if (!deflate && !gzip) {
+      int value_count = TSMimeHdrFieldValuesCount(reqp, hdr_loc, field);
+
+      while (value_count > 0) {
+        int val_len = 0;
+        const char *val;
+
+        --value_count;
+        val = TSMimeHdrFieldValueStringGet(reqp, hdr_loc, field, value_count, &val_len);
+
+        if (val_len == (int) strlen("gzip"))
+          gzip = !strncmp(val, "gzip", val_len);
+        else if (val_len == (int) strlen("deflate"))
+          deflate = !strncmp(val, "deflate", val_len);
+      }
+    }
+
+    tmp = TSMimeHdrFieldNextDup(reqp, hdr_loc, field);
+    TSMimeHdrFieldDestroy(reqp, hdr_loc, field);        //catch retval?
+    TSHandleMLocRelease(reqp, hdr_loc, field);
+    field = tmp;
+  }
+
+  //append a new accept-encoding field in the header
+  if (deflate || gzip) {
+    TSMimeHdrFieldCreate(reqp, hdr_loc, &field);
+    TSMimeHdrFieldNameSet(reqp, hdr_loc, field, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING);
+
+    if (gzip) {
+      TSMimeHdrFieldValueStringInsert(reqp, hdr_loc, field, -1, "gzip", strlen("gzip"));
+      info("normalized accept encoding to gzip");
+    } else if (deflate) {
+      TSMimeHdrFieldValueStringInsert(reqp, hdr_loc, field, -1, "deflate", strlen("deflate"));
+      info("normalized accept encoding to deflate");
+    }
+
+    TSMimeHdrFieldAppend(reqp, hdr_loc, field);
+    TSHandleMLocRelease(reqp, hdr_loc, field);
+  }
+}
+
+void
+hide_accept_encoding(TSHttpTxn /* txnp ATS_UNUSED */, 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(TSHttpTxn /* txnp ATS_UNUSED */, 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;
+  }
+}
+
+const char *
+init_hidden_header_name()
+{
+  char * hidden_header_name;
+  const char *var_name = "proxy.config.proxy_name";
+  TSMgmtString result;
+
+  if (TSMgmtStringGet(var_name, &result) != TS_SUCCESS) {
+    fatal("failed to get server name");
+  } else {
+    int hidden_header_name_len = strlen("x-accept-encoding-") + strlen(result);
+    hidden_header_name = (char *) TSmalloc(hidden_header_name_len + 1);
+    hidden_header_name[hidden_header_name_len] = 0;
+    sprintf(hidden_header_name, "x-accept-encoding-%s", result);
+  }
+  return hidden_header_name;
+}
+
+int
+register_plugin()
+{
+  TSPluginRegistrationInfo info;
+
+  info.plugin_name = (char*)"gzip";
+  info.vendor_name = (char*)"Apache";
+  info.support_email = (char*)"dev@trafficserver.apache.org";
+
+  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
+    return 0;
+  }
+  return 1;
+}
+
+const char *
+load_dictionary(const char *preload_file)
+{
+  char *dict = (char *) malloc(800000);
+  uLong dictId = adler32(0L, Z_NULL, 0);
+  uLong *adler = &dictId;
+
+  FILE *fp;
+  int i = 0;
+
+  fp = fopen(preload_file, "r");
+  if (!fp) {
+    fatal("gzip-transform: ERROR: Unable to open dict file %s", preload_file);
+  }
+
+  /* dict = (char *) calloc(8000, sizeof(char)); */
+
+  i = 0;
+  while (!feof(fp)) {
+    if (fscanf(fp, "%s\n", dict + i) == 1) {
+      i = strlen(dict);
+      strcat(dict + i, " ");
+      ++i;
+    }
+  }
+  dict[i - 1] = '\0';
+
+  /* TODO get the adler compute right */
+  *adler = adler32(*adler, (const Byte *) dict, sizeof(dict));
+  return dict;
+}
+
+void
+gzip_log_ratio(int64_t in, int64_t out)
+{
+  //  if (in) {
+  //  info("Compressed size %PRId64 (bytes), Original size %" PRId64", ratio: %f", out, in, ((float) (in - out) / in));
+  //} else {
+  //  debug("Compressed size %PRId64 (bytes), Original size %" PRId64", ratio: %f", out, in, 0.0F);
+  // }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/gzip/misc.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/gzip/misc.h b/plugins/experimental/ats_pagespeed/gzip/misc.h
new file mode 100644
index 0000000..c44fb6b
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/gzip/misc.h
@@ -0,0 +1,84 @@
+/** @file
+
+  Transforms content using gzip or deflate
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#ifndef _GZIP_MISC_H_
+#define _GZIP_MISC_H_
+
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#include <stdint.h>
+#include <zlib.h>
+#include <ts/ts.h>
+#include <stdlib.h>             //exit()
+#include <stdio.h>
+
+//zlib stuff, see [deflateInit2] at http://www.zlib.net/manual.html
+static const int ZLIB_MEMLEVEL = 9;     //min=1 (optimize for memory),max=9 (optimized for speed)
+static const int WINDOW_BITS_DEFLATE = -15;
+static const int WINDOW_BITS_GZIP = 31;
+
+//misc
+static const int COMPRESSION_TYPE_DEFLATE = 1;
+static const int COMPRESSION_TYPE_GZIP = 2;
+//this one is just for txnargset/get to point to
+static const int GZIP_ONE = 1;
+static const int DICT_PATH_MAX = 512;
+static const int DICT_ENTRY_MAX = 2048;
+
+//this one is used to rename the accept encoding header
+//it will be restored later on
+//to make it work, the name must be different then downstream proxies though
+//otherwise the downstream will restore the accept encoding header
+
+enum transform_state
+{
+  transform_state_initialized,
+  transform_state_output,
+  transform_state_finished
+};
+
+typedef struct
+{
+  TSHttpTxn txn;
+  TSVIO downstream_vio;
+  TSIOBuffer downstream_buffer;
+  TSIOBufferReader downstream_reader;
+  int downstream_length;
+  z_stream zstrm;
+  enum transform_state state;
+  int compression_type;
+} GzipData;
+
+
+voidpf gzip_alloc(voidpf opaque, uInt items, uInt size);
+void gzip_free(voidpf opaque, voidpf address);
+void normalize_accept_encoding(TSHttpTxn txnp, TSMBuffer reqp, TSMLoc hdr_loc);
+void hide_accept_encoding(TSHttpTxn txnp, TSMBuffer reqp, TSMLoc hdr_loc, const char * hidden_header_name);
+void restore_accept_encoding(TSHttpTxn txnp, TSMBuffer reqp, TSMLoc hdr_loc, const char * hidden_header_name);
+const char * init_hidden_header_name();
+int check_ts_version();
+int register_plugin();
+const char *load_dictionary(const char *preload_file);
+void gzip_log_ratio(int64_t in, int64_t out);
+
+#endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_pagespeed/scripts/prepare_psol.sh
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_pagespeed/scripts/prepare_psol.sh b/plugins/experimental/ats_pagespeed/scripts/prepare_psol.sh
new file mode 100755
index 0000000..5193e1d
--- /dev/null
+++ b/plugins/experimental/ats_pagespeed/scripts/prepare_psol.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+#
+# 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: jefftk@google.com (Jeff Kaufman)
+# Author: oschaaf@we-amp.com (Otto van der Schaaf)
+#
+# Usage:
+#   scripts/prepare_psol.sh /path/to/mod_pagespeed/src
+#
+# Creates a directory psol/ and copies headers and a few source files from a
+# depot_tools (glient) checkout into psol/include.  Along with creating
+# binaries, this is a step in preparing psol.tar.gz for distribution.
+#
+
+set -u  # check for undefined variables
+set -e  # exit on failed commands
+
+if [ "$(basename "$PWD")" != "ats_pagespeed" ] ; then
+  echo "$(basename $0) must be invoked from the ats_pagespeed directory"
+  exit 1
+fi
+
+if [ $# -ne 1 ] ; then
+  echo "Usage: $(basename $0) /path/to/mod_pagespeed/src"
+  exit 1
+fi
+
+MOD_PAGESPEED_SRC="$1"
+
+if [ "$(basename "$(dirname "$MOD_PAGESPEED_SRC")")/$( \
+        basename "$MOD_PAGESPEED_SRC")" != "mod_pagespeed/src" ] ; then
+  echo "Usage: $(basename $0) /path/to/mod_pagespeed/src"
+  exit 1
+fi
+
+if [ -e psol ] ; then
+  echo "A psol/ directory already exists.  Move it somewhere else and rerun."
+  exit 1
+fi
+mkdir psol/
+# Copy over the .h files, plus a few selected .cc and .c files.
+rsync -arvz "$MOD_PAGESPEED_SRC/" "psol/include/" --prune-empty-dirs \
+  --exclude=".svn" \
+  --exclude=".git" \
+  --include='*.h' \
+  --include='*/' \
+  --include="apr_thread_compatible_pool.cc" \
+  --include="serf_url_async_fetcher.cc" \
+  --include="apr_mem_cache.cc" \
+  --include="key_value_codec.cc" \
+  --include="apr_memcache2.c" \
+  --include="loopback_route_fetcher.cc" \
+  --include="add_headers_fetcher.cc" \
+  --include="console_css_out.cc" \
+  --include="console_out.cc" \
+  --include="dense_hash_map" \
+  --include="dense_hash_set" \
+  --include="sparse_hash_map" \
+  --include="sparse_hash_set" \
+  --include="sparsetable" \
+  --include="mod_pagespeed_console_out.cc" \
+  --include="mod_pagespeed_console_css_out.cc" \
+  --include="mod_pagespeed_console_html_out.cc" \
+  --exclude='*'
+mkdir -p psol/lib/Debug/linux/ia32
+mkdir -p psol/lib/Debug/linux/x64
+mkdir -p psol/lib/Release/linux/ia32
+mkdir -p psol/lib/Release/linux/x64
+
+# Log that we did this.
+SVN_REVISION="$(svn info $MOD_PAGESPEED_SRC | grep Revision | awk '{print $2}')"
+SVN_TAG="$(svn info $MOD_PAGESPEED_SRC | grep URL |  awk -F/ '{print $(NF-1)}')"
+
+DATE="$(date +%F)"
+echo "${DATE}: Copied from mod_pagespeed ${SVN_TAG}@r${SVN_REVISION} ($USER)" \
+  >> psol/include_history.txt
+
+echo
+echo "Output is in psol/include.  Now put binaries in psol/lib following"
+echo "https://github.com/pagespeed/ngx_pagespeed/wiki/Building-Release-Binaries"
+echo "and then you can distribute PSOL."
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1fe51067/plugins/experimental/ats_speed/.gitignore
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/.gitignore b/plugins/experimental/ats_speed/.gitignore
deleted file mode 100644
index a12b1d2..0000000
--- a/plugins/experimental/ats_speed/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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/1fe51067/plugins/experimental/ats_speed/Makefile
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/Makefile b/plugins/experimental/ats_speed/Makefile
deleted file mode 100644
index 7498b2b..0000000
--- a/plugins/experimental/ats_speed/Makefile
+++ /dev/null
@@ -1,83 +0,0 @@
-#  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.
-
-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 
-#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 *.cc -lstdc++ -lstdc++  -lpthread $(PSOL_LIBS) -lrt
-
-all: psol gzip/gzip.so ats_speed.so
-
-1.8.31.4.tar.gz:
-	wget --no-check-certificate https://dl.google.com/dl/page-speed/psol/1.8.31.4.tar.gz
-
-psol/: 1.8.31.4.tar.gz
-	tar -xzvf 1.8.31.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/1fe51067/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
deleted file mode 100755
index 80d3206..0000000
--- a/plugins/experimental/ats_speed/Makefile.psol_source
+++ /dev/null
@@ -1,49 +0,0 @@
-#  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.
-
-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/1fe51067/plugins/experimental/ats_speed/README.md
----------------------------------------------------------------------
diff --git a/plugins/experimental/ats_speed/README.md b/plugins/experimental/ats_speed/README.md
deleted file mode 100644
index ae5db92..0000000
--- a/plugins/experimental/ats_speed/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-![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/1fe51067/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
deleted file mode 100644
index 769e79b..0000000
--- a/plugins/experimental/ats_speed/ats_base_fetch.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-/** @file
-
-    A brief file description
-
-    @section license License
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-*/
-
-#include "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/1fe51067/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
deleted file mode 100644
index 8e4d93d..0000000
--- a/plugins/experimental/ats_speed/ats_base_fetch.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/** @file
-
-    A brief file description
-
-    @section license License
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-*/
-
-#ifndef 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_ */