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 "components/cronet/android/chromium_url_request.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/macros.h"
10 #include "components/cronet/android/url_request_adapter.h"
11 #include "components/cronet/android/url_request_context_adapter.h"
12 #include "jni/ChromiumUrlRequest_jni.h"
13 #include "net/base/net_errors.h"
14 #include "net/base/request_priority.h"
15 #include "net/http/http_response_headers.h"
17 using base::android::ConvertUTF8ToJavaString
;
18 using base::android::ConvertJavaStringToUTF8
;
23 net::RequestPriority
ConvertRequestPriority(jint request_priority
) {
24 switch (request_priority
) {
25 case REQUEST_PRIORITY_IDLE
:
27 case REQUEST_PRIORITY_LOWEST
:
29 case REQUEST_PRIORITY_LOW
:
31 case REQUEST_PRIORITY_MEDIUM
:
33 case REQUEST_PRIORITY_HIGHEST
:
40 void SetPostContentType(JNIEnv
* env
,
41 URLRequestAdapter
* request_adapter
,
42 jstring content_type
) {
43 std::string
method_post("POST");
44 request_adapter
->SetMethod(method_post
);
46 std::string
content_type_header("Content-Type");
47 std::string
content_type_string(ConvertJavaStringToUTF8(env
, content_type
));
49 request_adapter
->AddHeader(content_type_header
, content_type_string
);
52 // A delegate of URLRequestAdapter that delivers callbacks to the Java layer.
53 class JniURLRequestAdapterDelegate
54 : public URLRequestAdapter::URLRequestAdapterDelegate
{
56 JniURLRequestAdapterDelegate(JNIEnv
* env
, jobject owner
) {
57 owner_
= env
->NewGlobalRef(owner
);
60 void OnResponseStarted(URLRequestAdapter
* request_adapter
) override
{
61 JNIEnv
* env
= base::android::AttachCurrentThread();
62 cronet::Java_ChromiumUrlRequest_onResponseStarted(env
, owner_
);
65 void OnBytesRead(URLRequestAdapter
* request_adapter
,
66 int bytes_read
) override
{
67 if (bytes_read
!= 0) {
68 JNIEnv
* env
= base::android::AttachCurrentThread();
69 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
70 env
, env
->NewDirectByteBuffer(request_adapter
->Data(), bytes_read
));
71 cronet::Java_ChromiumUrlRequest_onBytesRead(
72 env
, owner_
, java_buffer
.obj());
76 void OnRequestFinished(URLRequestAdapter
* request_adapter
) override
{
77 JNIEnv
* env
= base::android::AttachCurrentThread();
78 cronet::Java_ChromiumUrlRequest_finish(env
, owner_
);
81 int ReadFromUploadChannel(net::IOBuffer
* buf
, int buf_length
) override
{
82 JNIEnv
* env
= base::android::AttachCurrentThread();
83 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
84 env
, env
->NewDirectByteBuffer(buf
->data(), buf_length
));
85 jint bytes_read
= cronet::Java_ChromiumUrlRequest_readFromUploadChannel(
86 env
, owner_
, java_buffer
.obj());
91 ~JniURLRequestAdapterDelegate() override
{
92 JNIEnv
* env
= base::android::AttachCurrentThread();
93 env
->DeleteGlobalRef(owner_
);
99 DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate
);
104 // Explicitly register static JNI functions.
105 bool ChromiumUrlRequestRegisterJni(JNIEnv
* env
) {
106 return RegisterNativesImpl(env
);
109 static jlong
CreateRequestAdapter(JNIEnv
* env
,
111 jlong jurl_request_context_adapter
,
113 jint jrequest_priority
) {
114 URLRequestContextAdapter
* context_adapter
=
115 reinterpret_cast<URLRequestContextAdapter
*>(jurl_request_context_adapter
);
116 DCHECK(context_adapter
);
118 GURL
url(ConvertJavaStringToUTF8(env
, jurl
));
120 VLOG(1) << "New chromium network request: " << url
.possibly_invalid_spec();
122 URLRequestAdapter
* adapter
= new URLRequestAdapter(
123 context_adapter
, new JniURLRequestAdapterDelegate(env
, jcaller
), url
,
124 ConvertRequestPriority(jrequest_priority
));
126 return reinterpret_cast<jlong
>(adapter
);
130 static void AddHeader(JNIEnv
* env
,
132 jlong jurl_request_adapter
,
133 jstring jheader_name
,
134 jstring jheader_value
) {
135 URLRequestAdapter
* request_adapter
=
136 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
137 DCHECK(request_adapter
);
139 std::string
header_name(ConvertJavaStringToUTF8(env
, jheader_name
));
140 std::string
header_value(ConvertJavaStringToUTF8(env
, jheader_value
));
142 request_adapter
->AddHeader(header_name
, header_value
);
145 static void SetMethod(JNIEnv
* env
,
147 jlong jurl_request_adapter
,
149 URLRequestAdapter
* request_adapter
=
150 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
151 DCHECK(request_adapter
);
153 std::string
method(ConvertJavaStringToUTF8(env
, jmethod
));
155 request_adapter
->SetMethod(method
);
158 static void SetUploadData(JNIEnv
* env
,
160 jlong jurl_request_adapter
,
161 jstring jcontent_type
,
162 jbyteArray jcontent
) {
163 URLRequestAdapter
* request_adapter
=
164 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
165 DCHECK(request_adapter
);
166 SetPostContentType(env
, request_adapter
, jcontent_type
);
168 if (jcontent
!= NULL
) {
169 jsize size
= env
->GetArrayLength(jcontent
);
171 jbyte
* content_bytes
= env
->GetByteArrayElements(jcontent
, NULL
);
172 request_adapter
->SetUploadContent(
173 reinterpret_cast<const char*>(content_bytes
), size
);
174 env
->ReleaseByteArrayElements(jcontent
, content_bytes
, 0);
179 static void SetUploadChannel(JNIEnv
* env
,
181 jlong jurl_request_adapter
,
182 jstring jcontent_type
,
183 jlong jcontent_length
) {
184 URLRequestAdapter
* request_adapter
=
185 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
186 DCHECK(request_adapter
);
187 SetPostContentType(env
, request_adapter
, jcontent_type
);
189 request_adapter
->SetUploadChannel(env
, jcontent_length
);
192 static void EnableChunkedUpload(JNIEnv
* env
,
194 jlong jurl_request_adapter
,
195 jstring jcontent_type
) {
196 URLRequestAdapter
* request_adapter
=
197 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
198 DCHECK(request_adapter
);
199 SetPostContentType(env
, request_adapter
, jcontent_type
);
201 request_adapter
->EnableChunkedUpload();
204 static void AppendChunk(JNIEnv
* env
,
206 jlong jurl_request_adapter
,
207 jobject jchunk_byte_buffer
,
209 jboolean jis_last_chunk
) {
210 URLRequestAdapter
* request_adapter
=
211 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
212 DCHECK(request_adapter
);
213 DCHECK(jchunk_byte_buffer
);
215 void* chunk
= env
->GetDirectBufferAddress(jchunk_byte_buffer
);
216 request_adapter
->AppendChunk(reinterpret_cast<const char*>(chunk
),
217 jchunk_size
, jis_last_chunk
);
221 static void Start(JNIEnv
* env
, jobject jcaller
, jlong jurl_request_adapter
) {
222 URLRequestAdapter
* request_adapter
=
223 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
224 if (request_adapter
!= NULL
)
225 request_adapter
->Start();
229 static void DestroyRequestAdapter(JNIEnv
* env
,
231 jlong jurl_request_adapter
) {
232 URLRequestAdapter
* request_adapter
=
233 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
234 if (request_adapter
!= NULL
)
235 request_adapter
->Destroy();
239 static void Cancel(JNIEnv
* env
, jobject jcaller
, jlong jurl_request_adapter
) {
240 URLRequestAdapter
* request_adapter
=
241 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
242 if (request_adapter
!= NULL
)
243 request_adapter
->Cancel();
246 static jint
GetErrorCode(JNIEnv
* env
,
248 jlong jurl_request_adapter
) {
249 URLRequestAdapter
* request_adapter
=
250 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
251 DCHECK(request_adapter
);
252 int error_code
= request_adapter
->error_code();
253 switch (error_code
) {
254 // TODO(mef): Investigate returning success on positive values, too, as
255 // they technically indicate success.
257 return REQUEST_ERROR_SUCCESS
;
259 // TODO(mef): Investigate this. The fact is that Chrome does not do this,
260 // and this library is not just being used for downloads.
262 // Comment from src/content/browser/download/download_resource_handler.cc:
263 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
264 // allowed since a number of servers in the wild close the connection too
265 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
266 // treat downloads as complete in both cases, so we follow their lead.
267 case net::ERR_CONTENT_LENGTH_MISMATCH
:
268 case net::ERR_INCOMPLETE_CHUNKED_ENCODING
:
269 return REQUEST_ERROR_SUCCESS
;
271 case net::ERR_INVALID_URL
:
272 case net::ERR_DISALLOWED_URL_SCHEME
:
273 case net::ERR_UNKNOWN_URL_SCHEME
:
274 return REQUEST_ERROR_MALFORMED_URL
;
276 case net::ERR_CONNECTION_TIMED_OUT
:
277 return REQUEST_ERROR_CONNECTION_TIMED_OUT
;
279 case net::ERR_NAME_NOT_RESOLVED
:
280 return REQUEST_ERROR_UNKNOWN_HOST
;
281 case net::ERR_TOO_MANY_REDIRECTS
:
282 return REQUEST_ERROR_TOO_MANY_REDIRECTS
;
284 return REQUEST_ERROR_UNKNOWN
;
287 static jstring
GetErrorString(JNIEnv
* env
,
289 jlong jurl_request_adapter
) {
290 URLRequestAdapter
* request_adapter
=
291 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
292 DCHECK(request_adapter
);
293 int error_code
= request_adapter
->error_code();
295 std::string error_string
= net::ErrorToString(error_code
);
298 "System error: %s(%d)",
299 error_string
.c_str(),
301 return ConvertUTF8ToJavaString(env
, buffer
).Release();
304 static jint
GetHttpStatusCode(JNIEnv
* env
,
306 jlong jurl_request_adapter
) {
307 URLRequestAdapter
* request_adapter
=
308 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
309 DCHECK(request_adapter
);
310 return request_adapter
->http_status_code();
313 static jstring
GetHttpStatusText(JNIEnv
* env
,
315 jlong jurl_request_adapter
) {
316 URLRequestAdapter
* request_adapter
=
317 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
318 DCHECK(request_adapter
);
319 return ConvertUTF8ToJavaString(env
, request_adapter
->http_status_text())
323 static jstring
GetContentType(JNIEnv
* env
,
325 jlong jurl_request_adapter
) {
326 URLRequestAdapter
* request_adapter
=
327 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
328 DCHECK(request_adapter
);
329 std::string type
= request_adapter
->content_type();
331 return ConvertUTF8ToJavaString(env
, type
.c_str()).Release();
337 static jlong
GetContentLength(JNIEnv
* env
,
339 jlong jurl_request_adapter
) {
340 URLRequestAdapter
* request_adapter
=
341 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
342 DCHECK(request_adapter
);
343 return request_adapter
->content_length();
346 static jstring
GetHeader(JNIEnv
* env
,
348 jlong jurl_request_adapter
,
349 jstring jheader_name
) {
350 URLRequestAdapter
* request_adapter
=
351 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
352 DCHECK(request_adapter
);
353 std::string header_name
= ConvertJavaStringToUTF8(env
, jheader_name
);
354 std::string header_value
= request_adapter
->GetHeader(header_name
);
355 if (!header_value
.empty())
356 return ConvertUTF8ToJavaString(env
, header_value
.c_str()).Release();
360 static void GetAllHeaders(JNIEnv
* env
,
362 jlong jurl_request_adapter
,
363 jobject jheaders_map
) {
364 URLRequestAdapter
* request_adapter
=
365 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
366 DCHECK(request_adapter
);
368 net::HttpResponseHeaders
* headers
= request_adapter
->GetResponseHeaders();
373 std::string header_name
;
374 std::string header_value
;
375 while (headers
->EnumerateHeaderLines(&iter
, &header_name
, &header_value
)) {
376 ScopedJavaLocalRef
<jstring
> name
=
377 ConvertUTF8ToJavaString(env
, header_name
);
378 ScopedJavaLocalRef
<jstring
> value
=
379 ConvertUTF8ToJavaString(env
, header_value
);
380 Java_ChromiumUrlRequest_onAppendResponseHeader(env
, jcaller
, jheaders_map
,
381 name
.obj(), value
.obj());
384 // Some implementations (notably HttpURLConnection) include a mapping for the
385 // null key; in HTTP's case, this maps to the HTTP status line.
386 ScopedJavaLocalRef
<jstring
> status_line
=
387 ConvertUTF8ToJavaString(env
, headers
->GetStatusLine());
388 Java_ChromiumUrlRequest_onAppendResponseHeader(env
, jcaller
, jheaders_map
,
389 NULL
, status_line
.obj());
392 static jstring
GetNegotiatedProtocol(JNIEnv
* env
,
394 jlong jurl_request_adapter
) {
395 URLRequestAdapter
* request_adapter
=
396 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
397 DCHECK(request_adapter
);
399 std::string negotiated_protocol
= request_adapter
->GetNegotiatedProtocol();
400 return ConvertUTF8ToJavaString(env
, negotiated_protocol
.c_str()).Release();
403 static jboolean
GetWasCached(JNIEnv
* env
,
405 jlong jurl_request_adapter
) {
406 URLRequestAdapter
* request_adapter
=
407 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
408 DCHECK(request_adapter
);
410 bool was_cached
= request_adapter
->GetWasCached();
411 return was_cached
? JNI_TRUE
: JNI_FALSE
;
414 static void DisableRedirects(JNIEnv
* env
, jobject jcaller
,
415 jlong jrequest_adapter
) {
416 URLRequestAdapter
* request_adapter
=
417 reinterpret_cast<URLRequestAdapter
*>(jrequest_adapter
);
418 DCHECK(request_adapter
);
419 request_adapter
->DisableRedirects();
422 } // namespace cronet