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/net/android_stream_reader_url_request_job.h" 6 7#include <string> 8 9#include "android_webview/browser/input_stream.h" 10#include "android_webview/browser/net/input_stream_reader.h" 11#include "base/android/jni_android.h" 12#include "base/android/jni_string.h" 13#include "base/bind.h" 14#include "base/bind_helpers.h" 15#include "base/lazy_instance.h" 16#include "base/message_loop/message_loop.h" 17#include "base/message_loop/message_loop_proxy.h" 18#include "base/strings/string_number_conversions.h" 19#include "base/task_runner.h" 20#include "base/threading/sequenced_worker_pool.h" 21#include "base/threading/thread.h" 22#include "content/public/browser/browser_thread.h" 23#include "net/base/io_buffer.h" 24#include "net/base/mime_util.h" 25#include "net/base/net_errors.h" 26#include "net/base/net_util.h" 27#include "net/http/http_response_headers.h" 28#include "net/http/http_response_info.h" 29#include "net/http/http_util.h" 30#include "net/url_request/url_request.h" 31#include "net/url_request/url_request_job_manager.h" 32 33using android_webview::InputStream; 34using android_webview::InputStreamReader; 35using base::android::AttachCurrentThread; 36using base::PostTaskAndReplyWithResult; 37using content::BrowserThread; 38 39namespace { 40 41const int kHTTPOk = 200; 42const int kHTTPNotFound = 404; 43 44const char kResponseHeaderViaShouldInterceptRequest[] = 45 "Client-Via: shouldInterceptRequest"; 46const char kHTTPOkText[] = "OK"; 47const char kHTTPNotFoundText[] = "Not Found"; 48 49} // namespace 50 51// The requests posted to the worker thread might outlive the job. Thread-safe 52// ref counting is used to ensure that the InputStream and InputStreamReader 53// members of this class are still there when the closure is run on the worker 54// thread. 55class InputStreamReaderWrapper : 56 public base::RefCountedThreadSafe<InputStreamReaderWrapper> { 57 public: 58 InputStreamReaderWrapper( 59 scoped_ptr<InputStream> input_stream, 60 scoped_ptr<InputStreamReader> input_stream_reader) 61 : input_stream_(input_stream.Pass()), 62 input_stream_reader_(input_stream_reader.Pass()) { 63 DCHECK(input_stream_); 64 DCHECK(input_stream_reader_); 65 } 66 67 android_webview::InputStream* input_stream() { 68 return input_stream_.get(); 69 } 70 71 int Seek(const net::HttpByteRange& byte_range) { 72 return input_stream_reader_->Seek(byte_range); 73 } 74 75 int ReadRawData(net::IOBuffer* buffer, int buffer_size) { 76 return input_stream_reader_->ReadRawData(buffer, buffer_size); 77 } 78 79 private: 80 friend class base::RefCountedThreadSafe<InputStreamReaderWrapper>; 81 ~InputStreamReaderWrapper() {} 82 83 scoped_ptr<android_webview::InputStream> input_stream_; 84 scoped_ptr<android_webview::InputStreamReader> input_stream_reader_; 85 86 DISALLOW_COPY_AND_ASSIGN(InputStreamReaderWrapper); 87}; 88 89AndroidStreamReaderURLRequestJob::AndroidStreamReaderURLRequestJob( 90 net::URLRequest* request, 91 net::NetworkDelegate* network_delegate, 92 scoped_ptr<Delegate> delegate) 93 : URLRequestJob(request, network_delegate), 94 delegate_(delegate.Pass()), 95 weak_factory_(this) { 96 DCHECK(delegate_); 97} 98 99AndroidStreamReaderURLRequestJob::~AndroidStreamReaderURLRequestJob() { 100} 101 102namespace { 103 104typedef base::Callback< 105 void(scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate>, 106 scoped_ptr<InputStream>)> OnInputStreamOpenedCallback; 107 108// static 109void OpenInputStreamOnWorkerThread( 110 scoped_refptr<base::MessageLoopProxy> job_thread_proxy, 111 scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate> delegate, 112 const GURL& url, 113 OnInputStreamOpenedCallback callback) { 114 115 JNIEnv* env = AttachCurrentThread(); 116 DCHECK(env); 117 118 scoped_ptr<InputStream> input_stream = delegate->OpenInputStream(env, url); 119 job_thread_proxy->PostTask(FROM_HERE, 120 base::Bind(callback, 121 base::Passed(delegate.Pass()), 122 base::Passed(input_stream.Pass()))); 123} 124 125} // namespace 126 127void AndroidStreamReaderURLRequestJob::Start() { 128 DCHECK(thread_checker_.CalledOnValidThread()); 129 // Start reading asynchronously so that all error reporting and data 130 // callbacks happen as they would for network requests. 131 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 132 net::ERR_IO_PENDING)); 133 134 // This could be done in the InputStreamReader but would force more 135 // complex synchronization in the delegate. 136 GetWorkerThreadRunner()->PostTask( 137 FROM_HERE, 138 base::Bind( 139 &OpenInputStreamOnWorkerThread, 140 base::MessageLoop::current()->message_loop_proxy(), 141 // This is intentional - the job could be deleted while the callback 142 // is executing on the background thread. 143 // The delegate will be "returned" to the job once the InputStream 144 // open attempt is completed. 145 base::Passed(&delegate_), 146 request()->url(), 147 base::Bind(&AndroidStreamReaderURLRequestJob::OnInputStreamOpened, 148 weak_factory_.GetWeakPtr()))); 149} 150 151void AndroidStreamReaderURLRequestJob::Kill() { 152 DCHECK(thread_checker_.CalledOnValidThread()); 153 weak_factory_.InvalidateWeakPtrs(); 154 URLRequestJob::Kill(); 155} 156 157scoped_ptr<InputStreamReader> 158AndroidStreamReaderURLRequestJob::CreateStreamReader(InputStream* stream) { 159 return make_scoped_ptr(new InputStreamReader(stream)); 160} 161 162void AndroidStreamReaderURLRequestJob::OnInputStreamOpened( 163 scoped_ptr<Delegate> returned_delegate, 164 scoped_ptr<android_webview::InputStream> input_stream) { 165 DCHECK(thread_checker_.CalledOnValidThread()); 166 DCHECK(returned_delegate); 167 delegate_ = returned_delegate.Pass(); 168 169 if (!input_stream) { 170 bool restart_required = false; 171 delegate_->OnInputStreamOpenFailed(request(), &restart_required); 172 if (restart_required) { 173 NotifyRestartRequired(); 174 } else { 175 // Clear the IO_PENDING status set in Start(). 176 SetStatus(net::URLRequestStatus()); 177 HeadersComplete(kHTTPNotFound, kHTTPNotFoundText); 178 } 179 return; 180 } 181 182 scoped_ptr<InputStreamReader> input_stream_reader( 183 CreateStreamReader(input_stream.get())); 184 DCHECK(input_stream_reader); 185 186 DCHECK(!input_stream_reader_wrapper_.get()); 187 input_stream_reader_wrapper_ = new InputStreamReaderWrapper( 188 input_stream.Pass(), input_stream_reader.Pass()); 189 190 PostTaskAndReplyWithResult( 191 GetWorkerThreadRunner(), 192 FROM_HERE, 193 base::Bind(&InputStreamReaderWrapper::Seek, 194 input_stream_reader_wrapper_, 195 byte_range_), 196 base::Bind(&AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted, 197 weak_factory_.GetWeakPtr())); 198} 199 200void AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted(int result) { 201 DCHECK(thread_checker_.CalledOnValidThread()); 202 // Clear the IO_PENDING status set in Start(). 203 SetStatus(net::URLRequestStatus()); 204 if (result >= 0) { 205 set_expected_content_size(result); 206 HeadersComplete(kHTTPOk, kHTTPOkText); 207 } else { 208 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result)); 209 } 210} 211 212void AndroidStreamReaderURLRequestJob::OnReaderReadCompleted(int result) { 213 DCHECK(thread_checker_.CalledOnValidThread()); 214 // The URLRequest API contract requires that: 215 // * NotifyDone be called once, to set the status code, indicate the job is 216 // finished (there will be no further IO), 217 // * NotifyReadComplete be called if false is returned from ReadRawData to 218 // indicate that the IOBuffer will not be used by the job anymore. 219 // There might be multiple calls to ReadRawData (and thus multiple calls to 220 // NotifyReadComplete), which is why NotifyDone is called only on errors 221 // (result < 0) and end of data (result == 0). 222 if (result == 0) { 223 NotifyDone(net::URLRequestStatus()); 224 } else if (result < 0) { 225 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result)); 226 } else { 227 // Clear the IO_PENDING status. 228 SetStatus(net::URLRequestStatus()); 229 } 230 NotifyReadComplete(result); 231} 232 233base::TaskRunner* AndroidStreamReaderURLRequestJob::GetWorkerThreadRunner() { 234 return static_cast<base::TaskRunner*>(BrowserThread::GetBlockingPool()); 235} 236 237bool AndroidStreamReaderURLRequestJob::ReadRawData(net::IOBuffer* dest, 238 int dest_size, 239 int* bytes_read) { 240 DCHECK(thread_checker_.CalledOnValidThread()); 241 if (!input_stream_reader_wrapper_.get()) { 242 // This will happen if opening the InputStream fails in which case the 243 // error is communicated by setting the HTTP response status header rather 244 // than failing the request during the header fetch phase. 245 *bytes_read = 0; 246 return true; 247 } 248 249 PostTaskAndReplyWithResult( 250 GetWorkerThreadRunner(), 251 FROM_HERE, 252 base::Bind(&InputStreamReaderWrapper::ReadRawData, 253 input_stream_reader_wrapper_, 254 make_scoped_refptr(dest), 255 dest_size), 256 base::Bind(&AndroidStreamReaderURLRequestJob::OnReaderReadCompleted, 257 weak_factory_.GetWeakPtr())); 258 259 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 260 net::ERR_IO_PENDING)); 261 return false; 262} 263 264bool AndroidStreamReaderURLRequestJob::GetMimeType( 265 std::string* mime_type) const { 266 DCHECK(thread_checker_.CalledOnValidThread()); 267 JNIEnv* env = AttachCurrentThread(); 268 DCHECK(env); 269 270 if (!input_stream_reader_wrapper_.get()) 271 return false; 272 273 // Since it's possible for this call to alter the InputStream a 274 // Seek or ReadRawData operation running in the background is not permitted. 275 DCHECK(!request_->status().is_io_pending()); 276 277 return delegate_->GetMimeType( 278 env, request(), input_stream_reader_wrapper_->input_stream(), mime_type); 279} 280 281bool AndroidStreamReaderURLRequestJob::GetCharset(std::string* charset) { 282 DCHECK(thread_checker_.CalledOnValidThread()); 283 JNIEnv* env = AttachCurrentThread(); 284 DCHECK(env); 285 286 if (!input_stream_reader_wrapper_.get()) 287 return false; 288 289 // Since it's possible for this call to alter the InputStream a 290 // Seek or ReadRawData operation running in the background is not permitted. 291 DCHECK(!request_->status().is_io_pending()); 292 293 return delegate_->GetCharset( 294 env, request(), input_stream_reader_wrapper_->input_stream(), charset); 295} 296 297void AndroidStreamReaderURLRequestJob::HeadersComplete( 298 int status_code, 299 const std::string& status_text) { 300 std::string status("HTTP/1.1 "); 301 status.append(base::IntToString(status_code)); 302 status.append(" "); 303 status.append(status_text); 304 // HttpResponseHeaders expects its input string to be terminated by two NULs. 305 status.append("\0\0", 2); 306 net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status); 307 308 if (status_code == kHTTPOk) { 309 if (expected_content_size() != -1) { 310 std::string content_length_header( 311 net::HttpRequestHeaders::kContentLength); 312 content_length_header.append(": "); 313 content_length_header.append( 314 base::Int64ToString(expected_content_size())); 315 headers->AddHeader(content_length_header); 316 } 317 318 std::string mime_type; 319 if (GetMimeType(&mime_type) && !mime_type.empty()) { 320 std::string content_type_header(net::HttpRequestHeaders::kContentType); 321 content_type_header.append(": "); 322 content_type_header.append(mime_type); 323 headers->AddHeader(content_type_header); 324 } 325 } 326 327 JNIEnv* env = AttachCurrentThread(); 328 DCHECK(env); 329 delegate_->AppendResponseHeaders(env, headers); 330 331 // Indicate that the response had been obtained via shouldInterceptRequest. 332 headers->AddHeader(kResponseHeaderViaShouldInterceptRequest); 333 334 response_info_.reset(new net::HttpResponseInfo()); 335 response_info_->headers = headers; 336 337 NotifyHeadersComplete(); 338} 339 340int AndroidStreamReaderURLRequestJob::GetResponseCode() const { 341 if (response_info_) 342 return response_info_->headers->response_code(); 343 return URLRequestJob::GetResponseCode(); 344} 345 346void AndroidStreamReaderURLRequestJob::GetResponseInfo( 347 net::HttpResponseInfo* info) { 348 if (response_info_) 349 *info = *response_info_; 350} 351 352void AndroidStreamReaderURLRequestJob::SetExtraRequestHeaders( 353 const net::HttpRequestHeaders& headers) { 354 std::string range_header; 355 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { 356 // We only extract the "Range" header so that we know how many bytes in the 357 // stream to skip and how many to read after that. 358 std::vector<net::HttpByteRange> ranges; 359 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { 360 if (ranges.size() == 1) { 361 byte_range_ = ranges[0]; 362 } else { 363 // We don't support multiple range requests in one single URL request, 364 // because we need to do multipart encoding here. 365 NotifyDone(net::URLRequestStatus( 366 net::URLRequestStatus::FAILED, 367 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 368 } 369 } 370 } 371} 372