blob: 1d586692094ab8dbbe970f49f39a4cbdacc3091e [file] [log] [blame]
#ifndef QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_
#define QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "http2/adapter/data_source.h"
#include "http2/adapter/http2_protocol.h"
#include "http2/adapter/mock_http2_visitor.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
#include "common/platform/api/quiche_test.h"
#include "spdy/core/spdy_protocol.h"
namespace http2 {
namespace adapter {
namespace test {
class DataSavingVisitor : public testing::StrictMock<MockHttp2Visitor> {
public:
ssize_t OnReadyToSend(absl::string_view data) override {
if (is_write_blocked_) {
return 0;
}
const size_t to_accept = std::min(send_limit_, data.size());
absl::StrAppend(&data_, data.substr(0, to_accept));
return to_accept;
}
const std::string& data() { return data_; }
void Clear() { data_.clear(); }
void set_send_limit(size_t limit) { send_limit_ = limit; }
bool is_write_blocked() const { return is_write_blocked_; }
void set_is_write_blocked(bool value) { is_write_blocked_ = value; }
private:
std::string data_;
size_t send_limit_ = std::numeric_limits<size_t>::max();
bool is_write_blocked_ = false;
};
// A test DataFrameSource that can be initialized with a single string payload,
// or a chunked payload.
class TestDataFrameSource : public DataFrameSource {
public:
TestDataFrameSource(Http2VisitorInterface& visitor,
absl::string_view data_payload,
bool has_fin = true);
TestDataFrameSource(Http2VisitorInterface& visitor,
absl::Span<absl::string_view> payload_fragments,
bool has_fin = true);
std::pair<ssize_t, bool> SelectPayloadLength(size_t max_length) override;
bool Send(absl::string_view frame_header, size_t payload_length) override;
bool send_fin() const override { return has_fin_; }
void set_is_data_available(bool value) { is_data_available_ = value; }
private:
Http2VisitorInterface& visitor_;
std::vector<std::string> payload_fragments_;
absl::string_view current_fragment_;
const bool has_fin_;
bool is_data_available_ = true;
};
// A simple class that can easily be adapted to act as a nghttp2_data_source.
class TestDataSource {
public:
explicit TestDataSource(absl::string_view data) : data_(std::string(data)) {}
absl::string_view ReadNext(size_t size) {
const size_t to_send = std::min(size, remaining_.size());
auto ret = remaining_.substr(0, to_send);
remaining_.remove_prefix(to_send);
return ret;
}
size_t SelectPayloadLength(size_t max_length) {
return std::min(max_length, remaining_.size());
}
nghttp2_data_provider MakeDataProvider() {
return nghttp2_data_provider{
.source = {.ptr = this},
.read_callback = [](nghttp2_session*, int32_t, uint8_t*, size_t length,
uint32_t* data_flags, nghttp2_data_source* source,
void*) -> ssize_t {
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
auto* s = static_cast<TestDataSource*>(source->ptr);
if (!s->is_data_available()) {
return NGHTTP2_ERR_DEFERRED;
}
const ssize_t ret = s->SelectPayloadLength(length);
if (ret < length) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
return ret;
}};
}
bool is_data_available() const { return is_data_available_; }
void set_is_data_available(bool value) { is_data_available_ = value; }
private:
const std::string data_;
absl::string_view remaining_ = data_;
bool is_data_available_ = true;
};
// These matchers check whether a string consists entirely of HTTP/2 frames of
// the specified ordered sequence. This is useful in tests where we want to show
// that one or more particular frame types are serialized for sending to the
// peer. The match will fail if there are input bytes not consumed by the
// matcher.
// Requires that frames match both types and lengths.
testing::Matcher<absl::string_view> EqualsFrames(
std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>>
types_and_lengths);
// Requires that frames match the specified types.
testing::Matcher<absl::string_view> EqualsFrames(
std::vector<spdy::SpdyFrameType> types);
testing::Matcher<const nghttp2_frame_hd*> HasFrameHeader(
uint32_t streamid,
uint8_t type,
const testing::Matcher<int> flags);
testing::Matcher<const nghttp2_frame*> IsData(
const testing::Matcher<uint32_t> stream_id,
const testing::Matcher<size_t> length,
const testing::Matcher<int> flags);
testing::Matcher<const nghttp2_frame*> IsHeaders(
const testing::Matcher<uint32_t> stream_id,
const testing::Matcher<int> flags,
const testing::Matcher<int> category);
testing::Matcher<const nghttp2_frame*> IsRstStream(
const testing::Matcher<uint32_t> stream_id,
const testing::Matcher<uint32_t> error_code);
testing::Matcher<const nghttp2_frame*> IsSettings(
const testing::Matcher<std::vector<Http2Setting>> values);
testing::Matcher<const nghttp2_frame*> IsPing(
const testing::Matcher<uint64_t> id);
testing::Matcher<const nghttp2_frame*> IsPingAck(
const testing::Matcher<uint64_t> id);
testing::Matcher<const nghttp2_frame*> IsGoAway(
const testing::Matcher<uint32_t> last_stream_id,
const testing::Matcher<uint32_t> error_code,
const testing::Matcher<absl::string_view> opaque_data);
testing::Matcher<const nghttp2_frame*> IsWindowUpdate(
const testing::Matcher<uint32_t> delta);
} // namespace test
} // namespace adapter
} // namespace http2
#endif // QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_