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
;
22 net::RequestPriority
ConvertRequestPriority(jint request_priority
) {
23 switch (request_priority
) {
24 case REQUEST_PRIORITY_IDLE
:
26 case REQUEST_PRIORITY_LOWEST
:
28 case REQUEST_PRIORITY_LOW
:
30 case REQUEST_PRIORITY_MEDIUM
:
32 case REQUEST_PRIORITY_HIGHEST
:
39 void SetPostContentType(JNIEnv
* env
,
40 URLRequestAdapter
* request
,
41 jstring content_type
) {
42 DCHECK(request
!= NULL
);
44 std::string
method_post("POST");
45 request
->SetMethod(method_post
);
47 std::string
content_type_header("Content-Type");
49 const char* content_type_utf8
= env
->GetStringUTFChars(content_type
, NULL
);
50 std::string
content_type_string(content_type_utf8
);
51 env
->ReleaseStringUTFChars(content_type
, content_type_utf8
);
53 request
->AddHeader(content_type_header
, content_type_string
);
56 // A delegate of URLRequestAdapter that delivers callbacks to the Java layer.
57 class JniURLRequestAdapterDelegate
58 : public URLRequestAdapter::URLRequestAdapterDelegate
{
60 JniURLRequestAdapterDelegate(JNIEnv
* env
, jobject owner
) {
61 owner_
= env
->NewGlobalRef(owner
);
64 virtual void OnResponseStarted(URLRequestAdapter
* request
) OVERRIDE
{
65 JNIEnv
* env
= base::android::AttachCurrentThread();
66 cronet::Java_ChromiumUrlRequest_onResponseStarted(env
, owner_
);
69 virtual void OnBytesRead(URLRequestAdapter
* request
) OVERRIDE
{
70 int bytes_read
= request
->bytes_read();
71 if (bytes_read
!= 0) {
72 JNIEnv
* env
= base::android::AttachCurrentThread();
73 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
74 env
, env
->NewDirectByteBuffer(request
->Data(), bytes_read
));
75 cronet::Java_ChromiumUrlRequest_onBytesRead(
76 env
, owner_
, java_buffer
.obj());
80 virtual void OnRequestFinished(URLRequestAdapter
* request
) OVERRIDE
{
81 JNIEnv
* env
= base::android::AttachCurrentThread();
82 cronet::Java_ChromiumUrlRequest_finish(env
, owner_
);
85 virtual int ReadFromUploadChannel(net::IOBuffer
* buf
,
86 int buf_length
) OVERRIDE
{
87 JNIEnv
* env
= base::android::AttachCurrentThread();
88 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
89 env
, env
->NewDirectByteBuffer(buf
->data(), buf_length
));
90 jint bytes_read
= cronet::Java_ChromiumUrlRequest_readFromUploadChannel(
91 env
, owner_
, java_buffer
.obj());
96 virtual ~JniURLRequestAdapterDelegate() {
97 JNIEnv
* env
= base::android::AttachCurrentThread();
98 env
->DeleteGlobalRef(owner_
);
104 DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate
);
109 // Explicitly register static JNI functions.
110 bool ChromiumUrlRequestRegisterJni(JNIEnv
* env
) {
111 return RegisterNativesImpl(env
);
114 static jlong
CreateRequestAdapter(JNIEnv
* env
,
116 jlong urlRequestContextAdapter
,
119 URLRequestContextAdapter
* context
=
120 reinterpret_cast<URLRequestContextAdapter
*>(urlRequestContextAdapter
);
121 DCHECK(context
!= NULL
);
123 const char* url_utf8
= env
->GetStringUTFChars(url_string
, NULL
);
125 VLOG(1) << "New chromium network request. URL:" << url_utf8
;
129 env
->ReleaseStringUTFChars(url_string
, url_utf8
);
131 URLRequestAdapter
* adapter
=
132 new URLRequestAdapter(context
,
133 new JniURLRequestAdapterDelegate(env
, object
),
135 ConvertRequestPriority(priority
));
137 return reinterpret_cast<jlong
>(adapter
);
141 static void AddHeader(JNIEnv
* env
,
143 jlong urlRequestAdapter
,
146 URLRequestAdapter
* request
=
147 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
150 std::string
name_string(base::android::ConvertJavaStringToUTF8(env
, name
));
151 std::string
value_string(base::android::ConvertJavaStringToUTF8(env
, value
));
153 request
->AddHeader(name_string
, value_string
);
156 static void SetMethod(JNIEnv
* env
,
158 jlong urlRequestAdapter
,
160 URLRequestAdapter
* request
=
161 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
164 std::string
method_string(
165 base::android::ConvertJavaStringToUTF8(env
, method
));
167 request
->SetMethod(method_string
);
170 static void SetUploadData(JNIEnv
* env
,
172 jlong urlRequestAdapter
,
173 jstring content_type
,
174 jbyteArray content
) {
175 URLRequestAdapter
* request
=
176 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
177 SetPostContentType(env
, request
, content_type
);
179 if (content
!= NULL
) {
180 jsize size
= env
->GetArrayLength(content
);
182 jbyte
* content_bytes
= env
->GetByteArrayElements(content
, NULL
);
183 request
->SetUploadContent(reinterpret_cast<const char*>(content_bytes
),
185 env
->ReleaseByteArrayElements(content
, content_bytes
, 0);
190 static void SetUploadChannel(JNIEnv
* env
,
192 jlong urlRequestAdapter
,
193 jstring content_type
,
194 jlong content_length
) {
195 URLRequestAdapter
* request
=
196 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
197 SetPostContentType(env
, request
, content_type
);
199 request
->SetUploadChannel(env
, content_length
);
202 static void EnableChunkedUpload(JNIEnv
* env
,
204 jlong urlRequestAdapter
,
205 jstring content_type
) {
206 URLRequestAdapter
* request
=
207 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
208 SetPostContentType(env
, request
, content_type
);
210 request
->EnableChunkedUpload();
213 static void AppendChunk(JNIEnv
* env
,
215 jlong urlRequestAdapter
,
216 jobject chunk_byte_buffer
,
218 jboolean is_last_chunk
) {
219 URLRequestAdapter
* request
=
220 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
221 DCHECK(chunk_byte_buffer
!= NULL
);
223 void* chunk
= env
->GetDirectBufferAddress(chunk_byte_buffer
);
224 request
->AppendChunk(
225 reinterpret_cast<const char*>(chunk
), chunk_size
, is_last_chunk
);
229 static void Start(JNIEnv
* env
, jobject object
, jlong urlRequestAdapter
) {
230 URLRequestAdapter
* request
=
231 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
232 if (request
!= NULL
) {
238 static void DestroyRequestAdapter(JNIEnv
* env
,
240 jlong urlRequestAdapter
) {
241 URLRequestAdapter
* request
=
242 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
243 if (request
!= NULL
) {
249 static void Cancel(JNIEnv
* env
, jobject object
, jlong urlRequestAdapter
) {
250 URLRequestAdapter
* request
=
251 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
252 if (request
!= NULL
) {
257 static jint
GetErrorCode(JNIEnv
* env
, jobject object
, jlong urlRequestAdapter
) {
258 URLRequestAdapter
* request
=
259 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
260 int error_code
= request
->error_code();
261 switch (error_code
) {
262 // TODO(mef): Investigate returning success on positive values, too, as
263 // they technically indicate success.
265 return REQUEST_ERROR_SUCCESS
;
267 // TODO(mef): Investigate this. The fact is that Chrome does not do this,
268 // and this library is not just being used for downloads.
270 // Comment from src/content/browser/download/download_resource_handler.cc:
271 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
272 // allowed since a number of servers in the wild close the connection too
273 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
274 // treat downloads as complete in both cases, so we follow their lead.
275 case net::ERR_CONTENT_LENGTH_MISMATCH
:
276 case net::ERR_INCOMPLETE_CHUNKED_ENCODING
:
277 return REQUEST_ERROR_SUCCESS
;
279 case net::ERR_INVALID_URL
:
280 case net::ERR_DISALLOWED_URL_SCHEME
:
281 case net::ERR_UNKNOWN_URL_SCHEME
:
282 return REQUEST_ERROR_MALFORMED_URL
;
284 case net::ERR_CONNECTION_TIMED_OUT
:
285 return REQUEST_ERROR_CONNECTION_TIMED_OUT
;
287 case net::ERR_NAME_NOT_RESOLVED
:
288 return REQUEST_ERROR_UNKNOWN_HOST
;
290 return REQUEST_ERROR_UNKNOWN
;
293 static jstring
GetErrorString(JNIEnv
* env
,
295 jlong urlRequestAdapter
) {
296 URLRequestAdapter
* request
=
297 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
298 int error_code
= request
->error_code();
300 std::string error_string
= net::ErrorToString(error_code
);
303 "System error: %s(%d)",
304 error_string
.c_str(),
306 return ConvertUTF8ToJavaString(env
, buffer
).Release();
309 static jint
GetHttpStatusCode(JNIEnv
* env
,
311 jlong urlRequestAdapter
) {
312 URLRequestAdapter
* request
=
313 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
314 return request
->http_status_code();
317 static jstring
GetContentType(JNIEnv
* env
,
319 jlong urlRequestAdapter
) {
320 URLRequestAdapter
* request
=
321 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
322 if (request
== NULL
) {
325 std::string type
= request
->content_type();
327 return ConvertUTF8ToJavaString(env
, type
.c_str()).Release();
333 static jlong
GetContentLength(JNIEnv
* env
,
335 jlong urlRequestAdapter
) {
336 URLRequestAdapter
* request
=
337 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
338 if (request
== NULL
) {
341 return request
->content_length();
344 static jstring
GetHeader(JNIEnv
* env
,
346 jlong urlRequestAdapter
,
348 URLRequestAdapter
* request
=
349 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
350 if (request
== NULL
) {
354 std::string name_string
= base::android::ConvertJavaStringToUTF8(env
, name
);
355 std::string value
= request
->GetHeader(name_string
);
356 if (!value
.empty()) {
357 return ConvertUTF8ToJavaString(env
, value
.c_str()).Release();
363 static void GetAllHeaders(JNIEnv
* env
,
365 jlong urlRequestAdapter
,
366 jobject headersMap
) {
367 URLRequestAdapter
* request
=
368 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
372 net::HttpResponseHeaders
* headers
= request
->GetResponseHeaders();
377 std::string header_name
;
378 std::string header_value
;
379 while (headers
->EnumerateHeaderLines(&iter
, &header_name
, &header_value
)) {
380 ScopedJavaLocalRef
<jstring
> name
=
381 ConvertUTF8ToJavaString(env
, header_name
);
382 ScopedJavaLocalRef
<jstring
> value
=
383 ConvertUTF8ToJavaString(env
, header_value
);
384 Java_ChromiumUrlRequest_onAppendResponseHeader(
385 env
, object
, headersMap
, name
.Release(), value
.Release());
388 // Some implementations (notably HttpURLConnection) include a mapping for the
389 // null key; in HTTP's case, this maps to the HTTP status line.
390 ScopedJavaLocalRef
<jstring
> status_line
=
391 ConvertUTF8ToJavaString(env
, headers
->GetStatusLine());
392 Java_ChromiumUrlRequest_onAppendResponseHeader(
393 env
, object
, headersMap
, NULL
, status_line
.Release());
396 } // namespace cronet