Roll src/third_party/WebKit aa8346d:dbb8a38 (svn 202629:202630)
[chromium-blink-merge.git] / components / cronet / android / cronet_url_request_adapter.cc
blob14115e07d9d720799a2e1b438b6ca15f0b4b0e09
1 // Copyright 2014 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.
5 #include "cronet_url_request_adapter.h"
7 #include <limits>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "components/cronet/android/cronet_url_request_context_adapter.h"
13 #include "jni/CronetUrlRequest_jni.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/load_flags.h"
16 #include "net/base/net_errors.h"
17 #include "net/base/request_priority.h"
18 #include "net/cert/cert_status_flags.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/http/http_status_code.h"
21 #include "net/http/http_util.h"
22 #include "net/ssl/ssl_info.h"
23 #include "net/url_request/redirect_info.h"
24 #include "net/url_request/url_request_context.h"
26 using base::android::ConvertUTF8ToJavaString;
28 namespace cronet {
30 // Explicitly register static JNI functions.
31 bool CronetUrlRequestAdapterRegisterJni(JNIEnv* env) {
32 return RegisterNativesImpl(env);
35 static jlong CreateRequestAdapter(JNIEnv* env,
36 const JavaParamRef<jobject>& jurl_request,
37 jlong jurl_request_context_adapter,
38 const JavaParamRef<jstring>& jurl_string,
39 jint jpriority) {
40 CronetURLRequestContextAdapter* context_adapter =
41 reinterpret_cast<CronetURLRequestContextAdapter*>(
42 jurl_request_context_adapter);
43 DCHECK(context_adapter);
45 GURL url(base::android::ConvertJavaStringToUTF8(env, jurl_string));
47 VLOG(1) << "New chromium network request_adapter: "
48 << url.possibly_invalid_spec();
50 CronetURLRequestAdapter* adapter =
51 new CronetURLRequestAdapter(context_adapter, env, jurl_request, url,
52 static_cast<net::RequestPriority>(jpriority));
54 return reinterpret_cast<jlong>(adapter);
57 // net::WrappedIOBuffer subclass for a buffer owned by a Java ByteBuffer. Keeps
58 // the ByteBuffer alive until destroyed. Uses WrappedIOBuffer because data() is
59 // owned by the embedder.
60 class CronetURLRequestAdapter::IOBufferWithByteBuffer
61 : public net::WrappedIOBuffer {
62 public:
63 // Creates a buffer wrapping the Java ByteBuffer |jbyte_buffer|. |data| points
64 // to the memory backed by the ByteBuffer, and position is the location to
65 // start writing.
66 IOBufferWithByteBuffer(
67 JNIEnv* env,
68 jobject jbyte_buffer,
69 void* data,
70 int position)
71 : net::WrappedIOBuffer(static_cast<char*>(data) + position),
72 initial_position_(position) {
73 DCHECK(data);
74 DCHECK_EQ(env->GetDirectBufferAddress(jbyte_buffer), data);
75 byte_buffer_.Reset(env, jbyte_buffer);
78 int initial_position() const { return initial_position_; }
80 jobject byte_buffer() const { return byte_buffer_.obj(); }
82 private:
83 ~IOBufferWithByteBuffer() override {}
85 base::android::ScopedJavaGlobalRef<jobject> byte_buffer_;
87 const int initial_position_;
90 CronetURLRequestAdapter::CronetURLRequestAdapter(
91 CronetURLRequestContextAdapter* context,
92 JNIEnv* env,
93 jobject jurl_request,
94 const GURL& url,
95 net::RequestPriority priority)
96 : context_(context),
97 initial_url_(url),
98 initial_priority_(priority),
99 initial_method_("GET"),
100 load_flags_(context->default_load_flags()) {
101 DCHECK(!context_->IsOnNetworkThread());
102 owner_.Reset(env, jurl_request);
105 CronetURLRequestAdapter::~CronetURLRequestAdapter() {
106 DCHECK(context_->IsOnNetworkThread());
109 jboolean CronetURLRequestAdapter::SetHttpMethod(JNIEnv* env,
110 jobject jcaller,
111 jstring jmethod) {
112 DCHECK(!context_->IsOnNetworkThread());
113 std::string method(base::android::ConvertJavaStringToUTF8(env, jmethod));
114 // Http method is a token, just as header name.
115 if (!net::HttpUtil::IsValidHeaderName(method))
116 return JNI_FALSE;
117 initial_method_ = method;
118 return JNI_TRUE;
121 jboolean CronetURLRequestAdapter::AddRequestHeader(JNIEnv* env,
122 jobject jcaller,
123 jstring jname,
124 jstring jvalue) {
125 DCHECK(!context_->IsOnNetworkThread());
126 std::string name(base::android::ConvertJavaStringToUTF8(env, jname));
127 std::string value(base::android::ConvertJavaStringToUTF8(env, jvalue));
128 if (!net::HttpUtil::IsValidHeaderName(name) ||
129 !net::HttpUtil::IsValidHeaderValue(value)) {
130 return JNI_FALSE;
132 initial_request_headers_.SetHeader(name, value);
133 return JNI_TRUE;
136 void CronetURLRequestAdapter::DisableCache(JNIEnv* env, jobject jcaller) {
137 DCHECK(!context_->IsOnNetworkThread());
138 load_flags_ |= net::LOAD_DISABLE_CACHE;
141 void CronetURLRequestAdapter::SetUpload(
142 scoped_ptr<net::UploadDataStream> upload) {
143 DCHECK(!context_->IsOnNetworkThread());
144 DCHECK(!upload_);
145 upload_ = upload.Pass();
148 void CronetURLRequestAdapter::Start(JNIEnv* env, jobject jcaller) {
149 DCHECK(!context_->IsOnNetworkThread());
150 context_->PostTaskToNetworkThread(
151 FROM_HERE, base::Bind(&CronetURLRequestAdapter::StartOnNetworkThread,
152 base::Unretained(this)));
155 void CronetURLRequestAdapter::GetStatus(JNIEnv* env,
156 jobject jcaller,
157 jobject jstatus_listener) const {
158 DCHECK(!context_->IsOnNetworkThread());
159 base::android::ScopedJavaGlobalRef<jobject> status_listener_ref;
160 status_listener_ref.Reset(env, jstatus_listener);
161 context_->PostTaskToNetworkThread(
162 FROM_HERE, base::Bind(&CronetURLRequestAdapter::GetStatusOnNetworkThread,
163 base::Unretained(this), status_listener_ref));
166 void CronetURLRequestAdapter::FollowDeferredRedirect(JNIEnv* env,
167 jobject jcaller) {
168 DCHECK(!context_->IsOnNetworkThread());
169 context_->PostTaskToNetworkThread(
170 FROM_HERE,
171 base::Bind(
172 &CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread,
173 base::Unretained(this)));
176 jboolean CronetURLRequestAdapter::ReadData(
177 JNIEnv* env, jobject jcaller, jobject jbyte_buffer, jint jposition,
178 jint jcapacity) {
179 DCHECK(!context_->IsOnNetworkThread());
180 DCHECK_LT(jposition, jcapacity);
182 void* data = env->GetDirectBufferAddress(jbyte_buffer);
183 if (!data)
184 return JNI_FALSE;
186 scoped_refptr<IOBufferWithByteBuffer> read_buffer(
187 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition));
189 int remaining_capacity = jcapacity - jposition;
191 context_->PostTaskToNetworkThread(
192 FROM_HERE, base::Bind(&CronetURLRequestAdapter::ReadDataOnNetworkThread,
193 base::Unretained(this),
194 read_buffer,
195 remaining_capacity));
196 return JNI_TRUE;
199 void CronetURLRequestAdapter::Destroy(JNIEnv* env, jobject jcaller) {
200 // Destroy could be called from any thread, including network thread (if
201 // posting task to executor throws an exception), but is posted, so |this|
202 // is valid until calling task is complete. Destroy() is always called from
203 // within a synchronized java block that guarantees no future posts to the
204 // network thread with the adapter pointer.
205 context_->PostTaskToNetworkThread(
206 FROM_HERE, base::Bind(&CronetURLRequestAdapter::DestroyOnNetworkThread,
207 base::Unretained(this)));
210 void CronetURLRequestAdapter::PopulateResponseHeaders(JNIEnv* env,
211 jobject jurl_request,
212 jobject jheaders_list) {
213 DCHECK(context_->IsOnNetworkThread());
214 const net::HttpResponseHeaders* headers = url_request_->response_headers();
215 if (headers == nullptr)
216 return;
218 void* iter = nullptr;
219 std::string header_name;
220 std::string header_value;
221 while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) {
222 base::android::ScopedJavaLocalRef<jstring> name =
223 ConvertUTF8ToJavaString(env, header_name);
224 base::android::ScopedJavaLocalRef<jstring> value =
225 ConvertUTF8ToJavaString(env, header_value);
226 Java_CronetUrlRequest_onAppendResponseHeader(
227 env, jurl_request, jheaders_list, name.obj(), value.obj());
231 base::android::ScopedJavaLocalRef<jstring>
232 CronetURLRequestAdapter::GetHttpStatusText(JNIEnv* env, jobject jcaller) const {
233 DCHECK(context_->IsOnNetworkThread());
234 const net::HttpResponseHeaders* headers = url_request_->response_headers();
235 return ConvertUTF8ToJavaString(env, headers->GetStatusText());
238 base::android::ScopedJavaLocalRef<jstring>
239 CronetURLRequestAdapter::GetNegotiatedProtocol(JNIEnv* env,
240 jobject jcaller) const {
241 DCHECK(context_->IsOnNetworkThread());
242 return ConvertUTF8ToJavaString(
243 env, url_request_->response_info().npn_negotiated_protocol);
246 base::android::ScopedJavaLocalRef<jstring>
247 CronetURLRequestAdapter::GetProxyServer(JNIEnv* env,
248 jobject jcaller) const {
249 DCHECK(context_->IsOnNetworkThread());
250 return ConvertUTF8ToJavaString(
251 env, url_request_->response_info().proxy_server.ToString());
254 jboolean CronetURLRequestAdapter::GetWasCached(JNIEnv* env,
255 jobject jcaller) const {
256 DCHECK(context_->IsOnNetworkThread());
257 return url_request_->response_info().was_cached;
260 // net::URLRequest::Delegate overrides (called on network thread).
262 void CronetURLRequestAdapter::OnReceivedRedirect(
263 net::URLRequest* request,
264 const net::RedirectInfo& redirect_info,
265 bool* defer_redirect) {
266 DCHECK(context_->IsOnNetworkThread());
267 DCHECK(request->status().is_success());
268 JNIEnv* env = base::android::AttachCurrentThread();
269 cronet::Java_CronetUrlRequest_onReceivedRedirect(
270 env, owner_.obj(),
271 ConvertUTF8ToJavaString(env, redirect_info.new_url.spec()).obj(),
272 redirect_info.status_code);
273 *defer_redirect = true;
276 void CronetURLRequestAdapter::OnSSLCertificateError(
277 net::URLRequest* request,
278 const net::SSLInfo& ssl_info,
279 bool fatal) {
280 DCHECK(context_->IsOnNetworkThread());
281 request->Cancel();
282 int net_error = net::MapCertStatusToNetError(ssl_info.cert_status);
283 JNIEnv* env = base::android::AttachCurrentThread();
284 cronet::Java_CronetUrlRequest_onError(
285 env, owner_.obj(), net_error,
286 ConvertUTF8ToJavaString(env, net::ErrorToString(net_error)).obj());
289 void CronetURLRequestAdapter::OnResponseStarted(net::URLRequest* request) {
290 DCHECK(context_->IsOnNetworkThread());
291 if (MaybeReportError(request))
292 return;
293 JNIEnv* env = base::android::AttachCurrentThread();
294 cronet::Java_CronetUrlRequest_onResponseStarted(env, owner_.obj(),
295 request->GetResponseCode());
298 void CronetURLRequestAdapter::OnReadCompleted(net::URLRequest* request,
299 int bytes_read) {
300 DCHECK(context_->IsOnNetworkThread());
301 if (MaybeReportError(request))
302 return;
303 if (bytes_read != 0) {
304 JNIEnv* env = base::android::AttachCurrentThread();
305 cronet::Java_CronetUrlRequest_onReadCompleted(
306 env, owner_.obj(), read_buffer_->byte_buffer(), bytes_read,
307 read_buffer_->initial_position());
308 // Free the read buffer. This lets the Java ByteBuffer be freed, if the
309 // embedder releases it, too.
310 read_buffer_ = nullptr;
311 } else {
312 JNIEnv* env = base::android::AttachCurrentThread();
313 cronet::Java_CronetUrlRequest_onSucceeded(
314 env, owner_.obj(), url_request_->GetTotalReceivedBytes());
318 void CronetURLRequestAdapter::StartOnNetworkThread() {
319 DCHECK(context_->IsOnNetworkThread());
320 VLOG(1) << "Starting chromium request: "
321 << initial_url_.possibly_invalid_spec().c_str()
322 << " priority: " << RequestPriorityToString(initial_priority_);
323 url_request_ = context_->GetURLRequestContext()->CreateRequest(
324 initial_url_, net::DEFAULT_PRIORITY, this);
325 url_request_->SetLoadFlags(load_flags_);
326 url_request_->set_method(initial_method_);
327 url_request_->SetExtraRequestHeaders(initial_request_headers_);
328 url_request_->SetPriority(initial_priority_);
329 if (upload_)
330 url_request_->set_upload(upload_.Pass());
331 url_request_->Start();
334 void CronetURLRequestAdapter::GetStatusOnNetworkThread(
335 const base::android::ScopedJavaGlobalRef<jobject>& status_listener_ref)
336 const {
337 DCHECK(context_->IsOnNetworkThread());
338 JNIEnv* env = base::android::AttachCurrentThread();
339 cronet::Java_CronetUrlRequest_onStatus(env, owner_.obj(),
340 status_listener_ref.obj(),
341 url_request_->GetLoadState().state);
344 void CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread() {
345 DCHECK(context_->IsOnNetworkThread());
346 url_request_->FollowDeferredRedirect();
349 void CronetURLRequestAdapter::ReadDataOnNetworkThread(
350 scoped_refptr<IOBufferWithByteBuffer> read_buffer,
351 int buffer_size) {
352 DCHECK(context_->IsOnNetworkThread());
353 DCHECK(read_buffer);
354 DCHECK(!read_buffer_);
356 read_buffer_ = read_buffer;
358 int bytes_read = 0;
359 url_request_->Read(read_buffer_.get(), buffer_size, &bytes_read);
360 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
361 if (url_request_->status().is_io_pending())
362 return;
364 OnReadCompleted(url_request_.get(), bytes_read);
367 void CronetURLRequestAdapter::DestroyOnNetworkThread() {
368 DCHECK(context_->IsOnNetworkThread());
369 delete this;
372 bool CronetURLRequestAdapter::MaybeReportError(net::URLRequest* request) const {
373 DCHECK_NE(net::URLRequestStatus::IO_PENDING, url_request_->status().status());
374 DCHECK_EQ(request, url_request_);
375 if (url_request_->status().is_success())
376 return false;
377 int net_error = url_request_->status().error();
378 VLOG(1) << "Error " << net::ErrorToString(net_error)
379 << " on chromium request: " << initial_url_.possibly_invalid_spec();
380 JNIEnv* env = base::android::AttachCurrentThread();
381 cronet::Java_CronetUrlRequest_onError(
382 env, owner_.obj(), net_error,
383 ConvertUTF8ToJavaString(env, net::ErrorToString(net_error)).obj());
384 return true;
387 } // namespace cronet