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"
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
;
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
,
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
{
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
66 IOBufferWithByteBuffer(
71 : net::WrappedIOBuffer(static_cast<char*>(data
) + position
),
72 initial_position_(position
) {
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(); }
83 ~IOBufferWithByteBuffer() override
{}
85 base::android::ScopedJavaGlobalRef
<jobject
> byte_buffer_
;
87 const int initial_position_
;
90 CronetURLRequestAdapter::CronetURLRequestAdapter(
91 CronetURLRequestContextAdapter
* context
,
95 net::RequestPriority priority
)
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
,
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
))
117 initial_method_
= method
;
121 jboolean
CronetURLRequestAdapter::AddRequestHeader(JNIEnv
* env
,
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
)) {
132 initial_request_headers_
.SetHeader(name
, value
);
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());
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
,
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
,
168 DCHECK(!context_
->IsOnNetworkThread());
169 context_
->PostTaskToNetworkThread(
172 &CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread
,
173 base::Unretained(this)));
176 jboolean
CronetURLRequestAdapter::ReadData(
177 JNIEnv
* env
, jobject jcaller
, jobject jbyte_buffer
, jint jposition
,
179 DCHECK(!context_
->IsOnNetworkThread());
180 DCHECK_LT(jposition
, jcapacity
);
182 void* data
= env
->GetDirectBufferAddress(jbyte_buffer
);
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),
195 remaining_capacity
));
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)
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(
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
,
280 DCHECK(context_
->IsOnNetworkThread());
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
))
293 JNIEnv
* env
= base::android::AttachCurrentThread();
294 cronet::Java_CronetUrlRequest_onResponseStarted(env
, owner_
.obj(),
295 request
->GetResponseCode());
298 void CronetURLRequestAdapter::OnReadCompleted(net::URLRequest
* request
,
300 DCHECK(context_
->IsOnNetworkThread());
301 if (MaybeReportError(request
))
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;
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_
);
330 url_request_
->set_upload(upload_
.Pass());
331 url_request_
->Start();
334 void CronetURLRequestAdapter::GetStatusOnNetworkThread(
335 const base::android::ScopedJavaGlobalRef
<jobject
>& status_listener_ref
)
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
,
352 DCHECK(context_
->IsOnNetworkThread());
354 DCHECK(!read_buffer_
);
356 read_buffer_
= read_buffer
;
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())
364 OnReadCompleted(url_request_
.get(), bytes_read
);
367 void CronetURLRequestAdapter::DestroyOnNetworkThread() {
368 DCHECK(context_
->IsOnNetworkThread());
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())
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());
387 } // namespace cronet