1 // Copyright 2018 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 #ifndef SERVICES_NETWORK_TEST_TEST_URL_LOADER_FACTORY_H_
6 #define SERVICES_NETWORK_TEST_TEST_URL_LOADER_FACTORY_H_
7 
8 #include <map>
9 #include <string>
10 #include <vector>
11 
12 #include "base/macros.h"
13 #include "base/memory/ref_counted.h"
14 #include "mojo/public/cpp/bindings/pending_receiver.h"
15 #include "mojo/public/cpp/bindings/pending_remote.h"
16 #include "mojo/public/cpp/bindings/receiver_set.h"
17 #include "mojo/public/cpp/bindings/remote.h"
18 #include "net/http/http_status_code.h"
19 #include "services/network/public/cpp/resource_request.h"
20 #include "services/network/public/mojom/url_loader.mojom.h"
21 #include "services/network/public/mojom/url_loader_factory.mojom.h"
22 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
23 
24 namespace network {
25 class WeakWrapperSharedURLLoaderFactory;
26 
27 // A helper class to ease testing code that uses URLLoader interface. A test
28 // would pass this factory instead of the production factory to code, and
29 // would prime it with response data for arbitrary URLs.
30 class TestURLLoaderFactory : public mojom::URLLoaderFactory {
31  public:
32   struct PendingRequest {
33     PendingRequest();
34     ~PendingRequest();
35     PendingRequest(PendingRequest&& other);
36     PendingRequest& operator=(PendingRequest&& other);
37 
38     mojo::Remote<mojom::URLLoaderClient> client;
39     ResourceRequest request;
40     uint32_t options;
41   };
42 
43   // Bitfield that is used with |SimulateResponseForPendingRequest()| to
44   // control which request is selected.
45   enum ResponseMatchFlags : uint32_t {
46     kMatchDefault = 0x0,
47     kUrlMatchPrefix = 0x1,   // Whether URLs are a match if they start with the
48                              // URL passed in to
49                              // SimulateResponseForPendingRequest
50     kMostRecentMatch = 0x2,  // Start with the most recent requests.
51   };
52 
53   // Flags used with |AddResponse| to control how it produces a response.
54   enum ResponseProduceFlags : uint32_t {
55     kResponseDefault = 0,
56     kResponseOnlyRedirectsNoDestination = 0x1,
57     kSendHeadersOnNetworkError = 0x2,
58   };
59 
60   TestURLLoaderFactory();
61   ~TestURLLoaderFactory() override;
62 
63   using Redirects =
64       std::vector<std::pair<net::RedirectInfo, mojom::URLResponseHeadPtr>>;
65 
66   // Adds a response to be served. There is one unique response per URL, and if
67   // this method is called multiple times for the same URL the last response
68   // data is used.
69   // This can be called before or after a request is made. If it's called after,
70   // then pending requests will be "woken up".
71   void AddResponse(const GURL& url,
72                    mojom::URLResponseHeadPtr head,
73                    const std::string& content,
74                    const URLLoaderCompletionStatus& status,
75                    Redirects redirects = Redirects(),
76                    ResponseProduceFlags rp_flags = kResponseDefault);
77 
78   // Simpler version of above for the common case of success or error page.
79   void AddResponse(const std::string& url,
80                    const std::string& content,
81                    net::HttpStatusCode status = net::HTTP_OK);
82 
83   // Returns true if there is a request for a given URL with a living client
84   // that did not produce a response yet. If |request_out| is non-null,
85   // it will give a const pointer to the request.
86   // WARNING: This does RunUntilIdle() first.
87   bool IsPending(const std::string& url,
88                  const ResourceRequest** request_out = nullptr);
89 
90   // Returns the total # of pending requests.
91   // WARNING: This does RunUntilIdle() first.
92   int NumPending();
93 
94   // Clear all the responses that were previously set.
95   void ClearResponses();
96 
97   using Interceptor = base::RepeatingCallback<void(const ResourceRequest&)>;
98   void SetInterceptor(const Interceptor& interceptor);
99 
100   // Returns a mutable list of pending requests, for consumers that need direct
101   // access. It's recommended that consumers use AddResponse() rather than
102   // servicing requests themselves, whenever possible.
pending_requests()103   std::vector<PendingRequest>* pending_requests() { return &pending_requests_; }
104 
105   // Returns the PendingRequest instance available at the given index |index|
106   // or null if not existing.
107   PendingRequest* GetPendingRequest(size_t index);
108 
109   // Sends a response for the first (oldest) pending request with URL |url|.
110   // Returns false if no such pending request exists.
111   // |flags| can be used to change the default behavior:
112   // - if kUrlMatchPrefix is set, the pending request is a match if its URL
113   //   starts with |url| (instead of being equal to |url|).
114   // - if kMostRecentMatch is set, the most recent (instead of oldest) pending
115   //   request matching is used.
116   bool SimulateResponseForPendingRequest(
117       const GURL& url,
118       const network::URLLoaderCompletionStatus& completion_status,
119       mojom::URLResponseHeadPtr response_head,
120       const std::string& content,
121       ResponseMatchFlags flags = kMatchDefault);
122 
123   // Simpler version of above for the common case of success or error page.
124   bool SimulateResponseForPendingRequest(
125       const std::string& url,
126       const std::string& content,
127       net::HttpStatusCode status = net::HTTP_OK,
128       ResponseMatchFlags flags = kMatchDefault);
129 
130   // Sends a response for the given request |request|.
131   //
132   // Differently from its variant above, this method does not remove |request|
133   // from |pending_requests_|.
134   //
135   // This method is useful to process requests at a given pre-defined order.
136   void SimulateResponseWithoutRemovingFromPendingList(
137       PendingRequest* request,
138       mojom::URLResponseHeadPtr head,
139       std::string content,
140       const URLLoaderCompletionStatus& status);
141 
142   // Simpler version of the method above.
143   void SimulateResponseWithoutRemovingFromPendingList(PendingRequest* request,
144                                                       std::string content);
145 
146   // mojom::URLLoaderFactory implementation.
147   void CreateLoaderAndStart(mojo::PendingReceiver<mojom::URLLoader> receiver,
148                             int32_t routing_id,
149                             int32_t request_id,
150                             uint32_t options,
151                             const ResourceRequest& url_request,
152                             mojo::PendingRemote<mojom::URLLoaderClient> client,
153                             const net::MutableNetworkTrafficAnnotationTag&
154                                 traffic_annotation) override;
155   void Clone(mojo::PendingReceiver<mojom::URLLoaderFactory> receiver) override;
156 
157   // Returns a 'safe' ref-counted weak wrapper around this TestURLLoaderFactory
158   // instance.
159   //
160   // Because this is a weak wrapper, it is possible for the underlying
161   // TestURLLoaderFactory instance to be destroyed while other code still holds
162   // a reference to it.
163   //
164   // The weak wrapper returned by this method is guaranteed to have had
165   // Detach() called before this is destructed, so that any future calls become
166   // no-ops, rather than a crash.
167   scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
168   GetSafeWeakWrapper();
169 
170  private:
171   bool CreateLoaderAndStartInternal(const GURL& url,
172                                     mojom::URLLoaderClient* client);
173 
174   static void SimulateResponse(mojom::URLLoaderClient* client,
175                                Redirects redirects,
176                                mojom::URLResponseHeadPtr head,
177                                std::string content,
178                                URLLoaderCompletionStatus status,
179                                ResponseProduceFlags response_flags);
180 
181   struct Response {
182     Response();
183     ~Response();
184     Response(Response&&);
185     Response& operator=(Response&&);
186     GURL url;
187     Redirects redirects;
188     mojom::URLResponseHeadPtr head;
189     std::string content;
190     URLLoaderCompletionStatus status;
191     ResponseProduceFlags flags;
192   };
193   std::map<GURL, Response> responses_;
194 
195   std::vector<PendingRequest> pending_requests_;
196 
197   scoped_refptr<network::WeakWrapperSharedURLLoaderFactory> weak_wrapper_;
198 
199   Interceptor interceptor_;
200   mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_;
201 
202   DISALLOW_COPY_AND_ASSIGN(TestURLLoaderFactory);
203 };
204 
205 }  // namespace network
206 
207 #endif  // SERVICES_NETWORK_TEST_TEST_URL_LOADER_FACTORY_H_
208