Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: android_webview/native/android_stream_reader_url_request_job.cc

Issue 11363123: [android_webview] Don't block the IO thread when reading from an InputStream. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix clang error Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "android_webview/native/android_stream_reader_url_request_job.h" 5 #include "android_webview/native/android_stream_reader_url_request_job.h"
6 6
7 #include "android_webview/native/input_stream.h"
8 #include "android_webview/native/input_stream_reader.h"
joth 2012/11/07 16:55:38 hmm interesting... if input_stream_reader could be
mkosiba (inactive) 2012/11/15 19:18:50 yup, it's just me being lazy. I'll do the move.
7 #include "base/android/jni_android.h" 9 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h" 10 #include "base/android/jni_string.h"
9 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/lazy_instance.h"
10 #include "base/message_loop.h" 14 #include "base/message_loop.h"
15 #include "base/threading/thread.h"
16 #include "content/public/browser/browser_thread.h"
11 #include "net/base/io_buffer.h" 17 #include "net/base/io_buffer.h"
12 #include "net/base/mime_util.h" 18 #include "net/base/mime_util.h"
13 #include "net/base/net_errors.h" 19 #include "net/base/net_errors.h"
14 #include "net/base/net_util.h" 20 #include "net/base/net_util.h"
15 #include "net/http/http_util.h" 21 #include "net/http/http_util.h"
16 #include "net/url_request/url_request.h" 22 #include "net/url_request/url_request.h"
17 #include "net/url_request/url_request_error_job.h"
18 #include "net/url_request/url_request_file_job.h"
19 #include "net/url_request/url_request_job_manager.h" 23 #include "net/url_request/url_request_job_manager.h"
20 // Disable "Warnings treated as errors" for input_stream_jni as it's a Java
21 // system class and we have to generate C++ hooks for all methods in the class
22 // even if they're unused.
23 #pragma GCC diagnostic ignored "-Wunused-function"
24 #include "jni/InputStream_jni.h"
25 24
25 using android_webview::InputStream;
26 using android_webview::InputStreamReader;
26 using base::android::AttachCurrentThread; 27 using base::android::AttachCurrentThread;
27 using base::android::ClearException; 28 using content::BrowserThread;
28 using base::android::ConvertUTF8ToJavaString;
29 using base::android::ScopedJavaGlobalRef;
30 using base::android::ScopedJavaLocalRef;
31 using JNI_InputStream::Java_InputStream_available;
32 using JNI_InputStream::Java_InputStream_skip;
33 using JNI_InputStream::Java_InputStream_readI_AB_I_I;
34
35 29
joth 2012/11/07 16:55:38 would be helpful to have a block comment explainin
mkosiba (inactive) 2012/11/15 19:18:50 simplified this significantly - you still think on
36 namespace { 30 namespace {
37 31
38 // Maximum number of bytes to be read in a single read. 32 class WorkerThread : public base::Thread {
39 const int kBufferSize = 4096; 33 public:
34 WorkerThread() : base::Thread("AndroidStreamReaderWorkerThread") {
joth 2012/11/07 16:55:38 nit: Worker seems spurious: AndroidStreamReaderThr
mkosiba (inactive) 2012/11/15 19:18:50 Done.
35 }
36 };
37
38 static base::LazyInstance<WorkerThread> g_worker_thread =
39 LAZY_INSTANCE_INITIALIZER;
joth 2012/11/07 16:55:38 I'm not thrilled with another thread hanging out a
mkosiba (inactive) 2012/11/15 19:18:50 I didn't know about the PostBlockingPoolTask metho
40 40
41 } // namespace 41 } // namespace
42 42
43 bool RegisterAndroidStreamReaderUrlRequestJob(JNIEnv* env) {
44 return JNI_InputStream::RegisterNativesImpl(env);
45 }
46
47 AndroidStreamReaderURLRequestJob::AndroidStreamReaderURLRequestJob( 43 AndroidStreamReaderURLRequestJob::AndroidStreamReaderURLRequestJob(
48 net::URLRequest* request, 44 net::URLRequest* request,
49 net::NetworkDelegate* network_delegate, 45 net::NetworkDelegate* network_delegate,
50 scoped_ptr<Delegate> delegate) 46 scoped_ptr<Delegate> delegate)
51 : URLRequestJob(request, network_delegate), 47 : URLRequestJob(request, network_delegate),
52 delegate_(delegate.Pass()), 48 delegate_(delegate.Pass()),
49 input_stream_reader_(NULL),
53 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { 50 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
54 DCHECK(delegate_.get()); 51 DCHECK(delegate_.get());
55 } 52 }
56 53
57 AndroidStreamReaderURLRequestJob::~AndroidStreamReaderURLRequestJob() { 54 AndroidStreamReaderURLRequestJob::~AndroidStreamReaderURLRequestJob() {
55 if (input_stream_reader_ != NULL) {
56 PostToWorkerThread(
57 FROM_HERE,
58 base::Bind(&base::DeletePointer<InputStreamReader>,
59 input_stream_reader_));
60 }
58 } 61 }
59 62
60 void AndroidStreamReaderURLRequestJob::Start() { 63 void AndroidStreamReaderURLRequestJob::Start() {
61 // Start reading asynchronously so that all error reporting and data 64 // Start reading asynchronously so that all error reporting and data
62 // callbacks happen as they would for network requests. 65 // callbacks happen as they would for network requests.
66 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
63 MessageLoop::current()->PostTask( 67 MessageLoop::current()->PostTask(
64 FROM_HERE, 68 FROM_HERE,
65 base::Bind(&AndroidStreamReaderURLRequestJob::StartAsync, 69 base::Bind(
66 weak_factory_.GetWeakPtr())); 70 &AndroidStreamReaderURLRequestJob::StartAsync,
71 weak_factory_.GetWeakPtr()));
72 }
73
74 void AndroidStreamReaderURLRequestJob::Kill() {
75 weak_factory_.InvalidateWeakPtrs();
76 URLRequestJob::Kill();
77 }
78
79 InputStreamReader* AndroidStreamReaderURLRequestJob::CreateStreamReader(
80 InputStream* stream) {
81 return new InputStreamReader(stream);
67 } 82 }
68 83
69 void AndroidStreamReaderURLRequestJob::StartAsync() { 84 void AndroidStreamReaderURLRequestJob::StartAsync() {
70 JNIEnv* env = AttachCurrentThread(); 85 JNIEnv* env = AttachCurrentThread();
71 DCHECK(env); 86 DCHECK(env);
72 87
73 stream_.Reset(env, delegate_->OpenInputStream(env, request()).obj()); 88 // TODO: This could be done in the InputStreamReader but would force more
joth 2012/11/07 16:55:38 is this really a TODO then? reads like a conscious
mkosiba (inactive) 2012/11/15 19:18:50 ok, removed TODO.
74 if (!stream_.obj()) { 89 // complex synchronization in the delegate.
75 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, 90 stream_ = delegate_->OpenInputStream(env, request());
76 net::ERR_FAILED)); 91 if (!stream_) {
92 NotifyDone(
93 net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED));
77 return; 94 return;
78 } 95 }
79 96
80 if (VerifyRequestedRange(env) && SkipToRequestedRange(env)) 97 DCHECK(!input_stream_reader_);
81 NotifyHeadersComplete(); 98 input_stream_reader_ = CreateStreamReader(stream_.get());
99 CHECK(input_stream_reader_);
joth 2012/11/07 16:55:38 nit dcheck
mkosiba (inactive) 2012/11/15 19:18:50 I wanted a CHECK since we'll end up crashing anywa
100
101 PostToWorkerThread(
102 FROM_HERE,
103 base::Bind(&InputStreamReader::Seek,
104 base::Unretained(input_stream_reader_),
105 byte_range_,
106 base::Bind(
107 &AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted,
108 weak_factory_.GetWeakPtr())));
82 } 109 }
83 110
84 bool AndroidStreamReaderURLRequestJob::VerifyRequestedRange(JNIEnv* env) { 111 void AndroidStreamReaderURLRequestJob::OnReaderSeekCompleted(
85 int32_t size = Java_InputStream_available(env, stream_.obj()); 112 int result) {
86 if (ClearException(env)) { 113 // Clear the IO_PENDING status
114 SetStatus(net::URLRequestStatus());
115 if (result >= 0) {
116 set_expected_content_size(result);
117 NotifyHeadersComplete();
118 } else {
119 // The InputStreamReader deletes itself on error.
120 input_stream_reader_ = NULL;
121
87 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, 122 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
88 net::ERR_FAILED)); 123 result));
89 return false; 124 }
125 }
126
127 void AndroidStreamReaderURLRequestJob::OnReaderReadCompleted(
benm (inactive) 2012/11/15 17:39:01 Please add a comment to explain the differences be
128 int result) {
129 if (result > 0) {
130 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
131 } else if (result == 0) {
132 NotifyDone(net::URLRequestStatus());
133 } else {
134 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
135 result));
90 } 136 }
91 137
92 if (size <= 0) 138 NotifyReadComplete(result);
93 return true;
94
95 // Check that the requested range was valid.
96 if (!byte_range_.ComputeBounds(size)) {
97 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
98 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
99 return false;
100 }
101
102 size = byte_range_.last_byte_position() -
103 byte_range_.first_byte_position() + 1;
104 DCHECK_GE(size, 0);
105 set_expected_content_size(size);
106
107 return true;
108 } 139 }
109 140
110 bool AndroidStreamReaderURLRequestJob::SkipToRequestedRange(JNIEnv* env) { 141 void AndroidStreamReaderURLRequestJob::PostToWorkerThread(
111 // Skip to the start of the requested data. This has to be done in a loop 142 const tracked_objects::Location& from_here,
112 // because the underlying InputStream is not guaranteed to skip the requested 143 const base::Closure& task) {
113 // number of bytes. 144 if(!g_worker_thread.Get().message_loop()) {
114 if (byte_range_.IsValid() && byte_range_.first_byte_position() != 0) { 145 bool thread_created = g_worker_thread.Get().StartWithOptions(
115 int64_t skipped, bytes_to_skip = byte_range_.first_byte_position(); 146 base::Thread::Options(MessageLoop::TYPE_IO, 0));
116 do { 147 CHECK(thread_created);
benm (inactive) 2012/11/15 17:39:01 DCHECK?
mkosiba (inactive) 2012/11/15 19:18:50 removed the code.
117 skipped = Java_InputStream_skip(env, stream_.obj(), bytes_to_skip);
118 if (ClearException(env)) {
119 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
120 net::ERR_FAILED));
121 return false;
122 }
123 if (skipped <= 0) {
124 NotifyDone(
125 net::URLRequestStatus(net::URLRequestStatus::FAILED,
126 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
127 return false;
128 }
129 } while ((bytes_to_skip -= skipped) > 0);
130 } 148 }
131 return true; 149 g_worker_thread.Get().message_loop()->PostTask(from_here, task);
132 } 150 }
133 151
134 bool AndroidStreamReaderURLRequestJob::ReadRawData(net::IOBuffer* dest, 152 bool AndroidStreamReaderURLRequestJob::ReadRawData(net::IOBuffer* dest,
135 int dest_size, 153 int dest_size,
136 int *bytes_read) { 154 int *bytes_read) {
137 DCHECK_NE(dest_size, 0); 155 DCHECK(input_stream_reader_);
138 DCHECK(bytes_read);
139 DCHECK(stream_.obj());
140 156
141 JNIEnv* env = AttachCurrentThread(); 157 PostToWorkerThread(
142 DCHECK(env); 158 FROM_HERE,
159 base::Bind(&InputStreamReader::ReadRawData,
160 base::Unretained(input_stream_reader_),
161 base::Unretained(dest),
162 dest_size,
163 base::Bind(
164 &AndroidStreamReaderURLRequestJob::OnReaderReadCompleted,
165 weak_factory_.GetWeakPtr())));
143 166
144 if (!buffer_.obj()) { 167 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
145 // Allocate transfer buffer. 168 return false;
146 buffer_.Reset(env, env->NewByteArray(kBufferSize));
147 if (ClearException(env)) {
148 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
149 net::ERR_FAILED));
150 return false;
151 }
152 }
153
154 jbyteArray buffer = buffer_.obj();
155 *bytes_read = 0;
156 if (!dest_size)
157 return true;
158
159 // Read data in multiples of the buffer size.
160 while (dest_size > 0) {
161 int read_size = std::min(dest_size, kBufferSize);
162 // TODO(skyostil): Make this non-blocking
163 int32_t byte_count =
164 Java_InputStream_readI_AB_I_I(env, stream_.obj(), buffer, 0, read_size);
165 if (byte_count <= 0) {
166 // net::URLRequestJob will call NotifyDone for us after the end of the
167 // file is reached.
168 break;
169 }
170
171 if (ClearException(env)) {
172 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
173 net::ERR_FAILED));
174 return false;
175 }
176
177 #ifndef NDEBUG
178 int32_t buffer_length = env->GetArrayLength(buffer);
179 DCHECK_GE(read_size, byte_count);
180 DCHECK_GE(buffer_length, byte_count);
181 #endif // NDEBUG
182
183 // Copy the data over to the provided C++ side buffer.
184 DCHECK_GE(dest_size, byte_count);
185 env->GetByteArrayRegion(buffer, 0, byte_count,
186 reinterpret_cast<jbyte*>(dest->data() + *bytes_read));
187
188 if (ClearException(env)) {
189 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
190 net::ERR_FAILED));
191 return false;
192 }
193
194 *bytes_read += byte_count;
195 dest_size -= byte_count;
196 }
197 return true;
198 } 169 }
199 170
200 bool AndroidStreamReaderURLRequestJob::GetMimeType( 171 bool AndroidStreamReaderURLRequestJob::GetMimeType(
201 std::string* mime_type) const { 172 std::string* mime_type) const {
202 JNIEnv* env = AttachCurrentThread(); 173 JNIEnv* env = AttachCurrentThread();
203 DCHECK(env); 174 DCHECK(env);
204 175
176 if (!stream_)
177 return false;
178
205 return delegate_->GetMimeType(env, 179 return delegate_->GetMimeType(env,
206 request(), 180 request(),
207 stream_.obj(), 181 *stream_,
208 mime_type); 182 mime_type);
209 } 183 }
210 184
211 bool AndroidStreamReaderURLRequestJob::GetCharset( 185 bool AndroidStreamReaderURLRequestJob::GetCharset(
212 std::string* charset) { 186 std::string* charset) {
213 JNIEnv* env = AttachCurrentThread(); 187 JNIEnv* env = AttachCurrentThread();
214 DCHECK(env); 188 DCHECK(env);
215 189
190 if (!stream_)
191 return false;
192
216 return delegate_->GetCharset(env, 193 return delegate_->GetCharset(env,
217 request(), 194 request(),
218 stream_.obj(), 195 *stream_,
219 charset); 196 charset);
220 } 197 }
221 198
222 void AndroidStreamReaderURLRequestJob::SetExtraRequestHeaders( 199 void AndroidStreamReaderURLRequestJob::SetExtraRequestHeaders(
223 const net::HttpRequestHeaders& headers) { 200 const net::HttpRequestHeaders& headers) {
224 std::string range_header; 201 std::string range_header;
225 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { 202 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
226 // We only care about "Range" header here. 203 // We only care about "Range" header here.
227 std::vector<net::HttpByteRange> ranges; 204 std::vector<net::HttpByteRange> ranges;
228 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { 205 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
229 if (ranges.size() == 1) { 206 if (ranges.size() == 1) {
230 byte_range_ = ranges[0]; 207 byte_range_ = ranges[0];
231 } else { 208 } else {
232 // We don't support multiple range requests in one single URL request, 209 // We don't support multiple range requests in one single URL request,
233 // because we need to do multipart encoding here. 210 // because we need to do multipart encoding here.
234 NotifyDone(net::URLRequestStatus( 211 NotifyDone(net::URLRequestStatus(
235 net::URLRequestStatus::FAILED, 212 net::URLRequestStatus::FAILED,
236 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); 213 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
237 } 214 }
238 } 215 }
239 } 216 }
240 } 217 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698