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::FollowDeferredRedirect(JNIEnv
* env
,
155 DCHECK(!context_
->IsOnNetworkThread());
156 context_
->PostTaskToNetworkThread(
159 &CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread
,
160 base::Unretained(this)));
163 jboolean
CronetURLRequestAdapter::ReadData(
164 JNIEnv
* env
, jobject jcaller
, jobject jbyte_buffer
, jint jposition
,
166 DCHECK(!context_
->IsOnNetworkThread());
167 DCHECK_LT(jposition
, jcapacity
);
169 void* data
= env
->GetDirectBufferAddress(jbyte_buffer
);
173 scoped_refptr
<IOBufferWithByteBuffer
> read_buffer(
174 new IOBufferWithByteBuffer(env
, jbyte_buffer
, data
, jposition
));
176 int remaining_capacity
= jcapacity
- jposition
;
178 context_
->PostTaskToNetworkThread(
179 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::ReadDataOnNetworkThread
,
180 base::Unretained(this),
182 remaining_capacity
));
186 void CronetURLRequestAdapter::Destroy(JNIEnv
* env
, jobject jcaller
) {
187 // Destroy could be called from any thread, including network thread (if
188 // posting task to executor throws an exception), but is posted, so |this|
189 // is valid until calling task is complete. Destroy() is always called from
190 // within a synchronized java block that guarantees no future posts to the
191 // network thread with the adapter pointer.
192 context_
->PostTaskToNetworkThread(
193 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::DestroyOnNetworkThread
,
194 base::Unretained(this)));
197 void CronetURLRequestAdapter::PopulateResponseHeaders(JNIEnv
* env
,
198 jobject jurl_request
,
199 jobject jheaders_list
) {
200 DCHECK(context_
->IsOnNetworkThread());
201 const net::HttpResponseHeaders
* headers
= url_request_
->response_headers();
202 if (headers
== nullptr)
205 void* iter
= nullptr;
206 std::string header_name
;
207 std::string header_value
;
208 while (headers
->EnumerateHeaderLines(&iter
, &header_name
, &header_value
)) {
209 base::android::ScopedJavaLocalRef
<jstring
> name
=
210 ConvertUTF8ToJavaString(env
, header_name
);
211 base::android::ScopedJavaLocalRef
<jstring
> value
=
212 ConvertUTF8ToJavaString(env
, header_value
);
213 Java_CronetUrlRequest_onAppendResponseHeader(
214 env
, jurl_request
, jheaders_list
, name
.obj(), value
.obj());
218 base::android::ScopedJavaLocalRef
<jstring
>
219 CronetURLRequestAdapter::GetHttpStatusText(JNIEnv
* env
, jobject jcaller
) const {
220 DCHECK(context_
->IsOnNetworkThread());
221 const net::HttpResponseHeaders
* headers
= url_request_
->response_headers();
222 return ConvertUTF8ToJavaString(env
, headers
->GetStatusText());
225 base::android::ScopedJavaLocalRef
<jstring
>
226 CronetURLRequestAdapter::GetNegotiatedProtocol(JNIEnv
* env
,
227 jobject jcaller
) const {
228 DCHECK(context_
->IsOnNetworkThread());
229 return ConvertUTF8ToJavaString(
230 env
, url_request_
->response_info().npn_negotiated_protocol
);
233 base::android::ScopedJavaLocalRef
<jstring
>
234 CronetURLRequestAdapter::GetProxyServer(JNIEnv
* env
,
235 jobject jcaller
) const {
236 DCHECK(context_
->IsOnNetworkThread());
237 return ConvertUTF8ToJavaString(
238 env
, url_request_
->response_info().proxy_server
.ToString());
241 jboolean
CronetURLRequestAdapter::GetWasCached(JNIEnv
* env
,
242 jobject jcaller
) const {
243 DCHECK(context_
->IsOnNetworkThread());
244 return url_request_
->response_info().was_cached
;
247 // net::URLRequest::Delegate overrides (called on network thread).
249 void CronetURLRequestAdapter::OnReceivedRedirect(
250 net::URLRequest
* request
,
251 const net::RedirectInfo
& redirect_info
,
252 bool* defer_redirect
) {
253 DCHECK(context_
->IsOnNetworkThread());
254 DCHECK(request
->status().is_success());
255 JNIEnv
* env
= base::android::AttachCurrentThread();
256 cronet::Java_CronetUrlRequest_onReceivedRedirect(
258 ConvertUTF8ToJavaString(env
, redirect_info
.new_url
.spec()).obj(),
259 redirect_info
.status_code
);
260 *defer_redirect
= true;
263 void CronetURLRequestAdapter::OnResponseStarted(net::URLRequest
* request
) {
264 DCHECK(context_
->IsOnNetworkThread());
265 if (MaybeReportError(request
))
267 JNIEnv
* env
= base::android::AttachCurrentThread();
268 cronet::Java_CronetUrlRequest_onResponseStarted(env
, owner_
.obj(),
269 request
->GetResponseCode());
272 void CronetURLRequestAdapter::OnReadCompleted(net::URLRequest
* request
,
274 DCHECK(context_
->IsOnNetworkThread());
275 if (MaybeReportError(request
))
277 if (bytes_read
!= 0) {
278 JNIEnv
* env
= base::android::AttachCurrentThread();
279 cronet::Java_CronetUrlRequest_onReadCompleted(
280 env
, owner_
.obj(), read_buffer_
->byte_buffer(), bytes_read
,
281 read_buffer_
->initial_position());
282 // Free the read buffer. This lets the Java ByteBuffer be freed, if the
283 // embedder releases it, too.
284 read_buffer_
= nullptr;
286 JNIEnv
* env
= base::android::AttachCurrentThread();
287 cronet::Java_CronetUrlRequest_onSucceeded(
288 env
, owner_
.obj(), url_request_
->GetTotalReceivedBytes());
292 void CronetURLRequestAdapter::StartOnNetworkThread() {
293 DCHECK(context_
->IsOnNetworkThread());
294 VLOG(1) << "Starting chromium request: "
295 << initial_url_
.possibly_invalid_spec().c_str()
296 << " priority: " << RequestPriorityToString(initial_priority_
);
297 url_request_
= context_
->GetURLRequestContext()->CreateRequest(
298 initial_url_
, net::DEFAULT_PRIORITY
, this);
299 url_request_
->SetLoadFlags(load_flags_
);
300 url_request_
->set_method(initial_method_
);
301 url_request_
->SetExtraRequestHeaders(initial_request_headers_
);
302 url_request_
->SetPriority(initial_priority_
);
304 url_request_
->set_upload(upload_
.Pass());
305 url_request_
->Start();
308 void CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread() {
309 DCHECK(context_
->IsOnNetworkThread());
310 url_request_
->FollowDeferredRedirect();
313 void CronetURLRequestAdapter::ReadDataOnNetworkThread(
314 scoped_refptr
<IOBufferWithByteBuffer
> read_buffer
,
316 DCHECK(context_
->IsOnNetworkThread());
318 DCHECK(!read_buffer_
);
320 read_buffer_
= read_buffer
;
323 url_request_
->Read(read_buffer_
.get(), buffer_size
, &bytes_read
);
324 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
325 if (url_request_
->status().is_io_pending())
328 OnReadCompleted(url_request_
.get(), bytes_read
);
331 void CronetURLRequestAdapter::DestroyOnNetworkThread() {
332 DCHECK(context_
->IsOnNetworkThread());
336 bool CronetURLRequestAdapter::MaybeReportError(net::URLRequest
* request
) const {
337 DCHECK_NE(net::URLRequestStatus::IO_PENDING
, url_request_
->status().status());
338 DCHECK_EQ(request
, url_request_
);
339 if (url_request_
->status().is_success())
341 int net_error
= url_request_
->status().error();
342 VLOG(1) << "Error " << net::ErrorToString(net_error
)
343 << " on chromium request: " << initial_url_
.possibly_invalid_spec();
344 JNIEnv
* env
= base::android::AttachCurrentThread();
345 cronet::Java_CronetUrlRequest_onError(
346 env
, owner_
.obj(), net_error
,
347 ConvertUTF8ToJavaString(env
, net::ErrorToString(net_error
)).obj());
351 } // namespace cronet