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 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. 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 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. 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. 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. 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. 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|. 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. 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. 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. 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. 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. 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. 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. 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: 207 PriorityGetter() : priority_(0) {} 208 virtual ~PriorityGetter() {} 209 210 SpdyPriority priority() const { 211 return priority_; 212 } 213 214 virtual void OnError(SpdyFramer::SpdyError error_code) OVERRIDE {} 215 virtual void OnStreamError(SpdyStreamId stream_id, 216 const std::string& description) OVERRIDE {} 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 } 225 virtual void OnSynReply(SpdyStreamId stream_id, 226 bool fin, 227 const SpdyHeaderBlock& headers) OVERRIDE {} 228 virtual void OnHeaders(SpdyStreamId stream_id, 229 bool fin, 230 const SpdyHeaderBlock& headers) OVERRIDE {} 231 virtual void OnDataFrameHeader(SpdyStreamId stream_id, 232 size_t length, 233 bool fin) OVERRIDE {} 234 virtual void OnStreamFrameData(SpdyStreamId stream_id, 235 const char* data, 236 size_t len, 237 bool fin) OVERRIDE {} 238 virtual void OnSettings(bool clear_persisted) OVERRIDE {} 239 virtual void OnSetting( 240 SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE {} 241 virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {} 242 virtual void OnRstStream(SpdyStreamId stream_id, 243 SpdyRstStreamStatus status) OVERRIDE {} 244 virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, 245 SpdyGoAwayStatus status) OVERRIDE {} 246 virtual void OnWindowUpdate(SpdyStreamId stream_id, 247 uint32 delta_window_size) OVERRIDE {} 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 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 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 285 StreamReleaserCallback::StreamReleaserCallback() {} 286 287 StreamReleaserCallback::~StreamReleaserCallback() {} 288 289 CompletionCallback StreamReleaserCallback::MakeCallback( 290 SpdyStreamRequest* request) { 291 return base::Bind(&StreamReleaserCallback::OnComplete, 292 base::Unretained(this), 293 request); 294 } 295 296 void StreamReleaserCallback::OnComplete( 297 SpdyStreamRequest* request, int result) { 298 if (result == OK) 299 request->ReleaseStream()->Cancel(); 300 SetResult(result); 301 } 302 303 MockECSignatureCreator::MockECSignatureCreator(crypto::ECPrivateKey* key) 304 : key_(key) { 305 } 306 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 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 332 MockECSignatureCreatorFactory::MockECSignatureCreatorFactory() { 333 crypto::ECSignatureCreator::SetFactoryForTesting(this); 334 } 335 336 MockECSignatureCreatorFactory::~MockECSignatureCreatorFactory() { 337 crypto::ECSignatureCreator::SetFactoryForTesting(NULL); 338 } 339 340 crypto::ECSignatureCreator* MockECSignatureCreatorFactory::Create( 341 crypto::ECPrivateKey* key) { 342 return new MockECSignatureCreator(key); 343 } 344 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 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 404 SpdySessionDependencies::~SpdySessionDependencies() {} 405 406 // static 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 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 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 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 503 SpdyURLRequestContext::~SpdyURLRequestContext() { 504 } 505 506 bool HasSpdySession(SpdySessionPool* pool, const SpdySessionKey& key) { 507 return pool->FindAvailableSession(key, BoundNetLog()) != NULL; 508 } 509 510 namespace { 511 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 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 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 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: 605 FakeSpdySessionClientSocket(int read_result) 606 : MockClientSocket(BoundNetLog()), 607 read_result_(read_result) {} 608 609 virtual ~FakeSpdySessionClientSocket() {} 610 611 virtual int Read(IOBuffer* buf, int buf_len, 612 const CompletionCallback& callback) OVERRIDE { 613 return read_result_; 614 } 615 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. 622 virtual NextProto GetNegotiatedProtocol() const OVERRIDE { 623 return kProtoUnknown; 624 } 625 626 // The functions below are not expected to be called. 627 628 virtual int Connect(const CompletionCallback& callback) OVERRIDE { 629 ADD_FAILURE(); 630 return ERR_UNEXPECTED; 631 } 632 633 virtual bool WasEverUsed() const OVERRIDE { 634 ADD_FAILURE(); 635 return false; 636 } 637 638 virtual bool UsingTCPFastOpen() const OVERRIDE { 639 ADD_FAILURE(); 640 return false; 641 } 642 643 virtual bool WasNpnNegotiated() const OVERRIDE { 644 ADD_FAILURE(); 645 return false; 646 } 647 648 virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE { 649 ADD_FAILURE(); 650 return false; 651 } 652 653 private: 654 int read_result_; 655 }; 656 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 677 base::WeakPtr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool, 678 const SpdySessionKey& key) { 679 return CreateFakeSpdySessionHelper(pool, key, OK); 680 } 681 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 690 SpdySessionPoolPeer::SpdySessionPoolPeer(SpdySessionPool* pool) : pool_(pool) { 691 } 692 693 void SpdySessionPoolPeer::RemoveAliases(const SpdySessionKey& key) { 694 pool_->RemoveAliases(key); 695 } 696 697 void SpdySessionPoolPeer::DisableDomainAuthenticationVerification() { 698 pool_->verify_domain_authentication_ = false; 699 } 700 701 void SpdySessionPoolPeer::SetEnableSendingInitialData(bool enabled) { 702 pool_->enable_sending_initial_data_ = enabled; 703 } 704 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 711 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 724 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructGetHeaderBlock( 725 base::StringPiece url) const { 726 return ConstructHeaderBlock("GET", url, NULL); 727 } 728 729 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 737 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeadHeaderBlock( 738 base::StringPiece url, 739 int64 content_length) const { 740 return ConstructHeaderBlock("HEAD", url, &content_length); 741 } 742 743 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPostHeaderBlock( 744 base::StringPiece url, 745 int64 content_length) const { 746 return ConstructHeaderBlock("POST", url, &content_length); 747 } 748 749 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPutHeaderBlock( 750 base::StringPiece url, 751 int64 content_length) const { 752 return ConstructHeaderBlock("PUT", url, &content_length); 753 } 754 755 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 792 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 804 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 830 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 850 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). 872 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 887 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 899 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 905 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway() const { 906 return ConstructSpdyGoAway(0); 907 } 908 909 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 915 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 922 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). 930 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 937 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 958 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 979 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 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 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 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 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 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 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 1141 SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(int stream_id) { 1142 return ConstructSpdySynReplyError("500 Internal Server Error", NULL, 0, 1); 1143 } 1144 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 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 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 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 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 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 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 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 1251 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 1257 const char* SpdyTestUtil::GetMethodKey() const { 1258 return is_spdy2() ? "method" : ":method"; 1259 } 1260 1261 const char* SpdyTestUtil::GetStatusKey() const { 1262 return is_spdy2() ? "status" : ":status"; 1263 } 1264 1265 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 1274 const char* SpdyTestUtil::GetSchemeKey() const { 1275 return is_spdy2() ? "scheme" : ":scheme"; 1276 } 1277 1278 const char* SpdyTestUtil::GetVersionKey() const { 1279 return is_spdy2() ? "version" : ":version"; 1280 } 1281 1282 const char* SpdyTestUtil::GetPathKey() const { 1283 return is_spdy2() ? "url" : ":path"; 1284 } 1285 1286 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 1307 void SpdyTestUtil::MaybeAddVersionHeader( 1308 SpdyFrameWithNameValueBlockIR* frame_ir) const { 1309 if (include_version_header()) { 1310 frame_ir->SetHeader(GetVersionKey(), "HTTP/1.1"); 1311 } 1312 } 1313 1314 void SpdyTestUtil::SetPriority(RequestPriority priority, 1315 SpdySynStreamIR* ir) const { 1316 ir->set_priority(ConvertRequestPriorityToSpdyPriority( 1317 priority, spdy_version())); 1318 } 1319 1320 } // namespace net 1321