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

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: Add missing files 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 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 json_fetch_reply_ =
169 R"({"url":"https://example.com",
170 "http_response_code":200,
171 "data":"Reply",
172 "headers":{"Content-Type":"text/plain"}})";
173
174 std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
175 GURL("https://example.com"), net::DEFAULT_PRIORITY, &request_delegate_));
176 request->SetReferrer("https://referrer.example.com");
177 request->SetExtraRequestHeaderByName("Extra-Header", "Value", true);
178 request->SetExtraRequestHeaderByName("User-Agent", "TestBrowser", true);
179 request->SetExtraRequestHeaderByName("Accept", "text/plain", true);
180 request->Start();
181 message_loop_.RunUntilIdle();
182
183 std::string expected_request_json =
184 R"({"url": "https://example.com/",
Sami 2016/08/19 11:54:00 Raw string literals are still banned :( Maybe we s
alex clarke (OOO till 29th) 2016/08/19 12:48:58 Rats, OK I'll reformat.
185 "headers": {
186 "Accept": "text/plain",
187 "Cookie": "",
188 "Extra-Header": "Value",
189 "Referer": "https://referrer.example.com/",
190 "User-Agent": "TestBrowser"
191 }
192 })";
193
194 EXPECT_THAT(fetch_request_, MatchesJson(expected_request_json));
195 }
196
197 TEST_F(GenericURLRequestJobTest, BasicRequestProperties) {
198 std::string reply =
199 R"({"url":"https://example.com",
200 "http_response_code":200,
201 "data":"Reply",
202 "headers":{"Content-Type":"text/html; charset=UTF-8"}})";
203
204 std::unique_ptr<net::URLRequest> request(
205 CreateAndCompleteJob(GURL("https://example.com"), reply));
206
207 EXPECT_EQ(200, request->GetResponseCode());
208
209 std::string mime_type;
210 request->GetMimeType(&mime_type);
211 EXPECT_EQ("text/html", mime_type);
212
213 std::string charset;
214 request->GetCharset(&charset);
215 EXPECT_EQ("utf-8", charset);
216
217 std::string content_type;
218 EXPECT_TRUE(request->response_info().headers->GetNormalizedHeader(
219 "Content-Type", &content_type));
220 EXPECT_EQ("text/html; charset=UTF-8", content_type);
221 }
222
223 TEST_F(GenericURLRequestJobTest, BasicRequestContents) {
224 std::string reply =
225 R"({"url":"https://example.com",
226 "http_response_code":200,
227 "data":"Reply",
228 "headers":{"Content-Type":"text/html; charset=UTF-8"}})";
229
230 std::unique_ptr<net::URLRequest> request(
231 CreateAndCompleteJob(GURL("https://example.com"), reply));
232
233 const int kBufferSize = 256;
234 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
235 int bytes_read;
236 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
237 EXPECT_EQ(5, bytes_read);
238 EXPECT_EQ("Reply", std::string(buffer->data(), 5));
239 }
240
241 TEST_F(GenericURLRequestJobTest, ReadInParts) {
242 std::string reply =
243 R"({"url":"https://example.com",
244 "http_response_code":200,
245 "data":"Reply",
246 "headers":{"Content-Type":"text/html; charset=UTF-8"}})";
247
248 std::unique_ptr<net::URLRequest> request(
249 CreateAndCompleteJob(GURL("https://example.com"), reply));
250
251 const int kBufferSize = 3;
252 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
253 int bytes_read;
254 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
255 EXPECT_EQ(3, bytes_read);
256 EXPECT_EQ("Rep", std::string(buffer->data(), bytes_read));
257
258 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
259 EXPECT_EQ(2, bytes_read);
260 EXPECT_EQ("ly", std::string(buffer->data(), bytes_read));
261
262 EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
263 EXPECT_EQ(0, bytes_read);
264 }
265
266 TEST_F(GenericURLRequestJobTest, RequestWithCookies) {
267 net::CookieList* cookies = cookie_store_.cookies();
268
269 // Basic matching cookie.
270 cookies->push_back(*net::CanonicalCookie::Create(
271 GURL("https://example.com"), "basic_cookie", "1", "example.com", "/",
272 base::Time(), base::Time(),
273 /* secure */ false,
274 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
275 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
276
277 // Matching secure cookie.
278 cookies->push_back(*net::CanonicalCookie::Create(
279 GURL("https://example.com"), "secure_cookie", "2", "example.com", "/",
280 base::Time(), base::Time(),
281 /* secure */ true,
282 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
283 /* enforce_strict_secure */ true, net::COOKIE_PRIORITY_DEFAULT));
284
285 // Matching http-only cookie.
286 cookies->push_back(*net::CanonicalCookie::Create(
287 GURL("https://example.com"), "http_only_cookie", "3", "example.com", "/",
288 base::Time(), base::Time(),
289 /* secure */ false,
290 /* http_only */ true, net::CookieSameSite::NO_RESTRICTION,
291 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
292
293 // Matching cookie with path.
294 cookies->push_back(*net::CanonicalCookie::Create(
295 GURL("https://example.com"), "cookie_with_path", "4", "example.com",
296 "/widgets", base::Time(), base::Time(),
297 /* secure */ false,
298 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
299 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
300
301 // Matching cookie with subdomain.
302 cookies->push_back(*net::CanonicalCookie::Create(
303 GURL("https://cdn.example.com"), "bad_subdomain_cookie", "5",
304 "cdn.example.com", "/", base::Time(), base::Time(),
305 /* secure */ false,
306 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
307 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
308
309 // Non-matching cookie (different site).
310 cookies->push_back(*net::CanonicalCookie::Create(
311 GURL("https://zombo.com"), "bad_site_cookie", "6", "zombo.com", "/",
312 base::Time(), base::Time(),
313 /* secure */ false,
314 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
315 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
316
317 // Non-matching cookie (different path).
318 cookies->push_back(*net::CanonicalCookie::Create(
319 GURL("https://example.com"), "bad_path_cookie", "7", "example.com",
320 "/gadgets", base::Time(), base::Time(),
321 /* secure */ false,
322 /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
323 /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
324
325 std::string reply =
326 R"({"url":"https://example.com",
327 "http_response_code":200,
328 "data":"Reply",
329 "headers":{"Content-Type":"text/html; charset=UTF-8"}})";
330
331 std::unique_ptr<net::URLRequest> request(
332 CreateAndCompleteJob(GURL("https://example.com"), reply));
333
334 std::string expected_request_json =
335 R"({"url": "https://example.com/",
336 "headers": {
337 "Cookie": "basic_cookie=1; secure_cookie=2; http_only_cookie=3",
338 "Referer": ""
339 }
340 })";
341
342 EXPECT_THAT(fetch_request_, MatchesJson(expected_request_json));
343 }
344
345 } // namespace headless
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698