1 // Copyright (c) 2012 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 "android_webview/browser/input_stream.h"
6 #include "android_webview/browser/net/android_stream_reader_url_request_job.h"
7 #include "android_webview/browser/net/aw_url_request_job_factory.h"
8 #include "android_webview/browser/net/input_stream_reader.h"
9 #include "base/format_macros.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/strings/stringprintf.h"
13 #include "net/base/request_priority.h"
14 #include "net/http/http_byte_range.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/url_request/url_request_job_factory_impl.h"
17 #include "net/url_request/url_request_test_util.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 using android_webview::InputStream
;
23 using android_webview::InputStreamReader
;
24 using net::TestDelegate
;
25 using net::TestJobInterceptor
;
26 using net::TestNetworkDelegate
;
27 using net::TestURLRequestContext
;
28 using net::TestURLRequest
;
32 using testing::InSequence
;
33 using testing::Invoke
;
34 using testing::InvokeWithoutArgs
;
35 using testing::NotNull
;
36 using testing::Return
;
37 using testing::SaveArg
;
38 using testing::SetArgPointee
;
39 using testing::StrictMock
;
41 using testing::WithArg
;
42 using testing::WithArgs
;
45 // Some of the classes will DCHECK on a null InputStream (which is desirable).
46 // The workaround is to use this class. None of the methods need to be
47 // implemented as the mock InputStreamReader should never forward calls to the
49 class NotImplInputStream
: public InputStream
{
51 NotImplInputStream() {}
52 virtual ~NotImplInputStream() {}
53 virtual bool BytesAvailable(int* bytes_available
) const OVERRIDE
{
57 virtual bool Skip(int64_t n
, int64_t* bytes_skipped
) OVERRIDE
{
61 virtual bool Read(net::IOBuffer
* dest
, int length
, int* bytes_read
) OVERRIDE
{
67 // Required in order to create an instance of AndroidStreamReaderURLRequestJob.
68 class StreamReaderDelegate
:
69 public AndroidStreamReaderURLRequestJob::Delegate
{
71 StreamReaderDelegate() {}
73 virtual scoped_ptr
<InputStream
> OpenInputStream(
75 const GURL
& url
) OVERRIDE
{
76 return make_scoped_ptr
<InputStream
>(new NotImplInputStream());
79 virtual void OnInputStreamOpenFailed(net::URLRequest
* request
,
80 bool* restart
) OVERRIDE
{
84 virtual bool GetMimeType(JNIEnv
* env
,
85 net::URLRequest
* request
,
86 android_webview::InputStream
* stream
,
87 std::string
* mime_type
) OVERRIDE
{
91 virtual bool GetCharset(JNIEnv
* env
,
92 net::URLRequest
* request
,
93 android_webview::InputStream
* stream
,
94 std::string
* charset
) OVERRIDE
{
98 virtual void AppendResponseHeaders(
100 net::HttpResponseHeaders
* headers
) OVERRIDE
{
105 class NullStreamReaderDelegate
: public StreamReaderDelegate
{
107 NullStreamReaderDelegate() {}
109 virtual scoped_ptr
<InputStream
> OpenInputStream(
111 const GURL
& url
) OVERRIDE
{
112 return make_scoped_ptr
<InputStream
>(NULL
);
116 class HeaderAlteringStreamReaderDelegate
: public NullStreamReaderDelegate
{
118 HeaderAlteringStreamReaderDelegate() {}
120 virtual void AppendResponseHeaders(
122 net::HttpResponseHeaders
* headers
) OVERRIDE
{
123 headers
->ReplaceStatusLine(kStatusLine
);
124 std::string
headerLine(kCustomHeaderName
);
125 headerLine
.append(": ");
126 headerLine
.append(kCustomHeaderValue
);
127 headers
->AddHeader(headerLine
);
130 static const int kResponseCode
;
131 static const char* kStatusLine
;
132 static const char* kCustomHeaderName
;
133 static const char* kCustomHeaderValue
;
136 const int HeaderAlteringStreamReaderDelegate::kResponseCode
= 401;
137 const char* HeaderAlteringStreamReaderDelegate::kStatusLine
=
139 const char* HeaderAlteringStreamReaderDelegate::kCustomHeaderName
=
141 const char* HeaderAlteringStreamReaderDelegate::kCustomHeaderValue
=
144 class MockInputStreamReader
: public InputStreamReader
{
146 MockInputStreamReader() : InputStreamReader(new NotImplInputStream()) {}
147 ~MockInputStreamReader() {}
149 MOCK_METHOD1(Seek
, int(const net::HttpByteRange
& byte_range
));
150 MOCK_METHOD2(ReadRawData
, int(net::IOBuffer
* buffer
, int buffer_size
));
154 class TestStreamReaderJob
: public AndroidStreamReaderURLRequestJob
{
157 net::URLRequest
* request
,
158 net::NetworkDelegate
* network_delegate
,
159 scoped_ptr
<Delegate
> delegate
,
160 scoped_ptr
<InputStreamReader
> stream_reader
)
161 : AndroidStreamReaderURLRequestJob(request
,
164 stream_reader_(stream_reader
.Pass()) {
165 message_loop_proxy_
= base::MessageLoopProxy::current();
168 virtual scoped_ptr
<InputStreamReader
> CreateStreamReader(
169 InputStream
* stream
) OVERRIDE
{
170 return stream_reader_
.Pass();
173 virtual ~TestStreamReaderJob() {}
175 virtual base::TaskRunner
* GetWorkerThreadRunner() OVERRIDE
{
176 return message_loop_proxy_
.get();
179 scoped_ptr
<InputStreamReader
> stream_reader_
;
180 scoped_refptr
<base::MessageLoopProxy
> message_loop_proxy_
;
183 class AndroidStreamReaderURLRequestJobTest
: public Test
{
185 AndroidStreamReaderURLRequestJobTest() {}
188 virtual void SetUp() {
189 context_
.set_job_factory(&factory_
);
190 context_
.set_network_delegate(&network_delegate_
);
191 req_
.reset(new TestURLRequest(GURL("content://foo"),
192 net::DEFAULT_PRIORITY
,
193 &url_request_delegate_
,
195 req_
->set_method("GET");
198 void SetRange(net::URLRequest
* req
, int first_byte
, int last_byte
) {
199 net::HttpRequestHeaders headers
;
200 headers
.SetHeader(net::HttpRequestHeaders::kRange
,
201 net::HttpByteRange::Bounded(
202 first_byte
, last_byte
).GetHeaderValue());
203 req
->SetExtraRequestHeaders(headers
);
206 void SetUpTestJob(scoped_ptr
<InputStreamReader
> stream_reader
) {
207 SetUpTestJob(stream_reader
.Pass(),
208 make_scoped_ptr(new StreamReaderDelegate())
209 .PassAs
<AndroidStreamReaderURLRequestJob::Delegate
>());
212 void SetUpTestJob(scoped_ptr
<InputStreamReader
> stream_reader
,
213 scoped_ptr
<AndroidStreamReaderURLRequestJob::Delegate
>
214 stream_reader_delegate
) {
215 TestStreamReaderJob
* test_stream_reader_job
=
216 new TestStreamReaderJob(
219 stream_reader_delegate
.Pass(),
220 stream_reader
.Pass());
221 // The Interceptor is owned by the |factory_|.
222 TestJobInterceptor
* protocol_handler
= new TestJobInterceptor
;
223 protocol_handler
->set_main_intercept_job(test_stream_reader_job
);
224 bool set_protocol
= factory_
.SetProtocolHandler("http", protocol_handler
);
225 DCHECK(set_protocol
);
227 protocol_handler
= new TestJobInterceptor
;
228 protocol_handler
->set_main_intercept_job(test_stream_reader_job
);
229 set_protocol
= factory_
.SetProtocolHandler("content", protocol_handler
);
230 DCHECK(set_protocol
);
233 base::MessageLoopForIO loop_
;
234 TestURLRequestContext context_
;
235 android_webview::AwURLRequestJobFactory factory_
;
236 TestDelegate url_request_delegate_
;
237 TestNetworkDelegate network_delegate_
;
238 scoped_ptr
<TestURLRequest
> req_
;
241 TEST_F(AndroidStreamReaderURLRequestJobTest
, ReadEmptyStream
) {
242 scoped_ptr
<StrictMock
<MockInputStreamReader
> > stream_reader(
243 new StrictMock
<MockInputStreamReader
>());
246 EXPECT_CALL(*stream_reader
, Seek(_
))
247 .WillOnce(Return(0));
248 EXPECT_CALL(*stream_reader
, ReadRawData(NotNull(), Gt(0)))
249 .WillOnce(Return(0));
252 SetUpTestJob(stream_reader
.PassAs
<InputStreamReader
>());
256 // The TestDelegate will quit the message loop on request completion.
257 base::MessageLoop::current()->Run();
259 EXPECT_FALSE(url_request_delegate_
.request_failed());
260 EXPECT_EQ(1, network_delegate_
.completed_requests());
261 EXPECT_EQ(0, network_delegate_
.error_count());
262 EXPECT_EQ(200, req_
->GetResponseCode());
265 TEST_F(AndroidStreamReaderURLRequestJobTest
, ReadWithNullStream
) {
266 SetUpTestJob(scoped_ptr
<InputStreamReader
>(),
267 make_scoped_ptr(new NullStreamReaderDelegate())
268 .PassAs
<AndroidStreamReaderURLRequestJob::Delegate
>());
271 // The TestDelegate will quit the message loop on request completion.
272 base::MessageLoop::current()->Run();
274 // The request_failed() method is named confusingly but all it checks is
275 // whether the request got as far as calling NotifyHeadersComplete.
276 EXPECT_FALSE(url_request_delegate_
.request_failed());
277 EXPECT_EQ(1, network_delegate_
.completed_requests());
278 // A null input stream shouldn't result in an error. See crbug.com/180950.
279 EXPECT_EQ(0, network_delegate_
.error_count());
280 EXPECT_EQ(404, req_
->GetResponseCode());
283 TEST_F(AndroidStreamReaderURLRequestJobTest
, ModifyHeadersAndStatus
) {
284 SetUpTestJob(scoped_ptr
<InputStreamReader
>(),
285 make_scoped_ptr(new HeaderAlteringStreamReaderDelegate())
286 .PassAs
<AndroidStreamReaderURLRequestJob::Delegate
>());
289 // The TestDelegate will quit the message loop on request completion.
290 base::MessageLoop::current()->Run();
292 // The request_failed() method is named confusingly but all it checks is
293 // whether the request got as far as calling NotifyHeadersComplete.
294 EXPECT_FALSE(url_request_delegate_
.request_failed());
295 EXPECT_EQ(1, network_delegate_
.completed_requests());
296 // A null input stream shouldn't result in an error. See crbug.com/180950.
297 EXPECT_EQ(0, network_delegate_
.error_count());
298 EXPECT_EQ(HeaderAlteringStreamReaderDelegate::kResponseCode
,
299 req_
->GetResponseCode());
300 EXPECT_EQ(HeaderAlteringStreamReaderDelegate::kStatusLine
,
301 req_
->response_headers()->GetStatusLine());
302 EXPECT_TRUE(req_
->response_headers()->HasHeader(
303 HeaderAlteringStreamReaderDelegate::kCustomHeaderName
));
304 std::string header_value
;
305 EXPECT_TRUE(req_
->response_headers()->EnumerateHeader(
306 NULL
, HeaderAlteringStreamReaderDelegate::kCustomHeaderName
,
308 EXPECT_EQ(HeaderAlteringStreamReaderDelegate::kCustomHeaderValue
,
312 TEST_F(AndroidStreamReaderURLRequestJobTest
, ReadPartOfStream
) {
313 const int bytes_available
= 128;
314 const int offset
= 32;
315 const int bytes_to_read
= bytes_available
- offset
;
316 scoped_ptr
<StrictMock
<MockInputStreamReader
> > stream_reader(
317 new StrictMock
<MockInputStreamReader
>());
320 EXPECT_CALL(*stream_reader
, Seek(_
))
321 .WillOnce(Return(bytes_available
));
322 EXPECT_CALL(*stream_reader
, ReadRawData(NotNull(), Ge(bytes_to_read
)))
323 .WillOnce(Return(bytes_to_read
/2));
324 EXPECT_CALL(*stream_reader
, ReadRawData(NotNull(), Ge(bytes_to_read
)))
325 .WillOnce(Return(bytes_to_read
/2));
326 EXPECT_CALL(*stream_reader
, ReadRawData(NotNull(), Ge(bytes_to_read
)))
327 .WillOnce(Return(0));
330 SetUpTestJob(stream_reader
.PassAs
<InputStreamReader
>());
332 SetRange(req_
.get(), offset
, bytes_available
);
335 base::MessageLoop::current()->Run();
337 EXPECT_FALSE(url_request_delegate_
.request_failed());
338 EXPECT_EQ(bytes_to_read
, url_request_delegate_
.bytes_received());
339 EXPECT_EQ(1, network_delegate_
.completed_requests());
340 EXPECT_EQ(0, network_delegate_
.error_count());
343 TEST_F(AndroidStreamReaderURLRequestJobTest
,
344 ReadStreamWithMoreAvailableThanActual
) {
345 const int bytes_available_reported
= 190;
346 const int bytes_available
= 128;
347 const int offset
= 0;
348 const int bytes_to_read
= bytes_available
- offset
;
349 scoped_ptr
<StrictMock
<MockInputStreamReader
> > stream_reader(
350 new StrictMock
<MockInputStreamReader
>());
353 EXPECT_CALL(*stream_reader
, Seek(_
))
354 .WillOnce(Return(bytes_available_reported
));
355 EXPECT_CALL(*stream_reader
, ReadRawData(NotNull(), Ge(bytes_to_read
)))
356 .WillOnce(Return(bytes_available
));
357 EXPECT_CALL(*stream_reader
, ReadRawData(NotNull(), Ge(bytes_to_read
)))
358 .WillOnce(Return(0));
361 SetUpTestJob(stream_reader
.PassAs
<InputStreamReader
>());
363 SetRange(req_
.get(), offset
, bytes_available_reported
);
366 base::MessageLoop::current()->Run();
368 EXPECT_FALSE(url_request_delegate_
.request_failed());
369 EXPECT_EQ(bytes_to_read
, url_request_delegate_
.bytes_received());
370 EXPECT_EQ(1, network_delegate_
.completed_requests());
371 EXPECT_EQ(0, network_delegate_
.error_count());
374 TEST_F(AndroidStreamReaderURLRequestJobTest
, DeleteJobMidWaySeek
) {
375 const int offset
= 20;
376 const int bytes_available
= 128;
378 scoped_ptr
<StrictMock
<MockInputStreamReader
> > stream_reader(
379 new StrictMock
<MockInputStreamReader
>());
380 EXPECT_CALL(*stream_reader
, Seek(_
))
381 .WillOnce(DoAll(InvokeWithoutArgs(&loop
, &base::RunLoop::Quit
),
382 Return(bytes_available
)));
383 ON_CALL(*stream_reader
, ReadRawData(_
, _
))
384 .WillByDefault(Return(0));
386 SetUpTestJob(stream_reader
.PassAs
<InputStreamReader
>());
388 SetRange(req_
.get(), offset
, bytes_available
);
393 EXPECT_EQ(0, network_delegate_
.completed_requests());
395 EXPECT_EQ(1, network_delegate_
.completed_requests());
398 TEST_F(AndroidStreamReaderURLRequestJobTest
, DeleteJobMidWayRead
) {
399 const int offset
= 20;
400 const int bytes_available
= 128;
402 scoped_ptr
<StrictMock
<MockInputStreamReader
> > stream_reader(
403 new StrictMock
<MockInputStreamReader
>());
404 net::CompletionCallback read_completion_callback
;
405 EXPECT_CALL(*stream_reader
, Seek(_
))
406 .WillOnce(Return(bytes_available
));
407 EXPECT_CALL(*stream_reader
, ReadRawData(_
, _
))
408 .WillOnce(DoAll(InvokeWithoutArgs(&loop
, &base::RunLoop::Quit
),
409 Return(bytes_available
)));
411 SetUpTestJob(stream_reader
.PassAs
<InputStreamReader
>());
413 SetRange(req_
.get(), offset
, bytes_available
);
418 EXPECT_EQ(0, network_delegate_
.completed_requests());
420 EXPECT_EQ(1, network_delegate_
.completed_requests());