Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / spdy / spdy_framer.cc
index 77aa413..edcc82e 100644 (file)
@@ -31,6 +31,25 @@ uLong CalculateDictionaryId(const char* dictionary,
                  dictionary_size);
 }
 
+// Check to see if the name and value of a cookie are both empty.
+bool IsCookieEmpty(const base::StringPiece& cookie) {
+  if (cookie.size() == 0) {
+     return true;
+  }
+  size_t pos = cookie.find('=');
+  if (pos  == base::StringPiece::npos) {
+     return false;
+  }
+  // Ignore leading whitespaces of cookie value.
+  size_t value_start = pos + 1;
+  for (; value_start < cookie.size(); value_start++) {
+     if (!(cookie[value_start] == ' ' || cookie[value_start] == '\t')) {
+        break;
+     }
+  }
+  return (pos == 0) && ((cookie.size() - value_start) == 0);
+}
+
 struct DictionaryIds {
   DictionaryIds()
     : v2_dictionary_id(CalculateDictionaryId(kV2Dictionary, kV2DictionarySize)),
@@ -53,11 +72,12 @@ const size_t kPriorityWeightPayloadSize = 1;
 
 }  // namespace
 
-const SpdyStreamId SpdyFramer::kInvalidStream = -1;
+const SpdyStreamId SpdyFramer::kInvalidStream = static_cast<SpdyStreamId>(-1);
 const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024;
+// The size of the control frame buffer. Must be >= the minimum size of the
 // largest control frame, which is SYN_STREAM. See GetSynStreamMinimumSize() for
 // calculation details.
-const size_t SpdyFramer::kControlFrameBufferSize = 18;
+const size_t SpdyFramer::kControlFrameBufferSize = 19;
 
 #ifdef DEBUG_SPDY_STATE_CHANGES
 #define CHANGE_STATE(newstate)                                  \
@@ -166,11 +186,10 @@ void SpdyFramer::Reset() {
   settings_scratch_.Reset();
   altsvc_scratch_.Reset();
   remaining_padding_payload_length_ = 0;
-  remaining_padding_length_fields_ = 0;
 }
 
 size_t SpdyFramer::GetDataFrameMinimumSize() const {
-  return SpdyConstants::GetDataFrameMinimumSize();
+  return SpdyConstants::GetDataFrameMinimumSize(protocol_version());
 }
 
 // Size, in bytes, of the control frame header.
@@ -469,8 +488,6 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) {
       return "RST_STREAM";
     case SETTINGS:
       return "SETTINGS";
-    case NOOP:
-      return "NOOP";
     case PING:
       return "PING";
     case GOAWAY:
@@ -481,16 +498,16 @@ const char* SpdyFramer::FrameTypeToString(SpdyFrameType type) {
       return "WINDOW_UPDATE";
     case CREDENTIAL:
       return "CREDENTIAL";
-    case BLOCKED:
-      return "BLOCKED";
     case PUSH_PROMISE:
       return "PUSH_PROMISE";
     case CONTINUATION:
       return "CONTINUATION";
-    case ALTSVC:
-      return "ALTSVC";
     case PRIORITY:
       return "PRIORITY";
+    case ALTSVC:
+      return "ALTSVC";
+    case BLOCKED:
+      return "BLOCKED";
   }
   return "UNKNOWN_CONTROL_TYPE";
 }
@@ -661,7 +678,8 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
   uint16 version = 0;
   bool is_control_frame = false;
 
-  uint16 control_frame_type_field = DATA;
+  uint16 control_frame_type_field =
+    SpdyConstants::DataFrameType(protocol_version());
   // ProcessControlFrameHeader() will set current_frame_type_ to the
   // correct value if this is a valid control frame.
   current_frame_type_ = DATA;
@@ -705,17 +723,21 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
     current_frame_length_ = remaining_data_length_ + reader->GetBytesConsumed();
   } else {
     version = protocol_version();
-    uint16 length_field = 0;
-    bool successful_read = reader->ReadUInt16(&length_field);
+    uint32 length_field = 0;
+    bool successful_read = reader->ReadUInt24(&length_field);
     DCHECK(successful_read);
 
-    uint8 control_frame_type_field_uint8 = DATA;
+    uint8 control_frame_type_field_uint8 =
+      SpdyConstants::DataFrameType(protocol_version());
     successful_read = reader->ReadUInt8(&control_frame_type_field_uint8);
     DCHECK(successful_read);
     // We check control_frame_type_field's validity in
     // ProcessControlFrameHeader().
     control_frame_type_field = control_frame_type_field_uint8;
-    is_control_frame = (control_frame_type_field != DATA);
+    is_control_frame = (protocol_version() > SPDY3) ?
+      control_frame_type_field !=
+      SpdyConstants::SerializeFrameType(protocol_version(), DATA) :
+      control_frame_type_field != 0;
 
     if (is_control_frame) {
       current_frame_length_ = length_field + GetControlFrameHeaderSize();
@@ -778,8 +800,8 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
 
     uint8 valid_data_flags = 0;
     if (protocol_version() > SPDY3) {
-      valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT |
-          DATA_FLAG_PAD_LOW | DATA_FLAG_PAD_HIGH;
+      valid_data_flags =
+          DATA_FLAG_FIN | DATA_FLAG_END_SEGMENT | DATA_FLAG_PADDED;
     } else {
       valid_data_flags = DATA_FLAG_FIN;
     }
@@ -812,15 +834,10 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
   DCHECK_EQ(SPDY_NO_ERROR, error_code_);
   DCHECK_LE(GetControlFrameHeaderSize(), current_frame_buffer_length_);
 
+  // TODO(mlavan): Either remove credential frames from the code entirely,
+  // or add them to parsing + serialization methods for SPDY3.
   // Early detection of deprecated frames that we ignore.
   if (protocol_version() <= SPDY3) {
-    if (control_frame_type_field == NOOP) {
-      current_frame_type_ = NOOP;
-      DVLOG(1) << "NOOP control frame found. Ignoring.";
-      CHANGE_STATE(SPDY_AUTO_RESET);
-      return;
-    }
-
     if (control_frame_type_field == CREDENTIAL) {
       current_frame_type_ = CREDENTIAL;
       DCHECK_EQ(SPDY3, protocol_version());
@@ -832,10 +849,31 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
 
   if (!SpdyConstants::IsValidFrameType(protocol_version(),
                                        control_frame_type_field)) {
-    DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field
-                  << " (protocol version: " << protocol_version() << ")";
-    set_error(SPDY_INVALID_CONTROL_FRAME);
-    return;
+    if (protocol_version() <= SPDY3) {
+      DLOG(WARNING) << "Invalid control frame type " << control_frame_type_field
+                    << " (protocol version: " << protocol_version() << ")";
+      set_error(SPDY_INVALID_CONTROL_FRAME);
+      return;
+    } else {
+      // In HTTP2 we ignore unknown frame types for extensibility, as long as
+      // the rest of the control frame header is valid.
+      // We rely on the visitor to check validity of current_frame_stream_id_.
+      bool valid_stream = visitor_->OnUnknownFrame(current_frame_stream_id_,
+                                                   control_frame_type_field);
+      if (valid_stream) {
+        DVLOG(1) << "Ignoring unknown frame type.";
+        CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD);
+      } else {
+        // Report an invalid frame error and close the stream if the
+        // stream_id is not valid.
+        DLOG(WARNING) << "Unknown control frame type "
+                      << control_frame_type_field
+                      << " received on invalid stream "
+                      << current_frame_stream_id_;
+        set_error(SPDY_INVALID_CONTROL_FRAME);
+      }
+      return;
+    }
   }
 
   current_frame_type_ = SpdyConstants::ParseFrameType(protocol_version(),
@@ -877,7 +915,7 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
       // plus a 4-byte length field in SPDY3 and below.
       size_t values_prefix_size = (protocol_version() <= SPDY3 ? 4 : 0);
       // Size of each key/value pair in bytes.
-      size_t setting_size = (protocol_version() <= SPDY3 ? 8 : 5);
+      size_t setting_size = SpdyConstants::GetSettingSize(protocol_version());
       if (current_frame_length_ < GetSettingsMinimumSize() ||
           (current_frame_length_ - GetControlFrameHeaderSize())
           % setting_size != values_prefix_size) {
@@ -930,6 +968,8 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
           min_size += 4;
         }
         if (current_frame_length_ < min_size) {
+          // TODO(mlavan): check here for HEADERS with no payload?
+          // (not allowed in SPDY4)
           set_error(SPDY_INVALID_CONTROL_FRAME);
         } else if (protocol_version() <= SPDY3 &&
                    current_frame_flags_ & ~CONTROL_FLAG_FIN) {
@@ -938,7 +978,7 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
                    current_frame_flags_ &
                        ~(CONTROL_FLAG_FIN | HEADERS_FLAG_PRIORITY |
                          HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_END_SEGMENT |
-                         HEADERS_FLAG_PAD_LOW | HEADERS_FLAG_PAD_HIGH)) {
+                         HEADERS_FLAG_PADDED)) {
           set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
         }
       }
@@ -953,7 +993,6 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
     case BLOCKED:
       if (current_frame_length_ != GetBlockedSize() ||
           protocol_version() <= SPDY3) {
-        // TODO(mlavan): BLOCKED frames are no longer part of SPDY4.
         set_error(SPDY_INVALID_CONTROL_FRAME);
       } else if (current_frame_flags_ != 0) {
         set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
@@ -967,7 +1006,7 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
       } else if (protocol_version() > SPDY3 &&
                  current_frame_flags_ &
                      ~(PUSH_PROMISE_FLAG_END_PUSH_PROMISE |
-                       HEADERS_FLAG_PAD_LOW | HEADERS_FLAG_PAD_HIGH)) {
+                       HEADERS_FLAG_PADDED)) {
         set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
       }
       break;
@@ -975,9 +1014,7 @@ void SpdyFramer::ProcessControlFrameHeader(uint16 control_frame_type_field) {
       if (current_frame_length_ < GetContinuationMinimumSize() ||
           protocol_version() <= SPDY3) {
         set_error(SPDY_INVALID_CONTROL_FRAME);
-      } else if (current_frame_flags_ &
-                 ~(HEADERS_FLAG_END_HEADERS | HEADERS_FLAG_PAD_LOW |
-                   HEADERS_FLAG_PAD_HIGH)) {
+      } else if (current_frame_flags_ & ~HEADERS_FLAG_END_HEADERS) {
         set_error(SPDY_INVALID_CONTROL_FRAME_FLAGS);
       }
       break;
@@ -1261,12 +1298,18 @@ void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers,
             break;
         }
         if (i < cookie_data.size()) {
-          cookie_values.push_back(cookie_data.substr(0, i));
-          cookie_length += i + 2 /* semicolon and space */;
+          if (!IsCookieEmpty(cookie_data.substr(0, i))) {
+            cookie_values.push_back(cookie_data.substr(0, i));
+            cookie_length += i + 2 /* semicolon and space */;
+          }
           cookie_data.remove_prefix(i + 1);
         } else {
-          cookie_values.push_back(cookie_data);
-          cookie_length += cookie_data.size();
+          if (!IsCookieEmpty(cookie_data)) {
+            cookie_values.push_back(cookie_data);
+            cookie_length += cookie_data.size();
+          } else if (cookie_length > 2) {
+            cookie_length -= 2 /* compensate for previously added length */;
+          }
           cookie_data.remove_prefix(i);
         }
       }
@@ -1317,6 +1360,7 @@ void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers,
   DCHECK_EQ(Z_OK, rv);
   z->clas = kZStandardData;
 }
+
 #endif  // !defined(USE_SYSTEM_ZLIB)
 
 size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
@@ -1519,7 +1563,7 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
                                    (current_frame_flags_ &
                                     HEADERS_FLAG_END_HEADERS) != 0);
         }
-        CHANGE_STATE(SPDY_READ_PADDING_LENGTH);
+        CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
         break;
       default:
         DCHECK(false);
@@ -1609,7 +1653,7 @@ size_t SpdyFramer::ProcessSettingsFramePayload(const char* data,
   size_t unprocessed_bytes = std::min(data_len, remaining_data_length_);
   size_t processed_bytes = 0;
 
-  size_t setting_size = protocol_version() <= SPDY3 ? 8 : 5;
+  size_t setting_size = SpdyConstants::GetSettingSize(protocol_version());
 
   // Loop over our incoming data.
   while (unprocessed_bytes > 0) {
@@ -1703,14 +1747,19 @@ bool SpdyFramer::ProcessSetting(const char* data) {
     flags = id_and_flags.flags();
     value = ntohl(*(reinterpret_cast<const uint32*>(data + 4)));
   } else {
-    id_field = *(reinterpret_cast<const uint8*>(data));
-    value = ntohl(*(reinterpret_cast<const uint32*>(data + 1)));
+    id_field = ntohs(*(reinterpret_cast<const uint16*>(data)));
+    value = ntohl(*(reinterpret_cast<const uint32*>(data + 2)));
   }
 
   // Validate id.
   if (!SpdyConstants::IsValidSettingId(protocol_version(), id_field)) {
     DLOG(WARNING) << "Unknown SETTINGS ID: " << id_field;
-    return false;
+    if (protocol_version() <= SPDY3) {
+      return false;
+    } else {
+      // In HTTP2 we ignore unknown settings for extensibility.
+      return true;
+    }
   }
   id = SpdyConstants::ParseSettingId(protocol_version(), id_field);
 
@@ -1790,9 +1839,21 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
         break;
       case PRIORITY: {
           DCHECK_LT(SPDY3, protocol_version());
-          // TODO(hkhalil): Process PRIORITY frames rather than ignore them.
-          reader.Seek(5);
+          uint32 parent_stream_id;
+          uint8 weight;
+          bool exclusive;
+          bool successful_read = true;
+          successful_read = reader.ReadUInt32(&parent_stream_id);
+          DCHECK(successful_read);
+          // Exclusivity is indicated by a single bit flag.
+          exclusive = (parent_stream_id >> 31) != 0;
+          // Zero out the highest-order bit to get the parent stream id.
+          parent_stream_id &= 0x7fffffff;
+          successful_read = reader.ReadUInt8(&weight);
+          DCHECK(successful_read);
           DCHECK(reader.IsDoneReading());
+          visitor_->OnPriority(
+              current_frame_stream_id_, parent_stream_id, weight, exclusive);
         }
         break;
       default:
@@ -1843,13 +1904,10 @@ size_t SpdyFramer::ProcessGoAwayFramePayload(const char* data, size_t len) {
           status = SpdyConstants::ParseGoAwayStatus(protocol_version(),
                                                     status_raw);
         } else {
-          DCHECK(false);
-          // Throw an error for SPDY4+, keep liberal behavior
-          // for earlier versions.
           if (protocol_version() > SPDY3) {
-            DLOG(WARNING) << "Invalid GO_AWAY status " << status_raw;
-            set_error(SPDY_INVALID_CONTROL_FRAME);
-            return 0;
+            // Treat unrecognized status codes as INTERNAL_ERROR as
+            // recommended by the HTTP/2 spec.
+            status = GOAWAY_INTERNAL_ERROR;
           }
         }
       }
@@ -1912,12 +1970,10 @@ size_t SpdyFramer::ProcessRstStreamFramePayload(const char* data, size_t len) {
                                                 status_raw)) {
         status = static_cast<SpdyRstStreamStatus>(status_raw);
       } else {
-        // Throw an error for SPDY4+, keep liberal behavior
-        // for earlier versions.
         if (protocol_version() > SPDY3) {
-          DLOG(WARNING) << "Invalid RST_STREAM status " << status_raw;
-          set_error(SPDY_INVALID_CONTROL_FRAME);
-          return 0;
+          // Treat unrecognized status codes as INTERNAL_ERROR as
+          // recommended by the HTTP/2 spec.
+          status = RST_STREAM_INTERNAL_ERROR;
         }
       }
       // Finished parsing the RST_STREAM header, call frame handler.
@@ -2068,56 +2124,44 @@ size_t SpdyFramer::ProcessAltSvcFramePayload(const char* data, size_t len) {
   return processed_bytes;
 }
 
+// TODO(raullenchai): ProcessFramePaddingLength should be able to deal with
+// HEADERS_FLAG_PADDED and PUSH_PROMISE_FLAG_PADDED as well (see b/15777051).
 size_t SpdyFramer::ProcessFramePaddingLength(const char* data, size_t len) {
   DCHECK_EQ(SPDY_READ_PADDING_LENGTH, state_);
+  DCHECK_EQ(remaining_padding_payload_length_, 0u);
 
   size_t original_len = len;
-  if (remaining_padding_length_fields_ == 0) {
-    DCHECK_EQ(remaining_padding_payload_length_, 0u);
-    bool pad_low = false;
-    bool pad_high = false;
-    if (current_frame_flags_ & DATA_FLAG_PAD_LOW) {
-      pad_low = true;
-      ++remaining_padding_length_fields_;
-    }
-    if (current_frame_flags_ & DATA_FLAG_PAD_HIGH) {
-      pad_high = true;
-      ++remaining_padding_length_fields_;
-    }
-    if ((pad_high && !pad_low) ||
-        remaining_data_length_ < remaining_padding_length_fields_) {
-      set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
+  if (current_frame_flags_ & DATA_FLAG_PADDED) {
+    if (len != 0) {
+      if (remaining_data_length_ < 1) {
+        set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
+        return 0;
+      }
+
+      remaining_padding_payload_length_ = *reinterpret_cast<const uint8*>(data);
+      ++data;
+      --len;
+      --remaining_data_length_;
+    } else {
+      // We don't have the data available for parsing the pad length field. Keep
+      // waiting.
       return 0;
     }
   }
 
-  // Parse the padding length.
-  while (len != 0 && remaining_padding_length_fields_ != 0) {
-    remaining_padding_payload_length_ =
-        (remaining_padding_payload_length_ << 8) +
-        *reinterpret_cast<const uint8*>(data);
-    ++data;
-    --len;
-    --remaining_padding_length_fields_;
-    --remaining_data_length_;
+  if (remaining_padding_payload_length_ > remaining_data_length_) {
+    set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
+    return 0;
   }
-
-  if (remaining_padding_length_fields_ == 0) {
-    if (remaining_padding_payload_length_ > remaining_data_length_) {
-      set_error(SPDY_INVALID_DATA_FRAME_FLAGS);
-      return 0;
-    }
-    if (current_frame_type_ == DATA) {
-      CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME);
-    } else {
-      DCHECK(current_frame_type_ == HEADERS ||
-             current_frame_type_ == PUSH_PROMISE ||
-             current_frame_type_ == CONTINUATION ||
-             current_frame_type_ == SYN_STREAM ||
-             current_frame_type_ == SYN_REPLY)
-          << current_frame_type_;
-      CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
-    }
+  if (current_frame_type_ == DATA) {
+    CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME);
+  } else {
+    DCHECK(current_frame_type_ == HEADERS ||
+           current_frame_type_ == PUSH_PROMISE ||
+           current_frame_type_ == SYN_STREAM ||
+           current_frame_type_ == SYN_REPLY)
+        << current_frame_type_;
+    CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
   }
   return original_len - len;
 }
@@ -2259,12 +2303,8 @@ SpdySerializedFrame* SpdyFramer::SerializeData(
 
   if (protocol_version() > SPDY3) {
     int num_padding_fields = 0;
-    if (data_ir.pad_low()) {
-      flags |= DATA_FLAG_PAD_LOW;
-      ++num_padding_fields;
-    }
-    if (data_ir.pad_high()) {
-      flags |= DATA_FLAG_PAD_HIGH;
+    if (data_ir.padded()) {
+      flags |= DATA_FLAG_PADDED;
       ++num_padding_fields;
     }
 
@@ -2273,10 +2313,7 @@ SpdySerializedFrame* SpdyFramer::SerializeData(
         GetDataFrameMinimumSize();
     SpdyFrameBuilder builder(size_with_padding, protocol_version());
     builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags);
-    if (data_ir.pad_high()) {
-      builder.WriteUInt8(data_ir.padding_payload_len() >> 8);
-    }
-    if (data_ir.pad_low()) {
+    if (data_ir.padded()) {
       builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
     }
     builder.WriteBytes(data_ir.data().data(), data_ir.data().length());
@@ -2306,12 +2343,8 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField(
   size_t frame_size = GetDataFrameMinimumSize();
   size_t num_padding_fields = 0;
   if (protocol_version() > SPDY3) {
-    if (data_ir.pad_low()) {
-      flags |= DATA_FLAG_PAD_LOW;
-      ++num_padding_fields;
-    }
-    if (data_ir.pad_high()) {
-      flags |= DATA_FLAG_PAD_HIGH;
+    if (data_ir.padded()) {
+      flags |= DATA_FLAG_PADDED;
       ++num_padding_fields;
     }
     frame_size += num_padding_fields;
@@ -2320,10 +2353,7 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField(
   SpdyFrameBuilder builder(frame_size, protocol_version());
   builder.WriteDataFrameHeader(*this, data_ir.stream_id(), flags);
   if (protocol_version() > SPDY3) {
-    if (data_ir.pad_high()) {
-      builder.WriteUInt8(data_ir.padding_payload_len() >> 8);
-    }
-    if (data_ir.pad_low()) {
+    if (data_ir.padded()) {
       builder.WriteUInt8(data_ir.padding_payload_len() & 0xff);
     }
     builder.OverwriteLength(*this,  num_padding_fields +
@@ -2337,6 +2367,7 @@ SpdySerializedFrame* SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField(
 
 SpdySerializedFrame* SpdyFramer::SerializeSynStream(
     const SpdySynStreamIR& syn_stream) {
+  DCHECK_GE(SPDY3, protocol_version());
   uint8 flags = 0;
   if (syn_stream.fin()) {
     flags |= CONTROL_FLAG_FIN;
@@ -2345,12 +2376,6 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream(
     // TODO(hkhalil): invalid for HTTP2.
     flags |= CONTROL_FLAG_UNIDIRECTIONAL;
   }
-  // In SPDY >= 4, SYN_STREAM frames are HEADERS frames, but for now
-  // we never expect to have to overflow into a CONTINUATION frame.
-  if (protocol_version() > SPDY3) {
-    flags |= HEADERS_FLAG_PRIORITY;
-    flags |= HEADERS_FLAG_END_HEADERS;
-  }
 
   // Sanitize priority.
   uint8 priority = syn_stream.priority();
@@ -2360,51 +2385,22 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream(
   }
 
   // The size of this frame, including variable-length name-value block.
-  size_t size = GetSynStreamMinimumSize();
-
-  string hpack_encoding;
-  if (protocol_version() > SPDY3) {
-    if (enable_compression_) {
-      GetHpackEncoder()->EncodeHeaderSet(
-          syn_stream.name_value_block(), &hpack_encoding);
-    } else {
-      GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
-          syn_stream.name_value_block(), &hpack_encoding);
-    }
-    size += hpack_encoding.size();
-  } else {
-    size += GetSerializedLength(syn_stream.name_value_block());
-  }
+  size_t size = GetSynStreamMinimumSize() +
+      GetSerializedLength(syn_stream.name_value_block());
 
   SpdyFrameBuilder builder(size, protocol_version());
-  if (protocol_version() <= SPDY3) {
-    builder.WriteControlFrameHeader(*this, SYN_STREAM, flags);
-    builder.WriteUInt32(syn_stream.stream_id());
-    builder.WriteUInt32(syn_stream.associated_to_stream_id());
-    builder.WriteUInt8(priority << ((protocol_version() <= SPDY2) ? 6 : 5));
-    builder.WriteUInt8(0);  // Unused byte where credential slot used to be.
-  } else {
-    builder.BeginNewFrame(*this,
-                          HEADERS,
-                          flags,
-                          syn_stream.stream_id());
-    // TODO(jgraettinger): Plumb priorities and stream dependencies.
-    builder.WriteUInt32(0);  // Non-exclusive bit and root stream ID.
-    builder.WriteUInt8(MapPriorityToWeight(priority));
-  }
+  builder.WriteControlFrameHeader(*this, SYN_STREAM, flags);
+  builder.WriteUInt32(syn_stream.stream_id());
+  builder.WriteUInt32(syn_stream.associated_to_stream_id());
+  builder.WriteUInt8(priority << ((protocol_version() <= SPDY2) ? 6 : 5));
+  builder.WriteUInt8(0);  // Unused byte where credential slot used to be.
   DCHECK_EQ(GetSynStreamMinimumSize(), builder.length());
-  if (protocol_version() > SPDY3) {
-    builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
-  } else {
-    SerializeNameValueBlock(&builder, syn_stream);
-  }
+  SerializeNameValueBlock(&builder, syn_stream);
 
   if (debug_visitor_) {
-    const size_t payload_len = protocol_version() > SPDY3 ?
-        hpack_encoding.size() :
+    const size_t payload_len =
         GetSerializedLength(protocol_version(),
                             &(syn_stream.name_value_block()));
-    // SPDY 4 reports this compression as a SYN_STREAM compression.
     debug_visitor_->OnSendCompressedFrame(syn_stream.stream_id(),
                                           SYN_STREAM,
                                           payload_len,
@@ -2416,32 +2412,15 @@ SpdySerializedFrame* SpdyFramer::SerializeSynStream(
 
 SpdySerializedFrame* SpdyFramer::SerializeSynReply(
     const SpdySynReplyIR& syn_reply) {
+  DCHECK_GE(SPDY3, protocol_version());
   uint8 flags = 0;
   if (syn_reply.fin()) {
     flags |= CONTROL_FLAG_FIN;
   }
-  // In SPDY >= 4, SYN_REPLY frames are HEADERS frames, but for now
-  // we never expect to have to overflow into a CONTINUATION frame.
-  if (protocol_version() > SPDY3) {
-    flags |= HEADERS_FLAG_END_HEADERS;
-  }
 
   // The size of this frame, including variable-length name-value block.
-  size_t size = GetSynReplyMinimumSize();
-
-  string hpack_encoding;
-  if (protocol_version() > SPDY3) {
-    if (enable_compression_) {
-      GetHpackEncoder()->EncodeHeaderSet(
-          syn_reply.name_value_block(), &hpack_encoding);
-    } else {
-      GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
-          syn_reply.name_value_block(), &hpack_encoding);
-    }
-    size += hpack_encoding.size();
-  } else {
-    size += GetSerializedLength(syn_reply.name_value_block());
-  }
+  const size_t size = GetSynReplyMinimumSize() +
+                      GetSerializedLength(syn_reply.name_value_block());
 
   SpdyFrameBuilder builder(size, protocol_version());
   if (protocol_version() <= SPDY3) {
@@ -2457,17 +2436,11 @@ SpdySerializedFrame* SpdyFramer::SerializeSynReply(
     builder.WriteUInt16(0);  // Unused.
   }
   DCHECK_EQ(GetSynReplyMinimumSize(), builder.length());
-  if (protocol_version() > SPDY3) {
-    builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
-  } else {
-    SerializeNameValueBlock(&builder, syn_reply);
-  }
+  SerializeNameValueBlock(&builder, syn_reply);
 
   if (debug_visitor_) {
-    const size_t payload_len = protocol_version() > SPDY3 ?
-        hpack_encoding.size() :
-        GetSerializedLength(protocol_version(),
-                            &(syn_reply.name_value_block()));
+    const size_t payload_len = GetSerializedLength(
+        protocol_version(), &(syn_reply.name_value_block()));
     debug_visitor_->OnSendCompressedFrame(syn_reply.stream_id(),
                                           SYN_REPLY,
                                           payload_len,
@@ -2525,7 +2498,7 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings(
   }
   const SpdySettingsIR::ValueMap* values = &(settings.values());
 
-  size_t setting_size = (protocol_version() <= SPDY3 ? 8 : 5);
+  size_t setting_size = SpdyConstants::GetSettingSize(protocol_version());
   // Size, in bytes, of this SETTINGS frame.
   const size_t size = GetSettingsMinimumSize() +
                       (values->size() * setting_size);
@@ -2562,8 +2535,8 @@ SpdySerializedFrame* SpdyFramer::SerializeSettings(
       uint32 id_and_flags_wire = flags_and_id.GetWireFormat(protocol_version());
       builder.WriteBytes(&id_and_flags_wire, 4);
     } else {
-      builder.WriteUInt8(SpdyConstants::SerializeSettingId(protocol_version(),
-                                                           it->first));
+      builder.WriteUInt16(SpdyConstants::SerializeSettingId(protocol_version(),
+                                                            it->first));
     }
     builder.WriteUInt32(it->second.value);
   }
@@ -2648,7 +2621,7 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
       DLOG(DFATAL) << "Priority out-of-bounds.";
       priority = GetLowestPriority();
     }
-    size += 4;
+    size += 5;
   }
 
   string hpack_encoding;
@@ -2661,7 +2634,7 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
           headers.name_value_block(), &hpack_encoding);
     }
     size += hpack_encoding.size();
-    if (size > GetControlFrameBufferMaxSize()) {
+    if (size > GetHeaderFragmentMaxSize()) {
       size += GetNumberRequiredContinuationFrames(size) *
               GetContinuationMinimumSize();
       flags &= ~HEADERS_FLAG_END_HEADERS;
@@ -2679,11 +2652,6 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
                           HEADERS,
                           flags,
                           headers.stream_id());
-    if (headers.has_priority()) {
-      // TODO(jgraettinger): Plumb priorities and stream dependencies.
-      builder.WriteUInt32(0);  // Non-exclusive bit and root stream ID.
-      builder.WriteUInt8(MapPriorityToWeight(priority));
-    }
   }
   if (protocol_version() <= SPDY2) {
     builder.WriteUInt16(0);  // Unused.
@@ -2691,6 +2659,11 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
   DCHECK_EQ(GetHeadersMinimumSize(), builder.length());
 
   if (protocol_version() > SPDY3) {
+    if (headers.has_priority()) {
+      // TODO(jgraettinger): Plumb priorities and stream dependencies.
+      builder.WriteUInt32(0);  // Non-exclusive bit and root stream ID.
+      builder.WriteUInt8(MapPriorityToWeight(priority));
+    }
     WritePayloadWithContinuation(&builder,
                                  hpack_encoding,
                                  headers.stream_id(),
@@ -2700,8 +2673,10 @@ SpdySerializedFrame* SpdyFramer::SerializeHeaders(
   }
 
   if (debug_visitor_) {
-    const size_t payload_len = protocol_version() > SPDY3 ?
-        hpack_encoding.size() :
+    // SPDY4 uses HPACK for header compression. However, continue to
+    // use GetSerializedLength() for an apples-to-apples comparision of
+    // compression performance between HPACK and SPDY w/ deflate.
+    const size_t payload_len =
         GetSerializedLength(protocol_version(),
                             &(headers.name_value_block()));
     debug_visitor_->OnSendCompressedFrame(headers.stream_id(),
@@ -2747,22 +2722,18 @@ SpdyFrame* SpdyFramer::SerializePushPromise(
   size_t size = GetPushPromiseMinimumSize();
 
   string hpack_encoding;
-  if (protocol_version() > SPDY3) {
-    if (enable_compression_) {
-      GetHpackEncoder()->EncodeHeaderSet(
-          push_promise.name_value_block(), &hpack_encoding);
-    } else {
-      GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
-          push_promise.name_value_block(), &hpack_encoding);
-    }
-    size += hpack_encoding.size();
-    if (size > GetControlFrameBufferMaxSize()) {
-      size += GetNumberRequiredContinuationFrames(size) *
-              GetContinuationMinimumSize();
-      flags &= ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
-    }
+  if (enable_compression_) {
+    GetHpackEncoder()->EncodeHeaderSet(
+        push_promise.name_value_block(), &hpack_encoding);
   } else {
-    size += GetSerializedLength(push_promise.name_value_block());
+    GetHpackEncoder()->EncodeHeaderSetWithoutCompression(
+        push_promise.name_value_block(), &hpack_encoding);
+  }
+  size += hpack_encoding.size();
+  if (size > GetHeaderFragmentMaxSize()) {
+    size += GetNumberRequiredContinuationFrames(size) *
+            GetContinuationMinimumSize();
+    flags &= ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
   }
 
   SpdyFrameBuilder builder(size, protocol_version());
@@ -2773,22 +2744,22 @@ SpdyFrame* SpdyFramer::SerializePushPromise(
   builder.WriteUInt32(push_promise.promised_stream_id());
   DCHECK_EQ(GetPushPromiseMinimumSize(), builder.length());
 
-  if (protocol_version() > SPDY3) {
-    WritePayloadWithContinuation(&builder,
-                                 hpack_encoding,
-                                 push_promise.stream_id(),
-                                 PUSH_PROMISE);
-  } else {
-    SerializeNameValueBlock(&builder, push_promise);
-  }
+  WritePayloadWithContinuation(&builder,
+                               hpack_encoding,
+                               push_promise.stream_id(),
+                               PUSH_PROMISE);
 
   if (debug_visitor_) {
-    const size_t payload_len = protocol_version() > SPDY3 ?
-        hpack_encoding.size() :
+    // SPDY4 uses HPACK for header compression. However, continue to
+    // use GetSerializedLength() for an apples-to-apples comparision of
+    // compression performance between HPACK and SPDY w/ deflate.
+    const size_t payload_len =
         GetSerializedLength(protocol_version(),
                             &(push_promise.name_value_block()));
     debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(),
-        PUSH_PROMISE, payload_len, builder.length());
+                                          PUSH_PROMISE,
+                                          payload_len,
+                                          builder.length());
   }
 
   return builder.take();
@@ -2823,13 +2794,6 @@ SpdyFrame* SpdyFramer::SerializeContinuation(
   DCHECK_EQ(GetContinuationMinimumSize(), builder.length());
 
   builder.WriteBytes(&hpack_encoding[0], hpack_encoding.size());
-
-  if (debug_visitor_) {
-    const size_t payload_len = hpack_encoding.size();
-    debug_visitor_->OnSendCompressedFrame(continuation.stream_id(),
-        CONTINUATION, payload_len, builder.length());
-  }
-
   return builder.take();
 }
 
@@ -2856,6 +2820,24 @@ SpdyFrame* SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc) {
   return builder.take();
 }
 
+SpdyFrame* SpdyFramer::SerializePriority(const SpdyPriorityIR& priority) {
+  DCHECK_LT(SPDY3, protocol_version());
+  size_t size = GetPrioritySize();
+
+  SpdyFrameBuilder builder(size, protocol_version());
+  builder.BeginNewFrame(*this, PRIORITY, kNoFlags, priority.stream_id());
+
+  // Make sure the highest-order bit in the parent stream id is zeroed out.
+  uint32 parent_stream_id = priority.parent_stream_id() & 0x7fffffff;
+  uint32 exclusive = priority.exclusive() ? 0x80000000 : 0;
+  // Set the one-bit exclusivity flag.
+  uint32 flag_and_parent_id = parent_stream_id | exclusive;
+  builder.WriteUInt32(flag_and_parent_id);
+  builder.WriteUInt8(priority.weight());
+  DCHECK_EQ(GetPrioritySize(), builder.length());
+  return builder.take();
+}
+
 namespace {
 
 class FrameSerializationVisitor : public SpdyFrameVisitor {
@@ -2907,6 +2889,9 @@ class FrameSerializationVisitor : public SpdyFrameVisitor {
   virtual void VisitAltSvc(const SpdyAltSvcIR& altsvc) OVERRIDE {
     frame_.reset(framer_->SerializeAltSvc(altsvc));
   }
+  virtual void VisitPriority(const SpdyPriorityIR& priority) OVERRIDE {
+    frame_.reset(framer_->SerializePriority(priority));
+  }
 
  private:
   SpdyFramer* framer_;
@@ -2935,7 +2920,7 @@ size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock& headers) {
 }
 
 size_t SpdyFramer::GetNumberRequiredContinuationFrames(size_t size) {
-  const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize();
+  const size_t kMaxControlFrameSize = GetHeaderFragmentMaxSize();
   DCHECK_GT(protocol_version(), SPDY3);
   DCHECK_GT(size, kMaxControlFrameSize);
   size_t overflow = size - kMaxControlFrameSize;
@@ -2946,7 +2931,7 @@ void SpdyFramer::WritePayloadWithContinuation(SpdyFrameBuilder* builder,
                                               const string& hpack_encoding,
                                               SpdyStreamId stream_id,
                                               SpdyFrameType type) {
-  const size_t kMaxControlFrameSize = GetControlFrameBufferMaxSize();
+  const size_t kMaxControlFrameSize = GetHeaderFragmentMaxSize();
 
     // In addition to the prefix, fixed_field_size includes the size of
     // any fields that come before the variable-length name/value block.