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