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