1 #include "quiche/http2/adapter/http2_protocol.h"
2 #include "quiche/http2/adapter/nghttp2_adapter.h"
3 #include "quiche/http2/adapter/oghttp2_adapter.h"
4 #include "quiche/http2/adapter/recording_http2_visitor.h"
5 #include "quiche/http2/adapter/test_frame_sequence.h"
6 #include "quiche/common/platform/api/quiche_test.h"
7 #include "quiche/spdy/core/spdy_protocol.h"
8 
9 namespace http2 {
10 namespace adapter {
11 namespace test {
12 namespace {
13 
TEST(AdapterImplComparisonTest,ClientHandlesFrames)14 TEST(AdapterImplComparisonTest, ClientHandlesFrames) {
15   RecordingHttp2Visitor nghttp2_visitor;
16   std::unique_ptr<NgHttp2Adapter> nghttp2_adapter =
17       NgHttp2Adapter::CreateClientAdapter(nghttp2_visitor);
18 
19   RecordingHttp2Visitor oghttp2_visitor;
20   OgHttp2Adapter::Options options;
21   options.perspective = Perspective::kClient;
22   std::unique_ptr<OgHttp2Adapter> oghttp2_adapter =
23       OgHttp2Adapter::Create(oghttp2_visitor, options);
24 
25   const std::string initial_frames = TestFrameSequence()
26                                          .ServerPreface()
27                                          .Ping(42)
28                                          .WindowUpdate(0, 1000)
29                                          .Serialize();
30 
31   nghttp2_adapter->ProcessBytes(initial_frames);
32   oghttp2_adapter->ProcessBytes(initial_frames);
33 
34   EXPECT_EQ(nghttp2_visitor.GetEventSequence(),
35             oghttp2_visitor.GetEventSequence());
36 
37   // TODO(b/181586191): Consider consistent behavior for delivering events on
38   // non-existent streams between nghttp2_adapter and oghttp2_adapter.
39 }
40 
TEST(AdapterImplComparisonTest,SubmitWindowUpdateBumpsWindow)41 TEST(AdapterImplComparisonTest, SubmitWindowUpdateBumpsWindow) {
42   RecordingHttp2Visitor nghttp2_visitor;
43   std::unique_ptr<NgHttp2Adapter> nghttp2_adapter =
44       NgHttp2Adapter::CreateClientAdapter(nghttp2_visitor);
45 
46   RecordingHttp2Visitor oghttp2_visitor;
47   OgHttp2Adapter::Options options;
48   options.perspective = Perspective::kClient;
49   std::unique_ptr<OgHttp2Adapter> oghttp2_adapter =
50       OgHttp2Adapter::Create(oghttp2_visitor, options);
51 
52   int result;
53 
54   const std::vector<Header> request_headers =
55       ToHeaders({{":method", "POST"},
56                  {":scheme", "https"},
57                  {":authority", "example.com"},
58                  {":path", "/"}});
59   const int kInitialFlowControlWindow = 65535;
60   const int kConnectionWindowIncrease = 192 * 1024;
61 
62   const int32_t nghttp2_stream_id =
63       nghttp2_adapter->SubmitRequest(request_headers, nullptr, nullptr);
64 
65   // Both the connection and stream flow control windows are increased.
66   nghttp2_adapter->SubmitWindowUpdate(0, kConnectionWindowIncrease);
67   nghttp2_adapter->SubmitWindowUpdate(nghttp2_stream_id,
68                                       kConnectionWindowIncrease);
69   result = nghttp2_adapter->Send();
70   EXPECT_EQ(0, result);
71   int nghttp2_window = nghttp2_adapter->GetReceiveWindowSize();
72   EXPECT_EQ(kInitialFlowControlWindow + kConnectionWindowIncrease,
73             nghttp2_window);
74 
75   const int32_t oghttp2_stream_id =
76       oghttp2_adapter->SubmitRequest(request_headers, nullptr, nullptr);
77   // Both the connection and stream flow control windows are increased.
78   oghttp2_adapter->SubmitWindowUpdate(0, kConnectionWindowIncrease);
79   oghttp2_adapter->SubmitWindowUpdate(oghttp2_stream_id,
80                                       kConnectionWindowIncrease);
81   result = oghttp2_adapter->Send();
82   EXPECT_EQ(0, result);
83   int oghttp2_window = oghttp2_adapter->GetReceiveWindowSize();
84   EXPECT_EQ(kInitialFlowControlWindow + kConnectionWindowIncrease,
85             oghttp2_window);
86 
87   // nghttp2 and oghttp2 agree on the advertised window.
88   EXPECT_EQ(nghttp2_window, oghttp2_window);
89 
90   ASSERT_EQ(nghttp2_stream_id, oghttp2_stream_id);
91 
92   const int kMaxFrameSize = 16 * 1024;
93   const std::string body_chunk(kMaxFrameSize, 'a');
94   auto sequence = TestFrameSequence();
95   sequence.ServerPreface().Headers(nghttp2_stream_id, {{":status", "200"}},
96                                    /*fin=*/false);
97   // This loop generates enough DATA frames to consume the window increase.
98   const int kNumFrames = kConnectionWindowIncrease / kMaxFrameSize;
99   for (int i = 0; i < kNumFrames; ++i) {
100     sequence.Data(nghttp2_stream_id, body_chunk);
101   }
102   const std::string frames = sequence.Serialize();
103 
104   nghttp2_adapter->ProcessBytes(frames);
105   // Marking the data consumed causes a window update, which is reflected in the
106   // advertised window size.
107   nghttp2_adapter->MarkDataConsumedForStream(nghttp2_stream_id,
108                                              kNumFrames * kMaxFrameSize);
109   result = nghttp2_adapter->Send();
110   EXPECT_EQ(0, result);
111   nghttp2_window = nghttp2_adapter->GetReceiveWindowSize();
112 
113   oghttp2_adapter->ProcessBytes(frames);
114   // Marking the data consumed causes a window update, which is reflected in the
115   // advertised window size.
116   oghttp2_adapter->MarkDataConsumedForStream(oghttp2_stream_id,
117                                              kNumFrames * kMaxFrameSize);
118   result = oghttp2_adapter->Send();
119   EXPECT_EQ(0, result);
120   oghttp2_window = oghttp2_adapter->GetReceiveWindowSize();
121 
122   const int kMinExpectation =
123       (kInitialFlowControlWindow + kConnectionWindowIncrease) / 2;
124   EXPECT_GT(nghttp2_window, kMinExpectation);
125   EXPECT_GT(oghttp2_window, kMinExpectation);
126 }
127 
TEST(AdapterImplComparisonTest,ServerHandlesFrames)128 TEST(AdapterImplComparisonTest, ServerHandlesFrames) {
129   RecordingHttp2Visitor nghttp2_visitor;
130   std::unique_ptr<NgHttp2Adapter> nghttp2_adapter =
131       NgHttp2Adapter::CreateServerAdapter(nghttp2_visitor);
132 
133   RecordingHttp2Visitor oghttp2_visitor;
134   OgHttp2Adapter::Options options;
135   options.perspective = Perspective::kServer;
136   std::unique_ptr<OgHttp2Adapter> oghttp2_adapter =
137       OgHttp2Adapter::Create(oghttp2_visitor, options);
138 
139   const std::string frames = TestFrameSequence()
140                                  .ClientPreface()
141                                  .Ping(42)
142                                  .WindowUpdate(0, 1000)
143                                  .Headers(1,
144                                           {{":method", "POST"},
145                                            {":scheme", "https"},
146                                            {":authority", "example.com"},
147                                            {":path", "/this/is/request/one"}},
148                                           /*fin=*/false)
149                                  .WindowUpdate(1, 2000)
150                                  .Data(1, "This is the request body.")
151                                  .Headers(3,
152                                           {{":method", "GET"},
153                                            {":scheme", "http"},
154                                            {":authority", "example.com"},
155                                            {":path", "/this/is/request/two"}},
156                                           /*fin=*/true)
157                                  .RstStream(3, Http2ErrorCode::CANCEL)
158                                  .Ping(47)
159                                  .Serialize();
160 
161   nghttp2_adapter->ProcessBytes(frames);
162   oghttp2_adapter->ProcessBytes(frames);
163 
164   EXPECT_EQ(nghttp2_visitor.GetEventSequence(),
165             oghttp2_visitor.GetEventSequence());
166 }
167 
168 }  // namespace
169 }  // namespace test
170 }  // namespace adapter
171 }  // namespace http2
172