1 // Copyright (c) 2013 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/spdy/spdy_test_util_common.h"
6
7 #include <cstddef>
8
9 #include "base/compiler_specific.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "net/cert/mock_cert_verifier.h"
14 #include "net/http/http_cache.h"
15 #include "net/http/http_network_session.h"
16 #include "net/http/http_network_transaction.h"
17 #include "net/http/http_server_properties_impl.h"
18 #include "net/socket/socket_test_util.h"
19 #include "net/socket/ssl_client_socket.h"
20 #include "net/socket/transport_client_socket_pool.h"
21 #include "net/spdy/buffered_spdy_framer.h"
22 #include "net/spdy/spdy_framer.h"
23 #include "net/spdy/spdy_http_utils.h"
24 #include "net/spdy/spdy_session.h"
25 #include "net/spdy/spdy_session_pool.h"
26 #include "net/spdy/spdy_stream.h"
27 #include "net/url_request/url_request_job_factory_impl.h"
28
29 namespace net {
30
31 namespace {
32
next_proto_is_spdy(NextProto next_proto)33 bool next_proto_is_spdy(NextProto next_proto) {
34 return next_proto >= kProtoSPDYMinimumVersion &&
35 next_proto <= kProtoSPDYMaximumVersion;
36 }
37
38 // Parses a URL into the scheme, host, and path components required for a
39 // SPDY request.
ParseUrl(base::StringPiece url,std::string * scheme,std::string * host,std::string * path)40 void ParseUrl(base::StringPiece url, std::string* scheme, std::string* host,
41 std::string* path) {
42 GURL gurl(url.as_string());
43 path->assign(gurl.PathForRequest());
44 scheme->assign(gurl.scheme());
45 host->assign(gurl.host());
46 if (gurl.has_port()) {
47 host->append(":");
48 host->append(gurl.port());
49 }
50 }
51
52 } // namespace
53
SpdyNextProtos()54 NextProtoVector SpdyNextProtos() {
55 NextProtoVector next_protos;
56 for (int i = kProtoMinimumVersion; i <= kProtoMaximumVersion; ++i) {
57 next_protos.push_back(static_cast<NextProto>(i));
58 }
59 return next_protos;
60 }
61
62 // Chop a frame into an array of MockWrites.
63 // |data| is the frame to chop.
64 // |length| is the length of the frame to chop.
65 // |num_chunks| is the number of chunks to create.
ChopWriteFrame(const char * data,int length,int num_chunks)66 MockWrite* ChopWriteFrame(const char* data, int length, int num_chunks) {
67 MockWrite* chunks = new MockWrite[num_chunks];
68 int chunk_size = length / num_chunks;
69 for (int index = 0; index < num_chunks; index++) {
70 const char* ptr = data + (index * chunk_size);
71 if (index == num_chunks - 1)
72 chunk_size += length % chunk_size; // The last chunk takes the remainder.
73 chunks[index] = MockWrite(ASYNC, ptr, chunk_size);
74 }
75 return chunks;
76 }
77
78 // Chop a SpdyFrame into an array of MockWrites.
79 // |frame| is the frame to chop.
80 // |num_chunks| is the number of chunks to create.
ChopWriteFrame(const SpdyFrame & frame,int num_chunks)81 MockWrite* ChopWriteFrame(const SpdyFrame& frame, int num_chunks) {
82 return ChopWriteFrame(frame.data(), frame.size(), num_chunks);
83 }
84
85 // Chop a frame into an array of MockReads.
86 // |data| is the frame to chop.
87 // |length| is the length of the frame to chop.
88 // |num_chunks| is the number of chunks to create.
ChopReadFrame(const char * data,int length,int num_chunks)89 MockRead* ChopReadFrame(const char* data, int length, int num_chunks) {
90 MockRead* chunks = new MockRead[num_chunks];
91 int chunk_size = length / num_chunks;
92 for (int index = 0; index < num_chunks; index++) {
93 const char* ptr = data + (index * chunk_size);
94 if (index == num_chunks - 1)
95 chunk_size += length % chunk_size; // The last chunk takes the remainder.
96 chunks[index] = MockRead(ASYNC, ptr, chunk_size);
97 }
98 return chunks;
99 }
100
101 // Chop a SpdyFrame into an array of MockReads.
102 // |frame| is the frame to chop.
103 // |num_chunks| is the number of chunks to create.
ChopReadFrame(const SpdyFrame & frame,int num_chunks)104 MockRead* ChopReadFrame(const SpdyFrame& frame, int num_chunks) {
105 return ChopReadFrame(frame.data(), frame.size(), num_chunks);
106 }
107
108 // Adds headers and values to a map.
109 // |extra_headers| is an array of { name, value } pairs, arranged as strings
110 // where the even entries are the header names, and the odd entries are the
111 // header values.
112 // |headers| gets filled in from |extra_headers|.
AppendToHeaderBlock(const char * const extra_headers[],int extra_header_count,SpdyHeaderBlock * headers)113 void AppendToHeaderBlock(const char* const extra_headers[],
114 int extra_header_count,
115 SpdyHeaderBlock* headers) {
116 std::string this_header;
117 std::string this_value;
118
119 if (!extra_header_count)
120 return;
121
122 // Sanity check: Non-NULL header list.
123 DCHECK(NULL != extra_headers) << "NULL header value pair list";
124 // Sanity check: Non-NULL header map.
125 DCHECK(NULL != headers) << "NULL header map";
126 // Copy in the headers.
127 for (int i = 0; i < extra_header_count; i++) {
128 // Sanity check: Non-empty header.
129 DCHECK_NE('\0', *extra_headers[i * 2]) << "Empty header value pair";
130 this_header = extra_headers[i * 2];
131 std::string::size_type header_len = this_header.length();
132 if (!header_len)
133 continue;
134 this_value = extra_headers[1 + (i * 2)];
135 std::string new_value;
136 if (headers->find(this_header) != headers->end()) {
137 // More than one entry in the header.
138 // Don't add the header again, just the append to the value,
139 // separated by a NULL character.
140
141 // Adjust the value.
142 new_value = (*headers)[this_header];
143 // Put in a NULL separator.
144 new_value.append(1, '\0');
145 // Append the new value.
146 new_value += this_value;
147 } else {
148 // Not a duplicate, just write the value.
149 new_value = this_value;
150 }
151 (*headers)[this_header] = new_value;
152 }
153 }
154
155 // Create a MockWrite from the given SpdyFrame.
CreateMockWrite(const SpdyFrame & req)156 MockWrite CreateMockWrite(const SpdyFrame& req) {
157 return MockWrite(ASYNC, req.data(), req.size());
158 }
159
160 // Create a MockWrite from the given SpdyFrame and sequence number.
CreateMockWrite(const SpdyFrame & req,int seq)161 MockWrite CreateMockWrite(const SpdyFrame& req, int seq) {
162 return CreateMockWrite(req, seq, ASYNC);
163 }
164
165 // Create a MockWrite from the given SpdyFrame and sequence number.
CreateMockWrite(const SpdyFrame & req,int seq,IoMode mode)166 MockWrite CreateMockWrite(const SpdyFrame& req, int seq, IoMode mode) {
167 return MockWrite(mode, req.data(), req.size(), seq);
168 }
169
170 // Create a MockRead from the given SpdyFrame.
CreateMockRead(const SpdyFrame & resp)171 MockRead CreateMockRead(const SpdyFrame& resp) {
172 return MockRead(ASYNC, resp.data(), resp.size());
173 }
174
175 // Create a MockRead from the given SpdyFrame and sequence number.
CreateMockRead(const SpdyFrame & resp,int seq)176 MockRead CreateMockRead(const SpdyFrame& resp, int seq) {
177 return CreateMockRead(resp, seq, ASYNC);
178 }
179
180 // Create a MockRead from the given SpdyFrame and sequence number.
CreateMockRead(const SpdyFrame & resp,int seq,IoMode mode)181 MockRead CreateMockRead(const SpdyFrame& resp, int seq, IoMode mode) {
182 return MockRead(mode, resp.data(), resp.size(), seq);
183 }
184
185 // Combines the given SpdyFrames into the given char array and returns
186 // the total length.
CombineFrames(const SpdyFrame ** frames,int num_frames,char * buff,int buff_len)187 int CombineFrames(const SpdyFrame** frames, int num_frames,
188 char* buff, int buff_len) {
189 int total_len = 0;
190 for (int i = 0; i < num_frames; ++i) {
191 total_len += frames[i]->size();
192 }
193 DCHECK_LE(total_len, buff_len);
194 char* ptr = buff;
195 for (int i = 0; i < num_frames; ++i) {
196 int len = frames[i]->size();
197 memcpy(ptr, frames[i]->data(), len);
198 ptr += len;
199 }
200 return total_len;
201 }
202
203 namespace {
204
205 class PriorityGetter : public BufferedSpdyFramerVisitorInterface {
206 public:
PriorityGetter()207 PriorityGetter() : priority_(0) {}
~PriorityGetter()208 virtual ~PriorityGetter() {}
209
priority() const210 SpdyPriority priority() const {
211 return priority_;
212 }
213
OnError(SpdyFramer::SpdyError error_code)214 virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {}
OnStreamError(SpdyStreamId stream_id,const std::string & description)215 virtual void OnStreamError(SpdyStreamId stream_id,
216 const std::string& description) OVERRIDE {}
OnSynStream(SpdyStreamId stream_id,SpdyStreamId associated_stream_id,SpdyPriority priority,bool fin,bool unidirectional,const SpdyHeaderBlock & headers)217 virtual void OnSynStream(SpdyStreamId stream_id,
218 SpdyStreamId associated_stream_id,
219 SpdyPriority priority,
220 bool fin,
221 bool unidirectional,
222 const SpdyHeaderBlock& headers) OVERRIDE {
223 priority_ = priority;
224 }
OnSynReply(SpdyStreamId stream_id,bool fin,const SpdyHeaderBlock & headers)225 virtual void OnSynReply(SpdyStreamId stream_id,
226 bool fin,
227 const SpdyHeaderBlock& headers) OVERRIDE {}
OnHeaders(SpdyStreamId stream_id,bool fin,const SpdyHeaderBlock & headers)228 virtual void OnHeaders(SpdyStreamId stream_id,
229 bool fin,
230 const SpdyHeaderBlock& headers) OVERRIDE {}
OnDataFrameHeader(SpdyStreamId stream_id,size_t length,bool fin)231 virtual void OnDataFrameHeader(SpdyStreamId stream_id,
232 size_t length,
233 bool fin) OVERRIDE {}
OnStreamFrameData(SpdyStreamId stream_id,const char * data,size_t len,bool fin)234 virtual void OnStreamFrameData(SpdyStreamId stream_id,
235 const char* data,
236 size_t len,
237 bool fin) OVERRIDE {}
OnSettings(bool clear_persisted)238 virtual void OnSettings(bool clear_persisted) OVERRIDE {}
OnSetting(SpdySettingsIds id,uint8 flags,uint32 value)239 virtual void OnSetting(
240 SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE {}
OnPing(SpdyPingId unique_id,bool is_ack)241 virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {}
OnRstStream(SpdyStreamId stream_id,SpdyRstStreamStatus status)242 virtual void OnRstStream(SpdyStreamId stream_id,
243 SpdyRstStreamStatus status) OVERRIDE {}
OnGoAway(SpdyStreamId last_accepted_stream_id,SpdyGoAwayStatus status)244 virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
245 SpdyGoAwayStatus status) OVERRIDE {}
OnWindowUpdate(SpdyStreamId stream_id,uint32 delta_window_size)246 virtual void OnWindowUpdate(SpdyStreamId stream_id,
247 uint32 delta_window_size) OVERRIDE {}
OnPushPromise(SpdyStreamId stream_id,SpdyStreamId promised_stream_id,const SpdyHeaderBlock & headers)248 virtual void OnPushPromise(SpdyStreamId stream_id,
249 SpdyStreamId promised_stream_id,
250 const SpdyHeaderBlock& headers) OVERRIDE {}
251
252 private:
253 SpdyPriority priority_;
254 };
255
256 } // namespace
257
GetSpdyPriority(SpdyMajorVersion version,const SpdyFrame & frame,SpdyPriority * priority)258 bool GetSpdyPriority(SpdyMajorVersion version,
259 const SpdyFrame& frame,
260 SpdyPriority* priority) {
261 BufferedSpdyFramer framer(version, false);
262 PriorityGetter priority_getter;
263 framer.set_visitor(&priority_getter);
264 size_t frame_size = frame.size();
265 if (framer.ProcessInput(frame.data(), frame_size) != frame_size) {
266 return false;
267 }
268 *priority = priority_getter.priority();
269 return true;
270 }
271
CreateStreamSynchronously(SpdyStreamType type,const base::WeakPtr<SpdySession> & session,const GURL & url,RequestPriority priority,const BoundNetLog & net_log)272 base::WeakPtr<SpdyStream> CreateStreamSynchronously(
273 SpdyStreamType type,
274 const base::WeakPtr<SpdySession>& session,
275 const GURL& url,
276 RequestPriority priority,
277 const BoundNetLog& net_log) {
278 SpdyStreamRequest stream_request;
279 int rv = stream_request.StartRequest(type, session, url, priority, net_log,
280 CompletionCallback());
281 return
282 (rv == OK) ? stream_request.ReleaseStream() : base::WeakPtr<SpdyStream>();
283 }
284
StreamReleaserCallback()285 StreamReleaserCallback::StreamReleaserCallback() {}
286
~StreamReleaserCallback()287 StreamReleaserCallback::~StreamReleaserCallback() {}
288
MakeCallback(SpdyStreamRequest * request)289 CompletionCallback StreamReleaserCallback::MakeCallback(
290 SpdyStreamRequest* request) {
291 return base::Bind(&StreamReleaserCallback::OnComplete,
292 base::Unretained(this),
293 request);
294 }
295
OnComplete(SpdyStreamRequest * request,int result)296 void StreamReleaserCallback::OnComplete(
297 SpdyStreamRequest* request, int result) {
298 if (result == OK)
299 request->ReleaseStream()->Cancel();
300 SetResult(result);
301 }
302
MockECSignatureCreator(crypto::ECPrivateKey * key)303 MockECSignatureCreator::MockECSignatureCreator(crypto::ECPrivateKey* key)
304 : key_(key) {
305 }
306
Sign(const uint8 * data,int data_len,std::vector<uint8> * signature)307 bool MockECSignatureCreator::Sign(const uint8* data,
308 int data_len,
309 std::vector<uint8>* signature) {
310 std::vector<uint8> private_key_value;
311 key_->ExportValue(&private_key_value);
312 std::string head = "fakesignature";
313 std::string tail = "/fakesignature";
314
315 signature->clear();
316 signature->insert(signature->end(), head.begin(), head.end());
317 signature->insert(signature->end(), private_key_value.begin(),
318 private_key_value.end());
319 signature->insert(signature->end(), '-');
320 signature->insert(signature->end(), data, data + data_len);
321 signature->insert(signature->end(), tail.begin(), tail.end());
322 return true;
323 }
324
DecodeSignature(const std::vector<uint8> & signature,std::vector<uint8> * out_raw_sig)325 bool MockECSignatureCreator::DecodeSignature(
326 const std::vector<uint8>& signature,
327 std::vector<uint8>* out_raw_sig) {
328 *out_raw_sig = signature;
329 return true;
330 }
331
MockECSignatureCreatorFactory()332 MockECSignatureCreatorFactory::MockECSignatureCreatorFactory() {
333 crypto::ECSignatureCreator::SetFactoryForTesting(this);
334 }
335
~MockECSignatureCreatorFactory()336 MockECSignatureCreatorFactory::~MockECSignatureCreatorFactory() {
337 crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
338 }
339
Create(crypto::ECPrivateKey * key)340 crypto::ECSignatureCreator* MockECSignatureCreatorFactory::Create(
341 crypto::ECPrivateKey* key) {
342 return new MockECSignatureCreator(key);
343 }
344
SpdySessionDependencies(NextProto protocol)345 SpdySessionDependencies::SpdySessionDependencies(NextProto protocol)
346 : host_resolver(new MockCachingHostResolver),
347 cert_verifier(new MockCertVerifier),
348 transport_security_state(new TransportSecurityState),
349 proxy_service(ProxyService::CreateDirect()),
350 ssl_config_service(new SSLConfigServiceDefaults),
351 socket_factory(new MockClientSocketFactory),
352 deterministic_socket_factory(new DeterministicMockClientSocketFactory),
353 http_auth_handler_factory(
354 HttpAuthHandlerFactory::CreateDefault(host_resolver.get())),
355 enable_ip_pooling(true),
356 enable_compression(false),
357 enable_ping(false),
358 enable_user_alternate_protocol_ports(false),
359 protocol(protocol),
360 stream_initial_recv_window_size(kSpdyStreamInitialWindowSize),
361 time_func(&base::TimeTicks::Now),
362 force_spdy_over_ssl(false),
363 force_spdy_always(false),
364 use_alternate_protocols(false),
365 enable_websocket_over_spdy(false),
366 net_log(NULL) {
367 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
368
369 // Note: The CancelledTransaction test does cleanup by running all
370 // tasks in the message loop (RunAllPending). Unfortunately, that
371 // doesn't clean up tasks on the host resolver thread; and
372 // TCPConnectJob is currently not cancellable. Using synchronous
373 // lookups allows the test to shutdown cleanly. Until we have
374 // cancellable TCPConnectJobs, use synchronous lookups.
375 host_resolver->set_synchronous_mode(true);
376 }
377
SpdySessionDependencies(NextProto protocol,ProxyService * proxy_service)378 SpdySessionDependencies::SpdySessionDependencies(
379 NextProto protocol, ProxyService* proxy_service)
380 : host_resolver(new MockHostResolver),
381 cert_verifier(new MockCertVerifier),
382 transport_security_state(new TransportSecurityState),
383 proxy_service(proxy_service),
384 ssl_config_service(new SSLConfigServiceDefaults),
385 socket_factory(new MockClientSocketFactory),
386 deterministic_socket_factory(new DeterministicMockClientSocketFactory),
387 http_auth_handler_factory(
388 HttpAuthHandlerFactory::CreateDefault(host_resolver.get())),
389 enable_ip_pooling(true),
390 enable_compression(false),
391 enable_ping(false),
392 enable_user_alternate_protocol_ports(false),
393 protocol(protocol),
394 stream_initial_recv_window_size(kSpdyStreamInitialWindowSize),
395 time_func(&base::TimeTicks::Now),
396 force_spdy_over_ssl(false),
397 force_spdy_always(false),
398 use_alternate_protocols(false),
399 enable_websocket_over_spdy(false),
400 net_log(NULL) {
401 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
402 }
403
~SpdySessionDependencies()404 SpdySessionDependencies::~SpdySessionDependencies() {}
405
406 // static
SpdyCreateSession(SpdySessionDependencies * session_deps)407 HttpNetworkSession* SpdySessionDependencies::SpdyCreateSession(
408 SpdySessionDependencies* session_deps) {
409 net::HttpNetworkSession::Params params = CreateSessionParams(session_deps);
410 params.client_socket_factory = session_deps->socket_factory.get();
411 HttpNetworkSession* http_session = new HttpNetworkSession(params);
412 SpdySessionPoolPeer pool_peer(http_session->spdy_session_pool());
413 pool_peer.SetEnableSendingInitialData(false);
414 return http_session;
415 }
416
417 // static
SpdyCreateSessionDeterministic(SpdySessionDependencies * session_deps)418 HttpNetworkSession* SpdySessionDependencies::SpdyCreateSessionDeterministic(
419 SpdySessionDependencies* session_deps) {
420 net::HttpNetworkSession::Params params = CreateSessionParams(session_deps);
421 params.client_socket_factory =
422 session_deps->deterministic_socket_factory.get();
423 HttpNetworkSession* http_session = new HttpNetworkSession(params);
424 SpdySessionPoolPeer pool_peer(http_session->spdy_session_pool());
425 pool_peer.SetEnableSendingInitialData(false);
426 return http_session;
427 }
428
429 // static
CreateSessionParams(SpdySessionDependencies * session_deps)430 net::HttpNetworkSession::Params SpdySessionDependencies::CreateSessionParams(
431 SpdySessionDependencies* session_deps) {
432 DCHECK(next_proto_is_spdy(session_deps->protocol)) <<
433 "Invalid protocol: " << session_deps->protocol;
434
435 net::HttpNetworkSession::Params params;
436 params.host_resolver = session_deps->host_resolver.get();
437 params.cert_verifier = session_deps->cert_verifier.get();
438 params.transport_security_state =
439 session_deps->transport_security_state.get();
440 params.proxy_service = session_deps->proxy_service.get();
441 params.ssl_config_service = session_deps->ssl_config_service.get();
442 params.http_auth_handler_factory =
443 session_deps->http_auth_handler_factory.get();
444 params.http_server_properties =
445 session_deps->http_server_properties.GetWeakPtr();
446 params.enable_spdy_compression = session_deps->enable_compression;
447 params.enable_spdy_ping_based_connection_checking = session_deps->enable_ping;
448 params.enable_user_alternate_protocol_ports =
449 session_deps->enable_user_alternate_protocol_ports;
450 params.spdy_default_protocol = session_deps->protocol;
451 params.spdy_stream_initial_recv_window_size =
452 session_deps->stream_initial_recv_window_size;
453 params.time_func = session_deps->time_func;
454 params.next_protos = session_deps->next_protos;
455 params.trusted_spdy_proxy = session_deps->trusted_spdy_proxy;
456 params.force_spdy_over_ssl = session_deps->force_spdy_over_ssl;
457 params.force_spdy_always = session_deps->force_spdy_always;
458 params.use_alternate_protocols = session_deps->use_alternate_protocols;
459 params.enable_websocket_over_spdy = session_deps->enable_websocket_over_spdy;
460 params.net_log = session_deps->net_log;
461 return params;
462 }
463
SpdyURLRequestContext(NextProto protocol,bool force_spdy_over_ssl,bool force_spdy_always)464 SpdyURLRequestContext::SpdyURLRequestContext(NextProto protocol,
465 bool force_spdy_over_ssl,
466 bool force_spdy_always)
467 : storage_(this) {
468 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
469
470 storage_.set_host_resolver(scoped_ptr<HostResolver>(new MockHostResolver));
471 storage_.set_cert_verifier(new MockCertVerifier);
472 storage_.set_transport_security_state(new TransportSecurityState);
473 storage_.set_proxy_service(ProxyService::CreateDirect());
474 storage_.set_ssl_config_service(new SSLConfigServiceDefaults);
475 storage_.set_http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault(
476 host_resolver()));
477 storage_.set_http_server_properties(
478 scoped_ptr<HttpServerProperties>(new HttpServerPropertiesImpl()));
479 storage_.set_job_factory(new URLRequestJobFactoryImpl());
480 net::HttpNetworkSession::Params params;
481 params.client_socket_factory = &socket_factory_;
482 params.host_resolver = host_resolver();
483 params.cert_verifier = cert_verifier();
484 params.transport_security_state = transport_security_state();
485 params.proxy_service = proxy_service();
486 params.ssl_config_service = ssl_config_service();
487 params.http_auth_handler_factory = http_auth_handler_factory();
488 params.network_delegate = network_delegate();
489 params.enable_spdy_compression = false;
490 params.enable_spdy_ping_based_connection_checking = false;
491 params.spdy_default_protocol = protocol;
492 params.force_spdy_over_ssl = force_spdy_over_ssl;
493 params.force_spdy_always = force_spdy_always;
494 params.http_server_properties = http_server_properties();
495 scoped_refptr<HttpNetworkSession> network_session(
496 new HttpNetworkSession(params));
497 SpdySessionPoolPeer pool_peer(network_session->spdy_session_pool());
498 pool_peer.SetEnableSendingInitialData(false);
499 storage_.set_http_transaction_factory(new HttpCache(
500 network_session.get(), HttpCache::DefaultBackend::InMemory(0)));
501 }
502
~SpdyURLRequestContext()503 SpdyURLRequestContext::~SpdyURLRequestContext() {
504 }
505
HasSpdySession(SpdySessionPool * pool,const SpdySessionKey & key)506 bool HasSpdySession(SpdySessionPool* pool, const SpdySessionKey& key) {
507 return pool->FindAvailableSession(key, BoundNetLog()) != NULL;
508 }
509
510 namespace {
511
CreateSpdySessionHelper(const scoped_refptr<HttpNetworkSession> & http_session,const SpdySessionKey & key,const BoundNetLog & net_log,Error expected_status,bool is_secure)512 base::WeakPtr<SpdySession> CreateSpdySessionHelper(
513 const scoped_refptr<HttpNetworkSession>& http_session,
514 const SpdySessionKey& key,
515 const BoundNetLog& net_log,
516 Error expected_status,
517 bool is_secure) {
518 EXPECT_FALSE(HasSpdySession(http_session->spdy_session_pool(), key));
519
520 scoped_refptr<TransportSocketParams> transport_params(
521 new TransportSocketParams(
522 key.host_port_pair(), false, false,
523 OnHostResolutionCallback()));
524
525 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
526 TestCompletionCallback callback;
527
528 int rv = ERR_UNEXPECTED;
529 if (is_secure) {
530 SSLConfig ssl_config;
531 scoped_refptr<SSLSocketParams> ssl_params(
532 new SSLSocketParams(transport_params,
533 NULL,
534 NULL,
535 key.host_port_pair(),
536 ssl_config,
537 key.privacy_mode(),
538 0,
539 false,
540 false));
541 rv = connection->Init(key.host_port_pair().ToString(),
542 ssl_params,
543 MEDIUM,
544 callback.callback(),
545 http_session->GetSSLSocketPool(
546 HttpNetworkSession::NORMAL_SOCKET_POOL),
547 net_log);
548 } else {
549 rv = connection->Init(key.host_port_pair().ToString(),
550 transport_params,
551 MEDIUM,
552 callback.callback(),
553 http_session->GetTransportSocketPool(
554 HttpNetworkSession::NORMAL_SOCKET_POOL),
555 net_log);
556 }
557
558 if (rv == ERR_IO_PENDING)
559 rv = callback.WaitForResult();
560
561 EXPECT_EQ(OK, rv);
562
563 base::WeakPtr<SpdySession> spdy_session =
564 http_session->spdy_session_pool()->CreateAvailableSessionFromSocket(
565 key, connection.Pass(), net_log, OK, is_secure);
566 // Failure is reported asynchronously.
567 EXPECT_TRUE(spdy_session != NULL);
568 EXPECT_TRUE(HasSpdySession(http_session->spdy_session_pool(), key));
569 return spdy_session;
570 }
571
572 } // namespace
573
CreateInsecureSpdySession(const scoped_refptr<HttpNetworkSession> & http_session,const SpdySessionKey & key,const BoundNetLog & net_log)574 base::WeakPtr<SpdySession> CreateInsecureSpdySession(
575 const scoped_refptr<HttpNetworkSession>& http_session,
576 const SpdySessionKey& key,
577 const BoundNetLog& net_log) {
578 return CreateSpdySessionHelper(http_session, key, net_log,
579 OK, false /* is_secure */);
580 }
581
TryCreateInsecureSpdySessionExpectingFailure(const scoped_refptr<HttpNetworkSession> & http_session,const SpdySessionKey & key,Error expected_error,const BoundNetLog & net_log)582 base::WeakPtr<SpdySession> TryCreateInsecureSpdySessionExpectingFailure(
583 const scoped_refptr<HttpNetworkSession>& http_session,
584 const SpdySessionKey& key,
585 Error expected_error,
586 const BoundNetLog& net_log) {
587 DCHECK_LT(expected_error, ERR_IO_PENDING);
588 return CreateSpdySessionHelper(http_session, key, net_log,
589 expected_error, false /* is_secure */);
590 }
591
CreateSecureSpdySession(const scoped_refptr<HttpNetworkSession> & http_session,const SpdySessionKey & key,const BoundNetLog & net_log)592 base::WeakPtr<SpdySession> CreateSecureSpdySession(
593 const scoped_refptr<HttpNetworkSession>& http_session,
594 const SpdySessionKey& key,
595 const BoundNetLog& net_log) {
596 return CreateSpdySessionHelper(http_session, key, net_log,
597 OK, true /* is_secure */);
598 }
599
600 namespace {
601
602 // A ClientSocket used for CreateFakeSpdySession() below.
603 class FakeSpdySessionClientSocket : public MockClientSocket {
604 public:
FakeSpdySessionClientSocket(int read_result)605 FakeSpdySessionClientSocket(int read_result)
606 : MockClientSocket(BoundNetLog()),
607 read_result_(read_result) {}
608
~FakeSpdySessionClientSocket()609 virtual ~FakeSpdySessionClientSocket() {}
610
Read(IOBuffer * buf,int buf_len,const CompletionCallback & callback)611 virtual int Read(IOBuffer* buf, int buf_len,
612 const CompletionCallback& callback) OVERRIDE {
613 return read_result_;
614 }
615
Write(IOBuffer * buf,int buf_len,const CompletionCallback & callback)616 virtual int Write(IOBuffer* buf, int buf_len,
617 const CompletionCallback& callback) OVERRIDE {
618 return ERR_IO_PENDING;
619 }
620
621 // Return kProtoUnknown to use the pool's default protocol.
GetNegotiatedProtocol() const622 virtual NextProto GetNegotiatedProtocol() const OVERRIDE {
623 return kProtoUnknown;
624 }
625
626 // The functions below are not expected to be called.
627
Connect(const CompletionCallback & callback)628 virtual int Connect(const CompletionCallback& callback) OVERRIDE {
629 ADD_FAILURE();
630 return ERR_UNEXPECTED;
631 }
632
WasEverUsed() const633 virtual bool WasEverUsed() const OVERRIDE {
634 ADD_FAILURE();
635 return false;
636 }
637
UsingTCPFastOpen() const638 virtual bool UsingTCPFastOpen() const OVERRIDE {
639 ADD_FAILURE();
640 return false;
641 }
642
WasNpnNegotiated() const643 virtual bool WasNpnNegotiated() const OVERRIDE {
644 ADD_FAILURE();
645 return false;
646 }
647
GetSSLInfo(SSLInfo * ssl_info)648 virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {
649 ADD_FAILURE();
650 return false;
651 }
652
653 private:
654 int read_result_;
655 };
656
CreateFakeSpdySessionHelper(SpdySessionPool * pool,const SpdySessionKey & key,Error expected_status)657 base::WeakPtr<SpdySession> CreateFakeSpdySessionHelper(
658 SpdySessionPool* pool,
659 const SpdySessionKey& key,
660 Error expected_status) {
661 EXPECT_NE(expected_status, ERR_IO_PENDING);
662 EXPECT_FALSE(HasSpdySession(pool, key));
663 scoped_ptr<ClientSocketHandle> handle(new ClientSocketHandle());
664 handle->SetSocket(scoped_ptr<StreamSocket>(new FakeSpdySessionClientSocket(
665 expected_status == OK ? ERR_IO_PENDING : expected_status)));
666 base::WeakPtr<SpdySession> spdy_session =
667 pool->CreateAvailableSessionFromSocket(
668 key, handle.Pass(), BoundNetLog(), OK, true /* is_secure */);
669 // Failure is reported asynchronously.
670 EXPECT_TRUE(spdy_session != NULL);
671 EXPECT_TRUE(HasSpdySession(pool, key));
672 return spdy_session;
673 }
674
675 } // namespace
676
CreateFakeSpdySession(SpdySessionPool * pool,const SpdySessionKey & key)677 base::WeakPtr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool,
678 const SpdySessionKey& key) {
679 return CreateFakeSpdySessionHelper(pool, key, OK);
680 }
681
TryCreateFakeSpdySessionExpectingFailure(SpdySessionPool * pool,const SpdySessionKey & key,Error expected_error)682 base::WeakPtr<SpdySession> TryCreateFakeSpdySessionExpectingFailure(
683 SpdySessionPool* pool,
684 const SpdySessionKey& key,
685 Error expected_error) {
686 DCHECK_LT(expected_error, ERR_IO_PENDING);
687 return CreateFakeSpdySessionHelper(pool, key, expected_error);
688 }
689
SpdySessionPoolPeer(SpdySessionPool * pool)690 SpdySessionPoolPeer::SpdySessionPoolPeer(SpdySessionPool* pool) : pool_(pool) {
691 }
692
RemoveAliases(const SpdySessionKey & key)693 void SpdySessionPoolPeer::RemoveAliases(const SpdySessionKey& key) {
694 pool_->RemoveAliases(key);
695 }
696
DisableDomainAuthenticationVerification()697 void SpdySessionPoolPeer::DisableDomainAuthenticationVerification() {
698 pool_->verify_domain_authentication_ = false;
699 }
700
SetEnableSendingInitialData(bool enabled)701 void SpdySessionPoolPeer::SetEnableSendingInitialData(bool enabled) {
702 pool_->enable_sending_initial_data_ = enabled;
703 }
704
SpdyTestUtil(NextProto protocol)705 SpdyTestUtil::SpdyTestUtil(NextProto protocol)
706 : protocol_(protocol),
707 spdy_version_(NextProtoToSpdyMajorVersion(protocol)) {
708 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
709 }
710
AddUrlToHeaderBlock(base::StringPiece url,SpdyHeaderBlock * headers) const711 void SpdyTestUtil::AddUrlToHeaderBlock(base::StringPiece url,
712 SpdyHeaderBlock* headers) const {
713 if (is_spdy2()) {
714 (*headers)["url"] = url.as_string();
715 } else {
716 std::string scheme, host, path;
717 ParseUrl(url, &scheme, &host, &path);
718 (*headers)[GetSchemeKey()] = scheme;
719 (*headers)[GetHostKey()] = host;
720 (*headers)[GetPathKey()] = path;
721 }
722 }
723
ConstructGetHeaderBlock(base::StringPiece url) const724 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructGetHeaderBlock(
725 base::StringPiece url) const {
726 return ConstructHeaderBlock("GET", url, NULL);
727 }
728
ConstructGetHeaderBlockForProxy(base::StringPiece url) const729 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructGetHeaderBlockForProxy(
730 base::StringPiece url) const {
731 scoped_ptr<SpdyHeaderBlock> headers(ConstructGetHeaderBlock(url));
732 if (is_spdy2())
733 (*headers)[GetPathKey()] = url.data();
734 return headers.Pass();
735 }
736
ConstructHeadHeaderBlock(base::StringPiece url,int64 content_length) const737 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeadHeaderBlock(
738 base::StringPiece url,
739 int64 content_length) const {
740 return ConstructHeaderBlock("HEAD", url, &content_length);
741 }
742
ConstructPostHeaderBlock(base::StringPiece url,int64 content_length) const743 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPostHeaderBlock(
744 base::StringPiece url,
745 int64 content_length) const {
746 return ConstructHeaderBlock("POST", url, &content_length);
747 }
748
ConstructPutHeaderBlock(base::StringPiece url,int64 content_length) const749 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPutHeaderBlock(
750 base::StringPiece url,
751 int64 content_length) const {
752 return ConstructHeaderBlock("PUT", url, &content_length);
753 }
754
ConstructSpdyFrame(const SpdyHeaderInfo & header_info,scoped_ptr<SpdyHeaderBlock> headers) const755 SpdyFrame* SpdyTestUtil::ConstructSpdyFrame(
756 const SpdyHeaderInfo& header_info,
757 scoped_ptr<SpdyHeaderBlock> headers) const {
758 BufferedSpdyFramer framer(spdy_version_, header_info.compressed);
759 SpdyFrame* frame = NULL;
760 switch (header_info.kind) {
761 case DATA:
762 frame = framer.CreateDataFrame(header_info.id, header_info.data,
763 header_info.data_length,
764 header_info.data_flags);
765 break;
766 case SYN_STREAM:
767 {
768 frame = framer.CreateSynStream(header_info.id, header_info.assoc_id,
769 header_info.priority,
770 header_info.control_flags,
771 headers.get());
772 }
773 break;
774 case SYN_REPLY:
775 frame = framer.CreateSynReply(header_info.id, header_info.control_flags,
776 headers.get());
777 break;
778 case RST_STREAM:
779 frame = framer.CreateRstStream(header_info.id, header_info.status);
780 break;
781 case HEADERS:
782 frame = framer.CreateHeaders(header_info.id, header_info.control_flags,
783 headers.get());
784 break;
785 default:
786 ADD_FAILURE();
787 break;
788 }
789 return frame;
790 }
791
ConstructSpdyFrame(const SpdyHeaderInfo & header_info,const char * const extra_headers[],int extra_header_count,const char * const tail_headers[],int tail_header_count) const792 SpdyFrame* SpdyTestUtil::ConstructSpdyFrame(const SpdyHeaderInfo& header_info,
793 const char* const extra_headers[],
794 int extra_header_count,
795 const char* const tail_headers[],
796 int tail_header_count) const {
797 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
798 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
799 if (tail_headers && tail_header_count)
800 AppendToHeaderBlock(tail_headers, tail_header_count, headers.get());
801 return ConstructSpdyFrame(header_info, headers.Pass());
802 }
803
ConstructSpdyControlFrame(scoped_ptr<SpdyHeaderBlock> headers,bool compressed,SpdyStreamId stream_id,RequestPriority request_priority,SpdyFrameType type,SpdyControlFlags flags,SpdyStreamId associated_stream_id) const804 SpdyFrame* SpdyTestUtil::ConstructSpdyControlFrame(
805 scoped_ptr<SpdyHeaderBlock> headers,
806 bool compressed,
807 SpdyStreamId stream_id,
808 RequestPriority request_priority,
809 SpdyFrameType type,
810 SpdyControlFlags flags,
811 SpdyStreamId associated_stream_id) const {
812 EXPECT_GE(type, FIRST_CONTROL_TYPE);
813 EXPECT_LE(type, LAST_CONTROL_TYPE);
814 const SpdyHeaderInfo header_info = {
815 type,
816 stream_id,
817 associated_stream_id,
818 ConvertRequestPriorityToSpdyPriority(request_priority, spdy_version_),
819 0, // credential slot
820 flags,
821 compressed,
822 RST_STREAM_INVALID, // status
823 NULL, // data
824 0, // length
825 DATA_FLAG_NONE
826 };
827 return ConstructSpdyFrame(header_info, headers.Pass());
828 }
829
ConstructSpdyControlFrame(const char * const extra_headers[],int extra_header_count,bool compressed,SpdyStreamId stream_id,RequestPriority request_priority,SpdyFrameType type,SpdyControlFlags flags,const char * const * tail_headers,int tail_header_size,SpdyStreamId associated_stream_id) const830 SpdyFrame* SpdyTestUtil::ConstructSpdyControlFrame(
831 const char* const extra_headers[],
832 int extra_header_count,
833 bool compressed,
834 SpdyStreamId stream_id,
835 RequestPriority request_priority,
836 SpdyFrameType type,
837 SpdyControlFlags flags,
838 const char* const* tail_headers,
839 int tail_header_size,
840 SpdyStreamId associated_stream_id) const {
841 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
842 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
843 if (tail_headers && tail_header_size)
844 AppendToHeaderBlock(tail_headers, tail_header_size / 2, headers.get());
845 return ConstructSpdyControlFrame(
846 headers.Pass(), compressed, stream_id,
847 request_priority, type, flags, associated_stream_id);
848 }
849
ConstructSpdyReplyString(const SpdyHeaderBlock & headers) const850 std::string SpdyTestUtil::ConstructSpdyReplyString(
851 const SpdyHeaderBlock& headers) const {
852 std::string reply_string;
853 for (SpdyHeaderBlock::const_iterator it = headers.begin();
854 it != headers.end(); ++it) {
855 std::string key = it->first;
856 // Remove leading colon from "special" headers (for SPDY3 and
857 // above).
858 if (spdy_version() >= SPDY3 && key[0] == ':')
859 key = key.substr(1);
860 std::vector<std::string> values;
861 base::SplitString(it->second, '\0', &values);
862 for (std::vector<std::string>::const_iterator it2 = values.begin();
863 it2 != values.end(); ++it2) {
864 reply_string += key + ": " + *it2 + "\n";
865 }
866 }
867 return reply_string;
868 }
869
870 // TODO(jgraettinger): Eliminate uses of this method in tests (prefer
871 // SpdySettingsIR).
ConstructSpdySettings(const SettingsMap & settings) const872 SpdyFrame* SpdyTestUtil::ConstructSpdySettings(
873 const SettingsMap& settings) const {
874 SpdySettingsIR settings_ir;
875 for (SettingsMap::const_iterator it = settings.begin();
876 it != settings.end();
877 ++it) {
878 settings_ir.AddSetting(
879 it->first,
880 (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0,
881 (it->second.first & SETTINGS_FLAG_PERSISTED) != 0,
882 it->second.second);
883 }
884 return CreateFramer(false)->SerializeFrame(settings_ir);
885 }
886
ConstructSpdySettingsAck() const887 SpdyFrame* SpdyTestUtil::ConstructSpdySettingsAck() const {
888 char kEmptyWrite[] = "";
889
890 if (spdy_version() > SPDY3) {
891 SpdySettingsIR settings_ir;
892 settings_ir.set_is_ack(true);
893 return CreateFramer(false)->SerializeFrame(settings_ir);
894 }
895 // No settings ACK write occurs. Create an empty placeholder write.
896 return new SpdyFrame(kEmptyWrite, 0, false);
897 }
898
ConstructSpdyPing(uint32 ping_id,bool is_ack) const899 SpdyFrame* SpdyTestUtil::ConstructSpdyPing(uint32 ping_id, bool is_ack) const {
900 SpdyPingIR ping_ir(ping_id);
901 ping_ir.set_is_ack(is_ack);
902 return CreateFramer(false)->SerializeFrame(ping_ir);
903 }
904
ConstructSpdyGoAway() const905 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway() const {
906 return ConstructSpdyGoAway(0);
907 }
908
ConstructSpdyGoAway(SpdyStreamId last_good_stream_id) const909 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway(
910 SpdyStreamId last_good_stream_id) const {
911 SpdyGoAwayIR go_ir(last_good_stream_id, GOAWAY_OK, "go away");
912 return CreateFramer(false)->SerializeFrame(go_ir);
913 }
914
ConstructSpdyGoAway(SpdyStreamId last_good_stream_id,SpdyGoAwayStatus status,const std::string & desc) const915 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway(SpdyStreamId last_good_stream_id,
916 SpdyGoAwayStatus status,
917 const std::string& desc) const {
918 SpdyGoAwayIR go_ir(last_good_stream_id, status, desc);
919 return CreateFramer(false)->SerializeFrame(go_ir);
920 }
921
ConstructSpdyWindowUpdate(const SpdyStreamId stream_id,uint32 delta_window_size) const922 SpdyFrame* SpdyTestUtil::ConstructSpdyWindowUpdate(
923 const SpdyStreamId stream_id, uint32 delta_window_size) const {
924 SpdyWindowUpdateIR update_ir(stream_id, delta_window_size);
925 return CreateFramer(false)->SerializeFrame(update_ir);
926 }
927
928 // TODO(jgraettinger): Eliminate uses of this method in tests (prefer
929 // SpdyRstStreamIR).
ConstructSpdyRstStream(SpdyStreamId stream_id,SpdyRstStreamStatus status) const930 SpdyFrame* SpdyTestUtil::ConstructSpdyRstStream(
931 SpdyStreamId stream_id,
932 SpdyRstStreamStatus status) const {
933 SpdyRstStreamIR rst_ir(stream_id, status, "");
934 return CreateFramer(false)->SerializeRstStream(rst_ir);
935 }
936
ConstructSpdyGet(const char * const url,bool compressed,SpdyStreamId stream_id,RequestPriority request_priority) const937 SpdyFrame* SpdyTestUtil::ConstructSpdyGet(
938 const char* const url,
939 bool compressed,
940 SpdyStreamId stream_id,
941 RequestPriority request_priority) const {
942 const SpdyHeaderInfo header_info = {
943 SYN_STREAM,
944 stream_id,
945 0, // associated stream ID
946 ConvertRequestPriorityToSpdyPriority(request_priority, spdy_version_),
947 0, // credential slot
948 CONTROL_FLAG_FIN,
949 compressed,
950 RST_STREAM_INVALID, // status
951 NULL, // data
952 0, // length
953 DATA_FLAG_NONE
954 };
955 return ConstructSpdyFrame(header_info, ConstructGetHeaderBlock(url));
956 }
957
ConstructSpdyGet(const char * const extra_headers[],int extra_header_count,bool compressed,int stream_id,RequestPriority request_priority,bool direct) const958 SpdyFrame* SpdyTestUtil::ConstructSpdyGet(const char* const extra_headers[],
959 int extra_header_count,
960 bool compressed,
961 int stream_id,
962 RequestPriority request_priority,
963 bool direct) const {
964 SpdySynStreamIR syn_stream(stream_id);
965 syn_stream.set_priority(
966 ConvertRequestPriorityToSpdyPriority(request_priority, spdy_version_));
967 syn_stream.set_fin(true);
968 syn_stream.SetHeader(GetMethodKey(), "GET");
969 syn_stream.SetHeader(GetHostKey(), "www.google.com");
970 syn_stream.SetHeader(GetSchemeKey(), "http");
971 syn_stream.SetHeader(GetPathKey(), (is_spdy2() && !direct) ?
972 "http://www.google.com/" : "/");
973 MaybeAddVersionHeader(&syn_stream);
974 AppendToHeaderBlock(extra_headers, extra_header_count,
975 syn_stream.mutable_name_value_block());
976 return CreateFramer(compressed)->SerializeFrame(syn_stream);
977 }
978
ConstructSpdyConnect(const char * const extra_headers[],int extra_header_count,int stream_id,RequestPriority priority) const979 SpdyFrame* SpdyTestUtil::ConstructSpdyConnect(
980 const char* const extra_headers[],
981 int extra_header_count,
982 int stream_id,
983 RequestPriority priority) const {
984 SpdySynStreamIR syn_stream(stream_id);
985 syn_stream.set_priority(
986 ConvertRequestPriorityToSpdyPriority(priority, spdy_version_));
987 syn_stream.SetHeader(GetMethodKey(), "CONNECT");
988 syn_stream.SetHeader(GetPathKey(), "www.google.com:443");
989 syn_stream.SetHeader(GetHostKey(), "www.google.com");
990 MaybeAddVersionHeader(&syn_stream);
991 AppendToHeaderBlock(extra_headers, extra_header_count,
992 syn_stream.mutable_name_value_block());
993 return CreateFramer(false)->SerializeFrame(syn_stream);
994 }
995
ConstructSpdyPush(const char * const extra_headers[],int extra_header_count,int stream_id,int associated_stream_id,const char * url)996 SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
997 int extra_header_count,
998 int stream_id,
999 int associated_stream_id,
1000 const char* url) {
1001 if (spdy_version() < SPDY4) {
1002 SpdySynStreamIR syn_stream(stream_id);
1003 syn_stream.set_associated_to_stream_id(associated_stream_id);
1004 syn_stream.SetHeader("hello", "bye");
1005 syn_stream.SetHeader(GetStatusKey(), "200 OK");
1006 syn_stream.SetHeader(GetVersionKey(), "HTTP/1.1");
1007 AddUrlToHeaderBlock(url, syn_stream.mutable_name_value_block());
1008 AppendToHeaderBlock(extra_headers,
1009 extra_header_count,
1010 syn_stream.mutable_name_value_block());
1011 return CreateFramer(false)->SerializeFrame(syn_stream);
1012 } else {
1013 SpdyPushPromiseIR push_promise(associated_stream_id, stream_id);
1014 AddUrlToHeaderBlock(url, push_promise.mutable_name_value_block());
1015 scoped_ptr<SpdyFrame> push_promise_frame(
1016 CreateFramer(false)->SerializeFrame(push_promise));
1017
1018 // Use SynStreamIR to create HEADERS+PRIORITY. Direct creation breaks
1019 // framer.
1020 SpdySynStreamIR headers(stream_id);
1021 SetPriority(LOWEST, &headers);
1022 headers.SetHeader("hello", "bye");
1023 headers.SetHeader(GetStatusKey(), "200 OK");
1024 AppendToHeaderBlock(
1025 extra_headers, extra_header_count, headers.mutable_name_value_block());
1026 scoped_ptr<SpdyFrame> headers_frame(
1027 CreateFramer(false)->SerializeFrame(headers));
1028
1029 int joint_data_size = push_promise_frame->size() + headers_frame->size();
1030 scoped_ptr<char[]> data(new char[joint_data_size]);
1031 const SpdyFrame* frames[2] = {
1032 push_promise_frame.get(), headers_frame.get(),
1033 };
1034 int combined_size =
1035 CombineFrames(frames, arraysize(frames), data.get(), joint_data_size);
1036 DCHECK_EQ(combined_size, joint_data_size);
1037 return new SpdyFrame(data.release(), joint_data_size, true);
1038 }
1039 }
1040
ConstructSpdyPush(const char * const extra_headers[],int extra_header_count,int stream_id,int associated_stream_id,const char * url,const char * status,const char * location)1041 SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
1042 int extra_header_count,
1043 int stream_id,
1044 int associated_stream_id,
1045 const char* url,
1046 const char* status,
1047 const char* location) {
1048 if (spdy_version() < SPDY4) {
1049 SpdySynStreamIR syn_stream(stream_id);
1050 syn_stream.set_associated_to_stream_id(associated_stream_id);
1051 syn_stream.SetHeader("hello", "bye");
1052 syn_stream.SetHeader(GetStatusKey(), status);
1053 syn_stream.SetHeader(GetVersionKey(), "HTTP/1.1");
1054 syn_stream.SetHeader("location", location);
1055 AddUrlToHeaderBlock(url, syn_stream.mutable_name_value_block());
1056 AppendToHeaderBlock(extra_headers,
1057 extra_header_count,
1058 syn_stream.mutable_name_value_block());
1059 return CreateFramer(false)->SerializeFrame(syn_stream);
1060 } else {
1061 SpdyPushPromiseIR push_promise(associated_stream_id, stream_id);
1062 AddUrlToHeaderBlock(url, push_promise.mutable_name_value_block());
1063 scoped_ptr<SpdyFrame> push_promise_frame(
1064 CreateFramer(false)->SerializeFrame(push_promise));
1065
1066 // Use SynStreamIR to create HEADERS+PRIORITY. Direct creation breaks
1067 // framer.
1068 SpdySynStreamIR headers(stream_id);
1069 SetPriority(LOWEST, &headers);
1070 headers.SetHeader("hello", "bye");
1071 headers.SetHeader(GetStatusKey(), status);
1072 headers.SetHeader("location", location);
1073 AppendToHeaderBlock(
1074 extra_headers, extra_header_count, headers.mutable_name_value_block());
1075 scoped_ptr<SpdyFrame> headers_frame(
1076 CreateFramer(false)->SerializeFrame(headers));
1077
1078 int joint_data_size = push_promise_frame->size() + headers_frame->size();
1079 scoped_ptr<char[]> data(new char[joint_data_size]);
1080 const SpdyFrame* frames[2] = {
1081 push_promise_frame.get(), headers_frame.get(),
1082 };
1083 int combined_size =
1084 CombineFrames(frames, arraysize(frames), data.get(), joint_data_size);
1085 DCHECK_EQ(combined_size, joint_data_size);
1086 return new SpdyFrame(data.release(), joint_data_size, true);
1087 }
1088 }
1089
ConstructInitialSpdyPushFrame(scoped_ptr<SpdyHeaderBlock> headers,int stream_id,int associated_stream_id)1090 SpdyFrame* SpdyTestUtil::ConstructInitialSpdyPushFrame(
1091 scoped_ptr<SpdyHeaderBlock> headers,
1092 int stream_id,
1093 int associated_stream_id) {
1094 if (spdy_version() < SPDY4) {
1095 SpdySynStreamIR syn_stream(stream_id);
1096 syn_stream.set_associated_to_stream_id(associated_stream_id);
1097 SetPriority(LOWEST, &syn_stream);
1098 syn_stream.set_name_value_block(*headers);
1099 return CreateFramer(false)->SerializeFrame(syn_stream);
1100 } else {
1101 SpdyPushPromiseIR push_promise(associated_stream_id, stream_id);
1102 push_promise.set_name_value_block(*headers);
1103 return CreateFramer(false)->SerializeFrame(push_promise);
1104 }
1105 }
1106
ConstructSpdyPushHeaders(int stream_id,const char * const extra_headers[],int extra_header_count)1107 SpdyFrame* SpdyTestUtil::ConstructSpdyPushHeaders(
1108 int stream_id,
1109 const char* const extra_headers[],
1110 int extra_header_count) {
1111 SpdyHeadersIR headers(stream_id);
1112 headers.SetHeader(GetStatusKey(), "200 OK");
1113 MaybeAddVersionHeader(&headers);
1114 AppendToHeaderBlock(extra_headers, extra_header_count,
1115 headers.mutable_name_value_block());
1116 return CreateFramer(false)->SerializeFrame(headers);
1117 }
1118
ConstructSpdySynReplyError(const char * const status,const char * const * const extra_headers,int extra_header_count,int stream_id)1119 SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(
1120 const char* const status,
1121 const char* const* const extra_headers,
1122 int extra_header_count,
1123 int stream_id) {
1124 SpdySynReplyIR syn_reply(stream_id);
1125 syn_reply.SetHeader("hello", "bye");
1126 syn_reply.SetHeader(GetStatusKey(), status);
1127 MaybeAddVersionHeader(&syn_reply);
1128 AppendToHeaderBlock(extra_headers, extra_header_count,
1129 syn_reply.mutable_name_value_block());
1130 return CreateFramer(false)->SerializeFrame(syn_reply);
1131 }
1132
ConstructSpdyGetSynReplyRedirect(int stream_id)1133 SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReplyRedirect(int stream_id) {
1134 static const char* const kExtraHeaders[] = {
1135 "location", "http://www.foo.com/index.php",
1136 };
1137 return ConstructSpdySynReplyError("301 Moved Permanently", kExtraHeaders,
1138 arraysize(kExtraHeaders)/2, stream_id);
1139 }
1140
ConstructSpdySynReplyError(int stream_id)1141 SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(int stream_id) {
1142 return ConstructSpdySynReplyError("500 Internal Server Error", NULL, 0, 1);
1143 }
1144
ConstructSpdyGetSynReply(const char * const extra_headers[],int extra_header_count,int stream_id)1145 SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReply(
1146 const char* const extra_headers[],
1147 int extra_header_count,
1148 int stream_id) {
1149 SpdySynReplyIR syn_reply(stream_id);
1150 syn_reply.SetHeader("hello", "bye");
1151 syn_reply.SetHeader(GetStatusKey(), "200");
1152 MaybeAddVersionHeader(&syn_reply);
1153 AppendToHeaderBlock(extra_headers, extra_header_count,
1154 syn_reply.mutable_name_value_block());
1155 return CreateFramer(false)->SerializeFrame(syn_reply);
1156 }
1157
ConstructSpdyPost(const char * url,SpdyStreamId stream_id,int64 content_length,RequestPriority priority,const char * const extra_headers[],int extra_header_count)1158 SpdyFrame* SpdyTestUtil::ConstructSpdyPost(const char* url,
1159 SpdyStreamId stream_id,
1160 int64 content_length,
1161 RequestPriority priority,
1162 const char* const extra_headers[],
1163 int extra_header_count) {
1164 const SpdyHeaderInfo kSynStartHeader = {
1165 SYN_STREAM,
1166 stream_id,
1167 0, // Associated stream ID
1168 ConvertRequestPriorityToSpdyPriority(priority, spdy_version_),
1169 kSpdyCredentialSlotUnused,
1170 CONTROL_FLAG_NONE,
1171 false, // Compressed
1172 RST_STREAM_INVALID,
1173 NULL, // Data
1174 0, // Length
1175 DATA_FLAG_NONE
1176 };
1177 return ConstructSpdyFrame(
1178 kSynStartHeader, ConstructPostHeaderBlock(url, content_length));
1179 }
1180
ConstructChunkedSpdyPost(const char * const extra_headers[],int extra_header_count)1181 SpdyFrame* SpdyTestUtil::ConstructChunkedSpdyPost(
1182 const char* const extra_headers[],
1183 int extra_header_count) {
1184 SpdySynStreamIR syn_stream(1);
1185 syn_stream.SetHeader(GetMethodKey(), "POST");
1186 syn_stream.SetHeader(GetPathKey(), "/");
1187 syn_stream.SetHeader(GetHostKey(), "www.google.com");
1188 syn_stream.SetHeader(GetSchemeKey(), "http");
1189 MaybeAddVersionHeader(&syn_stream);
1190 SetPriority(LOWEST, &syn_stream);
1191 AppendToHeaderBlock(extra_headers, extra_header_count,
1192 syn_stream.mutable_name_value_block());
1193 return CreateFramer(false)->SerializeFrame(syn_stream);
1194 }
1195
ConstructSpdyPostSynReply(const char * const extra_headers[],int extra_header_count)1196 SpdyFrame* SpdyTestUtil::ConstructSpdyPostSynReply(
1197 const char* const extra_headers[],
1198 int extra_header_count) {
1199 SpdySynReplyIR syn_reply(1);
1200 syn_reply.SetHeader("hello", "bye");
1201 syn_reply.SetHeader(GetStatusKey(), "200");
1202 syn_reply.SetHeader(GetPathKey(), "/index.php");
1203 MaybeAddVersionHeader(&syn_reply);
1204 AppendToHeaderBlock(extra_headers, extra_header_count,
1205 syn_reply.mutable_name_value_block());
1206 return CreateFramer(false)->SerializeFrame(syn_reply);
1207 }
1208
ConstructSpdyBodyFrame(int stream_id,bool fin)1209 SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id, bool fin) {
1210 SpdyFramer framer(spdy_version_);
1211 SpdyDataIR data_ir(stream_id,
1212 base::StringPiece(kUploadData, kUploadDataSize));
1213 data_ir.set_fin(fin);
1214 return framer.SerializeData(data_ir);
1215 }
1216
ConstructSpdyBodyFrame(int stream_id,const char * data,uint32 len,bool fin)1217 SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id,
1218 const char* data,
1219 uint32 len,
1220 bool fin) {
1221 SpdyFramer framer(spdy_version_);
1222 SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
1223 data_ir.set_fin(fin);
1224 return framer.SerializeData(data_ir);
1225 }
1226
ConstructWrappedSpdyFrame(const scoped_ptr<SpdyFrame> & frame,int stream_id)1227 SpdyFrame* SpdyTestUtil::ConstructWrappedSpdyFrame(
1228 const scoped_ptr<SpdyFrame>& frame,
1229 int stream_id) {
1230 return ConstructSpdyBodyFrame(stream_id, frame->data(),
1231 frame->size(), false);
1232 }
1233
MakeSpdyHeader(SpdyFrameType type)1234 const SpdyHeaderInfo SpdyTestUtil::MakeSpdyHeader(SpdyFrameType type) {
1235 const SpdyHeaderInfo kHeader = {
1236 type,
1237 1, // Stream ID
1238 0, // Associated stream ID
1239 ConvertRequestPriorityToSpdyPriority(LOWEST, spdy_version_),
1240 kSpdyCredentialSlotUnused,
1241 CONTROL_FLAG_FIN, // Control Flags
1242 false, // Compressed
1243 RST_STREAM_INVALID,
1244 NULL, // Data
1245 0, // Length
1246 DATA_FLAG_NONE
1247 };
1248 return kHeader;
1249 }
1250
CreateFramer(bool compressed) const1251 scoped_ptr<SpdyFramer> SpdyTestUtil::CreateFramer(bool compressed) const {
1252 scoped_ptr<SpdyFramer> framer(new SpdyFramer(spdy_version_));
1253 framer->set_enable_compression(compressed);
1254 return framer.Pass();
1255 }
1256
GetMethodKey() const1257 const char* SpdyTestUtil::GetMethodKey() const {
1258 return is_spdy2() ? "method" : ":method";
1259 }
1260
GetStatusKey() const1261 const char* SpdyTestUtil::GetStatusKey() const {
1262 return is_spdy2() ? "status" : ":status";
1263 }
1264
GetHostKey() const1265 const char* SpdyTestUtil::GetHostKey() const {
1266 if (protocol_ < kProtoSPDY3)
1267 return "host";
1268 if (protocol_ < kProtoSPDY4)
1269 return ":host";
1270 else
1271 return ":authority";
1272 }
1273
GetSchemeKey() const1274 const char* SpdyTestUtil::GetSchemeKey() const {
1275 return is_spdy2() ? "scheme" : ":scheme";
1276 }
1277
GetVersionKey() const1278 const char* SpdyTestUtil::GetVersionKey() const {
1279 return is_spdy2() ? "version" : ":version";
1280 }
1281
GetPathKey() const1282 const char* SpdyTestUtil::GetPathKey() const {
1283 return is_spdy2() ? "url" : ":path";
1284 }
1285
ConstructHeaderBlock(base::StringPiece method,base::StringPiece url,int64 * content_length) const1286 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeaderBlock(
1287 base::StringPiece method,
1288 base::StringPiece url,
1289 int64* content_length) const {
1290 std::string scheme, host, path;
1291 ParseUrl(url.data(), &scheme, &host, &path);
1292 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
1293 (*headers)[GetMethodKey()] = method.as_string();
1294 (*headers)[GetPathKey()] = path.c_str();
1295 (*headers)[GetHostKey()] = host.c_str();
1296 (*headers)[GetSchemeKey()] = scheme.c_str();
1297 if (include_version_header()) {
1298 (*headers)[GetVersionKey()] = "HTTP/1.1";
1299 }
1300 if (content_length) {
1301 std::string length_str = base::Int64ToString(*content_length);
1302 (*headers)["content-length"] = length_str;
1303 }
1304 return headers.Pass();
1305 }
1306
MaybeAddVersionHeader(SpdyFrameWithNameValueBlockIR * frame_ir) const1307 void SpdyTestUtil::MaybeAddVersionHeader(
1308 SpdyFrameWithNameValueBlockIR* frame_ir) const {
1309 if (include_version_header()) {
1310 frame_ir->SetHeader(GetVersionKey(), "HTTP/1.1");
1311 }
1312 }
1313
SetPriority(RequestPriority priority,SpdySynStreamIR * ir) const1314 void SpdyTestUtil::SetPriority(RequestPriority priority,
1315 SpdySynStreamIR* ir) const {
1316 ir->set_priority(ConvertRequestPriorityToSpdyPriority(
1317 priority, spdy_version()));
1318 }
1319
1320 } // namespace net
1321