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