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.
4
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"
18
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
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;
29 using testing::DoAll;
30 using testing::Ge;
31 using testing::Gt;
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;
40 using testing::Test;
41 using testing::WithArg;
42 using testing::WithArgs;
43 using testing::_;
44
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
48 // InputStream.
49 class NotImplInputStream : public InputStream {
50 public:
NotImplInputStream()51 NotImplInputStream() {}
~NotImplInputStream()52 virtual ~NotImplInputStream() {}
BytesAvailable(int * bytes_available) const53 virtual bool BytesAvailable(int* bytes_available) const OVERRIDE {
54 NOTIMPLEMENTED();
55 return false;
56 }
Skip(int64_t n,int64_t * bytes_skipped)57 virtual bool Skip(int64_t n, int64_t* bytes_skipped) OVERRIDE {
58 NOTIMPLEMENTED();
59 return false;
60 }
Read(net::IOBuffer * dest,int length,int * bytes_read)61 virtual bool Read(net::IOBuffer* dest, int length, int* bytes_read) OVERRIDE {
62 NOTIMPLEMENTED();
63 return false;
64 }
65 };
66
67 // Required in order to create an instance of AndroidStreamReaderURLRequestJob.
68 class StreamReaderDelegate :
69 public AndroidStreamReaderURLRequestJob::Delegate {
70 public:
StreamReaderDelegate()71 StreamReaderDelegate() {}
72
OpenInputStream(JNIEnv * env,const GURL & url)73 virtual scoped_ptr<InputStream> OpenInputStream(
74 JNIEnv* env,
75 const GURL& url) OVERRIDE {
76 return make_scoped_ptr<InputStream>(new NotImplInputStream());
77 }
78
OnInputStreamOpenFailed(net::URLRequest * request,bool * restart)79 virtual void OnInputStreamOpenFailed(net::URLRequest* request,
80 bool* restart) OVERRIDE {
81 *restart = false;
82 }
83
GetMimeType(JNIEnv * env,net::URLRequest * request,android_webview::InputStream * stream,std::string * mime_type)84 virtual bool GetMimeType(JNIEnv* env,
85 net::URLRequest* request,
86 android_webview::InputStream* stream,
87 std::string* mime_type) OVERRIDE {
88 return false;
89 }
90
GetCharset(JNIEnv * env,net::URLRequest * request,android_webview::InputStream * stream,std::string * charset)91 virtual bool GetCharset(JNIEnv* env,
92 net::URLRequest* request,
93 android_webview::InputStream* stream,
94 std::string* charset) OVERRIDE {
95 return false;
96 }
97
AppendResponseHeaders(JNIEnv * env,net::HttpResponseHeaders * headers)98 virtual void AppendResponseHeaders(
99 JNIEnv* env,
100 net::HttpResponseHeaders* headers) OVERRIDE {
101 // no-op
102 }
103 };
104
105 class NullStreamReaderDelegate : public StreamReaderDelegate {
106 public:
NullStreamReaderDelegate()107 NullStreamReaderDelegate() {}
108
OpenInputStream(JNIEnv * env,const GURL & url)109 virtual scoped_ptr<InputStream> OpenInputStream(
110 JNIEnv* env,
111 const GURL& url) OVERRIDE {
112 return make_scoped_ptr<InputStream>(NULL);
113 }
114 };
115
116 class HeaderAlteringStreamReaderDelegate : public NullStreamReaderDelegate {
117 public:
HeaderAlteringStreamReaderDelegate()118 HeaderAlteringStreamReaderDelegate() {}
119
AppendResponseHeaders(JNIEnv * env,net::HttpResponseHeaders * headers)120 virtual void AppendResponseHeaders(
121 JNIEnv* env,
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);
128 }
129
130 static const int kResponseCode;
131 static const char* kStatusLine;
132 static const char* kCustomHeaderName;
133 static const char* kCustomHeaderValue;
134 };
135
136 const int HeaderAlteringStreamReaderDelegate::kResponseCode = 401;
137 const char* HeaderAlteringStreamReaderDelegate::kStatusLine =
138 "HTTP/1.1 401 Gone";
139 const char* HeaderAlteringStreamReaderDelegate::kCustomHeaderName =
140 "X-Test-Header";
141 const char* HeaderAlteringStreamReaderDelegate::kCustomHeaderValue =
142 "TestHeaderValue";
143
144 class MockInputStreamReader : public InputStreamReader {
145 public:
MockInputStreamReader()146 MockInputStreamReader() : InputStreamReader(new NotImplInputStream()) {}
~MockInputStreamReader()147 ~MockInputStreamReader() {}
148
149 MOCK_METHOD1(Seek, int(const net::HttpByteRange& byte_range));
150 MOCK_METHOD2(ReadRawData, int(net::IOBuffer* buffer, int buffer_size));
151 };
152
153
154 class TestStreamReaderJob : public AndroidStreamReaderURLRequestJob {
155 public:
TestStreamReaderJob(net::URLRequest * request,net::NetworkDelegate * network_delegate,scoped_ptr<Delegate> delegate,scoped_ptr<InputStreamReader> stream_reader)156 TestStreamReaderJob(
157 net::URLRequest* request,
158 net::NetworkDelegate* network_delegate,
159 scoped_ptr<Delegate> delegate,
160 scoped_ptr<InputStreamReader> stream_reader)
161 : AndroidStreamReaderURLRequestJob(request,
162 network_delegate,
163 delegate.Pass()),
164 stream_reader_(stream_reader.Pass()) {
165 message_loop_proxy_ = base::MessageLoopProxy::current();
166 }
167
CreateStreamReader(InputStream * stream)168 virtual scoped_ptr<InputStreamReader> CreateStreamReader(
169 InputStream* stream) OVERRIDE {
170 return stream_reader_.Pass();
171 }
172 protected:
~TestStreamReaderJob()173 virtual ~TestStreamReaderJob() {}
174
GetWorkerThreadRunner()175 virtual base::TaskRunner* GetWorkerThreadRunner() OVERRIDE {
176 return message_loop_proxy_.get();
177 }
178
179 scoped_ptr<InputStreamReader> stream_reader_;
180 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
181 };
182
183 class AndroidStreamReaderURLRequestJobTest : public Test {
184 public:
AndroidStreamReaderURLRequestJobTest()185 AndroidStreamReaderURLRequestJobTest() {}
186
187 protected:
SetUp()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_,
194 &context_));
195 req_->set_method("GET");
196 }
197
SetRange(net::URLRequest * req,int first_byte,int last_byte)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);
204 }
205
SetUpTestJob(scoped_ptr<InputStreamReader> stream_reader)206 void SetUpTestJob(scoped_ptr<InputStreamReader> stream_reader) {
207 SetUpTestJob(stream_reader.Pass(),
208 make_scoped_ptr(new StreamReaderDelegate())
209 .PassAs<AndroidStreamReaderURLRequestJob::Delegate>());
210 }
211
SetUpTestJob(scoped_ptr<InputStreamReader> stream_reader,scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate> stream_reader_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(
217 req_.get(),
218 &network_delegate_,
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);
226
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);
231 }
232
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_;
239 };
240
TEST_F(AndroidStreamReaderURLRequestJobTest,ReadEmptyStream)241 TEST_F(AndroidStreamReaderURLRequestJobTest, ReadEmptyStream) {
242 scoped_ptr<StrictMock<MockInputStreamReader> > stream_reader(
243 new StrictMock<MockInputStreamReader>());
244 {
245 InSequence s;
246 EXPECT_CALL(*stream_reader, Seek(_))
247 .WillOnce(Return(0));
248 EXPECT_CALL(*stream_reader, ReadRawData(NotNull(), Gt(0)))
249 .WillOnce(Return(0));
250 }
251
252 SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
253
254 req_->Start();
255
256 // The TestDelegate will quit the message loop on request completion.
257 base::MessageLoop::current()->Run();
258
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());
263 }
264
TEST_F(AndroidStreamReaderURLRequestJobTest,ReadWithNullStream)265 TEST_F(AndroidStreamReaderURLRequestJobTest, ReadWithNullStream) {
266 SetUpTestJob(scoped_ptr<InputStreamReader>(),
267 make_scoped_ptr(new NullStreamReaderDelegate())
268 .PassAs<AndroidStreamReaderURLRequestJob::Delegate>());
269 req_->Start();
270
271 // The TestDelegate will quit the message loop on request completion.
272 base::MessageLoop::current()->Run();
273
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());
281 }
282
TEST_F(AndroidStreamReaderURLRequestJobTest,ModifyHeadersAndStatus)283 TEST_F(AndroidStreamReaderURLRequestJobTest, ModifyHeadersAndStatus) {
284 SetUpTestJob(scoped_ptr<InputStreamReader>(),
285 make_scoped_ptr(new HeaderAlteringStreamReaderDelegate())
286 .PassAs<AndroidStreamReaderURLRequestJob::Delegate>());
287 req_->Start();
288
289 // The TestDelegate will quit the message loop on request completion.
290 base::MessageLoop::current()->Run();
291
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,
307 &header_value));
308 EXPECT_EQ(HeaderAlteringStreamReaderDelegate::kCustomHeaderValue,
309 header_value);
310 }
311
TEST_F(AndroidStreamReaderURLRequestJobTest,ReadPartOfStream)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>());
318 {
319 InSequence s;
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));
328 }
329
330 SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
331
332 SetRange(req_.get(), offset, bytes_available);
333 req_->Start();
334
335 base::MessageLoop::current()->Run();
336
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());
341 }
342
TEST_F(AndroidStreamReaderURLRequestJobTest,ReadStreamWithMoreAvailableThanActual)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>());
351 {
352 InSequence s;
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));
359 }
360
361 SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
362
363 SetRange(req_.get(), offset, bytes_available_reported);
364 req_->Start();
365
366 base::MessageLoop::current()->Run();
367
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());
372 }
373
TEST_F(AndroidStreamReaderURLRequestJobTest,DeleteJobMidWaySeek)374 TEST_F(AndroidStreamReaderURLRequestJobTest, DeleteJobMidWaySeek) {
375 const int offset = 20;
376 const int bytes_available = 128;
377 base::RunLoop loop;
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));
385
386 SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
387
388 SetRange(req_.get(), offset, bytes_available);
389 req_->Start();
390
391 loop.Run();
392
393 EXPECT_EQ(0, network_delegate_.completed_requests());
394 req_->Cancel();
395 EXPECT_EQ(1, network_delegate_.completed_requests());
396 }
397
TEST_F(AndroidStreamReaderURLRequestJobTest,DeleteJobMidWayRead)398 TEST_F(AndroidStreamReaderURLRequestJobTest, DeleteJobMidWayRead) {
399 const int offset = 20;
400 const int bytes_available = 128;
401 base::RunLoop loop;
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)));
410
411 SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
412
413 SetRange(req_.get(), offset, bytes_available);
414 req_->Start();
415
416 loop.Run();
417
418 EXPECT_EQ(0, network_delegate_.completed_requests());
419 req_->Cancel();
420 EXPECT_EQ(1, network_delegate_.completed_requests());
421 }
422