summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-27 18:43:06 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-27 18:43:06 +0000
commitd27ea08b3c0d4b0d16e898755dd299381481bba6 (patch)
treedfc7ffcfe4178365c94ee84225858b84d6713ab8 /net
parent18713e5dd919fd5536b3cd8b8fbe1e358743c76c (diff)
downloadchromium_src-d27ea08b3c0d4b0d16e898755dd299381481bba6.zip
chromium_src-d27ea08b3c0d4b0d16e898755dd299381481bba6.tar.gz
chromium_src-d27ea08b3c0d4b0d16e898755dd299381481bba6.tar.bz2
Add support to SpdyFramer for the CREDENTIAL frame.
BUG=106103 TEST=None Review URL: http://codereview.chromium.org/8893003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115835 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/spdy/buffered_spdy_framer_unittest.cc5
-rw-r--r--net/spdy/spdy_frame_builder.cc15
-rw-r--r--net/spdy/spdy_frame_builder.h5
-rw-r--r--net/spdy/spdy_framer.cc114
-rw-r--r--net/spdy/spdy_framer.h42
-rw-r--r--net/spdy/spdy_framer_test.cc225
-rw-r--r--net/spdy/spdy_protocol.h50
-rw-r--r--net/spdy/spdy_session.cc6
-rw-r--r--net/spdy/spdy_session.h6
-rw-r--r--net/tools/flip_server/spdy_interface.cc6
-rw-r--r--net/tools/flip_server/spdy_interface.h2
11 files changed, 469 insertions, 7 deletions
diff --git a/net/spdy/buffered_spdy_framer_unittest.cc b/net/spdy/buffered_spdy_framer_unittest.cc
index 56c5679..746e750 100644
--- a/net/spdy/buffered_spdy_framer_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_unittest.cc
@@ -56,6 +56,11 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
LOG(FATAL) << "Unexpected OnStreamFrameData call.";
}
+ bool OnCredentialFrameData(const char*, size_t) {
+ LOG(FATAL) << "Unexpected OnCredentialFrameData call.";
+ return false;
+ }
+
void OnDataFrameHeader(const SpdyDataFrame* frame) {
LOG(FATAL) << "Unexpected OnDataFrameHeader call.";
}
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc
index c6625b2..92dc3cd 100644
--- a/net/spdy/spdy_frame_builder.cc
+++ b/net/spdy/spdy_frame_builder.cc
@@ -86,7 +86,7 @@ bool SpdyFrameBuilder::ReadString(void** iter, std::string* result) const {
}
bool SpdyFrameBuilder::ReadBytes(void** iter, const char** data,
- uint16 length) const {
+ uint32 length) const {
DCHECK(iter);
DCHECK(data);
@@ -111,6 +111,19 @@ bool SpdyFrameBuilder::ReadData(void** iter, const char** data,
return ReadBytes(iter, data, *length);
}
+bool SpdyFrameBuilder::ReadReadLen32PrefixedData(void** iter,
+ const char** data,
+ uint32* length) const {
+ DCHECK(iter);
+ DCHECK(data);
+ DCHECK(length);
+
+ if (!ReadUInt32(iter, length))
+ return false;
+
+ return ReadBytes(iter, data, *length);
+}
+
char* SpdyFrameBuilder::BeginWriteData(uint16 length) {
DCHECK_EQ(variable_buffer_offset_, 0U) <<
"There can only be one variable buffer in a SpdyFrameBuilder";
diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h
index b53adbc..e828a88 100644
--- a/net/spdy/spdy_frame_builder.h
+++ b/net/spdy/spdy_frame_builder.h
@@ -61,8 +61,11 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
bool ReadUInt16(void** iter, uint16* result) const;
bool ReadUInt32(void** iter, uint32* result) const;
bool ReadString(void** iter, std::string* result) const;
- bool ReadBytes(void** iter, const char** data, uint16 length) const;
+ bool ReadBytes(void** iter, const char** data, uint32 length) const;
bool ReadData(void** iter, const char** data, uint16* length) const;
+ bool ReadReadLen32PrefixedData(void** iter,
+ const char** data,
+ uint32* length) const;
// Methods for adding to the payload. These values are appended to the end
// of the SpdyFrameBuilder payload. When reading values, you must read them
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 8b41a48..f0d4efdf 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -20,8 +20,13 @@
#include "third_party/zlib/zlib.h"
#endif
+using std::vector;
+
namespace spdy {
+SpdyCredential::SpdyCredential() : slot(0) { }
+SpdyCredential::~SpdyCredential() { }
+
// Compute the id of our dictionary so that we know we're using the
// right one when asked for it.
uLong CalculateDictionaryId() {
@@ -187,6 +192,8 @@ const char* SpdyFramer::StateToString(int state) {
return "SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK";
case SPDY_CONTROL_FRAME_HEADER_BLOCK:
return "SPDY_CONTROL_FRAME_HEADER_BLOCK";
+ case SPDY_CREDENTIAL_FRAME_PAYLOAD:
+ return "SPDY_CREDENTIAL_FRAME_PAYLOAD";
}
return "UNKNOWN_STATE";
}
@@ -260,6 +267,8 @@ const char* SpdyFramer::ControlTypeToString(SpdyControlType type) {
return "HEADERS";
case WINDOW_UPDATE:
return "WINDOW_UPDATE";
+ case CREDENTIAL:
+ return "CREDENTIAL";
case NUM_CONTROL_FRAME_TYPES:
break;
}
@@ -323,6 +332,13 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
continue;
}
+ case SPDY_CREDENTIAL_FRAME_PAYLOAD: {
+ size_t bytes_read = ProcessCredentialFramePayload(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ continue;
+ }
+
case SPDY_CONTROL_FRAME_PAYLOAD: {
size_t bytes_read = ProcessControlFramePayload(data, len);
len -= bytes_read;
@@ -467,6 +483,11 @@ void SpdyFramer::ProcessControlFrameHeader() {
SpdyPingControlFrame::size() - SpdyControlFrame::kHeaderSize)
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
+ case CREDENTIAL:
+ if (current_control_frame.length() <
+ SpdyCredentialControlFrame::size() - SpdyControlFrame::kHeaderSize)
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ break;
default:
LOG(WARNING) << "Valid " << display_protocol_
<< " control frame with unhandled type: "
@@ -490,6 +511,12 @@ void SpdyFramer::ProcessControlFrameHeader() {
return;
}
+ if (current_control_frame.type() == CREDENTIAL) {
+ visitor_->OnControl(&current_control_frame);
+ CHANGE_STATE(SPDY_CREDENTIAL_FRAME_PAYLOAD);
+ return;
+ }
+
int32 frame_size_without_header_block;
switch (current_control_frame.type()) {
case SYN_STREAM:
@@ -686,6 +713,18 @@ size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
return original_len - len;
}
+size_t SpdyFramer::ProcessCredentialFramePayload(const char* data, size_t len) {
+ bool processed_succesfully = visitor_->OnCredentialFrameData(data, len);
+ remaining_control_payload_ -= len;
+ remaining_data_ -= len;
+ if (!processed_succesfully) {
+ set_error(SPDY_CREDENTIAL_FRAME_CORRUPT);
+ } else if (remaining_control_payload_ == 0) {
+ visitor_->OnCredentialFrameData(NULL, 0);
+ }
+ return len;
+}
+
size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) {
size_t original_len = len;
@@ -871,6 +910,39 @@ bool SpdyFramer::ParseSettings(const SpdySettingsControlFrame* frame,
return true;
}
+/* static */
+bool SpdyFramer::ParseCredentialData(const char* data, size_t len,
+ SpdyCredential* credential) {
+ DCHECK(credential);
+
+ void* iter = NULL;
+ SpdyFrameBuilder parser(data, len);
+ if (!parser.ReadUInt16(&iter, &credential->slot))
+ return false;
+
+ uint16 origin_len;
+ const char* origin_data;
+ if (!parser.ReadData(&iter, &origin_data, &origin_len))
+ return false;
+ credential->origin.assign(origin_data, origin_len);
+
+ uint32 proof_len;
+ const char* proof_data;
+ if (!parser.ReadReadLen32PrefixedData(&iter, &proof_data, &proof_len))
+ return false;
+ credential->proof.assign(proof_data, proof_len);
+
+ while (parser.IteratorHasRoomFor(iter, 1)) {
+ uint32 cert_len;
+ const char* cert_data;
+ if (!parser.ReadReadLen32PrefixedData(&iter, &cert_data, &cert_len))
+ return false;
+ credential->certs.push_back("");
+ credential->certs.back().assign(cert_data, cert_len);
+ }
+ return true;
+}
+
SpdySynStreamControlFrame* SpdyFramer::CreateSynStream(
SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority,
SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) {
@@ -1060,6 +1132,44 @@ SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate(
return reinterpret_cast<SpdyWindowUpdateControlFrame*>(frame.take());
}
+/* static */
+SpdyCredentialControlFrame* SpdyFramer::CreateCredentialFrame(
+ const SpdyCredential& credential) {
+ // Calculate the size of the frame by adding the size of the
+ // variable length data to the size of the fixed length data.
+ size_t frame_size = SpdyCredentialControlFrame::size() +
+ credential.origin.length() + credential.proof.length();
+ DCHECK_EQ(SpdyCredentialControlFrame::size(), 16u);
+ for (vector<std::string>::const_iterator cert = credential.certs.begin();
+ cert != credential.certs.end();
+ cert++) {
+ frame_size += sizeof(uint32); // size of the cert_length field
+ frame_size += cert->length(); // size of the cert_data field
+ }
+ size_t payload_size = frame_size - SpdyFrame::kHeaderSize;
+
+ SpdyFrameBuilder frame(frame_size);
+ // Create our FlagsAndLength.
+ SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
+ FlagsAndLength flags_length = CreateFlagsAndLength(flags, payload_size);
+
+ frame.WriteUInt16(kControlFlagMask | spdy_version_);
+ frame.WriteUInt16(CREDENTIAL);
+ frame.WriteBytes(&flags_length, sizeof(flags_length));
+ frame.WriteUInt16(credential.slot);
+ frame.WriteUInt16(credential.origin.size());
+ frame.WriteBytes(credential.origin.c_str(), credential.origin.size());
+ frame.WriteUInt32(credential.proof.size());
+ frame.WriteBytes(credential.proof.c_str(), credential.proof.size());
+ for (vector<std::string>::const_iterator cert = credential.certs.begin();
+ cert != credential.certs.end();
+ cert++) {
+ frame.WriteUInt32(cert->length());
+ frame.WriteBytes(cert->c_str(), cert->length());
+ }
+ return reinterpret_cast<SpdyCredentialControlFrame*>(frame.take());
+}
+
SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
const char* data,
uint32 len, SpdyDataFlags flags) {
@@ -1645,6 +1755,8 @@ size_t SpdyFramer::GetMinimumControlFrameSize(SpdyControlType type) {
return SpdyHeadersControlFrame::size();
case WINDOW_UPDATE:
return SpdyWindowUpdateControlFrame::size();
+ case CREDENTIAL:
+ return SpdyCredentialControlFrame::size();
case NUM_CONTROL_FRAME_TYPES:
break;
}
@@ -1686,6 +1798,7 @@ SpdyStreamId SpdyFramer::GetControlFrameStreamId(
case NOOP:
case PING:
case GOAWAY:
+ case CREDENTIAL:
case NUM_CONTROL_FRAME_TYPES: // makes compiler happy
break;
}
@@ -1712,6 +1825,7 @@ size_t SpdyFramer::BytesSafeToRead() const {
case SPDY_CONTROL_FRAME_HEADER_BLOCK:
return 0;
case SPDY_CONTROL_FRAME_PAYLOAD:
+ case SPDY_CREDENTIAL_FRAME_PAYLOAD:
case SPDY_IGNORE_REMAINING_PAYLOAD:
case SPDY_FORWARD_STREAM_FRAME:
return remaining_data_;
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index c1b47d9..1d4b000e 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -10,6 +10,7 @@
#include <map>
#include <string>
#include <utility>
+#include <vector>
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
@@ -51,6 +52,17 @@ typedef std::map<std::string, std::string> SpdyHeaderBlock;
typedef std::pair<spdy::SettingsFlagsAndId, uint32> SpdySetting;
typedef std::list<SpdySetting> SpdySettings;
+// A datastrcture for holding the contents of a CREDENTIAL frame.
+struct NET_EXPORT_PRIVATE SpdyCredential {
+ SpdyCredential();
+ ~SpdyCredential();
+
+ uint16 slot;
+ std::string origin;
+ std::vector<std::string> certs;
+ std::string proof;
+};
+
// SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer.
// Implement this interface to receive event callbacks as frames are
// decoded from the framer.
@@ -99,6 +111,18 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
const char* header_data,
size_t len) = 0;
+ // Called when a chunk of payload data for a credential frame is available.
+ // This is called after OnControl() is called with the credential frame
+ // associated with the payload being delivered here.
+ // |frame_data| A buffer containing the header data chunk received.
+ // |len| The length of the header data buffer. A length of zero indicates
+ // that the header data block has been completely sent.
+ // When this function returns true the visitor indicates that it accepted
+ // all of the data. Returning false indicates that that an unrecoverable
+ // error has occurred, such as bad header data or resource exhaustion.
+ virtual bool OnCredentialFrameData(const char* frame_data,
+ size_t len) = 0;
+
// Called when a data frame header is received. The frame's data
// payload will be provided via subsequent calls to
// OnStreamFrameData().
@@ -132,6 +156,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_FORWARD_STREAM_FRAME,
SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK,
SPDY_CONTROL_FRAME_HEADER_BLOCK,
+ SPDY_CREDENTIAL_FRAME_PAYLOAD,
};
// SPDY error codes.
@@ -143,6 +168,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_UNSUPPORTED_VERSION, // Control frame has unsupported version.
SPDY_DECOMPRESS_FAILURE, // There was an error decompressing.
SPDY_COMPRESS_FAILURE, // There was an error compressing.
+ SPDY_CREDENTIAL_FRAME_CORRUPT, // CREDENTIAL frame could not be parsed.
LAST_ERROR, // Must be the last entry in the enum.
};
@@ -260,10 +286,23 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SpdyStreamId stream_id,
uint32 delta_window_size);
+ // Creates an instance of SpdyCredentialControlFrame. The CREDENTIAL
+ // frame is used to send a client certificate to the server when
+ // request more than one origin are sent over the same SPDY session.
+ static SpdyCredentialControlFrame* CreateCredentialFrame(
+ const SpdyCredential& credential);
+
// Given a SpdySettingsControlFrame, extract the settings.
// Returns true on successful parse, false otherwise.
static bool ParseSettings(const SpdySettingsControlFrame* frame,
- SpdySettings* settings);
+ SpdySettings* settings);
+
+ // Given a SpdyCredentialControlFrame's payload, extract the credential.
+ // Returns true on successful parse, false otherwise.
+ // TODO(hkhalil): Implement CREDENTIAL frame parsing in SpdyFramer
+ // and eliminate this method.
+ static bool ParseCredentialData(const char* data, size_t len,
+ SpdyCredential* credential);
// Create a data frame.
// |stream_id| is the stream for this frame
@@ -370,6 +409,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t ProcessCommonHeader(const char* data, size_t len);
void ProcessControlFrameHeader();
size_t ProcessControlFramePayload(const char* data, size_t len);
+ size_t ProcessCredentialFramePayload(const char* data, size_t len);
size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len);
size_t ProcessControlFrameHeaderBlock(const char* data, size_t len);
size_t OldProcessControlFrameHeaderBlock(const char* data, size_t len);
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index 60d2731..c6fccb3 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -86,6 +86,7 @@ void CompareCharArraysWithHexError(
class TestSpdyVisitor : public SpdyFramerVisitorInterface {
public:
static const size_t kDefaultHeaderBufferSize = 64 * 1024;
+ static const size_t kDefaultCredentialBufferSize = 16 * 1024;
TestSpdyVisitor()
: use_compression_(false),
@@ -94,6 +95,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
syn_reply_frame_count_(0),
headers_frame_count_(0),
goaway_count_(0),
+ credential_count_(0),
data_bytes_(0),
fin_frame_count_(0),
fin_flag_count_(0),
@@ -107,7 +109,10 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
header_buffer_size_(kDefaultHeaderBufferSize),
header_stream_id_(-1),
header_control_type_(NUM_CONTROL_FRAME_TYPES),
- header_buffer_valid_(false) {
+ header_buffer_valid_(false),
+ credential_buffer_(new char[kDefaultCredentialBufferSize]),
+ credential_buffer_length_(0),
+ credential_buffer_size_(kDefaultCredentialBufferSize) {
}
void OnError(SpdyFramer* f) {
@@ -158,6 +163,9 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
case GOAWAY:
goaway_count_++;
break;
+ case CREDENTIAL:
+ credential_count_++;
+ break;
default:
DLOG(FATAL); // Error!
}
@@ -190,6 +198,26 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
return true;
}
+ bool OnCredentialFrameData(const char* credential_data, size_t len) {
+ if (len == 0) {
+ if (!framer_.ParseCredentialData(credential_buffer_.get(),
+ credential_buffer_length_,
+ &credential_)) {
+ ++error_count_;
+ }
+ return true;
+ }
+ const size_t available =
+ credential_buffer_size_ - credential_buffer_length_;
+ if (len > available) {
+ return false;
+ }
+ memcpy(credential_buffer_.get() + credential_buffer_length_,
+ credential_data, len);
+ credential_buffer_length_ += len;
+ return true;
+ }
+
// Convenience function which runs a framer simulation with particular input.
void SimulateInFramer(const unsigned char* input, size_t size) {
framer_.set_enable_compression(use_compression_);
@@ -244,6 +272,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
int syn_reply_frame_count_;
int headers_frame_count_;
int goaway_count_;
+ int credential_count_;
int data_bytes_;
int fin_frame_count_; // The count of RST_STREAM type frames received.
int fin_flag_count_; // The count of frames with the FIN flag set.
@@ -262,6 +291,11 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
SpdyControlType header_control_type_;
bool header_buffer_valid_;
SpdyHeaderBlock headers_;
+
+ scoped_array<char> credential_buffer_;
+ size_t credential_buffer_length_;
+ size_t credential_buffer_size_;
+ SpdyCredential credential_;
};
} // namespace test
@@ -1586,6 +1620,86 @@ TEST_F(SpdyFramerTest, CreateWindowUpdate) {
}
}
+TEST_F(SpdyFramerTest, CreateCredential) {
+ SpdyFramer framer;
+
+ {
+ const char kDescription[] = "CREDENTIAL frame";
+ const unsigned char kFrameData[] = {
+ 0x80, 0x02, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x3F,
+ 0x00, 0x03, 0x00, 0x0A,
+ 'g', 'o', 'o', 'g',
+ 'l', 'e', '.', 'c',
+ 'o', 'm', 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.origin = "google.com";
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+}
+
+TEST_F(SpdyFramerTest, ParseCredentialFrame) {
+ SpdyFramer framer;
+
+ {
+ unsigned char kFrameData[] = {
+ 0x80, 0x02, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x3F,
+ 0x00, 0x03, 0x00, 0x0A,
+ 'g', 'o', 'o', 'g',
+ 'l', 'e', '.', 'c',
+ 'o', 'm', 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
+ SpdyCredentialControlFrame frame(reinterpret_cast<char*>(kFrameData),
+ false);
+ SpdyCredential credential;
+ EXPECT_TRUE(SpdyFramer::ParseCredentialData(frame.payload(),
+ frame.length(),
+ &credential));
+ EXPECT_EQ(3u, credential.slot);
+ EXPECT_EQ("google.com", credential.origin);
+ EXPECT_EQ("proof", credential.proof);
+ EXPECT_EQ("a cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_EQ("another cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_EQ("final cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_TRUE(credential.certs.empty());
+ }
+}
+
TEST_F(SpdyFramerTest, DuplicateFrame) {
SpdyFramer framer;
@@ -1637,6 +1751,106 @@ TEST_F(SpdyFramerTest, ExpandBuffer_HeapSmash) {
}
}
+TEST_F(SpdyFramerTest, ReadCredentialFrame) {
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.origin = "google.com";
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ SpdyFramer framer;
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreateCredentialFrame(credential));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame.get()->data()),
+ control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.credential_count_);
+ EXPECT_EQ(control_frame->length(), visitor.credential_buffer_length_);
+ EXPECT_EQ(credential.slot, visitor.credential_.slot);
+ EXPECT_EQ(credential.origin, visitor.credential_.origin);
+ EXPECT_EQ(credential.proof, visitor.credential_.proof);
+ EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size());
+ for (size_t i = 0; i < credential.certs.size(); i++) {
+ EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]);
+ }
+}
+
+TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptOrigin) {
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.origin = "google.com";
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ SpdyFramer framer;
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreateCredentialFrame(credential));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ unsigned char* data =
+ reinterpret_cast<unsigned char*>(control_frame.get()->data());
+ // Origin length is past the end of the frame
+ data[SpdyControlFrame::kHeaderSize + 2] = 0xFF;
+ visitor.SimulateInFramer(
+ data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptProof) {
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.origin = "google.com";
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ SpdyFramer framer;
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreateCredentialFrame(credential));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ unsigned char* data =
+ reinterpret_cast<unsigned char*>(control_frame.get()->data());
+ size_t offset = SpdyControlFrame::kHeaderSize + 4 +
+ credential.origin.length();
+ data[offset] = 0xFF; // Proof length is past the end of the frame
+ visitor.SimulateInFramer(
+ data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptCertificate) {
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.origin = "google.com";
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ SpdyFramer framer;
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreateCredentialFrame(credential));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ unsigned char* data =
+ reinterpret_cast<unsigned char*>(control_frame.get()->data());
+ size_t offset = SpdyControlFrame::kHeaderSize + 4 +
+ credential.origin.length() + 4 + credential.proof.length();
+ data[offset] = 0xFF; // Certificate length is past the end of the frame
+ visitor.SimulateInFramer(
+ data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
TEST_F(SpdyFramerTest, ReadGarbage) {
SpdyFramer framer;
unsigned char garbage_frame[256];
@@ -1691,9 +1905,12 @@ TEST(SpdyFramer, StateToStringTest) {
EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK",
SpdyFramer::StateToString(
SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK));
+ EXPECT_STREQ("SPDY_CREDENTIAL_FRAME_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD));
EXPECT_STREQ("UNKNOWN_STATE",
SpdyFramer::StateToString(
- SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK + 1));
+ SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD + 1));
}
TEST(SpdyFramer, ErrorCodeToStringTest) {
@@ -1761,6 +1978,8 @@ TEST(SpdyFramer, ControlTypeToStringTest) {
SpdyFramer::ControlTypeToString(HEADERS));
EXPECT_STREQ("WINDOW_UPDATE",
SpdyFramer::ControlTypeToString(WINDOW_UPDATE));
+ EXPECT_STREQ("SETTINGS",
+ SpdyFramer::ControlTypeToString(SETTINGS));
EXPECT_STREQ("UNKNOWN_CONTROL_TYPE",
SpdyFramer::ControlTypeToString(NUM_CONTROL_FRAME_TYPES));
}
@@ -1784,6 +2003,8 @@ TEST(SpdyFramer, GetMinimumControlFrameSizeTest) {
SpdyFramer::GetMinimumControlFrameSize(HEADERS));
EXPECT_EQ(SpdyWindowUpdateControlFrame::size(),
SpdyFramer::GetMinimumControlFrameSize(WINDOW_UPDATE));
+ EXPECT_EQ(SpdyCredentialControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(CREDENTIAL));
EXPECT_EQ(static_cast<size_t>(0x7FFFFFFF),
SpdyFramer::GetMinimumControlFrameSize(NUM_CONTROL_FRAME_TYPES));
}
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index f2b5715..21c1d97 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -124,6 +124,25 @@
// | Delta-Window-Size (32 bits) |
// +----------------------------------+
//
+// Control Frame: CREDENTIAL
+// +----------------------------------+
+// |1|000000000000001|0000000000001010|
+// +----------------------------------+
+// | flags (8) | Length (24 bits) | >= 12
+// +----------------------------------+
+// | Slot (16 bits) | Origin Len (16)|
+// +----------------------------------+
+// | Origin |
+// +----------------------------------+
+// | Proof Length (32 bits) |
+// +----------------------------------+
+// | Proof |
+// +----------------------------------+ <+
+// | Certificate Length (32 bits) | |
+// +----------------------------------+ | Repeated until end of frame
+// | Certificate | |
+// +----------------------------------+ <+
+//
namespace spdy {
// This implementation of Spdy is version 2; It's like version 1, with some
@@ -160,6 +179,7 @@ enum SpdyControlType {
GOAWAY,
HEADERS,
WINDOW_UPDATE,
+ CREDENTIAL,
NUM_CONTROL_FRAME_TYPES
};
@@ -291,6 +311,17 @@ struct SpdyPingControlFrameBlock : SpdyFrameBlock {
uint32 unique_id_;
};
+// A CREDENTIAL Control Frame structure.
+struct SpdyCredentialControlFrameBlock : SpdyFrameBlock {
+ uint16 slot_;
+ uint16 origin_len_;
+ uint32 proof_len_;
+ // Variable data here.
+ // origin data
+ // proof data
+ // for each certificate: unit32 certificate_len + certificate_data[i]
+};
+
// A GOAWAY Control Frame structure.
struct SpdyGoAwayControlFrameBlock : SpdyFrameBlock {
SpdyStreamId last_accepted_stream_id_;
@@ -687,6 +718,25 @@ class SpdyPingControlFrame : public SpdyControlFrame {
}
};
+class SpdyCredentialControlFrame : public SpdyControlFrame {
+ public:
+ SpdyCredentialControlFrame() : SpdyControlFrame(size()) {}
+ SpdyCredentialControlFrame(char* data, bool owns_buffer)
+ : SpdyControlFrame(data, owns_buffer) {}
+
+ const char* payload() const {
+ return reinterpret_cast<const char*>(block()) + SpdyFrame::kHeaderSize;
+ }
+
+ static size_t size() { return sizeof(SpdyCredentialControlFrameBlock); }
+
+ private:
+ const struct SpdyCredentialControlFrameBlock* block() const {
+ return static_cast<SpdyCredentialControlFrameBlock*>(frame_);
+ }
+ DISALLOW_COPY_AND_ASSIGN(SpdyCredentialControlFrame);
+};
+
class SpdyGoAwayControlFrame : public SpdyControlFrame {
public:
SpdyGoAwayControlFrame() : SpdyControlFrame(size()) {}
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 6c0a584..de2a198 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -1352,6 +1352,12 @@ bool SpdySession::OnControlFrameHeaderData(
return true;
}
+bool SpdySession::OnCredentialFrameData(const char* frame_data,
+ size_t len) {
+ DCHECK(false);
+ return false;
+}
+
void SpdySession::OnDataFrameHeader(const spdy::SpdyDataFrame* frame) {
buffered_spdy_framer_.OnDataFrameHeader(frame);
}
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index fcdb193..2fc6621 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -309,6 +309,7 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
void OnPing(const spdy::SpdyPingControlFrame& frame);
void OnSettings(const spdy::SpdySettingsControlFrame& frame);
void OnWindowUpdate(const spdy::SpdyWindowUpdateControlFrame& frame);
+ void OnCredential(const spdy::SpdyCredentialControlFrame& frame);
// IO Callbacks
void OnReadComplete(int result);
@@ -318,7 +319,7 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
void SendSettings();
// Handle SETTINGS. Either when we send settings, or when we receive a
- // SETTINGS ontrol frame, update our SpdySession accordingly.
+ // SETTINGS control frame, update our SpdySession accordingly.
void HandleSettings(const spdy::SpdySettings& settings);
// Send the PING (preface-PING and trailing-PING) frames.
@@ -404,6 +405,9 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
const char* header_data,
size_t len) OVERRIDE;
+ virtual bool OnCredentialFrameData(const char* frame_data,
+ size_t len) OVERRIDE;
+
virtual void OnDataFrameHeader(const spdy::SpdyDataFrame* frame) OVERRIDE;
virtual void OnSyn(const spdy::SpdySynStreamControlFrame& frame,
diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc
index 6e76e5f..8f7a687 100644
--- a/net/tools/flip_server/spdy_interface.cc
+++ b/net/tools/flip_server/spdy_interface.cc
@@ -309,6 +309,11 @@ void SpdySM::OnStreamFrameData(SpdyStreamId stream_id,
interface->ProcessWriteInput(data, len);
}
+bool SpdySM::OnCredentialFrameData(const char* frame_data,
+ size_t len) {
+ return false;
+}
+
size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
return spdy_framer_->ProcessInput(data, len);
}
@@ -611,4 +616,3 @@ void SpdySM::GetOutput() {
}
} // namespace net
-
diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h
index 5598070..64562cc 100644
--- a/net/tools/flip_server/spdy_interface.h
+++ b/net/tools/flip_server/spdy_interface.h
@@ -69,6 +69,8 @@ class SpdySM : public spdy::SpdyFramerVisitorInterface,
virtual void OnDataFrameHeader(const spdy::SpdyDataFrame* frame) OVERRIDE;
virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id,
const char* data, size_t len) OVERRIDE;
+ virtual bool OnCredentialFrameData(const char* frame_data,
+ size_t len) OVERRIDE;
public:
virtual size_t ProcessReadInput(const char* data, size_t len) OVERRIDE;