trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jpe...@apache.org
Subject [1/2] git commit: TS-1462: SPDY proxy plugin
Date Tue, 09 Oct 2012 04:44:45 GMT
Updated Branches:
  refs/heads/master 9ce7cbc63 -> abe55a685


TS-1462: SPDY proxy plugin


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

Branch: refs/heads/master
Commit: abe55a685c5494f73f06a68bc88cd1c58de6f67b
Parents: 9ce7cbc
Author: James Peach <jpeach@apache.org>
Authored: Mon Oct 8 21:20:24 2012 -0700
Committer: James Peach <jpeach@apache.org>
Committed: Mon Oct 8 21:43:22 2012 -0700

----------------------------------------------------------------------
 CHANGES                                         |    2 +
 configure.ac                                    |    3 +
 plugins/experimental/Makefile.am                |    3 +-
 plugins/experimental/spdy/TODO                  |   20 +
 plugins/experimental/spdy/http.cc               |  305 ++++++++++
 plugins/experimental/spdy/http.h                |   89 +++
 plugins/experimental/spdy/io.cc                 |   87 +++
 plugins/experimental/spdy/io.h                  |  164 ++++++
 plugins/experimental/spdy/lib/base/atomic.h     |   57 ++
 plugins/experimental/spdy/lib/base/inet.h       |   71 +++
 plugins/experimental/spdy/lib/base/logging.cc   |   47 ++
 plugins/experimental/spdy/lib/base/logging.h    |   85 +++
 plugins/experimental/spdy/lib/spdy/message.cc   |  535 ++++++++++++++++++
 plugins/experimental/spdy/lib/spdy/spdy.h       |  291 ++++++++++
 plugins/experimental/spdy/lib/spdy/zstream.cc   |  123 ++++
 plugins/experimental/spdy/lib/spdy/zstream.h    |  119 ++++
 plugins/experimental/spdy/protocol.cc           |  187 ++++++
 plugins/experimental/spdy/protocol.h            |   47 ++
 plugins/experimental/spdy/spdy.cc               |  384 +++++++++++++
 plugins/experimental/spdy/stream.cc             |  370 ++++++++++++
 plugins/experimental/spdy/strings.cc            |  100 ++++
 plugins/experimental/spdy/tests/stubs.cc        |   39 ++
 plugins/experimental/spdy/tests/zstream_test.cc |  227 ++++++++
 23 files changed, 3354 insertions(+), 1 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index b989ad6..5408bd5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 3.3.1
 
+  *) [TS-1462] SPDY proxy plugin
+
   *) [TS-1488] Check the event`s cancel flag before put it into the PriorityEventQueue.
      Author: KuoTai.
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 01e5ee8..2425021 100644
--- a/configure.ac
+++ b/configure.ac
@@ -671,6 +671,8 @@ case $host_os in
       debug_opt="$common_opt"
       release_opt="-g $common_opt $optimizing_flags -fno-strict-aliasing"
       cxx_opt="-Wno-invalid-offsetof"
+      # clang on Darwin needs to use libc++ for any C++11 code.
+      AM_CONDITIONAL([BUILD_HAVE_LIBCXX], [ true ])
       ;;
     *) # gcc
       common_opt="-pipe -Wall -Werror -Wno-deprecated-declarations"
@@ -1592,6 +1594,7 @@ AC_CONFIG_FILES([plugins/experimental/custom_redirect/Makefile])
 AC_CONFIG_FILES([plugins/experimental/header_rewrite/Makefile])
 AC_CONFIG_FILES([plugins/experimental/metalink/Makefile])
 AC_CONFIG_FILES([plugins/experimental/gzip/Makefile])
+AC_CONFIG_FILES([plugins/experimental/spdy/Makefile])
 # various tools
 AC_CONFIG_FILES([tools/Makefile])
 # example plugins

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am
index 408f3b9..025ebce 100644
--- a/plugins/experimental/Makefile.am
+++ b/plugins/experimental/Makefile.am
@@ -23,5 +23,6 @@ SUBDIRS = \
  custom_redirect \
  header_rewrite \
  metalink \
- gzip
+ gzip \
+ spdy
 endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/TODO
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/TODO b/plugins/experimental/spdy/TODO
new file mode 100644
index 0000000..b0c08dd
--- /dev/null
+++ b/plugins/experimental/spdy/TODO
@@ -0,0 +1,20 @@
+- Testing, testing, testing
+
+  Currently, this plugin always crashes when sending a response.
+  It's probably a small fix, but the whole thing is *very* lightly
+  tested.
+
+- SPDY/4 protocol support
+
+  If SPDY/4 ever gets any traction we should implement that. At this point,
+  it's probably worth using a third-party SPDY marshalling library.
+
+- Chunked encoding support
+
+  Figure out what's neede to support origin servers sending chunked
+  requests.
+
+- SPDY client suport
+
+  Is might be useful to support sending SPDY requests to origin
+  servers. This would be necessary for push support, for example.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/http.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/http.cc b/plugins/experimental/spdy/http.cc
new file mode 100644
index 0000000..94cb9ac
--- /dev/null
+++ b/plugins/experimental/spdy/http.cc
@@ -0,0 +1,305 @@
+/*
+ * 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.
+ */
+
+// http.cc - Low level routines to write HTTP messages.
+
+#include <ts/ts.h>
+#include <spdy/spdy.h>
+#include <base/logging.h>
+#include "io.h"
+#include "http.h"
+#include "protocol.h"
+
+static void
+populate_http_headers(
+        TSMBuffer   buffer,
+        TSMLoc      header,
+        spdy::protocol_version version,
+        spdy::key_value_block& kvblock)
+{
+    char status[128];
+    char httpvers[sizeof("HTTP/xx.xx")];
+
+    int vers = TSHttpHdrVersionGet(buffer, header);
+    TSHttpStatus code = TSHttpHdrStatusGet(buffer, header);
+
+    snprintf(status, sizeof(status),
+            "%u %s", (unsigned)code, TSHttpHdrReasonLookup(code));
+    snprintf(httpvers, sizeof(httpvers),
+            "HTTP/%2u.%2u", TS_HTTP_MAJOR(vers), TS_HTTP_MINOR(vers));
+
+    if (version == spdy::PROTOCOL_VERSION_2) {
+        kvblock["status"] = status;
+        kvblock["version"] = httpvers;
+    } else {
+        kvblock[":status"] = status;
+        kvblock[":version"] = httpvers;
+    }
+}
+
+void
+http_send_response(
+        spdy_io_stream *    stream,
+        TSMBuffer           buffer,
+        TSMLoc              header)
+{
+    TSMLoc      field;
+    spdy::key_value_block kvblock;
+
+    debug_http_header(stream, buffer, header);
+
+    field = TSMimeHdrFieldGet(buffer, header, 0);
+    while (field) {
+        TSMLoc next;
+        std::pair<const char *, int> name;
+        std::pair<const char *, int> value;
+
+        name.first = TSMimeHdrFieldNameGet(buffer, header, field, &name.second);
+
+        // XXX zwoop says that pointer comparisons ought to be
+        // sufficient here. If we can test and confirm, we can
+        // remove all the strcmp()s.
+
+        //The Connection, Keep-Alive, Proxy-Connection, and
+        //Transfer-Encoding headers are not valid and MUST not be
+        //sent.
+        if (strcmp(name.first, TS_MIME_FIELD_CONNECTION) == 0 ||
+                strcmp(name.first, TS_MIME_FIELD_KEEP_ALIVE) == 0 ||
+                strcmp(name.first, TS_MIME_FIELD_PROXY_CONNECTION) == 0 ||
+                strcmp(name.first, TS_MIME_FIELD_TRANSFER_ENCODING) == 0) {
+            debug_http("[%p/%u] skipping %s header",
+                    stream->io, stream->stream_id, name.first);
+            goto skip;
+        }
+
+        value.first = TSMimeHdrFieldValueStringGet(buffer, header,
+                field, 0, &value.second);
+        kvblock.insert(std::string(name.first, name.second),
+                std::string(value.first, value.second));
+
+skip:
+       next = TSMimeHdrFieldNext(buffer, header, field);
+       TSHandleMLocRelease(buffer, header, field);
+       field = next;
+    }
+
+    populate_http_headers(buffer, header, stream->version, kvblock);
+    spdy_send_syn_reply(stream, kvblock);
+}
+
+void
+http_send_error(
+        spdy_io_stream  *   stream,
+        TSHttpStatus        status)
+{
+    scoped_mbuffer      buffer;
+    scoped_http_header  header(buffer.get());
+
+    TSHttpHdrTypeSet(buffer.get(), header.get(), TS_HTTP_TYPE_RESPONSE);
+    TSHttpHdrVersionSet(buffer.get(), header.get(), TS_HTTP_VERSION(1, 1));
+    TSHttpHdrStatusSet(buffer.get(), header.get(), status);
+
+    debug_http("[%p/%u] sending a HTTP %d result for %s %s://%s%s",
+            stream->io, stream->stream_id, status,
+            stream->kvblock.url().method.c_str(),
+            stream->kvblock.url().scheme.c_str(),
+            stream->kvblock.url().hostport.c_str(),
+            stream->kvblock.url().path.c_str());
+
+    http_send_response(stream, buffer.get(), header.get());
+    spdy_send_data_frame(stream, spdy::FLAG_FIN, nullptr, 0);
+}
+
+void
+http_send_content(
+        spdy_io_stream *    stream,
+        TSIOBufferReader    reader)
+{
+    TSIOBufferBlock blk;
+    int64_t         consumed = 0;
+
+    blk = TSIOBufferReaderStart(stream->input.reader);
+    while (blk) {
+        const char *    ptr;
+        int64_t         nbytes;
+
+        ptr = TSIOBufferBlockReadStart(blk, reader, &nbytes);
+        if (ptr && nbytes) {
+            spdy_send_data_frame(stream, 0 /* flags */, ptr, nbytes);
+            consumed += nbytes;
+        }
+
+        blk = TSIOBufferBlockNext(blk);
+    }
+
+    TSIOBufferReaderConsume(reader, consumed);
+}
+
+void
+debug_http_header(
+        const spdy_io_stream *  stream,
+        TSMBuffer               buffer,
+        TSMLoc                  header)
+{
+    if (unlikely(TSIsDebugTagSet("spdy.http"))) {
+        spdy_io_buffer  iobuf;
+        int64_t         nbytes;
+        int64_t         avail;
+        const char *    ptr;
+        TSIOBufferBlock blk;
+
+        TSHttpHdrPrint(buffer, header, iobuf.buffer);
+        blk = TSIOBufferStart(iobuf.buffer);
+        avail = TSIOBufferBlockReadAvail(blk, iobuf.reader);
+        ptr = (const char *)TSIOBufferBlockReadStart(blk, iobuf.reader, &nbytes);
+
+        debug_http(
+            "[%p/%u] http request (%" PRIu64 " of %" PRIu64 " bytes):\n%*.*s",
+            stream, stream->stream_id, nbytes, avail,
+            (int)nbytes, (int)nbytes, ptr);
+    }
+}
+
+http_parser::http_parser()
+    : parser(TSHttpParserCreate()), mbuffer(), header(mbuffer.get()), complete(false)
+{
+}
+
+http_parser::~http_parser()
+{
+    if (parser) {
+        TSHttpParserDestroy(parser);
+    }
+}
+
+ssize_t
+http_parser::parse(TSIOBufferReader reader)
+{
+    TSIOBufferBlock blk;
+    ssize_t         consumed = 0;
+
+    for (blk = TSIOBufferReaderStart(reader); blk;
+                blk = TSIOBufferBlockNext(blk)) {
+        const char *    ptr;
+        const char *    end;
+        int64_t         nbytes;
+        TSParseResult   result;
+
+        ptr = TSIOBufferBlockReadStart(blk, reader, &nbytes);
+        if (ptr == nullptr || nbytes == 0) {
+            continue;
+        }
+
+        end = ptr + nbytes;
+        result = TSHttpHdrParseResp(parser, mbuffer.get(), header.get(), &ptr, end);
+        switch (result) {
+        case TS_PARSE_ERROR:
+            return (ssize_t)result;
+        case TS_PARSE_DONE:
+        case TS_PARSE_OK:
+            this->complete = true;
+        case TS_PARSE_CONT:
+            // We consumed the buffer we got minus the remainder.
+            consumed += (nbytes - std::distance(ptr, end));
+        }
+
+        if (this->complete) {
+            break;
+        }
+    }
+
+    TSIOBufferReaderConsume(reader, consumed);
+    return consumed;
+}
+
+static void
+make_ts_http_url(
+        TSMBuffer   buffer,
+        TSMLoc      header,
+        const spdy::key_value_block& kvblock)
+{
+    TSReturnCode    tstatus;
+    TSMLoc          url;
+
+    tstatus = TSHttpHdrUrlGet(buffer, header, &url);
+    if (tstatus == TS_ERROR) {
+        tstatus = TSUrlCreate(buffer, &url);
+    }
+
+    TSUrlSchemeSet(buffer, url,
+            kvblock.url().scheme.data(), kvblock.url().scheme.size());
+    TSUrlHostSet(buffer, url,
+            kvblock.url().hostport.data(), kvblock.url().hostport.size());
+    TSUrlPathSet(buffer, url,
+            kvblock.url().path.data(), kvblock.url().path.size());
+    TSHttpHdrMethodSet(buffer, header,
+            kvblock.url().method.data(), kvblock.url().method.size());
+
+    TSHttpHdrUrlSet(buffer, header, url);
+
+    TSAssert(tstatus == TS_SUCCESS);
+}
+
+static TSMLoc
+make_ts_http_header(
+        TSMBuffer buffer,
+        const spdy::key_value_block& kvblock)
+{
+    scoped_http_header header(buffer);
+
+    TSHttpHdrTypeSet(buffer, header, TS_HTTP_TYPE_REQUEST);
+
+    // XXX extract the real HTTP version header from kvblock.url()
+    TSHttpHdrVersionSet(buffer, header, TS_HTTP_VERSION(1, 1));
+    make_ts_http_url(buffer, header, kvblock);
+
+    // Duplicate the header fields into the MIME header for the HTTP request we
+    // are building.
+    for (auto ptr(kvblock.begin()); ptr != kvblock.end(); ++ptr) {
+        if (ptr->first[0] != ':') {
+            TSMLoc field;
+
+            // XXX Need special handling for duplicate headers; we should
+            // append them as a multi-value
+
+            TSMimeHdrFieldCreateNamed(buffer, header,
+                    ptr->first.c_str(), -1, &field);
+            TSMimeHdrFieldValueStringInsert(buffer, header, field,
+                    -1, ptr->second.c_str(), -1);
+            TSMimeHdrFieldAppend(buffer, header, field);
+        }
+    }
+
+    return header.release();
+}
+
+scoped_http_header::scoped_http_header(
+        TSMBuffer b,
+        const spdy::key_value_block& kvblock)
+    : buffer(b)
+{
+    this->header = make_ts_http_header(buffer, kvblock);
+}
+
+scoped_http_header::scoped_http_header(TSMBuffer b)
+        : header(TS_NULL_MLOC), buffer(b)
+{
+    header = TSHttpHdrCreate(buffer);
+}
+
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/http.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/http.h b/plugins/experimental/spdy/http.h
new file mode 100644
index 0000000..a0f1475
--- /dev/null
+++ b/plugins/experimental/spdy/http.h
@@ -0,0 +1,89 @@
+/*
+ * 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 HTTP_H_E7A06C65_4FCF_46C0_8C97_455BEB9A3DE8
+#define HTTP_H_E7A06C65_4FCF_46C0_8C97_455BEB9A3DE8
+
+struct spdy_io_stream;
+namespace spdy { struct key_value_block; }
+
+// Send a HTTP error response on the given SPDY stream.
+void http_send_error(spdy_io_stream *, TSHttpStatus);
+
+// Send a HTTP response (HTTP header + MIME headers).
+void http_send_response(spdy_io_stream *, TSMBuffer, TSMLoc);
+
+// Send a chunk of the HTTP body content.
+void http_send_content(spdy_io_stream *, TSIOBufferReader);
+
+void debug_http_header(const spdy_io_stream *, TSMBuffer, TSMLoc);
+
+struct scoped_http_header
+{
+    explicit scoped_http_header(TSMBuffer b);
+    scoped_http_header(TSMBuffer, const spdy::key_value_block&);
+
+    scoped_http_header(TSMBuffer b, TSMLoc h)
+            : header(h), buffer(b) {
+    }
+
+    ~scoped_http_header() {
+        if (header != TS_NULL_MLOC) {
+            TSHttpHdrDestroy(buffer, header);
+            TSHandleMLocRelease(buffer, TS_NULL_MLOC, header);
+        }
+    }
+
+    operator bool() const {
+        return buffer != nullptr && header != TS_NULL_MLOC;
+    }
+
+    operator TSMLoc() const {
+        return header;
+    }
+
+    TSMLoc get() {
+        return header;
+    }
+
+    TSMLoc release() {
+        TSMLoc tmp = TS_NULL_MLOC;
+        std::swap(tmp, header);
+        return tmp;
+    }
+
+private:
+    TSMLoc      header;
+    TSMBuffer   buffer;
+};
+
+struct http_parser
+{
+    http_parser();
+    ~http_parser();
+
+    ssize_t parse(TSIOBufferReader);
+
+    TSHttpParser        parser;
+    scoped_mbuffer      mbuffer;
+    scoped_http_header  header;
+    bool                complete;
+};
+
+#endif /* HTTP_H_E7A06C65_4FCF_46C0_8C97_455BEB9A3DE8 */
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/io.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/io.cc b/plugins/experimental/spdy/io.cc
new file mode 100644
index 0000000..4c8ea96
--- /dev/null
+++ b/plugins/experimental/spdy/io.cc
@@ -0,0 +1,87 @@
+/*
+ * 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 <spdy/spdy.h>
+#include "io.h"
+#include <memory>
+
+spdy_io_control::spdy_io_control(TSVConn v)
+    : vconn(v), input(), output(), streams(), last_stream_id(0)
+{
+}
+
+spdy_io_control::~spdy_io_control()
+{
+    TSVConnClose(vconn);
+
+    for (auto ptr(streams.begin()); ptr != streams.end(); ++ptr) {
+        release(ptr->second);
+    }
+}
+
+void
+spdy_io_control::reenable()
+{
+
+    TSVIO vio = TSVConnWriteVIOGet(this->vconn);
+    TSMutex mutex = TSVIOMutexGet(vio);
+
+    TSMutexLock(mutex);
+    TSVIOReenable(vio);
+    TSMutexUnlock(mutex);
+}
+
+bool
+spdy_io_control::valid_client_stream_id(unsigned stream_id) const
+{
+    if (stream_id == 0) { return false; } // must not be zero
+    if ((stream_id % 2) == 0) { return false; } // must be odd
+    return stream_id > last_stream_id;
+}
+
+spdy_io_stream *
+spdy_io_control::create_stream(unsigned stream_id)
+{
+    std::auto_ptr<spdy_io_stream> ptr(new spdy_io_stream(stream_id));
+    std::pair<stream_map_type::iterator, bool> result;
+
+    result = streams.insert(std::make_pair(stream_id, ptr.get()));
+    if (result.second) {
+        // Insert succeeded, hold a refcount on the stream.
+        retain(ptr.get());
+        last_stream_id = stream_id;
+        return ptr.release();
+    }
+
+    // stream-id collision ... fail and autorelease.
+    return NULL;
+}
+
+void
+spdy_io_control::destroy_stream(unsigned stream_id)
+{
+    stream_map_type::iterator ptr(streams.find(stream_id));
+    if (ptr != streams.end()) {
+        std::lock_guard<spdy_io_stream::lock_type> lk(ptr->second->lock);
+        release(ptr->second);
+        streams.erase(ptr);
+    }
+}
+
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/io.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/io.h b/plugins/experimental/spdy/io.h
new file mode 100644
index 0000000..99a1491
--- /dev/null
+++ b/plugins/experimental/spdy/io.h
@@ -0,0 +1,164 @@
+/*
+ * 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 IO_H_C3455D48_1D3C_49C0_BB81_844F4C7946A5
+#define IO_H_C3455D48_1D3C_49C0_BB81_844F4C7946A5
+
+struct spdy_io_stream;
+struct spdy_io_control;
+struct http_parser;
+
+template <typename T, T (*Alloc)(void), TSReturnCode (*Destroy)(T)>
+struct scoped_ts_object
+{
+    scoped_ts_object() : ts(Alloc()) {
+    }
+
+    ~scoped_ts_object() {
+        if (ts) {
+            Destroy(ts);
+        }
+    }
+
+    T get() const {
+        return ts;
+    }
+
+    T release() {
+        T tmp(nullptr);
+        std::swap(ts, tmp);
+        return tmp;
+    }
+
+private:
+    T ts;
+};
+
+typedef scoped_ts_object<TSMBuffer, TSMBufferCreate, TSMBufferDestroy> scoped_mbuffer;
+
+template<> std::string stringof<TSEvent>(const TSEvent&);
+
+#include <base/atomic.h>
+#include "http.h"
+
+struct spdy_io_buffer {
+    TSIOBuffer          buffer;
+    TSIOBufferReader    reader;
+
+    spdy_io_buffer() {
+        buffer = TSIOBufferCreate();
+        reader = TSIOBufferReaderAlloc(buffer);
+    }
+
+    ~spdy_io_buffer() {
+        TSIOBufferReaderFree(reader);
+        TSIOBufferDestroy(buffer);
+    }
+
+    void consume(size_t nbytes) {
+        TSIOBufferReaderConsume(reader, nbytes);
+    }
+
+    void watermark(size_t nbytes) {
+        TSIOBufferWaterMarkSet(buffer, nbytes);
+    }
+
+};
+
+struct spdy_io_stream : public countable
+{
+    enum http_state_type : unsigned {
+        http_resolve_host       = 0x0001,
+        http_send_headers       = 0x0002,
+        http_receive_headers    = 0x0004,
+        http_send_content       = 0x0008,
+        http_receive_content    = 0x0010,
+        http_closed             = 0x0020
+    };
+
+    enum open_options : unsigned {
+        open_none = 0x0000,
+        open_with_system_resolver = 0x0001
+    };
+
+    explicit spdy_io_stream(unsigned);
+    virtual ~spdy_io_stream();
+
+    // Move kv into the stream and start processing it. Return true if the
+    // stream transitions to open state.
+    bool open(spdy::key_value_block&, open_options);
+    void close();
+
+    bool is_closed() const  { return !this->is_open(); }
+    bool is_open() const  { return this->action || this->vconn; }
+
+    typedef std::mutex lock_type;
+
+    unsigned                stream_id;
+    unsigned                http_state;
+
+    // NOTE: The caller *must* hold the stream lock when calling open() or
+    // close(), or processing any stream events.
+    lock_type               lock;
+
+    spdy::protocol_version  version;
+    TSAction                action;
+    TSVConn                 vconn;
+    TSCont                  continuation;
+    spdy::key_value_block   kvblock;
+
+    spdy_io_control *       io;
+    spdy_io_buffer          input;
+    spdy_io_buffer          output;
+    http_parser             hparser;
+
+    static spdy_io_stream * get(TSCont contp) {
+        return (spdy_io_stream *)TSContDataGet(contp);
+    }
+};
+
+struct spdy_io_control : public countable
+{
+    spdy_io_control(TSVConn);
+    ~spdy_io_control();
+
+    // TSVIOReenable() the associated TSVConnection.
+    void reenable();
+
+    bool                valid_client_stream_id(unsigned stream_id) const;
+    spdy_io_stream *    create_stream(unsigned stream_id);
+    void                destroy_stream(unsigned stream_id);
+
+    typedef std::map<unsigned, spdy_io_stream *> stream_map_type;
+
+    TSVConn             vconn;
+    spdy_io_buffer      input;
+    spdy_io_buffer      output;
+    stream_map_type     streams;
+    unsigned            last_stream_id;
+
+    spdy::zstream<spdy::compress>   compressor;
+    spdy::zstream<spdy::decompress> decompressor;
+
+    static spdy_io_control * get(TSCont contp) {
+        return (spdy_io_control *)TSContDataGet(contp);
+    }
+};
+
+#endif /* IO_H_C3455D48_1D3C_49C0_BB81_844F4C7946A5 */
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/base/atomic.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/base/atomic.h b/plugins/experimental/spdy/lib/base/atomic.h
new file mode 100644
index 0000000..56c5ef4
--- /dev/null
+++ b/plugins/experimental/spdy/lib/base/atomic.h
@@ -0,0 +1,57 @@
+/*
+ * 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 ATOMIC_H_A14B912F_B134_4D38_8EC1_51C50EC0FBE6
+#define ATOMIC_H_A14B912F_B134_4D38_8EC1_51C50EC0FBE6
+
+#include <mutex>
+#include <atomic>
+#include <thread>
+
+// Base mixin for reference-countable objects.
+struct countable
+{
+    countable() : refcnt(0) {}
+    virtual ~countable() {}
+
+private:
+    std::atomic<unsigned> refcnt;
+
+    template <typename T> friend T * retain(T * ptr);
+    template <typename T> friend void release(T * ptr);
+};
+
+// Increment the reference count of a countable object, returning it.
+template <typename T> T * retain(T * ptr) {
+    std::atomic_fetch_add_explicit(&ptr->refcnt, 1u, std::memory_order_acq_rel);
+    return ptr;
+}
+
+// Decrement the reference count of a countable object, deleting it if it was
+// the last reference.
+template <typename T> void release(T * ptr) {
+    unsigned count = std::atomic_fetch_sub_explicit(&ptr->refcnt, 1u, std::memory_order_acq_rel);
+    // If the previous refcount was 1, then we have decremented it to 0. We
+    // want to delete in that case.
+    if (count == 1u) {
+        delete ptr;
+    }
+}
+
+#endif /* ATOMIC_H_A14B912F_B134_4D38_8EC1_51C50EC0FBE6 */
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/base/inet.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/base/inet.h b/plugins/experimental/spdy/lib/base/inet.h
new file mode 100644
index 0000000..ee0bb21
--- /dev/null
+++ b/plugins/experimental/spdy/lib/base/inet.h
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INET_H_9452AE2E_D6D2_4B26_AF98_7550DC033E54
+#define INET_H_9452AE2E_D6D2_4B26_AF98_7550DC033E54
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+struct inet_address
+{
+    explicit inet_address(const struct sockaddr * addr) {
+        switch (addr->sa_family) {
+        case AF_INET:
+            memcpy(&sa.storage, addr, sizeof(sockaddr_in));
+            break;
+        case AF_INET6:
+            memcpy(&sa.storage, addr, sizeof(sockaddr_in6));
+            break;
+        default:
+            memset(&sa, 0, sizeof(sa));
+        }
+    }
+
+    uint16_t& port() {
+        switch (sa.storage.ss_family) {
+        case AF_INET:
+            return sa.in.sin_port;
+        case AF_INET6:
+            return sa.in6.sin6_port;
+        default:
+            TSError("invalid inet address type %u", sa.storage.ss_family);
+            abort();
+        }
+    }
+
+    const sockaddr * saddr() const {
+        return &sa.sa;
+    }
+
+private:
+    union {
+        struct sockaddr_in  in;
+        struct sockaddr_in6 in6;
+        struct sockaddr     sa;
+        struct sockaddr_storage storage;
+    } sa;
+};
+
+template <> std::string
+stringof<inet_address>(const inet_address& inaddr) {
+    return cstringof(*inaddr.saddr());
+}
+
+#endif /* INET_H_9452AE2E_D6D2_4B26_AF98_7550DC033E54 */
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/base/logging.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/base/logging.cc b/plugins/experimental/spdy/lib/base/logging.cc
new file mode 100644
index 0000000..918aaa7
--- /dev/null
+++ b/plugins/experimental/spdy/lib/base/logging.cc
@@ -0,0 +1,47 @@
+/*
+ * 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 "logging.h"
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+template <> std::string
+stringof<struct sockaddr>(const sockaddr& sa)
+{
+    char buf[INET6_ADDRSTRLEN + 1];
+    union {
+        const in_addr * in;
+        const in6_addr * in6;
+    } ptr;
+
+    switch (sa.sa_family) {
+    case AF_INET:
+        ptr.in = &((const sockaddr_in *)(&sa))->sin_addr;
+        break;
+    case AF_INET6:
+        ptr.in6 = &((const sockaddr_in6 *)(&sa))->sin6_addr;
+        break;
+    }
+
+    inet_ntop(sa.sa_family, ptr.in, buf, sizeof(buf));
+    return std::string(buf);
+}
+
+/* vim: set sw=4 ts=4 tw=79 et : */
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/base/logging.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/base/logging.h b/plugins/experimental/spdy/lib/base/logging.h
new file mode 100644
index 0000000..9198283
--- /dev/null
+++ b/plugins/experimental/spdy/lib/base/logging.h
@@ -0,0 +1,85 @@
+/*
+ * 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 LOGGING_H_E307AFC6_4429_42F6_8E66_4004C6A78795
+#define LOGGING_H_E307AFC6_4429_42F6_8E66_4004C6A78795
+
+#include <string>
+
+extern "C" {
+
+// TS logging APIs don't get format attributes, so make sure we have a
+// compatible forward declaration.
+void TSDebug(const char *, const char *, ...)
+    __attribute__((format(printf, 2, 3)));
+
+void TSError(const char *, ...)
+    __attribute__((format(printf, 1, 2)));
+
+int TSIsDebugTagSet(const char*);
+}
+
+template <typename T, unsigned N> unsigned countof(const T(&)[N]) {
+    return N;
+}
+
+template <typename T> std::string stringof(const T&);
+#define cstringof(x) stringof(x).c_str()
+
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define likely(x)   __builtin_expect(!!(x), 1)
+
+#define debug_tag(tag, fmt, ...) do { \
+    if (unlikely(TSIsDebugTagSet(tag))) { \
+        TSDebug(tag, fmt, ##__VA_ARGS__); \
+    } \
+} while(0)
+
+#define debug_protocol(fmt, ...) \
+    debug_tag("spdy.protocol", "%s:%d " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define debug_plugin(fmt, ...) \
+    debug_tag("spdy.plugin", "%s:%d " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define debug_http(fmt, ...) \
+    debug_tag("spdy.http", "%s:%d " fmt, __func__, __LINE__, ##__VA_ARGS__)
+
+// Internal logging helpers
+namespace detail {
+
+template <typename T>
+struct named_value
+{
+    const char * name;
+    T value;
+};
+
+template <typename T, unsigned N> const char *
+match(const named_value<T> (&names)[N], const T& value)
+{
+    for (unsigned i = 0; i < N; ++i) {
+        if (names[i].value == value) {
+            return names[i].name;
+        }
+    }
+
+    return "";
+}
+
+}
+
+#endif /* LOGGING_H_E307AFC6_4429_42F6_8E66_4004C6A78795 */
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/spdy/message.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/spdy/message.cc b/plugins/experimental/spdy/lib/spdy/message.cc
new file mode 100644
index 0000000..4521443
--- /dev/null
+++ b/plugins/experimental/spdy/lib/spdy/message.cc
@@ -0,0 +1,535 @@
+/*
+ * 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 "spdy.h"
+#include "zstream.h"
+#include <base/logging.h>
+
+#include <stdexcept>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <functional>
+#include <string.h>
+#include <arpa/inet.h>
+
+template<> std::string
+stringof<spdy::control_frame_type>(const spdy::control_frame_type& ev)
+{
+    static const detail::named_value<unsigned> control_names[] =
+    {
+        { "CONTROL_SYN_STREAM", 1 },
+        { "CONTROL_SYN_REPLY", 2 },
+        { "CONTROL_RST_STREAM", 3 },
+        { "CONTROL_SETTINGS", 4 },
+        { "CONTROL_PING", 6 },
+        { "CONTROL_GOAWAY", 7 },
+        { "CONTROL_HEADERS", 8 },
+        { "CONTROL_WINDOW_UPDATE", 9}
+    };
+
+    return detail::match(control_names, (unsigned)ev);
+}
+
+template<> std::string
+stringof<spdy::error>(const spdy::error& e)
+{
+    static const detail::named_value<unsigned> error_names[] =
+    {
+        { "PROTOCOL_ERROR", 1 },
+        { "INVALID_STREAM", 2 },
+        { "REFUSED_STREAM", 3 },
+        { "UNSUPPORTED_VERSION", 4 },
+        { "CANCEL", 5 },
+        { "FLOW_CONTROL_ERROR", 6 },
+        { "STREAM_IN_USE", 7 },
+        { "STREAM_ALREADY_CLOSED", 8 }
+    };
+
+    return detail::match(error_names, (unsigned)e);
+}
+
+// XXX for the insert and extract we assume that the compiler uses an intrinsic
+// to inline the memcpy() calls. Need to verify this by examining the
+// assembler.
+
+template <typename T> T
+extract(const uint8_t __restrict * &ptr) {
+    T val;
+    memcpy(&val, ptr, sizeof(val));
+    std::advance(ptr, sizeof(val));
+    return val;
+}
+
+template <> uint8_t
+extract<uint8_t>(const uint8_t __restrict * &ptr) {
+    return *ptr++;
+}
+
+template <typename T> void
+insert(const T& val, uint8_t __restrict * &ptr) {
+    memcpy(ptr, &val, sizeof(val));
+    std::advance(ptr, sizeof(val));
+}
+
+static inline uint32_t
+extract_stream_id(const uint8_t __restrict * &ptr)
+{
+    return ntohl(extract<uint32_t>(ptr)) & 0x7fffffffu;
+}
+
+static inline void
+insert_stream_id(uint32_t stream_id, uint8_t __restrict * &ptr)
+{
+    insert<uint32_t>(htonl(stream_id & 0x7fffffffu), ptr);
+}
+
+spdy::message_header
+spdy::message_header::parse(
+        const uint8_t __restrict * ptr, size_t len)
+{
+    message_header header;
+
+    if (len < message_header::size) {
+        throw protocol_error(std::string("short frame header"));
+    }
+
+    header.is_control = ((*ptr) & 0x80u) ? true : false;
+    if (header.is_control) {
+        uint32_t val;
+        header.control.version = ntohs(extract<uint16_t>(ptr)) & 0x7fffu;
+        header.control.type = (control_frame_type)ntohs(extract<uint16_t>(ptr));
+        val = ntohl(extract<uint32_t>(ptr));
+        header.flags = (val >> 24);
+        header.datalen = (val & 0x00ffffffu);
+    } else {
+        uint32_t val;
+        header.data.stream_id = extract_stream_id(ptr);
+        val = ntohl(extract<uint32_t>(ptr));
+        header.flags = (val >> 24);
+        header.datalen = (val & 0x00ffffffu);
+    }
+
+    return header;
+}
+
+size_t
+spdy::message_header::marshall(
+        const message_header& msg, uint8_t __restrict * ptr, size_t len)
+{
+    if (len < message_header::size) {
+        throw protocol_error(std::string("short message_header buffer"));
+    }
+
+    if (msg.is_control) {
+        insert<uint16_t>(htons(0x8000u | msg.control.version), ptr);
+        insert<uint16_t>(htons(msg.control.type), ptr);
+        insert<uint32_t>(htonl((msg.flags << 24) | (msg.datalen & 0x00ffffffu)), ptr);
+    } else {
+        insert_stream_id(msg.data.stream_id, ptr);
+        insert<uint32_t>(htonl((msg.flags << 24) | (msg.datalen & 0x00ffffffu)), ptr);
+    }
+
+    return message_header::size;
+}
+
+spdy::syn_stream_message
+spdy::syn_stream_message::parse(
+        const uint8_t __restrict * ptr, size_t len)
+{
+    syn_stream_message msg;
+
+    if (len < syn_stream_message::size) {
+        throw protocol_error(std::string("short syn_stream message"));
+    }
+
+    msg.stream_id = extract_stream_id(ptr);
+    msg.associated_id = extract_stream_id(ptr);
+    msg.priority = extract<uint8_t>(ptr) >> 5;  // top 3 bits are priority
+    (void)extract<uint8_t>(ptr); // skip unused byte
+    return msg;
+}
+
+spdy::goaway_message
+spdy::goaway_message::parse(
+        const uint8_t __restrict * ptr, size_t len)
+{
+    goaway_message msg;
+
+    if (len < goaway_message::size) {
+        throw protocol_error(std::string("short goaway_stream message"));
+    }
+
+    msg.last_stream_id = extract_stream_id(ptr);
+    msg.status_code = extract_stream_id(ptr);
+    return msg;
+}
+
+spdy::rst_stream_message
+spdy::rst_stream_message::parse(
+        const uint8_t __restrict * ptr, size_t len)
+{
+    rst_stream_message msg;
+
+    if (len < rst_stream_message::size) {
+        throw protocol_error(std::string("short rst_stream message"));
+    }
+
+    msg.stream_id = extract_stream_id(ptr);
+    msg.status_code = extract_stream_id(ptr);
+    return msg;
+}
+
+size_t
+spdy::rst_stream_message::marshall(
+        const rst_stream_message& msg, uint8_t __restrict * ptr, size_t len)
+{
+    if (len < rst_stream_message::size) {
+        throw protocol_error(std::string("short rst_stream buffer"));
+    }
+
+    insert_stream_id(msg.stream_id, ptr);
+    insert<uint32_t>(msg.status_code, ptr);
+    return rst_stream_message::size;
+}
+
+size_t
+spdy::syn_reply_message::marshall(
+        protocol_version            version,
+        const syn_reply_message&    msg,
+        uint8_t *                   ptr,
+        size_t                      len)
+{
+    if (len < size(version)) {
+        throw protocol_error(std::string("short syn_reply buffer"));
+    }
+
+    if (version < PROTOCOL_VERSION_3) {
+        // SPDYv2 has 2 extraneous bytes at the end. How nice that the SPDYv2
+        // spec is no longer on the internets.
+        insert_stream_id(msg.stream_id, ptr);
+        insert<uint16_t>(0, ptr);
+    } else {
+        insert_stream_id(msg.stream_id, ptr);
+    }
+
+    return size(version);
+}
+
+// +------------------------------------+
+// | Number of Name/Value pairs (int32) |
+// +------------------------------------+
+// |     Length of name (int32)         |
+// +------------------------------------+
+// |           Name (string)            |
+// +------------------------------------+
+// |     Length of value  (int32)       |
+// +------------------------------------+
+// |          Value   (string)          |
+// +------------------------------------+
+// |           (repeats)                |
+
+static spdy::zstream_error
+decompress_headers(
+        spdy::zstream<spdy::decompress>& decompressor,
+        std::vector<uint8_t>& bytes)
+{
+    ssize_t nbytes;
+
+    do {
+        size_t avail;
+        size_t old = bytes.size();
+        bytes.resize(bytes.size() + getpagesize());
+        avail = bytes.size() - old;
+        nbytes = decompressor.consume(&bytes[old], avail);
+        if (nbytes > 0) {
+            bytes.resize(old + nbytes);
+        } else {
+            bytes.resize(old);
+        }
+    } while (nbytes > 0);
+
+    if (nbytes < 0) {
+        return (spdy::zstream_error)(-nbytes);
+    }
+
+    return spdy::z_ok;
+}
+
+static ssize_t
+marshall_string_v2(
+        spdy::zstream<spdy::compress>&  compressor,
+        const std::string&              strval,
+        uint8_t *                       ptr,
+        size_t                          len,
+        unsigned                        flags)
+{
+    size_t      nbytes = 0;
+    ssize_t     status;
+    uint16_t    tmp16;
+
+    tmp16 = htons(strval.size());
+    compressor.input(&tmp16, sizeof(tmp16));
+    status = compressor.consume(ptr + nbytes, len - nbytes, flags);
+    if (status < 0) {
+        return status;
+    }
+
+    nbytes += status;
+
+    compressor.input(strval.c_str(), strval.size());
+    status = compressor.consume(ptr + nbytes, len - nbytes, flags);
+    if (status < 0) {
+        return status;
+    }
+
+    nbytes += status;
+    return nbytes;
+}
+
+static ssize_t
+marshall_name_value_pairs_v2(
+        spdy::zstream<spdy::compress>&  compressor,
+        const spdy::key_value_block&    kvblock,
+        uint8_t *                       ptr,
+        size_t                          len)
+{
+    size_t      nbytes = 0;
+    ssize_t     status;
+    uint16_t    tmp16;
+
+    tmp16 = htons(kvblock.size());
+    compressor.input(&tmp16, sizeof(tmp16));
+    status = compressor.consume(ptr + nbytes, len - nbytes, 0);
+    if (status < 0) {
+        return status;
+    }
+
+    nbytes += status;
+
+    for (auto kv(kvblock.begin()); kv != kvblock.end(); ++kv) {
+        status = marshall_string_v2(
+                compressor, kv->first, ptr + nbytes, len - nbytes, 0);
+        if (status < 0) {
+            return status;
+        }
+
+        nbytes += status;
+
+        status = marshall_string_v2(
+                compressor, kv->second, ptr + nbytes, len - nbytes, 0);
+        if (status < 0) {
+            return status;
+        }
+
+        nbytes += status;
+    }
+
+    do  {
+        status = compressor.consume(ptr + nbytes, len - nbytes, Z_SYNC_FLUSH);
+        if (status < 0) {
+            return status;
+        }
+        nbytes += status;
+    } while (status != 0);
+
+    return nbytes;
+}
+
+static spdy::key_value_block
+parse_name_value_pairs_v2(
+        const uint8_t __restrict * ptr, size_t len)
+{
+    int32_t npairs;
+    const uint8_t __restrict * end = ptr + len;
+
+    spdy::key_value_block kvblock;
+
+    if (len < sizeof(int32_t)) {
+        // XXX throw
+    }
+
+    npairs = ntohs(extract<int16_t>(ptr));
+    if (npairs < 1) {
+        //
+    }
+
+    while (npairs--) {
+        std::string key;
+        std::string val;
+        int32_t nbytes;
+
+        if (std::distance(ptr, end) < 8) {
+            // XXX
+        }
+
+        nbytes = ntohs(extract<uint16_t>(ptr));
+        if (std::distance(ptr, end) < nbytes) {
+            // XXX
+        }
+
+        key.assign((const char *)ptr, nbytes);
+        std::advance(ptr, nbytes);
+
+        nbytes = ntohs(extract<uint16_t>(ptr));
+        if (std::distance(ptr, end) < nbytes) {
+            // XXX
+        }
+
+        val.assign((const char *)ptr, nbytes);
+        std::advance(ptr, nbytes);
+
+        // XXX Extract this assignment section into a lambda. This would let us
+        // parse the kvblock into a key_value_block, or straight into the
+        // corresponding ATS data structures.
+        if (key == "host") {
+            kvblock.url().hostport = val;
+        } else if (key == "scheme") {
+            kvblock.url().scheme = val;
+        } else if (key == "url") {
+            kvblock.url().path = val;
+        } else if (key == "method") {
+            kvblock.url().method = val;
+        } else if (key == "version") {
+            kvblock.url().version = val;
+        } else {
+            kvblock.headers[key] = val;
+        }
+    }
+
+    return kvblock;
+}
+
+spdy::key_value_block
+spdy::key_value_block::parse(
+        protocol_version            version,
+        zstream<decompress>&        decompressor,
+        const uint8_t __restrict *  ptr,
+        size_t                      len)
+{
+    std::vector<uint8_t>    bytes;
+    key_value_block         kvblock;
+
+    if (version != PROTOCOL_VERSION_2) {
+        // XXX support v3 and throw a proper damn error.
+        throw std::runtime_error("unsupported version");
+    }
+
+    decompressor.input(ptr, len);
+    if (decompress_headers(decompressor, bytes) != z_ok) {
+        // XXX
+    }
+
+    return parse_name_value_pairs_v2(&bytes[0], bytes.size());
+}
+
+size_t
+spdy::key_value_block::marshall(
+        protocol_version            version,
+        spdy::zstream<compress>&    compressor,
+        const key_value_block&      kvblock,
+        uint8_t *                   ptr,
+        size_t                      len)
+{
+    ssize_t nbytes;
+
+    if (version != PROTOCOL_VERSION_2) {
+        // XXX support v3 and throw a proper damn error.
+        throw std::runtime_error("unsupported version");
+    }
+
+    nbytes = marshall_name_value_pairs_v2(compressor, kvblock, ptr, len);
+    if (nbytes < 0) {
+        throw std::runtime_error("marshalling failure");
+    }
+
+    return nbytes;
+}
+
+size_t
+spdy::key_value_block::nbytes(protocol_version version) const
+{
+    size_t nbytes = 0;
+    size_t lensz;
+
+    // Length fields are 2 bytes in SPDYv2 and 4 in later versions.
+    switch (version) {
+    case PROTOCOL_VERSION_3: lensz = 4; break;
+    case PROTOCOL_VERSION_2: lensz = 2; break;
+    default:
+        throw std::runtime_error("unsupported version");
+    }
+
+    nbytes += lensz;
+    for (auto ptr(begin()); ptr != end(); ++ptr) {
+        nbytes += lensz + ptr->first.size();
+        nbytes += lensz + ptr->second.size();
+    }
+
+    return nbytes;
+}
+
+struct lowercase : public std::unary_function<char, char>
+{
+    char operator() (char c) const {
+        // Return the lowercase ASCII only if it's in the uppercase
+        // ASCII range.
+        if (c > 0x40 && c < 0x5b) {
+            return c + 0x20;
+        }
+
+        return c;
+    }
+};
+
+void
+spdy::key_value_block::insert(
+        std::string key,
+        std::string value)
+{
+    std::transform(key.begin(), key.end(), key.begin(), lowercase());
+    headers[key] = value;
+}
+
+spdy::ping_message
+spdy::ping_message::parse(
+        const uint8_t __restrict * ptr, size_t len)
+{
+    ping_message msg;
+
+    if (len < ping_message::size) {
+        throw protocol_error(std::string("short ping message"));
+    }
+
+    msg.ping_id = ntohl(extract<uint32_t>(ptr));
+    return msg;
+}
+
+size_t
+spdy::ping_message::marshall(
+        const ping_message& msg, uint8_t __restrict * ptr, size_t len)
+{
+    if (len < ping_message::size) {
+        throw protocol_error(std::string("short ping_message buffer"));
+    }
+
+    insert<uint32_t>(htonl(msg.ping_id), ptr);
+    return ping_message::size;
+}
+
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/spdy/spdy.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/spdy/spdy.h b/plugins/experimental/spdy/lib/spdy/spdy.h
new file mode 100644
index 0000000..90bea0f
--- /dev/null
+++ b/plugins/experimental/spdy/lib/spdy/spdy.h
@@ -0,0 +1,291 @@
+/*
+ * 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 SPDY_H_57211D6A_F320_42E3_8205_89E651B4A5DB
+#define SPDY_H_57211D6A_F320_42E3_8205_89E651B4A5DB
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdexcept>
+#include <string>
+#include <map>
+
+#include "zstream.h"
+
+namespace spdy {
+
+    enum protocol_version : unsigned {
+        PROTOCOL_VERSION_2 = 2,
+        PROTOCOL_VERSION_3 = 3
+    };
+
+    enum : unsigned {
+        PROTOCOL_VERSION = 3,
+        MAX_FRAME_LENGTH = (1u << 24)
+    };
+
+    enum : unsigned {
+        FLAG_FIN            = 1,
+        FLAG_COMPRESSED     = 2
+   };
+
+    struct protocol_error : public std::runtime_error {
+        explicit protocol_error(const std::string& msg)
+            : std::runtime_error(msg) {
+        }
+    };
+
+    enum control_frame_type : unsigned {
+        CONTROL_SYN_STREAM      = 1,
+        CONTROL_SYN_REPLY       = 2,
+        CONTROL_RST_STREAM      = 3,
+        CONTROL_SETTINGS        = 4,
+        CONTROL_PING            = 6,
+        CONTROL_GOAWAY          = 7,
+        CONTROL_HEADERS         = 8,
+        CONTROL_WINDOW_UPDATE   = 9
+    };
+
+
+    enum error : unsigned {
+        PROTOCOL_ERROR        = 1,
+        INVALID_STREAM        = 2,
+        REFUSED_STREAM        = 3,
+        UNSUPPORTED_VERSION   = 4,
+        CANCEL                = 5,
+        FLOW_CONTROL_ERROR    = 6,
+        STREAM_IN_USE         = 7,
+        STREAM_ALREADY_CLOSED = 8
+    };
+
+    // Control frame header:
+    // +----------------------------------+
+    // |C| Version(15bits) | Type(16bits) |
+    // +----------------------------------+
+    // | Flags (8)  |  Length (24 bits)   |
+    // +----------------------------------+
+    // |               Data               |
+    // +----------------------------------+
+    //
+    // Data frame header:
+    // +----------------------------------+
+    // |C|       Stream-ID (31bits)       |
+    // +----------------------------------+
+    // | Flags (8)  |  Length (24 bits)   |
+    // +----------------------------------+
+    // |               Data               |
+    // +----------------------------------+
+
+    struct message_header
+    {
+        union {
+            struct {
+                unsigned version;
+                control_frame_type type;
+            } control;
+            struct {
+                unsigned stream_id;
+            } data;
+        };
+
+        bool        is_control;
+        uint8_t     flags;
+        uint32_t    datalen;
+
+        static message_header parse(const uint8_t *, size_t);
+        static size_t marshall(const message_header&, uint8_t *, size_t);
+        enum : unsigned { size = 8 }; /* bytes */
+    };
+
+    // SYN_STREAM frame:
+    //
+    // +------------------------------------+
+    // |1|    version    |         1        |
+    // +------------------------------------+
+    // |  Flags (8)  |  Length (24 bits)    |
+    // +------------------------------------+
+    // |X|           Stream-ID (31bits)     |
+    // +------------------------------------+
+    // |X| Associated-To-Stream-ID (31bits) |
+    // +------------------------------------+
+    // |  Pri | Unused | Header Count(int16)|
+    // +------------------------------------+   <+
+    // |     Length of name (int32)         |    | This section is the "Name/Value
+    // +------------------------------------+    | Header Block", and is compressed.
+    // |           Name (string)            |    |
+    // +------------------------------------+    |
+    // |     Length of value  (int32)       |    |
+    // +------------------------------------+    |
+    // |          Value   (string)          |    |
+    // +------------------------------------+    |
+    // |           (repeats)                |   <+
+
+    struct syn_stream_message
+    {
+        unsigned stream_id;
+        unsigned associated_id;
+        unsigned priority;
+        unsigned header_count;
+
+        static syn_stream_message parse(const uint8_t *, size_t);
+        enum : unsigned { size = 10 }; /* bytes */
+    };
+
+    // SYN_REPLY frame:
+    //
+    // +------------------------------------+
+    // |1|    version    |         2        |
+    // +------------------------------------+
+    // |  Flags (8)  |  Length (24 bits)    |
+    // +------------------------------------+
+    // |X|           Stream-ID (31bits)     |
+    // +------------------------------------+
+    // | Number of Name/Value pairs (int32) |   <+
+    // +------------------------------------+    |
+    // |     Length of name (int32)         |    | This section is the "Name/Value
+    // +------------------------------------+    | Header Block", and is compressed.
+    // |           Name (string)            |    |
+    // +------------------------------------+    |
+    // |     Length of value  (int32)       |    |
+    // +------------------------------------+    |
+    // |          Value   (string)          |    |
+    // +------------------------------------+    |
+    // |           (repeats)                |   <+
+
+    struct syn_reply_message
+    {
+        unsigned stream_id;
+
+        static syn_stream_message parse(const uint8_t *, size_t);
+        static size_t marshall(protocol_version, const syn_reply_message&, uint8_t *, size_t);
+
+        static unsigned size(protocol_version v) {
+            return (v == PROTOCOL_VERSION_2) ? 6 : 4; /* bytes */
+        }
+    };
+
+    // GOAWAY frame:
+    //
+    // +----------------------------------+
+    // |1|   version    |         7       |
+    // +----------------------------------+
+    // | 0 (flags) |     8 (length)       |
+    // +----------------------------------|
+    // |X|  Last-good-stream-ID (31 bits) |
+    // +----------------------------------+
+    // |          Status code             |
+    // +----------------------------------+
+
+    struct goaway_message
+    {
+        unsigned last_stream_id;
+        unsigned status_code;
+
+        static goaway_message parse(const uint8_t *, size_t);
+        enum : unsigned { size = 8 }; /* bytes */
+    };
+
+    struct rst_stream_message
+    {
+        unsigned stream_id;
+        unsigned status_code;
+
+        static rst_stream_message parse(const uint8_t *, size_t);
+        static size_t marshall(const rst_stream_message&, uint8_t *, size_t);
+        enum : unsigned { size = 8 }; /* bytes */
+    };
+
+    struct ping_message
+    {
+        unsigned ping_id;
+
+        static ping_message parse(const uint8_t *, size_t);
+        static size_t marshall(const ping_message&, uint8_t *, size_t);
+        enum : unsigned { size = 4 }; /* bytes */
+    };
+
+    struct url_components
+    {
+        std::string method;
+        std::string scheme;
+        std::string hostport;
+        std::string path;
+        std::string version;
+
+        bool is_complete() const {
+            return !(method.empty() && scheme.empty() && hostport.empty() &&
+                    path.empty() && version.empty());
+        }
+    };
+
+    struct key_value_block
+    {
+        typedef std::map<std::string, std::string> map_type;
+        typedef map_type::const_iterator const_iterator;
+        typedef map_type::iterator iterator;
+
+        map_type::size_type size() const {
+            return headers.size();
+        }
+
+        bool exists(const std::string& key) const {
+            return headers.find(key) != headers.end();
+        }
+
+        // Insert the lower-cased key.
+        void insert(std::string key, std::string value);
+
+        std::string& operator[] (const std::string& key) {
+            return headers[key];
+        }
+
+        const std::string& operator[] (const std::string& key) const {
+            return headers[key];
+        }
+
+        // Return the number of marshalling bytes this kvblock needs.
+        size_t nbytes(protocol_version) const;
+
+        const_iterator begin() const { return headers.begin(); }
+        const_iterator end() const { return headers.end(); }
+
+        url_components& url() { return components; }
+        const url_components& url() const { return components; }
+
+        url_components components;
+        mutable /* XXX */ map_type headers;
+
+        static key_value_block parse(protocol_version, zstream<decompress>&,
+                const uint8_t *, size_t);
+        static size_t marshall(protocol_version, zstream<compress>&,
+                const key_value_block&, uint8_t *, size_t);
+    };
+
+
+} // namespace spdy
+
+template <typename T> std::string stringof(const T&);
+
+template<> std::string
+stringof<spdy::control_frame_type>(const spdy::control_frame_type&);
+
+template<> std::string
+stringof<spdy::error>(const spdy::error&);
+
+#endif /* SPDY_H_57211D6A_F320_42E3_8205_89E651B4A5DB */
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/spdy/zstream.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/spdy/zstream.cc b/plugins/experimental/spdy/lib/spdy/zstream.cc
new file mode 100644
index 0000000..3f67b01
--- /dev/null
+++ b/plugins/experimental/spdy/lib/spdy/zstream.cc
@@ -0,0 +1,123 @@
+/*
+ * 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 "zstream.h"
+
+namespace spdy {
+
+const uint8_t dictionary[] =
+"optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
+"languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
+"f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
+"-agent10010120020120220320420520630030130230330430530630740040140240340440"
+"5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
+"glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
+"ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
+"sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
+"oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
+"ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
+"pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
+"ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
+".1statusversionurl";
+
+#if NOTYET
+unsigned long dictionary_id()
+{
+    unsigned long id;
+
+    id = adler32(0L, Z_NULL, 0);
+    id = adler32(id, dictionary, sizeof(dictionary));
+    return id;
+}
+#endif
+
+static zstream_error map_zerror(int error)
+{
+    const zstream_error z_errors[] =
+    {
+        z_version_error,    // Z_VERSION_ERROR  (-6)
+        z_buffer_error,     // Z_BUF_ERROR      (-5)
+        z_memory_error,     // Z_MEM_ERROR      (-4)
+        z_data_error,       // Z_DATA_ERROR     (-3)
+        z_stream_error,     // Z_STREAM_ERROR   (-2)
+        z_errno,            // Z_ERRNO          (-1)
+        z_ok,               // Z_OK             ( 0)
+        z_stream_end,       // Z_STREAM_END     ( 1)
+        z_need_dict         // Z_NEED_DICT      ( 2)
+    };
+    const zstream_error * z = &z_errors[6];
+    return z[error];
+}
+
+zstream_error decompress::init(z_stream * zstr)
+{
+    return map_zerror(inflateInit(zstr));
+}
+
+zstream_error decompress::transact(z_stream * zstr, int flush)
+{
+    int ret = inflate(zstr, flush);
+    if (ret == Z_NEED_DICT) {
+        // The spec says that the trailing NULL is not included in the
+        // dictionary, but in practice, Chrome does include it.
+        ret = inflateSetDictionary(zstr, dictionary, sizeof(dictionary));
+        if (ret == Z_OK) {
+            ret = inflate(zstr, flush);
+        }
+    }
+
+    return map_zerror(ret);
+}
+
+zstream_error decompress::destroy(z_stream * zstr)
+{
+    return map_zerror(inflateEnd(zstr));
+}
+
+zstream_error compress::init(z_stream * zstr)
+{
+    zstream_error status;
+
+    status = map_zerror(deflateInit(zstr, Z_DEFAULT_COMPRESSION));
+    if (status != z_ok) {
+        return status;
+    }
+
+    return map_zerror(deflateSetDictionary(zstr, dictionary, sizeof(dictionary)));
+}
+
+zstream_error compress::transact(z_stream * zstr, int flush)
+{
+    int ret = deflate(zstr, flush);
+    if (ret == Z_NEED_DICT) {
+        ret = deflateSetDictionary(zstr, dictionary, sizeof(dictionary));
+        if (ret == Z_OK) {
+            ret = deflate(zstr, flush);
+        }
+    }
+
+    return map_zerror(ret);
+}
+
+zstream_error compress::destroy(z_stream * zstr)
+{
+    return map_zerror(deflateEnd(zstr));
+}
+
+} // namespace spdy
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/lib/spdy/zstream.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/lib/spdy/zstream.h b/plugins/experimental/spdy/lib/spdy/zstream.h
new file mode 100644
index 0000000..d7ec6fc
--- /dev/null
+++ b/plugins/experimental/spdy/lib/spdy/zstream.h
@@ -0,0 +1,119 @@
+/*
+ * 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 ZSTREAM_H_EA418AC6_C57B_4597_9748_7C11D04B6586
+#define ZSTREAM_H_EA418AC6_C57B_4597_9748_7C11D04B6586
+
+#include <inttypes.h>
+#include <zlib.h>
+#include <string.h>
+
+namespace spdy {
+
+enum zstream_error
+{
+    z_ok = 0,
+    z_stream_end,
+    z_need_dict,
+    z_errno,
+    z_stream_error,
+    z_data_error,
+    z_memory_error,
+    z_buffer_error,
+    z_version_error
+};
+
+template <typename ZlibMechanism>
+struct zstream : public ZlibMechanism
+{
+    zstream() {
+        memset(&stream, 0, sizeof(stream));
+        stream.zalloc = Z_NULL;
+        stream.zfree = Z_NULL;
+        stream.opaque = Z_NULL;
+        ZlibMechanism::init(&stream);
+    }
+
+    bool drained() const {
+        return stream.avail_in == 0;
+    }
+
+    template <typename T, typename N>
+    void input(T * ptr, N nbytes) {
+        stream.next_in = (uint8_t *)ptr;
+        stream.avail_in = nbytes;
+    }
+
+    // Consume the input without producing any output.
+    zstream_error consume() {
+        zstream_error ret;
+        stream.next_out = (uint8_t *)1;
+        stream.avail_out = 0;
+        ret = ZlibMechanism::transact(&stream, 0);
+        return (ret == z_buffer_error) ? z_ok : ret;
+    }
+
+    // Return the number of output bytes or negative zstream_error on failure.
+    template <typename T, typename N>
+    ssize_t consume(T * ptr, N nbytes, unsigned flags = Z_SYNC_FLUSH) {
+        zstream_error ret;
+        stream.next_out = (uint8_t *)ptr;
+        stream.avail_out = nbytes;
+
+        ret = ZlibMechanism::transact(&stream, flags);
+        if (ret == z_buffer_error) {
+            return 0;
+        }
+
+        if (ret == z_ok || ret == z_stream_end) {
+            // return the number of bytes produced
+            return nbytes - stream.avail_out;
+        }
+
+        return -ret;
+    }
+
+    ~zstream() {
+        ZlibMechanism::destroy(&stream);
+    }
+
+private:
+    zstream(const zstream&); // disable
+    zstream& operator=(const zstream&); // disable
+
+    z_stream stream;
+};
+
+struct decompress
+{
+    zstream_error init(z_stream * zstr);
+    zstream_error transact(z_stream * zstr, int flush);
+    zstream_error destroy(z_stream * zstr);
+};
+
+struct compress
+{
+    zstream_error init(z_stream * zstr);
+    zstream_error transact(z_stream * zstr, int flush);
+    zstream_error destroy(z_stream * zstr);
+};
+
+} // namespace spdy
+
+#endif /* ZSTREAM_H_EA418AC6_C57B_4597_9748_7C11D04B6586 */
+/* vim: set sw=4 ts=4 tw=79 et : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/protocol.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/protocol.cc b/plugins/experimental/spdy/protocol.cc
new file mode 100644
index 0000000..219af5b
--- /dev/null
+++ b/plugins/experimental/spdy/protocol.cc
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// protocol.cc - Low level routines to write SPDY frames.
+
+#include <ts/ts.h>
+#include <spdy/spdy.h>
+#include <base/logging.h>
+#include "io.h"
+#include "protocol.h"
+
+#include <algorithm>
+#include <vector>
+#include <sys/param.h> // MAX
+
+void
+spdy_send_reset_stream(
+        spdy_io_control *   io,
+        unsigned            stream_id,
+        spdy::error         status)
+{
+    spdy::message_header hdr;
+    spdy::rst_stream_message rst;
+
+    uint8_t     buffer[spdy::message_header::size + spdy::rst_stream_message::size];
+    uint8_t *   ptr = buffer;
+    size_t      nbytes = 0;
+
+    hdr.is_control = true;
+    hdr.control.version = spdy::PROTOCOL_VERSION;
+    hdr.control.type = spdy::CONTROL_RST_STREAM;
+    hdr.flags = 0;
+    hdr.datalen = spdy::rst_stream_message::size;
+    rst.stream_id = stream_id;
+    rst.status_code = status;
+
+    nbytes += spdy::message_header::marshall(hdr, ptr, sizeof(buffer));
+    nbytes += spdy::rst_stream_message::marshall(rst, ptr, sizeof(buffer) - nbytes);
+
+    debug_protocol("[%p/%u] sending %s stream %u with error %s",
+            io, stream_id, cstringof(hdr.control.type), stream_id, cstringof(status));
+    TSIOBufferWrite(io->output.buffer, buffer, nbytes);
+}
+
+void
+spdy_send_syn_reply(
+        spdy_io_stream * stream,
+        const spdy::key_value_block& kvblock)
+{
+    union {
+        spdy::message_header hdr;
+        spdy::syn_reply_message syn;
+    } msg;
+
+    uint8_t     buffer[
+        MAX((unsigned)spdy::message_header::size, (unsigned)spdy::syn_stream_message::size)];
+    size_t      nbytes = 0;
+
+    std::vector<uint8_t> hdrs;
+
+    // Compress the kvblock into a temp buffer before we start. We need to know
+    // the size of this so we can fill in the datalen field. Since there's no
+    // way to go back and rewrite the data length into the TSIOBuffer, we need
+    // to use a temporary copy.
+    hdrs.resize(kvblock.nbytes(stream->version));
+    nbytes = spdy::key_value_block::marshall(stream->version,
+            stream->io->compressor, kvblock, &hdrs[0], hdrs.capacity());
+    hdrs.resize(nbytes);
+
+    msg.hdr.is_control = true;
+    msg.hdr.control.version = stream->version;
+    msg.hdr.control.type = spdy::CONTROL_SYN_REPLY;
+    msg.hdr.flags = 0;
+    msg.hdr.datalen = spdy::syn_reply_message::size(stream->version) + hdrs.size();
+    nbytes = TSIOBufferWrite(stream->io->output.buffer, buffer,
+            spdy::message_header::marshall(msg.hdr, buffer, sizeof(buffer)));
+
+    msg.syn.stream_id = stream->stream_id;
+    nbytes += TSIOBufferWrite(stream->io->output.buffer, buffer,
+            spdy::syn_reply_message::marshall(stream->version,
+                        msg.syn, buffer, sizeof(buffer)));
+
+    nbytes += TSIOBufferWrite(stream->io->output.buffer, &hdrs[0], hdrs.size());
+    debug_protocol("[%p/%u] sending %s hdr.datalen=%u",
+           stream->io, stream->stream_id, cstringof(spdy::CONTROL_SYN_REPLY),
+           (unsigned)msg.hdr.datalen);
+}
+
+void
+spdy_send_data_frame(
+        spdy_io_stream *    stream,
+        unsigned            flags,
+        const void *        ptr,
+        size_t              nbytes)
+{
+    spdy::message_header    hdr;
+    uint8_t                 buffer[spdy::message_header::size];
+    std::vector<uint8_t>    tmp;
+    ssize_t                 ret;
+
+    TSReleaseAssert(nbytes < spdy::MAX_FRAME_LENGTH);
+
+    // XXX If we are compressing the data, we need to make a temporary copy.
+    // When there is an ATS API that will let us rewrite the header, then we
+    // can marshall straight into the TSIOBiffer.
+    if (flags & spdy::FLAG_COMPRESSED) {
+        tmp.resize(nbytes + 64);
+        stream->io->compressor.input(ptr, nbytes);
+        nbytes = 0;
+
+        do {
+            ret = stream->io->compressor.consume(&tmp[nbytes], tmp.size() - nbytes);
+            if (ret > 0) {
+                nbytes += ret;
+            }
+        } while (ret > 0);
+
+        tmp.resize(nbytes);
+    }
+
+    hdr.is_control = false;
+    hdr.flags = flags;
+    hdr.datalen = nbytes;
+    hdr.data.stream_id = stream->stream_id;
+
+    spdy::message_header::marshall(hdr, buffer, sizeof(buffer));
+    TSIOBufferWrite(stream->io->output.buffer, buffer, spdy::message_header::size);
+
+    if (nbytes) {
+        if (flags & spdy::FLAG_COMPRESSED) {
+            TSIOBufferWrite(stream->io->output.buffer, &tmp[0], nbytes);
+        } else {
+            TSIOBufferWrite(stream->io->output.buffer, ptr, nbytes);
+        }
+    }
+
+    debug_protocol("[%p/%u] sending DATA flags=%x hdr.datalen=%u",
+            stream->io, stream->stream_id, flags, (unsigned)hdr.datalen);
+}
+
+void
+spdy_send_ping(
+        spdy_io_control *       io,
+        spdy::protocol_version  version,
+        unsigned                ping_id)
+{
+    union {
+        spdy::message_header    hdr;
+        spdy::ping_message      ping;
+    } msg;
+
+    size_t                  nbytes = 0;
+    uint8_t buffer[spdy::ping_message::size + spdy::message_header::size];
+
+    msg.hdr.is_control = true;
+    msg.hdr.control.version = version;
+    msg.hdr.control.type = spdy::CONTROL_PING;
+    msg.hdr.flags = 0;
+    msg.hdr.datalen = spdy::ping_message::size;
+    nbytes += spdy::message_header::marshall(
+            msg.hdr, buffer + nbytes, sizeof(buffer) - nbytes);
+
+    msg.ping.ping_id = ping_id;
+    nbytes += spdy::ping_message::marshall(
+            msg.ping, buffer + nbytes, sizeof(buffer) - nbytes);
+
+    TSIOBufferWrite(io->output.buffer, buffer, nbytes);
+
+    debug_protocol("[%p] sending PING id=%u", io, msg.ping.ping_id);
+}
+
+/* vim: set sw=4 tw=79 ts=4 et ai : */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/abe55a68/plugins/experimental/spdy/protocol.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/spdy/protocol.h b/plugins/experimental/spdy/protocol.h
new file mode 100644
index 0000000..2b1a4be
--- /dev/null
+++ b/plugins/experimental/spdy/protocol.h
@@ -0,0 +1,47 @@
+/*
+ * 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 PROTOCOL_H_46E29A3D_9EE6_4C4F_A355_FF42DE19EF18
+#define PROTOCOL_H_46E29A3D_9EE6_4C4F_A355_FF42DE19EF18
+
+void
+spdy_send_reset_stream(
+        spdy_io_control *   io,
+        unsigned            stream_id,
+        spdy::error         status);
+
+void
+spdy_send_syn_reply(
+        spdy_io_stream * stream,
+        const spdy::key_value_block& kvblock);
+
+void
+spdy_send_data_frame(
+        spdy_io_stream *    stream,
+        unsigned            flags,
+        const void *        ptr,
+        size_t              nbytes);
+
+void
+spdy_send_ping(
+        spdy_io_control *       io,
+        spdy::protocol_version  version,
+        unsigned                ping_id);
+
+#endif /* PROTOCOL_H_46E29A3D_9EE6_4C4F_A355_FF42DE19EF18 */
+/* vim: set sw=4 ts=4 tw=79 et : */


Mime
View raw message