OLD | NEW |
---|---|
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 } |
OLD | NEW |