Add ACCEPT_CH frame and implement serialization.
PiperOrigin-RevId: 355853425
Change-Id: Iec612eaf5b10fdbb80a0f6c1a098c003354a5973
diff --git a/spdy/core/spdy_framer.cc b/spdy/core/spdy_framer.cc
index 22d3fff..e34b068 100644
--- a/spdy/core/spdy_framer.cc
+++ b/spdy/core/spdy_framer.cc
@@ -780,6 +780,24 @@
return builder.take();
}
+SpdySerializedFrame SpdyFramer::SerializeAcceptCh(
+ const SpdyAcceptChIR& accept_ch) const {
+ const size_t total_size = accept_ch.size();
+ SpdyFrameBuilder builder(total_size);
+ builder.BeginNewFrame(SpdyFrameType::ACCEPT_CH, kNoFlags,
+ accept_ch.stream_id());
+
+ for (const SpdyAcceptChIR::OriginValuePair& entry : accept_ch.entries()) {
+ builder.WriteUInt16(entry.origin.size());
+ builder.WriteBytes(entry.origin.data(), entry.origin.size());
+ builder.WriteUInt16(entry.value.size());
+ builder.WriteBytes(entry.value.data(), entry.value.size());
+ }
+
+ QUICHE_DCHECK_EQ(total_size, builder.length());
+ return builder.take();
+}
+
SpdySerializedFrame SpdyFramer::SerializeUnknown(
const SpdyUnknownIR& unknown) const {
const size_t total_size = kFrameHeaderSize + unknown.payload().size();
@@ -837,6 +855,9 @@
const SpdyPriorityUpdateIR& priority_update) override {
frame_ = framer_->SerializePriorityUpdate(priority_update);
}
+ void VisitAcceptCh(const SpdyAcceptChIR& accept_ch) override {
+ frame_ = framer_->SerializeAcceptCh(accept_ch);
+ }
void VisitUnknown(const SpdyUnknownIR& unknown) override {
frame_ = framer_->SerializeUnknown(unknown);
}
@@ -928,6 +949,10 @@
flags_ = kNoFlags;
}
+ void VisitAcceptCh(const SpdyAcceptChIR& /*accept_ch*/) override {
+ flags_ = kNoFlags;
+ }
+
uint8_t flags() const { return flags_; }
private:
@@ -1231,6 +1256,24 @@
return ok;
}
+bool SpdyFramer::SerializeAcceptCh(const SpdyAcceptChIR& accept_ch,
+ ZeroCopyOutputBuffer* output) const {
+ const size_t total_size = accept_ch.size();
+ SpdyFrameBuilder builder(total_size, output);
+ bool ok = builder.BeginNewFrame(SpdyFrameType::ACCEPT_CH, kNoFlags,
+ accept_ch.stream_id());
+
+ for (const SpdyAcceptChIR::OriginValuePair& entry : accept_ch.entries()) {
+ ok = ok && builder.WriteUInt16(entry.origin.size());
+ ok = ok && builder.WriteBytes(entry.origin.data(), entry.origin.size());
+ ok = ok && builder.WriteUInt16(entry.value.size());
+ ok = ok && builder.WriteBytes(entry.value.data(), entry.value.size());
+ }
+
+ QUICHE_DCHECK_EQ(total_size, builder.length());
+ return ok;
+}
+
bool SpdyFramer::SerializeUnknown(const SpdyUnknownIR& unknown,
ZeroCopyOutputBuffer* output) const {
const size_t total_size = kFrameHeaderSize + unknown.payload().size();
@@ -1290,6 +1333,10 @@
const SpdyPriorityUpdateIR& priority_update) override {
result_ = framer_->SerializePriorityUpdate(priority_update, output_);
}
+ void VisitAcceptCh(const SpdyAcceptChIR& accept_ch) override {
+ result_ = framer_->SerializeAcceptCh(accept_ch, output_);
+ }
+
void VisitUnknown(const SpdyUnknownIR& unknown) override {
result_ = framer_->SerializeUnknown(unknown, output_);
}
diff --git a/spdy/core/spdy_framer.h b/spdy/core/spdy_framer.h
index 260a5df..d7a81c1 100644
--- a/spdy/core/spdy_framer.h
+++ b/spdy/core/spdy_framer.h
@@ -129,6 +129,10 @@
SpdySerializedFrame SerializePriorityUpdate(
const SpdyPriorityUpdateIR& priority_update) const;
+ // Serializes an ACCEPT_CH frame. See
+ // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02.
+ SpdySerializedFrame SerializeAcceptCh(const SpdyAcceptChIR& accept_ch) const;
+
// Serializes an unknown frame given a frame header and payload.
SpdySerializedFrame SerializeUnknown(const SpdyUnknownIR& unknown) const;
@@ -202,6 +206,11 @@
bool SerializePriorityUpdate(const SpdyPriorityUpdateIR& priority_update,
ZeroCopyOutputBuffer* output) const;
+ // Serializes an ACCEPT_CH frame. See
+ // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02.
+ bool SerializeAcceptCh(const SpdyAcceptChIR& accept_ch,
+ ZeroCopyOutputBuffer* output) const;
+
// Serializes an unknown frame given a frame header and payload.
bool SerializeUnknown(const SpdyUnknownIR& unknown,
ZeroCopyOutputBuffer* output) const;
diff --git a/spdy/core/spdy_framer_test.cc b/spdy/core/spdy_framer_test.cc
index 53bfc2c..96b9f4c 100644
--- a/spdy/core/spdy_framer_test.cc
+++ b/spdy/core/spdy_framer_test.cc
@@ -2544,6 +2544,36 @@
CompareFrame(kDescription, frame, kFrameData, ABSL_ARRAYSIZE(kFrameData));
}
+TEST_P(SpdyFramerTest, CreateAcceptCh) {
+ const char kDescription[] = "ACCEPT_CH frame";
+ const unsigned char kType = SerializeFrameType(SpdyFrameType::ACCEPT_CH);
+ const unsigned char kFrameData[] = {
+ 0x00, 0x00, 0x2d, // frame length
+ kType, // frame type
+ 0x00, // flags
+ 0x00, 0x00, 0x00, 0x00, // stream ID, must be 0 for ACCEPT_CH
+ 0x00, 0x0f, // origin length
+ 'w', 'w', 'w', '.', 'e', 'x', // origin
+ 'a', 'm', 'p', 'l', 'e', '.', //
+ 'c', 'o', 'm', //
+ 0x00, 0x03, // value length
+ 'f', 'o', 'o', // value
+ 0x00, 0x10, // origin length
+ 'm', 'a', 'i', 'l', '.', 'e', //
+ 'x', 'a', 'm', 'p', 'l', 'e', //
+ '.', 'c', 'o', 'm', //
+ 0x00, 0x03, // value length
+ 'b', 'a', 'r'}; // value
+ SpdyAcceptChIR accept_ch_ir(
+ {{"www.example.com", "foo"}, {"mail.example.com", "bar"}});
+ SpdySerializedFrame frame(framer_.SerializeFrame(accept_ch_ir));
+ if (use_output_) {
+ EXPECT_EQ(framer_.SerializeFrame(accept_ch_ir, &output_), frame.size());
+ frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false);
+ }
+ CompareFrame(kDescription, frame, kFrameData, ABSL_ARRAYSIZE(kFrameData));
+}
+
TEST_P(SpdyFramerTest, CreateUnknown) {
const char kDescription[] = "Unknown frame";
const uint8_t kType = 0xaf;
diff --git a/spdy/core/spdy_protocol.cc b/spdy/core/spdy_protocol.cc
index 2d627d8..2f66ba7 100644
--- a/spdy/core/spdy_protocol.cc
+++ b/spdy/core/spdy_protocol.cc
@@ -85,6 +85,8 @@
return true;
case SpdyFrameType::PRIORITY_UPDATE:
return true;
+ case SpdyFrameType::ACCEPT_CH:
+ return true;
}
return false;
}
@@ -153,6 +155,8 @@
return "ALTSVC";
case SpdyFrameType::PRIORITY_UPDATE:
return "PRIORITY_UPDATE";
+ case SpdyFrameType::ACCEPT_CH:
+ return "ACCEPT_CH";
}
return "UNKNOWN_FRAME_TYPE";
}
@@ -578,6 +582,23 @@
return kPriorityUpdateFrameMinimumSize + priority_field_value_.size();
}
+void SpdyAcceptChIR::Visit(SpdyFrameVisitor* visitor) const {
+ return visitor->VisitAcceptCh(*this);
+}
+
+SpdyFrameType SpdyAcceptChIR::frame_type() const {
+ return SpdyFrameType::ACCEPT_CH;
+}
+
+size_t SpdyAcceptChIR::size() const {
+ size_t total_size = kAcceptChFrameMinimumSize;
+ for (const OriginValuePair& entry : entries_) {
+ total_size += entry.origin.size() + entry.value.size() +
+ kAcceptChFramePerEntryOverhead;
+ }
+ return total_size;
+}
+
void SpdyUnknownIR::Visit(SpdyFrameVisitor* visitor) const {
return visitor->VisitUnknown(*this);
}
diff --git a/spdy/core/spdy_protocol.h b/spdy/core/spdy_protocol.h
index 8d5d3d8..635439b 100644
--- a/spdy/core/spdy_protocol.h
+++ b/spdy/core/spdy_protocol.h
@@ -100,6 +100,7 @@
// ALTSVC is a public extension.
ALTSVC = 0x0a,
PRIORITY_UPDATE = 0x10,
+ ACCEPT_CH = 0x89,
};
// Flags on data packets.
@@ -313,6 +314,11 @@
const size_t kGetAltSvcFrameMinimumSize = kFrameHeaderSize + 2;
// PRIORITY_UPDATE frame has prioritized_stream_id (4 octets) field.
const size_t kPriorityUpdateFrameMinimumSize = kFrameHeaderSize + 4;
+// ACCEPT_CH frame may have empty payload.
+const size_t kAcceptChFrameMinimumSize = kFrameHeaderSize;
+// Each ACCEPT_CH frame entry has a 16-bit origin length and a 16-bit value
+// length.
+const size_t kAcceptChFramePerEntryOverhead = 4;
// Maximum possible configurable size of a frame in octets.
const size_t kMaxFrameSizeLimit = kSpdyMaxFrameSizeLimit + kFrameHeaderSize;
@@ -915,6 +921,30 @@
std::string priority_field_value_;
};
+class QUICHE_EXPORT_PRIVATE SpdyAcceptChIR : public SpdyFrameIR {
+ public:
+ struct OriginValuePair {
+ std::string origin;
+ std::string value;
+ };
+
+ SpdyAcceptChIR(std::vector<OriginValuePair> entries)
+ : entries_(std::move(entries)) {}
+ SpdyAcceptChIR(const SpdyAcceptChIR&) = delete;
+ SpdyAcceptChIR& operator=(const SpdyAcceptChIR&) = delete;
+
+ void Visit(SpdyFrameVisitor* visitor) const override;
+
+ SpdyFrameType frame_type() const override;
+
+ size_t size() const override;
+
+ const std::vector<OriginValuePair>& entries() const { return entries_; }
+
+ private:
+ std::vector<OriginValuePair> entries_;
+};
+
// Represents a frame of unrecognized type.
class QUICHE_EXPORT_PRIVATE SpdyUnknownIR : public SpdyFrameIR {
public:
@@ -1056,6 +1086,7 @@
virtual void VisitData(const SpdyDataIR& data) = 0;
virtual void VisitPriorityUpdate(
const SpdyPriorityUpdateIR& priority_update) = 0;
+ virtual void VisitAcceptCh(const SpdyAcceptChIR& accept_ch) = 0;
virtual void VisitUnknown(const SpdyUnknownIR& /*unknown*/) {
// TODO(birenroy): make abstract.
}