Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(100)

Side by Side Diff: net/http/bidirectional_stream_unittest.cc

Issue 1326503003: Added a net::BidirectionalStream to expose a bidirectional streaming interface (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address Misha's comments Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/http/bidirectional_stream.h"
6
7 #include "base/macros.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/run_loop.h"
10 #include "base/strings/string_piece.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/test_data_directory.h"
13 #include "net/http/http_network_session.h"
14 #include "net/http/http_request_info.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/log/net_log.h"
17 #include "net/socket/socket_test_util.h"
18 #include "net/spdy/spdy_session.h"
19 #include "net/spdy/spdy_test_util_common.h"
20 #include "net/spdy/spdy_test_util_common.h"
21 #include "net/test/cert_test_util.h"
22 #include "net/url_request/url_request_test_util.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace net {
26
27 namespace {
28
29 const char kBodyData[] = "Body data";
30 const int kBodyDataSize = arraysize(kBodyData);
31 const base::StringPiece kBodyDataStringPiece(kBodyData, kBodyDataSize);
32 // Size of the buffer to be allocated for each read.
33 const size_t kReadBufferSize = 4096;
34
35 // Delegate that automatically reads data from the stream, but does not send
36 // any data unless it is told to.
37 class TestBidirectionalStreamDelegate : public BidirectionalStream::Delegate {
38 public:
39 TestBidirectionalStreamDelegate(IOBuffer* read_buf, int read_buf_len)
40 : bytes_read_(0),
41 error_(OK),
42 read_buf_(read_buf),
43 read_buf_len_(read_buf_len) {}
44
45 ~TestBidirectionalStreamDelegate() override {}
46
47 void OnRequestHeadersSent() override {}
48
49 void OnHeaders(const SpdyHeaderBlock& response_headers) override {
50 response_headers_ = response_headers;
51 StartOrContinueReading();
52 }
53
54 void OnReadCompleted(int bytes_read) override {
mmenke 2015/12/08 22:58:34 Check we haven't received trailers yet?
xunjieli 2015/12/10 23:25:50 Actually we aren't delaying OnTrailers event until
mmenke 2015/12/11 16:42:53 That seems a bit weird. Document it? Or change t
xunjieli 2015/12/11 23:48:39 gRPC does not require the underlying implemenation
55 CHECK_GT(bytes_read, OK);
56 bytes_read_ += bytes_read;
57 data_received_.append(read_buf_->data(), bytes_read_);
58 StartOrContinueReading();
59 }
60
61 void OnDataSent() override {}
mmenke 2015/12/08 22:58:34 No tests at this layer send any data. Add some?
xunjieli 2015/12/10 23:25:51 There's one test, SendPostRequest, that sends data
62
63 void OnTrailers(const SpdyHeaderBlock& trailers) override {
mmenke 2015/12/08 22:58:35 Should we check for re-entrancy in the callbacks?
xunjieli 2015/12/10 23:25:51 That sounds like something we should do. How shoul
mmenke 2015/12/11 16:42:53 I may have had something more clever in mind yeste
xunjieli 2015/12/11 23:48:39 Done.
64 trailers_ = trailers;
65 loop_.Quit();
66 }
67
68 void OnFailed(int error) override {
69 CHECK_NE(OK, error);
70 error_ = error;
71 loop_.Quit();
72 }
73
74 void CreateBidirectionalStream(
75 const BidirectionalStream::RequestInfo& request_info,
76 RequestPriority priority,
77 HttpNetworkSession* session) {
78 stream_.reset(
79 new BidirectionalStream(request_info, priority, session, this));
80 loop_.Run();
mmenke 2015/12/08 22:58:35 "CreateBidirectionalStream" does not indicate "Ent
xunjieli 2015/12/10 23:25:51 Done.
81 }
82
83 void SendData(IOBuffer* data, int length, bool end_of_stream) {
84 stream_->SendData(data, length, end_of_stream);
85 }
86
87 // Const getters for internal states.
88 const std::string& data_received() const { return data_received_; }
89 int bytes_read() const { return bytes_read_; }
90 int error() const { return error_; }
91 const SpdyHeaderBlock response_headers() const { return response_headers_; }
92 const SpdyHeaderBlock trailers() const { return trailers_; }
93
94 private:
95 // Starts or continues read data from |stream_| until there is no more byte
96 // can be read synchronously.
mmenke 2015/12/08 22:58:34 nit: Grammar.
xunjieli 2015/12/10 23:25:51 Done.
97 void StartOrContinueReading() {
98 int rv = stream_->ReadData(read_buf_.get(), read_buf_len_);
99 while (rv > 0) {
100 bytes_read_ += rv;
101 data_received_.append(read_buf_->data(), rv);
102 rv = stream_->ReadData(read_buf_.get(), read_buf_len_);
103 }
104 if (rv == OK)
mmenke 2015/12/08 22:58:35 No tests simulate async reads / writes, or multipl
xunjieli 2015/12/10 23:25:51 Those are in bidirectional_stream_spdy_job_unittes
105 loop_.Quit();
106 }
107
108 SpdyHeaderBlock response_headers_;
109 SpdyHeaderBlock trailers_;
110 int bytes_read_;
mmenke 2015/12/08 22:58:35 This is just data_received_.size(), right? Can we
xunjieli 2015/12/10 23:25:51 Done.
111 int error_;
112 scoped_refptr<IOBuffer> read_buf_;
113 int read_buf_len_;
114 std::string data_received_;
115 base::RunLoop loop_;
116 scoped_ptr<BidirectionalStream> stream_;
117
118 DISALLOW_COPY_AND_ASSIGN(TestBidirectionalStreamDelegate);
119 };
120
121 // A delegate that sends data after request headers are sent.
122 class SendDataDelegate : public TestBidirectionalStreamDelegate {
123 public:
124 SendDataDelegate(IOBuffer* buf, int buf_len, base::StringPiece data)
125 : TestBidirectionalStreamDelegate(buf, buf_len), data_(data) {}
126
127 ~SendDataDelegate() override {}
128
129 void OnRequestHeadersSent() override {
130 TestBidirectionalStreamDelegate::OnRequestHeadersSent();
131 if (data_.data()) {
132 scoped_refptr<StringIOBuffer> buf(new StringIOBuffer(data_.as_string()));
133 SendData(buf.get(), buf->size(), NO_MORE_DATA_TO_SEND);
134 }
135 }
136
137 private:
138 base::StringPiece data_;
139
140 DISALLOW_COPY_AND_ASSIGN(SendDataDelegate);
141 };
142
143 class BidirectionalStreamTest : public testing::Test {
144 public:
145 BidirectionalStreamTest()
146 : spdy_util_(kProtoHTTP2, false), session_deps_(kProtoHTTP2) {}
147
148 protected:
149 // Initializes the session using SequencedSocketData.
150 void InitSession(MockRead* reads,
151 size_t reads_count,
152 MockWrite* writes,
153 size_t writes_count,
154 const SpdySessionKey& key) {
155 sequenced_data_.reset(
156 new SequencedSocketData(reads, reads_count, writes, writes_count));
157 session_deps_.socket_factory->AddSocketDataProvider(sequenced_data_.get());
158 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
159 session_ = CreateSecureSpdySession(http_session_.get(), key, BoundNetLog());
160 }
161
162 SpdyTestUtil spdy_util_;
163 SpdySessionDependencies session_deps_;
164 scoped_ptr<SequencedSocketData> sequenced_data_;
165 scoped_ptr<HttpNetworkSession> http_session_;
166 base::WeakPtr<SpdySession> session_;
167 };
168
169 } // namespace
170
171 TEST_F(BidirectionalStreamTest, CreateInsecureStream) {
172 BidirectionalStream::RequestInfo request;
173 request.method = "GET";
174 request.url = GURL("http://www.example.org/");
175
176 TestBidirectionalStreamDelegate delegate(nullptr, 0);
177 HttpNetworkSession::Params params =
178 SpdySessionDependencies::CreateSessionParams(&session_deps_);
179 scoped_ptr<HttpNetworkSession> session(new HttpNetworkSession(params));
180 delegate.CreateBidirectionalStream(request, LOWEST, session.get());
181 EXPECT_EQ(ERR_DISALLOWED_URL_SCHEME, delegate.error());
182 }
183
184 TEST_F(BidirectionalStreamTest, SendPostRequest) {
185 spdy_util_.set_default_url(GURL("https://www.example.org"));
186 BufferedSpdyFramer framer(spdy_util_.spdy_version(), false);
187
188 scoped_ptr<SpdyFrame> req(spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
189 scoped_ptr<SpdyFrame> body(
190 framer.CreateDataFrame(1, kBodyData, kBodyDataSize, DATA_FLAG_FIN));
191 MockWrite writes[] = {
192 CreateMockWrite(*req, 0), // request
193 CreateMockWrite(*body, 1), // POST upload frame
194 };
195 const char* const kExtraResponseHeaders[] = {"header-name", "header-value"};
196 scoped_ptr<SpdyFrame> resp(
197 spdy_util_.ConstructSpdyPostSynReply(kExtraResponseHeaders, 1));
198
199 scoped_ptr<SpdyFrame> resp_data(
200 framer.CreateDataFrame(1, kBodyData, kBodyDataSize, DATA_FLAG_NONE));
201
202 const char* const kExtraHeaders[] = {"foo", "bar"};
203 scoped_ptr<SpdyFrame> trailers(
204 spdy_util_.ConstructSpdyHeaderFrame(1, kExtraHeaders, 1, true));
205
206 MockRead reads[] = {
207 CreateMockRead(*resp, 2), CreateMockRead(*resp_data, 3),
208 CreateMockRead(*trailers, 4), MockRead(SYNCHRONOUS, 0, 5) // EOF
mmenke 2015/12/08 22:58:34 Should we check async reads? Requests without tra
xunjieli 2015/12/10 23:25:51 Those are in bidirectional_stream_spdy_job_unittes
mmenke 2015/12/11 16:42:53 Others may disagree, but I actually think if we're
xunjieli 2015/12/11 23:48:39 Done. I think your idea is better. If we add QUIC
209 };
210
211 HostPortPair host_port_pair("www.example.org", 443);
212 SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
213 PRIVACY_MODE_DISABLED);
214
215 SSLSocketDataProvider ssl_data(ASYNC, OK);
216 ssl_data.SetNextProto(kProtoHTTP2);
217 ssl_data.cert = ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
218 ASSERT_TRUE(ssl_data.cert.get());
219 session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
220
221 InitSession(reads, arraysize(reads), writes, arraysize(writes), key);
222
223 BidirectionalStream::RequestInfo request;
224 request.method = "POST";
225 request.url = GURL("https://www.example.org/");
226
227 scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
228 scoped_ptr<SendDataDelegate> delegate(new SendDataDelegate(
229 read_buffer.get(), kReadBufferSize, kBodyDataStringPiece));
230 delegate->CreateBidirectionalStream(request, DEFAULT_PRIORITY,
231 http_session_.get());
232
233 const SpdyHeaderBlock response_headers = delegate->response_headers();
234 EXPECT_EQ("200", response_headers.find(":status")->second);
235 EXPECT_EQ("header-value", response_headers.find("header-name")->second);
236 EXPECT_EQ(kBodyDataSize, delegate->bytes_read());
237 EXPECT_EQ(std::string(kBodyData, kBodyDataSize), delegate->data_received());
238 EXPECT_EQ("bar", delegate->trailers().find("foo")->second);
239 }
240
241 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698