blob: 8f83da56f90d5483201b289a9d338a9799c31e1b [file] [log] [blame]
#include "http2/adapter/callback_visitor.h"
#include "http2/adapter/mock_nghttp2_callbacks.h"
#include "http2/adapter/nghttp2_test_utils.h"
#include "http2/adapter/test_utils.h"
#include "common/platform/api/quiche_test.h"
namespace http2 {
namespace adapter {
namespace test {
namespace {
using testing::_;
enum FrameType {
DATA,
HEADERS,
PRIORITY,
RST_STREAM,
SETTINGS,
PUSH_PROMISE,
PING,
GOAWAY,
WINDOW_UPDATE,
CONTINUATION,
};
// Tests connection-level events.
TEST(ClientCallbackVisitorUnitTest, ConnectionFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kClient,
*MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
// SETTINGS
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, SETTINGS, _)));
visitor.OnFrameHeader(0, 0, SETTINGS, 0);
visitor.OnSettingsStart();
EXPECT_CALL(callbacks, OnFrameRecv(IsSettings(testing::IsEmpty())));
visitor.OnSettingsEnd();
// PING
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, PING, _)));
visitor.OnFrameHeader(0, 8, PING, 0);
EXPECT_CALL(callbacks, OnFrameRecv(IsPing(42)));
visitor.OnPing(42, false);
// WINDOW_UPDATE
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, WINDOW_UPDATE, _)));
visitor.OnFrameHeader(0, 4, WINDOW_UPDATE, 0);
EXPECT_CALL(callbacks, OnFrameRecv(IsWindowUpdate(1000)));
visitor.OnWindowUpdate(0, 1000);
// PING ack
EXPECT_CALL(callbacks,
OnBeginFrame(HasFrameHeader(0, PING, NGHTTP2_FLAG_ACK)));
visitor.OnFrameHeader(0, 8, PING, 1);
EXPECT_CALL(callbacks, OnFrameRecv(IsPingAck(247)));
visitor.OnPing(247, true);
// GOAWAY
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, GOAWAY, 0)));
visitor.OnFrameHeader(0, 19, GOAWAY, 0);
EXPECT_CALL(callbacks, OnFrameRecv(IsGoAway(5, NGHTTP2_ENHANCE_YOUR_CALM,
"calm down!!")));
visitor.OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!");
EXPECT_EQ(visitor.stream_map_size(), 0);
}
TEST(ClientCallbackVisitorUnitTest, StreamFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kClient,
*MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
EXPECT_EQ(visitor.stream_map_size(), 0);
// HEADERS on stream 1
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, _)));
visitor.OnFrameHeader(1, 23, HEADERS, 4);
EXPECT_CALL(callbacks,
OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
visitor.OnBeginHeadersForStream(1);
EXPECT_EQ(visitor.stream_map_size(), 1);
EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _));
visitor.OnHeaderForStream(1, ":status", "200");
EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _));
visitor.OnHeaderForStream(1, "server", "my-fake-server");
EXPECT_CALL(callbacks,
OnHeader(_, "date", "Tue, 6 Apr 2021 12:54:01 GMT", _));
visitor.OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT");
EXPECT_CALL(callbacks, OnHeader(_, "trailer", "x-server-status", _));
visitor.OnHeaderForStream(1, "trailer", "x-server-status");
EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
visitor.OnEndHeadersForStream(1);
// DATA for stream 1
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, DATA, 0)));
visitor.OnFrameHeader(1, 26, DATA, 0);
visitor.OnBeginDataForStream(1, 26);
EXPECT_CALL(callbacks, OnDataChunkRecv(0, 1, "This is the response body."));
EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, 0)));
visitor.OnDataForStream(1, "This is the response body.");
// Trailers for stream 1, with a different nghttp2 "category".
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, _)));
visitor.OnFrameHeader(1, 23, HEADERS, 4);
EXPECT_CALL(callbacks, OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_HEADERS)));
visitor.OnBeginHeadersForStream(1);
EXPECT_CALL(callbacks, OnHeader(_, "x-server-status", "OK", _));
visitor.OnHeaderForStream(1, "x-server-status", "OK");
EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_HEADERS)));
visitor.OnEndHeadersForStream(1);
// RST_STREAM on stream 3
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(3, RST_STREAM, 0)));
visitor.OnFrameHeader(3, 4, RST_STREAM, 0);
// No change in stream map size.
EXPECT_EQ(visitor.stream_map_size(), 1);
EXPECT_CALL(callbacks, OnFrameRecv(IsRstStream(3, NGHTTP2_INTERNAL_ERROR)));
visitor.OnRstStream(3, Http2ErrorCode::INTERNAL_ERROR);
EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_INTERNAL_ERROR));
visitor.OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR);
// More stream close events
EXPECT_CALL(callbacks,
OnBeginFrame(HasFrameHeader(1, DATA, NGHTTP2_FLAG_END_STREAM)));
visitor.OnFrameHeader(1, 0, DATA, 1);
EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, NGHTTP2_FLAG_END_STREAM)));
visitor.OnBeginDataForStream(1, 0);
visitor.OnEndStream(1);
EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR));
visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR);
// Stream map is empty again.
EXPECT_EQ(visitor.stream_map_size(), 0);
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(5, RST_STREAM, _)));
visitor.OnFrameHeader(5, 4, RST_STREAM, 0);
EXPECT_CALL(callbacks, OnFrameRecv(IsRstStream(5, NGHTTP2_REFUSED_STREAM)));
visitor.OnRstStream(5, Http2ErrorCode::REFUSED_STREAM);
EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_REFUSED_STREAM));
visitor.OnCloseStream(5, Http2ErrorCode::REFUSED_STREAM);
EXPECT_EQ(visitor.stream_map_size(), 0);
}
TEST(ClientCallbackVisitorUnitTest, HeadersWithContinuation) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kClient,
*MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
// HEADERS on stream 1
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, 0x0)));
visitor.OnFrameHeader(1, 23, HEADERS, 0x0);
EXPECT_CALL(callbacks,
OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
visitor.OnBeginHeadersForStream(1);
EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _));
visitor.OnHeaderForStream(1, ":status", "200");
EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _));
visitor.OnHeaderForStream(1, "server", "my-fake-server");
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, CONTINUATION, 0x4)));
visitor.OnFrameHeader(1, 23, CONTINUATION, 0x4);
EXPECT_CALL(callbacks,
OnHeader(_, "date", "Tue, 6 Apr 2021 12:54:01 GMT", _));
visitor.OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT");
EXPECT_CALL(callbacks, OnHeader(_, "trailer", "x-server-status", _));
visitor.OnHeaderForStream(1, "trailer", "x-server-status");
EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
visitor.OnEndHeadersForStream(1);
}
TEST(ServerCallbackVisitorUnitTest, ConnectionFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kServer,
*MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
// SETTINGS
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, SETTINGS, _)));
visitor.OnFrameHeader(0, 0, SETTINGS, 0);
visitor.OnSettingsStart();
EXPECT_CALL(callbacks, OnFrameRecv(IsSettings(testing::IsEmpty())));
visitor.OnSettingsEnd();
// PING
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, PING, _)));
visitor.OnFrameHeader(0, 8, PING, 0);
EXPECT_CALL(callbacks, OnFrameRecv(IsPing(42)));
visitor.OnPing(42, false);
// WINDOW_UPDATE
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(0, WINDOW_UPDATE, _)));
visitor.OnFrameHeader(0, 4, WINDOW_UPDATE, 0);
EXPECT_CALL(callbacks, OnFrameRecv(IsWindowUpdate(1000)));
visitor.OnWindowUpdate(0, 1000);
// PING ack
EXPECT_CALL(callbacks,
OnBeginFrame(HasFrameHeader(0, PING, NGHTTP2_FLAG_ACK)));
visitor.OnFrameHeader(0, 8, PING, 1);
EXPECT_CALL(callbacks, OnFrameRecv(IsPingAck(247)));
visitor.OnPing(247, true);
EXPECT_EQ(visitor.stream_map_size(), 0);
}
TEST(ServerCallbackVisitorUnitTest, StreamFrames) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kServer,
*MockNghttp2Callbacks::GetCallbacks(), &callbacks);
testing::InSequence seq;
// HEADERS on stream 1
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(
1, HEADERS, NGHTTP2_FLAG_END_HEADERS)));
visitor.OnFrameHeader(1, 23, HEADERS, 4);
EXPECT_CALL(callbacks, OnBeginHeaders(IsHeaders(1, NGHTTP2_FLAG_END_HEADERS,
NGHTTP2_HCAT_REQUEST)));
visitor.OnBeginHeadersForStream(1);
EXPECT_EQ(visitor.stream_map_size(), 1);
EXPECT_CALL(callbacks, OnHeader(_, ":method", "POST", _));
visitor.OnHeaderForStream(1, ":method", "POST");
EXPECT_CALL(callbacks, OnHeader(_, ":path", "/example/path", _));
visitor.OnHeaderForStream(1, ":path", "/example/path");
EXPECT_CALL(callbacks, OnHeader(_, ":scheme", "https", _));
visitor.OnHeaderForStream(1, ":scheme", "https");
EXPECT_CALL(callbacks, OnHeader(_, ":authority", "example.com", _));
visitor.OnHeaderForStream(1, ":authority", "example.com");
EXPECT_CALL(callbacks, OnHeader(_, "accept", "text/html", _));
visitor.OnHeaderForStream(1, "accept", "text/html");
EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, NGHTTP2_FLAG_END_HEADERS,
NGHTTP2_HCAT_REQUEST)));
visitor.OnEndHeadersForStream(1);
// DATA on stream 1
EXPECT_CALL(callbacks,
OnBeginFrame(HasFrameHeader(1, DATA, NGHTTP2_FLAG_END_STREAM)));
visitor.OnFrameHeader(1, 25, DATA, NGHTTP2_FLAG_END_STREAM);
visitor.OnBeginDataForStream(1, 25);
EXPECT_CALL(callbacks, OnDataChunkRecv(NGHTTP2_FLAG_END_STREAM, 1,
"This is the request body."));
EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, NGHTTP2_FLAG_END_STREAM)));
visitor.OnDataForStream(1, "This is the request body.");
visitor.OnEndStream(1);
EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR));
visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR);
EXPECT_EQ(visitor.stream_map_size(), 0);
// RST_STREAM on stream 3
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(3, RST_STREAM, 0)));
visitor.OnFrameHeader(3, 4, RST_STREAM, 0);
EXPECT_CALL(callbacks, OnFrameRecv(IsRstStream(3, NGHTTP2_INTERNAL_ERROR)));
visitor.OnRstStream(3, Http2ErrorCode::INTERNAL_ERROR);
EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_INTERNAL_ERROR));
visitor.OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR);
EXPECT_EQ(visitor.stream_map_size(), 0);
}
TEST(ServerCallbackVisitorUnitTest, DataWithPadding) {
testing::StrictMock<MockNghttp2Callbacks> callbacks;
CallbackVisitor visitor(Perspective::kServer,
*MockNghttp2Callbacks::GetCallbacks(), &callbacks);
const size_t kPaddingLength = 39;
const uint8_t kFlags = NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_END_STREAM;
testing::InSequence seq;
// DATA on stream 1
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, DATA, kFlags)));
EXPECT_TRUE(visitor.OnFrameHeader(1, 25 + kPaddingLength, DATA, kFlags));
EXPECT_TRUE(visitor.OnBeginDataForStream(1, 25 + kPaddingLength));
// Padding before data.
EXPECT_TRUE(visitor.OnDataPaddingLength(1, kPaddingLength));
EXPECT_CALL(callbacks,
OnDataChunkRecv(kFlags, 1, "This is the request body."));
EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, kFlags, kPaddingLength)));
EXPECT_TRUE(visitor.OnDataForStream(1, "This is the request body."));
visitor.OnEndStream(1);
EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR));
visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR);
// DATA on stream 3
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(3, DATA, kFlags)));
EXPECT_TRUE(visitor.OnFrameHeader(3, 25 + kPaddingLength, DATA, kFlags));
EXPECT_TRUE(visitor.OnBeginDataForStream(3, 25 + kPaddingLength));
// Data before padding.
EXPECT_CALL(callbacks,
OnDataChunkRecv(kFlags, 3, "This is the request body."));
EXPECT_TRUE(visitor.OnDataForStream(3, "This is the request body."));
EXPECT_CALL(callbacks, OnFrameRecv(IsData(3, _, kFlags, kPaddingLength)));
EXPECT_TRUE(visitor.OnDataPaddingLength(3, kPaddingLength));
visitor.OnEndStream(3);
EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_NO_ERROR));
visitor.OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR);
// DATA on stream 5
EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(5, DATA, kFlags)));
EXPECT_TRUE(visitor.OnFrameHeader(5, 25 + kPaddingLength, DATA, kFlags));
EXPECT_TRUE(visitor.OnBeginDataForStream(5, 25 + kPaddingLength));
// Error during padding.
EXPECT_CALL(callbacks,
OnDataChunkRecv(kFlags, 5, "This is the request body."));
EXPECT_TRUE(visitor.OnDataForStream(5, "This is the request body."));
EXPECT_CALL(callbacks, OnFrameRecv(IsData(5, _, kFlags, kPaddingLength)))
.WillOnce(testing::Return(NGHTTP2_ERR_CALLBACK_FAILURE));
EXPECT_FALSE(visitor.OnDataPaddingLength(5, kPaddingLength));
visitor.OnEndStream(3);
EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_NO_ERROR));
visitor.OnCloseStream(5, Http2ErrorCode::HTTP2_NO_ERROR);
}
} // namespace
} // namespace test
} // namespace adapter
} // namespace http2