TS-2729: Add HTTP/2 support to ATS

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

Branch: refs/heads/master
Commit: e4347ef80f2d4035eef7e3f5bd899d176e68cc23
Parents: 683a377
Author: Ryo Okubo <[email protected]>
Authored: Mon Feb 23 12:03:54 2015 -0800
Committer: Bryan Call <[email protected]>
Committed: Mon Feb 23 13:42:12 2015 -0800

----------------------------------------------------------------------
 lib/records/RecHttp.cc              |    7 +-
 mgmt/RecordsConfig.cc               |    8 +
 proxy/Main.cc                       |    2 +
 proxy/Makefile.am                   |    1 +
 proxy/hdrs/HdrToken.cc              |    8 +
 proxy/hdrs/HdrToken.h               |    6 +
 proxy/hdrs/MIME.cc                  |   10 +
 proxy/hdrs/MIME.h                   |   51 +-
 proxy/http/HttpClientSession.cc     |   22 +-
 proxy/http/HttpClientSession.h      |    3 +
 proxy/http/HttpTransact.cc          |   41 ++
 proxy/http/HttpTransact.h           |    1 +
 proxy/http/Makefile.am              |    3 +-
 proxy/http2/HPACK.cc                |  156 +++--
 proxy/http2/HPACK.h                 |   27 +-
 proxy/http2/HTTP2.cc                |  401 ++++++++++--
 proxy/http2/HTTP2.h                 |  127 +++-
 proxy/http2/Http2ClientSession.cc   |   85 ++-
 proxy/http2/Http2ClientSession.h    |   32 +
 proxy/http2/Http2ConnectionState.cc | 1015 ++++++++++++++++++++++++++++--
 proxy/http2/Http2ConnectionState.h  |  144 ++++-
 21 files changed, 1928 insertions(+), 222 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/lib/records/RecHttp.cc
----------------------------------------------------------------------
diff --git a/lib/records/RecHttp.cc b/lib/records/RecHttp.cc
index 5d263a2..6551788 100644
--- a/lib/records/RecHttp.cc
+++ b/lib/records/RecHttp.cc
@@ -596,8 +596,13 @@ ts_session_protocol_well_known_name_indices_init()
   SPDY_PROTOCOL_SET.markIn(TS_NPN_PROTOCOL_INDEX_SPDY_3_1);
 
   DEFAULT_TLS_SESSION_PROTOCOL_SET.markAllIn();
+
   // Don't enable HTTP/2 by default until it is stable.
-  DEFAULT_TLS_SESSION_PROTOCOL_SET.markOut(HTTP2_PROTOCOL_SET);
+  int http2_enabled = 0;
+  REC_ReadConfigInteger(http2_enabled, "proxy.config.http2.enabled");
+  if (!http2_enabled) {
+    DEFAULT_TLS_SESSION_PROTOCOL_SET.markOut(HTTP2_PROTOCOL_SET);
+  }
 
   DEFAULT_NON_TLS_SESSION_PROTOCOL_SET = HTTP_PROTOCOL_SET;
 }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/mgmt/RecordsConfig.cc
----------------------------------------------------------------------
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index aeebcf1..0b86350 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1990,6 +1990,14 @@ static const RecordElement RecordsConfig[] =
   {RECT_CONFIG, "proxy.config.spdy.accept_no_activity_timeout", RECD_INT, 
"120", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
   ,
 
+  //############
+  //#
+  //# HTTP/2 global configuration.
+  //#
+  //############
+  {RECT_CONFIG, "proxy.config.http2.enabled", RECD_INT, "0", RECU_RESTART_TM, 
RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
+  ,
+
   //# Add LOCAL Records Here
   {RECT_LOCAL, "proxy.local.incoming_ip_to_bind", RECD_STRING, NULL, 
RECU_NULL, RR_NULL, RECC_NULL, NULL, RECA_NULL}
   ,

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/Main.cc
----------------------------------------------------------------------
diff --git a/proxy/Main.cc b/proxy/Main.cc
index 9d197e3..d2a0be6 100644
--- a/proxy/Main.cc
+++ b/proxy/Main.cc
@@ -80,6 +80,7 @@ extern "C" int plock(int);
 #include "MgmtUtils.h"
 #include "StatPages.h"
 #include "HTTP.h"
+#include "HuffmanCodec.h"
 #include "Plugin.h"
 #include "DiagsConfig.h"
 #include "CoreUtils.h"
@@ -1185,6 +1186,7 @@ init_http_header()
   url_init();
   mime_init();
   http_init();
+  hpack_huffman_init();
 }
 
 struct AutoStopCont: public Continuation

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/Makefile.am
----------------------------------------------------------------------
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
index a225e5a..ce5041f 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -38,6 +38,7 @@ AM_CPPFLAGS = \
   -I$(top_srcdir)/lib/records \
   -I$(top_srcdir)/lib/ts \
   -I$(srcdir)/http \
+  -I$(srcdir)/http2 \
   -I$(srcdir)/spdy \
   -I$(srcdir)/logging \
   -I$(srcdir)/http/remap  \

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/hdrs/HdrToken.cc
----------------------------------------------------------------------
diff --git a/proxy/hdrs/HdrToken.cc b/proxy/hdrs/HdrToken.cc
index 9f012e6..9b44ae3 100644
--- a/proxy/hdrs/HdrToken.cc
+++ b/proxy/hdrs/HdrToken.cc
@@ -148,6 +148,10 @@ static const char *_hdrtoken_strs[] = {
   "Sec-WebSocket-Key",
   "Sec-WebSocket-Version",
 
+  // HTTP/2 cleartext
+  MIME_UPGRADE_H2C_TOKEN,
+  "HTTP2-Settings",
+
   // URL schemes
   "file",
   "ftp",
@@ -481,6 +485,10 @@ static const char *_hdrtoken_commonly_tokenized_strs[] = {
   "Sec-WebSocket-Key",
   "Sec-WebSocket-Version",
 
+  // HTTP/2 cleartext
+  MIME_UPGRADE_H2C_TOKEN,
+  "HTTP2-Settings",
+
   // URL schemes
   "file",
   "ftp",

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/hdrs/HdrToken.h
----------------------------------------------------------------------
diff --git a/proxy/hdrs/HdrToken.h b/proxy/hdrs/HdrToken.h
index 95dc95c..d273454 100644
--- a/proxy/hdrs/HdrToken.h
+++ b/proxy/hdrs/HdrToken.h
@@ -388,4 +388,10 @@ hdrtoken_wks_to_flags(const char *wks)
 /*-------------------------------------------------------------------------
   -------------------------------------------------------------------------*/
 
+// HTTP/2 Upgrade token
+#define MIME_UPGRADE_H2C_TOKEN "h2c-14"
+
+/*-------------------------------------------------------------------------
+  -------------------------------------------------------------------------*/
+
 #endif /* __HDRTOKEN_H__ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/hdrs/MIME.cc
----------------------------------------------------------------------
diff --git a/proxy/hdrs/MIME.cc b/proxy/hdrs/MIME.cc
index ca8de1a..85ddd3f 100644
--- a/proxy/hdrs/MIME.cc
+++ b/proxy/hdrs/MIME.cc
@@ -158,6 +158,7 @@ const char *MIME_FIELD_X_ID;
 const char *MIME_FIELD_X_FORWARDED_FOR;
 const char *MIME_FIELD_SEC_WEBSOCKET_KEY;
 const char *MIME_FIELD_SEC_WEBSOCKET_VERSION;
+const char *MIME_FIELD_HTTP2_SETTINGS;
 
 const char *MIME_VALUE_BYTES;
 const char *MIME_VALUE_CHUNKED;
@@ -182,6 +183,7 @@ const char *MIME_VALUE_PUBLIC;
 const char *MIME_VALUE_S_MAXAGE;
 const char *MIME_VALUE_NEED_REVALIDATE_ONCE;
 const char *MIME_VALUE_WEBSOCKET;
+const char *MIME_VALUE_H2C;
 
 // Cache-control: extension "need-revalidate-once" is used internally by T.S.
 // to invalidate a document, and it is not returned/forwarded.
@@ -271,6 +273,7 @@ int MIME_LEN_X_ID;
 int MIME_LEN_X_FORWARDED_FOR;
 int MIME_LEN_SEC_WEBSOCKET_KEY;
 int MIME_LEN_SEC_WEBSOCKET_VERSION;
+int MIME_LEN_HTTP2_SETTINGS;
 
 int MIME_WKSIDX_ACCEPT;
 int MIME_WKSIDX_ACCEPT_CHARSET;
@@ -348,6 +351,7 @@ int MIME_WKSIDX_X_ID;
 int MIME_WKSIDX_X_FORWARDED_FOR;
 int MIME_WKSIDX_SEC_WEBSOCKET_KEY;
 int MIME_WKSIDX_SEC_WEBSOCKET_VERSION;
+int MIME_WKSIDX_HTTP2_SETTINGS;
 
 /***********************************************************************
  *                                                                     *
@@ -722,6 +726,8 @@ mime_init()
     MIME_FIELD_SEC_WEBSOCKET_KEY = hdrtoken_string_to_wks("Sec-WebSocket-Key");
     MIME_FIELD_SEC_WEBSOCKET_VERSION = 
hdrtoken_string_to_wks("Sec-WebSocket-Version");
 
+    MIME_FIELD_HTTP2_SETTINGS = hdrtoken_string_to_wks("HTTP2-Settings");
+
 
     MIME_LEN_ACCEPT = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT);
     MIME_LEN_ACCEPT_CHARSET = 
hdrtoken_wks_to_length(MIME_FIELD_ACCEPT_CHARSET);
@@ -801,6 +807,8 @@ mime_init()
     MIME_LEN_SEC_WEBSOCKET_KEY = 
hdrtoken_wks_to_length(MIME_FIELD_SEC_WEBSOCKET_KEY);
     MIME_LEN_SEC_WEBSOCKET_VERSION = 
hdrtoken_wks_to_length(MIME_FIELD_SEC_WEBSOCKET_VERSION);
 
+    MIME_LEN_HTTP2_SETTINGS = 
hdrtoken_wks_to_length(MIME_FIELD_HTTP2_SETTINGS);
+
 
     MIME_WKSIDX_ACCEPT = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT);
     MIME_WKSIDX_ACCEPT_CHARSET = 
hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_CHARSET);
@@ -877,6 +885,7 @@ mime_init()
     MIME_WKSIDX_X_FORWARDED_FOR = 
hdrtoken_wks_to_index(MIME_FIELD_X_FORWARDED_FOR);
     MIME_WKSIDX_SEC_WEBSOCKET_KEY = 
hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_KEY);
     MIME_WKSIDX_SEC_WEBSOCKET_VERSION = 
hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_VERSION);
+    MIME_WKSIDX_HTTP2_SETTINGS = 
hdrtoken_wks_to_index(MIME_FIELD_HTTP2_SETTINGS);
 
     MIME_VALUE_BYTES = hdrtoken_string_to_wks("bytes");
     MIME_VALUE_CHUNKED = hdrtoken_string_to_wks("chunked");
@@ -901,6 +910,7 @@ mime_init()
     MIME_VALUE_S_MAXAGE = hdrtoken_string_to_wks("s-maxage");
     MIME_VALUE_NEED_REVALIDATE_ONCE = 
hdrtoken_string_to_wks("need-revalidate-once");
     MIME_VALUE_WEBSOCKET = hdrtoken_string_to_wks("websocket");
+    MIME_VALUE_H2C = hdrtoken_string_to_wks(MIME_UPGRADE_H2C_TOKEN);
 
 
     mime_init_date_format_table();

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/hdrs/MIME.h
----------------------------------------------------------------------
diff --git a/proxy/hdrs/MIME.h b/proxy/hdrs/MIME.h
index 860af82..4b001b1 100644
--- a/proxy/hdrs/MIME.h
+++ b/proxy/hdrs/MIME.h
@@ -389,6 +389,7 @@ extern const char *MIME_FIELD_X_ID;
 extern const char *MIME_FIELD_X_FORWARDED_FOR;
 extern const char *MIME_FIELD_SEC_WEBSOCKET_KEY;
 extern const char *MIME_FIELD_SEC_WEBSOCKET_VERSION;
+extern const char *MIME_FIELD_HTTP2_SETTINGS;
 
 extern const char *MIME_VALUE_BYTES;
 extern const char *MIME_VALUE_CHUNKED;
@@ -413,6 +414,7 @@ extern const char *MIME_VALUE_PUBLIC;
 extern const char *MIME_VALUE_S_MAXAGE;
 extern const char *MIME_VALUE_NEED_REVALIDATE_ONCE;
 extern const char *MIME_VALUE_WEBSOCKET;
+extern const char *MIME_VALUE_H2C;
 
 extern int MIME_LEN_ACCEPT;
 extern int MIME_LEN_ACCEPT_CHARSET;
@@ -515,6 +517,8 @@ extern int MIME_LEN_NEED_REVALIDATE_ONCE;
 extern int MIME_LEN_SEC_WEBSOCKET_KEY;
 extern int MIME_LEN_SEC_WEBSOCKET_VERSION;
 
+extern int MIME_LEN_HTTP2_SETTINGS;
+
 extern int MIME_WKSIDX_ACCEPT;
 extern int MIME_WKSIDX_ACCEPT_CHARSET;
 extern int MIME_WKSIDX_ACCEPT_ENCODING;
@@ -590,6 +594,7 @@ extern int MIME_WKSIDX_INT_DATA_INFO;
 extern int MIME_WKSIDX_X_ID;
 extern int MIME_WKSIDX_SEC_WEBSOCKET_KEY;
 extern int MIME_WKSIDX_SEC_WEBSOCKET_VERSION;
+extern int MIME_WKSIDX_HTTP2_SETTINGS;
 
 /***********************************************************************
  *                                                                     *
@@ -920,13 +925,13 @@ public:
 
   int parse(MIMEParser * parser, const char **start, const char *end, bool 
must_copy_strs, bool eof);
 
-  int value_get_index(const char *name, int name_length, const char *value, 
int value_length);
-  const char *value_get(const char *name, int name_length, int *value_length);
-  int32_t value_get_int(const char *name, int name_length);
-  uint32_t value_get_uint(const char *name, int name_length);
-  int64_t value_get_int64(const char *name, int name_length);
-  time_t value_get_date(const char *name, int name_length);
-  int value_get_comma_list(const char *name, int name_length, StrList * list);
+  int value_get_index(const char *name, int name_length, const char *value, 
int value_length) const;
+  const char *value_get(const char *name, int name_length, int *value_length) 
const;
+  int32_t value_get_int(const char *name, int name_length) const;
+  uint32_t value_get_uint(const char *name, int name_length) const;
+  int64_t value_get_int64(const char *name, int name_length) const;
+  time_t value_get_date(const char *name, int name_length) const;
+  int value_get_comma_list(const char *name, int name_length, StrList * list) 
const;
 
   void value_set(const char *name, int name_length, const char *value, int 
value_length);
   void value_set_int(const char *name, int name_length, int32_t value);
@@ -948,7 +953,7 @@ public:
   void field_value_append(MIMEField * field,
                           const char *value, int value_length, bool 
prepend_comma = false, const char separator = ',');
   time_t get_age();
-  int64_t get_content_length();
+  int64_t get_content_length() const;
   time_t get_date();
   time_t get_expires();
   time_t get_if_modified_since();
@@ -1214,9 +1219,9 @@ MIMEHdr::parse(MIMEParser * parser, const char **start, 
const char *end, bool mu
 /*-------------------------------------------------------------------------
   -------------------------------------------------------------------------*/
 inline int
-MIMEHdr::value_get_index(const char *name, int name_length, const char *value, 
int value_length)
+MIMEHdr::value_get_index(const char *name, int name_length, const char *value, 
int value_length) const
 {
-  MIMEField *field = field_find(name, name_length);
+  const MIMEField *field = field_find(name, name_length);
   if (field)
     return field->value_get_index(value, value_length);
   else
@@ -1227,10 +1232,10 @@ MIMEHdr::value_get_index(const char *name, int 
name_length, const char *value, i
   -------------------------------------------------------------------------*/
 
 inline const char *
-MIMEHdr::value_get(const char *name, int name_length, int *value_length_return)
+MIMEHdr::value_get(const char *name, int name_length, int 
*value_length_return) const
 {
 //    ink_assert(valid());
-  MIMEField *field = field_find(name, name_length);
+  const MIMEField *field = field_find(name, name_length);
 
   if (field)
     return (mime_field_value_get(field, value_length_return));
@@ -1239,9 +1244,9 @@ MIMEHdr::value_get(const char *name, int name_length, int 
*value_length_return)
 }
 
 inline int32_t
-MIMEHdr::value_get_int(const char *name, int name_length)
+MIMEHdr::value_get_int(const char *name, int name_length) const
 {
-  MIMEField *field = field_find(name, name_length);
+  const MIMEField *field = field_find(name, name_length);
 
   if (field)
     return (mime_field_value_get_int(field));
@@ -1250,9 +1255,9 @@ MIMEHdr::value_get_int(const char *name, int name_length)
 }
 
 inline uint32_t
-MIMEHdr::value_get_uint(const char *name, int name_length)
+MIMEHdr::value_get_uint(const char *name, int name_length) const
 {
-  MIMEField *field = field_find(name, name_length);
+  const MIMEField *field = field_find(name, name_length);
 
   if (field)
     return (mime_field_value_get_uint(field));
@@ -1261,9 +1266,9 @@ MIMEHdr::value_get_uint(const char *name, int name_length)
 }
 
 inline int64_t
-MIMEHdr::value_get_int64(const char *name, int name_length)
+MIMEHdr::value_get_int64(const char *name, int name_length) const
 {
-  MIMEField *field = field_find(name, name_length);
+  const MIMEField *field = field_find(name, name_length);
 
   if (field)
     return (mime_field_value_get_int64(field));
@@ -1272,9 +1277,9 @@ MIMEHdr::value_get_int64(const char *name, int 
name_length)
 }
 
 inline time_t
-MIMEHdr::value_get_date(const char *name, int name_length)
+MIMEHdr::value_get_date(const char *name, int name_length) const
 {
-  MIMEField *field = field_find(name, name_length);
+  const MIMEField *field = field_find(name, name_length);
 
   if (field)
     return (mime_field_value_get_date(field));
@@ -1283,9 +1288,9 @@ MIMEHdr::value_get_date(const char *name, int name_length)
 }
 
 inline int
-MIMEHdr::value_get_comma_list(const char *name, int name_length, StrList * 
list)
+MIMEHdr::value_get_comma_list(const char *name, int name_length, StrList * 
list) const
 {
-  MIMEField *field = field_find(name, name_length);
+  const MIMEField *field = field_find(name, name_length);
 
   if (field)
     return (field->value_get_comma_list(list));
@@ -1420,7 +1425,7 @@ MIMEHdr::get_age()
   -------------------------------------------------------------------------*/
 
 inline int64_t
-MIMEHdr::get_content_length()
+MIMEHdr::get_content_length() const
 {
   return (value_get_int64(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH));
 }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http/HttpClientSession.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpClientSession.cc b/proxy/http/HttpClientSession.cc
index 2ea5522..7bd19fc 100644
--- a/proxy/http/HttpClientSession.cc
+++ b/proxy/http/HttpClientSession.cc
@@ -37,6 +37,7 @@
 #include "HttpDebugNames.h"
 #include "HttpServerSession.h"
 #include "Plugin.h"
+#include "Http2ClientSession.h"
 
 #define DebugHttpSsn(fmt, ...) DebugSsn(this, "http_cs", fmt, __VA_ARGS__)
 
@@ -61,9 +62,9 @@ ClassAllocator<HttpClientSession> 
httpClientSessionAllocator("httpClientSessionA
 HttpClientSession::HttpClientSession()
   : con_id(0), client_vc(NULL), magic(HTTP_CS_MAGIC_DEAD),
     transact_count(0), tcp_init_cwnd_set(false),
-    half_close(false), conn_decrease(false), bound_ss(NULL),
-    read_buffer(NULL), current_reader(NULL), read_state(HCS_INIT),
-    ka_vio(NULL), slave_ka_vio(NULL),
+    half_close(false), conn_decrease(false), upgrade_to_h2c(false),
+    bound_ss(NULL),read_buffer(NULL), current_reader(NULL),
+    read_state(HCS_INIT), ka_vio(NULL), slave_ka_vio(NULL),
     outbound_port(0), f_outbound_transparent(false),
     host_res_style(HOST_RES_IPV4), acl_record(NULL),
     m_active(false)
@@ -75,7 +76,7 @@ HttpClientSession::destroy()
 {
   DebugHttpSsn("[%" PRId64 "] session destroy", con_id);
 
-  ink_release_assert(client_vc == NULL);
+  ink_release_assert(upgrade_to_h2c || !client_vc);
   ink_release_assert(bound_ss == NULL);
   ink_assert(read_buffer);
 
@@ -296,9 +297,16 @@ HttpClientSession::do_io_close(int alerrno)
     
client_vc->set_active_timeout(HRTIME_SECONDS(current_reader->t_state.txn_conf->keep_alive_no_activity_timeout_out));
   } else {
     read_state = HCS_CLOSED;
-    client_vc->do_io_close(alerrno);
-    DebugHttpSsn("[%" PRId64 "] session closed", con_id);
-    client_vc = NULL;
+    if (upgrade_to_h2c) {
+      Http2ClientSession * h2_session = http2ClientSessionAllocator.alloc();
+      
h2_session->set_upgrade_context(&current_reader->t_state.hdr_info.client_request);
+      h2_session->new_connection(client_vc, NULL, NULL, false /* backdoor */);
+      // TODO Consider about handling HTTP/1 hooks and stats
+    } else {
+      client_vc->do_io_close(alerrno);
+      DebugHttpSsn("[%" PRId64 "] session closed", con_id);
+      client_vc = NULL;
+    }
     HTTP_SUM_DYN_STAT(http_transactions_per_client_con, transact_count);
     HTTP_DECREMENT_DYN_STAT(http_current_client_connections_stat);
     conn_decrease = false;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http/HttpClientSession.h
----------------------------------------------------------------------
diff --git a/proxy/http/HttpClientSession.h b/proxy/http/HttpClientSession.h
index 24bd22a..6009e83 100644
--- a/proxy/http/HttpClientSession.h
+++ b/proxy/http/HttpClientSession.h
@@ -89,6 +89,8 @@ public:
 
   int get_transact_count() const { return  transact_count; }
 
+  void set_h2c_upgrade_flag() { upgrade_to_h2c = true; }
+
 private:
   HttpClientSession(HttpClientSession &);
 
@@ -113,6 +115,7 @@ private:
   bool tcp_init_cwnd_set;
   bool half_close;
   bool conn_decrease;
+  bool upgrade_to_h2c; // Switching to HTTP/2 with upgrade mechanism
 
   HttpServerSession *bound_ss;
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http/HttpTransact.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index 7cc6bec..a4b8e29 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -979,6 +979,29 @@ bool HttpTransact::handle_upgrade_request(State *s) {
         DebugTxn("http_trans_upgrade", "Unable to upgrade connection to 
websockets, invalid headers (RFC 6455).");
       }
     }
+
+    /*
+       draft-ietf-httpbis-http2-15
+
+       3.2.  Starting HTTP/2 for "http" URIs
+
+       The client makes an HTTP/1.1 request
+       that includes an Upgrade header field identifying HTTP/2 with the
+       "h2c" token.  The HTTP/1.1 request MUST include exactly one
+       HTTP2-Settings header field.
+     */
+    if (s->upgrade_token_wks == MIME_VALUE_H2C) {
+      MIMEField *http2_settings = 
s->hdr_info.client_request.field_find(MIME_FIELD_HTTP2_SETTINGS, 
MIME_LEN_HTTP2_SETTINGS);
+
+      // TODO Check whether h2c is enabled or not.
+      if (http2_settings) {
+        s->state_machine->ua_session->set_h2c_upgrade_flag();
+        build_upgrade_response(s);
+        TRANSACT_RETURN_VAL(SM_ACTION_INTERNAL_CACHE_NOOP, NULL, true);
+      } else {
+        DebugTxn("http_trans_upgrade", "Unable to upgrade connection to h2c, 
invalid headers");
+      }
+    }
   } else {
     DebugTxn("http_trans_upgrade", "Transaction requested upgrade for unknown 
protocol: %s", upgrade_hdr_val);
   }
@@ -8169,6 +8192,24 @@ HttpTransact::build_redirect_response(State* s)
   s->arena.str_free(to_free);
 }
 
+void
+HttpTransact::build_upgrade_response(State* s)
+{
+  DebugTxn("http_upgrade", "[HttpTransact::build_upgrade_response]");
+
+  // 101 Switching Protocols
+  HTTPStatus status_code = HTTP_STATUS_SWITCHING_PROTOCOL;
+  const char *reason_phrase = http_hdr_reason_lookup(status_code);
+  build_response(s, &s->hdr_info.client_response, s->client_info.http_version, 
status_code, reason_phrase);
+
+  //////////////////////////
+  // set upgrade headers  //
+  //////////////////////////
+  HTTPHdr *h = &s->hdr_info.client_response;
+  h->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "Upgrade", 
strlen("Upgrade"));
+  h->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, MIME_UPGRADE_H2C_TOKEN, 
strlen(MIME_UPGRADE_H2C_TOKEN));
+}
+
 const char *
 HttpTransact::get_error_string(int erno)
 {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http/HttpTransact.h
----------------------------------------------------------------------
diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h
index 4de29e8..4219677 100644
--- a/proxy/http/HttpTransact.h
+++ b/proxy/http/HttpTransact.h
@@ -1359,6 +1359,7 @@ public:
   static void build_error_response(State *s, HTTPStatus status_code, const 
char *reason_phrase_or_null, const char *error_body_type,
                                    const char *format, ...);
   static void build_redirect_response(State* s);
+  static void build_upgrade_response(State* s);
   static const char *get_error_string(int erno);
 
   // the stat functions

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http/Makefile.am
----------------------------------------------------------------------
diff --git a/proxy/http/Makefile.am b/proxy/http/Makefile.am
index 8ff6985..258b1ee 100644
--- a/proxy/http/Makefile.am
+++ b/proxy/http/Makefile.am
@@ -32,7 +32,8 @@ AM_CPPFLAGS = \
   -I$(top_srcdir)/proxy/shared \
   -I$(top_srcdir)/proxy/http/remap \
   -I$(top_srcdir)/proxy/logging \
-  -I$(top_srcdir)/proxy/spdy
+  -I$(top_srcdir)/proxy/spdy \
+  -I$(top_srcdir)/proxy/http2
 
 noinst_HEADERS = HttpProxyServerMain.h
 noinst_LIBRARIES = libhttp.a

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http2/HPACK.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/HPACK.cc b/proxy/http2/HPACK.cc
index 2ef5083..f56a934 100644
--- a/proxy/http2/HPACK.cc
+++ b/proxy/http2/HPACK.cc
@@ -180,10 +180,10 @@ const static struct {
 };
 
 int
-Http2HeaderTable::get_header_from_indexing_tables(uint32_t index, 
MIMEFieldWrapper& field) const
+Http2DynamicTable::get_header_from_indexing_tables(uint32_t index, 
MIMEFieldWrapper& field) const
 {
   // Index Address Space starts at 1, so index == 0 is invalid.
-  if (!index) return -1;
+  if (!index) return HPACK_ERROR_COMPRESSION_ERROR;
 
   if (index < TS_HPACK_STATIC_TABLE_ENTRY_NUM) {
     field.name_set(STATIC_TABLE[index].name, strlen(STATIC_TABLE[index].name));
@@ -201,7 +201,7 @@ Http2HeaderTable::get_header_from_indexing_tables(uint32_t 
index, MIMEFieldWrapp
     // 3.3.3.  Index Address Space
     // Indices strictly greater than the sum of the lengths of both tables
     // MUST be treated as a decoding error.
-    return -1;
+    return HPACK_ERROR_COMPRESSION_ERROR;
   }
 
   return 0;
@@ -212,9 +212,9 @@ Http2HeaderTable::get_header_from_indexing_tables(uint32_t 
index, MIMEFieldWrapp
 // are evicted from the end of the header table until the size of the
 // header table is less than or equal to the maximum size.
 void
-Http2HeaderTable::set_header_table_size(uint32_t new_size)
+Http2DynamicTable::set_dynamic_table_size(uint32_t new_size)
 {
-  uint32_t old_size = _settings_header_table_size;
+  uint32_t old_size = _settings_dynamic_table_size;
   while (old_size > new_size) {
     int last_name_len, last_value_len;
     MIMEField* last_field = _headers.last();
@@ -227,25 +227,25 @@ Http2HeaderTable::set_header_table_size(uint32_t new_size)
     _mhdr->field_delete(last_field, false);
   }
 
-  _settings_header_table_size = new_size;
+  _settings_dynamic_table_size = new_size;
 }
 
 void
-Http2HeaderTable::add_header_field(const MIMEField * field)
+Http2DynamicTable::add_header_field(const MIMEField * field)
 {
   int name_len, value_len;
   const char * name = field->name_get(&name_len);
   const char * value = field->value_get(&value_len);
   uint32_t header_size = ADDITIONAL_OCTETS + name_len + value_len;
 
-  if (header_size > _settings_header_table_size) {
+  if (header_size > _settings_dynamic_table_size) {
     // 5.3. It is not an error to attempt to add an entry that is larger than 
the maximum size; an
     // attempt to add an entry larger than the entire table causes the table 
to be emptied of all existing entries.
     _headers.clear();
     _mhdr->fields_clear();
   } else {
     _current_size += header_size;
-    while (_current_size > _settings_header_table_size) {
+    while (_current_size > _settings_dynamic_table_size) {
       int last_name_len, last_value_len;
       MIMEField* last_field = _headers.last();
 
@@ -310,9 +310,9 @@ encode_integer(uint8_t *buf_start, const uint8_t *buf_end, 
uint32_t value, uint8
   uint8_t *p = buf_start;
 
   if (value < (static_cast<uint32_t>(1 << n) - 1)) {
-    *(p++) |= value;
+    *(p++) = value;
   } else {
-    *(p++) |= (1 << n) - 1;
+    *(p++) = (1 << n) - 1;
     value -= (1 << n) - 1;
     while (value >= 128) {
       if (p >= buf_end) {
@@ -442,10 +442,16 @@ encode_literal_header_field(uint8_t *buf_start, const 
uint8_t *buf_end, const MI
   }
   *(p++) = flag;
 
-  // Name String
+  // Convert field name to lower case
+  Arena arena;
   int name_len;
   const char* name = header.name_get(&name_len);
-  len = encode_string(p, buf_end, name, name_len);
+  char* lower_name = arena.str_store(name, name_len);
+  for (int i=0; i<name_len; i++)
+    lower_name[i] = ParseRules::ink_tolower(lower_name[i]);
+
+  // Name String
+  len = encode_string(p, buf_end, lower_name, name_len);
   if (len == -1) return -1;
   p += len;
 
@@ -488,13 +494,13 @@ decode_integer(uint32_t& dst, const uint8_t *buf_start, 
const uint8_t *buf_end,
   if (dst == static_cast<uint32_t>(1 << n) - 1) {
     int m = 0;
     do {
-      if (++p >= buf_end) return -1;
+      if (++p >= buf_end) return HPACK_ERROR_COMPRESSION_ERROR;
 
       uint32_t added_value = *p & 0x7f;
       if ((UINT32_MAX >> m) < added_value) {
         // Excessively large integer encodings - in value or octet
         // length - MUST be treated as a decoding error.
-        return -1;
+        return HPACK_ERROR_COMPRESSION_ERROR;
       }
       dst += added_value << m;
       m += 7;
@@ -507,7 +513,7 @@ decode_integer(uint32_t& dst, const uint8_t *buf_start, 
const uint8_t *buf_end,
 // 6.2 return content from String Data (Length octets)
 // with huffman decoding if it is encoded
 int64_t
-decode_string(char **c_str, uint32_t& c_str_length, const uint8_t *buf_start, 
const uint8_t *buf_end)
+decode_string(Arena& arena, char **str, uint32_t& str_length, const uint8_t 
*buf_start, const uint8_t *buf_end)
 {
   const uint8_t *p = buf_start;
   bool isHuffman = *p & 0x80;
@@ -515,26 +521,26 @@ decode_string(char **c_str, uint32_t& c_str_length, const 
uint8_t *buf_start, co
   int64_t len = 0;
 
   len = decode_integer(encoded_string_len, p, buf_end, 7);
-  if (len == -1) return -1;
+  if (len == HPACK_ERROR_COMPRESSION_ERROR || encoded_string_len == 0) return 
HPACK_ERROR_COMPRESSION_ERROR;
   p += len;
 
   if (encoded_string_len > HEADER_FIELD_LIMIT_LENGTH || buf_start + 
encoded_string_len >= buf_end) {
-    return -1;
+    return HPACK_ERROR_COMPRESSION_ERROR;
   }
 
   if (isHuffman) {
     // Allocate temporary area twice the size of before decoded data
-    *c_str = static_cast<char*>(ats_malloc(encoded_string_len * 2));
+    *str = arena.str_alloc(encoded_string_len * 2);
 
-    len = huffman_decode(*c_str, p, encoded_string_len);
-    if (len == -1) return -1;
-    c_str_length = len;
+    len = huffman_decode(*str, p, encoded_string_len);
+    if (len == HPACK_ERROR_COMPRESSION_ERROR) return 
HPACK_ERROR_COMPRESSION_ERROR;
+    str_length = len;
   } else {
-    *c_str = static_cast<char*>(ats_malloc(encoded_string_len));
+    *str = arena.str_alloc(encoded_string_len);
 
-    memcpy(*c_str, reinterpret_cast<const char*>(p), encoded_string_len);
+    memcpy(*str, reinterpret_cast<const char*>(p), encoded_string_len);
 
-    c_str_length = encoded_string_len;
+    str_length = encoded_string_len;
   }
 
   return p + encoded_string_len - buf_start;
@@ -542,15 +548,27 @@ decode_string(char **c_str, uint32_t& c_str_length, const 
uint8_t *buf_start, co
 
 // 7.1. Indexed Header Field Representation
 int64_t
-decode_indexed_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2HeaderTable& header_table)
+decode_indexed_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2DynamicTable& dynamic_table)
 {
   uint32_t index = 0;
   int64_t len = 0;
+
   len = decode_integer(index, buf_start, buf_end, 7);
-  if (len == -1) return -1;
+  if (len == HPACK_ERROR_COMPRESSION_ERROR) return 
HPACK_ERROR_COMPRESSION_ERROR;
 
-  if (header_table.get_header_from_indexing_tables(index, header) == -1) {
-    return -1;
+  if (dynamic_table.get_header_from_indexing_tables(index, header) == 
HPACK_ERROR_COMPRESSION_ERROR) {
+    return HPACK_ERROR_COMPRESSION_ERROR;
+  }
+
+  if (is_debug_tag_set("http2_hpack_decode")) {
+    int decoded_name_len;
+    const char *decoded_name = header.name_get(&decoded_name_len);
+    int decoded_value_len;
+    const char *decoded_value = header.value_get(&decoded_value_len);
+
+    Arena arena;
+    Debug("http2_hpack_decode", "Decoded field:  %s: %s\n",
+        arena.str_store(decoded_name, decoded_name_len), 
arena.str_store(decoded_value, decoded_value_len));
   }
 
   return len;
@@ -558,7 +576,7 @@ decode_indexed_header_field(MIMEFieldWrapper& header, const 
uint8_t *buf_start,
 
 // 7.2.  Literal Header Field Representation
 int64_t
-decode_literal_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2HeaderTable& header_table)
+decode_literal_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2DynamicTable& dynamic_table)
 {
   const uint8_t *p = buf_start;
   bool isIncremental = false;
@@ -579,40 +597,58 @@ decode_literal_header_field(MIMEFieldWrapper& header, 
const uint8_t *buf_start,
     len = decode_integer(index, p, buf_end, 4);
   }
 
-  if (len == -1) {
-    return -1;
-  }
+  if (len == HPACK_ERROR_COMPRESSION_ERROR) return 
HPACK_ERROR_COMPRESSION_ERROR;
 
   p += len;
 
+  Arena arena;
+
+  // Decode header field name
   if (index) {
-    header_table.get_header_from_indexing_tables(index, header);
+    dynamic_table.get_header_from_indexing_tables(index, header);
   } else {
-    char *c_name = NULL;
-    uint32_t c_name_len = 0;
-    len = decode_string(&c_name, c_name_len, p, buf_end);
-    if (len == -1) {
-      ats_free(c_name);
-      return -1;
+    char* name_str = NULL;
+    uint32_t name_str_len = 0;
+
+    len = decode_string(arena, &name_str, name_str_len, p, buf_end);
+    if (len == HPACK_ERROR_COMPRESSION_ERROR) return 
HPACK_ERROR_COMPRESSION_ERROR;
+
+    // Check whether header field name is lower case
+    for (uint32_t i=0; i<name_str_len; i++) {
+      if (ParseRules::is_upalpha(name_str[i])) {
+        return -2;
+      }
     }
+
     p += len;
-    header.name_set(c_name, c_name_len);
-    ats_free(c_name);
-  }
-  char *c_value = NULL;
-  uint32_t c_value_len = 0;
-  len = decode_string(&c_value, c_value_len, p, buf_end);
-  if (len == -1) {
-    ats_free(c_value);
-    return -1;
+    header.name_set(name_str, name_str_len);
   }
+
+  // Decode header field value
+  char* value_str = NULL;
+  uint32_t value_str_len = 0;
+
+  len = decode_string(arena, &value_str, value_str_len, p, buf_end);
+  if (len == HPACK_ERROR_COMPRESSION_ERROR) return 
HPACK_ERROR_COMPRESSION_ERROR;
+
   p += len;
-  header.value_set(c_value, c_value_len);
-  ats_free(c_value);
+  header.value_set(value_str, value_str_len);
+
 
   // Incremental Indexing adds header to header table as new entry
   if (isIncremental) {
-    header_table.add_header_field(header.field_get());
+    dynamic_table.add_header_field(header.field_get());
+  }
+
+  // Print decoded header field
+  if (is_debug_tag_set("http2_hpack_decode")) {
+    int decoded_name_len;
+    const char *decoded_name = header.name_get(&decoded_name_len);
+    int decoded_value_len;
+    const char *decoded_value = header.value_get(&decoded_value_len);
+
+    Debug("http2_hpack_decode", "Decoded field:  %s: %s\n",
+        arena.str_store(decoded_name, decoded_name_len), 
arena.str_store(decoded_value, decoded_value_len));
   }
 
   return p - buf_start;
@@ -620,20 +656,16 @@ decode_literal_header_field(MIMEFieldWrapper& header, 
const uint8_t *buf_start,
 
 // 7.3. Header Table Size Update
 int64_t
-update_header_table_size(const uint8_t *buf_start, const uint8_t *buf_end, 
Http2HeaderTable& header_table)
+update_dynamic_table_size(const uint8_t *buf_start, const uint8_t *buf_end, 
Http2DynamicTable& dynamic_table)
 {
-  if (buf_start == buf_end) return -1;
-
-  int64_t len = 0;
+  if (buf_start == buf_end) return HPACK_ERROR_COMPRESSION_ERROR;
 
   // Update header table size if its required.
-  if ((*buf_start & 0xe0) == 0x20) {
-    uint32_t size = 0;
-    len = decode_integer(size, buf_start, buf_end, 5);
-    if (len == -1) return -1;
+  uint32_t size = 0;
+  int64_t len = decode_integer(size, buf_start, buf_end, 5);
+  if (len == HPACK_ERROR_COMPRESSION_ERROR) return 
HPACK_ERROR_COMPRESSION_ERROR;
 
-    header_table.set_header_table_size(size);
-  }
+  dynamic_table.set_dynamic_table_size(size);
 
   return len;
 }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http2/HPACK.h
----------------------------------------------------------------------
diff --git a/proxy/http2/HPACK.h b/proxy/http2/HPACK.h
index 593b193..f0ff8d0 100644
--- a/proxy/http2/HPACK.h
+++ b/proxy/http2/HPACK.h
@@ -40,6 +40,11 @@ extern const unsigned HPACK_LEN_AUTHORITY;
 extern const unsigned HPACK_LEN_PATH;
 extern const unsigned HPACK_LEN_STATUS;
 
+// It means that any header field can be compressed/decompressed by ATS
+const static int HPACK_ERROR_COMPRESSION_ERROR  = -1;
+// It means that any header field is invalid in HTTP/2 spec
+const static int HPACK_ERROR_HTTP2_PROTOCOL_ERROR = -2;
+
 enum HpackFieldType
 {
   HPACK_FIELD_INDEX,              // HPACK 7.1 Indexed Header Field 
Representation
@@ -83,17 +88,17 @@ private:
   MIMEHdrImpl * _mh;
 };
 
-// 3.2 Header Table
-class Http2HeaderTable
+// 2.3.2. Dynamic Table
+class Http2DynamicTable
 {
 public:
 
-  Http2HeaderTable() : _current_size(0), _settings_header_table_size(4096) {
+  Http2DynamicTable() : _current_size(0), _settings_dynamic_table_size(4096) {
     _mhdr = new MIMEHdr();
     _mhdr->create();
   }
 
-  ~Http2HeaderTable() {
+  ~Http2DynamicTable() {
     _headers.clear();
     _mhdr->fields_clear();
     delete _mhdr;
@@ -101,7 +106,7 @@ public:
 
   void add_header_field(const MIMEField * field);
   int get_header_from_indexing_tables(uint32_t index, MIMEFieldWrapper& 
header_field) const;
-  void set_header_table_size(uint32_t new_size);
+  void set_dynamic_table_size(uint32_t new_size);
 
 private:
 
@@ -114,7 +119,7 @@ private:
   }
 
   uint32_t          _current_size;
-  uint32_t          _settings_header_table_size;
+  uint32_t          _settings_dynamic_table_size;
 
   MIMEHdr *         _mhdr;
   Vec<MIMEField *>  _headers;
@@ -138,7 +143,7 @@ decode_integer(uint32_t& dst, const uint8_t *buf_start, 
const uint8_t *buf_end,
 int64_t
 encode_string(uint8_t *buf_start, const uint8_t *buf_end, const char* value, 
size_t value_len);
 int64_t
-decode_string(char **c_str, uint32_t& c_str_length, const uint8_t *buf_start, 
const uint8_t *buf_end);
+decode_string(Arena& arena, char **str, uint32_t& str_length, const uint8_t 
*buf_start, const uint8_t *buf_end);
 
 int64_t
 encode_indexed_header_field(uint8_t *buf_start, const uint8_t *buf_end, 
uint32_t index);
@@ -147,11 +152,13 @@ encode_literal_header_field(uint8_t *buf_start, const 
uint8_t *buf_end, const MI
 int64_t
 encode_literal_header_field(uint8_t *buf_start, const uint8_t *buf_end, const 
MIMEFieldWrapper& header, HpackFieldType type);
 
+// When these functions returns minus value, any error occurs
+// TODO Separate error code and length of processed buffer
 int64_t
-decode_indexed_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2HeaderTable& header_table);
+decode_indexed_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2DynamicTable& dynamic_table);
 int64_t
-decode_literal_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2HeaderTable& header_table);
+decode_literal_header_field(MIMEFieldWrapper& header, const uint8_t 
*buf_start, const uint8_t *buf_end, Http2DynamicTable& dynamic_table);
 int64_t
-update_header_table_size(const uint8_t *buf_start, const uint8_t *buf_end, 
Http2HeaderTable& header_table);
+update_dynamic_table_size(const uint8_t *buf_start, const uint8_t *buf_end, 
Http2DynamicTable& dynamic_table);
 
 #endif /* __HPACK_H__ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http2/HTTP2.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc
index 920c633..e11e8a6 100644
--- a/proxy/http2/HTTP2.cc
+++ b/proxy/http2/HTTP2.cc
@@ -27,6 +27,7 @@
 #include "ink_assert.h"
 
 const char * const HTTP2_CONNECTION_PREFACE = "PRI * 
HTTP/2.0\r\n\r\nSM\r\n\r\n";
+static size_t HPACK_LEN_STATUS_VALUE_STR = 3;
 
 union byte_pointer {
   byte_pointer(void * p) : ptr(p) {}
@@ -45,6 +46,13 @@ union byte_addressable_value
 };
 
 static void
+write_and_advance(byte_pointer& dst, const uint8_t* src, size_t length)
+{
+  memcpy(dst.u8, src, length);
+  dst.u8 += length;
+}
+
+static void
 write_and_advance(byte_pointer& dst, uint32_t src)
 {
   byte_addressable_value<uint32_t> pval;
@@ -54,8 +62,6 @@ write_and_advance(byte_pointer& dst, uint32_t src)
   dst.u8 += sizeof(pval.bytes);
 }
 
-// Avoid a [-Werror,-Wunused-function] error until we need this overload ...
-#if 0
 static void
 write_and_advance(byte_pointer& dst, uint16_t src)
 {
@@ -65,7 +71,6 @@ write_and_advance(byte_pointer& dst, uint16_t src)
   memcpy(dst.u8, pval.bytes, sizeof(pval.bytes));
   dst.u8 += sizeof(pval.bytes);
 }
-#endif
 
 static void
 write_and_advance(byte_pointer& dst, uint8_t src)
@@ -102,8 +107,6 @@ http2_are_frame_flags_valid(uint8_t ftype, uint8_t fflags)
     HTTP2_FLAGS_GOAWAY_MASK,
     HTTP2_FLAGS_WINDOW_UPDATE_MASK,
     HTTP2_FLAGS_CONTINUATION_MASK,
-    HTTP2_FLAGS_ALTSVC_MASK,
-    HTTP2_FLAGS_BLOCKED_MASK,
   };
 
   // The frame flags are valid for this frame if nothing outside the defined 
bits is set.
@@ -117,7 +120,7 @@ http2_frame_header_is_valid(const Http2FrameHeader& hdr)
     return false;
   }
 
-  if (hdr.length > HTTP2_MAX_FRAME_PAYLOAD) {
+  if (hdr.length > HTTP2_MAX_FRAME_SIZE) {
     return false;
   }
 
@@ -150,6 +153,16 @@ http2_settings_parameter_is_valid(const 
Http2SettingsParameter& param)
     return false;
   }
 
+  if (param.id == HTTP2_SETTINGS_ENABLE_PUSH &&
+      param.value != 0 && param.value != 1) {
+    return false;
+  }
+
+  if (param.id == HTTP2_SETTINGS_MAX_FRAME_SIZE &&
+      (param.value < (1 << 14) || param.value > (1 << 24) -1)) {
+    return false;
+  }
+
   return true;
 }
 
@@ -213,6 +226,60 @@ http2_write_frame_header(const Http2FrameHeader& hdr, 
IOVec iov)
   return true;
 }
 
+bool
+http2_write_data(const uint8_t* src, size_t length, const IOVec& iov)
+{
+  byte_pointer ptr(iov.iov_base);
+  write_and_advance(ptr, src, length);
+
+  return true;
+}
+
+bool
+http2_write_headers(const uint8_t* src, size_t length, const IOVec& iov)
+{
+  byte_pointer ptr(iov.iov_base);
+  write_and_advance(ptr, src, length);
+
+  return true;
+}
+
+bool
+http2_write_rst_stream(uint32_t error_code, IOVec iov)
+{
+  byte_pointer ptr(iov.iov_base);
+
+  write_and_advance(ptr, error_code);
+
+  return true;
+}
+
+bool
+http2_write_settings(const Http2SettingsParameter& param, IOVec iov)
+{
+  byte_pointer ptr(iov.iov_base);
+
+  if (unlikely(iov.iov_len < HTTP2_SETTINGS_PARAMETER_LEN)) {
+    return false;
+  }
+
+  write_and_advance(ptr, param.id);
+  write_and_advance(ptr, param.value);
+
+  return true;
+}
+
+bool
+http2_write_ping(const uint8_t * opaque_data, IOVec iov)
+{
+  if (iov.iov_len != HTTP2_PING_LEN)
+    return false;
+
+  memcpy(iov.iov_base, opaque_data, HTTP2_PING_LEN);
+
+  return true;
+}
+
 // 6.8. GOAWAY
 //
 // 0                   1                   2                   3
@@ -240,6 +307,70 @@ http2_write_goaway(const Http2Goaway& goaway, IOVec iov)
   return true;
 }
 
+bool
+http2_write_window_update(const uint32_t new_size, const IOVec& iov)
+{
+  byte_pointer ptr(iov.iov_base);
+  write_and_advance(ptr, new_size);
+
+  return true;
+}
+
+bool
+http2_parse_headers_parameter(IOVec iov, Http2HeadersParameter& params)
+{
+  byte_pointer ptr(iov.iov_base);
+  memcpy_and_advance(params.pad_length, ptr);
+
+  return true;
+}
+
+
+// 6.3.  PRIORITY
+//
+// 0                   1                   2                   3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |E|                  Stream Dependency (31)                     |
+// +-+-------------+-----------------------------------------------+
+// |   Weight (8)  |
+// +-+-------------+
+
+bool
+http2_parse_priority_parameter(IOVec iov, Http2Priority& params)
+{
+  byte_pointer ptr(iov.iov_base);
+  byte_addressable_value<uint32_t> dependency;
+
+  memcpy_and_advance(dependency.bytes, ptr);
+  memcpy_and_advance(params.weight, ptr);
+
+  params.stream_dependency = ntohs(dependency.value);
+
+  return true;
+}
+
+// 6.4.  RST_STREAM
+//
+// 0                   1                   2                   3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                        Error Code (32)                        |
+// +---------------------------------------------------------------+
+
+bool
+http2_parse_rst_stream(IOVec iov, Http2RstStream& rst_stream)
+{
+  byte_pointer ptr(iov.iov_base);
+  byte_addressable_value<uint32_t> ec;
+
+  memcpy_and_advance(ec.bytes, ptr);
+
+  rst_stream.error_code = ntohl(ec.value);
+
+  return true;
+}
+
 // 6.5.1.  SETTINGS Format
 //
 // 0                   1                   2                   3
@@ -270,6 +401,56 @@ http2_parse_settings_parameter(IOVec iov, 
Http2SettingsParameter& param)
   return true;
 }
 
+
+// 6.8.  GOAWAY
+//
+// 0                   1                   2                   3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |R|                  Last-Stream-ID (31)                        |
+// +-+-------------------------------------------------------------+
+// |                      Error Code (32)                          |
+// +---------------------------------------------------------------+
+// |                  Additional Debug Data (*)                    |
+// +---------------------------------------------------------------+
+
+bool
+http2_parse_goaway(IOVec iov, Http2Goaway& goaway)
+{
+  byte_pointer ptr(iov.iov_base);
+  byte_addressable_value<uint32_t> sid;
+  byte_addressable_value<uint32_t> ec;
+
+  memcpy_and_advance(sid.bytes, ptr);
+  memcpy_and_advance(ec.bytes, ptr);
+
+  goaway.last_streamid = ntohl(sid.value);
+  goaway.error_code = ntohl(ec.value);
+  return true;
+}
+
+
+// 6.9.  WINDOW_UPDATE
+//
+// 0                   1                   2                   3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |R|              Window Size Increment (31)                     |
+// +-+-------------------------------------------------------------+
+
+bool
+http2_parse_window_update(IOVec iov, uint32_t& size)
+{
+  byte_pointer ptr(iov.iov_base);
+  byte_addressable_value<uint32_t> s;
+
+  memcpy_and_advance(s.bytes, ptr);
+
+  size = ntohl(s.value);
+
+  return true;
+}
+
 MIMEParseResult
 convert_from_2_to_1_1_header(HTTPHdr* headers)
 {
@@ -348,38 +529,118 @@ convert_from_2_to_1_1_header(HTTPHdr* headers)
     headers->field_delete(HPACK_VALUE_STATUS, HPACK_LEN_STATUS);
   }
 
+  // Intermediaries SHOULD also remove other connection-
+  // specific header fields, such as Keep-Alive, Proxy-Connection,
+  // Transfer-Encoding and Upgrade, even if they are not nominated by
+  // Connection.
+  headers->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
+  headers->field_delete(MIME_FIELD_KEEP_ALIVE, MIME_LEN_KEEP_ALIVE);
+  headers->field_delete(MIME_FIELD_PROXY_CONNECTION, 
MIME_LEN_PROXY_CONNECTION);
+  headers->field_delete(MIME_FIELD_TRANSFER_ENCODING, 
MIME_LEN_TRANSFER_ENCODING);
+  headers->field_delete(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
+
   return PARSE_DONE;
 }
 
+void
+convert_headers_from_1_1_to_2(HTTPHdr* in)
+{
+  // Intermediaries SHOULD also remove other connection-
+  // specific header fields, such as Keep-Alive, Proxy-Connection,
+  // Transfer-Encoding and Upgrade, even if they are not nominated by
+  // Connection.
+  in->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
+  in->field_delete(MIME_FIELD_KEEP_ALIVE, MIME_LEN_KEEP_ALIVE);
+  in->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
+  in->field_delete(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING);
+  in->field_delete(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
+}
+
+int64_t
+http2_write_psuedo_headers(HTTPHdr* in, uint8_t* out, uint64_t out_len, 
Http2DynamicTable& /* dynamic_table */)
+{
+  uint8_t *p = out;
+  uint8_t *end = out + out_len;
+  int64_t len;
+
+  ink_assert(http_hdr_type_get(in->m_http) != HTTP_TYPE_UNKNOWN);
+
+  // TODO Check whether buffer size is enough
+
+  // Set psuedo header
+  if (http_hdr_type_get(in->m_http) == HTTP_TYPE_RESPONSE) {
+    char status_str[HPACK_LEN_STATUS_VALUE_STR+1];
+    snprintf(status_str, sizeof(status_str), "%d", in->status_get());
+
+    // Add 'Status:' dummy header field
+    MIMEField *status_field = mime_field_create(in->m_heap, 
in->m_http->m_fields_impl);
+    mime_field_name_value_set(in->m_heap, in->m_mime, status_field, -1,
+                              HPACK_VALUE_STATUS, HPACK_LEN_STATUS,
+                              status_str, HPACK_LEN_STATUS_VALUE_STR, true, 
HPACK_LEN_STATUS+HPACK_LEN_STATUS_VALUE_STR, 0);
+    mime_hdr_field_attach(in->m_mime, status_field, 1, NULL);
+
+    // Encode psuedo headers by HPACK
+    MIMEFieldWrapper header(status_field, in->m_heap, 
in->m_http->m_fields_impl);
+    len = encode_literal_header_field(p, end, header, 
HPACK_FIELD_NEVERINDEX_LITERAL);
+    if (len == -1) return -1;
+    p += len;
+
+    // Remove dummy header field
+    in->field_delete(HPACK_VALUE_STATUS, HPACK_LEN_STATUS);
+  }
+
+  return p - out;
+}
+
 int64_t
-convert_from_1_1_to_2_header(HTTPHdr* in, uint8_t* out, uint64_t out_len, 
Http2HeaderTable& /* header_table */)
+http2_write_header_fragment(HTTPHdr* in, MIMEFieldIter& field_iter, uint8_t* 
out, uint64_t out_len, Http2DynamicTable& /* dynamic_table */, bool& cont)
 {
   uint8_t *p = out;
   uint8_t *end = out + out_len;
   int64_t len;
 
   ink_assert(http_hdr_type_get(in->m_http) != HTTP_TYPE_UNKNOWN);
+  ink_assert(in);
 
   // TODO Get a index value from the tables for the header field, and then 
choose a representation type.
   // TODO Each indexing types per field should be passed by a caller, HTTP/2 
implementation.
 
+  // Get first header field which is required encoding
   MIMEField* field;
-  MIMEFieldIter field_iter;
-  for (field = in->iter_get_first(&field_iter); field != NULL; field = 
in->iter_get_next(&field_iter)) {
+  if (!field_iter.m_block) {
+    field = in->iter_get_first(&field_iter);
+  } else {
+    field = in->iter_get_next(&field_iter);
+  }
+
+  // Set mime headers
+  cont = false;
+  while (field) {
+    MIMEFieldIter current_iter = field_iter;
     do {
       MIMEFieldWrapper header(field, in->m_heap, in->m_http->m_fields_impl);
       if ((len = encode_literal_header_field(p, end, header, 
HPACK_FIELD_INDEXED_LITERAL)) == -1) {
-        return -1;
+        if (!cont) {
+          // Parsing a part of headers is done
+          cont = true;
+          field_iter = current_iter;
+          return p - out;
+        } else {
+          // Parse error
+          return -1;
+        }
       }
       p += len;
     } while (field->has_dups() && (field = field->m_next_dup) != NULL);
+    field = in->iter_get_next(&field_iter);
   }
 
+  // Parsing all headers is done
   return p - out;
 }
 
-MIMEParseResult
-http2_parse_header_fragment(HTTPHdr * hdr, IOVec iov, Http2HeaderTable& 
header_table)
+int64_t
+http2_parse_header_fragment(HTTPHdr * hdr, IOVec iov, Http2DynamicTable& 
dynamic_table, bool cont)
 {
   uint8_t * buf_start = (uint8_t *)iov.iov_base;
   uint8_t * buf_end = (uint8_t *)iov.iov_base + iov.iov_len;
@@ -391,10 +652,6 @@ http2_parse_header_fragment(HTTPHdr * hdr, IOVec iov, 
Http2HeaderTable& header_t
   do {
     int64_t read_bytes = 0;
 
-    if ((read_bytes = update_header_table_size(cursor, buf_end, header_table)) 
== -1) {
-      return PARSE_ERROR;
-    }
-
     // decode a header field encoded by HPACK
     MIMEField *field = mime_field_create(heap, hh->m_fields_impl);
     MIMEFieldWrapper header(field, heap, hh->m_fields_impl);
@@ -402,29 +659,91 @@ http2_parse_header_fragment(HTTPHdr * hdr, IOVec iov, 
Http2HeaderTable& header_t
 
     switch (ftype) {
     case HPACK_FIELD_INDEX:
-      if ((read_bytes = decode_indexed_header_field(header, cursor, buf_end, 
header_table)) == -1) {
-        return PARSE_ERROR;
+      read_bytes = decode_indexed_header_field(header, cursor, buf_end, 
dynamic_table);
+      if (read_bytes == HPACK_ERROR_COMPRESSION_ERROR) {
+        if (cont) {
+          // Parsing a part of headers is done
+          return cursor - buf_start;
+        } else {
+          // Parse error
+          return HPACK_ERROR_COMPRESSION_ERROR;
+        }
       }
       cursor += read_bytes;
       break;
     case HPACK_FIELD_INDEXED_LITERAL:
     case HPACK_FIELD_NOINDEX_LITERAL:
     case HPACK_FIELD_NEVERINDEX_LITERAL:
-      if ((read_bytes = decode_literal_header_field(header, cursor, buf_end, 
header_table)) == -1) {
-        return PARSE_ERROR;
+      read_bytes = decode_literal_header_field(header, cursor, buf_end, 
dynamic_table);
+      if (read_bytes == HPACK_ERROR_COMPRESSION_ERROR) {
+        if (cont) {
+          // Parsing a part of headers is done
+          return cursor - buf_start;
+        } else {
+          // Parse error
+          return HPACK_ERROR_COMPRESSION_ERROR;
+        }
       }
       cursor += read_bytes;
       break;
     case HPACK_FIELD_TABLESIZE_UPDATE:
-      // XXX not supported yet
-      return PARSE_ERROR;
+      read_bytes = update_dynamic_table_size(cursor, buf_end, dynamic_table);
+      if (read_bytes == HPACK_ERROR_COMPRESSION_ERROR) {
+        if (cont) {
+          // Parsing a part of headers is done
+          return cursor - buf_start;
+        } else {
+          // Parse error
+          return HPACK_ERROR_COMPRESSION_ERROR;
+        }
+      }
+      cursor += read_bytes;
+      continue;
+    }
+
+    int name_len = 0;
+    const char * name = field->name_get(&name_len);
+
+    // ':' started header name is only allowed for psuedo headers
+    if (hdr->fields_count() >= 4 && (name_len <= 0 || name[0] == ':')) {
+      // Decoded header field is invalid
+      return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
+    }
+
+    // when The TE header field is received, it MUST NOT contain any
+    // value other than "trailers".
+    if (name_len == MIME_LEN_TE &&
+        strncmp(name, MIME_FIELD_TE, name_len) == 0) {
+      int value_len = 0;
+      const char * value = field->value_get(&value_len);
+      if (!(value_len == strlen("trailers") &&
+            strncmp(value, "trailers", value_len) == 0)) {
+        return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
+      }
     }
 
     // Store to HdrHeap
     mime_hdr_field_attach(hh->m_fields_impl, field, 1, NULL);
+
+    // Check psuedo headers
+    if (hdr->fields_count() == 4) {
+      if (hdr->field_find(HPACK_VALUE_SCHEME,    HPACK_LEN_SCHEME)    == NULL 
||
+          hdr->field_find(HPACK_VALUE_METHOD,    HPACK_LEN_METHOD)    == NULL 
||
+          hdr->field_find(HPACK_VALUE_PATH,      HPACK_LEN_PATH)      == NULL 
||
+          hdr->field_find(HPACK_VALUE_AUTHORITY, HPACK_LEN_AUTHORITY) == NULL) 
{
+        // Decoded header field is invalid
+        return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
+      }
+    }
   } while (cursor < buf_end);
 
-  return PARSE_DONE;
+  // Psuedo headers is insufficient
+  if (hdr->fields_count() < 4 && !cont) {
+    return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
+  }
+
+  // Parsing all headers is done
+  return cursor - buf_start;
 }
 
 #if TS_HAS_TESTS
@@ -621,7 +940,7 @@ REGRESSION_TEST(HPACK_Encode)(RegressionTest * t, int, int 
*pstatus)
   box = REGRESSION_TEST_PASSED;
 
   uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST];
-  Http2HeaderTable header_table;
+  Http2DynamicTable dynamic_table;
 
   // FIXME Current encoder don't support indexing.
   for (unsigned int i=0; 
i<sizeof(encoded_field_test_case)/sizeof(encoded_field_test_case[0]); i++) {
@@ -641,9 +960,17 @@ REGRESSION_TEST(HPACK_Encode)(RegressionTest * t, int, int 
*pstatus)
     }
 
     memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST);
-    int len = convert_from_1_1_to_2_header(headers, buf, 
BUFSIZE_FOR_REGRESSION_TEST, header_table);
+    convert_headers_from_1_1_to_2(headers);
+    uint64_t buf_len = BUFSIZE_FOR_REGRESSION_TEST;
+    int64_t len = http2_write_psuedo_headers(headers, buf, buf_len, 
dynamic_table);
+    buf_len -= len;
 
-    box.check(len == encoded_field_test_case[i].encoded_field_len, "encoded 
length was %d, expecting %d",
+    MIMEFieldIter field_iter;
+    field_iter.m_block = NULL;
+    bool cont = false;
+    len += http2_write_header_fragment(headers, field_iter, buf, buf_len, 
dynamic_table, cont);
+
+    box.check(len == encoded_field_test_case[i].encoded_field_len, "encoded 
length was %ld, expecting %d",
         len, encoded_field_test_case[i].encoded_field_len);
     box.check(len > 0 && memcmp(buf, encoded_field_test_case[i].encoded_field, 
len) == 0, "encoded value was invalid");
   }
@@ -673,23 +1000,21 @@ REGRESSION_TEST(HPACK_DecodeString)(RegressionTest * t, 
int, int *pstatus)
   TestBox box(t, pstatus);
   box = REGRESSION_TEST_PASSED;
 
+  Arena arena;
   char* actual = NULL;
-  uint32_t actual_len;
+  uint32_t actual_len = 0;
 
   hpack_huffman_init();
 
   for (unsigned int i=0; 
i<sizeof(string_test_case)/sizeof(string_test_case[0]); i++) {
-    int len = decode_string(&actual, actual_len, 
string_test_case[i].encoded_field,
+    int len = decode_string(arena, &actual, actual_len, 
string_test_case[i].encoded_field,
         string_test_case[i].encoded_field + 
string_test_case[i].encoded_field_len);
 
     box.check(len == string_test_case[i].encoded_field_len, "decoded length 
was %d, expecting %d",
         len, string_test_case[i].encoded_field_len);
     box.check(actual_len == string_test_case[i].raw_string_len, "length of 
decoded string was %d, expecting %d",
         actual_len, string_test_case[i].raw_string_len);
-    box.check(len > 0 && memcmp(actual, string_test_case[i].raw_string, 
actual_len) == 0, "decoded string was invalid");
-
-    ats_free(actual);
-    actual = NULL;
+    box.check(memcmp(actual, string_test_case[i].raw_string, actual_len) == 0, 
"decoded string was invalid");
   }
 }
 
@@ -698,7 +1023,7 @@ 
REGRESSION_TEST(HPACK_DecodeIndexedHeaderField)(RegressionTest * t, int, int *ps
   TestBox box(t, pstatus);
   box = REGRESSION_TEST_PASSED;
 
-  Http2HeaderTable header_table;
+  Http2DynamicTable dynamic_table;
 
   for (unsigned int i=0; 
i<sizeof(indexed_test_case)/sizeof(indexed_test_case[0]); i++) {
     ats_scoped_obj<HTTPHdr> headers(new HTTPHdr);
@@ -707,7 +1032,7 @@ 
REGRESSION_TEST(HPACK_DecodeIndexedHeaderField)(RegressionTest * t, int, int *ps
     MIMEFieldWrapper header(field, headers->m_heap, 
headers->m_http->m_fields_impl);
 
     int len = decode_indexed_header_field(header, 
indexed_test_case[i].encoded_field,
-        
indexed_test_case[i].encoded_field+indexed_test_case[i].encoded_field_len, 
header_table);
+        
indexed_test_case[i].encoded_field+indexed_test_case[i].encoded_field_len, 
dynamic_table);
 
     box.check(len == indexed_test_case[i].encoded_field_len, "decoded length 
was %d, expecting %d",
         len, indexed_test_case[i].encoded_field_len);
@@ -729,7 +1054,7 @@ 
REGRESSION_TEST(HPACK_DecodeLiteralHeaderField)(RegressionTest * t, int, int *ps
   TestBox box(t, pstatus);
   box = REGRESSION_TEST_PASSED;
 
-  Http2HeaderTable header_table;
+  Http2DynamicTable dynamic_table;
 
   for (unsigned int i=0; 
i<sizeof(literal_test_case)/sizeof(literal_test_case[0]); i++) {
     ats_scoped_obj<HTTPHdr> headers(new HTTPHdr);
@@ -738,7 +1063,7 @@ 
REGRESSION_TEST(HPACK_DecodeLiteralHeaderField)(RegressionTest * t, int, int *ps
     MIMEFieldWrapper header(field, headers->m_heap, 
headers->m_http->m_fields_impl);
 
     int len = decode_literal_header_field(header, 
literal_test_case[i].encoded_field,
-        
literal_test_case[i].encoded_field+literal_test_case[i].encoded_field_len, 
header_table);
+        
literal_test_case[i].encoded_field+literal_test_case[i].encoded_field_len, 
dynamic_table);
 
     box.check(len == literal_test_case[i].encoded_field_len, "decoded length 
was %d, expecting %d",
         len, literal_test_case[i].encoded_field_len);
@@ -760,13 +1085,13 @@ REGRESSION_TEST(HPACK_Decode)(RegressionTest * t, int, 
int *pstatus)
   TestBox box(t, pstatus);
   box = REGRESSION_TEST_PASSED;
 
-  Http2HeaderTable header_table;
+  Http2DynamicTable dynamic_table;
 
   for (unsigned int i=0; 
i<sizeof(encoded_field_test_case)/sizeof(encoded_field_test_case[0]); i++) {
     ats_scoped_obj<HTTPHdr> headers(new HTTPHdr);
     headers->create(HTTP_TYPE_REQUEST);
 
-    http2_parse_header_fragment(headers, 
make_iovec(encoded_field_test_case[i].encoded_field, 
encoded_field_test_case[i].encoded_field_len), header_table);
+    http2_parse_header_fragment(headers, 
make_iovec(encoded_field_test_case[i].encoded_field, 
encoded_field_test_case[i].encoded_field_len), dynamic_table, false);
 
     for (unsigned int j=0; 
j<sizeof(raw_field_test_case[i])/sizeof(raw_field_test_case[i][0]); j++) {
       const char* expected_name  = raw_field_test_case[i][j].raw_name;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http2/HTTP2.h
----------------------------------------------------------------------
diff --git a/proxy/http2/HTTP2.h b/proxy/http2/HTTP2.h
index 6ae2b2a..c644acd 100644
--- a/proxy/http2/HTTP2.h
+++ b/proxy/http2/HTTP2.h
@@ -41,11 +41,25 @@ extern const char * const HTTP2_CONNECTION_PREFACE;
 const size_t HTTP2_CONNECTION_PREFACE_LEN = 24;
 
 const size_t HTTP2_FRAME_HEADER_LEN = 9;
+const size_t HTTP2_DATA_PADLEN_LEN = 1;
+const size_t HTTP2_HEADERS_PADLEN_LEN = 1;
+const size_t HTTP2_PRIORITY_LEN = 5;
+const size_t HTTP2_RST_STREAM_LEN = 4;
+const size_t HTTP2_PING_LEN = 8;
 const size_t HTTP2_GOAWAY_LEN = 8;
+const size_t HTTP2_WINDOW_UPDATE_LEN = 4;
 const size_t HTTP2_SETTINGS_PARAMETER_LEN = 6;
 
-// 4.2. Frame Size. The absolute maximum size of a frame payload is 2^14-1 
(16,383) octets.
-const size_t HTTP2_MAX_FRAME_PAYLOAD = 16383;
+// SETTINGS initial values
+const uint32_t HTTP2_HEADER_TABLE_SIZE      = 4096;
+const uint32_t HTTP2_ENABLE_PUSH            = 0;     // Server Push is NOT 
supported
+const uint32_t HTTP2_MAX_CONCURRENT_STREAMS = 100;
+const uint32_t HTTP2_INITIAL_WINDOW_SIZE    = 65535;
+const uint32_t HTTP2_MAX_FRAME_SIZE         = 16384;
+const uint32_t HTTP2_MAX_HEADER_LIST_SIZE   = UINT_MAX;
+
+// 6.9.1 The Flow Control Window
+static const Http2WindowSize HTTP2_MAX_WINDOW_SIZE = 0x7FFFFFFF;
 
 enum Http2ErrorCode
 {
@@ -67,6 +81,18 @@ enum Http2ErrorCode
   HTTP2_ERROR_MAX,
 };
 
+// 5.1. Stream States
+enum Http2StreamState
+{
+  HTTP2_STREAM_STATE_IDLE,
+  HTTP2_STREAM_STATE_RESERVED_LOCAL,
+  HTTP2_STREAM_STATE_RESERVED_REMOTE,
+  HTTP2_STREAM_STATE_OPEN,
+  HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL,
+  HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE,
+  HTTP2_STREAM_STATE_CLOSED
+};
+
 enum Http2FrameType
 {
   HTTP2_FRAME_TYPE_DATA = 0,
@@ -79,8 +105,6 @@ enum Http2FrameType
   HTTP2_FRAME_TYPE_GOAWAY = 7,
   HTTP2_FRAME_TYPE_WINDOW_UPDATE = 8,
   HTTP2_FRAME_TYPE_CONTINUATION = 9,
-  HTTP2_FRAME_TYPE_ALTSVC = 10,
-  HTTP2_FRAME_TYPE_BLOCKED = 11,
 
   HTTP2_FRAME_TYPE_MAX,
 };
@@ -89,10 +113,7 @@ enum Http2FrameType
 enum Http2FrameFlagsData
 {
   HTTP2_FLAGS_DATA_END_STREAM = 0x01,
-  HTTP2_FLAGS_DATA_END_SEGMENT = 0x02,
-  HTTP2_FLAGS_DATA_PAD_LOW = 0x08,
-  HTTP2_FLAGS_DATA_PAD_HIGH = 0x10,
-  HTTP2_FLAGS_DATA_COMPRESSESD = 0x20,
+  HTTP2_FLAGS_DATA_PADDED = 0x08,
 
   HTTP2_FLAGS_DATA_MASK = 0x2B,
 };
@@ -101,9 +122,8 @@ enum Http2FrameFlagsData
 enum Http2FrameFlagsHeaders
 {
   HTTP2_FLAGS_HEADERS_END_STREAM = 0x01,
-  HTTP2_FLAGS_HEADERS_END_SEGMENT = 0x02,
-  HTTP2_FLAGS_HEADERS_PAD_LOW = 0x08,
-  HTTP2_FLAGS_HEADERS_PAD_HIGH = 0x10,
+  HTTP2_FLAGS_HEADERS_END_HEADERS = 0x04,
+  HTTP2_FLAGS_HEADERS_PADDED = 0x08,
   HTTP2_FLAGS_HEADERS_PRIORITY = 0x20,
 
   HTTP2_FLAGS_HEADERS_MASK = 0x2B,
@@ -169,18 +189,6 @@ enum Http2FrameFlagsContinuation
   HTTP2_FLAGS_CONTINUATION_MASK = 0x1C,
 };
 
-// 6.11 Altsvc
-enum Http2FrameFlagsAltsvc
-{
-  HTTP2_FLAGS_ALTSVC_MASK = 0x00
-};
-
-// 6.12 Blocked
-enum Http2FrameFlagsBlocked
-{
-  HTTP2_FLAGS_BLOCKED_MASK = 0x00
-};
-
 // 6.5.2 Defined SETTINGS Parameters
 enum Http2SettingsIdentifier
 {
@@ -210,6 +218,20 @@ struct Http2SettingsParameter
   uint32_t  value;
 };
 
+// 6.3 PRIORITY
+struct Http2Priority
+{
+  uint32_t stream_dependency;
+  uint8_t  weight;
+};
+
+// 6.2 HEADERS Format
+struct Http2HeadersParameter
+{
+  uint8_t pad_length;
+  Http2Priority priority;
+};
+
 // 6.8 GOAWAY Format
 struct Http2Goaway
 {
@@ -221,11 +243,11 @@ struct Http2Goaway
   // just complicates memory management.
 };
 
-// 6.9.1 The Flow Control Window
-static const Http2WindowSize HTTP2_MAX_WINDOW_SIZE = 0x7FFFFFFF;
-
-// 6.9.2 Initial Flow Control Window Size
-static const Http2WindowSize HTTP2_INITIAL_WINDOW_SIZE = 0x0000FFFF;
+// 6.4 RST_STREAM Format
+struct Http2RstStream
+{
+  uint32_t error_code;
+};
 
 static inline bool
 http2_is_client_streamid(Http2StreamId streamid) {
@@ -234,7 +256,7 @@ http2_is_client_streamid(Http2StreamId streamid) {
 
 static inline bool
 http2_is_server_streamid(Http2StreamId streamid) {
-  return (streamid & 0x1u) == 0x0u;
+  return (streamid & 0x1u) == 0x0u && streamid != 0x0u;
 }
 
 bool
@@ -244,24 +266,63 @@ bool
 http2_write_frame_header(const Http2FrameHeader&, IOVec);
 
 bool
+http2_write_data(const uint8_t*, size_t, const IOVec&);
+
+bool
+http2_write_headers(const uint8_t*, size_t, const IOVec&);
+
+bool
+http2_write_rst_stream(uint32_t, IOVec);
+
+bool
+http2_write_settings(const Http2SettingsParameter&, IOVec);
+
+bool
+http2_write_ping(const uint8_t *, IOVec);
+
+bool
 http2_write_goaway(const Http2Goaway&, IOVec);
 
 bool
+http2_write_window_update(const uint32_t new_size, const IOVec&);
+
+bool
 http2_frame_header_is_valid(const Http2FrameHeader&);
 
 bool
 http2_settings_parameter_is_valid(const Http2SettingsParameter&);
 
 bool
+http2_parse_headers_parameter(IOVec, Http2HeadersParameter&);
+
+bool
+http2_parse_priority_parameter(IOVec, Http2Priority&);
+
+bool
+http2_parse_rst_stream(IOVec, Http2RstStream&);
+
+bool
 http2_parse_settings_parameter(IOVec, Http2SettingsParameter&);
 
-MIMEParseResult
-http2_parse_header_fragment(HTTPHdr *, IOVec, Http2HeaderTable&);
+bool
+http2_parse_goaway(IOVec, Http2Goaway&);
+
+bool
+http2_parse_window_update(IOVec, uint32_t&);
+
+int64_t
+http2_parse_header_fragment(HTTPHdr *, IOVec, Http2DynamicTable&, bool);
 
 MIMEParseResult
-convert_from_2_to_1_1_header(HTTPHdr * header);
+convert_from_2_to_1_1_header(HTTPHdr *);
+
+void
+convert_headers_from_1_1_to_2(HTTPHdr*);
+
+int64_t
+http2_write_psuedo_headers(HTTPHdr*, uint8_t*, uint64_t, Http2DynamicTable&);
 
 int64_t
-convert_from_1_1_to_2_header(HTTPHdr * in, uint8_t * out, uint64_t out_len, 
Http2HeaderTable& header_table);
+http2_write_header_fragment(HTTPHdr*, MIMEFieldIter&, uint8_t*, uint64_t, 
Http2DynamicTable&, bool&);
 
 #endif /* __HTTP2_H__ */

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http2/Http2ClientSession.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/Http2ClientSession.cc 
b/proxy/http2/Http2ClientSession.cc
index 3929e2e..bf763d5 100644
--- a/proxy/http2/Http2ClientSession.cc
+++ b/proxy/http2/Http2ClientSession.cc
@@ -59,7 +59,7 @@ send_connection_event(Continuation * cont, int event, void * 
edata)
 }
 
 Http2ClientSession::Http2ClientSession()
-  : con_id(0), client_vc(NULL), read_buffer(NULL), sm_reader(NULL), 
write_buffer(NULL), sm_writer(NULL)
+  : con_id(0), client_vc(NULL), read_buffer(NULL), sm_reader(NULL), 
write_buffer(NULL), sm_writer(NULL), upgrade_context(), is_sending_goaway(false)
 {
 }
 
@@ -86,8 +86,14 @@ Http2ClientSession::start()
   
HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_read_connection_preface);
 
   read_vio = this->do_io_read(this, INT64_MAX, this->read_buffer);
-  this->do_io_write(this, INT64_MAX, this->sm_writer);
+  write_vio = this->do_io_write(this, INT64_MAX, this->sm_writer);
 
+  // 3.5 HTTP/2 Connection Preface. Upon establishment of a TCP connection and
+  // determination that HTTP/2 will be used by both peers, each endpoint MUST
+  // send a connection preface as a final confirmation ...
+  //this->write_buffer->write(HTTP2_CONNECTION_PREFACE, 
HTTP2_CONNECTION_PREFACE_LEN);
+
+  this->connection_state.init();
   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_INIT, 
this);
   this->handleEvent(VC_EVENT_READ_READY, read_vio);
 }
@@ -108,6 +114,7 @@ Http2ClientSession::new_connection(NetVConnection * new_vc, 
MIOBuffer * iobuf, I
   DebugHttp2Ssn("session born, netvc %p", this->client_vc);
 
   this->read_buffer = iobuf ? iobuf : 
new_MIOBuffer(HTTP2_HEADER_BUFFER_SIZE_INDEX);
+  this->read_buffer->water_mark = HTTP2_MAX_FRAME_SIZE;
   this->sm_reader = reader ? reader : this->read_buffer->alloc_reader();
 
   this->write_buffer = new_MIOBuffer(HTTP2_HEADER_BUFFER_SIZE_INDEX);
@@ -116,6 +123,43 @@ Http2ClientSession::new_connection(NetVConnection * 
new_vc, MIOBuffer * iobuf, I
   do_api_callout(TS_HTTP_SSN_START_HOOK);
 }
 
+void
+Http2ClientSession::set_upgrade_context(HTTPHdr * h)
+{
+  upgrade_context.req_header = new HTTPHdr();
+  upgrade_context.req_header->copy(h);
+
+  MIMEField *settings = 
upgrade_context.req_header->field_find(MIME_FIELD_HTTP2_SETTINGS, 
MIME_LEN_HTTP2_SETTINGS);
+  int svlen;
+  const char* sv = settings->value_get(&svlen);
+
+  // Maybe size of data decoded by Base64URL is lower than size of encoded 
data.
+  unsigned char out_buf[svlen];
+  if (sv && svlen > 0) {
+    size_t decoded_len;
+    ats_base64_decode(sv, svlen, out_buf, svlen, &decoded_len);
+    for (size_t nbytes=0; nbytes<decoded_len; 
nbytes+=HTTP2_SETTINGS_PARAMETER_LEN) {
+      Http2SettingsParameter param;
+      if (!http2_parse_settings_parameter(make_iovec(out_buf+nbytes, 
HTTP2_SETTINGS_PARAMETER_LEN), param) ||
+          !http2_settings_parameter_is_valid(param)) {
+        // TODO ignore incoming invalid parameters and send suitable SETTINGS 
frame.
+      }
+      upgrade_context.client_settings.set((Http2SettingsIdentifier)param.id, 
param.value);
+    }
+  }
+
+  // Such intermediaries SHOULD also remove other connection-
+  // specific header fields, such as Keep-Alive, Proxy-Connection,
+  // Transfer-Encoding and Upgrade, even if they are not nominated by
+  // Connection.
+  upgrade_context.req_header->field_delete(MIME_FIELD_CONNECTION, 
MIME_LEN_CONNECTION);
+  upgrade_context.req_header->field_delete(MIME_FIELD_KEEP_ALIVE, 
MIME_LEN_KEEP_ALIVE);
+  upgrade_context.req_header->field_delete(MIME_FIELD_PROXY_CONNECTION, 
MIME_LEN_PROXY_CONNECTION);
+  upgrade_context.req_header->field_delete(MIME_FIELD_TRANSFER_ENCODING, 
MIME_LEN_TRANSFER_ENCODING);
+  upgrade_context.req_header->field_delete(MIME_FIELD_UPGRADE, 
MIME_LEN_UPGRADE);
+  upgrade_context.req_header->field_delete(MIME_FIELD_HTTP2_SETTINGS, 
MIME_LEN_HTTP2_SETTINGS);
+}
+
 VIO *
 Http2ClientSession::do_io_read(Continuation * c, int64_t nbytes, MIOBuffer * 
buf)
 {
@@ -169,6 +213,13 @@ Http2ClientSession::main_event_handler(int event, void * 
edata)
   case HTTP2_SESSION_EVENT_XMIT: {
     Http2Frame * frame = (Http2Frame *)edata;
     frame->xmit(this->write_buffer);
+
+    // Mark to wait until sending GOAWAY is complete
+    if (frame->header().type == HTTP2_FRAME_TYPE_GOAWAY) {
+      is_sending_goaway = true;
+    }
+
+    write_reenable();
     return 0;
   }
 
@@ -181,6 +232,17 @@ Http2ClientSession::main_event_handler(int event, void * 
edata)
 
   case VC_EVENT_WRITE_COMPLETE:
   case VC_EVENT_WRITE_READY:
+    // After sending GOAWAY, close the connection
+    if (is_sending_goaway && write_vio->ntodo() <= 0) {
+      this->do_io_close();
+    }
+    return 0;
+
+  case TS_FETCH_EVENT_EXT_HEAD_DONE:
+  case TS_FETCH_EVENT_EXT_BODY_READY:
+  case TS_FETCH_EVENT_EXT_BODY_DONE:
+    // Process responses from origin server
+    send_connection_event(&this->connection_state, event, edata);
     return 0;
 
   default:
@@ -265,12 +327,23 @@ Http2ClientSession::state_start_frame_read(int event, 
void * edata)
     }
 
     // If we know up front that the payload is too long, nuke this connection.
-    if (this->current_hdr.length > HTTP2_MAX_FRAME_PAYLOAD) {
-      // XXX nuke it with HTTP2_ERROR_FRAME_SIZE_ERROR!
+    if (this->current_hdr.length > 
this->connection_state.client_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)) {
+      this->connection_state.send_goaway_frame(this->current_hdr.streamid, 
HTTP2_ERROR_FRAME_SIZE_ERROR);
+      return 0;
     }
 
-    if (!http2_is_client_streamid(this->current_hdr.streamid)) {
-      // XXX nuke it with HTTP2_ERROR_PROTOCOL_ERROR!
+    // Allow only stream id = 0 or streams started by client.
+    if (this->current_hdr.streamid != 0 &&
+        !http2_is_client_streamid(this->current_hdr.streamid)) {
+      this->connection_state.send_goaway_frame(this->current_hdr.streamid, 
HTTP2_ERROR_PROTOCOL_ERROR);
+      return 0;
+    }
+
+    // CONTINUATIONs MUST follow behind HEADERS which doesn't have END_HEADERS
+    if (this->connection_state.get_continued_id() != 0 &&
+        this->current_hdr.type != HTTP2_FRAME_TYPE_CONTINUATION) {
+      this->connection_state.send_goaway_frame(this->current_hdr.streamid, 
HTTP2_ERROR_PROTOCOL_ERROR);
+      return 0;
     }
 
     HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_complete_frame_read);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e4347ef8/proxy/http2/Http2ClientSession.h
----------------------------------------------------------------------
diff --git a/proxy/http2/Http2ClientSession.h b/proxy/http2/Http2ClientSession.h
index 8648b43..e785dbb 100644
--- a/proxy/http2/Http2ClientSession.h
+++ b/proxy/http2/Http2ClientSession.h
@@ -41,6 +41,23 @@
 
 static size_t const HTTP2_HEADER_BUFFER_SIZE_INDEX = 
CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX;
 
+// To support Upgrade: h2c
+struct Http2UpgradeContext
+{
+  ~Http2UpgradeContext() {
+    if (req_header) {
+      req_header->clear();
+      delete req_header;
+    }
+  }
+
+  // Modified request header
+  HTTPHdr * req_header;
+
+  // Decoded HTTP2-Settings Header Field
+  Http2ConnectionSettings client_settings;
+};
+
 class Http2Frame
 {
 public:
@@ -133,6 +150,13 @@ public:
     return this->con_id;
   }
 
+  sockaddr const* get_client_addr() { return client_vc->get_remote_addr(); }
+
+  void write_reenable() { write_vio->reenable(); }
+
+  void set_upgrade_context(HTTPHdr * h);
+  const Http2UpgradeContext& get_upgrade_context() const { return 
upgrade_context; }
+
 private:
 
   Http2ClientSession(Http2ClientSession &); // noncopyable
@@ -153,6 +177,14 @@ private:
   IOBufferReader *      sm_writer;
   Http2FrameHeader      current_hdr;
   Http2ConnectionState  connection_state;
+
+  // For Upgrade: h2c
+  Http2UpgradeContext upgrade_context;
+
+  VIO * write_vio;
+
+  // Mark whether ATS is sending GOAWAY
+  bool is_sending_goaway;
 };
 
 extern ClassAllocator<Http2ClientSession> http2ClientSessionAllocator;

Reply via email to