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.
5 #include "net/spdy/spdy_test_util_common.h"
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"
32 bool next_proto_is_spdy(NextProto next_proto) {
33 return next_proto >= kProtoSPDYMinimumVersion &&
34 next_proto <= kProtoSPDYMaximumVersion;
37 // Parses a URL into the scheme, host, and path components required for a
39 void ParseUrl(base::StringPiece url, std::string* scheme, std::string* host,
41 GURL gurl(url.as_string());
42 path->assign(gurl.PathForRequest());
43 scheme->assign(gurl.scheme());
44 host->assign(gurl.host());
45 if (gurl.has_port()) {
47 host->append(gurl.port());
53 std::vector<NextProto> SpdyNextProtos() {
54 std::vector<NextProto> next_protos;
55 for (int i = kProtoMinimumVersion; i <= kProtoMaximumVersion; ++i) {
56 next_protos.push_back(static_cast<NextProto>(i));
61 // Chop a frame into an array of MockWrites.
62 // |data| is the frame to chop.
63 // |length| is the length of the frame to chop.
64 // |num_chunks| is the number of chunks to create.
65 MockWrite* ChopWriteFrame(const char* data, int length, int num_chunks) {
66 MockWrite* chunks = new MockWrite[num_chunks];
67 int chunk_size = length / num_chunks;
68 for (int index = 0; index < num_chunks; index++) {
69 const char* ptr = data + (index * chunk_size);
70 if (index == num_chunks - 1)
71 chunk_size += length % chunk_size; // The last chunk takes the remainder.
72 chunks[index] = MockWrite(ASYNC, ptr, chunk_size);
77 // Chop a SpdyFrame into an array of MockWrites.
78 // |frame| is the frame to chop.
79 // |num_chunks| is the number of chunks to create.
80 MockWrite* ChopWriteFrame(const SpdyFrame& frame, int num_chunks) {
81 return ChopWriteFrame(frame.data(), frame.size(), num_chunks);
84 // Chop a frame into an array of MockReads.
85 // |data| is the frame to chop.
86 // |length| is the length of the frame to chop.
87 // |num_chunks| is the number of chunks to create.
88 MockRead* ChopReadFrame(const char* data, int length, int num_chunks) {
89 MockRead* chunks = new MockRead[num_chunks];
90 int chunk_size = length / num_chunks;
91 for (int index = 0; index < num_chunks; index++) {
92 const char* ptr = data + (index * chunk_size);
93 if (index == num_chunks - 1)
94 chunk_size += length % chunk_size; // The last chunk takes the remainder.
95 chunks[index] = MockRead(ASYNC, ptr, chunk_size);
100 // Chop a SpdyFrame into an array of MockReads.
101 // |frame| is the frame to chop.
102 // |num_chunks| is the number of chunks to create.
103 MockRead* ChopReadFrame(const SpdyFrame& frame, int num_chunks) {
104 return ChopReadFrame(frame.data(), frame.size(), num_chunks);
107 // Adds headers and values to a map.
108 // |extra_headers| is an array of { name, value } pairs, arranged as strings
109 // where the even entries are the header names, and the odd entries are the
111 // |headers| gets filled in from |extra_headers|.
112 void AppendToHeaderBlock(const char* const extra_headers[],
113 int extra_header_count,
114 SpdyHeaderBlock* headers) {
115 std::string this_header;
116 std::string this_value;
118 if (!extra_header_count)
121 // Sanity check: Non-NULL header list.
122 DCHECK(NULL != extra_headers) << "NULL header value pair list";
123 // Sanity check: Non-NULL header map.
124 DCHECK(NULL != headers) << "NULL header map";
125 // Copy in the headers.
126 for (int i = 0; i < extra_header_count; i++) {
127 // Sanity check: Non-empty header.
128 DCHECK_NE('\0', *extra_headers[i * 2]) << "Empty header value pair";
129 this_header = extra_headers[i * 2];
130 std::string::size_type header_len = this_header.length();
133 this_value = extra_headers[1 + (i * 2)];
134 std::string new_value;
135 if (headers->find(this_header) != headers->end()) {
136 // More than one entry in the header.
137 // Don't add the header again, just the append to the value,
138 // separated by a NULL character.
141 new_value = (*headers)[this_header];
142 // Put in a NULL separator.
143 new_value.append(1, '\0');
144 // Append the new value.
145 new_value += this_value;
147 // Not a duplicate, just write the value.
148 new_value = this_value;
150 (*headers)[this_header] = new_value;
154 // Create a MockWrite from the given SpdyFrame.
155 MockWrite CreateMockWrite(const SpdyFrame& req) {
156 return MockWrite(ASYNC, req.data(), req.size());
159 // Create a MockWrite from the given SpdyFrame and sequence number.
160 MockWrite CreateMockWrite(const SpdyFrame& req, int seq) {
161 return CreateMockWrite(req, seq, ASYNC);
164 // Create a MockWrite from the given SpdyFrame and sequence number.
165 MockWrite CreateMockWrite(const SpdyFrame& req, int seq, IoMode mode) {
166 return MockWrite(mode, req.data(), req.size(), seq);
169 // Create a MockRead from the given SpdyFrame.
170 MockRead CreateMockRead(const SpdyFrame& resp) {
171 return MockRead(ASYNC, resp.data(), resp.size());
174 // Create a MockRead from the given SpdyFrame and sequence number.
175 MockRead CreateMockRead(const SpdyFrame& resp, int seq) {
176 return CreateMockRead(resp, seq, ASYNC);
179 // Create a MockRead from the given SpdyFrame and sequence number.
180 MockRead CreateMockRead(const SpdyFrame& resp, int seq, IoMode mode) {
181 return MockRead(mode, resp.data(), resp.size(), seq);
184 // Combines the given SpdyFrames into the given char array and returns
186 int CombineFrames(const SpdyFrame** frames, int num_frames,
187 char* buff, int buff_len) {
189 for (int i = 0; i < num_frames; ++i) {
190 total_len += frames[i]->size();
192 DCHECK_LE(total_len, buff_len);
194 for (int i = 0; i < num_frames; ++i) {
195 int len = frames[i]->size();
196 memcpy(ptr, frames[i]->data(), len);
204 class PriorityGetter : public BufferedSpdyFramerVisitorInterface {
206 PriorityGetter() : priority_(0) {}
207 virtual ~PriorityGetter() {}
209 SpdyPriority priority() const {
213 virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {}
214 virtual void OnStreamError(SpdyStreamId stream_id,
215 const std::string& description) OVERRIDE {}
216 virtual void OnSynStream(SpdyStreamId stream_id,
217 SpdyStreamId associated_stream_id,
218 SpdyPriority priority,
221 const SpdyHeaderBlock& headers) OVERRIDE {
222 priority_ = priority;
224 virtual void OnSynReply(SpdyStreamId stream_id,
226 const SpdyHeaderBlock& headers) OVERRIDE {}
227 virtual void OnHeaders(SpdyStreamId stream_id,
229 const SpdyHeaderBlock& headers) OVERRIDE {}
230 virtual void OnDataFrameHeader(SpdyStreamId stream_id,
232 bool fin) OVERRIDE {}
233 virtual void OnStreamFrameData(SpdyStreamId stream_id,
236 bool fin) OVERRIDE {}
237 virtual void OnSettings(bool clear_persisted) OVERRIDE {}
238 virtual void OnSetting(
239 SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE {}
240 virtual void OnPing(SpdyPingId unique_id) OVERRIDE {}
241 virtual void OnRstStream(SpdyStreamId stream_id,
242 SpdyRstStreamStatus status) OVERRIDE {}
243 virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
244 SpdyGoAwayStatus status) OVERRIDE {}
245 virtual void OnWindowUpdate(SpdyStreamId stream_id,
246 uint32 delta_window_size) OVERRIDE {}
247 virtual void OnPushPromise(SpdyStreamId stream_id,
248 SpdyStreamId promised_stream_id) OVERRIDE {}
251 SpdyPriority priority_;
256 bool GetSpdyPriority(SpdyMajorVersion version,
257 const SpdyFrame& frame,
258 SpdyPriority* priority) {
259 BufferedSpdyFramer framer(version, false);
260 PriorityGetter priority_getter;
261 framer.set_visitor(&priority_getter);
262 size_t frame_size = frame.size();
263 if (framer.ProcessInput(frame.data(), frame_size) != frame_size) {
266 *priority = priority_getter.priority();
270 base::WeakPtr<SpdyStream> CreateStreamSynchronously(
272 const base::WeakPtr<SpdySession>& session,
274 RequestPriority priority,
275 const BoundNetLog& net_log) {
276 SpdyStreamRequest stream_request;
277 int rv = stream_request.StartRequest(type, session, url, priority, net_log,
278 CompletionCallback());
280 (rv == OK) ? stream_request.ReleaseStream() : base::WeakPtr<SpdyStream>();
283 StreamReleaserCallback::StreamReleaserCallback() {}
285 StreamReleaserCallback::~StreamReleaserCallback() {}
287 CompletionCallback StreamReleaserCallback::MakeCallback(
288 SpdyStreamRequest* request) {
289 return base::Bind(&StreamReleaserCallback::OnComplete,
290 base::Unretained(this),
294 void StreamReleaserCallback::OnComplete(
295 SpdyStreamRequest* request, int result) {
297 request->ReleaseStream()->Cancel();
301 MockECSignatureCreator::MockECSignatureCreator(crypto::ECPrivateKey* key)
305 bool MockECSignatureCreator::Sign(const uint8* data,
307 std::vector<uint8>* signature) {
308 std::vector<uint8> private_key_value;
309 key_->ExportValue(&private_key_value);
310 std::string head = "fakesignature";
311 std::string tail = "/fakesignature";
314 signature->insert(signature->end(), head.begin(), head.end());
315 signature->insert(signature->end(), private_key_value.begin(),
316 private_key_value.end());
317 signature->insert(signature->end(), '-');
318 signature->insert(signature->end(), data, data + data_len);
319 signature->insert(signature->end(), tail.begin(), tail.end());
323 bool MockECSignatureCreator::DecodeSignature(
324 const std::vector<uint8>& signature,
325 std::vector<uint8>* out_raw_sig) {
326 *out_raw_sig = signature;
330 MockECSignatureCreatorFactory::MockECSignatureCreatorFactory() {
331 crypto::ECSignatureCreator::SetFactoryForTesting(this);
334 MockECSignatureCreatorFactory::~MockECSignatureCreatorFactory() {
335 crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
338 crypto::ECSignatureCreator* MockECSignatureCreatorFactory::Create(
339 crypto::ECPrivateKey* key) {
340 return new MockECSignatureCreator(key);
343 SpdySessionDependencies::SpdySessionDependencies(NextProto protocol)
344 : host_resolver(new MockCachingHostResolver),
345 cert_verifier(new MockCertVerifier),
346 transport_security_state(new TransportSecurityState),
347 proxy_service(ProxyService::CreateDirect()),
348 ssl_config_service(new SSLConfigServiceDefaults),
349 socket_factory(new MockClientSocketFactory),
350 deterministic_socket_factory(new DeterministicMockClientSocketFactory),
351 http_auth_handler_factory(
352 HttpAuthHandlerFactory::CreateDefault(host_resolver.get())),
353 enable_ip_pooling(true),
354 enable_compression(false),
356 enable_user_alternate_protocol_ports(false),
358 stream_initial_recv_window_size(kSpdyStreamInitialWindowSize),
359 time_func(&base::TimeTicks::Now),
361 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
363 // Note: The CancelledTransaction test does cleanup by running all
364 // tasks in the message loop (RunAllPending). Unfortunately, that
365 // doesn't clean up tasks on the host resolver thread; and
366 // TCPConnectJob is currently not cancellable. Using synchronous
367 // lookups allows the test to shutdown cleanly. Until we have
368 // cancellable TCPConnectJobs, use synchronous lookups.
369 host_resolver->set_synchronous_mode(true);
372 SpdySessionDependencies::SpdySessionDependencies(
373 NextProto protocol, ProxyService* proxy_service)
374 : host_resolver(new MockHostResolver),
375 cert_verifier(new MockCertVerifier),
376 transport_security_state(new TransportSecurityState),
377 proxy_service(proxy_service),
378 ssl_config_service(new SSLConfigServiceDefaults),
379 socket_factory(new MockClientSocketFactory),
380 deterministic_socket_factory(new DeterministicMockClientSocketFactory),
381 http_auth_handler_factory(
382 HttpAuthHandlerFactory::CreateDefault(host_resolver.get())),
383 enable_ip_pooling(true),
384 enable_compression(false),
386 enable_user_alternate_protocol_ports(false),
388 stream_initial_recv_window_size(kSpdyStreamInitialWindowSize),
389 time_func(&base::TimeTicks::Now),
391 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
394 SpdySessionDependencies::~SpdySessionDependencies() {}
397 HttpNetworkSession* SpdySessionDependencies::SpdyCreateSession(
398 SpdySessionDependencies* session_deps) {
399 net::HttpNetworkSession::Params params = CreateSessionParams(session_deps);
400 params.client_socket_factory = session_deps->socket_factory.get();
401 HttpNetworkSession* http_session = new HttpNetworkSession(params);
402 SpdySessionPoolPeer pool_peer(http_session->spdy_session_pool());
403 pool_peer.SetEnableSendingInitialData(false);
408 HttpNetworkSession* SpdySessionDependencies::SpdyCreateSessionDeterministic(
409 SpdySessionDependencies* session_deps) {
410 net::HttpNetworkSession::Params params = CreateSessionParams(session_deps);
411 params.client_socket_factory =
412 session_deps->deterministic_socket_factory.get();
413 HttpNetworkSession* http_session = new HttpNetworkSession(params);
414 SpdySessionPoolPeer pool_peer(http_session->spdy_session_pool());
415 pool_peer.SetEnableSendingInitialData(false);
420 net::HttpNetworkSession::Params SpdySessionDependencies::CreateSessionParams(
421 SpdySessionDependencies* session_deps) {
422 DCHECK(next_proto_is_spdy(session_deps->protocol)) <<
423 "Invalid protocol: " << session_deps->protocol;
425 net::HttpNetworkSession::Params params;
426 params.host_resolver = session_deps->host_resolver.get();
427 params.cert_verifier = session_deps->cert_verifier.get();
428 params.transport_security_state =
429 session_deps->transport_security_state.get();
430 params.proxy_service = session_deps->proxy_service.get();
431 params.ssl_config_service = session_deps->ssl_config_service.get();
432 params.http_auth_handler_factory =
433 session_deps->http_auth_handler_factory.get();
434 params.http_server_properties =
435 session_deps->http_server_properties.GetWeakPtr();
436 params.enable_spdy_compression = session_deps->enable_compression;
437 params.enable_spdy_ping_based_connection_checking = session_deps->enable_ping;
438 params.enable_user_alternate_protocol_ports =
439 session_deps->enable_user_alternate_protocol_ports;
440 params.spdy_default_protocol = session_deps->protocol;
441 params.spdy_stream_initial_recv_window_size =
442 session_deps->stream_initial_recv_window_size;
443 params.time_func = session_deps->time_func;
444 params.trusted_spdy_proxy = session_deps->trusted_spdy_proxy;
445 params.net_log = session_deps->net_log;
449 SpdyURLRequestContext::SpdyURLRequestContext(NextProto protocol)
451 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
453 storage_.set_host_resolver(scoped_ptr<HostResolver>(new MockHostResolver));
454 storage_.set_cert_verifier(new MockCertVerifier);
455 storage_.set_transport_security_state(new TransportSecurityState);
456 storage_.set_proxy_service(ProxyService::CreateDirect());
457 storage_.set_ssl_config_service(new SSLConfigServiceDefaults);
458 storage_.set_http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault(
460 storage_.set_http_server_properties(
461 scoped_ptr<HttpServerProperties>(new HttpServerPropertiesImpl()));
462 net::HttpNetworkSession::Params params;
463 params.client_socket_factory = &socket_factory_;
464 params.host_resolver = host_resolver();
465 params.cert_verifier = cert_verifier();
466 params.transport_security_state = transport_security_state();
467 params.proxy_service = proxy_service();
468 params.ssl_config_service = ssl_config_service();
469 params.http_auth_handler_factory = http_auth_handler_factory();
470 params.network_delegate = network_delegate();
471 params.enable_spdy_compression = false;
472 params.enable_spdy_ping_based_connection_checking = false;
473 params.spdy_default_protocol = protocol;
474 params.http_server_properties = http_server_properties();
475 scoped_refptr<HttpNetworkSession> network_session(
476 new HttpNetworkSession(params));
477 SpdySessionPoolPeer pool_peer(network_session->spdy_session_pool());
478 pool_peer.SetEnableSendingInitialData(false);
479 storage_.set_http_transaction_factory(new HttpCache(
480 network_session.get(), HttpCache::DefaultBackend::InMemory(0)));
483 SpdyURLRequestContext::~SpdyURLRequestContext() {
486 bool HasSpdySession(SpdySessionPool* pool, const SpdySessionKey& key) {
487 return pool->FindAvailableSession(key, BoundNetLog()) != NULL;
492 base::WeakPtr<SpdySession> CreateSpdySessionHelper(
493 const scoped_refptr<HttpNetworkSession>& http_session,
494 const SpdySessionKey& key,
495 const BoundNetLog& net_log,
496 Error expected_status,
498 EXPECT_FALSE(HasSpdySession(http_session->spdy_session_pool(), key));
500 scoped_refptr<TransportSocketParams> transport_params(
501 new TransportSocketParams(
502 key.host_port_pair(), false, false,
503 OnHostResolutionCallback()));
505 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
506 TestCompletionCallback callback;
508 int rv = ERR_UNEXPECTED;
510 SSLConfig ssl_config;
511 scoped_refptr<SSLSocketParams> ssl_params(
512 new SSLSocketParams(transport_params,
515 key.host_port_pair(),
521 rv = connection->Init(key.host_port_pair().ToString(),
525 http_session->GetSSLSocketPool(
526 HttpNetworkSession::NORMAL_SOCKET_POOL),
529 rv = connection->Init(key.host_port_pair().ToString(),
533 http_session->GetTransportSocketPool(
534 HttpNetworkSession::NORMAL_SOCKET_POOL),
538 if (rv == ERR_IO_PENDING)
539 rv = callback.WaitForResult();
543 base::WeakPtr<SpdySession> spdy_session;
546 http_session->spdy_session_pool()->CreateAvailableSessionFromSocket(
547 key, connection.Pass(), net_log, OK, &spdy_session,
549 EXPECT_EQ(expected_status == OK, spdy_session != NULL);
550 EXPECT_EQ(expected_status == OK,
551 HasSpdySession(http_session->spdy_session_pool(), key));
557 base::WeakPtr<SpdySession> CreateInsecureSpdySession(
558 const scoped_refptr<HttpNetworkSession>& http_session,
559 const SpdySessionKey& key,
560 const BoundNetLog& net_log) {
561 return CreateSpdySessionHelper(http_session, key, net_log,
562 OK, false /* is_secure */);
565 void TryCreateInsecureSpdySessionExpectingFailure(
566 const scoped_refptr<HttpNetworkSession>& http_session,
567 const SpdySessionKey& key,
568 Error expected_error,
569 const BoundNetLog& net_log) {
570 DCHECK_LT(expected_error, ERR_IO_PENDING);
571 CreateSpdySessionHelper(http_session, key, net_log,
572 expected_error, false /* is_secure */);
575 base::WeakPtr<SpdySession> CreateSecureSpdySession(
576 const scoped_refptr<HttpNetworkSession>& http_session,
577 const SpdySessionKey& key,
578 const BoundNetLog& net_log) {
579 return CreateSpdySessionHelper(http_session, key, net_log,
580 OK, true /* is_secure */);
585 // A ClientSocket used for CreateFakeSpdySession() below.
586 class FakeSpdySessionClientSocket : public MockClientSocket {
588 FakeSpdySessionClientSocket(int read_result)
589 : MockClientSocket(BoundNetLog()),
590 read_result_(read_result) {}
592 virtual ~FakeSpdySessionClientSocket() {}
594 virtual int Read(IOBuffer* buf, int buf_len,
595 const CompletionCallback& callback) OVERRIDE {
599 virtual int Write(IOBuffer* buf, int buf_len,
600 const CompletionCallback& callback) OVERRIDE {
601 return ERR_IO_PENDING;
604 // Return kProtoUnknown to use the pool's default protocol.
605 virtual NextProto GetNegotiatedProtocol() const OVERRIDE {
606 return kProtoUnknown;
609 // The functions below are not expected to be called.
611 virtual int Connect(const CompletionCallback& callback) OVERRIDE {
613 return ERR_UNEXPECTED;
616 virtual bool WasEverUsed() const OVERRIDE {
621 virtual bool UsingTCPFastOpen() const OVERRIDE {
626 virtual bool WasNpnNegotiated() const OVERRIDE {
631 virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {
640 base::WeakPtr<SpdySession> CreateFakeSpdySessionHelper(
641 SpdySessionPool* pool,
642 const SpdySessionKey& key,
643 Error expected_status) {
644 EXPECT_NE(expected_status, ERR_IO_PENDING);
645 EXPECT_FALSE(HasSpdySession(pool, key));
646 base::WeakPtr<SpdySession> spdy_session;
647 scoped_ptr<ClientSocketHandle> handle(new ClientSocketHandle());
648 handle->SetSocket(scoped_ptr<StreamSocket>(new FakeSpdySessionClientSocket(
649 expected_status == OK ? ERR_IO_PENDING : expected_status)));
652 pool->CreateAvailableSessionFromSocket(
653 key, handle.Pass(), BoundNetLog(), OK, &spdy_session,
654 true /* is_secure */));
655 EXPECT_EQ(expected_status == OK, spdy_session != NULL);
656 EXPECT_EQ(expected_status == OK, HasSpdySession(pool, key));
662 base::WeakPtr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool,
663 const SpdySessionKey& key) {
664 return CreateFakeSpdySessionHelper(pool, key, OK);
667 void TryCreateFakeSpdySessionExpectingFailure(SpdySessionPool* pool,
668 const SpdySessionKey& key,
669 Error expected_error) {
670 DCHECK_LT(expected_error, ERR_IO_PENDING);
671 CreateFakeSpdySessionHelper(pool, key, expected_error);
674 SpdySessionPoolPeer::SpdySessionPoolPeer(SpdySessionPool* pool) : pool_(pool) {
677 void SpdySessionPoolPeer::RemoveAliases(const SpdySessionKey& key) {
678 pool_->RemoveAliases(key);
681 void SpdySessionPoolPeer::DisableDomainAuthenticationVerification() {
682 pool_->verify_domain_authentication_ = false;
685 void SpdySessionPoolPeer::SetEnableSendingInitialData(bool enabled) {
686 pool_->enable_sending_initial_data_ = enabled;
689 SpdyTestUtil::SpdyTestUtil(NextProto protocol)
690 : protocol_(protocol),
691 spdy_version_(NextProtoToSpdyMajorVersion(protocol)) {
692 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol;
695 void SpdyTestUtil::AddUrlToHeaderBlock(base::StringPiece url,
696 SpdyHeaderBlock* headers) const {
698 (*headers)["url"] = url.as_string();
700 std::string scheme, host, path;
701 ParseUrl(url, &scheme, &host, &path);
702 (*headers)[GetSchemeKey()] = scheme;
703 (*headers)[GetHostKey()] = host;
704 (*headers)[GetPathKey()] = path;
708 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructGetHeaderBlock(
709 base::StringPiece url) const {
710 return ConstructHeaderBlock("GET", url, NULL);
713 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructGetHeaderBlockForProxy(
714 base::StringPiece url) const {
715 scoped_ptr<SpdyHeaderBlock> headers(ConstructGetHeaderBlock(url));
717 (*headers)[GetPathKey()] = url.data();
718 return headers.Pass();
721 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeadHeaderBlock(
722 base::StringPiece url,
723 int64 content_length) const {
724 return ConstructHeaderBlock("HEAD", url, &content_length);
727 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPostHeaderBlock(
728 base::StringPiece url,
729 int64 content_length) const {
730 return ConstructHeaderBlock("POST", url, &content_length);
733 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPutHeaderBlock(
734 base::StringPiece url,
735 int64 content_length) const {
736 return ConstructHeaderBlock("PUT", url, &content_length);
739 SpdyFrame* SpdyTestUtil::ConstructSpdyFrame(
740 const SpdyHeaderInfo& header_info,
741 scoped_ptr<SpdyHeaderBlock> headers) const {
742 BufferedSpdyFramer framer(spdy_version_, header_info.compressed);
743 SpdyFrame* frame = NULL;
744 switch (header_info.kind) {
746 frame = framer.CreateDataFrame(header_info.id, header_info.data,
747 header_info.data_length,
748 header_info.data_flags);
752 frame = framer.CreateSynStream(header_info.id, header_info.assoc_id,
753 header_info.priority,
754 header_info.control_flags,
759 frame = framer.CreateSynReply(header_info.id, header_info.control_flags,
763 frame = framer.CreateRstStream(header_info.id, header_info.status);
766 frame = framer.CreateHeaders(header_info.id, header_info.control_flags,
776 SpdyFrame* SpdyTestUtil::ConstructSpdyFrame(const SpdyHeaderInfo& header_info,
777 const char* const extra_headers[],
778 int extra_header_count,
779 const char* const tail_headers[],
780 int tail_header_count) const {
781 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
782 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
783 if (tail_headers && tail_header_count)
784 AppendToHeaderBlock(tail_headers, tail_header_count, headers.get());
785 return ConstructSpdyFrame(header_info, headers.Pass());
788 SpdyFrame* SpdyTestUtil::ConstructSpdyControlFrame(
789 scoped_ptr<SpdyHeaderBlock> headers,
791 SpdyStreamId stream_id,
792 RequestPriority request_priority,
794 SpdyControlFlags flags,
795 SpdyStreamId associated_stream_id) const {
796 EXPECT_GE(type, FIRST_CONTROL_TYPE);
797 EXPECT_LE(type, LAST_CONTROL_TYPE);
798 const SpdyHeaderInfo header_info = {
801 associated_stream_id,
802 ConvertRequestPriorityToSpdyPriority(request_priority, spdy_version_),
803 0, // credential slot
806 RST_STREAM_INVALID, // status
811 return ConstructSpdyFrame(header_info, headers.Pass());
814 SpdyFrame* SpdyTestUtil::ConstructSpdyControlFrame(
815 const char* const extra_headers[],
816 int extra_header_count,
818 SpdyStreamId stream_id,
819 RequestPriority request_priority,
821 SpdyControlFlags flags,
822 const char* const* tail_headers,
823 int tail_header_size,
824 SpdyStreamId associated_stream_id) const {
825 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
826 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
827 if (tail_headers && tail_header_size)
828 AppendToHeaderBlock(tail_headers, tail_header_size / 2, headers.get());
829 return ConstructSpdyControlFrame(
830 headers.Pass(), compressed, stream_id,
831 request_priority, type, flags, associated_stream_id);
834 std::string SpdyTestUtil::ConstructSpdyReplyString(
835 const SpdyHeaderBlock& headers) const {
836 std::string reply_string;
837 for (SpdyHeaderBlock::const_iterator it = headers.begin();
838 it != headers.end(); ++it) {
839 std::string key = it->first;
840 // Remove leading colon from "special" headers (for SPDY3 and
842 if (spdy_version() >= SPDY3 && key[0] == ':')
844 std::vector<std::string> values;
845 base::SplitString(it->second, '\0', &values);
846 for (std::vector<std::string>::const_iterator it2 = values.begin();
847 it2 != values.end(); ++it2) {
848 reply_string += key + ": " + *it2 + "\n";
854 // TODO(jgraettinger): Eliminate uses of this method in tests (prefer
856 SpdyFrame* SpdyTestUtil::ConstructSpdySettings(
857 const SettingsMap& settings) const {
858 SpdySettingsIR settings_ir;
859 for (SettingsMap::const_iterator it = settings.begin();
860 it != settings.end();
862 settings_ir.AddSetting(
864 (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0,
865 (it->second.first & SETTINGS_FLAG_PERSISTED) != 0,
868 return CreateFramer()->SerializeSettings(settings_ir);
871 SpdyFrame* SpdyTestUtil::ConstructSpdyPing(uint32 ping_id) const {
872 SpdyPingIR ping_ir(ping_id);
873 return CreateFramer()->SerializePing(ping_ir);
876 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway() const {
877 return ConstructSpdyGoAway(0);
880 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway(
881 SpdyStreamId last_good_stream_id) const {
882 SpdyGoAwayIR go_ir(last_good_stream_id, GOAWAY_OK, "go away");
883 return CreateFramer()->SerializeGoAway(go_ir);
886 SpdyFrame* SpdyTestUtil::ConstructSpdyWindowUpdate(
887 const SpdyStreamId stream_id, uint32 delta_window_size) const {
888 SpdyWindowUpdateIR update_ir(stream_id, delta_window_size);
889 return CreateFramer()->SerializeWindowUpdate(update_ir);
892 // TODO(jgraettinger): Eliminate uses of this method in tests (prefer
894 SpdyFrame* SpdyTestUtil::ConstructSpdyRstStream(
895 SpdyStreamId stream_id,
896 SpdyRstStreamStatus status) const {
897 SpdyRstStreamIR rst_ir(stream_id, status, "RST");
898 return CreateFramer()->SerializeRstStream(rst_ir);
901 SpdyFrame* SpdyTestUtil::ConstructSpdyGet(
902 const char* const url,
904 SpdyStreamId stream_id,
905 RequestPriority request_priority) const {
906 const SpdyHeaderInfo header_info = {
909 0, // associated stream ID
910 ConvertRequestPriorityToSpdyPriority(request_priority, spdy_version_),
911 0, // credential slot
914 RST_STREAM_INVALID, // status
919 return ConstructSpdyFrame(header_info, ConstructGetHeaderBlock(url));
922 SpdyFrame* SpdyTestUtil::ConstructSpdyGet(const char* const extra_headers[],
923 int extra_header_count,
926 RequestPriority request_priority,
928 const bool spdy2 = is_spdy2();
929 const char* url = (spdy2 && !direct) ? "http://www.google.com/" : "/";
930 const char* const kStandardGetHeaders[] = {
931 GetMethodKey(), "GET",
932 GetHostKey(), "www.google.com",
933 GetSchemeKey(), "http",
934 GetVersionKey(), "HTTP/1.1",
937 return ConstructSpdyControlFrame(extra_headers,
945 arraysize(kStandardGetHeaders),
949 SpdyFrame* SpdyTestUtil::ConstructSpdyConnect(
950 const char* const extra_headers[],
951 int extra_header_count,
953 RequestPriority priority) const {
954 const char* const kConnectHeaders[] = {
955 GetMethodKey(), "CONNECT",
956 GetPathKey(), "www.google.com:443",
957 GetHostKey(), "www.google.com",
958 GetVersionKey(), "HTTP/1.1",
960 return ConstructSpdyControlFrame(extra_headers,
962 /*compressed*/ false,
968 arraysize(kConnectHeaders),
972 SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
973 int extra_header_count,
975 int associated_stream_id,
977 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
978 (*headers)["hello"] = "bye";
979 (*headers)[GetStatusKey()] = "200 OK";
980 (*headers)[GetVersionKey()] = "HTTP/1.1";
981 AddUrlToHeaderBlock(url, headers.get());
982 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
983 return ConstructSpdyControlFrame(headers.Pass(),
989 associated_stream_id);
992 SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[],
993 int extra_header_count,
995 int associated_stream_id,
998 const char* location) {
999 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
1000 (*headers)["hello"] = "bye";
1001 (*headers)[GetStatusKey()] = status;
1002 (*headers)[GetVersionKey()] = "HTTP/1.1";
1003 (*headers)["location"] = location;
1004 AddUrlToHeaderBlock(url, headers.get());
1005 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get());
1006 return ConstructSpdyControlFrame(headers.Pass(),
1012 associated_stream_id);
1015 SpdyFrame* SpdyTestUtil::ConstructSpdyPushHeaders(
1017 const char* const extra_headers[],
1018 int extra_header_count) {
1019 const char* const kStandardGetHeaders[] = {
1020 GetStatusKey(), "200 OK",
1021 GetVersionKey(), "HTTP/1.1"
1023 return ConstructSpdyControlFrame(extra_headers,
1030 kStandardGetHeaders,
1031 arraysize(kStandardGetHeaders),
1035 SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(
1036 const char* const status,
1037 const char* const* const extra_headers,
1038 int extra_header_count,
1040 const char* const kStandardGetHeaders[] = {
1042 GetStatusKey(), status,
1043 GetVersionKey(), "HTTP/1.1"
1045 return ConstructSpdyControlFrame(extra_headers,
1052 kStandardGetHeaders,
1053 arraysize(kStandardGetHeaders),
1057 SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReplyRedirect(int stream_id) {
1058 static const char* const kExtraHeaders[] = {
1059 "location", "http://www.foo.com/index.php",
1061 return ConstructSpdySynReplyError("301 Moved Permanently", kExtraHeaders,
1062 arraysize(kExtraHeaders)/2, stream_id);
1065 SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(int stream_id) {
1066 return ConstructSpdySynReplyError("500 Internal Server Error", NULL, 0, 1);
1069 SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReply(
1070 const char* const extra_headers[],
1071 int extra_header_count,
1073 const char* const kStandardGetHeaders[] = {
1075 GetStatusKey(), "200",
1076 GetVersionKey(), "HTTP/1.1"
1078 return ConstructSpdyControlFrame(extra_headers,
1085 kStandardGetHeaders,
1086 arraysize(kStandardGetHeaders),
1090 SpdyFrame* SpdyTestUtil::ConstructSpdyPost(const char* url,
1091 SpdyStreamId stream_id,
1092 int64 content_length,
1093 RequestPriority priority,
1094 const char* const extra_headers[],
1095 int extra_header_count) {
1096 const SpdyHeaderInfo kSynStartHeader = {
1099 0, // Associated stream ID
1100 ConvertRequestPriorityToSpdyPriority(priority, spdy_version_),
1101 kSpdyCredentialSlotUnused,
1103 false, // Compressed
1109 return ConstructSpdyFrame(
1110 kSynStartHeader, ConstructPostHeaderBlock(url, content_length));
1113 SpdyFrame* SpdyTestUtil::ConstructChunkedSpdyPost(
1114 const char* const extra_headers[],
1115 int extra_header_count) {
1116 const char* post_headers[] = {
1117 GetMethodKey(), "POST",
1119 GetHostKey(), "www.google.com",
1120 GetSchemeKey(), "http",
1121 GetVersionKey(), "HTTP/1.1"
1123 return ConstructSpdyControlFrame(extra_headers,
1131 arraysize(post_headers),
1135 SpdyFrame* SpdyTestUtil::ConstructSpdyPostSynReply(
1136 const char* const extra_headers[],
1137 int extra_header_count) {
1138 const char* const kStandardGetHeaders[] = {
1140 GetStatusKey(), "200",
1141 GetPathKey(), "/index.php",
1142 GetVersionKey(), "HTTP/1.1"
1144 return ConstructSpdyControlFrame(extra_headers,
1151 kStandardGetHeaders,
1152 arraysize(kStandardGetHeaders),
1156 SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id, bool fin) {
1157 SpdyFramer framer(spdy_version_);
1158 SpdyDataIR data_ir(stream_id,
1159 base::StringPiece(kUploadData, kUploadDataSize));
1160 data_ir.set_fin(fin);
1161 return framer.SerializeData(data_ir);
1164 SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id,
1168 SpdyFramer framer(spdy_version_);
1169 SpdyDataIR data_ir(stream_id, base::StringPiece(data, len));
1170 data_ir.set_fin(fin);
1171 return framer.SerializeData(data_ir);
1174 SpdyFrame* SpdyTestUtil::ConstructWrappedSpdyFrame(
1175 const scoped_ptr<SpdyFrame>& frame,
1177 return ConstructSpdyBodyFrame(stream_id, frame->data(),
1178 frame->size(), false);
1181 const SpdyHeaderInfo SpdyTestUtil::MakeSpdyHeader(SpdyFrameType type) {
1182 const SpdyHeaderInfo kHeader = {
1185 0, // Associated stream ID
1186 ConvertRequestPriorityToSpdyPriority(LOWEST, spdy_version_),
1187 kSpdyCredentialSlotUnused,
1188 CONTROL_FLAG_FIN, // Control Flags
1189 false, // Compressed
1198 scoped_ptr<SpdyFramer> SpdyTestUtil::CreateFramer() const {
1199 return scoped_ptr<SpdyFramer>(new SpdyFramer(spdy_version_));
1202 const char* SpdyTestUtil::GetMethodKey() const {
1203 return is_spdy2() ? "method" : ":method";
1206 const char* SpdyTestUtil::GetStatusKey() const {
1207 return is_spdy2() ? "status" : ":status";
1210 const char* SpdyTestUtil::GetHostKey() const {
1211 return is_spdy2() ? "host" : ":host";
1214 const char* SpdyTestUtil::GetSchemeKey() const {
1215 return is_spdy2() ? "scheme" : ":scheme";
1218 const char* SpdyTestUtil::GetVersionKey() const {
1219 return is_spdy2() ? "version" : ":version";
1222 const char* SpdyTestUtil::GetPathKey() const {
1223 return is_spdy2() ? "url" : ":path";
1226 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeaderBlock(
1227 base::StringPiece method,
1228 base::StringPiece url,
1229 int64* content_length) const {
1230 std::string scheme, host, path;
1231 ParseUrl(url.data(), &scheme, &host, &path);
1232 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
1233 (*headers)[GetMethodKey()] = method.as_string();
1234 (*headers)[GetPathKey()] = path.c_str();
1235 (*headers)[GetHostKey()] = host.c_str();
1236 (*headers)[GetSchemeKey()] = scheme.c_str();
1237 (*headers)[GetVersionKey()] = "HTTP/1.1";
1238 if (content_length) {
1239 std::string length_str = base::Int64ToString(*content_length);
1240 (*headers)["content-length"] = length_str;
1242 return headers.Pass();