• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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