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