OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/spdy/spdy_test_util_common.h" | |
6 | |
7 #include <cstddef> | |
8 | |
9 #include "base/compiler_specific.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "base/strings/string_split.h" | |
13 #include "net/cert/mock_cert_verifier.h" | |
14 #include "net/http/http_cache.h" | |
15 #include "net/http/http_network_session.h" | |
16 #include "net/http/http_network_transaction.h" | |
17 #include "net/http/http_server_properties_impl.h" | |
18 #include "net/socket/socket_test_util.h" | |
19 #include "net/socket/ssl_client_socket.h" | |
20 #include "net/socket/transport_client_socket_pool.h" | |
21 #include "net/spdy/buffered_spdy_framer.h" | |
22 #include "net/spdy/spdy_framer.h" | |
23 #include "net/spdy/spdy_http_utils.h" | |
24 #include "net/spdy/spdy_session.h" | |
25 #include "net/spdy/spdy_session_pool.h" | |
26 #include "net/spdy/spdy_stream.h" | |
27 #include "net/url_request/url_request_job_factory_impl.h" | |
28 | |
29 namespace net { | |
30 | |
31 namespace { | |
32 | |
33 bool next_proto_is_spdy(NextProto next_proto) { | |
34 return next_proto >= kProtoSPDYMinimumVersion && | |
35 next_proto <= kProtoSPDYMaximumVersion; | |
36 } | |
37 | |
38 // Parses a URL into the scheme, host, and path components required for a | |
39 // SPDY request. | |
40 void ParseUrl(base::StringPiece url, std::string* scheme, std::string* host, | |
41 std::string* path) { | |
42 GURL gurl(url.as_string()); | |
43 path->assign(gurl.PathForRequest()); | |
44 scheme->assign(gurl.scheme()); | |
45 host->assign(gurl.host()); | |
46 if (gurl.has_port()) { | |
47 host->append(":"); | |
48 host->append(gurl.port()); | |
49 } | |
50 } | |
51 | |
52 } // namespace | |
53 | |
54 NextProtoVector SpdyNextProtos() { | |
55 NextProtoVector next_protos; | |
56 next_protos.push_back(kProtoHTTP11); | |
57 next_protos.push_back(kProtoSPDY31); | |
58 next_protos.push_back(kProtoSPDY4_14); | |
59 next_protos.push_back(kProtoSPDY4_15); | |
60 next_protos.push_back(kProtoQUIC1SPDY3); | |
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. | |
68 MockWrite* 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. | |
83 MockWrite* 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. | |
91 MockRead* 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. | |
106 MockRead* 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|. | |
115 void 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. | |
158 MockWrite 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. | |
163 MockWrite 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. | |
168 MockWrite 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. | |
173 MockRead 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. | |
178 MockRead 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. | |
183 MockRead 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. | |
189 int 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 | |
205 namespace { | |
206 | |
207 class PriorityGetter : public BufferedSpdyFramerVisitorInterface { | |
208 public: | |
209 PriorityGetter() : priority_(0) {} | |
210 ~PriorityGetter() override {} | |
211 | |
212 SpdyPriority priority() const { | |
213 return priority_; | |
214 } | |
215 | |
216 void OnError(SpdyFramer::SpdyError error_code) override {} | |
217 void OnStreamError(SpdyStreamId stream_id, | |
218 const std::string& description) override {} | |
219 void OnSynStream(SpdyStreamId stream_id, | |
220 SpdyStreamId associated_stream_id, | |
221 SpdyPriority priority, | |
222 bool fin, | |
223 bool unidirectional, | |
224 const SpdyHeaderBlock& headers) override { | |
225 priority_ = priority; | |
226 } | |
227 void OnSynReply(SpdyStreamId stream_id, | |
228 bool fin, | |
229 const SpdyHeaderBlock& headers) override {} | |
230 void OnHeaders(SpdyStreamId stream_id, | |
231 bool has_priority, | |
232 SpdyPriority priority, | |
233 bool fin, | |
234 const SpdyHeaderBlock& headers) override { | |
235 if (has_priority) { | |
236 priority_ = priority; | |
237 } | |
238 } | |
239 void OnDataFrameHeader(SpdyStreamId stream_id, | |
240 size_t length, | |
241 bool fin) override {} | |
242 void OnStreamFrameData(SpdyStreamId stream_id, | |
243 const char* data, | |
244 size_t len, | |
245 bool fin) override {} | |
246 void OnSettings(bool clear_persisted) override {} | |
247 void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override {} | |
248 void OnPing(SpdyPingId unique_id, bool is_ack) override {} | |
249 void OnRstStream(SpdyStreamId stream_id, | |
250 SpdyRstStreamStatus status) override {} | |
251 void OnGoAway(SpdyStreamId last_accepted_stream_id, | |
252 SpdyGoAwayStatus status) override {} | |
253 void OnWindowUpdate(SpdyStreamId stream_id, | |
254 uint32 delta_window_size) override {} | |
255 void OnPushPromise(SpdyStreamId stream_id, | |
256 SpdyStreamId promised_stream_id, | |
257 const SpdyHeaderBlock& headers) override {} | |
258 bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override { | |
259 return false; | |
260 } | |
261 | |
262 private: | |
263 SpdyPriority priority_; | |
264 }; | |
265 | |
266 } // namespace | |
267 | |
268 bool GetSpdyPriority(SpdyMajorVersion version, | |
269 const SpdyFrame& frame, | |
270 SpdyPriority* priority) { | |
271 BufferedSpdyFramer framer(version, false); | |
272 PriorityGetter priority_getter; | |
273 framer.set_visitor(&priority_getter); | |
274 size_t frame_size = frame.size(); | |
275 if (framer.ProcessInput(frame.data(), frame_size) != frame_size) { | |
276 return false; | |
277 } | |
278 *priority = priority_getter.priority(); | |
279 return true; | |
280 } | |
281 | |
282 base::WeakPtr<SpdyStream> CreateStreamSynchronously( | |
283 SpdyStreamType type, | |
284 const base::WeakPtr<SpdySession>& session, | |
285 const GURL& url, | |
286 RequestPriority priority, | |
287 const BoundNetLog& net_log) { | |
288 SpdyStreamRequest stream_request; | |
289 int rv = stream_request.StartRequest(type, session, url, priority, net_log, | |
290 CompletionCallback()); | |
291 return | |
292 (rv == OK) ? stream_request.ReleaseStream() : base::WeakPtr<SpdyStream>(); | |
293 } | |
294 | |
295 StreamReleaserCallback::StreamReleaserCallback() {} | |
296 | |
297 StreamReleaserCallback::~StreamReleaserCallback() {} | |
298 | |
299 CompletionCallback StreamReleaserCallback::MakeCallback( | |
300 SpdyStreamRequest* request) { | |
301 return base::Bind(&StreamReleaserCallback::OnComplete, | |
302 base::Unretained(this), | |
303 request); | |
304 } | |
305 | |
306 void StreamReleaserCallback::OnComplete( | |
307 SpdyStreamRequest* request, int result) { | |
308 if (result == OK) | |
309 request->ReleaseStream()->Cancel(); | |
310 SetResult(result); | |
311 } | |
312 | |
313 MockECSignatureCreator::MockECSignatureCreator(crypto::ECPrivateKey* key) | |
314 : key_(key) { | |
315 } | |
316 | |
317 bool MockECSignatureCreator::Sign(const uint8* data, | |
318 int data_len, | |
319 std::vector<uint8>* signature) { | |
320 std::vector<uint8> private_key_value; | |
321 key_->ExportValue(&private_key_value); | |
322 std::string head = "fakesignature"; | |
323 std::string tail = "/fakesignature"; | |
324 | |
325 signature->clear(); | |
326 signature->insert(signature->end(), head.begin(), head.end()); | |
327 signature->insert(signature->end(), private_key_value.begin(), | |
328 private_key_value.end()); | |
329 signature->insert(signature->end(), '-'); | |
330 signature->insert(signature->end(), data, data + data_len); | |
331 signature->insert(signature->end(), tail.begin(), tail.end()); | |
332 return true; | |
333 } | |
334 | |
335 bool MockECSignatureCreator::DecodeSignature( | |
336 const std::vector<uint8>& signature, | |
337 std::vector<uint8>* out_raw_sig) { | |
338 *out_raw_sig = signature; | |
339 return true; | |
340 } | |
341 | |
342 MockECSignatureCreatorFactory::MockECSignatureCreatorFactory() { | |
343 crypto::ECSignatureCreator::SetFactoryForTesting(this); | |
344 } | |
345 | |
346 MockECSignatureCreatorFactory::~MockECSignatureCreatorFactory() { | |
347 crypto::ECSignatureCreator::SetFactoryForTesting(NULL); | |
348 } | |
349 | |
350 crypto::ECSignatureCreator* MockECSignatureCreatorFactory::Create( | |
351 crypto::ECPrivateKey* key) { | |
352 return new MockECSignatureCreator(key); | |
353 } | |
354 | |
355 SpdySessionDependencies::SpdySessionDependencies(NextProto protocol) | |
356 : host_resolver(new MockCachingHostResolver), | |
357 cert_verifier(new MockCertVerifier), | |
358 transport_security_state(new TransportSecurityState), | |
359 proxy_service(ProxyService::CreateDirect()), | |
360 ssl_config_service(new SSLConfigServiceDefaults), | |
361 socket_factory(new MockClientSocketFactory), | |
362 deterministic_socket_factory(new DeterministicMockClientSocketFactory), | |
363 http_auth_handler_factory( | |
364 HttpAuthHandlerFactory::CreateDefault(host_resolver.get())), | |
365 enable_ip_pooling(true), | |
366 enable_compression(false), | |
367 enable_ping(false), | |
368 enable_user_alternate_protocol_ports(false), | |
369 protocol(protocol), | |
370 stream_initial_recv_window_size( | |
371 SpdySession::GetInitialWindowSize(protocol)), | |
372 time_func(&base::TimeTicks::Now), | |
373 force_spdy_over_ssl(false), | |
374 force_spdy_always(false), | |
375 use_alternate_protocols(false), | |
376 net_log(NULL) { | |
377 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol; | |
378 | |
379 // Note: The CancelledTransaction test does cleanup by running all | |
380 // tasks in the message loop (RunAllPending). Unfortunately, that | |
381 // doesn't clean up tasks on the host resolver thread; and | |
382 // TCPConnectJob is currently not cancellable. Using synchronous | |
383 // lookups allows the test to shutdown cleanly. Until we have | |
384 // cancellable TCPConnectJobs, use synchronous lookups. | |
385 host_resolver->set_synchronous_mode(true); | |
386 } | |
387 | |
388 SpdySessionDependencies::SpdySessionDependencies(NextProto protocol, | |
389 ProxyService* proxy_service) | |
390 : host_resolver(new MockHostResolver), | |
391 cert_verifier(new MockCertVerifier), | |
392 transport_security_state(new TransportSecurityState), | |
393 proxy_service(proxy_service), | |
394 ssl_config_service(new SSLConfigServiceDefaults), | |
395 socket_factory(new MockClientSocketFactory), | |
396 deterministic_socket_factory(new DeterministicMockClientSocketFactory), | |
397 http_auth_handler_factory( | |
398 HttpAuthHandlerFactory::CreateDefault(host_resolver.get())), | |
399 enable_ip_pooling(true), | |
400 enable_compression(false), | |
401 enable_ping(false), | |
402 enable_user_alternate_protocol_ports(false), | |
403 protocol(protocol), | |
404 stream_initial_recv_window_size( | |
405 SpdySession::GetInitialWindowSize(protocol)), | |
406 time_func(&base::TimeTicks::Now), | |
407 force_spdy_over_ssl(false), | |
408 force_spdy_always(false), | |
409 use_alternate_protocols(false), | |
410 net_log(NULL) { | |
411 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol; | |
412 } | |
413 | |
414 SpdySessionDependencies::~SpdySessionDependencies() {} | |
415 | |
416 // static | |
417 HttpNetworkSession* SpdySessionDependencies::SpdyCreateSession( | |
418 SpdySessionDependencies* session_deps) { | |
419 net::HttpNetworkSession::Params params = CreateSessionParams(session_deps); | |
420 params.client_socket_factory = session_deps->socket_factory.get(); | |
421 HttpNetworkSession* http_session = new HttpNetworkSession(params); | |
422 SpdySessionPoolPeer pool_peer(http_session->spdy_session_pool()); | |
423 pool_peer.SetEnableSendingInitialData(false); | |
424 return http_session; | |
425 } | |
426 | |
427 // static | |
428 HttpNetworkSession* SpdySessionDependencies::SpdyCreateSessionDeterministic( | |
429 SpdySessionDependencies* session_deps) { | |
430 net::HttpNetworkSession::Params params = CreateSessionParams(session_deps); | |
431 params.client_socket_factory = | |
432 session_deps->deterministic_socket_factory.get(); | |
433 HttpNetworkSession* http_session = new HttpNetworkSession(params); | |
434 SpdySessionPoolPeer pool_peer(http_session->spdy_session_pool()); | |
435 pool_peer.SetEnableSendingInitialData(false); | |
436 return http_session; | |
437 } | |
438 | |
439 // static | |
440 net::HttpNetworkSession::Params SpdySessionDependencies::CreateSessionParams( | |
441 SpdySessionDependencies* session_deps) { | |
442 DCHECK(next_proto_is_spdy(session_deps->protocol)) << | |
443 "Invalid protocol: " << session_deps->protocol; | |
444 | |
445 net::HttpNetworkSession::Params params; | |
446 params.host_resolver = session_deps->host_resolver.get(); | |
447 params.cert_verifier = session_deps->cert_verifier.get(); | |
448 params.transport_security_state = | |
449 session_deps->transport_security_state.get(); | |
450 params.proxy_service = session_deps->proxy_service.get(); | |
451 params.ssl_config_service = session_deps->ssl_config_service.get(); | |
452 params.http_auth_handler_factory = | |
453 session_deps->http_auth_handler_factory.get(); | |
454 params.http_server_properties = | |
455 session_deps->http_server_properties.GetWeakPtr(); | |
456 params.enable_spdy_compression = session_deps->enable_compression; | |
457 params.enable_spdy_ping_based_connection_checking = session_deps->enable_ping; | |
458 params.enable_user_alternate_protocol_ports = | |
459 session_deps->enable_user_alternate_protocol_ports; | |
460 params.spdy_default_protocol = session_deps->protocol; | |
461 params.spdy_stream_initial_recv_window_size = | |
462 session_deps->stream_initial_recv_window_size; | |
463 params.time_func = session_deps->time_func; | |
464 params.next_protos = session_deps->next_protos; | |
465 params.trusted_spdy_proxy = session_deps->trusted_spdy_proxy; | |
466 params.force_spdy_over_ssl = session_deps->force_spdy_over_ssl; | |
467 params.force_spdy_always = session_deps->force_spdy_always; | |
468 params.use_alternate_protocols = session_deps->use_alternate_protocols; | |
469 params.net_log = session_deps->net_log; | |
470 return params; | |
471 } | |
472 | |
473 SpdyURLRequestContext::SpdyURLRequestContext(NextProto protocol, | |
474 bool force_spdy_over_ssl, | |
475 bool force_spdy_always) | |
476 : storage_(this) { | |
477 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol; | |
478 | |
479 storage_.set_host_resolver(scoped_ptr<HostResolver>(new MockHostResolver)); | |
480 storage_.set_cert_verifier(new MockCertVerifier); | |
481 storage_.set_transport_security_state(new TransportSecurityState); | |
482 storage_.set_proxy_service(ProxyService::CreateDirect()); | |
483 storage_.set_ssl_config_service(new SSLConfigServiceDefaults); | |
484 storage_.set_http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault( | |
485 host_resolver())); | |
486 storage_.set_http_server_properties( | |
487 scoped_ptr<HttpServerProperties>(new HttpServerPropertiesImpl())); | |
488 storage_.set_job_factory(new URLRequestJobFactoryImpl()); | |
489 net::HttpNetworkSession::Params params; | |
490 params.client_socket_factory = &socket_factory_; | |
491 params.host_resolver = host_resolver(); | |
492 params.cert_verifier = cert_verifier(); | |
493 params.transport_security_state = transport_security_state(); | |
494 params.proxy_service = proxy_service(); | |
495 params.ssl_config_service = ssl_config_service(); | |
496 params.http_auth_handler_factory = http_auth_handler_factory(); | |
497 params.network_delegate = network_delegate(); | |
498 params.enable_spdy_compression = false; | |
499 params.enable_spdy_ping_based_connection_checking = false; | |
500 params.spdy_default_protocol = protocol; | |
501 params.force_spdy_over_ssl = force_spdy_over_ssl; | |
502 params.force_spdy_always = force_spdy_always; | |
503 params.http_server_properties = http_server_properties(); | |
504 scoped_refptr<HttpNetworkSession> network_session( | |
505 new HttpNetworkSession(params)); | |
506 SpdySessionPoolPeer pool_peer(network_session->spdy_session_pool()); | |
507 pool_peer.SetEnableSendingInitialData(false); | |
508 storage_.set_http_transaction_factory(new HttpCache( | |
509 network_session.get(), HttpCache::DefaultBackend::InMemory(0))); | |
510 } | |
511 | |
512 SpdyURLRequestContext::~SpdyURLRequestContext() { | |
513 AssertNoURLRequests(); | |
514 } | |
515 | |
516 bool HasSpdySession(SpdySessionPool* pool, const SpdySessionKey& key) { | |
517 return pool->FindAvailableSession(key, BoundNetLog()) != NULL; | |
518 } | |
519 | |
520 namespace { | |
521 | |
522 base::WeakPtr<SpdySession> CreateSpdySessionHelper( | |
523 const scoped_refptr<HttpNetworkSession>& http_session, | |
524 const SpdySessionKey& key, | |
525 const BoundNetLog& net_log, | |
526 Error expected_status, | |
527 bool is_secure) { | |
528 EXPECT_FALSE(HasSpdySession(http_session->spdy_session_pool(), key)); | |
529 | |
530 scoped_refptr<TransportSocketParams> transport_params( | |
531 new TransportSocketParams( | |
532 key.host_port_pair(), false, false, OnHostResolutionCallback(), | |
533 TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT)); | |
534 | |
535 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); | |
536 TestCompletionCallback callback; | |
537 | |
538 int rv = ERR_UNEXPECTED; | |
539 if (is_secure) { | |
540 SSLConfig ssl_config; | |
541 scoped_refptr<SSLSocketParams> ssl_params( | |
542 new SSLSocketParams(transport_params, | |
543 NULL, | |
544 NULL, | |
545 key.host_port_pair(), | |
546 ssl_config, | |
547 key.privacy_mode(), | |
548 0, | |
549 false, | |
550 false)); | |
551 rv = connection->Init(key.host_port_pair().ToString(), | |
552 ssl_params, | |
553 MEDIUM, | |
554 callback.callback(), | |
555 http_session->GetSSLSocketPool( | |
556 HttpNetworkSession::NORMAL_SOCKET_POOL), | |
557 net_log); | |
558 } else { | |
559 rv = connection->Init(key.host_port_pair().ToString(), | |
560 transport_params, | |
561 MEDIUM, | |
562 callback.callback(), | |
563 http_session->GetTransportSocketPool( | |
564 HttpNetworkSession::NORMAL_SOCKET_POOL), | |
565 net_log); | |
566 } | |
567 | |
568 if (rv == ERR_IO_PENDING) | |
569 rv = callback.WaitForResult(); | |
570 | |
571 EXPECT_EQ(OK, rv); | |
572 | |
573 base::WeakPtr<SpdySession> spdy_session = | |
574 http_session->spdy_session_pool()->CreateAvailableSessionFromSocket( | |
575 key, connection.Pass(), net_log, OK, is_secure); | |
576 // Failure is reported asynchronously. | |
577 EXPECT_TRUE(spdy_session != NULL); | |
578 EXPECT_TRUE(HasSpdySession(http_session->spdy_session_pool(), key)); | |
579 return spdy_session; | |
580 } | |
581 | |
582 } // namespace | |
583 | |
584 base::WeakPtr<SpdySession> CreateInsecureSpdySession( | |
585 const scoped_refptr<HttpNetworkSession>& http_session, | |
586 const SpdySessionKey& key, | |
587 const BoundNetLog& net_log) { | |
588 return CreateSpdySessionHelper(http_session, key, net_log, | |
589 OK, false /* is_secure */); | |
590 } | |
591 | |
592 base::WeakPtr<SpdySession> TryCreateInsecureSpdySessionExpectingFailure( | |
593 const scoped_refptr<HttpNetworkSession>& http_session, | |
594 const SpdySessionKey& key, | |
595 Error expected_error, | |
596 const BoundNetLog& net_log) { | |
597 DCHECK_LT(expected_error, ERR_IO_PENDING); | |
598 return CreateSpdySessionHelper(http_session, key, net_log, | |
599 expected_error, false /* is_secure */); | |
600 } | |
601 | |
602 base::WeakPtr<SpdySession> CreateSecureSpdySession( | |
603 const scoped_refptr<HttpNetworkSession>& http_session, | |
604 const SpdySessionKey& key, | |
605 const BoundNetLog& net_log) { | |
606 return CreateSpdySessionHelper(http_session, key, net_log, | |
607 OK, true /* is_secure */); | |
608 } | |
609 | |
610 namespace { | |
611 | |
612 // A ClientSocket used for CreateFakeSpdySession() below. | |
613 class FakeSpdySessionClientSocket : public MockClientSocket { | |
614 public: | |
615 FakeSpdySessionClientSocket(int read_result) | |
616 : MockClientSocket(BoundNetLog()), | |
617 read_result_(read_result) {} | |
618 | |
619 ~FakeSpdySessionClientSocket() override {} | |
620 | |
621 int Read(IOBuffer* buf, | |
622 int buf_len, | |
623 const CompletionCallback& callback) override { | |
624 return read_result_; | |
625 } | |
626 | |
627 int Write(IOBuffer* buf, | |
628 int buf_len, | |
629 const CompletionCallback& callback) override { | |
630 return ERR_IO_PENDING; | |
631 } | |
632 | |
633 // Return kProtoUnknown to use the pool's default protocol. | |
634 NextProto GetNegotiatedProtocol() const override { return kProtoUnknown; } | |
635 | |
636 // The functions below are not expected to be called. | |
637 | |
638 int Connect(const CompletionCallback& callback) override { | |
639 ADD_FAILURE(); | |
640 return ERR_UNEXPECTED; | |
641 } | |
642 | |
643 bool WasEverUsed() const override { | |
644 ADD_FAILURE(); | |
645 return false; | |
646 } | |
647 | |
648 bool UsingTCPFastOpen() const override { | |
649 ADD_FAILURE(); | |
650 return false; | |
651 } | |
652 | |
653 bool WasNpnNegotiated() const override { | |
654 ADD_FAILURE(); | |
655 return false; | |
656 } | |
657 | |
658 bool GetSSLInfo(SSLInfo* ssl_info) override { | |
659 ADD_FAILURE(); | |
660 return false; | |
661 } | |
662 | |
663 private: | |
664 int read_result_; | |
665 }; | |
666 | |
667 base::WeakPtr<SpdySession> CreateFakeSpdySessionHelper( | |
668 SpdySessionPool* pool, | |
669 const SpdySessionKey& key, | |
670 Error expected_status) { | |
671 EXPECT_NE(expected_status, ERR_IO_PENDING); | |
672 EXPECT_FALSE(HasSpdySession(pool, key)); | |
673 scoped_ptr<ClientSocketHandle> handle(new ClientSocketHandle()); | |
674 handle->SetSocket(scoped_ptr<StreamSocket>(new FakeSpdySessionClientSocket( | |
675 expected_status == OK ? ERR_IO_PENDING : expected_status))); | |
676 base::WeakPtr<SpdySession> spdy_session = | |
677 pool->CreateAvailableSessionFromSocket( | |
678 key, handle.Pass(), BoundNetLog(), OK, true /* is_secure */); | |
679 // Failure is reported asynchronously. | |
680 EXPECT_TRUE(spdy_session != NULL); | |
681 EXPECT_TRUE(HasSpdySession(pool, key)); | |
682 return spdy_session; | |
683 } | |
684 | |
685 } // namespace | |
686 | |
687 base::WeakPtr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool, | |
688 const SpdySessionKey& key) { | |
689 return CreateFakeSpdySessionHelper(pool, key, OK); | |
690 } | |
691 | |
692 base::WeakPtr<SpdySession> TryCreateFakeSpdySessionExpectingFailure( | |
693 SpdySessionPool* pool, | |
694 const SpdySessionKey& key, | |
695 Error expected_error) { | |
696 DCHECK_LT(expected_error, ERR_IO_PENDING); | |
697 return CreateFakeSpdySessionHelper(pool, key, expected_error); | |
698 } | |
699 | |
700 SpdySessionPoolPeer::SpdySessionPoolPeer(SpdySessionPool* pool) : pool_(pool) { | |
701 } | |
702 | |
703 void SpdySessionPoolPeer::RemoveAliases(const SpdySessionKey& key) { | |
704 pool_->RemoveAliases(key); | |
705 } | |
706 | |
707 void SpdySessionPoolPeer::DisableDomainAuthenticationVerification() { | |
708 pool_->verify_domain_authentication_ = false; | |
709 } | |
710 | |
711 void SpdySessionPoolPeer::SetEnableSendingInitialData(bool enabled) { | |
712 pool_->enable_sending_initial_data_ = enabled; | |
713 } | |
714 | |
715 SpdyTestUtil::SpdyTestUtil(NextProto protocol) | |
716 : protocol_(protocol), | |
717 spdy_version_(NextProtoToSpdyMajorVersion(protocol)) { | |
718 DCHECK(next_proto_is_spdy(protocol)) << "Invalid protocol: " << protocol; | |
719 } | |
720 | |
721 void SpdyTestUtil::AddUrlToHeaderBlock(base::StringPiece url, | |
722 SpdyHeaderBlock* headers) const { | |
723 std::string scheme, host, path; | |
724 ParseUrl(url, &scheme, &host, &path); | |
725 (*headers)[GetSchemeKey()] = scheme; | |
726 (*headers)[GetHostKey()] = host; | |
727 (*headers)[GetPathKey()] = path; | |
728 } | |
729 | |
730 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructGetHeaderBlock( | |
731 base::StringPiece url) const { | |
732 return ConstructHeaderBlock("GET", url, NULL); | |
733 } | |
734 | |
735 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructGetHeaderBlockForProxy( | |
736 base::StringPiece url) const { | |
737 scoped_ptr<SpdyHeaderBlock> headers(ConstructGetHeaderBlock(url)); | |
738 return headers.Pass(); | |
739 } | |
740 | |
741 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeadHeaderBlock( | |
742 base::StringPiece url, | |
743 int64 content_length) const { | |
744 return ConstructHeaderBlock("HEAD", url, &content_length); | |
745 } | |
746 | |
747 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPostHeaderBlock( | |
748 base::StringPiece url, | |
749 int64 content_length) const { | |
750 return ConstructHeaderBlock("POST", url, &content_length); | |
751 } | |
752 | |
753 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructPutHeaderBlock( | |
754 base::StringPiece url, | |
755 int64 content_length) const { | |
756 return ConstructHeaderBlock("PUT", url, &content_length); | |
757 } | |
758 | |
759 SpdyFrame* SpdyTestUtil::ConstructSpdyFrame( | |
760 const SpdyHeaderInfo& header_info, | |
761 scoped_ptr<SpdyHeaderBlock> headers) const { | |
762 BufferedSpdyFramer framer(spdy_version_, header_info.compressed); | |
763 SpdyFrame* frame = NULL; | |
764 switch (header_info.kind) { | |
765 case DATA: | |
766 frame = framer.CreateDataFrame(header_info.id, header_info.data, | |
767 header_info.data_length, | |
768 header_info.data_flags); | |
769 break; | |
770 case SYN_STREAM: | |
771 { | |
772 frame = framer.CreateSynStream(header_info.id, header_info.assoc_id, | |
773 header_info.priority, | |
774 header_info.control_flags, | |
775 headers.get()); | |
776 } | |
777 break; | |
778 case SYN_REPLY: | |
779 frame = framer.CreateSynReply(header_info.id, header_info.control_flags, | |
780 headers.get()); | |
781 break; | |
782 case RST_STREAM: | |
783 frame = framer.CreateRstStream(header_info.id, header_info.status); | |
784 break; | |
785 case HEADERS: | |
786 frame = framer.CreateHeaders(header_info.id, header_info.control_flags, | |
787 header_info.priority, | |
788 headers.get()); | |
789 break; | |
790 default: | |
791 ADD_FAILURE(); | |
792 break; | |
793 } | |
794 return frame; | |
795 } | |
796 | |
797 SpdyFrame* SpdyTestUtil::ConstructSpdyFrame(const SpdyHeaderInfo& header_info, | |
798 const char* const extra_headers[], | |
799 int extra_header_count, | |
800 const char* const tail_headers[], | |
801 int tail_header_count) const { | |
802 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock()); | |
803 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get()); | |
804 if (tail_headers && tail_header_count) | |
805 AppendToHeaderBlock(tail_headers, tail_header_count, headers.get()); | |
806 return ConstructSpdyFrame(header_info, headers.Pass()); | |
807 } | |
808 | |
809 SpdyFrame* SpdyTestUtil::ConstructSpdyControlFrame( | |
810 scoped_ptr<SpdyHeaderBlock> headers, | |
811 bool compressed, | |
812 SpdyStreamId stream_id, | |
813 RequestPriority request_priority, | |
814 SpdyFrameType type, | |
815 SpdyControlFlags flags, | |
816 SpdyStreamId associated_stream_id) const { | |
817 EXPECT_GE(type, DATA); | |
818 EXPECT_LE(type, PRIORITY); | |
819 const SpdyHeaderInfo header_info = { | |
820 type, | |
821 stream_id, | |
822 associated_stream_id, | |
823 ConvertRequestPriorityToSpdyPriority(request_priority, spdy_version_), | |
824 0, // credential slot | |
825 flags, | |
826 compressed, | |
827 RST_STREAM_INVALID, // status | |
828 NULL, // data | |
829 0, // length | |
830 DATA_FLAG_NONE | |
831 }; | |
832 return ConstructSpdyFrame(header_info, headers.Pass()); | |
833 } | |
834 | |
835 SpdyFrame* SpdyTestUtil::ConstructSpdyControlFrame( | |
836 const char* const extra_headers[], | |
837 int extra_header_count, | |
838 bool compressed, | |
839 SpdyStreamId stream_id, | |
840 RequestPriority request_priority, | |
841 SpdyFrameType type, | |
842 SpdyControlFlags flags, | |
843 const char* const* tail_headers, | |
844 int tail_header_size, | |
845 SpdyStreamId associated_stream_id) const { | |
846 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock()); | |
847 AppendToHeaderBlock(extra_headers, extra_header_count, headers.get()); | |
848 if (tail_headers && tail_header_size) | |
849 AppendToHeaderBlock(tail_headers, tail_header_size / 2, headers.get()); | |
850 return ConstructSpdyControlFrame( | |
851 headers.Pass(), compressed, stream_id, | |
852 request_priority, type, flags, associated_stream_id); | |
853 } | |
854 | |
855 std::string SpdyTestUtil::ConstructSpdyReplyString( | |
856 const SpdyHeaderBlock& headers) const { | |
857 std::string reply_string; | |
858 for (SpdyHeaderBlock::const_iterator it = headers.begin(); | |
859 it != headers.end(); ++it) { | |
860 std::string key = it->first; | |
861 // Remove leading colon from "special" headers (for SPDY3 and | |
862 // above). | |
863 if (spdy_version() >= SPDY3 && key[0] == ':') | |
864 key = key.substr(1); | |
865 std::vector<std::string> values; | |
866 base::SplitString(it->second, '\0', &values); | |
867 for (std::vector<std::string>::const_iterator it2 = values.begin(); | |
868 it2 != values.end(); ++it2) { | |
869 reply_string += key + ": " + *it2 + "\n"; | |
870 } | |
871 } | |
872 return reply_string; | |
873 } | |
874 | |
875 // TODO(jgraettinger): Eliminate uses of this method in tests (prefer | |
876 // SpdySettingsIR). | |
877 SpdyFrame* SpdyTestUtil::ConstructSpdySettings( | |
878 const SettingsMap& settings) const { | |
879 SpdySettingsIR settings_ir; | |
880 for (SettingsMap::const_iterator it = settings.begin(); | |
881 it != settings.end(); | |
882 ++it) { | |
883 settings_ir.AddSetting( | |
884 it->first, | |
885 (it->second.first & SETTINGS_FLAG_PLEASE_PERSIST) != 0, | |
886 (it->second.first & SETTINGS_FLAG_PERSISTED) != 0, | |
887 it->second.second); | |
888 } | |
889 return CreateFramer(false)->SerializeFrame(settings_ir); | |
890 } | |
891 | |
892 SpdyFrame* SpdyTestUtil::ConstructSpdySettingsAck() const { | |
893 char kEmptyWrite[] = ""; | |
894 | |
895 if (spdy_version() > SPDY3) { | |
896 SpdySettingsIR settings_ir; | |
897 settings_ir.set_is_ack(true); | |
898 return CreateFramer(false)->SerializeFrame(settings_ir); | |
899 } | |
900 // No settings ACK write occurs. Create an empty placeholder write. | |
901 return new SpdyFrame(kEmptyWrite, 0, false); | |
902 } | |
903 | |
904 SpdyFrame* SpdyTestUtil::ConstructSpdyPing(uint32 ping_id, bool is_ack) const { | |
905 SpdyPingIR ping_ir(ping_id); | |
906 ping_ir.set_is_ack(is_ack); | |
907 return CreateFramer(false)->SerializeFrame(ping_ir); | |
908 } | |
909 | |
910 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway() const { | |
911 return ConstructSpdyGoAway(0); | |
912 } | |
913 | |
914 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway( | |
915 SpdyStreamId last_good_stream_id) const { | |
916 SpdyGoAwayIR go_ir(last_good_stream_id, GOAWAY_OK, "go away"); | |
917 return CreateFramer(false)->SerializeFrame(go_ir); | |
918 } | |
919 | |
920 SpdyFrame* SpdyTestUtil::ConstructSpdyGoAway(SpdyStreamId last_good_stream_id, | |
921 SpdyGoAwayStatus status, | |
922 const std::string& desc) const { | |
923 SpdyGoAwayIR go_ir(last_good_stream_id, status, desc); | |
924 return CreateFramer(false)->SerializeFrame(go_ir); | |
925 } | |
926 | |
927 SpdyFrame* SpdyTestUtil::ConstructSpdyWindowUpdate( | |
928 const SpdyStreamId stream_id, uint32 delta_window_size) const { | |
929 SpdyWindowUpdateIR update_ir(stream_id, delta_window_size); | |
930 return CreateFramer(false)->SerializeFrame(update_ir); | |
931 } | |
932 | |
933 // TODO(jgraettinger): Eliminate uses of this method in tests (prefer | |
934 // SpdyRstStreamIR). | |
935 SpdyFrame* SpdyTestUtil::ConstructSpdyRstStream( | |
936 SpdyStreamId stream_id, | |
937 SpdyRstStreamStatus status) const { | |
938 SpdyRstStreamIR rst_ir(stream_id, status, ""); | |
939 return CreateFramer(false)->SerializeRstStream(rst_ir); | |
940 } | |
941 | |
942 SpdyFrame* SpdyTestUtil::ConstructSpdyGet( | |
943 const char* const url, | |
944 bool compressed, | |
945 SpdyStreamId stream_id, | |
946 RequestPriority request_priority) const { | |
947 scoped_ptr<SpdyHeaderBlock> block(ConstructGetHeaderBlock(url)); | |
948 return ConstructSpdySyn( | |
949 stream_id, *block, request_priority, compressed, true); | |
950 } | |
951 | |
952 SpdyFrame* SpdyTestUtil::ConstructSpdyGet(const char* const extra_headers[], | |
953 int extra_header_count, | |
954 bool compressed, | |
955 int stream_id, | |
956 RequestPriority request_priority, | |
957 bool direct) const { | |
958 SpdyHeaderBlock block; | |
959 block[GetMethodKey()] = "GET"; | |
960 block[GetPathKey()] = "/"; | |
961 block[GetHostKey()] = "www.google.com"; | |
962 block[GetSchemeKey()] = "http"; | |
963 MaybeAddVersionHeader(&block); | |
964 AppendToHeaderBlock(extra_headers, extra_header_count, &block); | |
965 return ConstructSpdySyn(stream_id, block, request_priority, compressed, true); | |
966 } | |
967 | |
968 SpdyFrame* SpdyTestUtil::ConstructSpdyConnect( | |
969 const char* const extra_headers[], | |
970 int extra_header_count, | |
971 int stream_id, | |
972 RequestPriority priority, | |
973 const HostPortPair& host_port_pair) const { | |
974 SpdyHeaderBlock block; | |
975 block[GetMethodKey()] = "CONNECT"; | |
976 block[GetPathKey()] = host_port_pair.ToString(); | |
977 block[GetHostKey()] = (host_port_pair.port() == 443) | |
978 ? host_port_pair.host() | |
979 : host_port_pair.ToString(); | |
980 MaybeAddVersionHeader(&block); | |
981 AppendToHeaderBlock(extra_headers, extra_header_count, &block); | |
982 return ConstructSpdySyn(stream_id, block, priority, false, false); | |
983 } | |
984 | |
985 SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[], | |
986 int extra_header_count, | |
987 int stream_id, | |
988 int associated_stream_id, | |
989 const char* url) { | |
990 if (spdy_version() < SPDY4) { | |
991 SpdySynStreamIR syn_stream(stream_id); | |
992 syn_stream.set_associated_to_stream_id(associated_stream_id); | |
993 syn_stream.SetHeader("hello", "bye"); | |
994 syn_stream.SetHeader(GetStatusKey(), "200 OK"); | |
995 syn_stream.SetHeader(GetVersionKey(), "HTTP/1.1"); | |
996 AddUrlToHeaderBlock(url, syn_stream.mutable_name_value_block()); | |
997 AppendToHeaderBlock(extra_headers, | |
998 extra_header_count, | |
999 syn_stream.mutable_name_value_block()); | |
1000 return CreateFramer(false)->SerializeFrame(syn_stream); | |
1001 } else { | |
1002 SpdyPushPromiseIR push_promise(associated_stream_id, stream_id); | |
1003 AddUrlToHeaderBlock(url, push_promise.mutable_name_value_block()); | |
1004 scoped_ptr<SpdyFrame> push_promise_frame( | |
1005 CreateFramer(false)->SerializeFrame(push_promise)); | |
1006 | |
1007 SpdyHeadersIR headers(stream_id); | |
1008 headers.SetHeader("hello", "bye"); | |
1009 headers.SetHeader(GetStatusKey(), "200 OK"); | |
1010 AppendToHeaderBlock( | |
1011 extra_headers, extra_header_count, headers.mutable_name_value_block()); | |
1012 scoped_ptr<SpdyFrame> headers_frame( | |
1013 CreateFramer(false)->SerializeFrame(headers)); | |
1014 | |
1015 int joint_data_size = push_promise_frame->size() + headers_frame->size(); | |
1016 scoped_ptr<char[]> data(new char[joint_data_size]); | |
1017 const SpdyFrame* frames[2] = { | |
1018 push_promise_frame.get(), headers_frame.get(), | |
1019 }; | |
1020 int combined_size = | |
1021 CombineFrames(frames, arraysize(frames), data.get(), joint_data_size); | |
1022 DCHECK_EQ(combined_size, joint_data_size); | |
1023 return new SpdyFrame(data.release(), joint_data_size, true); | |
1024 } | |
1025 } | |
1026 | |
1027 SpdyFrame* SpdyTestUtil::ConstructSpdyPush(const char* const extra_headers[], | |
1028 int extra_header_count, | |
1029 int stream_id, | |
1030 int associated_stream_id, | |
1031 const char* url, | |
1032 const char* status, | |
1033 const char* location) { | |
1034 if (spdy_version() < SPDY4) { | |
1035 SpdySynStreamIR syn_stream(stream_id); | |
1036 syn_stream.set_associated_to_stream_id(associated_stream_id); | |
1037 syn_stream.SetHeader("hello", "bye"); | |
1038 syn_stream.SetHeader(GetStatusKey(), status); | |
1039 syn_stream.SetHeader(GetVersionKey(), "HTTP/1.1"); | |
1040 syn_stream.SetHeader("location", location); | |
1041 AddUrlToHeaderBlock(url, syn_stream.mutable_name_value_block()); | |
1042 AppendToHeaderBlock(extra_headers, | |
1043 extra_header_count, | |
1044 syn_stream.mutable_name_value_block()); | |
1045 return CreateFramer(false)->SerializeFrame(syn_stream); | |
1046 } else { | |
1047 SpdyPushPromiseIR push_promise(associated_stream_id, stream_id); | |
1048 AddUrlToHeaderBlock(url, push_promise.mutable_name_value_block()); | |
1049 scoped_ptr<SpdyFrame> push_promise_frame( | |
1050 CreateFramer(false)->SerializeFrame(push_promise)); | |
1051 | |
1052 SpdyHeadersIR headers(stream_id); | |
1053 headers.SetHeader("hello", "bye"); | |
1054 headers.SetHeader(GetStatusKey(), status); | |
1055 headers.SetHeader("location", location); | |
1056 AppendToHeaderBlock( | |
1057 extra_headers, extra_header_count, headers.mutable_name_value_block()); | |
1058 scoped_ptr<SpdyFrame> headers_frame( | |
1059 CreateFramer(false)->SerializeFrame(headers)); | |
1060 | |
1061 int joint_data_size = push_promise_frame->size() + headers_frame->size(); | |
1062 scoped_ptr<char[]> data(new char[joint_data_size]); | |
1063 const SpdyFrame* frames[2] = { | |
1064 push_promise_frame.get(), headers_frame.get(), | |
1065 }; | |
1066 int combined_size = | |
1067 CombineFrames(frames, arraysize(frames), data.get(), joint_data_size); | |
1068 DCHECK_EQ(combined_size, joint_data_size); | |
1069 return new SpdyFrame(data.release(), joint_data_size, true); | |
1070 } | |
1071 } | |
1072 | |
1073 SpdyFrame* SpdyTestUtil::ConstructInitialSpdyPushFrame( | |
1074 scoped_ptr<SpdyHeaderBlock> headers, | |
1075 int stream_id, | |
1076 int associated_stream_id) { | |
1077 if (spdy_version() < SPDY4) { | |
1078 SpdySynStreamIR syn_stream(stream_id); | |
1079 syn_stream.set_associated_to_stream_id(associated_stream_id); | |
1080 SetPriority(LOWEST, &syn_stream); | |
1081 syn_stream.set_name_value_block(*headers); | |
1082 return CreateFramer(false)->SerializeFrame(syn_stream); | |
1083 } else { | |
1084 SpdyPushPromiseIR push_promise(associated_stream_id, stream_id); | |
1085 push_promise.set_name_value_block(*headers); | |
1086 return CreateFramer(false)->SerializeFrame(push_promise); | |
1087 } | |
1088 } | |
1089 | |
1090 SpdyFrame* SpdyTestUtil::ConstructSpdyPushHeaders( | |
1091 int stream_id, | |
1092 const char* const extra_headers[], | |
1093 int extra_header_count) { | |
1094 SpdyHeadersIR headers(stream_id); | |
1095 headers.SetHeader(GetStatusKey(), "200 OK"); | |
1096 MaybeAddVersionHeader(&headers); | |
1097 AppendToHeaderBlock(extra_headers, extra_header_count, | |
1098 headers.mutable_name_value_block()); | |
1099 return CreateFramer(false)->SerializeFrame(headers); | |
1100 } | |
1101 | |
1102 SpdyFrame* SpdyTestUtil::ConstructSpdySyn(int stream_id, | |
1103 const SpdyHeaderBlock& block, | |
1104 RequestPriority priority, | |
1105 bool compressed, | |
1106 bool fin) const { | |
1107 if (protocol_ < kProtoSPDY4MinimumVersion) { | |
1108 SpdySynStreamIR syn_stream(stream_id); | |
1109 syn_stream.set_name_value_block(block); | |
1110 syn_stream.set_priority( | |
1111 ConvertRequestPriorityToSpdyPriority(priority, spdy_version())); | |
1112 syn_stream.set_fin(fin); | |
1113 return CreateFramer(compressed)->SerializeFrame(syn_stream); | |
1114 } else { | |
1115 SpdyHeadersIR headers(stream_id); | |
1116 headers.set_name_value_block(block); | |
1117 headers.set_has_priority(true); | |
1118 headers.set_priority( | |
1119 ConvertRequestPriorityToSpdyPriority(priority, spdy_version())); | |
1120 headers.set_fin(fin); | |
1121 return CreateFramer(compressed)->SerializeFrame(headers); | |
1122 } | |
1123 } | |
1124 | |
1125 SpdyFrame* SpdyTestUtil::ConstructSpdyReply(int stream_id, | |
1126 const SpdyHeaderBlock& headers) { | |
1127 if (protocol_ < kProtoSPDY4MinimumVersion) { | |
1128 SpdySynReplyIR syn_reply(stream_id); | |
1129 syn_reply.set_name_value_block(headers); | |
1130 return CreateFramer(false)->SerializeFrame(syn_reply); | |
1131 } else { | |
1132 SpdyHeadersIR reply(stream_id); | |
1133 reply.set_name_value_block(headers); | |
1134 return CreateFramer(false)->SerializeFrame(reply); | |
1135 } | |
1136 } | |
1137 | |
1138 SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError( | |
1139 const char* const status, | |
1140 const char* const* const extra_headers, | |
1141 int extra_header_count, | |
1142 int stream_id) { | |
1143 SpdyHeaderBlock block; | |
1144 block["hello"] = "bye"; | |
1145 block[GetStatusKey()] = status; | |
1146 MaybeAddVersionHeader(&block); | |
1147 AppendToHeaderBlock(extra_headers, extra_header_count, &block); | |
1148 | |
1149 return ConstructSpdyReply(stream_id, block); | |
1150 } | |
1151 | |
1152 SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReplyRedirect(int stream_id) { | |
1153 static const char* const kExtraHeaders[] = { | |
1154 "location", "http://www.foo.com/index.php", | |
1155 }; | |
1156 return ConstructSpdySynReplyError("301 Moved Permanently", kExtraHeaders, | |
1157 arraysize(kExtraHeaders)/2, stream_id); | |
1158 } | |
1159 | |
1160 SpdyFrame* SpdyTestUtil::ConstructSpdySynReplyError(int stream_id) { | |
1161 return ConstructSpdySynReplyError("500 Internal Server Error", NULL, 0, 1); | |
1162 } | |
1163 | |
1164 SpdyFrame* SpdyTestUtil::ConstructSpdyGetSynReply( | |
1165 const char* const extra_headers[], | |
1166 int extra_header_count, | |
1167 int stream_id) { | |
1168 SpdyHeaderBlock block; | |
1169 block["hello"] = "bye"; | |
1170 block[GetStatusKey()] = "200"; | |
1171 MaybeAddVersionHeader(&block); | |
1172 AppendToHeaderBlock(extra_headers, extra_header_count, &block); | |
1173 | |
1174 return ConstructSpdyReply(stream_id, block); | |
1175 } | |
1176 | |
1177 SpdyFrame* SpdyTestUtil::ConstructSpdyPost(const char* url, | |
1178 SpdyStreamId stream_id, | |
1179 int64 content_length, | |
1180 RequestPriority priority, | |
1181 const char* const extra_headers[], | |
1182 int extra_header_count) { | |
1183 scoped_ptr<SpdyHeaderBlock> block( | |
1184 ConstructPostHeaderBlock(url, content_length)); | |
1185 AppendToHeaderBlock(extra_headers, extra_header_count, block.get()); | |
1186 return ConstructSpdySyn(stream_id, *block, priority, false, false); | |
1187 } | |
1188 | |
1189 SpdyFrame* SpdyTestUtil::ConstructChunkedSpdyPost( | |
1190 const char* const extra_headers[], | |
1191 int extra_header_count) { | |
1192 SpdyHeaderBlock block; | |
1193 block[GetMethodKey()] = "POST"; | |
1194 block[GetPathKey()] = "/"; | |
1195 block[GetHostKey()] = "www.google.com"; | |
1196 block[GetSchemeKey()] = "http"; | |
1197 MaybeAddVersionHeader(&block); | |
1198 AppendToHeaderBlock(extra_headers, extra_header_count, &block); | |
1199 return ConstructSpdySyn(1, block, LOWEST, false, false); | |
1200 } | |
1201 | |
1202 SpdyFrame* SpdyTestUtil::ConstructSpdyPostSynReply( | |
1203 const char* const extra_headers[], | |
1204 int extra_header_count) { | |
1205 // TODO(jgraettinger): Remove this method. | |
1206 return ConstructSpdyGetSynReply(NULL, 0, 1); | |
1207 } | |
1208 | |
1209 SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id, bool fin) { | |
1210 SpdyFramer framer(spdy_version_); | |
1211 SpdyDataIR data_ir(stream_id, | |
1212 base::StringPiece(kUploadData, kUploadDataSize)); | |
1213 data_ir.set_fin(fin); | |
1214 return framer.SerializeData(data_ir); | |
1215 } | |
1216 | |
1217 SpdyFrame* SpdyTestUtil::ConstructSpdyBodyFrame(int stream_id, | |
1218 const char* data, | |
1219 uint32 len, | |
1220 bool fin) { | |
1221 SpdyFramer framer(spdy_version_); | |
1222 SpdyDataIR data_ir(stream_id, base::StringPiece(data, len)); | |
1223 data_ir.set_fin(fin); | |
1224 return framer.SerializeData(data_ir); | |
1225 } | |
1226 | |
1227 SpdyFrame* SpdyTestUtil::ConstructWrappedSpdyFrame( | |
1228 const scoped_ptr<SpdyFrame>& frame, | |
1229 int stream_id) { | |
1230 return ConstructSpdyBodyFrame(stream_id, frame->data(), | |
1231 frame->size(), false); | |
1232 } | |
1233 | |
1234 const SpdyHeaderInfo SpdyTestUtil::MakeSpdyHeader(SpdyFrameType type) { | |
1235 const SpdyHeaderInfo kHeader = { | |
1236 type, | |
1237 1, // Stream ID | |
1238 0, // Associated stream ID | |
1239 ConvertRequestPriorityToSpdyPriority(LOWEST, spdy_version_), | |
1240 kSpdyCredentialSlotUnused, | |
1241 CONTROL_FLAG_FIN, // Control Flags | |
1242 false, // Compressed | |
1243 RST_STREAM_INVALID, | |
1244 NULL, // Data | |
1245 0, // Length | |
1246 DATA_FLAG_NONE | |
1247 }; | |
1248 return kHeader; | |
1249 } | |
1250 | |
1251 scoped_ptr<SpdyFramer> SpdyTestUtil::CreateFramer(bool compressed) const { | |
1252 scoped_ptr<SpdyFramer> framer(new SpdyFramer(spdy_version_)); | |
1253 framer->set_enable_compression(compressed); | |
1254 return framer.Pass(); | |
1255 } | |
1256 | |
1257 const char* SpdyTestUtil::GetMethodKey() const { | |
1258 return ":method"; | |
1259 } | |
1260 | |
1261 const char* SpdyTestUtil::GetStatusKey() const { | |
1262 return ":status"; | |
1263 } | |
1264 | |
1265 const char* SpdyTestUtil::GetHostKey() const { | |
1266 if (protocol_ < kProtoSPDY4MinimumVersion) | |
1267 return ":host"; | |
1268 else | |
1269 return ":authority"; | |
1270 } | |
1271 | |
1272 const char* SpdyTestUtil::GetSchemeKey() const { | |
1273 return ":scheme"; | |
1274 } | |
1275 | |
1276 const char* SpdyTestUtil::GetVersionKey() const { | |
1277 return ":version"; | |
1278 } | |
1279 | |
1280 const char* SpdyTestUtil::GetPathKey() const { | |
1281 return ":path"; | |
1282 } | |
1283 | |
1284 scoped_ptr<SpdyHeaderBlock> SpdyTestUtil::ConstructHeaderBlock( | |
1285 base::StringPiece method, | |
1286 base::StringPiece url, | |
1287 int64* content_length) const { | |
1288 std::string scheme, host, path; | |
1289 ParseUrl(url.data(), &scheme, &host, &path); | |
1290 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock()); | |
1291 (*headers)[GetMethodKey()] = method.as_string(); | |
1292 (*headers)[GetPathKey()] = path.c_str(); | |
1293 (*headers)[GetHostKey()] = host.c_str(); | |
1294 (*headers)[GetSchemeKey()] = scheme.c_str(); | |
1295 if (include_version_header()) { | |
1296 (*headers)[GetVersionKey()] = "HTTP/1.1"; | |
1297 } | |
1298 if (content_length) { | |
1299 std::string length_str = base::Int64ToString(*content_length); | |
1300 (*headers)["content-length"] = length_str; | |
1301 } | |
1302 return headers.Pass(); | |
1303 } | |
1304 | |
1305 void SpdyTestUtil::MaybeAddVersionHeader( | |
1306 SpdyFrameWithNameValueBlockIR* frame_ir) const { | |
1307 if (include_version_header()) { | |
1308 frame_ir->SetHeader(GetVersionKey(), "HTTP/1.1"); | |
1309 } | |
1310 } | |
1311 | |
1312 void SpdyTestUtil::MaybeAddVersionHeader(SpdyHeaderBlock* block) const { | |
1313 if (include_version_header()) { | |
1314 (*block)[GetVersionKey()] = "HTTP/1.1"; | |
1315 } | |
1316 } | |
1317 | |
1318 void SpdyTestUtil::SetPriority(RequestPriority priority, | |
1319 SpdySynStreamIR* ir) const { | |
1320 ir->set_priority(ConvertRequestPriorityToSpdyPriority( | |
1321 priority, spdy_version())); | |
1322 } | |
1323 | |
1324 } // namespace net | |
OLD | NEW |