1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/spdy/spdy_test_util_spdy2.h"
9 #include "base/basictypes.h"
10 #include "base/compiler_specific.h"
11 #include "base/string_number_conversions.h"
12 #include "base/string_util.h"
13 #include "net/cert/mock_cert_verifier.h"
14 #include "net/http/http_network_session.h"
15 #include "net/http/http_network_transaction.h"
16 #include "net/http/http_server_properties_impl.h"
17 #include "net/spdy/buffered_spdy_framer.h"
18 #include "net/spdy/spdy_http_utils.h"
19 #include "net/spdy/spdy_session.h"
20 #include "testing/gtest/include/gtest/gtest.h"
23 namespace test_spdy2
{
27 // Parses a URL into the scheme, host, and path components required for a
29 void ParseUrl(const char* const url
, std::string
* scheme
, std::string
* host
,
32 path
->assign(gurl
.PathForRequest());
33 scheme
->assign(gurl
.scheme());
34 host
->assign(gurl
.host());
35 if (gurl
.has_port()) {
37 host
->append(gurl
.port());
43 scoped_ptr
<SpdyHeaderBlock
> ConstructGetHeaderBlock(base::StringPiece url
) {
44 std::string scheme
, host
, path
;
45 ParseUrl(url
.data(), &scheme
, &host
, &path
);
46 const char* const headers
[] = {
50 "scheme", scheme
.c_str(),
53 scoped_ptr
<SpdyHeaderBlock
> header_block(new SpdyHeaderBlock());
54 AppendToHeaderBlock(headers
, arraysize(headers
) / 2, header_block
.get());
55 return header_block
.Pass();
58 scoped_ptr
<SpdyHeaderBlock
> ConstructPostHeaderBlock(base::StringPiece url
,
59 int64 content_length
) {
60 std::string scheme
, host
, path
;
61 ParseUrl(url
.data(), &scheme
, &host
, &path
);
62 std::string length_str
= base::Int64ToString(content_length
);
63 const char* const headers
[] = {
67 "scheme", scheme
.c_str(),
68 "version", "HTTP/1.1",
69 "content-length", length_str
.c_str()
71 scoped_ptr
<SpdyHeaderBlock
> header_block(new SpdyHeaderBlock());
72 AppendToHeaderBlock(headers
, arraysize(headers
) / 2, header_block
.get());
73 return header_block
.Pass();
76 SpdyFrame
* ConstructSpdyFrame(const SpdyHeaderInfo
& header_info
,
77 scoped_ptr
<SpdyHeaderBlock
> headers
) {
78 BufferedSpdyFramer
framer(kSpdyVersion2
, header_info
.compressed
);
79 SpdyFrame
* frame
= NULL
;
80 switch (header_info
.kind
) {
82 frame
= framer
.CreateDataFrame(header_info
.id
, header_info
.data
,
83 header_info
.data_length
,
84 header_info
.data_flags
);
87 frame
= framer
.CreateSynStream(header_info
.id
, header_info
.assoc_id
,
88 header_info
.priority
, 0,
89 header_info
.control_flags
,
90 header_info
.compressed
, headers
.get());
93 frame
= framer
.CreateSynReply(header_info
.id
, header_info
.control_flags
,
94 header_info
.compressed
, headers
.get());
97 frame
= framer
.CreateRstStream(header_info
.id
, header_info
.status
);
100 frame
= framer
.CreateHeaders(header_info
.id
, header_info
.control_flags
,
101 header_info
.compressed
, headers
.get());
110 SpdyFrame
* ConstructSpdyFrame(const SpdyHeaderInfo
& header_info
,
111 const char* const extra_headers
[],
112 int extra_header_count
,
113 const char* const tail
[],
114 int tail_header_count
) {
115 scoped_ptr
<SpdyHeaderBlock
> headers(new SpdyHeaderBlock());
116 AppendToHeaderBlock(extra_headers
, extra_header_count
, headers
.get());
117 if (tail
&& tail_header_count
)
118 AppendToHeaderBlock(tail
, tail_header_count
, headers
.get());
119 return ConstructSpdyFrame(header_info
, headers
.Pass());
122 SpdyFrame
* ConstructSpdySettings(const SettingsMap
& settings
) {
123 BufferedSpdyFramer
framer(2, false);
124 return framer
.CreateSettings(settings
);
127 SpdyFrame
* ConstructSpdyCredential(
128 const SpdyCredential
& credential
) {
129 BufferedSpdyFramer
framer(2, false);
130 return framer
.CreateCredentialFrame(credential
);
133 SpdyFrame
* ConstructSpdyPing(uint32 ping_id
) {
134 BufferedSpdyFramer
framer(2, false);
135 return framer
.CreatePingFrame(ping_id
);
138 SpdyFrame
* ConstructSpdyGoAway() {
139 return ConstructSpdyGoAway(0);
142 SpdyFrame
* ConstructSpdyGoAway(SpdyStreamId last_good_stream_id
) {
143 BufferedSpdyFramer
framer(2, false);
144 return framer
.CreateGoAway(last_good_stream_id
, GOAWAY_OK
);
147 SpdyFrame
* ConstructSpdyWindowUpdate(
148 const SpdyStreamId stream_id
, uint32 delta_window_size
) {
149 BufferedSpdyFramer
framer(2, false);
150 return framer
.CreateWindowUpdate(stream_id
, delta_window_size
);
153 SpdyFrame
* ConstructSpdyRstStream(SpdyStreamId stream_id
,
154 SpdyRstStreamStatus status
) {
155 BufferedSpdyFramer
framer(2, false);
156 return framer
.CreateRstStream(stream_id
, status
);
159 int ConstructSpdyHeader(const char* const extra_headers
[],
160 int extra_header_count
,
164 const char* this_header
= NULL
;
165 const char* this_value
= NULL
;
166 if (!buffer
|| !buffer_length
)
169 // Sanity check: Non-empty header list.
170 DCHECK(NULL
!= extra_headers
) << "NULL extra headers pointer";
171 // Sanity check: Index out of range.
172 DCHECK((index
>= 0) && (index
< extra_header_count
))
174 << " out of range [0, " << extra_header_count
<< ")";
175 this_header
= extra_headers
[index
* 2];
176 // Sanity check: Non-empty header.
179 std::string::size_type header_len
= strlen(this_header
);
182 this_value
= extra_headers
[1 + (index
* 2)];
183 // Sanity check: Non-empty value.
186 int n
= base::snprintf(buffer
,
194 SpdyFrame
* ConstructSpdyControlFrame(const char* const extra_headers
[],
195 int extra_header_count
,
198 RequestPriority request_priority
,
200 SpdyControlFlags flags
,
201 const char* const* kHeaders
,
203 EXPECT_GE(type
, FIRST_CONTROL_TYPE
);
204 EXPECT_LE(type
, LAST_CONTROL_TYPE
);
205 return ConstructSpdyControlFrame(extra_headers
,
217 SpdyFrame
* ConstructSpdyControlFrame(const char* const extra_headers
[],
218 int extra_header_count
,
220 SpdyStreamId stream_id
,
221 RequestPriority request_priority
,
223 SpdyControlFlags flags
,
224 const char* const* kHeaders
,
226 SpdyStreamId associated_stream_id
) {
227 EXPECT_GE(type
, FIRST_CONTROL_TYPE
);
228 EXPECT_LE(type
, LAST_CONTROL_TYPE
);
229 const SpdyHeaderInfo kSynStartHeader
= {
231 stream_id
, // Stream ID
232 associated_stream_id
, // Associated stream ID
233 ConvertRequestPriorityToSpdyPriority(request_priority
, 2),
235 kSpdyCredentialSlotUnused
,
236 flags
, // Control Flags
237 compressed
, // Compressed
238 RST_STREAM_INVALID
, // Status
241 DATA_FLAG_NONE
// Data Flags
243 return ConstructSpdyFrame(kSynStartHeader
,
250 SpdyFrame
* ConstructSpdyGet(const char* const url
,
252 SpdyStreamId stream_id
,
253 RequestPriority request_priority
) {
254 const SpdyHeaderInfo kSynStartHeader
= {
255 SYN_STREAM
, // Kind = Syn
256 stream_id
, // Stream ID
257 0, // Associated stream ID
258 ConvertRequestPriorityToSpdyPriority(request_priority
, 2),
260 kSpdyCredentialSlotUnused
,
261 CONTROL_FLAG_FIN
, // Control Flags
262 compressed
, // Compressed
263 RST_STREAM_INVALID
, // Status
266 DATA_FLAG_NONE
// Data Flags
268 return ConstructSpdyFrame(kSynStartHeader
, ConstructGetHeaderBlock(url
));
271 SpdyFrame
* ConstructSpdyGet(const char* const extra_headers
[],
272 int extra_header_count
,
275 RequestPriority request_priority
) {
276 return ConstructSpdyGet(extra_headers
, extra_header_count
, compressed
,
277 stream_id
, request_priority
, true);
280 SpdyFrame
* ConstructSpdyGet(const char* const extra_headers
[],
281 int extra_header_count
,
284 RequestPriority request_priority
,
286 const char* const kStandardGetHeaders
[] = {
288 "url", (direct
? "/" : "http://www.google.com/"),
289 "host", "www.google.com",
291 "version", "HTTP/1.1"
293 return ConstructSpdyControlFrame(extra_headers
,
301 arraysize(kStandardGetHeaders
));
304 SpdyFrame
* ConstructSpdyConnect(const char* const extra_headers
[],
305 int extra_header_count
,
307 const char* const kConnectHeaders
[] = {
309 "url", "www.google.com:443",
310 "host", "www.google.com",
311 "version", "HTTP/1.1",
313 return ConstructSpdyControlFrame(extra_headers
,
315 /*compressed*/ false,
321 arraysize(kConnectHeaders
));
324 SpdyFrame
* ConstructSpdyPush(const char* const extra_headers
[],
325 int extra_header_count
,
327 int associated_stream_id
) {
328 const char* const kStandardGetHeaders
[] = {
331 "version", "HTTP/1.1"
333 return ConstructSpdyControlFrame(extra_headers
,
341 arraysize(kStandardGetHeaders
),
342 associated_stream_id
);
345 SpdyFrame
* ConstructSpdyPush(const char* const extra_headers
[],
346 int extra_header_count
,
348 int associated_stream_id
,
350 const char* const kStandardGetHeaders
[] = {
354 "version", "HTTP/1.1"
356 return ConstructSpdyControlFrame(extra_headers
,
364 arraysize(kStandardGetHeaders
),
365 associated_stream_id
);
368 SpdyFrame
* ConstructSpdyPush(const char* const extra_headers
[],
369 int extra_header_count
,
371 int associated_stream_id
,
374 const char* location
) {
375 const char* const kStandardGetHeaders
[] = {
378 "location", location
,
380 "version", "HTTP/1.1"
382 return ConstructSpdyControlFrame(extra_headers
,
390 arraysize(kStandardGetHeaders
),
391 associated_stream_id
);
394 SpdyFrame
* ConstructSpdyPush(int stream_id
,
395 int associated_stream_id
,
397 const char* const kStandardGetHeaders
[] = {
400 return ConstructSpdyControlFrame(0,
408 arraysize(kStandardGetHeaders
),
409 associated_stream_id
);
412 SpdyFrame
* ConstructSpdyPushHeaders(int stream_id
,
413 const char* const extra_headers
[],
414 int extra_header_count
) {
415 const char* const kStandardGetHeaders
[] = {
417 "version", "HTTP/1.1"
419 return ConstructSpdyControlFrame(extra_headers
,
427 arraysize(kStandardGetHeaders
));
430 SpdyFrame
* ConstructSpdySynReplyError(const char* const status
,
431 const char* const* const extra_headers
,
432 int extra_header_count
,
434 const char* const kStandardGetHeaders
[] = {
437 "version", "HTTP/1.1"
439 return ConstructSpdyControlFrame(extra_headers
,
447 arraysize(kStandardGetHeaders
));
450 SpdyFrame
* ConstructSpdyGetSynReplyRedirect(int stream_id
) {
451 static const char* const kExtraHeaders
[] = {
452 "location", "http://www.foo.com/index.php",
454 return ConstructSpdySynReplyError("301 Moved Permanently", kExtraHeaders
,
455 arraysize(kExtraHeaders
)/2, stream_id
);
458 SpdyFrame
* ConstructSpdySynReplyError(int stream_id
) {
459 return ConstructSpdySynReplyError("500 Internal Server Error", NULL
, 0, 1);
462 SpdyFrame
* ConstructSpdyGetSynReply(const char* const extra_headers
[],
463 int extra_header_count
,
465 static const char* const kStandardGetHeaders
[] = {
468 "version", "HTTP/1.1"
470 return ConstructSpdyControlFrame(extra_headers
,
478 arraysize(kStandardGetHeaders
));
481 SpdyFrame
* ConstructSpdyPost(const char* url
,
482 int64 content_length
,
483 const char* const extra_headers
[],
484 int extra_header_count
) {
485 const SpdyHeaderInfo kSynStartHeader
= {
486 SYN_STREAM
, // Kind = Syn
488 0, // Associated stream ID
489 ConvertRequestPriorityToSpdyPriority(LOWEST
, 2),
491 kSpdyCredentialSlotUnused
,
492 CONTROL_FLAG_NONE
, // Control Flags
494 RST_STREAM_INVALID
, // Status
497 DATA_FLAG_NONE
// Data Flags
499 return ConstructSpdyFrame(
500 kSynStartHeader
, ConstructPostHeaderBlock(url
, content_length
));
503 SpdyFrame
* ConstructChunkedSpdyPost(const char* const extra_headers
[],
504 int extra_header_count
) {
505 const char* post_headers
[] = {
508 "host", "www.google.com",
510 "version", "HTTP/1.1"
512 return ConstructSpdyControlFrame(extra_headers
,
520 arraysize(post_headers
));
523 SpdyFrame
* ConstructSpdyPostSynReply(const char* const extra_headers
[],
524 int extra_header_count
) {
525 static const char* const kStandardGetHeaders
[] = {
529 "version", "HTTP/1.1"
531 return ConstructSpdyControlFrame(extra_headers
,
539 arraysize(kStandardGetHeaders
));
542 SpdyFrame
* ConstructSpdyBodyFrame(int stream_id
, bool fin
) {
543 BufferedSpdyFramer
framer(2, false);
544 return framer
.CreateDataFrame(
545 stream_id
, kUploadData
, kUploadDataSize
,
546 fin
? DATA_FLAG_FIN
: DATA_FLAG_NONE
);
549 SpdyFrame
* ConstructSpdyBodyFrame(int stream_id
, const char* data
,
550 uint32 len
, bool fin
) {
551 BufferedSpdyFramer
framer(2, false);
552 return framer
.CreateDataFrame(
553 stream_id
, data
, len
, fin
? DATA_FLAG_FIN
: DATA_FLAG_NONE
);
556 SpdyFrame
* ConstructWrappedSpdyFrame(const scoped_ptr
<SpdyFrame
>& frame
,
558 return ConstructSpdyBodyFrame(stream_id
, frame
->data(),
559 frame
->size(), false);
562 int ConstructSpdyReplyString(const char* const extra_headers
[],
563 int extra_header_count
,
567 char* buffer_write
= buffer
;
568 int buffer_left
= buffer_length
;
569 SpdyHeaderBlock headers
;
570 if (!buffer
|| !buffer_length
)
572 // Copy in the extra headers.
573 AppendToHeaderBlock(extra_headers
, extra_header_count
, &headers
);
574 // The iterator gets us the list of header/value pairs in sorted order.
575 SpdyHeaderBlock::iterator next
= headers
.begin();
576 SpdyHeaderBlock::iterator last
= headers
.end();
577 for ( ; next
!= last
; ++next
) {
579 int value_len
, current_len
, offset
;
580 const char* header_string
= next
->first
.c_str();
581 frame_size
+= AppendToBuffer(header_string
,
582 next
->first
.length(),
585 frame_size
+= AppendToBuffer(": ",
589 // Write the value(s).
590 const char* value_string
= next
->second
.c_str();
591 // Check if it's split among two or more values.
592 value_len
= next
->second
.length();
593 current_len
= strlen(value_string
);
595 // Handle the first N-1 values.
596 while (current_len
< value_len
) {
597 // Finish this line -- write the current value.
598 frame_size
+= AppendToBuffer(value_string
+ offset
,
599 current_len
- offset
,
602 frame_size
+= AppendToBuffer("\n",
606 // Advance to next value.
607 offset
= current_len
+ 1;
608 current_len
+= 1 + strlen(value_string
+ offset
);
609 // Start another line -- add the header again.
610 frame_size
+= AppendToBuffer(header_string
,
611 next
->first
.length(),
614 frame_size
+= AppendToBuffer(": ",
619 EXPECT_EQ(value_len
, current_len
);
620 // Copy the last (or only) value.
621 frame_size
+= AppendToBuffer(value_string
+ offset
,
625 frame_size
+= AppendToBuffer("\n",
633 SpdySessionDependencies::SpdySessionDependencies()
634 : host_resolver(new MockCachingHostResolver
),
635 cert_verifier(new MockCertVerifier
),
636 proxy_service(ProxyService::CreateDirect()),
637 ssl_config_service(new SSLConfigServiceDefaults
),
638 socket_factory(new MockClientSocketFactory
),
639 deterministic_socket_factory(new DeterministicMockClientSocketFactory
),
640 http_auth_handler_factory(
641 HttpAuthHandlerFactory::CreateDefault(host_resolver
.get())),
642 enable_ip_pooling(true),
643 enable_compression(false),
645 enable_user_alternate_protocol_ports(false),
646 time_func(&base::TimeTicks::Now
),
648 // Note: The CancelledTransaction test does cleanup by running all
649 // tasks in the message loop (RunAllPending). Unfortunately, that
650 // doesn't clean up tasks on the host resolver thread; and
651 // TCPConnectJob is currently not cancellable. Using synchronous
652 // lookups allows the test to shutdown cleanly. Until we have
653 // cancellable TCPConnectJobs, use synchronous lookups.
654 host_resolver
->set_synchronous_mode(true);
657 SpdySessionDependencies::SpdySessionDependencies(ProxyService
* proxy_service
)
658 : host_resolver(new MockHostResolver
),
659 cert_verifier(new MockCertVerifier
),
660 proxy_service(proxy_service
),
661 ssl_config_service(new SSLConfigServiceDefaults
),
662 socket_factory(new MockClientSocketFactory
),
663 deterministic_socket_factory(new DeterministicMockClientSocketFactory
),
664 http_auth_handler_factory(
665 HttpAuthHandlerFactory::CreateDefault(host_resolver
.get())),
666 enable_ip_pooling(true),
667 enable_compression(false),
669 enable_user_alternate_protocol_ports(false),
670 time_func(&base::TimeTicks::Now
),
673 SpdySessionDependencies::~SpdySessionDependencies() {}
676 HttpNetworkSession
* SpdySessionDependencies::SpdyCreateSession(
677 SpdySessionDependencies
* session_deps
) {
678 net::HttpNetworkSession::Params params
= CreateSessionParams(session_deps
);
679 params
.client_socket_factory
= session_deps
->socket_factory
.get();
680 HttpNetworkSession
* http_session
= new HttpNetworkSession(params
);
681 SpdySessionPoolPeer
pool_peer(http_session
->spdy_session_pool());
682 pool_peer
.EnableSendingInitialSettings(false);
687 HttpNetworkSession
* SpdySessionDependencies::SpdyCreateSessionDeterministic(
688 SpdySessionDependencies
* session_deps
) {
689 net::HttpNetworkSession::Params params
= CreateSessionParams(session_deps
);
690 params
.client_socket_factory
=
691 session_deps
->deterministic_socket_factory
.get();
692 HttpNetworkSession
* http_session
= new HttpNetworkSession(params
);
693 SpdySessionPoolPeer
pool_peer(http_session
->spdy_session_pool());
694 pool_peer
.EnableSendingInitialSettings(false);
699 net::HttpNetworkSession::Params
SpdySessionDependencies::CreateSessionParams(
700 SpdySessionDependencies
* session_deps
) {
701 net::HttpNetworkSession::Params params
;
702 params
.host_resolver
= session_deps
->host_resolver
.get();
703 params
.cert_verifier
= session_deps
->cert_verifier
.get();
704 params
.proxy_service
= session_deps
->proxy_service
.get();
705 params
.ssl_config_service
= session_deps
->ssl_config_service
;
706 params
.http_auth_handler_factory
=
707 session_deps
->http_auth_handler_factory
.get();
708 params
.http_server_properties
= &session_deps
->http_server_properties
;
709 params
.enable_spdy_ip_pooling
= session_deps
->enable_ip_pooling
;
710 params
.enable_spdy_compression
= session_deps
->enable_compression
;
711 params
.enable_spdy_ping_based_connection_checking
= session_deps
->enable_ping
;
712 params
.enable_user_alternate_protocol_ports
=
713 session_deps
->enable_user_alternate_protocol_ports
;
714 params
.spdy_default_protocol
= kProtoSPDY2
;
715 params
.time_func
= session_deps
->time_func
;
716 params
.trusted_spdy_proxy
= session_deps
->trusted_spdy_proxy
;
717 params
.net_log
= session_deps
->net_log
;
721 SpdyURLRequestContext::SpdyURLRequestContext()
722 : ALLOW_THIS_IN_INITIALIZER_LIST(storage_(this)) {
723 storage_
.set_host_resolver(scoped_ptr
<HostResolver
>(new MockHostResolver
));
724 storage_
.set_cert_verifier(new MockCertVerifier
);
725 storage_
.set_proxy_service(ProxyService::CreateDirect());
726 storage_
.set_ssl_config_service(new SSLConfigServiceDefaults
);
727 storage_
.set_http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault(
729 storage_
.set_http_server_properties(new HttpServerPropertiesImpl
);
730 net::HttpNetworkSession::Params params
;
731 params
.client_socket_factory
= &socket_factory_
;
732 params
.host_resolver
= host_resolver();
733 params
.cert_verifier
= cert_verifier();
734 params
.proxy_service
= proxy_service();
735 params
.ssl_config_service
= ssl_config_service();
736 params
.http_auth_handler_factory
= http_auth_handler_factory();
737 params
.network_delegate
= network_delegate();
738 params
.enable_spdy_compression
= false;
739 params
.enable_spdy_ping_based_connection_checking
= false;
740 params
.spdy_default_protocol
= kProtoSPDY2
;
741 params
.http_server_properties
= http_server_properties();
742 scoped_refptr
<HttpNetworkSession
> network_session(
743 new HttpNetworkSession(params
));
744 SpdySessionPoolPeer
pool_peer(network_session
->spdy_session_pool());
745 pool_peer
.EnableSendingInitialSettings(false);
746 storage_
.set_http_transaction_factory(new HttpCache(
748 HttpCache::DefaultBackend::InMemory(0)));
751 SpdyURLRequestContext::~SpdyURLRequestContext() {
754 const SpdyHeaderInfo
MakeSpdyHeader(SpdyFrameType type
) {
755 const SpdyHeaderInfo kHeader
= {
758 0, // Associated stream ID
759 ConvertRequestPriorityToSpdyPriority(LOWEST
, 2), // Priority
760 kSpdyCredentialSlotUnused
,
761 CONTROL_FLAG_FIN
, // Control Flags
763 RST_STREAM_INVALID
, // Status
766 DATA_FLAG_NONE
// Data Flags
771 } // namespace test_spdy2