You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bc...@apache.org on 2015/08/25 22:21:12 UTC

[1/2] trafficserver git commit: TS-3752: Problem with larger headers and HTTP/2

Repository: trafficserver
Updated Branches:
  refs/heads/6.0.x ed8e7a4b3 -> a248e11c0


TS-3752: Problem with larger headers and HTTP/2

(cherry picked from commit 00ce2f1113baa9485262695a66ee67a08fc5d121)


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

Branch: refs/heads/6.0.x
Commit: 8013e761122bd9e86cd2e097cb7c0c224d6fb895
Parents: ed8e7a4
Author: Masaori Koshiba <mk...@yahoo-corp.jp>
Authored: Fri Aug 14 15:37:38 2015 -0700
Committer: Bryan Call <bc...@apache.org>
Committed: Tue Aug 25 13:19:45 2015 -0700

----------------------------------------------------------------------
 proxy/http2/HPACK.cc                |  14 +-
 proxy/http2/HTTP2.cc                |  71 ++++-----
 proxy/http2/HTTP2.h                 |  23 ++-
 proxy/http2/Http2ClientSession.cc   |  24 ++-
 proxy/http2/Http2ConnectionState.cc | 264 ++++++++++++++++---------------
 proxy/http2/Http2ConnectionState.h  |  63 +++++---
 6 files changed, 244 insertions(+), 215 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8013e761/proxy/http2/HPACK.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/HPACK.cc b/proxy/http2/HPACK.cc
index b37eef6..d65a6b1 100644
--- a/proxy/http2/HPACK.cc
+++ b/proxy/http2/HPACK.cc
@@ -43,8 +43,6 @@ const unsigned HPACK_LEN_STATUS = countof(":status") - 1;
 // Section 6.2), plus 32.
 const static unsigned ADDITIONAL_OCTETS = 32;
 
-const static uint32_t HEADER_FIELD_LIMIT_LENGTH = 4096;
-
 typedef enum {
   TS_HPACK_STATIC_TABLE_0 = 0,
   TS_HPACK_STATIC_TABLE_AUTHORITY,
@@ -241,8 +239,10 @@ Http2DynamicTable::add_header_field(const MIMEField *field)
   uint32_t header_size = ADDITIONAL_OCTETS + name_len + value_len;
 
   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.
+    // 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 {
@@ -538,7 +538,7 @@ decode_string(Arena &arena, char **str, uint32_t &str_length, const uint8_t *buf
     return HPACK_ERROR_COMPRESSION_ERROR;
   p += len;
 
-  if (encoded_string_len > HEADER_FIELD_LIMIT_LENGTH || (p + encoded_string_len) > buf_end) {
+  if ((p + encoded_string_len) > buf_end) {
     return HPACK_ERROR_COMPRESSION_ERROR;
   }
 
@@ -603,7 +603,8 @@ decode_literal_header_field(MIMEFieldWrapper &header, const uint8_t *buf_start,
   HpackFieldType ftype = hpack_parse_field_type(*p);
 
   if (ftype == HPACK_FIELD_INDEXED_LITERAL) {
-    // 7.2.1. index extraction based on Literal Header Field with Incremental Indexing
+    // 7.2.1. index extraction based on Literal Header Field with Incremental
+    // Indexing
     len = decode_integer(index, p, buf_end, 6);
     isIncremental = true;
   } else if (ftype == HPACK_FIELD_NEVERINDEX_LITERAL) {
@@ -655,7 +656,6 @@ decode_literal_header_field(MIMEFieldWrapper &header, const uint8_t *buf_start,
   p += len;
   header.value_set(value_str, value_str_len);
 
-
   // Incremental Indexing adds header to header table as new entry
   if (isIncremental) {
     dynamic_table.add_header_field(header.field_get());

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8013e761/proxy/http2/HTTP2.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc
index 3a84a59..d77242c 100644
--- a/proxy/http2/HTTP2.cc
+++ b/proxy/http2/HTTP2.cc
@@ -111,7 +111,8 @@ http2_are_frame_flags_valid(uint8_t ftype, uint8_t fflags)
     HTTP2_FLAGS_WINDOW_UPDATE_MASK, HTTP2_FLAGS_CONTINUATION_MASK,
   };
 
-  // The frame flags are valid for this frame if nothing outside the defined bits is set.
+  // The frame flags are valid for this frame if nothing outside the defined
+  // bits is set.
   return (fflags & ~mask[ftype]) == 0;
 }
 
@@ -325,7 +326,6 @@ http2_parse_headers_parameter(IOVec iov, Http2HeadersParameter &params)
   return true;
 }
 
-
 // 6.3.  PRIORITY
 //
 // 0                   1                   2                   3
@@ -401,7 +401,6 @@ http2_parse_settings_parameter(IOVec iov, Http2SettingsParameter &param)
   return true;
 }
 
-
 // 6.8.  GOAWAY
 //
 // 0                   1                   2                   3
@@ -429,7 +428,6 @@ http2_parse_goaway(IOVec iov, Http2Goaway &goaway)
   return true;
 }
 
-
 // 6.9.  WINDOW_UPDATE
 //
 // 0                   1                   2                   3
@@ -584,8 +582,10 @@ http2_write_header_fragment(HTTPHdr *in, MIMEFieldIter &field_iter, uint8_t *out
   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.
+  // 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;
@@ -629,17 +629,17 @@ http2_write_header_fragment(HTTPHdr *in, MIMEFieldIter &field_iter, uint8_t *out
   return p - out;
 }
 
+/*
+ * Decode Header Blocks to Header List.
+ */
 int64_t
-http2_parse_header_fragment(HTTPHdr *hdr, IOVec iov, Http2DynamicTable &dynamic_table, bool cont)
+http2_decode_header_blocks(HTTPHdr *hdr, const uint8_t *buf_start, const uint8_t *buf_end, Http2DynamicTable &dynamic_table)
 {
-  const uint8_t *buf_start = (uint8_t *)iov.iov_base;
-  const uint8_t *buf_end = buf_start + iov.iov_len;
-
-  uint8_t *cursor = (uint8_t *)iov.iov_base; // place the cursor at the start
+  const uint8_t *cursor = buf_start;
   HdrHeap *heap = hdr->m_heap;
   HTTPHdrImpl *hh = hdr->m_http;
 
-  do {
+  while (cursor < buf_end) {
     int64_t read_bytes = 0;
 
     // decode a header field encoded by HPACK
@@ -651,13 +651,7 @@ http2_parse_header_fragment(HTTPHdr *hdr, IOVec iov, Http2DynamicTable &dynamic_
     case HPACK_FIELD_INDEX:
       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;
-        }
+        return HPACK_ERROR_COMPRESSION_ERROR;
       }
       cursor += read_bytes;
       break;
@@ -666,26 +660,14 @@ http2_parse_header_fragment(HTTPHdr *hdr, IOVec iov, Http2DynamicTable &dynamic_
     case HPACK_FIELD_NEVERINDEX_LITERAL:
       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;
-        }
+        return HPACK_ERROR_COMPRESSION_ERROR;
       }
       cursor += read_bytes;
       break;
     case HPACK_FIELD_TABLESIZE_UPDATE:
       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;
-        }
+        return HPACK_ERROR_COMPRESSION_ERROR;
       }
       cursor += read_bytes;
       continue;
@@ -700,7 +682,8 @@ http2_parse_header_fragment(HTTPHdr *hdr, IOVec iov, Http2DynamicTable &dynamic_
       return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
     }
 
-    // rfc7540,sec8.1.2.2: Any message containing connection-specific header fields MUST be treated as malformed
+    // rfc7540,sec8.1.2.2: Any message containing connection-specific header
+    // fields MUST be treated as malformed
     if (name == MIME_FIELD_CONNECTION) {
       return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
     }
@@ -738,10 +721,10 @@ http2_parse_header_fragment(HTTPHdr *hdr, IOVec iov, Http2DynamicTable &dynamic_
         return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
       }
     }
-  } while (cursor < buf_end);
+  }
 
   // Psuedo headers is insufficient
-  if (hdr->fields_count() < 4 && !cont) {
+  if (hdr->fields_count() < 4) {
     return HPACK_ERROR_HTTP2_PROTOCOL_ERROR;
   }
 
@@ -788,7 +771,6 @@ Http2::init()
                      static_cast<int>(HTTP2_STAT_TOTAL_CLIENT_CONNECTION_COUNT), RecRawStatSyncSum);
 }
 
-
 #if TS_HAS_TESTS
 
 #include "ts/TestBox.h"
@@ -799,10 +781,11 @@ const static int MAX_TEST_FIELD_NUM = 8;
 
 /***********************************************************************************
  *                                                                                 *
- *                   Test cases for regression test                                *
+ *                   Test cases for regression test *
  *                                                                                 *
- * Some test cases are based on examples of specification.                         *
- * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09#appendix-D  *
+ * Some test cases are based on examples of specification. *
+ * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09#appendix-D
+ **
  *                                                                                 *
  ***********************************************************************************/
 
@@ -909,7 +892,7 @@ const static struct {
 
 /***********************************************************************************
  *                                                                                 *
- *                                Regression test codes                            *
+ *                                Regression test codes *
  *                                                                                 *
  ***********************************************************************************/
 
@@ -1153,9 +1136,9 @@ REGRESSION_TEST(HPACK_Decode)(RegressionTest *t, int, int *pstatus)
     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),
-                                dynamic_table, false);
+    http2_decode_header_blocks(headers, encoded_field_test_case[i].encoded_field,
+                               encoded_field_test_case[i].encoded_field + encoded_field_test_case[i].encoded_field_len,
+                               dynamic_table);
 
     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/8013e761/proxy/http2/HTTP2.h
----------------------------------------------------------------------
diff --git a/proxy/http2/HTTP2.h b/proxy/http2/HTTP2.h
index 3183ec0..29f31fe 100644
--- a/proxy/http2/HTTP2.h
+++ b/proxy/http2/HTTP2.h
@@ -34,7 +34,8 @@ class HTTPHdr;
 
 typedef unsigned Http2StreamId;
 
-// 6.9.2 Initial Flow Control Window Size - the flow control window can be come negative
+// 6.9.2 Initial Flow Control Window Size - the flow control window can be come
+// negative
 // so we need to track it with a signed type.
 typedef int32_t Http2WindowSize;
 
@@ -62,7 +63,8 @@ const uint32_t HTTP2_MAX_HEADER_LIST_SIZE = UINT_MAX;
 
 // Statistics
 enum {
-  HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT,  // Current # of active HTTP2 sessions.
+  HTTP2_STAT_CURRENT_CLIENT_SESSION_COUNT,  // Current # of active HTTP2
+                                            // sessions.
   HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT,   // Current # of active HTTP2 streams.
   HTTP2_STAT_TOTAL_TRANSACTIONS_TIME,       // Total stream time and streams
   HTTP2_STAT_TOTAL_CLIENT_CONNECTION_COUNT, // Total connections running http2
@@ -247,6 +249,8 @@ struct Http2Priority {
 
 // 6.2 HEADERS Format
 struct Http2HeadersParameter {
+  Http2HeadersParameter() : pad_length(0) {}
+
   uint8_t pad_length;
   Http2Priority priority;
 };
@@ -258,8 +262,10 @@ struct Http2Goaway {
   Http2StreamId last_streamid;
   uint32_t error_code;
 
-  // NOTE: we don't (de)serialize the variable length debug data at this layer because there's
-  // really nothing we can do with it without some out of band agreement. Trying to deal with it
+  // NOTE: we don't (de)serialize the variable length debug data at this layer
+  // because there's
+  // really nothing we can do with it without some out of band agreement. Trying
+  // to deal with it
   // just complicates memory management.
 };
 
@@ -314,7 +320,7 @@ bool http2_parse_goaway(IOVec, Http2Goaway &);
 
 bool http2_parse_window_update(IOVec, uint32_t &);
 
-int64_t http2_parse_header_fragment(HTTPHdr *, IOVec, Http2DynamicTable &, bool);
+int64_t http2_decode_header_blocks(HTTPHdr *, const uint8_t *, const uint8_t *, Http2DynamicTable &);
 
 MIMEParseResult convert_from_2_to_1_1_header(HTTPHdr *);
 
@@ -322,9 +328,10 @@ int64_t http2_write_psuedo_headers(HTTPHdr *, uint8_t *, uint64_t, Http2DynamicT
 
 int64_t http2_write_header_fragment(HTTPHdr *, MIMEFieldIter &, uint8_t *, uint64_t, Http2DynamicTable &, bool &);
 
-
-// Not sure where else to put this, but figure this is as good of a start as anything else.
-// Right now, only the static init() is available, which sets up some basic librecords
+// Not sure where else to put this, but figure this is as good of a start as
+// anything else.
+// Right now, only the static init() is available, which sets up some basic
+// librecords
 // dependencies.
 class Http2
 {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8013e761/proxy/http2/Http2ClientSession.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/Http2ClientSession.cc b/proxy/http2/Http2ClientSession.cc
index 0ea6fca..535cfe8 100644
--- a/proxy/http2/Http2ClientSession.cc
+++ b/proxy/http2/Http2ClientSession.cc
@@ -42,7 +42,8 @@
 
 ClassAllocator<Http2ClientSession> http2ClientSessionAllocator("http2ClientSessionAllocator");
 
-// memcpy the requested bytes from the IOBufferReader, returning how many were actually copied.
+// memcpy the requested bytes from the IOBufferReader, returning how many were
+// actually copied.
 static inline unsigned
 copy_from_buffer_reader(void *dst, IOBufferReader *reader, unsigned nbytes)
 {
@@ -95,7 +96,8 @@ Http2ClientSession::start()
   // 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->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);
@@ -150,7 +152,8 @@ Http2ClientSession::set_upgrade_context(HTTPHdr *h)
       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.
+        // TODO ignore incoming invalid parameters and send suitable SETTINGS
+        // frame.
       }
       upgrade_context.client_settings.set((Http2SettingsIdentifier)param.id, param.value);
     }
@@ -186,7 +189,8 @@ Http2ClientSession::do_io_shutdown(ShutdownHowTo_t howto)
   this->client_vc->do_io_shutdown(howto);
 }
 
-// XXX Currently, we don't have a half-closed state, but we will need to implement that. After we send a GOAWAY, there
+// XXX Currently, we don't have a half-closed state, but we will need to
+// implement that. After we send a GOAWAY, there
 // are scenarios where we would like to complete the outstanding streams.
 
 void
@@ -294,8 +298,10 @@ Http2ClientSession::state_read_connection_preface(int event, void *edata)
     }
   }
 
-  // XXX We don't have enough data to check the connection preface. We should reset the accept inactivity
-  // timeout. We should have a maximum timeout to get the session started though.
+  // XXX We don't have enough data to check the connection preface. We should
+  // reset the accept inactivity
+  // timeout. We should have a maximum timeout to get the session started
+  // though.
 
   vio->reenable();
   return 0;
@@ -351,9 +357,11 @@ Http2ClientSession::state_start_frame_read(int event, void *edata)
     }
 
     // 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) {
+    Http2StreamId continued_stream_id = this->connection_state.get_continued_stream_id();
+
+    if (continued_stream_id != 0 && this->current_hdr.type != HTTP2_FRAME_TYPE_CONTINUATION) {
       SCOPED_MUTEX_LOCK(lock, this->connection_state.mutex, this_ethread());
-      if (!this->connection_state.is_state_closed()) {
+      if (!this->connection_state.is_state_closed() || continued_stream_id != this->current_hdr.streamid) {
         this->connection_state.send_goaway_frame(this->current_hdr.streamid, HTTP2_ERROR_PROTOCOL_ERROR);
       }
       return 0;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8013e761/proxy/http2/Http2ConnectionState.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/Http2ConnectionState.cc b/proxy/http2/Http2ConnectionState.cc
index 311e508..7569ff3 100644
--- a/proxy/http2/Http2ConnectionState.cc
+++ b/proxy/http2/Http2ConnectionState.cc
@@ -71,7 +71,8 @@ rcv_data_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const Http2
 
   DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received DATA frame.", cs.connection_id());
 
-  // If a DATA frame is received whose stream identifier field is 0x0, the recipient MUST
+  // If a DATA frame is received whose stream identifier field is 0x0, the
+  // recipient MUST
   // respond with a connection error of type PROTOCOL_ERROR.
   if (!http2_is_client_streamid(id)) {
     return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
@@ -86,7 +87,8 @@ rcv_data_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const Http2
     }
   }
 
-  // If a DATA frame is received whose stream is not in "open" or "half closed (local)" state,
+  // If a DATA frame is received whose stream is not in "open" or "half closed
+  // (local)" state,
   // the recipient MUST respond with a stream error of type STREAM_CLOSED.
   if (stream->get_state() != HTTP2_STREAM_STATE_OPEN && stream->get_state() != HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) {
     return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_STREAM_CLOSED);
@@ -158,28 +160,33 @@ rcv_data_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const Http2
   return Http2Error(HTTP2_ERROR_CLASS_NONE);
 }
 
+/*
+ * [RFC 7540] 6.2 HEADERS Frame
+ *
+ * NOTE: HEADERS Frame and CONTINUATION Frame
+ *   1. A HEADERS frame with the END_STREAM flag set can be followed by
+ *CONTINUATION frames on the same stream.
+ *   2. A HEADERS frame without the END_HEADERS flag set MUST be followed by a
+ *CONTINUATION frame
+ */
 static Http2Error
 rcv_headers_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const Http2Frame &frame)
 {
-  char buf[BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_HEADERS])];
-  unsigned nbytes = 0;
-  Http2StreamId id = frame.header().streamid;
-  Http2HeadersParameter params;
+  const Http2StreamId stream_id = frame.header().streamid;
   const uint32_t payload_length = frame.header().length;
 
   DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received HEADERS frame.", cs.connection_id());
 
-  if (!http2_is_client_streamid(id)) {
+  if (!http2_is_client_streamid(stream_id)) {
     return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
   }
 
-  if (id <= cstate.get_latest_stream_id()) {
+  if (stream_id <= cstate.get_latest_stream_id()) {
     return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_STREAM_CLOSED);
   }
 
   // Create new stream
-  Http2Stream *stream = cstate.create_stream(id);
-
+  Http2Stream *stream = cstate.create_stream(stream_id);
   if (!stream) {
     return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
   }
@@ -192,83 +199,71 @@ rcv_headers_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const Ht
     return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR);
   }
 
-  // A receiver MUST treat the receipt of any other type of frame or
-  // a frame on a different stream as a connection error of type PROTOCOL_ERROR.
-  if (cstate.get_continued_id() != 0) {
-    return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
-  }
+  Http2HeadersParameter params;
+  uint32_t header_block_fragment_offset = 0;
+  uint32_t header_block_fragment_length = payload_length;
 
-  // Change state. If changing is invalid, raise PROTOCOL_ERROR
-  if (!stream->change_state(frame.header().type, frame.header().flags)) {
-    return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
+  if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_STREAM) {
+    stream->end_stream = true;
   }
 
-  // Check whether padding exists or not.
+  // NOTE: Strip padding if exists
   if (frame.header().flags & HTTP2_FLAGS_HEADERS_PADDED) {
-    frame.reader()->memcpy(buf, HTTP2_HEADERS_PADLEN_LEN, nbytes);
-    nbytes += HTTP2_HEADERS_PADLEN_LEN;
+    uint8_t buf[HTTP2_HEADERS_PADLEN_LEN] = {0};
+    frame.reader()->memcpy(buf, HTTP2_HEADERS_PADLEN_LEN);
+
     if (!http2_parse_headers_parameter(make_iovec(buf, HTTP2_HEADERS_PADLEN_LEN), params)) {
       return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
 
     if (params.pad_length > payload_length) {
-      // If the length of the padding is the length of the
-      // frame payload or greater, the recipient MUST treat this as a
-      // connection error of type PROTOCOL_ERROR.
       return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
-  } else {
-    params.pad_length = 0;
+
+    header_block_fragment_offset += HTTP2_HEADERS_PADLEN_LEN;
+    header_block_fragment_length -= (HTTP2_HEADERS_PADLEN_LEN + params.pad_length);
   }
 
-  // Check whether parameters of priority exist or not.
-  // TODO Currently priority is NOT supported.
+  // NOTE: Parse priority parameters if exists
+  // TODO: Currently priority is NOT supported. TS-3535 will fix this.
   if (frame.header().flags & HTTP2_FLAGS_HEADERS_PRIORITY) {
-    frame.reader()->memcpy(buf, HTTP2_PRIORITY_LEN, nbytes);
-    nbytes += HTTP2_PRIORITY_LEN;
+    uint8_t buf[HTTP2_PRIORITY_LEN] = {0};
+
+    frame.reader()->memcpy(buf, HTTP2_PRIORITY_LEN, header_block_fragment_offset);
     if (!http2_parse_priority_parameter(make_iovec(buf, HTTP2_PRIORITY_LEN), params.priority)) {
       return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
-  }
 
-  // Parse request headers encoded by HPACK
-  const uint32_t unpadded_length = payload_length - params.pad_length;
-  uint32_t remaining_bytes = 0;
-  for (;;) {
-    size_t read_len = sizeof(buf) - remaining_bytes;
-    if (nbytes + read_len > unpadded_length)
-      read_len -= nbytes + read_len - unpadded_length;
-    unsigned read_bytes = read_rcv_buffer(buf + remaining_bytes, read_len, nbytes, frame);
-    IOVec header_block_fragment = make_iovec(buf, read_bytes + remaining_bytes);
+    header_block_fragment_offset += HTTP2_PRIORITY_LEN;
+    header_block_fragment_length -= HTTP2_PRIORITY_LEN;
+  }
 
-    bool cont = nbytes < payload_length || !(frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS);
-    int64_t decoded_bytes = stream->decode_request_header(header_block_fragment, *cstate.local_dynamic_table, cont);
+  stream->header_blocks = static_cast<uint8_t *>(ats_malloc(header_block_fragment_length));
+  frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, header_block_fragment_offset);
 
-    // 4.3. A receiver MUST terminate the connection with a
-    // connection error of type COMPRESSION_ERROR if it does
-    // not decompress a header block.
-    if (decoded_bytes == 0 || decoded_bytes == HPACK_ERROR_COMPRESSION_ERROR) {
-      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_COMPRESSION_ERROR);
-    }
+  stream->header_blocks_length = header_block_fragment_length;
 
-    if (decoded_bytes == HPACK_ERROR_HTTP2_PROTOCOL_ERROR) {
-      return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR);
+  if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
+    // NOTE: If there are END_HEADERS flag, decode stored Header Blocks.
+    if (!stream->change_state(HTTP2_FRAME_TYPE_HEADERS, frame.header().flags)) {
+      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
 
-    remaining_bytes = header_block_fragment.iov_len - decoded_bytes;
-    memmove(buf, buf + header_block_fragment.iov_len - remaining_bytes, remaining_bytes);
+    const int64_t decoded_bytes = stream->decode_header_blocks(*cstate.local_dynamic_table);
 
-    if (nbytes >= payload_length - params.pad_length) {
-      if (!(frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS)) {
-        cstate.set_continued_headers(buf, remaining_bytes, id);
-      }
-      break;
+    if (decoded_bytes == 0 || decoded_bytes == HPACK_ERROR_COMPRESSION_ERROR) {
+      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_COMPRESSION_ERROR);
+    } else if (decoded_bytes == HPACK_ERROR_HTTP2_PROTOCOL_ERROR) {
+      return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR);
     }
-  }
 
-  // backposting
-  if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
     stream->init_fetcher(cstate);
+  } else {
+    // NOTE: Expect CONTINUATION Frame. Do NOT change state of stream or decode
+    // Header Blocks.
+    DebugSsn(&cs, "http2_cs", "[%" PRId64 "] No END_HEADERS flag, expecting CONTINUATION frame.", cs.connection_id());
+
+    cstate.set_continued_stream_id(stream_id);
   }
 
   return Http2Error(HTTP2_ERROR_CLASS_NONE);
@@ -415,7 +410,8 @@ rcv_settings_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const H
     cstate.client_settings.set((Http2SettingsIdentifier)param.id, param.value);
   }
 
-  // 6.5 Once all values have been applied, the recipient MUST immediately emit a
+  // 6.5 Once all values have been applied, the recipient MUST immediately emit
+  // a
   // SETTINGS frame with the ACK flag set.
   Http2Frame ackFrame(HTTP2_FRAME_TYPE_SETTINGS, 0, HTTP2_FLAGS_SETTINGS_ACK);
   cstate.ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &ackFrame);
@@ -441,8 +437,10 @@ rcv_ping_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const Http2
 
   DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received PING frame.", cs.connection_id());
 
-  //  If a PING frame is received with a stream identifier field value other than
-  //  0x0, the recipient MUST respond with a connection error of type PROTOCOL_ERROR.
+  //  If a PING frame is received with a stream identifier field value other
+  //  than
+  //  0x0, the recipient MUST respond with a connection error of type
+  //  PROTOCOL_ERROR.
   if (frame.header().streamid != 0x0) {
     return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
   }
@@ -518,8 +516,10 @@ rcv_window_update_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, co
     frame.reader()->memcpy(buf, sizeof(buf), 0);
     http2_parse_window_update(make_iovec(buf, sizeof(buf)), size);
 
-    // A receiver MUST treat the receipt of a WINDOW_UPDATE frame with a connection
-    // flow control window increment of 0 as a connection error of type PROTOCOL_ERROR;
+    // A receiver MUST treat the receipt of a WINDOW_UPDATE frame with a
+    // connection
+    // flow control window increment of 0 as a connection error of type
+    // PROTOCOL_ERROR;
     if (size == 0) {
       return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
@@ -553,7 +553,8 @@ rcv_window_update_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, co
     http2_parse_window_update(make_iovec(buf, sizeof(buf)), size);
 
     // A receiver MUST treat the receipt of a WINDOW_UPDATE frame with an
-    // flow control window increment of 0 as a stream error of type PROTOCOL_ERROR;
+    // flow control window increment of 0 as a stream error of type
+    // PROTOCOL_ERROR;
     if (size == 0) {
       return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR);
     }
@@ -579,19 +580,30 @@ rcv_window_update_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, co
   return Http2Error(HTTP2_ERROR_CLASS_NONE);
 }
 
+/*
+ * [RFC 7540] 6.10 CONTINUATION
+ *
+ * NOTE: Logically, the CONTINUATION frames are part of the HEADERS frame. ([RFC
+ *7540] 6.2 HEADERS)
+ *
+ */
 static Http2Error
 rcv_continuation_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, const Http2Frame &frame)
 {
-  char buf[BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_CONTINUATION])];
-  unsigned nbytes = 0;
   const Http2StreamId stream_id = frame.header().streamid;
+  const uint32_t payload_length = frame.header().length;
 
   DebugSsn(&cs, "http2_cs", "[%" PRId64 "] Received CONTINUATION frame.", cs.connection_id());
 
+  if (!http2_is_client_streamid(stream_id)) {
+    return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
+  }
+
   // Find opened stream
   // CONTINUATION frames MUST be associated with a stream.  If a
   // CONTINUATION frame is received whose stream identifier field is 0x0,
-  // the recipient MUST respond with a connection error (Section 5.4.1) of
+  // the recipient MUST respond with a connection error ([RFC 7540] Section
+  // 5.4.1) of
   // type PROTOCOL_ERROR.
   Http2Stream *stream = cstate.find_stream(stream_id);
   if (stream == NULL) {
@@ -600,70 +612,54 @@ rcv_continuation_frame(Http2ClientSession &cs, Http2ConnectionState &cstate, con
     } else {
       return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
+  } else {
+    switch (stream->get_state()) {
+    case HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE:
+      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_STREAM_CLOSED);
+    case HTTP2_STREAM_STATE_IDLE:
+      break;
+    default:
+      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
+    }
   }
 
-  // A CONTINUATION frame MUST be preceded by a HEADERS, PUSH_PROMISE or
-  // CONTINUATION frame without the END_HEADERS flag set. A recipient
-  // that observes violation of this rule MUST respond with a connection
-  // error (Section 5.4.1) of type PROTOCOL_ERROR.
-  if (stream->get_state() != HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE && stream->get_state() != HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) {
+  // keep track of how many bytes we get in the frame
+  stream->request_header_length += payload_length;
+  if (stream->request_header_length > Http2::max_request_header_size) {
+    Error("HTTP/2 payload for headers exceeded: %u", stream->request_header_length);
     return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
   }
 
-  // A receiver MUST treat the receipt of any other type of frame or
-  // a frame on a different stream as a connection error of type PROTOCOL_ERROR.
-  if (stream->get_id() != cstate.get_continued_id()) {
+  if (!stream->header_blocks) {
     return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
   }
 
-  const IOVec remaining_data = cstate.get_continued_headers();
-  uint32_t remaining_bytes = remaining_data.iov_len;
-  if (remaining_bytes && remaining_data.iov_base) {
-    memcpy(buf, remaining_data.iov_base, remaining_data.iov_len);
-  }
+  uint32_t header_blocks_offset = stream->header_blocks_length;
+  stream->header_blocks_length += payload_length;
 
-  // Parse request headers encoded by HPACK
-  for (;;) {
-    unsigned read_bytes = read_rcv_buffer(buf + remaining_bytes, sizeof(buf) - remaining_bytes, nbytes, frame);
-    IOVec header_block_fragment = make_iovec(buf, read_bytes + remaining_bytes);
-
-    // keep track of how many bytes we get in the frame
-    stream->request_header_length += frame.header().length;
-    if (stream->request_header_length > Http2::max_request_header_size) {
-      Error("HTTP/2 payload for headers exceeded: %u", stream->request_header_length);
-      // XXX Should we respond with 431 (Request Header Fields Too Large) ?
-      return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR);
-    }
+  stream->header_blocks = static_cast<uint8_t *>(ats_realloc(stream->header_blocks, stream->header_blocks_length));
+  frame.reader()->memcpy(stream->header_blocks + header_blocks_offset, payload_length);
 
-    bool cont = nbytes < frame.header().length || !(frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS);
-    int64_t decoded_bytes = stream->decode_request_header(header_block_fragment, *cstate.local_dynamic_table, cont);
-
-    // A receiver MUST terminate the connection with a
-    // connection error of type COMPRESSION_ERROR if it does
-    // not decompress a header block.
-    if (decoded_bytes == 0 || decoded_bytes == HPACK_ERROR_COMPRESSION_ERROR) {
-      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_COMPRESSION_ERROR);
-    }
+  if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
+    // NOTE: If there are END_HEADERS flag, decode stored Header Blocks.
+    cstate.clear_continued_stream_id();
 
-    if (decoded_bytes == HPACK_ERROR_HTTP2_PROTOCOL_ERROR) {
+    if (!stream->change_state(HTTP2_FRAME_TYPE_CONTINUATION, frame.header().flags)) {
       return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
 
-    remaining_bytes = header_block_fragment.iov_len - decoded_bytes;
-    memmove(buf, buf + header_block_fragment.iov_len - remaining_bytes, remaining_bytes);
+    const int64_t decoded_bytes = stream->decode_header_blocks(*cstate.local_dynamic_table);
 
-    if (nbytes >= frame.header().length) {
-      if (!(frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS)) {
-        cstate.set_continued_headers(buf, remaining_bytes, stream_id);
-      }
-      break;
+    if (decoded_bytes == 0 || decoded_bytes == HPACK_ERROR_COMPRESSION_ERROR) {
+      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_COMPRESSION_ERROR);
+    } else if (decoded_bytes == HPACK_ERROR_HTTP2_PROTOCOL_ERROR) {
+      return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_PROTOCOL_ERROR);
     }
-  }
 
-  // backposting
-  if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
-    cstate.finish_continued_headers();
     stream->init_fetcher(cstate);
+  } else {
+    // NOTE: Expect another CONTINUATION Frame. Do nothing.
+    DebugSsn(&cs, "http2_cs", "[%" PRId64 "] No END_HEADERS flag, expecting CONTINUATION frame.", cs.connection_id());
   }
 
   return Http2Error(HTTP2_ERROR_CLASS_NONE);
@@ -693,10 +689,12 @@ Http2ConnectionState::main_event_handler(int event, void *edata)
 
     // 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 ... The server connection
+    // send a connection preface as a final confirmation ... The server
+    // connection
     // preface consists of a potentially empty SETTINGS frame.
 
-    // Load the server settings from the records.config / RecordsConfig.cc settings.
+    // Load the server settings from the records.config / RecordsConfig.cc
+    // settings.
     Http2ConnectionSettings configured_settings;
     configured_settings.settings_from_configs();
     send_settings_frame(configured_settings);
@@ -723,7 +721,8 @@ Http2ConnectionState::main_event_handler(int event, void *edata)
     Http2Error error;
 
     // 5.5 Extending HTTP/2
-    //   Implementations MUST discard frames that have unknown or unsupported types.
+    //   Implementations MUST discard frames that have unknown or unsupported
+    //   types.
     if (frame->header().type >= HTTP2_FRAME_TYPE_MAX) {
       DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Discard a frame which has unknown type, type=%x",
                this->ua_session->connection_id(), frame->header().type);
@@ -740,9 +739,12 @@ Http2ConnectionState::main_event_handler(int event, void *edata)
       if (error.cls == HTTP2_ERROR_CLASS_CONNECTION) {
         this->send_goaway_frame(last_streamid, error.code);
         cleanup_streams();
-        // XXX We need to think a bit harder about how to coordinate the client session and the
-        // protocol connection. At this point, the protocol is shutting down, but there's no way
-        // to tell that to the client session. Perhaps this could be solved by implementing the
+        // XXX We need to think a bit harder about how to coordinate the client
+        // session and the
+        // protocol connection. At this point, the protocol is shutting down,
+        // but there's no way
+        // to tell that to the client session. Perhaps this could be solved by
+        // implementing the
         // half-closed state ...
         SET_HANDLER(&Http2ConnectionState::state_closed);
       } else if (error.cls == HTTP2_ERROR_CLASS_STREAM) {
@@ -956,8 +958,10 @@ Http2ConnectionState::send_data_frame(FetchSM *fetch_sm)
 
     if (flags & HTTP2_FLAGS_DATA_END_STREAM) {
       // Delete a stream immediately
-      // TODO its should not be deleted for a several time to handling RST_STREAM and WINDOW_UPDATE.
-      // See 'closed' state written at https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.1
+      // TODO its should not be deleted for a several time to handling
+      // RST_STREAM and WINDOW_UPDATE.
+      // See 'closed' state written at
+      // https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.1
       this->delete_stream(stream);
       break;
     }
@@ -979,7 +983,8 @@ Http2ConnectionState::send_headers_frame(FetchSM *fetch_sm)
   payload_length += http2_write_psuedo_headers(resp_header, payload_buffer, buf_len, *(this->remote_dynamic_table));
 
   // If response body is empty, set END_STREAM flag to HEADERS frame
-  // Must check to ensure content-length is there.  Otherwise the value defaults to 0
+  // Must check to ensure content-length is there.  Otherwise the value defaults
+  // to 0
   if (resp_header->presence(MIME_PRESENCE_CONTENT_LENGTH) && resp_header->get_content_length() == 0) {
     flags |= HTTP2_FLAGS_HEADERS_END_STREAM;
   }
@@ -1156,7 +1161,7 @@ Http2Stream::set_body_to_fetcher(const void *data, size_t len)
 }
 
 /*
- * 5.1.  Stream States
+ * 5.1. Stream States
  *
  *                       +--------+
  *                 PP    |        |    PP
@@ -1190,8 +1195,13 @@ Http2Stream::change_state(uint8_t type, uint8_t flags)
   switch (_state) {
   case HTTP2_STREAM_STATE_IDLE:
     if (type == HTTP2_FRAME_TYPE_HEADERS) {
-      if (flags & HTTP2_FLAGS_HEADERS_END_STREAM) {
-        // Skip OPEN _state
+      if (end_stream && flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
+        _state = HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
+      } else {
+        _state = HTTP2_STREAM_STATE_OPEN;
+      }
+    } else if (type == HTTP2_FRAME_TYPE_CONTINUATION) {
+      if (end_stream && flags & HTTP2_FLAGS_CONTINUATION_END_HEADERS) {
         _state = HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
       } else {
         _state = HTTP2_STREAM_STATE_OPEN;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/8013e761/proxy/http2/Http2ConnectionState.h
----------------------------------------------------------------------
diff --git a/proxy/http2/Http2ConnectionState.h b/proxy/http2/Http2ConnectionState.h
index 14d7f01..41297f0 100644
--- a/proxy/http2/Http2ConnectionState.h
+++ b/proxy/http2/Http2ConnectionState.h
@@ -35,7 +35,8 @@ class Http2ConnectionSettings
 public:
   Http2ConnectionSettings()
   {
-    // 6.5.2.  Defined SETTINGS Parameters. These should generally not be modified,
+    // 6.5.2.  Defined SETTINGS Parameters. These should generally not be
+    // modified,
     // only if the protocol changes should these change.
     settings[indexof(HTTP2_SETTINGS_ENABLE_PUSH)] = 0; // Disabled for now
 
@@ -99,14 +100,14 @@ class Http2Stream
 {
 public:
   Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size)
-    : client_rwnd(initial_rwnd), server_rwnd(initial_rwnd), _id(sid), _state(HTTP2_STREAM_STATE_IDLE), _fetch_sm(NULL),
-      body_done(false), data_length(0)
+    : client_rwnd(initial_rwnd), server_rwnd(initial_rwnd), header_blocks(NULL), header_blocks_length(0), request_header_length(0),
+      end_stream(false), _id(sid), _state(HTTP2_STREAM_STATE_IDLE), _fetch_sm(NULL), body_done(false), data_length(0)
   {
     _thread = this_ethread();
     HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread);
     _start_time = ink_hrtime();
+    // FIXME: Are you sure? every "stream" needs _req_header?
     _req_header.create(HTTP_TYPE_REQUEST);
-    request_header_length = 0;
   }
 
   ~Http2Stream()
@@ -120,6 +121,9 @@ public:
       _fetch_sm->ext_destroy();
       _fetch_sm = NULL;
     }
+    if (header_blocks) {
+      ats_free(header_blocks);
+    }
   }
 
   // Operate FetchSM
@@ -154,9 +158,10 @@ public:
   bool change_state(uint8_t type, uint8_t flags);
 
   int64_t
-  decode_request_header(const IOVec &iov, Http2DynamicTable &dynamic_table, bool cont)
+  decode_header_blocks(Http2DynamicTable &dynamic_table)
   {
-    return http2_parse_header_fragment(&_req_header, iov, dynamic_table, cont);
+    return http2_decode_header_blocks(&_req_header, (const uint8_t *)header_blocks,
+                                      (const uint8_t *)header_blocks + header_blocks_length, dynamic_table);
   }
 
   // Check entire DATA payload length if content-length: header is exist
@@ -177,7 +182,12 @@ public:
 
   LINK(Http2Stream, link);
 
-  uint32_t request_header_length;
+  uint8_t *header_blocks;
+  uint32_t header_blocks_length;  // total length of header blocks (not include
+                                  // Padding or other fields)
+  uint32_t request_header_length; // total length of payload (include Padding
+                                  // and other fields)
+  bool end_stream;
 
 private:
   ink_hrtime _start_time;
@@ -191,10 +201,10 @@ private:
   uint64_t data_length;
 };
 
-
 // Http2ConnectionState
 //
-// Capture the semantics of a HTTP/2 connection. The client session captures the frame layer, and the
+// Capture the semantics of a HTTP/2 connection. The client session captures the
+// frame layer, and the
 // connection state captures the connection-wide state.
 
 class Http2ConnectionState : public Continuation
@@ -202,7 +212,7 @@ class Http2ConnectionState : public Continuation
 public:
   Http2ConnectionState()
     : Continuation(NULL), ua_session(NULL), client_rwnd(Http2::initial_window_size), server_rwnd(Http2::initial_window_size),
-      stream_list(), latest_streamid(0), client_streams_count(0), continued_id(0)
+      stream_list(), latest_streamid(0), client_streams_count(0), continued_stream_id(0)
   {
     SET_HANDLER(&Http2ConnectionState::main_event_handler);
   }
@@ -257,17 +267,20 @@ public:
 
   // Continuated header decoding
   Http2StreamId
-  get_continued_id() const
+  get_continued_stream_id() const
+  {
+    return continued_stream_id;
+  }
+  void
+  set_continued_stream_id(Http2StreamId stream_id)
   {
-    return continued_id;
+    continued_stream_id = stream_id;
   }
-  const IOVec &
-  get_continued_headers() const
+  void
+  clear_continued_stream_id()
   {
-    return continued_buffer;
+    continued_stream_id = 0;
   }
-  void set_continued_headers(const char *buf, uint32_t len, Http2StreamId id);
-  void finish_continued_headers();
 
   // Connection level window size
   ssize_t client_rwnd, server_rwnd;
@@ -292,17 +305,25 @@ private:
   Http2ConnectionState &operator=(const Http2ConnectionState &); // noncopyable
 
   // NOTE: 'stream_list' has only active streams.
-  //   If given Stream Identifier is not found in stream_list and it is less than or equal to latest_streamid, the state of Stream
+  //   If given Stream Identifier is not found in stream_list and it is less
+  //   than or equal to latest_streamid, the state of Stream
   //   is CLOSED.
-  //   If given Stream Identifier is not found in stream_list and it is greater than latest_streamid, the state of Stream is IDLE.
+  //   If given Stream Identifier is not found in stream_list and it is greater
+  //   than latest_streamid, the state of Stream is IDLE.
   DLL<Http2Stream> stream_list;
   Http2StreamId latest_streamid;
 
   // Counter for current acive streams which is started by client
   uint32_t client_streams_count;
 
-  // The buffer used for storing incomplete fragments of a header field which consists of multiple frames.
-  Http2StreamId continued_id;
+  // NOTE: Id of stream which MUST receive CONTINUATION frame.
+  //   - [RFC 7540] 6.2 HEADERS
+  //     "A HEADERS frame without the END_HEADERS flag set MUST be followed by a
+  //     CONTINUATION frame for the same stream."
+  //   - [RFC 7540] 6.10 CONTINUATION
+  //     "If the END_HEADERS bit is not set, this frame MUST be followed by
+  //     another CONTINUATION frame."
+  Http2StreamId continued_stream_id;
   IOVec continued_buffer;
 };
 


[2/2] trafficserver git commit: TS-3752: Problem with larger headers and HTTP/2 Added back in the debug messages

Posted by bc...@apache.org.
TS-3752: Problem with larger headers and HTTP/2
Added back in the debug messages

(cherry picked from commit a5818b350d48ea14598d1c876124b393a936f3c9)


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

Branch: refs/heads/6.0.x
Commit: a248e11c054503dc8ce6d35e6eac7cca7d3e95f2
Parents: 8013e76
Author: Bryan Call <bc...@apache.org>
Authored: Thu Aug 13 14:05:22 2015 -0700
Committer: Bryan Call <bc...@apache.org>
Committed: Tue Aug 25 13:20:46 2015 -0700

----------------------------------------------------------------------
 proxy/http2/Http2ConnectionState.cc | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/a248e11c/proxy/http2/Http2ConnectionState.cc
----------------------------------------------------------------------
diff --git a/proxy/http2/Http2ConnectionState.cc b/proxy/http2/Http2ConnectionState.cc
index 7569ff3..4e34a8f 100644
--- a/proxy/http2/Http2ConnectionState.cc
+++ b/proxy/http2/Http2ConnectionState.cc
@@ -861,6 +861,8 @@ Http2ConnectionState::cleanup_streams()
 void
 Http2ConnectionState::set_continued_headers(const char *buf, uint32_t len, Http2StreamId id)
 {
+  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send CONTINUATION frame.", this->ua_session->connection_id());
+
   if (buf && len > 0) {
     if (!continued_buffer.iov_base) {
       continued_buffer.iov_base = static_cast<uint8_t *>(ats_malloc(len));
@@ -906,11 +908,11 @@ Http2ConnectionState::update_initial_rwnd(Http2WindowSize new_size)
 void
 Http2ConnectionState::send_data_frame(FetchSM *fetch_sm)
 {
+  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send DATA frame", this->ua_session->connection_id());
+
   size_t buf_len = BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_DATA]) - HTTP2_FRAME_HEADER_LEN;
   uint8_t payload_buffer[buf_len];
 
-  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send DATA frame.", this->ua_session->connection_id());
-
   Http2Stream *stream = static_cast<Http2Stream *>(fetch_sm->ext_get_user_data());
 
   for (;;) {
@@ -971,6 +973,8 @@ Http2ConnectionState::send_data_frame(FetchSM *fetch_sm)
 void
 Http2ConnectionState::send_headers_frame(FetchSM *fetch_sm)
 {
+  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send HEADERS frame.", this->ua_session->connection_id());
+
   const size_t buf_len = BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_HEADERS]) - HTTP2_FRAME_HEADER_LEN;
   uint8_t payload_buffer[buf_len];
   size_t payload_length = 0;
@@ -1021,6 +1025,8 @@ Http2ConnectionState::send_headers_frame(FetchSM *fetch_sm)
 void
 Http2ConnectionState::send_rst_stream_frame(Http2StreamId id, Http2ErrorCode ec)
 {
+  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send RST_STREAM frame.", this->ua_session->connection_id());
+
   Http2Frame rst_stream(HTTP2_FRAME_TYPE_RST_STREAM, id, 0);
 
   rst_stream.alloc(buffer_size_index[HTTP2_FRAME_TYPE_RST_STREAM]);
@@ -1070,6 +1076,8 @@ Http2ConnectionState::send_settings_frame(const Http2ConnectionSettings &new_set
 void
 Http2ConnectionState::send_ping_frame(Http2StreamId id, uint8_t flag, const uint8_t *opaque_data)
 {
+  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send PING frame.", this->ua_session->connection_id());
+
   Http2Frame ping(HTTP2_FRAME_TYPE_PING, id, flag);
 
   ping.alloc(buffer_size_index[HTTP2_FRAME_TYPE_PING]);
@@ -1084,6 +1092,8 @@ Http2ConnectionState::send_ping_frame(Http2StreamId id, uint8_t flag, const uint
 void
 Http2ConnectionState::send_goaway_frame(Http2StreamId id, Http2ErrorCode ec)
 {
+  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send GOAWAY frame.", this->ua_session->connection_id());
+
   Http2Frame frame(HTTP2_FRAME_TYPE_GOAWAY, 0, 0);
   Http2Goaway goaway;
 
@@ -1106,6 +1116,8 @@ Http2ConnectionState::send_goaway_frame(Http2StreamId id, Http2ErrorCode ec)
 void
 Http2ConnectionState::send_window_update_frame(Http2StreamId id, uint32_t size)
 {
+  DebugSsn(this->ua_session, "http2_cs", "[%" PRId64 "] Send WINDOW_UPDATE frame.", this->ua_session->connection_id());
+
   // Create WINDOW_UPDATE frame
   Http2Frame window_update(HTTP2_FRAME_TYPE_WINDOW_UPDATE, id, 0x0);
   window_update.alloc(buffer_size_index[HTTP2_FRAME_TYPE_WINDOW_UPDATE]);