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

Side by Side Diff: net/spdy/spdy_websocket_stream_unittest.cc

Issue 3020068: WebSocket over SPDY. (Closed)
Patch Set: fix unittests Created 10 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
« no previous file with comments | « net/spdy/spdy_websocket_stream.cc ('k') | net/websockets/websocket_job.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 <string>
6 #include <vector>
7
8 #include "net/spdy/spdy_websocket_stream.h"
9 #include "net/base/completion_callback.h"
10 #include "net/proxy/proxy_server.h"
11 #include "net/spdy/spdy_protocol.h"
12 #include "net/spdy/spdy_session.h"
13 #include "net/spdy/spdy_test_util.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace net {
17
18 spdy::SpdyFrame* ConstructSpdyWebSocketHandshakeReq(
19 const char* const url,
20 const char* const origin,
21 const char* const protocol,
22 bool compressed,
23 spdy::SpdyStreamId stream_id,
24 RequestPriority request_priority) {
25 const SpdyHeaderInfo kSynStreamHeader = {
26 spdy::SYN_STREAM,
27 stream_id,
28 0, // Associated stream ID
29 net::ConvertRequestPriorityToSpdyPriority(request_priority),
30 spdy::CONTROL_FLAG_NONE,
31 compressed,
32 spdy::INVALID, // Status
33 NULL, // Data,
34 0, // Length
35 spdy::DATA_FLAG_NONE
36 };
37
38 const char* const headers[] = {
39 "url",
40 url,
41 "origin",
42 origin,
43 "protocol",
44 protocol,
45 };
46 int header_size = arraysize(headers) / 2;
47 if (protocol == NULL)
48 header_size -= 1;
49
50 return ConstructSpdyPacket(
51 kSynStreamHeader,
52 NULL,
53 0,
54 headers,
55 header_size);
56 }
57
58 spdy::SpdyFrame* ConstructSpdyWebSocketHandshakeResp(
59 const char* const url,
60 const char* const origin,
61 const char* const protocol,
62 bool compressed,
63 spdy::SpdyStreamId stream_id,
64 RequestPriority request_priority) {
65 const SpdyHeaderInfo kSynReplyHeader = {
66 spdy::SYN_REPLY,
67 stream_id,
68 0, // Associated stream ID
69 net::ConvertRequestPriorityToSpdyPriority(request_priority),
70 spdy::CONTROL_FLAG_NONE,
71 false,
72 spdy::INVALID, // Status
73 NULL, // Data
74 0, // Length
75 spdy::DATA_FLAG_NONE
76 };
77
78 const char* const headers[] = {
79 "sec-websocket-location",
80 url,
81 "sec-websocket-origin",
82 origin,
83 "sec-websocket-protocol",
84 protocol,
85 };
86 int header_size = arraysize(headers) / 2;
87 if (protocol == NULL)
88 header_size -= 1;
89
90 return ConstructSpdyPacket(
91 kSynReplyHeader,
92 NULL,
93 0,
94 headers,
95 header_size);
96 }
97
98 spdy::SpdyFrame* ConstructSpdyWebSocketFrame(
99 const char* data,
100 int len,
101 spdy::SpdyStreamId stream_id,
102 bool fin) {
103 spdy::SpdyFramer framer;
104 return framer.CreateDataFrame(
105 stream_id, data, len,
106 fin ? spdy::DATA_FLAG_FIN : spdy::DATA_FLAG_NONE);
107 }
108
109 struct SpdyWebSocketStreamEvent {
110 enum EventType {
111 EVENT_CREATED,
112 EVENT_SENT_HEADERS, EVENT_RECEIVED_HEADER,
113 EVENT_SENT_DATA, EVENT_RECEIVED_DATA,
114 EVENT_CLOSE,
115 };
116 SpdyWebSocketStreamEvent(EventType type,
117 SpdyWebSocketStream* stream,
118 const spdy::SpdyHeaderBlock& headers,
119 int result,
120 const std::string& data)
121 : event_type(type),
122 stream(stream),
123 headers(headers),
124 result(result),
125 data(data) {}
126
127 EventType event_type;
128 SpdyWebSocketStream* stream;
129 spdy::SpdyHeaderBlock headers;
130 int result;
131 std::string data;
132 };
133
134 class SpdyWebSocketStreamEventRecorder : public SpdyWebSocketStreamDelegate {
135 public:
136 explicit SpdyWebSocketStreamEventRecorder(CompletionCallback* callback)
137 : on_created_(NULL),
138 on_sent_headers_(NULL),
139 on_received_header_(NULL),
140 on_sent_data_(NULL),
141 on_received_data_(NULL),
142 on_close_(NULL),
143 callback_(callback) {}
144 virtual ~SpdyWebSocketStreamEventRecorder() {
145 delete on_created_;
146 delete on_sent_headers_;
147 delete on_received_header_;
148 delete on_sent_data_;
149 delete on_received_data_;
150 delete on_close_;
151 }
152
153 void SetOnCreated(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
154 on_created_ = callback;
155 }
156 void SetOnSentHeaders(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
157 on_sent_headers_ = callback;
158 }
159 void SetOnReceivedHeader(
160 Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
161 on_received_header_ = callback;
162 }
163 void SetOnSentData(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
164 on_sent_data_ = callback;
165 }
166 void SetOnReceivedData(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
167 on_received_data_ = callback;
168 }
169 void SetOnClose(Callback1<SpdyWebSocketStreamEvent*>::Type* callback) {
170 on_close_ = callback;
171 }
172
173 virtual void OnCreatedSpdyStream(SpdyWebSocketStream* stream, int result) {
174 events_.push_back(
175 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_CREATED,
176 stream,
177 spdy::SpdyHeaderBlock(),
178 result,
179 std::string()));
180 if (on_created_)
181 on_created_->Run(&events_.back());
182 }
183
184 virtual void OnSentSpdyHeaders(SpdyWebSocketStream* stream) {
185 events_.push_back(
186 SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
187 stream,
188 spdy::SpdyHeaderBlock(),
189 OK,
190 std::string()));
191 if (on_sent_data_)
192 on_sent_data_->Run(&events_.back());
193 }
194 virtual int OnReceivedSpdyResponseHeader(
195 SpdyWebSocketStream* stream,
196 const spdy::SpdyHeaderBlock& headers,
197 int status) {
198 events_.push_back(
199 SpdyWebSocketStreamEvent(
200 SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
201 stream,
202 headers,
203 status,
204 std::string()));
205 if (on_received_header_)
206 on_received_header_->Run(&events_.back());
207 return status;
208 }
209 virtual void OnSentSpdyData(
210 SpdyWebSocketStream* stream, int amount_sent) {
211 events_.push_back(
212 SpdyWebSocketStreamEvent(
213 SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
214 stream,
215 spdy::SpdyHeaderBlock(),
216 amount_sent,
217 std::string()));
218 if (on_sent_data_)
219 on_sent_data_->Run(&events_.back());
220 }
221 virtual void OnReceivedSpdyData(
222 SpdyWebSocketStream* stream, const char* data, int length) {
223 events_.push_back(
224 SpdyWebSocketStreamEvent(
225 SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
226 stream,
227 spdy::SpdyHeaderBlock(),
228 length,
229 std::string(data, length)));
230 if (on_received_data_)
231 on_received_data_->Run(&events_.back());
232 }
233 virtual void OnCloseSpdyStream(SpdyWebSocketStream* stream) {
234 events_.push_back(
235 SpdyWebSocketStreamEvent(
236 SpdyWebSocketStreamEvent::EVENT_CLOSE,
237 stream,
238 spdy::SpdyHeaderBlock(),
239 OK,
240 std::string()));
241 if (on_close_)
242 on_close_->Run(&events_.back());
243 if (callback_)
244 callback_->Run(net::OK);
245 }
246
247 const std::vector<SpdyWebSocketStreamEvent>& GetSeenEvents() const {
248 return events_;
249 }
250
251 private:
252 std::vector<SpdyWebSocketStreamEvent> events_;
253 Callback1<SpdyWebSocketStreamEvent*>::Type* on_created_;
254 Callback1<SpdyWebSocketStreamEvent*>::Type* on_sent_headers_;
255 Callback1<SpdyWebSocketStreamEvent*>::Type* on_received_header_;
256 Callback1<SpdyWebSocketStreamEvent*>::Type* on_sent_data_;
257 Callback1<SpdyWebSocketStreamEvent*>::Type* on_received_data_;
258 Callback1<SpdyWebSocketStreamEvent*>::Type* on_close_;
259 CompletionCallback* callback_;
260
261 DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStreamEventRecorder);
262 };
263
264 class SpdyWebSocketStreamTest : public testing::Test {
265 public:
266 OrderedSocketData* data() { return data_; }
267
268 void DoSendHelloFrame(SpdyWebSocketStreamEvent* event) {
269 std::string frame;
270 frame.append(1, '\0');
271 frame.append("hello");
272 frame.append(1, '\xff');
273 event->stream->SendData(frame.data(), frame.size());
274 }
275
276 void DoSendClosingFrame(SpdyWebSocketStreamEvent* event) {
277 static const char kClosingFrame[] = "\xff\0";
278 event->stream->SendData(kClosingFrame, 2);
279 }
280
281 void DoClose(SpdyWebSocketStreamEvent* event) {
282 event->stream->Close();
283 }
284
285 protected:
286 SpdyWebSocketStreamTest() {}
287 virtual ~SpdyWebSocketStreamTest() {}
288
289 virtual void SetUp() {}
290 virtual void TearDown() {
291 MessageLoop::current()->RunAllPending();
292 }
293
294 void EnableCompression(bool enabled) {
295 spdy::SpdyFramer::set_enable_compression_default(enabled);
296 }
297 int InitSession(MockRead* reads, size_t reads_count,
298 MockWrite* writes, size_t writes_count,
299 HostPortPair& host_port_pair) {
300 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
301 data_ = new OrderedSocketData(reads, reads_count, writes, writes_count);
302 session_deps_.socket_factory->AddSocketDataProvider(data_.get());
303 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
304 session_ = http_session_->spdy_session_pool()->
305 Get(pair, http_session_->mutable_spdy_settings(), BoundNetLog());
306 tcp_params_ = new TCPSocketParams(host_port_pair.host(),
307 host_port_pair.port(),
308 MEDIUM, GURL(), false);
309 TestCompletionCallback callback;
310 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
311 EXPECT_EQ(ERR_IO_PENDING,
312 connection->Init(host_port_pair.ToString(), tcp_params_, MEDIUM,
313 &callback, http_session_->tcp_socket_pool(),
314 BoundNetLog()));
315 EXPECT_EQ(OK, callback.WaitForResult());
316 return session_->InitializeWithSocket(connection.release(), false, OK);
317 }
318
319 SpdySessionDependencies session_deps_;
320 scoped_refptr<OrderedSocketData> data_;
321 scoped_refptr<HttpNetworkSession> http_session_;
322 scoped_refptr<SpdySession> session_;
323 scoped_refptr<TCPSocketParams> tcp_params_;
324 };
325
326 TEST_F(SpdyWebSocketStreamTest, Echo) {
327 EnableCompression(false);
328 SpdySession::SetSSLMode(false);
329
330 spdy::SpdyStreamId stream_id = 1;
331
332 scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyWebSocketHandshakeReq(
333 "ws://example.com/echo",
334 "http://example.com/wsdemo",
335 NULL,
336 false,
337 stream_id,
338 HIGHEST));
339 scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyWebSocketHandshakeResp(
340 "ws://example.com/echo",
341 "http://example.com/wsdemo",
342 NULL,
343 false,
344 stream_id,
345 HIGHEST));
346
347 static const char* const msg_frame = "\0hello\xff";
348 scoped_ptr<spdy::SpdyFrame> msg(ConstructSpdyWebSocketFrame(
349 msg_frame, 7, stream_id, false));
350
351 static const char* const closing_frame = "\xff\0";
352 scoped_ptr<spdy::SpdyFrame> closing(ConstructSpdyWebSocketFrame(
353 closing_frame, 2, stream_id, false));
354
355 MockWrite writes[] = {
356 CreateMockWrite(*req.get(), 1),
357 CreateMockWrite(*msg.get(), 3),
358 CreateMockWrite(*closing.get(), 5)
359 };
360
361 MockRead reads[] = {
362 CreateMockRead(*resp.get(), 2),
363 CreateMockRead(*msg.get(), 4),
364 // Skip sequence 6 to notify closing has been sent.
365 CreateMockRead(*closing.get(), 7),
366 MockRead(false, 0, 8) // EOF
367 };
368
369 HostPortPair host_port_pair("example.com", 80);
370 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
371 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
372 host_port_pair));
373
374 TestCompletionCallback callback;
375
376 scoped_ptr<SpdyWebSocketStreamEventRecorder> delegate(
377 new SpdyWebSocketStreamEventRecorder(&callback));
378 // Necessary for NewCallback.
379 SpdyWebSocketStreamTest* test = this;
380 delegate->SetOnReceivedHeader(
381 NewCallback(test, &SpdyWebSocketStreamTest::DoSendHelloFrame));
382 delegate->SetOnReceivedData(
383 NewCallback(test, &SpdyWebSocketStreamTest::DoSendClosingFrame));
384
385 scoped_ptr<SpdyWebSocketStream> websocket_stream(
386 new SpdyWebSocketStream(session_, delegate.get()));
387
388 BoundNetLog net_log;
389 GURL url("ws://example.com/echo");
390 ASSERT_EQ(OK, websocket_stream->InitializeStream(url, HIGHEST, net_log));
391
392 EXPECT_EQ(stream_id, websocket_stream->stream_id());
393
394
395 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
396 (*headers)["url"] = "ws://example.com/echo";
397 (*headers)["origin"] = "http://example.com/wsdemo";
398
399 websocket_stream->SendRequest(headers);
400
401 callback.WaitForResult();
402
403 const std::vector<SpdyWebSocketStreamEvent>& events =
404 delegate->GetSeenEvents();
405 ASSERT_EQ(8U, events.size());
406
407 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CREATED,
408 events[0].event_type);
409 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
410 events[1].event_type);
411 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
412 events[2].event_type);
413 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
414 events[3].event_type);
415 EXPECT_EQ(7, events[3].result);
416 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
417 events[4].event_type);
418 EXPECT_EQ(7, events[4].result);
419 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
420 events[5].event_type);
421 EXPECT_EQ(2, events[5].result);
422 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
423 events[6].event_type);
424 EXPECT_EQ(2, events[6].result);
425 EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE,
426 events[7].event_type);
427
428 EXPECT_TRUE(!http_session_->spdy_session_pool()->HasSession(pair));
429 EXPECT_TRUE(data()->at_read_eof());
430 EXPECT_TRUE(data()->at_write_eof());
431 }
432
433 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/spdy_websocket_stream.cc ('k') | net/websockets/websocket_job.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698