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/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/run_loop.h"
13#include "base/strings/stringprintf.h"
14#include "net/base/request_priority.h"
15#include "net/http/http_byte_range.h"
16#include "net/http/http_response_headers.h"
17#include "net/url_request/url_request.h"
18#include "net/url_request/url_request_job_factory_impl.h"
19#include "net/url_request/url_request_test_util.h"
20
21#include "testing/gmock/include/gmock/gmock.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24using android_webview::InputStream;
25using android_webview::InputStreamReader;
26using net::TestDelegate;
27using net::TestJobInterceptor;
28using net::TestNetworkDelegate;
29using net::TestURLRequestContext;
30using net::URLRequest;
31using testing::DoAll;
32using testing::Ge;
33using testing::Gt;
34using testing::InSequence;
35using testing::Invoke;
36using testing::InvokeWithoutArgs;
37using testing::NotNull;
38using testing::Return;
39using testing::SaveArg;
40using testing::SetArgPointee;
41using testing::StrictMock;
42using testing::Test;
43using testing::WithArg;
44using testing::WithArgs;
45using testing::_;
46
47// Some of the classes will DCHECK on a null InputStream (which is desirable).
48// The workaround is to use this class. None of the methods need to be
49// implemented as the mock InputStreamReader should never forward calls to the
50// InputStream.
51class NotImplInputStream : public InputStream {
52 public:
53  NotImplInputStream() {}
54  virtual ~NotImplInputStream() {}
55  virtual bool BytesAvailable(int* bytes_available) const OVERRIDE {
56    NOTIMPLEMENTED();
57    return false;
58  }
59  virtual bool Skip(int64_t n, int64_t* bytes_skipped) OVERRIDE {
60    NOTIMPLEMENTED();
61    return false;
62  }
63  virtual bool Read(net::IOBuffer* dest, int length, int* bytes_read) OVERRIDE {
64    NOTIMPLEMENTED();
65    return false;
66  }
67};
68
69// Required in order to create an instance of AndroidStreamReaderURLRequestJob.
70class StreamReaderDelegate :
71    public AndroidStreamReaderURLRequestJob::Delegate {
72 public:
73  StreamReaderDelegate() {}
74
75  virtual scoped_ptr<InputStream> OpenInputStream(
76      JNIEnv* env,
77      const GURL& url) OVERRIDE {
78    return make_scoped_ptr<InputStream>(new NotImplInputStream());
79  }
80
81  virtual void OnInputStreamOpenFailed(net::URLRequest* request,
82                                       bool* restart) OVERRIDE {
83    *restart = false;
84  }
85
86  virtual bool GetMimeType(JNIEnv* env,
87                           net::URLRequest* request,
88                           android_webview::InputStream* stream,
89                           std::string* mime_type) OVERRIDE {
90    return false;
91  }
92
93  virtual bool GetCharset(JNIEnv* env,
94                          net::URLRequest* request,
95                          android_webview::InputStream* stream,
96                          std::string* charset) OVERRIDE {
97    return false;
98  }
99
100  virtual void AppendResponseHeaders(
101      JNIEnv* env,
102      net::HttpResponseHeaders* headers) OVERRIDE {
103    // no-op
104  }
105};
106
107class NullStreamReaderDelegate : public StreamReaderDelegate {
108 public:
109  NullStreamReaderDelegate() {}
110
111  virtual scoped_ptr<InputStream> OpenInputStream(
112      JNIEnv* env,
113      const GURL& url) OVERRIDE {
114    return make_scoped_ptr<InputStream>(NULL);
115  }
116};
117
118class HeaderAlteringStreamReaderDelegate : public NullStreamReaderDelegate {
119 public:
120  HeaderAlteringStreamReaderDelegate() {}
121
122  virtual void AppendResponseHeaders(
123      JNIEnv* env,
124      net::HttpResponseHeaders* headers) OVERRIDE {
125    headers->ReplaceStatusLine(kStatusLine);
126    std::string headerLine(kCustomHeaderName);
127    headerLine.append(": ");
128    headerLine.append(kCustomHeaderValue);
129    headers->AddHeader(headerLine);
130  }
131
132  static const int kResponseCode;
133  static const char* kStatusLine;
134  static const char* kCustomHeaderName;
135  static const char* kCustomHeaderValue;
136};
137
138const int HeaderAlteringStreamReaderDelegate::kResponseCode = 401;
139const char* HeaderAlteringStreamReaderDelegate::kStatusLine =
140    "HTTP/1.1 401 Gone";
141const char* HeaderAlteringStreamReaderDelegate::kCustomHeaderName =
142    "X-Test-Header";
143const char* HeaderAlteringStreamReaderDelegate::kCustomHeaderValue =
144    "TestHeaderValue";
145
146class MockInputStreamReader : public InputStreamReader {
147 public:
148  MockInputStreamReader() : InputStreamReader(new NotImplInputStream()) {}
149  ~MockInputStreamReader() {}
150
151  MOCK_METHOD1(Seek, int(const net::HttpByteRange& byte_range));
152  MOCK_METHOD2(ReadRawData, int(net::IOBuffer* buffer, int buffer_size));
153};
154
155
156class TestStreamReaderJob : public AndroidStreamReaderURLRequestJob {
157 public:
158  TestStreamReaderJob(
159      net::URLRequest* request,
160      net::NetworkDelegate* network_delegate,
161      scoped_ptr<Delegate> delegate,
162      scoped_ptr<InputStreamReader> stream_reader)
163      : AndroidStreamReaderURLRequestJob(request,
164                                         network_delegate,
165                                         delegate.Pass()),
166        stream_reader_(stream_reader.Pass()) {
167    message_loop_proxy_ = base::MessageLoopProxy::current();
168  }
169
170  virtual scoped_ptr<InputStreamReader> CreateStreamReader(
171      InputStream* stream) OVERRIDE {
172    return stream_reader_.Pass();
173  }
174 protected:
175  virtual ~TestStreamReaderJob() {}
176
177  virtual base::TaskRunner* GetWorkerThreadRunner() OVERRIDE {
178    return message_loop_proxy_.get();
179  }
180
181  scoped_ptr<InputStreamReader> stream_reader_;
182  scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
183};
184
185class AndroidStreamReaderURLRequestJobTest : public Test {
186 public:
187  AndroidStreamReaderURLRequestJobTest() {}
188
189 protected:
190  virtual void SetUp() {
191    context_.set_job_factory(&factory_);
192    context_.set_network_delegate(&network_delegate_);
193    req_ = context_.CreateRequest(GURL("content://foo"),
194                                  net::DEFAULT_PRIORITY,
195                                  &url_request_delegate_,
196                                  NULL);
197    req_->set_method("GET");
198  }
199
200  void SetRange(net::URLRequest* req, int first_byte, int last_byte) {
201    net::HttpRequestHeaders headers;
202    headers.SetHeader(net::HttpRequestHeaders::kRange,
203                      net::HttpByteRange::Bounded(
204                          first_byte, last_byte).GetHeaderValue());
205    req->SetExtraRequestHeaders(headers);
206  }
207
208  void SetUpTestJob(scoped_ptr<InputStreamReader> stream_reader) {
209    SetUpTestJob(stream_reader.Pass(),
210                 make_scoped_ptr(new StreamReaderDelegate())
211                     .PassAs<AndroidStreamReaderURLRequestJob::Delegate>());
212  }
213
214  void SetUpTestJob(scoped_ptr<InputStreamReader> stream_reader,
215                    scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate>
216                        stream_reader_delegate) {
217    TestStreamReaderJob* test_stream_reader_job =
218        new TestStreamReaderJob(
219            req_.get(),
220            &network_delegate_,
221            stream_reader_delegate.Pass(),
222            stream_reader.Pass());
223    // The Interceptor is owned by the |factory_|.
224    TestJobInterceptor* protocol_handler = new TestJobInterceptor;
225    protocol_handler->set_main_intercept_job(test_stream_reader_job);
226    bool set_protocol = factory_.SetProtocolHandler("http", protocol_handler);
227    DCHECK(set_protocol);
228
229    protocol_handler = new TestJobInterceptor;
230    protocol_handler->set_main_intercept_job(test_stream_reader_job);
231    set_protocol = factory_.SetProtocolHandler("content", protocol_handler);
232    DCHECK(set_protocol);
233  }
234
235  base::MessageLoopForIO loop_;
236  TestURLRequestContext context_;
237  android_webview::AwURLRequestJobFactory factory_;
238  TestDelegate url_request_delegate_;
239  TestNetworkDelegate network_delegate_;
240  scoped_ptr<URLRequest> req_;
241};
242
243TEST_F(AndroidStreamReaderURLRequestJobTest, ReadEmptyStream) {
244  scoped_ptr<StrictMock<MockInputStreamReader> > stream_reader(
245      new StrictMock<MockInputStreamReader>());
246  {
247    InSequence s;
248    EXPECT_CALL(*stream_reader, Seek(_))
249        .WillOnce(Return(0));
250    EXPECT_CALL(*stream_reader, ReadRawData(NotNull(), Gt(0)))
251        .WillOnce(Return(0));
252  }
253
254  SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
255
256  req_->Start();
257
258  // The TestDelegate will quit the message loop on request completion.
259  base::MessageLoop::current()->Run();
260
261  EXPECT_FALSE(url_request_delegate_.request_failed());
262  EXPECT_EQ(1, network_delegate_.completed_requests());
263  EXPECT_EQ(0, network_delegate_.error_count());
264  EXPECT_EQ(200, req_->GetResponseCode());
265}
266
267TEST_F(AndroidStreamReaderURLRequestJobTest, ReadWithNullStream) {
268  SetUpTestJob(scoped_ptr<InputStreamReader>(),
269               make_scoped_ptr(new NullStreamReaderDelegate())
270                   .PassAs<AndroidStreamReaderURLRequestJob::Delegate>());
271  req_->Start();
272
273  // The TestDelegate will quit the message loop on request completion.
274  base::MessageLoop::current()->Run();
275
276  // The request_failed() method is named confusingly but all it checks is
277  // whether the request got as far as calling NotifyHeadersComplete.
278  EXPECT_FALSE(url_request_delegate_.request_failed());
279  EXPECT_EQ(1, network_delegate_.completed_requests());
280  // A null input stream shouldn't result in an error. See crbug.com/180950.
281  EXPECT_EQ(0, network_delegate_.error_count());
282  EXPECT_EQ(404, req_->GetResponseCode());
283}
284
285TEST_F(AndroidStreamReaderURLRequestJobTest, ModifyHeadersAndStatus) {
286  SetUpTestJob(scoped_ptr<InputStreamReader>(),
287               make_scoped_ptr(new HeaderAlteringStreamReaderDelegate())
288                   .PassAs<AndroidStreamReaderURLRequestJob::Delegate>());
289  req_->Start();
290
291  // The TestDelegate will quit the message loop on request completion.
292  base::MessageLoop::current()->Run();
293
294  // The request_failed() method is named confusingly but all it checks is
295  // whether the request got as far as calling NotifyHeadersComplete.
296  EXPECT_FALSE(url_request_delegate_.request_failed());
297  EXPECT_EQ(1, network_delegate_.completed_requests());
298  // A null input stream shouldn't result in an error. See crbug.com/180950.
299  EXPECT_EQ(0, network_delegate_.error_count());
300  EXPECT_EQ(HeaderAlteringStreamReaderDelegate::kResponseCode,
301            req_->GetResponseCode());
302  EXPECT_EQ(HeaderAlteringStreamReaderDelegate::kStatusLine,
303            req_->response_headers()->GetStatusLine());
304  EXPECT_TRUE(req_->response_headers()->HasHeader(
305      HeaderAlteringStreamReaderDelegate::kCustomHeaderName));
306  std::string header_value;
307  EXPECT_TRUE(req_->response_headers()->EnumerateHeader(
308      NULL, HeaderAlteringStreamReaderDelegate::kCustomHeaderName,
309      &header_value));
310  EXPECT_EQ(HeaderAlteringStreamReaderDelegate::kCustomHeaderValue,
311            header_value);
312}
313
314TEST_F(AndroidStreamReaderURLRequestJobTest, ReadPartOfStream) {
315  const int bytes_available = 128;
316  const int offset = 32;
317  const int bytes_to_read = bytes_available - offset;
318  scoped_ptr<StrictMock<MockInputStreamReader> > stream_reader(
319      new StrictMock<MockInputStreamReader>());
320  {
321    InSequence s;
322    EXPECT_CALL(*stream_reader, Seek(_))
323        .WillOnce(Return(bytes_available));
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(bytes_to_read/2));
328    EXPECT_CALL(*stream_reader, ReadRawData(NotNull(), Ge(bytes_to_read)))
329        .WillOnce(Return(0));
330  }
331
332  SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
333
334  SetRange(req_.get(), offset, bytes_available);
335  req_->Start();
336
337  base::MessageLoop::current()->Run();
338
339  EXPECT_FALSE(url_request_delegate_.request_failed());
340  EXPECT_EQ(bytes_to_read, url_request_delegate_.bytes_received());
341  EXPECT_EQ(1, network_delegate_.completed_requests());
342  EXPECT_EQ(0, network_delegate_.error_count());
343}
344
345TEST_F(AndroidStreamReaderURLRequestJobTest,
346       ReadStreamWithMoreAvailableThanActual) {
347  const int bytes_available_reported = 190;
348  const int bytes_available = 128;
349  const int offset = 0;
350  const int bytes_to_read = bytes_available - offset;
351  scoped_ptr<StrictMock<MockInputStreamReader> > stream_reader(
352      new StrictMock<MockInputStreamReader>());
353  {
354    InSequence s;
355    EXPECT_CALL(*stream_reader, Seek(_))
356        .WillOnce(Return(bytes_available_reported));
357    EXPECT_CALL(*stream_reader, ReadRawData(NotNull(), Ge(bytes_to_read)))
358        .WillOnce(Return(bytes_available));
359    EXPECT_CALL(*stream_reader, ReadRawData(NotNull(), Ge(bytes_to_read)))
360        .WillOnce(Return(0));
361  }
362
363  SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
364
365  SetRange(req_.get(), offset, bytes_available_reported);
366  req_->Start();
367
368  base::MessageLoop::current()->Run();
369
370  EXPECT_FALSE(url_request_delegate_.request_failed());
371  EXPECT_EQ(bytes_to_read, url_request_delegate_.bytes_received());
372  EXPECT_EQ(1, network_delegate_.completed_requests());
373  EXPECT_EQ(0, network_delegate_.error_count());
374}
375
376TEST_F(AndroidStreamReaderURLRequestJobTest, DeleteJobMidWaySeek) {
377  const int offset = 20;
378  const int bytes_available = 128;
379  base::RunLoop loop;
380  scoped_ptr<StrictMock<MockInputStreamReader> > stream_reader(
381      new StrictMock<MockInputStreamReader>());
382  EXPECT_CALL(*stream_reader, Seek(_))
383      .WillOnce(DoAll(InvokeWithoutArgs(&loop, &base::RunLoop::Quit),
384                      Return(bytes_available)));
385  ON_CALL(*stream_reader, ReadRawData(_, _))
386      .WillByDefault(Return(0));
387
388  SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
389
390  SetRange(req_.get(), offset, bytes_available);
391  req_->Start();
392
393  loop.Run();
394
395  EXPECT_EQ(0, network_delegate_.completed_requests());
396  req_->Cancel();
397  EXPECT_EQ(1, network_delegate_.completed_requests());
398}
399
400TEST_F(AndroidStreamReaderURLRequestJobTest, DeleteJobMidWayRead) {
401  const int offset = 20;
402  const int bytes_available = 128;
403  base::RunLoop loop;
404  scoped_ptr<StrictMock<MockInputStreamReader> > stream_reader(
405      new StrictMock<MockInputStreamReader>());
406  net::CompletionCallback read_completion_callback;
407  EXPECT_CALL(*stream_reader, Seek(_))
408      .WillOnce(Return(bytes_available));
409  EXPECT_CALL(*stream_reader, ReadRawData(_, _))
410      .WillOnce(DoAll(InvokeWithoutArgs(&loop, &base::RunLoop::Quit),
411                      Return(bytes_available)));
412
413  SetUpTestJob(stream_reader.PassAs<InputStreamReader>());
414
415  SetRange(req_.get(), offset, bytes_available);
416  req_->Start();
417
418  loop.Run();
419
420  EXPECT_EQ(0, network_delegate_.completed_requests());
421  req_->Cancel();
422  EXPECT_EQ(1, network_delegate_.completed_requests());
423}
424