Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(100)

Side by Side Diff: headless/public/util/generic_url_request_job_test.cc

Issue 2260793002: Headless utility classes for making deterministic protocol handlers (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressing comments Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "headless/public/util/generic_url_request_job.h"
6
7 #include <memory>
8 #include <string>
9 #include <vector>
10
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/values.h"
18 #include "headless/public/util/expedited_dispatcher.h"
19 #include "headless/public/util/testing/generic_url_request_mocks.h"
20 #include "headless/public/util/url_fetcher.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/url_request/url_request_job_factory_impl.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 std::ostream& operator<<(std::ostream& os, const base::Value& value) {
27 std::string json;
28 base::JSONWriter::WriteWithOptions(
29 value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
30 os << json;
31 return os;
32 }
33
34 MATCHER_P(MatchesJson, json, json.c_str()) {
35 std::unique_ptr<base::Value> expected(
36 base::JSONReader::Read(json, base::JSON_PARSE_RFC));
37 return arg.Equals(expected.get());
38 }
39
40 namespace headless {
41
42 namespace {
43
44 class MockFetcher : public URLFetcher {
45 public:
46 MockFetcher(base::DictionaryValue* fetch_request,
47 const std::string& json_reply)
48 : fetch_reply_(base::JSONReader::Read(json_reply, base::JSON_PARSE_RFC)),
49 fetch_request_(fetch_request) {
50 CHECK(fetch_reply_) << "Invalid json: " << json_reply;
51 }
52
53 ~MockFetcher() override {}
54
55 void StartFetch(const GURL& url,
56 const net::HttpRequestHeaders& request_headers,
57 ResultListener* result_listener) override {
58 // Record the request.
59 fetch_request_->SetString("url", url.spec());
60 std::unique_ptr<base::DictionaryValue> headers(new base::DictionaryValue);
61 for (net::HttpRequestHeaders::Iterator it(request_headers); it.GetNext();) {
62 headers->SetString(it.name(), it.value());
63 }
64 fetch_request_->Set("headers", std::move(headers));
65
66 // Return the canned response.
67 base::DictionaryValue* reply_dictionary;
68 ASSERT_TRUE(fetch_reply_->GetAsDictionary(&reply_dictionary));
69 std::string final_url;
70 ASSERT_TRUE(reply_dictionary->GetString("url", &final_url));
71 int http_response_code;
72 ASSERT_TRUE(reply_dictionary->GetInteger("http_response_code",
73 &http_response_code));
74 ASSERT_TRUE(reply_dictionary->GetString("data", &response_data_));
75 base::DictionaryValue* reply_headers_dictionary;
76 ASSERT_TRUE(
77 reply_dictionary->GetDictionary("headers", &reply_headers_dictionary));
78 scoped_refptr<net::HttpResponseHeaders> response_headers(
79 new net::HttpResponseHeaders(""));
80 for (base::DictionaryValue::Iterator it(*reply_headers_dictionary);
81 !it.IsAtEnd(); it.Advance()) {
82 std::string value;
83 ASSERT_TRUE(it.value().GetAsString(&value));
84 response_headers->AddHeader(
85 base::StringPrintf("%s: %s", it.key().c_str(), value.c_str()));
86 }
87 result_listener->OnFetchComplete(
88 GURL(final_url), http_response_code, std::move(response_headers),
89 response_data_.c_str(), response_data_.size());
90 }
91
92 private:
93 std::unique_ptr<base::Value> fetch_reply_;
94 base::DictionaryValue* fetch_request_; // NOT OWNED
95 std::string response_data_; // Here to ensure the required lifetime.
96 };
97
98 class MockProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
99 public:
100 // Details of the fetch will be stored in |fetch_request|.
101 // The fetch response will be created from parsing |json_fetch_reply_|.
102 MockProtocolHandler(base::DictionaryValue* fetch_request,
103 std::string* json_fetch_reply,
104 URLRequestDispatcher* dispatcher,
105 GenericURLRequestJob::Delegate* job_delegate)
106 : fetch_request_(fetch_request),
107 json_fetch_reply_(json_fetch_reply),
108 job_delegate_(job_delegate),
109 dispatcher_(dispatcher) {}
110
111 // net::URLRequestJobFactory::ProtocolHandler override.
112 net::URLRequestJob* MaybeCreateJob(
113 net::URLRequest* request,
114 net::NetworkDelegate* network_delegate) const override {
115 return new GenericURLRequestJob(
116 request, network_delegate, dispatcher_,
117 base::MakeUnique<MockFetcher>(fetch_request_, *json_fetch_reply_),
118 job_delegate_);
119 }
120
121 private:
122 base::DictionaryValue* fetch_request_; // NOT OWNED
123 std::string* json_fetch_reply_; // NOT OWNED
124 GenericURLRequestJob::Delegate* job_delegate_; // NOT OWNED
125 URLRequestDispatcher* dispatcher_; // NOT OWNED
126 };
127
128 } // namespace
129
130 class GenericURLRequestJobTest : public testing::Test {
131 public:
132 GenericURLRequestJobTest() : dispatcher_(message_loop_.task_runner()) {
133 url_request_job_factory_.SetProtocolHandler(
134 "https", base::WrapUnique(new MockProtocolHandler(
135 &fetch_request_, &json_fetch_reply_, &dispatcher_,
136 &job_delegate_)));
137 url_request_context_.set_job_factory(&url_request_job_factory_);
138 url_request_context_.set_cookie_store(&cookie_store_);
139 }
140
141 std::unique_ptr<net::URLRequest> CreateAndCompleteJob(
142 const GURL& url,
143 const std::string& json_reply) {
144 json_fetch_reply_ = json_reply;
145
146 std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
147 url, net::DEFAULT_PRIORITY, &request_delegate_));
148 request->Start();
149 message_loop_.RunUntilIdle();
150 return request;
151 }
152
153 protected:
154 base::MessageLoop message_loop_;
155 ExpeditedDispatcher dispatcher_;
156
157 net::URLRequestJobFactoryImpl url_request_job_factory_;
158 net::URLRequestContext url_request_context_;
159 MockCookieStore cookie_store_;
160
161 MockURLRequestDelegate request_delegate_;
162 base::DictionaryValue fetch_request_; // The request sent to MockFetcher.
163 std::string json_fetch_reply_; // The reply to be sent by MockFetcher.
164 MockGenericURLRequestJobDelegate job_delegate_;
165 };
166
167 TEST_F(GenericURLRequestJobTest, BasicRequestParams) {
168 // TODO(alexclarke): Lobby for raw string literals and use them here!
169 json_fetch_reply_ =
170 "{\"url\":\"https://example.com\","
171 " \"http_response_code\":200,"
172 " \"data\":\"Reply\","
173 " \"headers\":{\"Content-Type\":\"text/plain\"}}";
174
175 std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
176 GURL("https://example.com"), net::DEFAULT_PRIORITY, &request_delegate_));
177 request->SetReferrer("https://referrer.example.com");
178 request->SetExtraRequestHeaderByName("Extra-Header", "Value", true);
179 request->SetExtraRequestHeaderByName("User-Agent", "TestBrowser", true);
180 request->SetExtraRequestHeaderByName("Accept", "text/plain", true);
181 request->Start();
182 message_loop_.RunUntilIdle();
183
184 std::string expected_request_json =
185 "{\"url\": \"https://example.com/\","
186 " \"headers\": {"
187 " \"Accept\": \"text/plain\","
188 " \"Cookie\": \"\","
189 " \"Extra-Header\": \"Value\","
190 " \"Referer\": \"https://referrer.example.com/\","
191 " \"User-Agent\": \"TestBrowser\""
192 " }"
193 "}";
194
195 EXPECT_THAT(fetch_request_, MatchesJson(expected_request_json));
196 }
197
198 TEST_F(GenericURLRequestJobTest, BasicRequestProperties) {
199 std::string reply =
200 "{\"url\":\"https://example.com\","
201 " \"http_response_code\":200,"
202 " \"data\":\"Reply\","
203 " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
204
205 std::unique_ptr<net::URLRequest> request(
206 CreateAndCompleteJob(GURL("https://example.com"), reply));
207
208 EXPECT_EQ(200, request->GetResponseCode());
209
210 std::string mime_type;
211 request->GetMimeType(&mime_type);
212 EXPECT_EQ("text/html", mime_type);
213
214 std::string charset;
215 request->GetCharset(&charset);
216 EXPECT_EQ("utf-8", charset);
217
218 std::string content_type;
219 EXPECT_TRUE(request->response_info().headers->GetNormalizedHeader(
220 "Content-Type", &content_type));
221 EXPECT_EQ("text/html; charset=UTF-8", content_type);
222 }
223
224 TEST_F(GenericURLRequestJobTest, BasicRequestContents) {
225 std::string reply =
226 "{\"url\":\"https://example.com\","
227 " \"http_response_code\":200,"
228 " \"data\":\"Reply\","
229 " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
230
231 std::unique_ptr<net::URLRequest> request(
232 CreateAndCompleteJob(GURL("https://example.com"), reply));
233
234 const int kBufferSize = 256;
235 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
236 int bytes_read;
237 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
238 EXPECT_EQ(5, bytes_read);
239 EXPECT_EQ("Reply", std::string(buffer->data(), 5));
240 }
241
242 TEST_F(GenericURLRequestJobTest, ReadInParts) {
243 std::string reply =
244 "{\"url\":\"https://example.com\","
245 " \"http_response_code\":200,"
246 " \"data\":\"Reply\","
247 " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
248
249 std::unique_ptr<net::URLRequest> request(
250 CreateAndCompleteJob(GURL("https://example.com"), reply));
251
252 const int kBufferSize = 3;
253 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
254 int bytes_read;
255 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
256 EXPECT_EQ(3, bytes_read);
257 EXPECT_EQ("Rep", std::string(buffer->data(), bytes_read));
258
259 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
260 EXPECT_EQ(2, bytes_read);
261 EXPECT_EQ("ly", std::string(buffer->data(), bytes_read));
262
263 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
264 EXPECT_EQ(0, bytes_read);
265 }
266
267 TEST_F(GenericURLRequestJobTest, RequestWithCookies) {
268 net::CookieList* cookies = cookie_store_.cookies();
269
270 // Basic matching cookie.
271 cookies->push_back(*net::CanonicalCookie::Create(
272 GURL("https://example.com"), "basic_cookie", "1", "example.com", "/",
273 base::Time(), base::Time(),
274 /* secure */ false,
275 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
276 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
277
278 // Matching secure cookie.
279 cookies->push_back(*net::CanonicalCookie::Create(
280 GURL("https://example.com"), "secure_cookie", "2", "example.com", "/",
281 base::Time(), base::Time(),
282 /* secure */ true,
283 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
284 /* enforce_strict_secure */ true, net::COOKIE_PRIORITY_DEFAULT));
285
286 // Matching http-only cookie.
287 cookies->push_back(*net::CanonicalCookie::Create(
288 GURL("https://example.com"), "http_only_cookie", "3", "example.com", "/",
289 base::Time(), base::Time(),
290 /* secure */ false,
291 /* http_only */ true, net::CookieSameSite::NO_RESTRICTION,
292 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
293
294 // Matching cookie with path.
295 cookies->push_back(*net::CanonicalCookie::Create(
296 GURL("https://example.com"), "cookie_with_path", "4", "example.com",
297 "/widgets", base::Time(), base::Time(),
298 /* secure */ false,
299 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
300 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
301
302 // Matching cookie with subdomain.
303 cookies->push_back(*net::CanonicalCookie::Create(
304 GURL("https://cdn.example.com"), "bad_subdomain_cookie", "5",
305 "cdn.example.com", "/", base::Time(), base::Time(),
306 /* secure */ false,
307 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
308 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
309
310 // Non-matching cookie (different site).
311 cookies->push_back(*net::CanonicalCookie::Create(
312 GURL("https://zombo.com"), "bad_site_cookie", "6", "zombo.com", "/",
313 base::Time(), base::Time(),
314 /* secure */ false,
315 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
316 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
317
318 // Non-matching cookie (different path).
319 cookies->push_back(*net::CanonicalCookie::Create(
320 GURL("https://example.com"), "bad_path_cookie", "7", "example.com",
321 "/gadgets", base::Time(), base::Time(),
322 /* secure */ false,
323 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
324 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
325
326 std::string reply =
327 "{\"url\":\"https://example.com\","
328 " \"http_response_code\":200,"
329 " \"data\":\"Reply\","
330 " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
331
332 std::unique_ptr<net::URLRequest> request(
333 CreateAndCompleteJob(GURL("https://example.com"), reply));
334
335 std::string expected_request_json =
336 "{\"url\": \"https://example.com/\","
337 " \"headers\": {"
338 " \"Cookie\": \"basic_cookie=1; secure_cookie=2; http_only_cookie=3\","
339 " \"Referer\": \"\""
340 " }"
341 "}";
342
343 EXPECT_THAT(fetch_request_, MatchesJson(expected_request_json));
344 }
345
346 } // namespace headless
OLDNEW
« no previous file with comments | « headless/public/util/generic_url_request_job.cc ('k') | headless/public/util/managed_dispatch_url_request_job.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698