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/http/http_response_headers.h"
19 #include "net/http/http_status_code.h"
20 #include "net/http/http_util.h"
21 #include "net/url_request/redirect_info.h"
22 #include "net/url_request/url_request_context.h"
24 using base::android::ConvertUTF8ToJavaString
;
28 // Explicitly register static JNI functions.
29 bool CronetUrlRequestAdapterRegisterJni(JNIEnv
* env
) {
30 return RegisterNativesImpl(env
);
33 static jlong
CreateRequestAdapter(JNIEnv
* env
,
35 jlong jurl_request_context_adapter
,
38 CronetURLRequestContextAdapter
* context_adapter
=
39 reinterpret_cast<CronetURLRequestContextAdapter
*>(
40 jurl_request_context_adapter
);
41 DCHECK(context_adapter
);
43 GURL
url(base::android::ConvertJavaStringToUTF8(env
, jurl_string
));
45 VLOG(1) << "New chromium network request_adapter: "
46 << url
.possibly_invalid_spec();
48 CronetURLRequestAdapter
* adapter
=
49 new CronetURLRequestAdapter(context_adapter
, env
, jurl_request
, url
,
50 static_cast<net::RequestPriority
>(jpriority
));
52 return reinterpret_cast<jlong
>(adapter
);
55 // net::WrappedIOBuffer subclass for a buffer owned by a Java ByteBuffer. Keeps
56 // the ByteBuffer alive until destroyed. Uses WrappedIOBuffer because data() is
57 // owned by the embedder.
58 class CronetURLRequestAdapter::IOBufferWithByteBuffer
59 : public net::WrappedIOBuffer
{
61 // Creates a buffer wrapping the Java ByteBuffer |jbyte_buffer|. |data| points
62 // to the memory backed by the ByteBuffer, and position is the location to
64 IOBufferWithByteBuffer(
69 : net::WrappedIOBuffer(static_cast<char*>(data
) + position
),
70 initial_position_(position
) {
72 DCHECK_EQ(env
->GetDirectBufferAddress(jbyte_buffer
), data
);
73 byte_buffer_
.Reset(env
, jbyte_buffer
);
76 int initial_position() const { return initial_position_
; }
78 jobject
byte_buffer() const { return byte_buffer_
.obj(); }
81 ~IOBufferWithByteBuffer() override
{}
83 base::android::ScopedJavaGlobalRef
<jobject
> byte_buffer_
;
85 const int initial_position_
;
88 CronetURLRequestAdapter::CronetURLRequestAdapter(
89 CronetURLRequestContextAdapter
* context
,
93 net::RequestPriority priority
)
96 initial_priority_(priority
),
97 initial_method_("GET"),
98 load_flags_(context
->default_load_flags()) {
99 DCHECK(!context_
->IsOnNetworkThread());
100 owner_
.Reset(env
, jurl_request
);
103 CronetURLRequestAdapter::~CronetURLRequestAdapter() {
104 DCHECK(context_
->IsOnNetworkThread());
107 jboolean
CronetURLRequestAdapter::SetHttpMethod(JNIEnv
* env
,
110 DCHECK(!context_
->IsOnNetworkThread());
111 std::string
method(base::android::ConvertJavaStringToUTF8(env
, jmethod
));
112 // Http method is a token, just as header name.
113 if (!net::HttpUtil::IsValidHeaderName(method
))
115 initial_method_
= method
;
119 jboolean
CronetURLRequestAdapter::AddRequestHeader(JNIEnv
* env
,
123 DCHECK(!context_
->IsOnNetworkThread());
124 std::string
name(base::android::ConvertJavaStringToUTF8(env
, jname
));
125 std::string
value(base::android::ConvertJavaStringToUTF8(env
, jvalue
));
126 if (!net::HttpUtil::IsValidHeaderName(name
) ||
127 !net::HttpUtil::IsValidHeaderValue(value
)) {
130 initial_request_headers_
.SetHeader(name
, value
);
134 void CronetURLRequestAdapter::DisableCache(JNIEnv
* env
, jobject jcaller
) {
135 DCHECK(!context_
->IsOnNetworkThread());
136 load_flags_
|= net::LOAD_DISABLE_CACHE
;
139 void CronetURLRequestAdapter::SetUpload(
140 scoped_ptr
<net::UploadDataStream
> upload
) {
141 DCHECK(!context_
->IsOnNetworkThread());
143 upload_
= upload
.Pass();
146 void CronetURLRequestAdapter::Start(JNIEnv
* env
, jobject jcaller
) {
147 DCHECK(!context_
->IsOnNetworkThread());
148 context_
->PostTaskToNetworkThread(
149 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::StartOnNetworkThread
,
150 base::Unretained(this)));
153 void CronetURLRequestAdapter::GetStatus(JNIEnv
* env
,
155 jobject jstatus_listener
) const {
156 DCHECK(!context_
->IsOnNetworkThread());
157 base::android::ScopedJavaGlobalRef
<jobject
> status_listener_ref
;
158 status_listener_ref
.Reset(env
, jstatus_listener
);
159 context_
->PostTaskToNetworkThread(
160 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::GetStatusOnNetworkThread
,
161 base::Unretained(this), status_listener_ref
));
164 void CronetURLRequestAdapter::FollowDeferredRedirect(JNIEnv
* env
,
166 DCHECK(!context_
->IsOnNetworkThread());
167 context_
->PostTaskToNetworkThread(
170 &CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread
,
171 base::Unretained(this)));
174 jboolean
CronetURLRequestAdapter::ReadData(
175 JNIEnv
* env
, jobject jcaller
, jobject jbyte_buffer
, jint jposition
,
177 DCHECK(!context_
->IsOnNetworkThread());
178 DCHECK_LT(jposition
, jcapacity
);
180 void* data
= env
->GetDirectBufferAddress(jbyte_buffer
);
184 scoped_refptr
<IOBufferWithByteBuffer
> read_buffer(
185 new IOBufferWithByteBuffer(env
, jbyte_buffer
, data
, jposition
));
187 int remaining_capacity
= jcapacity
- jposition
;
189 context_
->PostTaskToNetworkThread(
190 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::ReadDataOnNetworkThread
,
191 base::Unretained(this),
193 remaining_capacity
));
197 void CronetURLRequestAdapter::Destroy(JNIEnv
* env
, jobject jcaller
) {
198 // Destroy could be called from any thread, including network thread (if
199 // posting task to executor throws an exception), but is posted, so |this|
200 // is valid until calling task is complete. Destroy() is always called from
201 // within a synchronized java block that guarantees no future posts to the
202 // network thread with the adapter pointer.
203 context_
->PostTaskToNetworkThread(
204 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::DestroyOnNetworkThread
,
205 base::Unretained(this)));
208 void CronetURLRequestAdapter::PopulateResponseHeaders(JNIEnv
* env
,
209 jobject jurl_request
,
210 jobject jheaders_list
) {
211 DCHECK(context_
->IsOnNetworkThread());
212 const net::HttpResponseHeaders
* headers
= url_request_
->response_headers();
213 if (headers
== nullptr)
216 void* iter
= nullptr;
217 std::string header_name
;
218 std::string header_value
;
219 while (headers
->EnumerateHeaderLines(&iter
, &header_name
, &header_value
)) {
220 base::android::ScopedJavaLocalRef
<jstring
> name
=
221 ConvertUTF8ToJavaString(env
, header_name
);
222 base::android::ScopedJavaLocalRef
<jstring
> value
=
223 ConvertUTF8ToJavaString(env
, header_value
);
224 Java_CronetUrlRequest_onAppendResponseHeader(
225 env
, jurl_request
, jheaders_list
, name
.obj(), value
.obj());
229 base::android::ScopedJavaLocalRef
<jstring
>
230 CronetURLRequestAdapter::GetHttpStatusText(JNIEnv
* env
, jobject jcaller
) const {
231 DCHECK(context_
->IsOnNetworkThread());
232 const net::HttpResponseHeaders
* headers
= url_request_
->response_headers();
233 return ConvertUTF8ToJavaString(env
, headers
->GetStatusText());
236 base::android::ScopedJavaLocalRef
<jstring
>
237 CronetURLRequestAdapter::GetNegotiatedProtocol(JNIEnv
* env
,
238 jobject jcaller
) const {
239 DCHECK(context_
->IsOnNetworkThread());
240 return ConvertUTF8ToJavaString(
241 env
, url_request_
->response_info().npn_negotiated_protocol
);
244 base::android::ScopedJavaLocalRef
<jstring
>
245 CronetURLRequestAdapter::GetProxyServer(JNIEnv
* env
,
246 jobject jcaller
) const {
247 DCHECK(context_
->IsOnNetworkThread());
248 return ConvertUTF8ToJavaString(
249 env
, url_request_
->response_info().proxy_server
.ToString());
252 jboolean
CronetURLRequestAdapter::GetWasCached(JNIEnv
* env
,
253 jobject jcaller
) const {
254 DCHECK(context_
->IsOnNetworkThread());
255 return url_request_
->response_info().was_cached
;
258 // net::URLRequest::Delegate overrides (called on network thread).
260 void CronetURLRequestAdapter::OnReceivedRedirect(
261 net::URLRequest
* request
,
262 const net::RedirectInfo
& redirect_info
,
263 bool* defer_redirect
) {
264 DCHECK(context_
->IsOnNetworkThread());
265 DCHECK(request
->status().is_success());
266 JNIEnv
* env
= base::android::AttachCurrentThread();
267 cronet::Java_CronetUrlRequest_onReceivedRedirect(
269 ConvertUTF8ToJavaString(env
, redirect_info
.new_url
.spec()).obj(),
270 redirect_info
.status_code
);
271 *defer_redirect
= true;
274 void CronetURLRequestAdapter::OnResponseStarted(net::URLRequest
* request
) {
275 DCHECK(context_
->IsOnNetworkThread());
276 if (MaybeReportError(request
))
278 JNIEnv
* env
= base::android::AttachCurrentThread();
279 cronet::Java_CronetUrlRequest_onResponseStarted(env
, owner_
.obj(),
280 request
->GetResponseCode());
283 void CronetURLRequestAdapter::OnReadCompleted(net::URLRequest
* request
,
285 DCHECK(context_
->IsOnNetworkThread());
286 if (MaybeReportError(request
))
288 if (bytes_read
!= 0) {
289 JNIEnv
* env
= base::android::AttachCurrentThread();
290 cronet::Java_CronetUrlRequest_onReadCompleted(
291 env
, owner_
.obj(), read_buffer_
->byte_buffer(), bytes_read
,
292 read_buffer_
->initial_position());
293 // Free the read buffer. This lets the Java ByteBuffer be freed, if the
294 // embedder releases it, too.
295 read_buffer_
= nullptr;
297 JNIEnv
* env
= base::android::AttachCurrentThread();
298 cronet::Java_CronetUrlRequest_onSucceeded(
299 env
, owner_
.obj(), url_request_
->GetTotalReceivedBytes());
303 void CronetURLRequestAdapter::StartOnNetworkThread() {
304 DCHECK(context_
->IsOnNetworkThread());
305 VLOG(1) << "Starting chromium request: "
306 << initial_url_
.possibly_invalid_spec().c_str()
307 << " priority: " << RequestPriorityToString(initial_priority_
);
308 url_request_
= context_
->GetURLRequestContext()->CreateRequest(
309 initial_url_
, net::DEFAULT_PRIORITY
, this);
310 url_request_
->SetLoadFlags(load_flags_
);
311 url_request_
->set_method(initial_method_
);
312 url_request_
->SetExtraRequestHeaders(initial_request_headers_
);
313 url_request_
->SetPriority(initial_priority_
);
315 url_request_
->set_upload(upload_
.Pass());
316 url_request_
->Start();
319 void CronetURLRequestAdapter::GetStatusOnNetworkThread(
320 const base::android::ScopedJavaGlobalRef
<jobject
>& status_listener_ref
)
322 DCHECK(context_
->IsOnNetworkThread());
323 JNIEnv
* env
= base::android::AttachCurrentThread();
324 cronet::Java_CronetUrlRequest_onStatus(env
, owner_
.obj(),
325 status_listener_ref
.obj(),
326 url_request_
->GetLoadState().state
);
329 void CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread() {
330 DCHECK(context_
->IsOnNetworkThread());
331 url_request_
->FollowDeferredRedirect();
334 void CronetURLRequestAdapter::ReadDataOnNetworkThread(
335 scoped_refptr
<IOBufferWithByteBuffer
> read_buffer
,
337 DCHECK(context_
->IsOnNetworkThread());
339 DCHECK(!read_buffer_
);
341 read_buffer_
= read_buffer
;
344 url_request_
->Read(read_buffer_
.get(), buffer_size
, &bytes_read
);
345 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
346 if (url_request_
->status().is_io_pending())
349 OnReadCompleted(url_request_
.get(), bytes_read
);
352 void CronetURLRequestAdapter::DestroyOnNetworkThread() {
353 DCHECK(context_
->IsOnNetworkThread());
357 bool CronetURLRequestAdapter::MaybeReportError(net::URLRequest
* request
) const {
358 DCHECK_NE(net::URLRequestStatus::IO_PENDING
, url_request_
->status().status());
359 DCHECK_EQ(request
, url_request_
);
360 if (url_request_
->status().is_success())
362 int net_error
= url_request_
->status().error();
363 VLOG(1) << "Error " << net::ErrorToString(net_error
)
364 << " on chromium request: " << initial_url_
.possibly_invalid_spec();
365 JNIEnv
* env
= base::android::AttachCurrentThread();
366 cronet::Java_CronetUrlRequest_onError(
367 env
, owner_
.obj(), net_error
,
368 ConvertUTF8ToJavaString(env
, net::ErrorToString(net_error
)).obj());
372 } // namespace cronet