blob: 8cad139ee95b040cb320211848de34779d061196 [file] [log] [blame]
#include "http2/adapter/test_frame_sequence.h"
#include "http2/adapter/http2_util.h"
#include "http2/adapter/oghttp2_util.h"
#include "spdy/core/hpack/hpack_encoder.h"
#include "spdy/core/spdy_framer.h"
namespace http2 {
namespace adapter {
namespace test {
std::vector<const Header> ToHeaders(
absl::Span<const std::pair<absl::string_view, absl::string_view>> headers) {
std::vector<const Header> out;
for (const auto& header : headers) {
out.push_back(
std::make_pair(HeaderRep(header.first), HeaderRep(header.second)));
}
return out;
}
TestFrameSequence& TestFrameSequence::ClientPreface(
absl::Span<const Http2Setting> settings) {
preface_ = spdy::kHttp2ConnectionHeaderPrefix;
return Settings(settings);
}
TestFrameSequence& TestFrameSequence::ServerPreface(
absl::Span<const Http2Setting> settings) {
return Settings(settings);
}
TestFrameSequence& TestFrameSequence::Data(Http2StreamId stream_id,
absl::string_view payload,
bool fin,
absl::optional<int> padding_length) {
auto data = absl::make_unique<spdy::SpdyDataIR>(stream_id, payload);
data->set_fin(fin);
if (padding_length) {
data->set_padding_len(padding_length.value());
}
frames_.push_back(std::move(data));
return *this;
}
TestFrameSequence& TestFrameSequence::RstStream(Http2StreamId stream_id,
Http2ErrorCode error) {
frames_.push_back(absl::make_unique<spdy::SpdyRstStreamIR>(
stream_id, TranslateErrorCode(error)));
return *this;
}
TestFrameSequence& TestFrameSequence::Settings(
absl::Span<const Http2Setting> settings) {
auto settings_frame = absl::make_unique<spdy::SpdySettingsIR>();
for (const Http2Setting& setting : settings) {
settings_frame->AddSetting(setting.id, setting.value);
}
frames_.push_back(std::move(settings_frame));
return *this;
}
TestFrameSequence& TestFrameSequence::SettingsAck() {
auto settings = absl::make_unique<spdy::SpdySettingsIR>();
settings->set_is_ack(true);
frames_.push_back(std::move(settings));
return *this;
}
TestFrameSequence& TestFrameSequence::PushPromise(
Http2StreamId stream_id, Http2StreamId promised_stream_id,
absl::Span<const Header> headers) {
frames_.push_back(absl::make_unique<spdy::SpdyPushPromiseIR>(
stream_id, promised_stream_id, ToHeaderBlock(headers)));
return *this;
}
TestFrameSequence& TestFrameSequence::Ping(Http2PingId id) {
frames_.push_back(absl::make_unique<spdy::SpdyPingIR>(id));
return *this;
}
TestFrameSequence& TestFrameSequence::PingAck(Http2PingId id) {
auto ping = absl::make_unique<spdy::SpdyPingIR>(id);
ping->set_is_ack(true);
frames_.push_back(std::move(ping));
return *this;
}
TestFrameSequence& TestFrameSequence::GoAway(Http2StreamId last_good_stream_id,
Http2ErrorCode error,
absl::string_view payload) {
frames_.push_back(absl::make_unique<spdy::SpdyGoAwayIR>(
last_good_stream_id, TranslateErrorCode(error), std::string(payload)));
return *this;
}
TestFrameSequence& TestFrameSequence::Headers(
Http2StreamId stream_id,
absl::Span<const std::pair<absl::string_view, absl::string_view>> headers,
bool fin, bool add_continuation) {
return Headers(stream_id, ToHeaders(headers), fin, add_continuation);
}
TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id,
spdy::Http2HeaderBlock block,
bool fin, bool add_continuation) {
if (add_continuation) {
// The normal intermediate representations don't allow you to represent a
// nonterminal HEADERS frame explicitly, so we'll need to use
// SpdyUnknownIRs. For simplicity, and in order not to mess up HPACK state,
// the payload will be uncompressed.
spdy::HpackEncoder encoder;
encoder.DisableCompression();
std::string encoded_block = encoder.EncodeHeaderBlock(block);
const size_t pos = encoded_block.size() / 2;
const uint8_t flags = fin ? 0x1 : 0x0;
frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
stream_id, static_cast<uint8_t>(spdy::SpdyFrameType::HEADERS), flags,
encoded_block.substr(0, pos)));
auto continuation = absl::make_unique<spdy::SpdyContinuationIR>(stream_id);
continuation->set_end_headers(true);
continuation->take_encoding(encoded_block.substr(pos));
frames_.push_back(std::move(continuation));
} else {
auto headers =
absl::make_unique<spdy::SpdyHeadersIR>(stream_id, std::move(block));
headers->set_fin(fin);
frames_.push_back(std::move(headers));
}
return *this;
}
TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id,
absl::Span<const Header> headers,
bool fin, bool add_continuation) {
return Headers(stream_id, ToHeaderBlock(headers), fin, add_continuation);
}
TestFrameSequence& TestFrameSequence::WindowUpdate(Http2StreamId stream_id,
int32_t delta) {
frames_.push_back(
absl::make_unique<spdy::SpdyWindowUpdateIR>(stream_id, delta));
return *this;
}
TestFrameSequence& TestFrameSequence::Priority(Http2StreamId stream_id,
Http2StreamId parent_stream_id,
int weight,
bool exclusive) {
frames_.push_back(absl::make_unique<spdy::SpdyPriorityIR>(
stream_id, parent_stream_id, weight, exclusive));
return *this;
}
TestFrameSequence& TestFrameSequence::Metadata(Http2StreamId stream_id,
absl::string_view payload,
bool multiple_frames) {
const std::string encoded_payload = MetadataBlockForPayload(payload);
if (multiple_frames) {
const size_t pos = encoded_payload.size() / 2;
frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
stream_id, kMetadataFrameType, 0, encoded_payload.substr(0, pos)));
frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
stream_id, kMetadataFrameType, kMetadataEndFlag,
encoded_payload.substr(pos)));
} else {
frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>(
stream_id, kMetadataFrameType, kMetadataEndFlag,
std::move(encoded_payload)));
}
return *this;
}
std::string TestFrameSequence::Serialize() {
std::string result;
if (!preface_.empty()) {
result = preface_;
}
spdy::SpdyFramer framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
for (const auto& frame : frames_) {
spdy::SpdySerializedFrame f = framer.SerializeFrame(*frame);
absl::StrAppend(&result, absl::string_view(f));
}
return result;
}
std::string TestFrameSequence::MetadataBlockForPayload(
absl::string_view payload) {
// Encode the payload using a header block.
spdy::SpdyHeaderBlock block;
block["example-payload"] = payload;
spdy::HpackEncoder encoder;
encoder.DisableCompression();
return encoder.EncodeHeaderBlock(block);
}
} // namespace test
} // namespace adapter
} // namespace http2