You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2014/04/20 21:20:49 UTC

[32/50] [abbrv] git commit: TS-2431: Migrate Taobao SPDY plugin to ATS core

TS-2431: Migrate Taobao SPDY plugin to ATS core

This path depends on FetchSM, so seriously speaking, the migration
to ATS core is finished *partially*. I had tried to remove the dependency
of FetchSM and create a specific VC for each http request in spdy sm, but I
found it was not so easy, so I give up temporary. Let me push this
patch to community before it's perfect enough, at least, this series
patches can statisfy TAOBAO's current demand.

With this patch, ATS supports SPDY(v3/v3.1), the SPDY can run with/without
SSL:
 1) SPDY without SSL:
    It will share the same port number with HTTP, and ATS will recognize
    SPDY/HTTP by detecting the first byte of client request.

 2) SPDY with SSL:
    When running SPDY with SSL, the code needs OpenSSL(>=1.01), if this
    version of OpenSSL is installed in user-defined DIR, you may need to
    use '--with-openssl=<dir>' option to tell ATS where to search.

Signed-off-by: Yunkai Zhang <qi...@taobao.com>


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

Branch: refs/heads/lua_config
Commit: 79dd5035f9d361b5e0c8a2b4f71d2bd8dc243fb3
Parents: f90f3a4
Author: Yunkai Zhang <qi...@taobao.com>
Authored: Mon Mar 17 02:58:50 2014 +0800
Committer: Yunkai Zhang <qi...@taobao.com>
Committed: Fri Mar 21 01:31:27 2014 +0800

----------------------------------------------------------------------
 iocore/net/SSLNextProtocolSet.cc  |   3 +-
 iocore/net/UnixNetAccept.cc       |   8 +-
 lib/ts/apidefs.h.in               |   1 +
 proxy/InkAPI.cc                   |   5 +-
 proxy/http/HttpProxyServerMain.cc |  13 +-
 proxy/spdy/Makefile.am            |  10 +
 proxy/spdy/P_SpdyCallbacks.h      | 248 ++++++++++++++++
 proxy/spdy/P_SpdyCommon.h         |  75 +++++
 proxy/spdy/P_SpdySM.h             | 129 +++++++++
 proxy/spdy/SpdyAcceptCont.cc      |  14 +-
 proxy/spdy/SpdyCallbacks.cc       | 513 +++++++++++++++++++++++++++++++++
 proxy/spdy/SpdyCommon.cc          | 136 +++++++++
 proxy/spdy/SpdySM.cc              | 415 ++++++++++++++++++++++++++
 13 files changed, 1562 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/iocore/net/SSLNextProtocolSet.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNextProtocolSet.cc b/iocore/net/SSLNextProtocolSet.cc
index ed0a5e3..c0ff61c 100644
--- a/iocore/net/SSLNextProtocolSet.cc
+++ b/iocore/net/SSLNextProtocolSet.cc
@@ -185,7 +185,8 @@ SSLNextProtocolSet::NextProtocolEndpoint::NextProtocolEndpoint(
   if (proto == TS_NPN_PROTOCOL_HTTP_1_1 ||
       proto == TS_NPN_PROTOCOL_HTTP_1_0) {
     proto_stack = ((1u << TS_PROTO_TLS) | (1u << TS_PROTO_HTTP));
-  } else if (proto == TS_NPN_PROTOCOL_SPDY_3 ||
+  } else if (proto == TS_NPN_PROTOCOL_SPDY_3_1 ||
+             proto == TS_NPN_PROTOCOL_SPDY_3 ||
              proto == TS_NPN_PROTOCOL_SPDY_2 ||
              proto == TS_NPN_PROTOCOL_SPDY_1) {
     proto_stack = ((1u << TS_PROTO_TLS) | (1u << TS_PROTO_SPDY));

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/iocore/net/UnixNetAccept.cc
----------------------------------------------------------------------
diff --git a/iocore/net/UnixNetAccept.cc b/iocore/net/UnixNetAccept.cc
index af4d8f9..4962917 100644
--- a/iocore/net/UnixNetAccept.cc
+++ b/iocore/net/UnixNetAccept.cc
@@ -331,8 +331,10 @@ NetAccept::do_blocking_accept(EThread * t)
 
     // Use 'NULL' to Bypass thread allocator
     vc = createSuitableVC(NULL, con);
-    if (!vc)
+    if (!vc) {
+      con.close();
       return -1;
+    }
     vc->from_accept_thread = true;
     vc->id = net_next_connection_number();
     alloc_cache = NULL;
@@ -467,8 +469,10 @@ NetAccept::acceptFastEvent(int event, void *ep)
       } while (res < 0 && (errno == EAGAIN || errno == EINTR));
 
       vc = createSuitableVC(e->ethread, con);
-      if (!vc)
+      if (!vc) {
+        con.close();
         goto Ldone;
+      }
 
     } else {
       res = fd;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/lib/ts/apidefs.h.in
----------------------------------------------------------------------
diff --git a/lib/ts/apidefs.h.in b/lib/ts/apidefs.h.in
index 0356921..ffd8cbb 100644
--- a/lib/ts/apidefs.h.in
+++ b/lib/ts/apidefs.h.in
@@ -1155,6 +1155,7 @@ extern "C"
   extern tsapi const char * TS_NPN_PROTOCOL_SPDY_1;
   extern tsapi const char * TS_NPN_PROTOCOL_SPDY_2;
   extern tsapi const char * TS_NPN_PROTOCOL_SPDY_3;
+  extern tsapi const char * TS_NPN_PROTOCOL_SPDY_3_1;
 
   /* --------------------------------------------------------------------------
      MLoc Constants */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/InkAPI.cc
----------------------------------------------------------------------
diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc
index 682f889..c5fb465 100644
--- a/proxy/InkAPI.cc
+++ b/proxy/InkAPI.cc
@@ -361,8 +361,9 @@ tsapi int TS_HTTP_LEN_PUSH;
 tsapi const char * TS_NPN_PROTOCOL_HTTP_1_0 = "http/1.0";
 tsapi const char * TS_NPN_PROTOCOL_HTTP_1_1 = "http/1.1";
 tsapi const char * TS_NPN_PROTOCOL_SPDY_1   = "spdy/1";   // obsolete
-tsapi const char * TS_NPN_PROTOCOL_SPDY_2   = "spdy/2";   // shipping
-tsapi const char * TS_NPN_PROTOCOL_SPDY_3   = "spdy/3";   // upcoming
+tsapi const char * TS_NPN_PROTOCOL_SPDY_2   = "spdy/2";
+tsapi const char * TS_NPN_PROTOCOL_SPDY_3   = "spdy/3";
+tsapi const char * TS_NPN_PROTOCOL_SPDY_3_1 = "spdy/3.1";
 
 /* MLoc Constants */
 tsapi const TSMLoc TS_NULL_MLOC = (TSMLoc)NULL;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/http/HttpProxyServerMain.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpProxyServerMain.cc b/proxy/http/HttpProxyServerMain.cc
index 8988253..449418a 100644
--- a/proxy/http/HttpProxyServerMain.cc
+++ b/proxy/http/HttpProxyServerMain.cc
@@ -173,10 +173,21 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor& acceptor, HttpProxyPort& port, unsigned
   proto->registerEndpoint(TS_PROTO_SPDY, spdy);
 
   if (port.isSSL()) {
-    // ALPN selects the first server-offered protocol, so make sure that we offer HTTP/1.1 first.
+    //
+    // ALPN selects the first server-offered protocol,
+    // so make sure that we offer the newest protocol first.
+    //
+
+    // HTTP
     ssl->registerEndpoint(TS_NPN_PROTOCOL_HTTP_1_1, http);
     ssl->registerEndpoint(TS_NPN_PROTOCOL_HTTP_1_0, http);
 
+    // SPDY
+#if TS_HAS_SPDY
+    ssl->registerEndpoint(TS_NPN_PROTOCOL_SPDY_3_1, spdy);
+    ssl->registerEndpoint(TS_NPN_PROTOCOL_SPDY_3, spdy);
+#endif
+
     ink_scoped_mutex lock(ssl_plugin_mutex);
     ssl_plugin_acceptors.push(ssl);
   }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/Makefile.am
----------------------------------------------------------------------
diff --git a/proxy/spdy/Makefile.am b/proxy/spdy/Makefile.am
index 63911e1..a85dc61 100644
--- a/proxy/spdy/Makefile.am
+++ b/proxy/spdy/Makefile.am
@@ -36,3 +36,13 @@ noinst_LIBRARIES = libspdy.a
 libspdy_a_SOURCES = \
   P_SpdyAcceptCont.h \
   SpdyAcceptCont.cc
+
+if BUILD_SPDY
+  libspdy_a_SOURCES += \
+  P_SpdyCallbacks.h \
+  P_SpdyCommon.h \
+  P_SpdySM.h \
+  SpdyCallbacks.cc \
+  SpdyCommon.cc \
+  SpdySM.cc
+endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/P_SpdyCallbacks.h
----------------------------------------------------------------------
diff --git a/proxy/spdy/P_SpdyCallbacks.h b/proxy/spdy/P_SpdyCallbacks.h
new file mode 100644
index 0000000..e2715f0
--- /dev/null
+++ b/proxy/spdy/P_SpdyCallbacks.h
@@ -0,0 +1,248 @@
+
+#ifndef __P_SPDY_CALLBACKS_H__
+#define __P_SPDY_CALLBACKS_H__
+
+#include <spdylay/spdylay.h>
+class SpdySM;
+
+void spdy_callbacks_init(spdylay_session_callbacks *callbacks);
+void spdy_prepare_status_response(SpdySM *sm, int stream_id, const char *status);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when |session| wants to send data to the
+ * remote peer. The implementation of this function must send at most
+ * |length| bytes of data stored in |data|. The |flags| is currently
+ * not used and always 0. It must return the number of bytes sent if
+ * it succeeds.  If it cannot send any single byte without blocking,
+ * it must return :enum:`SPDYLAY_ERR_WOULDBLOCK`. For other errors, it
+ * must return :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`.
+ */
+ssize_t spdy_send_callback
+(spdylay_session *session,
+ const uint8_t *data, size_t length, int flags, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when |session| wants to receive data from
+ * the remote peer. The implementation of this function must read at
+ * most |length| bytes of data and store it in |buf|. The |flags| is
+ * currently not used and always 0. It must return the number of bytes
+ * written in |buf| if it succeeds. If it cannot read any single byte
+ * without blocking, it must return :enum:`SPDYLAY_ERR_WOULDBLOCK`. If
+ * it gets EOF before it reads any single byte, it must return
+ * :enum:`SPDYLAY_ERR_EOF`. For other errors, it must return
+ * :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`.
+ */
+ssize_t spdy_recv_callback
+(spdylay_session *session,
+ uint8_t *buf, size_t length, int flags, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked by `spdylay_session_recv()` when a
+ * control frame is received.
+ */
+void spdy_on_ctrl_recv_callback
+(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked by `spdylay_session_recv()` when an
+ * invalid control frame is received. The |status_code| is one of the
+ * :enum:`spdylay_status_code` and indicates the error. When this
+ * callback function is invoked, the library automatically submits
+ * either RST_STREAM or GOAWAY frame.
+ */
+void spdy_on_invalid_ctrl_recv_callback
+(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
+ uint32_t status_code, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when a chunk of data in DATA frame is
+ * received. The |stream_id| is the stream ID this DATA frame belongs
+ * to. The |flags| is the flags of DATA frame which this data chunk is
+ * contained. ``(flags & SPDYLAY_DATA_FLAG_FIN) != 0`` does not
+ * necessarily mean this chunk of data is the last one in the
+ * stream. You should use :type:`spdylay_on_data_recv_callback` to
+ * know all data frames are received.
+ */
+void spdy_on_data_chunk_recv_callback
+(spdylay_session *session, uint8_t flags, int32_t stream_id,
+ const uint8_t *data, size_t len, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when DATA frame is received. The actual
+ * data it contains are received by
+ * :type:`spdylay_on_data_chunk_recv_callback`.
+ */
+void spdy_on_data_recv_callback
+(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked before the control frame |frame| of type
+ * |type| is sent. This may be useful, for example, to know the stream
+ * ID of SYN_STREAM frame (see also
+ * `spdylay_session_get_stream_user_data()`), which is not assigned
+ * when it was queued.
+ */
+void spdy_before_ctrl_send_callback
+(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked after the control frame |frame| of type
+ * |type| is sent.
+ */
+void spdy_on_ctrl_send_callback
+(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked after the control frame |frame| of type
+ * |type| is not sent because of the error. The error is indicated by
+ * the |error_code|, which is one of the values defined in
+ * :type:`spdylay_error`.
+ */
+void spdy_on_ctrl_not_send_callback
+(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
+ int error_code, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked after DATA frame is sent.
+ */
+void spdy_on_data_send_callback
+(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when the stream |stream_id| is
+ * closed. The reason of closure is indicated by the
+ * |status_code|. The stream_user_data, which was specified in
+ * `spdylay_submit_request()` or `spdylay_submit_syn_stream()`, is
+ * still available in this function.
+ */
+void spdy_on_stream_close_callback
+(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when the library needs the cryptographic
+ * proof that the client has possession of the private key associated
+ * with the certificate for the given |origin|.  If called with
+ * |prooflen| == 0, the implementation of this function must return
+ * the length of the proof in bytes. If called with |prooflen| > 0,
+ * write proof into |proof| exactly |prooflen| bytes and return 0.
+ *
+ * Because the client certificate vector has limited number of slots,
+ * the application code may be required to pass the same proof more
+ * than once.
+ */
+ssize_t spdy_get_credential_proof
+(spdylay_session *session, const spdylay_origin *origin,
+ uint8_t *proof, size_t prooflen, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when the library needs the length of the
+ * client certificate chain for the given |origin|.  The
+ * implementation of this function must return the length of the
+ * client certificate chain.  If no client certificate is required for
+ * the given |origin|, return 0.  If positive integer is returned,
+ * :type:`spdylay_get_credential_proof` and
+ * :type:`spdylay_get_credential_cert` callback functions will be used
+ * to get the cryptographic proof and certificate respectively.
+ */
+ssize_t spdy_get_credential_ncerts
+(spdylay_session *session, const spdylay_origin *origin, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when the library needs the client
+ * certificate for the given |origin|. The |idx| is the index of the
+ * certificate chain and 0 means the leaf certificate of the chain.
+ * If called with |certlen| == 0, the implementation of this function
+ * must return the length of the certificate in bytes. If called with
+ * |certlen| > 0, write certificate into |cert| exactly |certlen|
+ * bytes and return 0.
+ */
+ssize_t spdy_get_credential_cert
+(spdylay_session *session, const spdylay_origin *origin, size_t idx,
+ uint8_t *cert, size_t certlen, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when the request from the remote peer is
+ * received.  In other words, the frame with FIN flag set is received.
+ * In HTTP, this means HTTP request, including request body, is fully
+ * received.
+ */
+void spdy_on_request_recv_callback
+(spdylay_session *session, int32_t stream_id, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when the received control frame octets
+ * could not be parsed correctly. The |type| indicates the type of
+ * received control frame. The |head| is the pointer to the header of
+ * the received frame. The |headlen| is the length of the
+ * |head|. According to the SPDY spec, the |headlen| is always 8. In
+ * other words, the |head| is the first 8 bytes of the received frame.
+ * The |payload| is the pointer to the data portion of the received
+ * frame.  The |payloadlen| is the length of the |payload|. This is
+ * the data after the length field. The |error_code| is one of the
+ * error code defined in :enum:`spdylay_error` and indicates the
+ * error.
+ */
+void spdy_on_ctrl_recv_parse_error_callback
+(spdylay_session *session, spdylay_frame_type type,
+ const uint8_t *head, size_t headlen,
+ const uint8_t *payload, size_t payloadlen,
+ int error_code, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * Callback function invoked when the received control frame type is
+ * unknown. The |head| is the pointer to the header of the received
+ * frame. The |headlen| is the length of the |head|. According to the
+ * SPDY spec, the |headlen| is always 8. In other words, the |head| is
+ * the first 8 bytes of the received frame.  The |payload| is the
+ * pointer to the data portion of the received frame.  The
+ * |payloadlen| is the length of the |payload|. This is the data after
+ * the length field.
+ */
+void spdy_on_unknown_ctrl_recv_callback
+(spdylay_session *session,
+ const uint8_t *head, size_t headlen,
+ const uint8_t *payload, size_t payloadlen,
+ void *user_data);
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/P_SpdyCommon.h
----------------------------------------------------------------------
diff --git a/proxy/spdy/P_SpdyCommon.h b/proxy/spdy/P_SpdyCommon.h
new file mode 100644
index 0000000..87364df
--- /dev/null
+++ b/proxy/spdy/P_SpdyCommon.h
@@ -0,0 +1,75 @@
+
+#ifndef __P_SPDY_COMMON_H__
+#define __P_SPDY_COMMON_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <limits.h>
+#include <string.h>
+#include <string>
+#include <vector>
+#include <map>
+
+#include "P_Net.h"
+#include "ts/ts.h"
+#include "ts/libts.h"
+#include "ts/experimental.h"
+#include <spdylay/spdylay.h>
+using namespace std;
+
+#define STATUS_200      "200 OK"
+#define STATUS_304      "304 Not Modified"
+#define STATUS_400      "400 Bad Request"
+#define STATUS_404      "404 Not Found"
+#define STATUS_405      "405 Method Not Allowed"
+#define STATUS_500      "500 Internal Server Error"
+#define DEFAULT_HTML    "index.html"
+#define SPDYD_SERVER    "ATS Spdylay/" SPDYLAY_VERSION
+
+#define atomic_fetch_and_add(a, b)  __sync_fetch_and_add(&a, b)
+#define atomic_fetch_and_sub(a, b)  __sync_fetch_and_sub(&a, b)
+#define atomic_inc(a)   atomic_fetch_and_add(a, 1)
+#define atomic_dec(a)   atomic_fetch_and_sub(a, 1)
+
+struct SpdyConfig {
+  bool verbose;
+  bool enable_tls;
+  bool keep_host_port;
+  int serv_port;
+  int max_concurrent_streams;
+  int initial_window_size;
+  spdylay_session_callbacks callbacks;
+};
+
+struct Config {
+  SpdyConfig spdy;
+  int nr_accept_threads;
+  int accept_no_activity_timeout;
+  int no_activity_timeout_in;
+};
+
+// Spdy Name/Value pairs
+class SpdyNV {
+public:
+
+  SpdyNV(TSFetchSM fetch_sm);
+  ~SpdyNV();
+
+public:
+  const char **nv;
+
+private:
+  SpdyNV();
+  void *mime_hdr;
+  char status[64];
+  char version[64];
+};
+
+string http_date(time_t t);
+int spdy_config_load();
+
+extern Config SPDY_CFG;
+#endif
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/P_SpdySM.h
----------------------------------------------------------------------
diff --git a/proxy/spdy/P_SpdySM.h b/proxy/spdy/P_SpdySM.h
new file mode 100644
index 0000000..85395db
--- /dev/null
+++ b/proxy/spdy/P_SpdySM.h
@@ -0,0 +1,129 @@
+
+#ifndef __P_SPDY_SM_H__
+#define __P_SPDY_SM_H__
+
+#include "P_SpdyCommon.h"
+#include "P_SpdyCallbacks.h"
+#include <openssl/md5.h>
+
+
+class SpdySM;
+typedef int (*SpdySMHandler) (TSCont contp, TSEvent event, void *data);
+
+
+class SpdyRequest
+{
+public:
+  SpdyRequest():
+    spdy_sm(NULL), stream_id(-1), fetch_sm(NULL),
+    has_submitted_data(false), need_resume_data(false),
+    fetch_data_len(0), delta_window_size(0),
+    fetch_body_completed(false)
+  {
+  }
+
+  SpdyRequest(SpdySM *sm, int id):
+    spdy_sm(NULL), stream_id(-1), fetch_sm(NULL),
+    has_submitted_data(false), need_resume_data(false),
+    fetch_data_len(0), delta_window_size(0),
+    fetch_body_completed(false)
+  {
+    init(sm, id);
+  }
+
+  ~SpdyRequest()
+  {
+    clear();
+  }
+
+  void init(SpdySM *sm, int id)
+  {
+    spdy_sm = sm;
+    stream_id = id;
+    headers.clear();
+
+    MD5_Init(&recv_md5);
+    start_time = TShrtime();
+  }
+
+  void clear();
+
+  void append_nv(char **nv)
+  {
+    for(int i = 0; nv[i]; i += 2) {
+      headers.push_back(make_pair(nv[i], nv[i+1]));
+    }
+  }
+
+public:
+  int event;
+  SpdySM *spdy_sm;
+  int stream_id;
+  TSHRTime start_time;
+  TSFetchSM fetch_sm;
+  bool has_submitted_data;
+  bool need_resume_data;
+  int fetch_data_len;
+  int delta_window_size;
+  bool fetch_body_completed;
+  vector<pair<string, string> > headers;
+
+  string url;
+  string host;
+  string path;
+  string scheme;
+  string method;
+  string version;
+
+  MD5_CTX recv_md5;
+};
+
+class SpdySM
+{
+
+public:
+
+  SpdySM();
+  SpdySM(TSVConn conn);
+  ~SpdySM()
+  {
+    clear();
+  }
+
+  void init(TSVConn conn);
+  void clear();
+
+public:
+
+  int64_t sm_id;
+  uint64_t total_size;
+  TSHRTime start_time;
+
+  TSVConn net_vc;
+  TSCont  contp;
+
+  TSIOBuffer req_buffer;
+  TSIOBufferReader req_reader;
+
+  TSIOBuffer resp_buffer;
+  TSIOBufferReader resp_reader;
+
+  TSVIO   read_vio;
+  TSVIO   write_vio;
+
+  SpdySMHandler current_handler;
+
+  int event;
+  spdylay_session *session;
+
+  map<int32_t, SpdyRequest*> req_map;
+};
+
+
+void spdy_sm_create(TSVConn cont);
+
+extern ClassAllocator<SpdySM> spdySMAllocator;
+extern ClassAllocator<SpdyRequest> spdyRequestAllocator;
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/SpdyAcceptCont.cc
----------------------------------------------------------------------
diff --git a/proxy/spdy/SpdyAcceptCont.cc b/proxy/spdy/SpdyAcceptCont.cc
index b9efc80..95bfe03 100644
--- a/proxy/spdy/SpdyAcceptCont.cc
+++ b/proxy/spdy/SpdyAcceptCont.cc
@@ -22,16 +22,26 @@
  */
 
 #include "P_SpdyAcceptCont.h"
+#if TS_HAS_SPDY
+#include "P_SpdySM.h"
+#endif
 
 SpdyAcceptCont::SpdyAcceptCont(Continuation *ep)
     : AcceptCont(new_ProxyMutex()), endpoint(ep)
 {
+#if TS_HAS_SPDY
+  spdy_config_load();
+#endif
   SET_HANDLER(&SpdyAcceptCont::mainEvent);
 }
 
 int
-SpdyAcceptCont::mainEvent(int event, void *netvc)
+SpdyAcceptCont::mainEvent(int /* event */, void *netvc)
 {
-  printf("spdy accepted\n");
+#if TS_HAS_SPDY
+  spdy_sm_create((TSCont)netvc);
+#else
+  (void)(netvc);
+#endif
   return 0;
 }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/SpdyCallbacks.cc
----------------------------------------------------------------------
diff --git a/proxy/spdy/SpdyCallbacks.cc b/proxy/spdy/SpdyCallbacks.cc
new file mode 100644
index 0000000..2e1b1f5
--- /dev/null
+++ b/proxy/spdy/SpdyCallbacks.cc
@@ -0,0 +1,513 @@
+
+#include "P_SpdyCallbacks.h"
+#include "P_SpdySM.h"
+#include <arpa/inet.h>
+
+void
+spdy_callbacks_init(spdylay_session_callbacks *callbacks)
+{
+  memset(callbacks, 0, sizeof(spdylay_session_callbacks));
+
+  callbacks->send_callback = spdy_send_callback;
+  callbacks->recv_callback = spdy_recv_callback;
+  callbacks->on_ctrl_recv_callback = spdy_on_ctrl_recv_callback;
+  callbacks->on_invalid_ctrl_recv_callback = spdy_on_invalid_ctrl_recv_callback;
+  callbacks->on_data_chunk_recv_callback = spdy_on_data_chunk_recv_callback;
+  callbacks->on_data_recv_callback = spdy_on_data_recv_callback;
+  callbacks->before_ctrl_send_callback = spdy_before_ctrl_send_callback;
+  callbacks->on_ctrl_send_callback = spdy_on_ctrl_send_callback;
+  callbacks->on_ctrl_not_send_callback = spdy_on_ctrl_not_send_callback;
+  callbacks->on_data_send_callback = spdy_on_data_send_callback;
+  callbacks->on_stream_close_callback = spdy_on_stream_close_callback;
+  callbacks->on_request_recv_callback = spdy_on_request_recv_callback;
+  callbacks->get_credential_proof = spdy_get_credential_proof;
+  callbacks->get_credential_ncerts = spdy_get_credential_ncerts;
+  callbacks->get_credential_cert = spdy_get_credential_cert;
+  callbacks->on_ctrl_recv_parse_error_callback = spdy_on_ctrl_recv_parse_error_callback;
+  callbacks->on_unknown_ctrl_recv_callback = spdy_on_unknown_ctrl_recv_callback;
+}
+
+void
+spdy_prepare_status_response(SpdySM *sm, int stream_id, const char *status)
+{
+  SpdyRequest *req = sm->req_map[stream_id];
+  string date_str = http_date(time(0));
+  const char **nv = new const char*[8+req->headers.size()*2+1];
+
+  nv[0] = ":status";
+  nv[1] = status;
+  nv[2] = ":version";
+  nv[3] = "HTTP/1.1";
+  nv[4] = "server";
+  nv[5] = SPDYD_SERVER;
+  nv[6] = "date";
+  nv[7] = date_str.c_str();
+
+  for(size_t i = 0; i < req->headers.size(); ++i) {
+    nv[8+i*2] = req->headers[i].first.c_str();
+    nv[8+i*2+1] = req->headers[i].second.c_str();
+  }
+  nv[8+req->headers.size()*2] = 0;
+
+  int r = spdylay_submit_response(sm->session, stream_id, nv, NULL);
+  TSAssert(r == 0);
+
+  TSVIOReenable(sm->write_vio);
+  delete [] nv;
+}
+
+static void
+spdy_show_data_frame(const char *head_str, spdylay_session * /*session*/, uint8_t flags,
+                     int32_t stream_id, int32_t length, void *user_data)
+{
+  if (!is_debug_tag_set("spdy"))
+    return;
+
+  SpdySM *sm = (SpdySM *)user_data;
+
+  Debug("spdy", "%s DATA frame (sm_id:%"PRIu64", stream_id:%d, flag:%d, length:%d)\n",
+        head_str, sm->sm_id, stream_id, flags, length);
+}
+
+static void
+spdy_show_ctl_frame(const char *head_str, spdylay_session * /*session*/, spdylay_frame_type type,
+                    spdylay_frame *frame, void *user_data)
+{
+  if (!is_debug_tag_set("spdy"))
+    return;
+
+  SpdySM *sm = (SpdySM *)user_data;
+  switch (type) {
+  case SPDYLAY_SYN_STREAM: {
+    spdylay_syn_stream *f = (spdylay_syn_stream *)frame;
+    Debug("spdy", "%s SYN_STREAM (sm_id:%"PRIu64", stream_id:%d, flag:%d, length:%d)\n",
+          head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length);
+    int j, i;
+    j = i = 0;
+    while (f->nv[j]) {
+      Debug("spdy", "    %s: %s\n", f->nv[j], f->nv[j+1]);
+      i++;
+      j = 2*i;
+    }
+  }
+    break;
+  case SPDYLAY_SYN_REPLY: {
+    spdylay_syn_reply *f = (spdylay_syn_reply *)frame;
+    Debug("spdy", "%s SYN_REPLY (sm_id:%"PRIu64", stream_id:%d, flag:%d, length:%d)\n",
+          head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length);
+    int j, i;
+    j = i = 0;
+    while (f->nv[j]) {
+      Debug("spdy", "    %s: %s\n", f->nv[j], f->nv[j+1]);
+      i++;
+      j = 2*i;
+    }
+  }
+    break;
+  case SPDYLAY_WINDOW_UPDATE: {
+    spdylay_window_update *f = (spdylay_window_update *)frame;
+    Debug("spdy", "%s WINDOW_UPDATE (sm_id:%"PRIu64", stream_id:%d, flag:%d, delta_window_size:%d)\n",
+          head_str, sm->sm_id, f->stream_id, f->hd.flags, f->delta_window_size);
+  }
+    break;
+  case SPDYLAY_SETTINGS: {
+    spdylay_settings *f = (spdylay_settings *)frame;
+    Debug("spdy", "%s SETTINGS frame (sm_id:%"PRIu64", flag:%d, length:%d, niv:%zu)\n",
+          head_str, sm->sm_id, f->hd.flags, f->hd.length, f->niv);
+    for (size_t i = 0; i < f->niv; i++) {
+      Debug("spdy", "    (%d:%d)\n", f->iv[i].settings_id, f->iv[i].value);
+    }
+  }
+    break;
+  case SPDYLAY_HEADERS: {
+    spdylay_headers *f = (spdylay_headers *)frame;
+    Debug("spdy", "%s HEADERS frame (sm_id:%"PRIu64", stream_id:%d, flag:%d, length:%d)\n",
+          head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length);
+  }
+    break;
+  case SPDYLAY_RST_STREAM: {
+    spdylay_rst_stream *f = (spdylay_rst_stream *)frame;
+    Debug("spdy", "%s RST_STREAM (sm_id:%"PRIu64", stream_id:%d, flag:%d, length:%d, code:%d)\n",
+          head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length, f->status_code);
+  }
+    break;
+  case SPDYLAY_GOAWAY: {
+    spdylay_goaway *f = (spdylay_goaway *)frame;
+    Debug("spdy", "%s GOAWAY frame (sm_id:%"PRIu64", last_good_stream_id:%d, flag:%d, length:%d\n",
+          head_str, sm->sm_id, f->last_good_stream_id, f->hd.flags, f->hd.length);
+  }
+  default:
+    break;
+  }
+  return;
+}
+
+static int
+spdy_fetcher_launch(SpdyRequest *req, TSFetchMethod method)
+{
+  string url;
+  int fetch_flags;
+  const sockaddr *client_addr;
+  SpdySM *sm = req->spdy_sm;
+
+  url = req->scheme + "://" + req->host + req->path;
+  client_addr = TSNetVConnRemoteAddrGet(sm->net_vc);
+
+  req->url = url;
+  Debug("spdy", "++++Request[%" PRIu64 ":%d] %s\n", sm->sm_id, req->stream_id, req->url.c_str());
+
+  //
+  // HTTP content should be dechunked before packed into SPDY.
+  //
+  fetch_flags = TS_FETCH_FLAGS_DECHUNK;
+  req->fetch_sm = TSFetchCreate(sm->contp, method,
+                                url.c_str(), req->version.c_str(),
+                                client_addr, fetch_flags);
+  TSFetchUserDataSet(req->fetch_sm, req);
+
+  //
+  // Set header list
+  //
+  for (size_t i = 0; i < req->headers.size(); i++) {
+
+    if (*req->headers[i].first.c_str() == ':')
+      continue;
+
+    TSFetchHeaderAdd(req->fetch_sm,
+                     req->headers[i].first.c_str(), req->headers[i].first.size(),
+                     req->headers[i].second.c_str(), req->headers[i].second.size());
+  }
+
+  TSFetchLaunch(req->fetch_sm);
+  return 0;
+}
+
+ssize_t
+spdy_send_callback(spdylay_session * /*session*/, const uint8_t *data, size_t length,
+                   int /*flags*/, void *user_data)
+{
+  SpdySM  *sm = (SpdySM*)user_data;
+
+  sm->total_size += length;
+  TSIOBufferWrite(sm->resp_buffer, data, length);
+
+  Debug("spdy", "----spdy_send_callback, length:%zu\n", length);
+
+  return length;
+}
+
+ssize_t
+spdy_recv_callback(spdylay_session * /*session*/, uint8_t *buf, size_t length,
+                   int /*flags*/, void *user_data)
+{
+  const char *start;
+  TSIOBufferBlock blk, next_blk;
+  int64_t already, blk_len, need, wavail;
+
+  SpdySM  *sm = (SpdySM*)user_data;
+
+  already = 0;
+  blk = TSIOBufferReaderStart(sm->req_reader);
+
+  while (blk) {
+
+    wavail = length - already;
+
+    next_blk = TSIOBufferBlockNext(blk);
+    start = TSIOBufferBlockReadStart(blk, sm->req_reader, &blk_len);
+
+    need = blk_len > wavail ? wavail : blk_len;
+
+    memcpy(&buf[already], start, need);
+    already += need;
+
+    if (already >= (int64_t)length)
+      break;
+
+    blk = next_blk;
+  }
+
+  TSIOBufferReaderConsume(sm->req_reader, already);
+  TSVIOReenable(sm->read_vio);
+
+  if (!already)
+    return SPDYLAY_ERR_WOULDBLOCK;
+
+  return already;
+}
+
+static void
+spdy_process_syn_stream_frame(SpdySM *sm, SpdyRequest *req)
+{
+  // validate request headers
+  for(size_t i = 0; i < req->headers.size(); ++i) {
+    const std::string &field = req->headers[i].first;
+    const std::string &value = req->headers[i].second;
+
+    if(field == ":path")
+      req->path = value;
+    else if(field == ":method")
+      req->method = value;
+    else if(field == ":scheme")
+      req->scheme = value;
+    else if(field == ":version")
+      req->version = value;
+    else if(field == ":host")
+      req->host = value;
+  }
+
+  if(!req->path.size()|| !req->method.size() || !req->scheme.size()
+     || !req->version.size() || !req->host.size()) {
+    spdy_prepare_status_response(sm, req->stream_id, STATUS_400);
+    return;
+  }
+
+
+  if (req->method == "GET")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_GET);
+  else if (req->method == "POST")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_POST);
+  else if (req->method == "PURGE")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_PURGE);
+  else if (req->method == "PUT")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_PUT);
+  else if (req->method == "HEAD")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_HEAD);
+  else if (req->method == "CONNECT")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_CONNECT);
+  else if (req->method == "DELETE")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_DELETE);
+  else if (req->method == "LAST")
+    spdy_fetcher_launch(req, TS_FETCH_METHOD_LAST);
+  else
+    spdy_prepare_status_response(sm, req->stream_id, STATUS_405);
+
+}
+
+void
+spdy_on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
+                           spdylay_frame *frame, void *user_data)
+{
+  int         stream_id;
+  SpdyRequest *req;
+  SpdySM      *sm = (SpdySM*)user_data;
+
+  spdy_show_ctl_frame("++++RECV", session, type, frame, user_data);
+
+  switch (type) {
+
+  case SPDYLAY_SYN_STREAM:
+    stream_id = frame->syn_stream.stream_id;
+    req = spdyRequestAllocator.alloc();
+    req->init(sm, stream_id);
+    req->append_nv(frame->syn_stream.nv);
+    sm->req_map[stream_id] = req;
+    spdy_process_syn_stream_frame(sm, req);
+    break;
+
+  case SPDYLAY_HEADERS:
+    stream_id = frame->syn_stream.stream_id;
+    req = sm->req_map[stream_id];
+    req->append_nv(frame->headers.nv);
+    break;
+
+  case SPDYLAY_WINDOW_UPDATE:
+    TSVIOReenable(sm->write_vio);
+    break;
+
+  default:
+    break;
+  }
+  return;
+}
+
+void
+spdy_on_invalid_ctrl_recv_callback(spdylay_session * /*session*/,
+                                   spdylay_frame_type /*type*/,
+                                   spdylay_frame * /*frame*/,
+                                   uint32_t /*status_code*/,
+                                   void * /*user_data*/)
+{
+  //TODO
+  return;
+}
+
+void
+spdy_on_data_chunk_recv_callback(spdylay_session * /*session*/, uint8_t /*flags*/,
+                                 int32_t stream_id, const uint8_t *data,
+                                 size_t len, void *user_data)
+{
+  SpdySM *sm = (SpdySM *)user_data;
+  SpdyRequest *req = sm->req_map[stream_id];
+
+  //
+  // SpdyRequest has been deleted on error, drop this data;
+  //
+  if (!req)
+    return;
+
+  Debug("spdy", "++++Fetcher Append Data, len:%zu\n", len);
+  TSFetchWriteData(req->fetch_sm, data, len);
+
+  return;
+}
+
+void
+spdy_on_data_recv_callback(spdylay_session *session, uint8_t flags,
+                           int32_t stream_id, int32_t length, void *user_data)
+{
+  SpdySM *sm = (SpdySM *)user_data;
+  SpdyRequest *req = sm->req_map[stream_id];
+
+  spdy_show_data_frame("++++RECV", session, flags, stream_id, length, user_data);
+
+  //
+  // After SpdyRequest has been deleted on error, the corresponding
+  // client might continue to send POST data, We should reenable
+  // sm->write_vio so that WINDOW_UPDATE has a chance to be sent.
+  //
+  if (!req) {
+    TSVIOReenable(sm->write_vio);
+    return;
+  }
+
+  req->delta_window_size += length;
+
+  Debug("spdy", "----sm_id:%"PRId64", stream_id:%d, delta_window_size:%d\n",
+        sm->sm_id, stream_id, req->delta_window_size);
+
+  if (req->delta_window_size >= SPDY_CFG.spdy.initial_window_size/2) {
+    Debug("spdy", "----Reenable write_vio for WINDOW_UPDATE frame, delta_window_size:%d\n",
+          req->delta_window_size);
+
+    //
+    // Need not to send WINDOW_UPDATE frame here, what we should
+    // do is to reenable sm->write_vio, and than spdylay_session_send()
+    // will be triggered and it'll send WINDOW_UPDATE frame automatically.
+    //
+    TSVIOReenable(sm->write_vio);
+
+    req->delta_window_size = 0;
+  }
+
+  return;
+}
+
+void
+spdy_before_ctrl_send_callback(spdylay_session * /*session*/,
+                               spdylay_frame_type /*type*/,
+                               spdylay_frame * /*frame*/,
+                               void * /*user_data*/)
+{
+  //TODO
+  return;
+}
+
+void
+spdy_on_ctrl_send_callback(spdylay_session *session, spdylay_frame_type type,
+                           spdylay_frame *frame, void *user_data)
+{
+  spdy_show_ctl_frame("----SEND", session, type, frame, user_data);
+
+  return;
+}
+
+void
+spdy_on_ctrl_not_send_callback(spdylay_session * /*session*/,
+                               spdylay_frame_type /*type*/,
+                               spdylay_frame * /*frame*/,
+                               int /*error_code*/,
+                               void * /*user_data*/)
+{
+  //TODO
+  return;
+}
+
+void
+spdy_on_data_send_callback(spdylay_session *session, uint8_t flags,
+                           int32_t stream_id, int32_t length, void *user_data)
+{
+  SpdySM *sm = (SpdySM *)user_data;
+
+  spdy_show_data_frame("----SEND", session, flags, stream_id, length, user_data);
+
+  TSVIOReenable(sm->read_vio);
+  return;
+}
+
+void
+spdy_on_stream_close_callback(spdylay_session * /*session*/,
+                              int32_t /*stream_id*/,
+                              spdylay_status_code /*status_code*/,
+                              void * /*user_data*/)
+{
+  //TODO
+  return;
+}
+
+ssize_t
+spdy_get_credential_proof(spdylay_session * /*session*/,
+                          const spdylay_origin * /*origin*/,
+                          uint8_t * /*proof*/,
+                          size_t /*prooflen*/,
+                          void * /*user_data*/)
+{
+  //TODO
+  return 0;
+}
+
+ssize_t
+spdy_get_credential_ncerts(spdylay_session * /*session*/,
+                           const spdylay_origin * /*origin*/,
+                           void * /*user_data*/)
+{
+  //TODO
+  return 0;
+}
+
+ssize_t
+spdy_get_credential_cert(spdylay_session * /*session*/,
+                         const spdylay_origin * /*origin*/,
+                         size_t /*idx*/,
+                         uint8_t * /*cert*/,
+                         size_t /*certlen*/,
+                         void * /*user_data*/)
+{
+  //TODO
+  return 0;
+}
+
+void
+spdy_on_request_recv_callback(spdylay_session * /*session*/,
+                              int32_t /*stream_id*/,
+                              void * /*user_data*/)
+{
+  //TODO
+  return;
+}
+
+void
+spdy_on_ctrl_recv_parse_error_callback(spdylay_session * /*session*/,
+                                       spdylay_frame_type /*type*/,
+                                       const uint8_t * /*head*/,
+                                       size_t /*headlen*/,
+                                       const uint8_t * /*payload*/,
+                                       size_t /*payloadlen*/,
+                                       int /*error_code*/,
+                                       void * /*user_data*/)
+{
+  //TODO
+  return;
+}
+
+void
+spdy_on_unknown_ctrl_recv_callback(spdylay_session * /*session*/,
+                                   const uint8_t * /*head*/,
+                                   size_t /*headlen*/,
+                                   const uint8_t * /*payload*/,
+                                   size_t /*payloadlen*/,
+                                   void * /*user_data*/)
+{
+  //TODO
+  return;
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/SpdyCommon.cc
----------------------------------------------------------------------
diff --git a/proxy/spdy/SpdyCommon.cc b/proxy/spdy/SpdyCommon.cc
new file mode 100644
index 0000000..8205360
--- /dev/null
+++ b/proxy/spdy/SpdyCommon.cc
@@ -0,0 +1,136 @@
+
+#include "P_SpdyCommon.h"
+#include "P_SpdyCallbacks.h"
+
+Config SPDY_CFG;
+
+string
+http_date(time_t t)
+{
+  char buf[32];
+  tm* tms = gmtime(&t); // returned struct is statically allocated.
+  size_t r = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tms);
+  return std::string(&buf[0], &buf[r]);
+}
+
+
+int
+spdy_config_load()
+{
+  SPDY_CFG.nr_accept_threads = 1;
+  SPDY_CFG.accept_no_activity_timeout = 30;
+  SPDY_CFG.no_activity_timeout_in = 30;
+  SPDY_CFG.spdy.verbose = false;
+  SPDY_CFG.spdy.enable_tls = false;
+  SPDY_CFG.spdy.keep_host_port = false;
+  //
+  // SPDY plugin will share the same port number with
+  // http server, unless '--port' is given.
+  //
+  SPDY_CFG.spdy.serv_port = -1;
+  SPDY_CFG.spdy.max_concurrent_streams = 1000;
+  SPDY_CFG.spdy.initial_window_size = (64 << 10);
+
+  spdy_callbacks_init(&SPDY_CFG.spdy.callbacks);
+
+  return 0;
+}
+
+SpdyNV::SpdyNV(TSFetchSM fetch_sm)
+{
+  int i, len;
+  char *p;
+  const char *name, *value;
+  int name_len, value_len, hdr_len, nr_fields;
+  TSMLoc loc, field_loc, next_loc;
+  TSMBuffer bufp;
+
+  bufp = TSFetchRespHdrMBufGet(fetch_sm);
+  loc = TSFetchRespHdrMLocGet(fetch_sm);
+
+  hdr_len = TSMimeHdrLengthGet(bufp, loc);
+  mime_hdr = malloc(hdr_len);
+  TSReleaseAssert(mime_hdr);
+
+  nr_fields = TSMimeHdrFieldsCount(bufp, loc);
+  nv = (const char **)malloc((2*nr_fields + 5) * sizeof(char *));
+  TSReleaseAssert(nv);
+
+  //
+  // Process Status and Version
+  //
+  i = TSHttpHdrVersionGet(bufp, loc);
+  snprintf(version, sizeof(version), "HTTP/%d.%d", TS_HTTP_MAJOR(i), TS_HTTP_MINOR(i));
+
+  i = TSHttpHdrStatusGet(bufp, loc);
+  value = (char *)TSHttpHdrReasonGet(bufp, loc, &value_len);
+  snprintf(status, sizeof(version), "%d ", i);
+  i = strlen(status);
+  len = sizeof(status) - i;
+  len = value_len > len ? len : value_len;
+  strncpy(&status[i], value, len);
+  status[len + i] = '\0';;
+
+  i = 0;
+  nv[i++] = ":version";
+  nv[i++] = version;
+  nv[i++] = ":status";
+  nv[i++] = status;
+
+  //
+  // Process HTTP headers
+  //
+  p = (char *)mime_hdr;
+  field_loc = TSMimeHdrFieldGet(bufp, loc, 0);
+  while (field_loc) {
+    name = TSMimeHdrFieldNameGet(bufp, loc, field_loc, &name_len);
+    TSReleaseAssert(name && name_len);
+
+    //
+    // According SPDY v3 spec, in RESPONSE:
+    // The Connection, Keep-Alive, Proxy-Connection, and
+    // Transfer-Encoding headers are not valid and MUST not be sent.
+    //
+    if (!strncasecmp(name, "Connection", name_len))
+      goto next;
+
+    if (!strncasecmp(name, "Keep-Alive", name_len))
+      goto next;
+
+    if (!strncasecmp(name, "Proxy-Connection", name_len))
+      goto next;
+
+    if (!strncasecmp(name, "Transfer-Encoding", name_len))
+      goto next;
+
+    strncpy(p, name, name_len);
+    nv[i++] = p;
+    p += name_len;
+    *p++ = '\0';
+
+    value = TSMimeHdrFieldValueStringGet(bufp, loc, field_loc, -1, &value_len);
+    TSReleaseAssert(value && value_len);
+    strncpy(p, value, value_len);
+    nv[i++] = p;
+    p += value_len;
+    *p++ = '\0';
+
+next:
+    next_loc = TSMimeHdrFieldNext(bufp, loc, field_loc);
+    TSHandleMLocRelease(bufp, loc, field_loc);
+    field_loc = next_loc;
+  }
+  nv[i] = NULL;
+
+  if (field_loc)
+    TSHandleMLocRelease(bufp, loc, field_loc);
+}
+
+SpdyNV::~SpdyNV()
+{
+  if (nv)
+    free(nv);
+
+  if (mime_hdr)
+    free(mime_hdr);
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/79dd5035/proxy/spdy/SpdySM.cc
----------------------------------------------------------------------
diff --git a/proxy/spdy/SpdySM.cc b/proxy/spdy/SpdySM.cc
new file mode 100644
index 0000000..9657aec
--- /dev/null
+++ b/proxy/spdy/SpdySM.cc
@@ -0,0 +1,415 @@
+
+
+#include "P_SpdySM.h"
+#include "I_Net.h"
+
+ClassAllocator<SpdySM> spdySMAllocator("SpdySMAllocator");
+ClassAllocator<SpdyRequest> spdyRequestAllocator("SpdyRequestAllocator");
+
+static int spdy_main_handler(TSCont contp, TSEvent event, void *edata);
+static int spdy_start_handler(TSCont contp, TSEvent event, void *edata);
+static int spdy_default_handler(TSCont contp, TSEvent event, void *edata);
+static int spdy_process_read(TSEvent event, SpdySM *sm);
+static int spdy_process_write(TSEvent event, SpdySM *sm);
+static int spdy_process_fetch(TSEvent event, SpdySM *sm, void *edata);
+static int spdy_process_fetch_header(TSEvent event, SpdySM *sm, TSFetchSM fetch_sm);
+static int spdy_process_fetch_body(TSEvent event, SpdySM *sm, TSFetchSM fetch_sm);
+static uint64_t g_sm_id;
+static uint64_t g_sm_cnt;
+
+void
+SpdyRequest::clear()
+{
+  if (fetch_sm)
+    TSFetchDestroy(fetch_sm);
+
+  headers.clear();
+
+  Debug("spdy", "****Delete Request[%" PRIu64 ":%d]\n", spdy_sm->sm_id, stream_id);
+}
+
+SpdySM::SpdySM():
+  net_vc(NULL), contp(NULL),
+  req_buffer(NULL), req_reader(NULL),
+  resp_buffer(NULL), resp_reader(NULL),
+  read_vio(NULL), write_vio(NULL), session(NULL)
+{}
+
+SpdySM::SpdySM(TSVConn conn):
+  net_vc(NULL), contp(NULL),
+  req_buffer(NULL), req_reader(NULL),
+  resp_buffer(NULL), resp_reader(NULL),
+  read_vio(NULL), write_vio(NULL), session(NULL)
+
+{
+  init(conn);
+}
+
+void
+SpdySM::init(TSVConn conn)
+{
+  int version, r;
+  UnixNetVConnection *vc;
+
+  net_vc = conn;
+  req_map.clear();
+  vc = (UnixNetVConnection *)(conn);
+
+  if (vc->selected_next_protocol == TS_NPN_PROTOCOL_SPDY_3_1)
+    version = SPDYLAY_PROTO_SPDY3_1;
+  else if (vc->selected_next_protocol == TS_NPN_PROTOCOL_SPDY_3)
+    version = SPDYLAY_PROTO_SPDY3;
+  else if (vc->selected_next_protocol == TS_NPN_PROTOCOL_SPDY_2)
+    version = SPDYLAY_PROTO_SPDY2;
+  else
+    version = SPDYLAY_PROTO_SPDY3;
+
+  r = spdylay_session_server_new(&session, version,
+                                 &SPDY_CFG.spdy.callbacks, this);
+  ink_release_assert(r == 0);
+  sm_id = atomic_inc(g_sm_id);
+  total_size = 0;
+  start_time = TShrtime();
+}
+
+void
+SpdySM::clear()
+{
+  uint64_t nr_pending;
+  int last_event = event;
+  //
+  // SpdyRequest depends on SpdySM,
+  // we should delete it firstly to avoid race.
+  //
+  map<int, SpdyRequest*>::iterator iter = req_map.begin();
+  map<int, SpdyRequest*>::iterator endIter = req_map.end();
+  for(; iter != endIter; ++iter) {
+    SpdyRequest *req = iter->second;
+    req->clear();
+    spdyRequestAllocator.free(req);
+  }
+  req_map.clear();
+
+  if (net_vc) {
+    TSVConnClose(net_vc);
+    net_vc = NULL;
+  }
+
+  if (contp) {
+    TSContDestroy(contp);
+    contp = NULL;
+  }
+
+  if (req_reader) {
+    TSIOBufferReaderFree(req_reader);
+    req_reader = NULL;
+  }
+
+  if (req_buffer) {
+    TSIOBufferDestroy(req_buffer);
+    req_buffer = NULL;
+  }
+
+  if (resp_reader) {
+    TSIOBufferReaderFree(resp_reader);
+    resp_reader = NULL;
+  }
+
+  if (resp_buffer) {
+    TSIOBufferDestroy(resp_buffer);
+    resp_buffer = NULL;
+  }
+
+  if (session) {
+    spdylay_session_del(session);
+    session = NULL;
+  }
+
+  nr_pending = atomic_dec(g_sm_cnt);
+  Debug("spdy-free", "****Delete SpdySM[%"PRIu64"], last event:%d, nr_pending:%"PRIu64"\n",
+        sm_id, last_event, --nr_pending);
+}
+
+void
+spdy_sm_create(TSVConn cont)
+{
+  SpdySM  *sm;
+  NetVConnection *netvc = (NetVConnection *)cont;
+
+  sm = spdySMAllocator.alloc();
+  sm->init(cont);
+  atomic_inc(g_sm_cnt);
+
+  sm->contp = TSContCreate(spdy_main_handler, TSMutexCreate());
+  TSContDataSet(sm->contp, sm);
+
+  netvc->set_inactivity_timeout(HRTIME_SECONDS(SPDY_CFG.accept_no_activity_timeout));
+
+  sm->current_handler = &spdy_start_handler;
+  TSContSchedule(sm->contp, 0, TS_THREAD_POOL_DEFAULT);       // schedule now
+}
+
+static int
+spdy_main_handler(TSCont contp, TSEvent event, void *edata)
+{
+  SpdySM          *sm;
+  SpdySMHandler   spdy_current_handler;
+
+  sm = (SpdySM*)TSContDataGet(contp);
+  spdy_current_handler = sm->current_handler;
+
+  return (*spdy_current_handler) (contp, event, edata);
+}
+
+static int
+spdy_start_handler(TSCont contp, TSEvent /*event*/, void * /*data*/)
+{
+  int     r;
+  spdylay_settings_entry entry;
+
+  SpdySM  *sm = (SpdySM*)TSContDataGet(contp);
+
+  sm->req_buffer = TSIOBufferCreate();
+  sm->req_reader = TSIOBufferReaderAlloc(sm->req_buffer);
+
+  sm->resp_buffer = TSIOBufferCreate();
+  sm->resp_reader = TSIOBufferReaderAlloc(sm->resp_buffer);
+
+  sm->read_vio = TSVConnRead(sm->net_vc, contp, sm->req_buffer, INT64_MAX);
+  sm->write_vio = TSVConnWrite(sm->net_vc, contp, sm->resp_reader, INT64_MAX);
+
+  sm->current_handler = &spdy_default_handler;
+
+  /* send initial settings frame */
+  entry.settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;
+  entry.value = SPDY_CFG.spdy.max_concurrent_streams;
+  entry.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
+
+  r = spdylay_submit_settings(sm->session, SPDYLAY_FLAG_SETTINGS_NONE, &entry, 1);
+  TSAssert(r == 0);
+
+  TSVIOReenable(sm->write_vio);
+  return 0;
+}
+
+static int
+spdy_default_handler(TSCont contp, TSEvent event, void *edata)
+{
+  int ret = 0;
+  bool from_fetch = false;
+  NetVConnection *netvc;
+  SpdySM  *sm = (SpdySM*)TSContDataGet(contp);
+  sm->event = event;
+  netvc = (NetVConnection *)sm->net_vc;
+
+  if (edata == sm->read_vio) {
+    Debug("spdy", "++++[READ EVENT]\n");
+    if (event != TS_EVENT_VCONN_READ_READY &&
+        event != TS_EVENT_VCONN_READ_COMPLETE) {
+      ret = -1;
+      goto out;
+    }
+    ret = spdy_process_read(event, sm);
+  } else if (edata == sm->write_vio) {
+    Debug("spdy", "----[WRITE EVENT]\n");
+    if (event != TS_EVENT_VCONN_WRITE_READY &&
+        event != TS_EVENT_VCONN_WRITE_COMPLETE) {
+      ret = -1;
+      goto out;
+    }
+    ret = spdy_process_write(event, sm);
+  } else {
+    from_fetch = true;
+    ret = spdy_process_fetch(event, sm, edata);
+  }
+
+  Debug("spdy-event", "++++SpdySM[%"PRIu64"], EVENT:%d, ret:%d, nr_pending:%"PRIu64"\n",
+        sm->sm_id, event, ret, g_sm_cnt);
+out:
+  if (ret) {
+    sm->clear();
+    spdySMAllocator.free(sm);
+  } else if (!from_fetch) {
+    netvc->set_inactivity_timeout(HRTIME_SECONDS(SPDY_CFG.no_activity_timeout_in));
+  }
+
+  return 0;
+}
+
+static int
+spdy_process_read(TSEvent event, SpdySM *sm)
+{
+  return spdylay_session_recv(sm->session);
+}
+
+static int
+spdy_process_write(TSEvent event, SpdySM *sm)
+{
+  int ret;
+
+  ret = spdylay_session_send(sm->session);
+
+  if (TSIOBufferReaderAvail(sm->resp_reader) > 0)
+    TSVIOReenable(sm->write_vio);
+  else {
+    Debug("spdy", "----TOTAL SEND (sm_id:%"PRIu64", total_size:%"PRIu64", total_send:%"PRId64")\n",
+          sm->sm_id, sm->total_size, TSVIONDoneGet(sm->write_vio));
+
+    //
+    // We should reenable read_vio when no data to be written,
+    // otherwise it could lead to hang issue when client POST
+    // data is waiting to be read.
+    //
+    TSVIOReenable(sm->read_vio);
+  }
+
+  return ret;
+}
+
+static int
+spdy_process_fetch(TSEvent event, SpdySM *sm, void *edata)
+{
+  int ret = -1;
+  TSFetchSM fetch_sm = (TSFetchSM)edata;
+  SpdyRequest *req = (SpdyRequest *)TSFetchUserDataGet(fetch_sm);
+
+  switch ((int)event) {
+
+  case TS_FETCH_EVENT_EXT_HEAD_DONE:
+    Debug("spdy", "----[FETCH HEADER DONE]\n");
+    ret = spdy_process_fetch_header(event, sm, fetch_sm);
+    break;
+
+  case TS_FETCH_EVENT_EXT_BODY_READY:
+    Debug("spdy", "----[FETCH BODY READY]\n");
+    ret = spdy_process_fetch_body(event, sm, fetch_sm);
+    break;
+
+  case TS_FETCH_EVENT_EXT_BODY_DONE:
+    Debug("spdy", "----[FETCH BODY DONE]\n");
+    req->fetch_body_completed = true;
+    ret = spdy_process_fetch_body(event, sm, fetch_sm);
+    break;
+
+  default:
+    Debug("spdy", "----[FETCH ERROR]\n");
+    if (req->fetch_body_completed)
+      ret = 0; // Ignore fetch errors after FETCH BODY DONE
+    else
+      req->fetch_sm = NULL;
+    break;
+  }
+
+  if (ret) {
+    spdy_prepare_status_response(sm, req->stream_id, STATUS_500);
+    sm->req_map.erase(req->stream_id);
+    req->clear();
+    spdyRequestAllocator.free(req);
+  }
+
+  return 0;
+}
+
+static int
+spdy_process_fetch_header(TSEvent /*event*/, SpdySM *sm, TSFetchSM fetch_sm)
+{
+  int ret;
+  SpdyRequest *req = (SpdyRequest *)TSFetchUserDataGet(fetch_sm);
+  SpdyNV spdy_nv(fetch_sm);
+
+  Debug("spdy", "----spdylay_submit_syn_reply\n");
+  ret = spdylay_submit_syn_reply(sm->session,
+                                 SPDYLAY_CTRL_FLAG_NONE, req->stream_id,
+                                 spdy_nv.nv);
+
+  TSVIOReenable(sm->write_vio);
+  return ret;
+}
+
+static ssize_t
+spdy_read_fetch_body_callback(spdylay_session * /*session*/, int32_t stream_id,
+                              uint8_t *buf, size_t length, int *eof,
+                              spdylay_data_source *source, void *user_data)
+{
+
+  static int g_call_cnt;
+  int64_t already;
+
+  SpdySM *sm = (SpdySM *)user_data;
+  SpdyRequest *req = (SpdyRequest *)source->ptr;
+
+  //
+  // req has been deleted, ignore this data.
+  //
+  if (req != sm->req_map[stream_id]) {
+    Debug("spdy", "    stream_id:%d, call:%d, req has been deleted, return 0\n",
+          stream_id, g_call_cnt);
+    *eof = 1;
+    return 0;
+  }
+
+  already = TSFetchReadData(req->fetch_sm, buf, length);
+
+  Debug("spdy", "    stream_id:%d, call:%d, length:%ld, already:%ld\n",
+        stream_id, g_call_cnt, length, already);
+  if (SPDY_CFG.spdy.verbose)
+    MD5_Update(&req->recv_md5, buf, already);
+
+  TSVIOReenable(sm->write_vio);
+  g_call_cnt++;
+
+  req->fetch_data_len += already;
+  if (already < (int64_t)length) {
+    if (req->event == TS_FETCH_EVENT_EXT_BODY_DONE) {
+      TSHRTime end_time = TShrtime();
+      Debug("spdy", "----Request[%" PRIu64 ":%d] %s %lld %d\n", sm->sm_id, req->stream_id,
+            req->url.c_str(), (end_time - req->start_time)/TS_HRTIME_MSECOND,
+            req->fetch_data_len);
+      unsigned char digest[MD5_DIGEST_LENGTH];
+      if (SPDY_CFG.spdy.verbose ) {
+        MD5_Final(digest, &req->recv_md5);
+        Debug("spdy", "----recv md5sum: ");
+        for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
+          Debug("spdy", "%02x", digest[i]);
+        }
+        Debug("spdy", "\n");
+      }
+      *eof = 1;
+      sm->req_map.erase(stream_id);
+      req->clear();
+      spdyRequestAllocator.free(req);
+    } else if (already == 0) {
+      req->need_resume_data = true;
+      return SPDYLAY_ERR_DEFERRED;
+    }
+  }
+
+  return already;
+}
+
+static int
+spdy_process_fetch_body(TSEvent event, SpdySM *sm, TSFetchSM fetch_sm)
+{
+  int ret = 0;
+  spdylay_data_provider data_prd;
+  SpdyRequest *req = (SpdyRequest *)TSFetchUserDataGet(fetch_sm);
+  req->event = event;
+
+  data_prd.source.ptr = (void *)req;
+  data_prd.read_callback = spdy_read_fetch_body_callback;
+
+  if (!req->has_submitted_data) {
+    req->has_submitted_data = true;
+    Debug("spdy", "----spdylay_submit_data\n");
+    ret = spdylay_submit_data(sm->session, req->stream_id,
+                              SPDYLAY_DATA_FLAG_FIN, &data_prd);
+  } else if (req->need_resume_data) {
+    Debug("spdy", "----spdylay_session_resume_data\n");
+    ret = spdylay_session_resume_data(sm->session, req->stream_id);
+    if (ret == SPDYLAY_ERR_INVALID_ARGUMENT)
+      ret = 0;
+  }
+
+  TSVIOReenable(sm->write_vio);
+  return ret;
+}