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

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: 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
(Empty)
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/native/android_stream_reader_url_request_job.h"
6
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/bind.h"
10 #include "base/message_loop.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/mime_util.h"
13 #include "net/base/net_errors.h"
14 #include "net/base/net_util.h"
15 #include "net/http/http_util.h"
16 #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"
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
26 using base::android::AttachCurrentThread;
27 using base::android::ClearException;
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
36 namespace {
37
38 // Maximum number of bytes to be read in a single read.
39 const int kBufferSize = 4096;
40
41 } // namespace
42
43 bool RegisterAndroidStreamReaderUrlRequestJob(JNIEnv* env) {
44 return JNI_InputStream::RegisterNativesImpl(env);
45 }
46
47 AndroidStreamReaderURLRequestJob::AndroidStreamReaderURLRequestJob(
48 net::URLRequest* request,
49 net::NetworkDelegate* network_delegate,
50 scoped_ptr<Delegate> delegate)
51 : URLRequestJob(request, network_delegate),
52 delegate_(delegate.Pass()),
53 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
54 DCHECK(delegate_.get());
55 }
56
57 AndroidStreamReaderURLRequestJob::~AndroidStreamReaderURLRequestJob() {
58 }
59
60 void AndroidStreamReaderURLRequestJob::Start() {
61 // Start reading asynchronously so that all error reporting and data
62 // callbacks happen as they would for network requests.
63 MessageLoop::current()->PostTask(
64 FROM_HERE,
65 base::Bind(&AndroidStreamReaderURLRequestJob::StartAsync,
66 weak_factory_.GetWeakPtr()));
67 }
68
69 void AndroidStreamReaderURLRequestJob::StartAsync() {
70 JNIEnv* env = AttachCurrentThread();
71 DCHECK(env);
72
73 stream_.Reset(env, delegate_->OpenInputStream(env, request()).obj());
74 if (!stream_.obj()) {
75 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
76 net::ERR_FAILED));
77 return;
78 }
79
80 if (VerifyRequestedRange(env) && SkipToRequestedRange(env))
81 NotifyHeadersComplete();
82 }
83
84 bool AndroidStreamReaderURLRequestJob::VerifyRequestedRange(JNIEnv* env) {
85 int32_t size = Java_InputStream_available(env, stream_.obj());
86 if (ClearException(env)) {
87 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
88 net::ERR_FAILED));
89 return false;
90 }
91
92 if (size <= 0)
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 }
109
110 bool AndroidStreamReaderURLRequestJob::SkipToRequestedRange(JNIEnv* env) {
111 // Skip to the start of the requested data. This has to be done in a loop
112 // because the underlying InputStream is not guaranteed to skip the requested
113 // number of bytes.
114 if (byte_range_.IsValid() && byte_range_.first_byte_position() != 0) {
115 int64_t skipped, bytes_to_skip = byte_range_.first_byte_position();
116 do {
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 }
131 return true;
132 }
133
134 bool AndroidStreamReaderURLRequestJob::ReadRawData(net::IOBuffer* dest,
135 int dest_size,
136 int *bytes_read) {
137 DCHECK_NE(dest_size, 0);
138 DCHECK(bytes_read);
139 DCHECK(stream_.obj());
140
141 JNIEnv* env = AttachCurrentThread();
142 DCHECK(env);
143
144 if (!buffer_.obj()) {
145 // Allocate transfer buffer.
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 }
199
200 bool AndroidStreamReaderURLRequestJob::GetMimeType(
201 std::string* mime_type) const {
202 JNIEnv* env = AttachCurrentThread();
203 DCHECK(env);
204
205 return delegate_->GetMimeType(env,
206 request(),
207 stream_.obj(),
208 mime_type);
209 }
210
211 bool AndroidStreamReaderURLRequestJob::GetCharset(
212 std::string* charset) {
213 JNIEnv* env = AttachCurrentThread();
214 DCHECK(env);
215
216 return delegate_->GetCharset(env,
217 request(),
218 stream_.obj(),
219 charset);
220 }
221
222 void AndroidStreamReaderURLRequestJob::SetExtraRequestHeaders(
223 const net::HttpRequestHeaders& headers) {
224 std::string range_header;
225 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
226 // We only care about "Range" header here.
227 std::vector<net::HttpByteRange> ranges;
228 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
229 if (ranges.size() == 1) {
230 byte_range_ = ranges[0];
231 } else {
232 // We don't support multiple range requests in one single URL request,
233 // because we need to do multipart encoding here.
234 NotifyDone(net::URLRequestStatus(
235 net::URLRequestStatus::FAILED,
236 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
237 }
238 }
239 }
240 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698