summaryrefslogtreecommitdiff
path: root/chromium/net/spdy/spdy_test_util_common.h
blob: fe64b69334427bd42cb202d8bae0964d2f23a97b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_SPDY_SPDY_TEST_UTIL_COMMON_H_
#define NET_SPDY_SPDY_TEST_UTIL_COMMON_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string_piece.h"
#include "crypto/ec_private_key.h"
#include "net/base/completion_once_callback.h"
#include "net/base/proxy_server.h"
#include "net/base/request_priority.h"
#include "net/base/test_completion_callback.h"
#include "net/cert/cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_network_session.h"
#include "net/http/http_response_info.h"
#include "net/http/http_server_properties.h"
#include "net/http/transport_security_state.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(ENABLE_REPORTING)
#include "net/network_error_logging/network_error_logging_service.h"
#include "net/reporting/reporting_service.h"
#endif

class GURL;

namespace net {

class CTPolicyEnforcer;
class ClientSocketFactory;
class HashValue;
class HostPortPair;
class HostResolver;
class QuicContext;
class HttpUserAgentSettings;
class NetLogWithSource;
class SpdySessionKey;
class SpdyStream;
class SpdyStreamRequest;
class TransportSecurityState;
class URLRequestContextBuilder;

// Default upload data used by both, mock objects and framer when creating
// data frames.
const char kDefaultUrl[] = "https://www.example.org/";
const char kUploadData[] = "hello!";
const int kUploadDataSize = std::size(kUploadData) - 1;

// While HTTP/2 protocol defines default SETTINGS_MAX_HEADER_LIST_SIZE_FOR_TEST
// to be unlimited, BufferedSpdyFramer constructor requires a value.
const uint32_t kMaxHeaderListSizeForTest = 1024;

// Chop a spdy::SpdySerializedFrame into an array of MockWrites.
// |frame| is the frame to chop.
// |num_chunks| is the number of chunks to create.
std::unique_ptr<MockWrite[]> ChopWriteFrame(
    const spdy::SpdySerializedFrame& frame,
    int num_chunks);

// Adds headers and values to a map.
// |extra_headers| is an array of { name, value } pairs, arranged as strings
// where the even entries are the header names, and the odd entries are the
// header values.
// |headers| gets filled in from |extra_headers|.
void AppendToHeaderBlock(const char* const extra_headers[],
                         int extra_header_count,
                         spdy::Http2HeaderBlock* headers);

// Create an async MockWrite from the given spdy::SpdySerializedFrame.
MockWrite CreateMockWrite(const spdy::SpdySerializedFrame& req);

// Create an async MockWrite from the given spdy::SpdySerializedFrame and
// sequence number.
MockWrite CreateMockWrite(const spdy::SpdySerializedFrame& req, int seq);

MockWrite CreateMockWrite(const spdy::SpdySerializedFrame& req,
                          int seq,
                          IoMode mode);

// Create a MockRead from the given spdy::SpdySerializedFrame.
MockRead CreateMockRead(const spdy::SpdySerializedFrame& resp);

// Create a MockRead from the given spdy::SpdySerializedFrame and sequence
// number.
MockRead CreateMockRead(const spdy::SpdySerializedFrame& resp, int seq);

MockRead CreateMockRead(const spdy::SpdySerializedFrame& resp,
                        int seq,
                        IoMode mode);

// Combines the given vector of spdy::SpdySerializedFrame into a single frame.
spdy::SpdySerializedFrame CombineFrames(
    std::vector<const spdy::SpdySerializedFrame*> frames);

// Returns the spdy::SpdyPriority embedded in the given frame.  Returns true
// and fills in |priority| on success.
bool GetSpdyPriority(const spdy::SpdySerializedFrame& frame,
                     spdy::SpdyPriority* priority);

// Tries to create a stream in |session| synchronously. Returns NULL
// on failure.
base::WeakPtr<SpdyStream> CreateStreamSynchronously(
    SpdyStreamType type,
    const base::WeakPtr<SpdySession>& session,
    const GURL& url,
    RequestPriority priority,
    const NetLogWithSource& net_log,
    bool detect_broken_connection = false,
    base::TimeDelta heartbeat_interval = base::Seconds(0));

// Helper class used by some tests to release a stream as soon as it's
// created.
class StreamReleaserCallback : public TestCompletionCallbackBase {
 public:
  StreamReleaserCallback();

  ~StreamReleaserCallback() override;

  // Returns a callback that releases |request|'s stream.
  CompletionOnceCallback MakeCallback(SpdyStreamRequest* request);

 private:
  void OnComplete(SpdyStreamRequest* request, int result);
};

// Helper to manage the lifetimes of the dependencies for a
// HttpNetworkTransaction.
struct SpdySessionDependencies {
  // Default set of dependencies -- "null" proxy service.
  SpdySessionDependencies();

  // Custom proxy service dependency.
  explicit SpdySessionDependencies(
      std::unique_ptr<ProxyResolutionService> proxy_resolution_service);

  SpdySessionDependencies(SpdySessionDependencies&&);

  ~SpdySessionDependencies();

  SpdySessionDependencies& operator=(SpdySessionDependencies&&);

  HostResolver* GetHostResolver() {
    return alternate_host_resolver ? alternate_host_resolver.get()
                                   : host_resolver.get();
  }

  static std::unique_ptr<HttpNetworkSession> SpdyCreateSession(
      SpdySessionDependencies* session_deps);

  // Variant that ignores session_deps->socket_factory, and uses the passed in
  // |factory| instead.
  static std::unique_ptr<HttpNetworkSession> SpdyCreateSessionWithSocketFactory(
      SpdySessionDependencies* session_deps,
      ClientSocketFactory* factory);
  static HttpNetworkSessionParams CreateSessionParams(
      SpdySessionDependencies* session_deps);
  static HttpNetworkSessionContext CreateSessionContext(
      SpdySessionDependencies* session_deps);

  // NOTE: host_resolver must be ordered before http_auth_handler_factory.
  std::unique_ptr<MockHostResolverBase> host_resolver;
  // For using a HostResolver not derived from MockHostResolverBase.
  std::unique_ptr<HostResolver> alternate_host_resolver;
  std::unique_ptr<CertVerifier> cert_verifier;
  std::unique_ptr<TransportSecurityState> transport_security_state;
  std::unique_ptr<CTPolicyEnforcer> ct_policy_enforcer;
  std::unique_ptr<ProxyResolutionService> proxy_resolution_service;
  std::unique_ptr<HttpUserAgentSettings> http_user_agent_settings;
  std::unique_ptr<SSLConfigService> ssl_config_service;
  std::unique_ptr<MockClientSocketFactory> socket_factory;
  std::unique_ptr<HttpAuthHandlerFactory> http_auth_handler_factory;
  std::unique_ptr<HttpServerProperties> http_server_properties;
  std::unique_ptr<QuicContext> quic_context;
#if BUILDFLAG(ENABLE_REPORTING)
  std::unique_ptr<ReportingService> reporting_service;
  std::unique_ptr<NetworkErrorLoggingService> network_error_logging_service;
#endif
  bool enable_ip_pooling = true;
  bool enable_ping = false;
  bool enable_user_alternate_protocol_ports = false;
  bool enable_quic = false;
  bool enable_server_push_cancellation = false;
  size_t session_max_recv_window_size = kDefaultInitialWindowSize;
  int session_max_queued_capped_frames = kSpdySessionMaxQueuedCappedFrames;
  spdy::SettingsMap http2_settings;
  SpdySession::TimeFunc time_func;
  bool enable_http2_alternative_service = false;
  bool enable_http2_settings_grease = false;
  absl::optional<SpdySessionPool::GreasedHttp2Frame> greased_http2_frame;
  bool http2_end_stream_with_data_frame = false;
  raw_ptr<NetLog> net_log = nullptr;
  bool disable_idle_sockets_close_on_memory_pressure = false;
  bool enable_early_data = false;
  bool key_auth_cache_server_entries_by_network_anonymization_key = false;
  bool enable_priority_update = false;
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
  bool go_away_on_ip_change = true;
#else
  bool go_away_on_ip_change = false;
#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
  bool ignore_ip_address_changes = false;
};

std::unique_ptr<URLRequestContextBuilder>
CreateSpdyTestURLRequestContextBuilder(
    ClientSocketFactory* client_socket_factory);

// Equivalent to pool->GetIfExists(spdy_session_key, NetLogWithSource()) !=
// NULL.
bool HasSpdySession(SpdySessionPool* pool, const SpdySessionKey& key);

// Creates a SPDY session for the given key and puts it in the SPDY
// session pool in |http_session|. A SPDY session for |key| must not
// already exist.
base::WeakPtr<SpdySession> CreateSpdySession(HttpNetworkSession* http_session,
                                             const SpdySessionKey& key,
                                             const NetLogWithSource& net_log);

// Like CreateSpdySession(), but does not fail if there is already an IP
// pooled session for |key|.
base::WeakPtr<SpdySession> CreateSpdySessionWithIpBasedPoolingDisabled(
    HttpNetworkSession* http_session,
    const SpdySessionKey& key,
    const NetLogWithSource& net_log);

// Creates a SPDY session for the given key and puts it in |pool|.
// The returned session will neither receive nor send any data.
// A SPDY session for |key| must not already exist.
base::WeakPtr<SpdySession> CreateFakeSpdySession(SpdySessionPool* pool,
                                                 const SpdySessionKey& key);

class SpdySessionPoolPeer {
 public:
  explicit SpdySessionPoolPeer(SpdySessionPool* pool);

  SpdySessionPoolPeer(const SpdySessionPoolPeer&) = delete;
  SpdySessionPoolPeer& operator=(const SpdySessionPoolPeer&) = delete;

  void RemoveAliases(const SpdySessionKey& key);
  void SetEnableSendingInitialData(bool enabled);

 private:
  const raw_ptr<SpdySessionPool> pool_;
};

class SpdyTestUtil {
 public:
  SpdyTestUtil();
  ~SpdyTestUtil();

  // Add the appropriate headers to put |url| into |block|.
  void AddUrlToHeaderBlock(base::StringPiece url,
                           spdy::Http2HeaderBlock* headers) const;

  static spdy::Http2HeaderBlock ConstructGetHeaderBlock(base::StringPiece url);
  static spdy::Http2HeaderBlock ConstructGetHeaderBlockForProxy(
      base::StringPiece url);
  static spdy::Http2HeaderBlock ConstructHeadHeaderBlock(
      base::StringPiece url,
      int64_t content_length);
  static spdy::Http2HeaderBlock ConstructPostHeaderBlock(
      base::StringPiece url,
      int64_t content_length);
  static spdy::Http2HeaderBlock ConstructPutHeaderBlock(base::StringPiece url,
                                                        int64_t content_length);

  // Construct an expected SPDY reply string from the given headers.
  std::string ConstructSpdyReplyString(
      const spdy::Http2HeaderBlock& headers) const;

  // Construct an expected SPDY SETTINGS frame.
  // |settings| are the settings to set.
  // Returns the constructed frame.
  spdy::SpdySerializedFrame ConstructSpdySettings(
      const spdy::SettingsMap& settings);

  // Constructs an expected SPDY SETTINGS acknowledgement frame.
  spdy::SpdySerializedFrame ConstructSpdySettingsAck();

  // Construct a SPDY PING frame.  Returns the constructed frame.
  spdy::SpdySerializedFrame ConstructSpdyPing(uint32_t ping_id, bool is_ack);

  // Construct a SPDY GOAWAY frame with the specified last_good_stream_id.
  // Returns the constructed frame.
  spdy::SpdySerializedFrame ConstructSpdyGoAway(
      spdy::SpdyStreamId last_good_stream_id);

  // Construct a SPDY GOAWAY frame with the specified last_good_stream_id,
  // status, and description. Returns the constructed frame.
  spdy::SpdySerializedFrame ConstructSpdyGoAway(
      spdy::SpdyStreamId last_good_stream_id,
      spdy::SpdyErrorCode error_code,
      const std::string& desc);

  // Construct a SPDY WINDOW_UPDATE frame.  Returns the constructed frame.
  spdy::SpdySerializedFrame ConstructSpdyWindowUpdate(
      spdy::SpdyStreamId stream_id,
      uint32_t delta_window_size);

  // Construct a SPDY RST_STREAM frame.  Returns the constructed frame.
  spdy::SpdySerializedFrame ConstructSpdyRstStream(
      spdy::SpdyStreamId stream_id,
      spdy::SpdyErrorCode error_code);

  // Construct a PRIORITY frame. The weight is derived from |request_priority|.
  // Returns the constructed frame.
  spdy::SpdySerializedFrame ConstructSpdyPriority(
      spdy::SpdyStreamId stream_id,
      spdy::SpdyStreamId parent_stream_id,
      RequestPriority request_priority,
      bool exclusive);

  // Constructs a standard SPDY GET HEADERS frame for |url| with header
  // compression.
  // |extra_headers| are the extra header-value pairs, which typically
  // will vary the most between calls.
  spdy::SpdySerializedFrame ConstructSpdyGet(const char* const url,
                                             spdy::SpdyStreamId stream_id,
                                             RequestPriority request_priority);

  // Constructs a standard SPDY GET HEADERS frame with header compression.
  // |extra_headers| are the extra header-value pairs, which typically
  // will vary the most between calls.  If |direct| is false, the
  // the full url will be used instead of simply the path.
  spdy::SpdySerializedFrame ConstructSpdyGet(const char* const extra_headers[],
                                             int extra_header_count,
                                             int stream_id,
                                             RequestPriority request_priority);

  // Constructs a SPDY HEADERS frame for a CONNECT request.
  spdy::SpdySerializedFrame ConstructSpdyConnect(
      const char* const extra_headers[],
      int extra_header_count,
      int stream_id,
      RequestPriority priority,
      const HostPortPair& host_port_pair);

  // Constructs a PUSH_PROMISE frame and a HEADERS frame on the pushed stream.
  // |extra_headers| are the extra header-value pairs, which typically
  // will vary the most between calls.
  // Returns a spdy::SpdySerializedFrame object with the two frames
  // concatenated.
  spdy::SpdySerializedFrame ConstructSpdyPush(const char* const extra_headers[],
                                              int extra_header_count,
                                              int stream_id,
                                              int associated_stream_id,
                                              const char* url);
  spdy::SpdySerializedFrame ConstructSpdyPush(const char* const extra_headers[],
                                              int extra_header_count,
                                              int stream_id,
                                              int associated_stream_id,
                                              const char* url,
                                              const char* status,
                                              const char* location);

  // Constructs a PUSH_PROMISE frame.
  spdy::SpdySerializedFrame ConstructSpdyPushPromise(
      spdy::SpdyStreamId associated_stream_id,
      spdy::SpdyStreamId stream_id,
      spdy::Http2HeaderBlock headers);

  spdy::SpdySerializedFrame ConstructSpdyPushHeaders(
      int stream_id,
      const char* const extra_headers[],
      int extra_header_count);

  // Constructs a HEADERS frame with the request header compression context with
  // END_STREAM flag set to |fin|.
  spdy::SpdySerializedFrame ConstructSpdyResponseHeaders(
      int stream_id,
      spdy::Http2HeaderBlock headers,
      bool fin);

  // Construct a HEADERS frame carrying exactly the given headers and priority.
  spdy::SpdySerializedFrame ConstructSpdyHeaders(int stream_id,
                                                 spdy::Http2HeaderBlock headers,
                                                 RequestPriority priority,
                                                 bool fin);

  // Construct a reply HEADERS frame carrying exactly the given headers and the
  // default priority.
  spdy::SpdySerializedFrame ConstructSpdyReply(int stream_id,
                                               spdy::Http2HeaderBlock headers);

  // Constructs a standard SPDY HEADERS frame to match the SPDY GET.
  // |extra_headers| are the extra header-value pairs, which typically
  // will vary the most between calls.
  spdy::SpdySerializedFrame ConstructSpdyGetReply(
      const char* const extra_headers[],
      int extra_header_count,
      int stream_id);

  // Constructs a standard SPDY HEADERS frame with an Internal Server
  // Error status code.
  spdy::SpdySerializedFrame ConstructSpdyReplyError(int stream_id);

  // Constructs a standard SPDY HEADERS frame with the specified status code.
  spdy::SpdySerializedFrame ConstructSpdyReplyError(
      const char* const status,
      const char* const* const extra_headers,
      int extra_header_count,
      int stream_id);

  // Constructs a standard SPDY POST HEADERS frame.
  // |extra_headers| are the extra header-value pairs, which typically
  // will vary the most between calls.
  spdy::SpdySerializedFrame ConstructSpdyPost(const char* url,
                                              spdy::SpdyStreamId stream_id,
                                              int64_t content_length,
                                              RequestPriority priority,
                                              const char* const extra_headers[],
                                              int extra_header_count);

  // Constructs a chunked transfer SPDY POST HEADERS frame.
  // |extra_headers| are the extra header-value pairs, which typically
  // will vary the most between calls.
  spdy::SpdySerializedFrame ConstructChunkedSpdyPost(
      const char* const extra_headers[],
      int extra_header_count);

  // Constructs a standard SPDY HEADERS frame to match the SPDY POST.
  // |extra_headers| are the extra header-value pairs, which typically
  // will vary the most between calls.
  spdy::SpdySerializedFrame ConstructSpdyPostReply(
      const char* const extra_headers[],
      int extra_header_count);

  // Constructs a single SPDY data frame with the contents "hello!"
  spdy::SpdySerializedFrame ConstructSpdyDataFrame(int stream_id, bool fin);

  // Constructs a single SPDY data frame with the given content.
  spdy::SpdySerializedFrame ConstructSpdyDataFrame(int stream_id,
                                                   base::StringPiece data,
                                                   bool fin);

  // Constructs a single SPDY data frame with the given content and padding.
  spdy::SpdySerializedFrame ConstructSpdyDataFrame(int stream_id,
                                                   base::StringPiece data,
                                                   bool fin,
                                                   int padding_length);

  // Wraps |frame| in the payload of a data frame in stream |stream_id|.
  spdy::SpdySerializedFrame ConstructWrappedSpdyFrame(
      const spdy::SpdySerializedFrame& frame,
      int stream_id);

  // Serialize a spdy::SpdyFrameIR with |headerless_spdy_framer_|.
  spdy::SpdySerializedFrame SerializeFrame(const spdy::SpdyFrameIR& frame_ir);

  // Called when necessary (when it will affect stream dependency specification
  // when setting dependencies based on priorioties) to notify the utility
  // class of stream destruction.
  void UpdateWithStreamDestruction(int stream_id);

  void set_default_url(const GURL& url) { default_url_ = url; }

 private:
  // |content_length| may be NULL, in which case the content-length
  // header will be omitted.
  static spdy::Http2HeaderBlock ConstructHeaderBlock(base::StringPiece method,
                                                     base::StringPiece url,
                                                     int64_t* content_length);

  // Multiple SpdyFramers are required to keep track of header compression
  // state.
  // Use to serialize frames (request or response) without headers.
  spdy::SpdyFramer headerless_spdy_framer_;
  // Use to serialize request frames with headers.
  spdy::SpdyFramer request_spdy_framer_;
  // Use to serialize response frames with headers.
  spdy::SpdyFramer response_spdy_framer_;

  GURL default_url_;

  // Track a FIFO list of the stream_id of all created requests by priority.
  std::map<int, std::vector<int>> priority_to_stream_id_list_;
};

namespace test {

// Returns a SHA1 HashValue in which each byte has the value |label|.
HashValue GetTestHashValue(uint8_t label);

// A test implementation of ServerPushDelegate that caches all the pushed
// request and provides a interface to cancel the push given url.
class TestServerPushDelegate : public ServerPushDelegate {
 public:
  TestServerPushDelegate();
  ~TestServerPushDelegate() override;

  void OnPush(std::unique_ptr<ServerPushHelper> push_helper,
              const NetLogWithSource& session_net_log) override;

  bool CancelPush(GURL url);

 private:
  std::map<GURL, std::unique_ptr<ServerPushHelper>> push_helpers;
};

}  // namespace test
}  // namespace net

#endif  // NET_SPDY_SPDY_TEST_UTIL_COMMON_H_