1 // Copyright 2014 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.
5 #include "content/child/web_url_loader_impl.h"
10 #include "base/command_line.h"
11 #include "base/macros.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/time/time.h"
16 #include "content/child/request_extra_data.h"
17 #include "content/child/request_info.h"
18 #include "content/child/resource_dispatcher.h"
19 #include "content/public/child/fixed_received_data.h"
20 #include "content/public/child/request_peer.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/public/common/resource_response_info.h"
23 #include "net/base/net_errors.h"
24 #include "net/http/http_response_headers.h"
25 #include "net/http/http_util.h"
26 #include "net/url_request/redirect_info.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/WebKit/public/platform/WebString.h"
29 #include "third_party/WebKit/public/platform/WebURLError.h"
30 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
31 #include "third_party/WebKit/public/platform/WebURLRequest.h"
32 #include "third_party/WebKit/public/platform/WebURLResponse.h"
38 const char kTestURL
[] = "http://foo";
39 const char kTestData
[] = "blah!";
41 const char kFtpDirMimeType
[] = "text/vnd.chromium.ftp-dir";
42 // Simple FTP directory listing. Tests are not concerned with correct parsing,
43 // but rather correct cleanup when deleted while parsing. Important details of
44 // this list are that it contains more than one entry that are not "." or "..".
45 const char kFtpDirListing
[] =
46 "drwxr-xr-x 3 ftp ftp 4096 May 15 18:11 goat\n"
47 "drwxr-xr-x 3 ftp ftp 4096 May 15 18:11 hat";
49 const char kMultipartResponseMimeType
[] = "multipart/x-mixed-replace";
50 const char kMultipartResponseHeaders
[] =
51 "HTTP/1.0 200 Peachy\r\n"
52 "Content-Type: multipart/x-mixed-replace; boundary=boundary\r\n\r\n";
53 // Simple multipart response. Imporant details for the tests are that it
54 // contains multiple chunks, and that it doesn't end with a boundary, so will
55 // send data in OnResponseComplete. Also, it will resolve to kTestData.
56 const char kMultipartResponse
[] =
58 "Content-type: text/html\n\n"
61 "Content-type: text/html\n\n"
64 class TestResourceDispatcher
: public ResourceDispatcher
{
66 TestResourceDispatcher() :
67 ResourceDispatcher(nullptr, nullptr),
72 ~TestResourceDispatcher() override
{}
74 // TestDispatcher implementation:
76 int StartAsync(const RequestInfo
& request_info
,
77 ResourceRequestBody
* request_body
,
78 RequestPeer
* peer
) override
{
81 url_
= request_info
.url
;
85 void Cancel(int request_id
) override
{
86 EXPECT_FALSE(canceled_
);
90 RequestPeer
* peer() { return peer_
; }
92 bool canceled() { return canceled_
; }
94 const GURL
& url() { return url_
; }
101 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcher
);
104 class TestWebURLLoaderClient
: public blink::WebURLLoaderClient
{
106 TestWebURLLoaderClient(
107 ResourceDispatcher
* dispatcher
,
108 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
109 : loader_(new WebURLLoaderImpl(dispatcher
, task_runner
)),
110 expect_multipart_response_(false),
111 delete_on_receive_redirect_(false),
112 delete_on_receive_response_(false),
113 delete_on_receive_data_(false),
114 delete_on_finish_(false),
115 delete_on_fail_(false),
116 did_receive_redirect_(false),
117 did_receive_response_(false),
118 did_finish_(false) {}
120 ~TestWebURLLoaderClient() override
{}
122 // blink::WebURLLoaderClient implementation:
123 void willSendRequest(
124 blink::WebURLLoader
* loader
,
125 blink::WebURLRequest
& newRequest
,
126 const blink::WebURLResponse
& redirectResponse
) override
{
127 EXPECT_TRUE(loader_
);
128 EXPECT_EQ(loader_
.get(), loader
);
129 // No test currently simulates mutiple redirects.
130 EXPECT_FALSE(did_receive_redirect_
);
131 did_receive_redirect_
= true;
133 if (delete_on_receive_redirect_
)
137 void didSendData(blink::WebURLLoader
* loader
,
138 unsigned long long bytesSent
,
139 unsigned long long totalBytesToBeSent
) override
{
140 EXPECT_TRUE(loader_
);
141 EXPECT_EQ(loader_
.get(), loader
);
144 void didReceiveResponse(
145 blink::WebURLLoader
* loader
,
146 const blink::WebURLResponse
& response
) override
{
147 EXPECT_TRUE(loader_
);
148 EXPECT_EQ(loader_
.get(), loader
);
150 // Only multipart requests may receive multiple response headers.
151 EXPECT_TRUE(expect_multipart_response_
|| !did_receive_response_
);
153 did_receive_response_
= true;
154 response_
= response
;
155 if (delete_on_receive_response_
)
159 void didDownloadData(blink::WebURLLoader
* loader
,
161 int encodedDataLength
) override
{
162 EXPECT_TRUE(loader_
);
163 EXPECT_EQ(loader_
.get(), loader
);
166 void didReceiveData(blink::WebURLLoader
* loader
,
169 int encodedDataLength
) override
{
170 EXPECT_TRUE(loader_
);
171 EXPECT_EQ(loader_
.get(), loader
);
172 // The response should have started, but must not have finished, or failed.
173 EXPECT_TRUE(did_receive_response_
);
174 EXPECT_FALSE(did_finish_
);
175 EXPECT_EQ(net::OK
, error_
.reason
);
176 EXPECT_EQ("", error_
.domain
.utf8());
178 received_data_
.append(data
, dataLength
);
180 if (delete_on_receive_data_
)
184 void didReceiveCachedMetadata(blink::WebURLLoader
* loader
,
186 int dataLength
) override
{
187 EXPECT_EQ(loader_
.get(), loader
);
190 void didFinishLoading(blink::WebURLLoader
* loader
,
192 int64_t totalEncodedDataLength
) override
{
193 EXPECT_TRUE(loader_
);
194 EXPECT_EQ(loader_
.get(), loader
);
195 EXPECT_TRUE(did_receive_response_
);
196 EXPECT_FALSE(did_finish_
);
199 if (delete_on_finish_
)
203 void didFail(blink::WebURLLoader
* loader
,
204 const blink::WebURLError
& error
) override
{
205 EXPECT_TRUE(loader_
);
206 EXPECT_EQ(loader_
.get(), loader
);
207 EXPECT_FALSE(did_finish_
);
214 WebURLLoaderImpl
* loader() { return loader_
.get(); }
215 void DeleteLoader() {
219 void set_expect_multipart_response() { expect_multipart_response_
= true; }
221 void set_delete_on_receive_redirect() { delete_on_receive_redirect_
= true; }
222 void set_delete_on_receive_response() { delete_on_receive_response_
= true; }
223 void set_delete_on_receive_data() { delete_on_receive_data_
= true; }
224 void set_delete_on_finish() { delete_on_finish_
= true; }
225 void set_delete_on_fail() { delete_on_fail_
= true; }
227 bool did_receive_redirect() const { return did_receive_redirect_
; }
228 bool did_receive_response() const { return did_receive_response_
; }
229 const std::string
& received_data() const { return received_data_
; }
230 bool did_finish() const { return did_finish_
; }
231 const blink::WebURLError
& error() const { return error_
; }
232 const blink::WebURLResponse
& response() const { return response_
; }
235 scoped_ptr
<WebURLLoaderImpl
> loader_
;
237 bool expect_multipart_response_
;
239 bool delete_on_receive_redirect_
;
240 bool delete_on_receive_response_
;
241 bool delete_on_receive_data_
;
242 bool delete_on_finish_
;
243 bool delete_on_fail_
;
245 bool did_receive_redirect_
;
246 bool did_receive_response_
;
247 std::string received_data_
;
249 blink::WebURLError error_
;
250 blink::WebURLResponse response_
;
252 DISALLOW_COPY_AND_ASSIGN(TestWebURLLoaderClient
);
255 class WebURLLoaderImplTest
: public testing::Test
{
257 explicit WebURLLoaderImplTest()
258 : client_(&dispatcher_
, message_loop_
.task_runner()) {}
259 ~WebURLLoaderImplTest() override
{}
261 void DoStartAsyncRequest() {
262 blink::WebURLRequest request
;
263 request
.initialize();
264 request
.setURL(GURL(kTestURL
));
265 client()->loader()->loadAsynchronously(request
, client());
269 void DoReceiveRedirect() {
270 EXPECT_FALSE(client()->did_receive_redirect());
271 net::RedirectInfo redirect_info
;
272 redirect_info
.status_code
= 302;
273 redirect_info
.new_method
= "GET";
274 redirect_info
.new_url
= GURL(kTestURL
);
275 redirect_info
.new_first_party_for_cookies
= GURL(kTestURL
);
276 peer()->OnReceivedRedirect(redirect_info
,
277 content::ResourceResponseInfo());
278 EXPECT_TRUE(client()->did_receive_redirect());
281 void DoReceiveResponse() {
282 EXPECT_FALSE(client()->did_receive_response());
283 peer()->OnReceivedResponse(content::ResourceResponseInfo());
284 EXPECT_TRUE(client()->did_receive_response());
287 // Assumes it is called only once for a request.
288 void DoReceiveData() {
289 EXPECT_EQ("", client()->received_data());
290 peer()->OnReceivedData(make_scoped_ptr(new FixedReceivedData(
291 kTestData
, strlen(kTestData
), strlen(kTestData
))));
292 EXPECT_EQ(kTestData
, client()->received_data());
295 void DoCompleteRequest() {
296 EXPECT_FALSE(client()->did_finish());
297 peer()->OnCompletedRequest(net::OK
, false, false, "", base::TimeTicks(),
299 EXPECT_TRUE(client()->did_finish());
300 // There should be no error.
301 EXPECT_EQ(net::OK
, client()->error().reason
);
302 EXPECT_EQ("", client()->error().domain
.utf8());
305 void DoFailRequest() {
306 EXPECT_FALSE(client()->did_finish());
307 peer()->OnCompletedRequest(net::ERR_FAILED
, false, false, "",
308 base::TimeTicks(), strlen(kTestData
));
309 EXPECT_FALSE(client()->did_finish());
310 EXPECT_EQ(net::ERR_FAILED
, client()->error().reason
);
311 EXPECT_EQ(net::kErrorDomain
, client()->error().domain
.utf8());
314 void DoReceiveResponseFtp() {
315 EXPECT_FALSE(client()->did_receive_response());
316 content::ResourceResponseInfo response_info
;
317 response_info
.mime_type
= kFtpDirMimeType
;
318 peer()->OnReceivedResponse(response_info
);
319 EXPECT_TRUE(client()->did_receive_response());
322 void DoReceiveDataFtp() {
323 peer()->OnReceivedData(make_scoped_ptr(new FixedReceivedData(
324 kFtpDirListing
, strlen(kFtpDirListing
), strlen(kFtpDirListing
))));
325 // The FTP delegate should modify the data the client sees.
326 EXPECT_NE(kFtpDirListing
, client()->received_data());
329 void DoReceiveResponseMultipart() {
330 EXPECT_FALSE(client()->did_receive_response());
331 content::ResourceResponseInfo response_info
;
332 response_info
.headers
= new net::HttpResponseHeaders(
333 net::HttpUtil::AssembleRawHeaders(kMultipartResponseHeaders
,
334 strlen(kMultipartResponseHeaders
)));
335 response_info
.mime_type
= kMultipartResponseMimeType
;
336 peer()->OnReceivedResponse(response_info
);
337 EXPECT_TRUE(client()->did_receive_response());
340 void DoReceiveDataMultipart() {
341 peer()->OnReceivedData(make_scoped_ptr(
342 new FixedReceivedData(kMultipartResponse
, strlen(kMultipartResponse
),
343 strlen(kMultipartResponse
))));
344 // Multipart delegate should modify the data the client sees.
345 EXPECT_NE(kMultipartResponse
, client()->received_data());
348 TestWebURLLoaderClient
* client() { return &client_
; }
349 TestResourceDispatcher
* dispatcher() { return &dispatcher_
; }
350 RequestPeer
* peer() { return dispatcher()->peer(); }
351 base::MessageLoop
* message_loop() { return &message_loop_
; }
354 base::MessageLoop message_loop_
;
355 TestResourceDispatcher dispatcher_
;
356 TestWebURLLoaderClient client_
;
359 TEST_F(WebURLLoaderImplTest
, Success
) {
360 DoStartAsyncRequest();
364 EXPECT_FALSE(dispatcher()->canceled());
365 EXPECT_EQ(kTestData
, client()->received_data());
368 TEST_F(WebURLLoaderImplTest
, Redirect
) {
369 DoStartAsyncRequest();
374 EXPECT_FALSE(dispatcher()->canceled());
375 EXPECT_EQ(kTestData
, client()->received_data());
378 TEST_F(WebURLLoaderImplTest
, Failure
) {
379 DoStartAsyncRequest();
383 EXPECT_FALSE(dispatcher()->canceled());
386 // The client may delete the WebURLLoader during any callback from the loader.
387 // These tests make sure that doesn't result in a crash.
388 TEST_F(WebURLLoaderImplTest
, DeleteOnReceiveRedirect
) {
389 client()->set_delete_on_receive_redirect();
390 DoStartAsyncRequest();
394 TEST_F(WebURLLoaderImplTest
, DeleteOnReceiveResponse
) {
395 client()->set_delete_on_receive_response();
396 DoStartAsyncRequest();
400 TEST_F(WebURLLoaderImplTest
, DeleteOnReceiveData
) {
401 client()->set_delete_on_receive_data();
402 DoStartAsyncRequest();
407 TEST_F(WebURLLoaderImplTest
, DeleteOnFinish
) {
408 client()->set_delete_on_finish();
409 DoStartAsyncRequest();
415 TEST_F(WebURLLoaderImplTest
, DeleteOnFail
) {
416 client()->set_delete_on_fail();
417 DoStartAsyncRequest();
423 TEST_F(WebURLLoaderImplTest
, DeleteBeforeResponseDataURL
) {
424 blink::WebURLRequest request
;
425 request
.initialize();
426 request
.setURL(GURL("data:text/html;charset=utf-8,blah!"));
427 client()->loader()->loadAsynchronously(request
, client());
428 client()->DeleteLoader();
429 message_loop()->RunUntilIdle();
430 EXPECT_FALSE(client()->did_receive_response());
435 TEST_F(WebURLLoaderImplTest
, DataURL
) {
436 blink::WebURLRequest request
;
437 request
.initialize();
438 request
.setURL(GURL("data:text/html;charset=utf-8,blah!"));
439 client()->loader()->loadAsynchronously(request
, client());
440 message_loop()->RunUntilIdle();
441 EXPECT_EQ("blah!", client()->received_data());
442 EXPECT_TRUE(client()->did_finish());
443 EXPECT_EQ(net::OK
, client()->error().reason
);
444 EXPECT_EQ("", client()->error().domain
.utf8());
447 TEST_F(WebURLLoaderImplTest
, DataURLDeleteOnReceiveResponse
) {
448 blink::WebURLRequest request
;
449 request
.initialize();
450 request
.setURL(GURL("data:text/html;charset=utf-8,blah!"));
451 client()->set_delete_on_receive_response();
452 client()->loader()->loadAsynchronously(request
, client());
453 message_loop()->RunUntilIdle();
454 EXPECT_TRUE(client()->did_receive_response());
455 EXPECT_EQ("", client()->received_data());
456 EXPECT_FALSE(client()->did_finish());
459 TEST_F(WebURLLoaderImplTest
, DataURLDeleteOnReceiveData
) {
460 blink::WebURLRequest request
;
461 request
.initialize();
462 request
.setURL(GURL("data:text/html;charset=utf-8,blah!"));
463 client()->set_delete_on_receive_data();
464 client()->loader()->loadAsynchronously(request
, client());
465 message_loop()->RunUntilIdle();
466 EXPECT_TRUE(client()->did_receive_response());
467 EXPECT_EQ("blah!", client()->received_data());
468 EXPECT_FALSE(client()->did_finish());
471 TEST_F(WebURLLoaderImplTest
, DataURLDeleteOnFinish
) {
472 blink::WebURLRequest request
;
473 request
.initialize();
474 request
.setURL(GURL("data:text/html;charset=utf-8,blah!"));
475 client()->set_delete_on_finish();
476 client()->loader()->loadAsynchronously(request
, client());
477 message_loop()->RunUntilIdle();
478 EXPECT_TRUE(client()->did_receive_response());
479 EXPECT_EQ("blah!", client()->received_data());
480 EXPECT_TRUE(client()->did_finish());
483 TEST_F(WebURLLoaderImplTest
, DataURLDefersLoading
) {
484 blink::WebURLRequest request
;
485 request
.initialize();
486 request
.setURL(GURL("data:text/html;charset=utf-8,blah!"));
487 client()->loader()->loadAsynchronously(request
, client());
489 // setDefersLoading() might be called with either false or true in no
490 // specific order. The user of the API will not have sufficient information
491 // about the WebURLLoader's internal state, so the latter gracefully needs to
492 // handle calling setDefersLoading any number of times with any values from
493 // any point in time.
495 client()->loader()->setDefersLoading(false);
496 client()->loader()->setDefersLoading(true);
497 client()->loader()->setDefersLoading(true);
498 message_loop()->RunUntilIdle();
499 EXPECT_FALSE(client()->did_finish());
501 client()->loader()->setDefersLoading(false);
502 client()->loader()->setDefersLoading(true);
503 message_loop()->RunUntilIdle();
504 EXPECT_FALSE(client()->did_finish());
506 client()->loader()->setDefersLoading(false);
507 message_loop()->RunUntilIdle();
508 EXPECT_TRUE(client()->did_finish());
510 client()->loader()->setDefersLoading(true);
511 client()->loader()->setDefersLoading(false);
512 client()->loader()->setDefersLoading(false);
513 message_loop()->RunUntilIdle();
514 EXPECT_TRUE(client()->did_finish());
516 EXPECT_EQ("blah!", client()->received_data());
517 EXPECT_EQ(net::OK
, client()->error().reason
);
518 EXPECT_EQ("", client()->error().domain
.utf8());
521 // FTP integration tests. These are focused more on safe deletion than correct
522 // parsing of FTP responses.
524 TEST_F(WebURLLoaderImplTest
, Ftp
) {
525 DoStartAsyncRequest();
526 DoReceiveResponseFtp();
529 EXPECT_FALSE(dispatcher()->canceled());
532 TEST_F(WebURLLoaderImplTest
, FtpDeleteOnReceiveResponse
) {
533 client()->set_delete_on_receive_response();
534 DoStartAsyncRequest();
535 DoReceiveResponseFtp();
537 // No data should have been received.
538 EXPECT_EQ("", client()->received_data());
541 TEST_F(WebURLLoaderImplTest
, FtpDeleteOnReceiveFirstData
) {
542 client()->set_delete_on_receive_data();
543 DoStartAsyncRequest();
544 DoReceiveResponseFtp();
546 EXPECT_NE("", client()->received_data());
549 TEST_F(WebURLLoaderImplTest
, FtpDeleteOnReceiveMoreData
) {
550 DoStartAsyncRequest();
551 DoReceiveResponseFtp();
554 // Directory listings are only parsed once the request completes, so this will
555 // cancel in DoReceiveDataFtp, before the request finishes.
556 client()->set_delete_on_receive_data();
557 peer()->OnCompletedRequest(net::OK
, false, false, "", base::TimeTicks(),
559 EXPECT_FALSE(client()->did_finish());
562 TEST_F(WebURLLoaderImplTest
, FtpDeleteOnFinish
) {
563 client()->set_delete_on_finish();
564 DoStartAsyncRequest();
565 DoReceiveResponseFtp();
570 TEST_F(WebURLLoaderImplTest
, FtpDeleteOnFail
) {
571 client()->set_delete_on_fail();
572 DoStartAsyncRequest();
573 DoReceiveResponseFtp();
578 // Multipart integration tests. These are focused more on safe deletion than
579 // correct parsing of Multipart responses.
581 TEST_F(WebURLLoaderImplTest
, Multipart
) {
582 client()->set_expect_multipart_response();
583 DoStartAsyncRequest();
584 DoReceiveResponseMultipart();
585 DoReceiveDataMultipart();
587 EXPECT_EQ(kTestData
, client()->received_data());
588 EXPECT_FALSE(dispatcher()->canceled());
591 TEST_F(WebURLLoaderImplTest
, MultipartDeleteOnReceiveFirstResponse
) {
592 client()->set_expect_multipart_response();
593 client()->set_delete_on_receive_response();
594 DoStartAsyncRequest();
595 DoReceiveResponseMultipart();
596 EXPECT_EQ("", client()->received_data());
599 TEST_F(WebURLLoaderImplTest
, MultipartDeleteOnReceiveSecondResponse
) {
600 client()->set_expect_multipart_response();
601 DoStartAsyncRequest();
602 DoReceiveResponseMultipart();
603 client()->set_delete_on_receive_response();
604 DoReceiveDataMultipart();
605 EXPECT_EQ("", client()->received_data());
608 TEST_F(WebURLLoaderImplTest
, MultipartDeleteOnReceiveFirstData
) {
609 client()->set_expect_multipart_response();
610 client()->set_delete_on_receive_data();
611 DoStartAsyncRequest();
612 DoReceiveResponseMultipart();
613 DoReceiveDataMultipart();
614 EXPECT_EQ("bl", client()->received_data());
617 TEST_F(WebURLLoaderImplTest
, MultipartDeleteOnReceiveMoreData
) {
618 client()->set_expect_multipart_response();
619 DoStartAsyncRequest();
620 DoReceiveResponseMultipart();
621 DoReceiveDataMultipart();
622 // For multipart responses, the delegate may send some data when notified
623 // of a request completing.
624 client()->set_delete_on_receive_data();
625 peer()->OnCompletedRequest(net::OK
, false, false, "", base::TimeTicks(),
627 EXPECT_FALSE(client()->did_finish());
628 EXPECT_EQ(kTestData
, client()->received_data());
631 TEST_F(WebURLLoaderImplTest
, MultipartDeleteFinish
) {
632 client()->set_expect_multipart_response();
633 client()->set_delete_on_finish();
634 DoStartAsyncRequest();
635 DoReceiveResponseMultipart();
636 DoReceiveDataMultipart();
638 EXPECT_EQ(kTestData
, client()->received_data());
641 TEST_F(WebURLLoaderImplTest
, MultipartDeleteFail
) {
642 client()->set_expect_multipart_response();
643 client()->set_delete_on_fail();
644 DoStartAsyncRequest();
645 DoReceiveResponseMultipart();
646 DoReceiveDataMultipart();
650 // PlzNavigate: checks that the stream override parameters provided on
651 // navigation commit are properly applied.
652 TEST_F(WebURLLoaderImplTest
, BrowserSideNavigationCommit
) {
653 // Initialize the request and the stream override.
654 const GURL kStreamURL
= GURL("http://bar");
655 const std::string kMimeType
= "text/html";
656 blink::WebURLRequest request
;
657 request
.initialize();
658 request
.setURL(GURL(kTestURL
));
659 request
.setFrameType(blink::WebURLRequest::FrameTypeTopLevel
);
660 request
.setRequestContext(blink::WebURLRequest::RequestContextFrame
);
661 scoped_ptr
<StreamOverrideParameters
> stream_override(
662 new StreamOverrideParameters());
663 stream_override
->stream_url
= kStreamURL
;
664 stream_override
->response
.mime_type
= kMimeType
;
665 RequestExtraData
* extra_data
= new RequestExtraData();
666 extra_data
->set_stream_override(stream_override
.Pass());
667 request
.setExtraData(extra_data
);
668 base::CommandLine::ForCurrentProcess()->AppendSwitch(
669 switches::kEnableBrowserSideNavigation
);
671 client()->loader()->loadAsynchronously(request
, client());
673 // The stream url should have been requestead instead of the request url.
675 EXPECT_EQ(kStreamURL
, dispatcher()->url());
677 EXPECT_FALSE(client()->did_receive_response());
678 peer()->OnReceivedResponse(content::ResourceResponseInfo());
679 EXPECT_TRUE(client()->did_receive_response());
681 // The response info should have been overriden.
682 ASSERT_FALSE(client()->response().isNull());
683 EXPECT_EQ(kMimeType
, client()->response().mimeType().latin1());
687 EXPECT_FALSE(dispatcher()->canceled());
688 EXPECT_EQ(kTestData
, client()->received_data());
692 } // namespace content