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;
+}