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/org_chromium_net_UrlRequest.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/UrlRequest_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_UrlRequest_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_UrlRequest_onBytesRead(env
, owner_
, java_buffer
.obj());
79 virtual void OnRequestFinished(URLRequestAdapter
* request
) OVERRIDE
{
80 JNIEnv
* env
= base::android::AttachCurrentThread();
81 cronet::Java_UrlRequest_finish(env
, owner_
);
84 virtual int ReadFromUploadChannel(net::IOBuffer
* buf
,
85 int buf_length
) OVERRIDE
{
86 JNIEnv
* env
= base::android::AttachCurrentThread();
87 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
88 env
, env
->NewDirectByteBuffer(buf
->data(), buf_length
));
89 jint bytes_read
= cronet::Java_UrlRequest_readFromUploadChannel(
90 env
, owner_
, java_buffer
.obj());
95 virtual ~JniURLRequestAdapterDelegate() {
96 JNIEnv
* env
= base::android::AttachCurrentThread();
97 env
->DeleteGlobalRef(owner_
);
103 DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate
);
108 // Explicitly register static JNI functions.
109 bool UrlRequestRegisterJni(JNIEnv
* env
) { return RegisterNativesImpl(env
); }
111 static jlong
CreateRequestAdapter(JNIEnv
* env
,
113 jlong urlRequestContextAdapter
,
116 URLRequestContextAdapter
* context
=
117 reinterpret_cast<URLRequestContextAdapter
*>(urlRequestContextAdapter
);
118 DCHECK(context
!= NULL
);
120 const char* url_utf8
= env
->GetStringUTFChars(url_string
, NULL
);
122 VLOG(1) << "New chromium network request. URL:" << url_utf8
;
126 env
->ReleaseStringUTFChars(url_string
, url_utf8
);
128 URLRequestAdapter
* adapter
=
129 new URLRequestAdapter(context
,
130 new JniURLRequestAdapterDelegate(env
, object
),
132 ConvertRequestPriority(priority
));
134 return reinterpret_cast<jlong
>(adapter
);
138 static void AddHeader(JNIEnv
* env
,
140 jlong urlRequestAdapter
,
143 URLRequestAdapter
* request
=
144 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
147 std::string
name_string(base::android::ConvertJavaStringToUTF8(env
, name
));
148 std::string
value_string(base::android::ConvertJavaStringToUTF8(env
, value
));
150 request
->AddHeader(name_string
, value_string
);
153 static void SetMethod(JNIEnv
* env
,
155 jlong urlRequestAdapter
,
157 URLRequestAdapter
* request
=
158 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
161 std::string
method_string(
162 base::android::ConvertJavaStringToUTF8(env
, method
));
164 request
->SetMethod(method_string
);
167 static void SetUploadData(JNIEnv
* env
,
169 jlong urlRequestAdapter
,
170 jstring content_type
,
171 jbyteArray content
) {
172 URLRequestAdapter
* request
=
173 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
174 SetPostContentType(env
, request
, content_type
);
176 if (content
!= NULL
) {
177 jsize size
= env
->GetArrayLength(content
);
179 jbyte
* content_bytes
= env
->GetByteArrayElements(content
, NULL
);
180 request
->SetUploadContent(reinterpret_cast<const char*>(content_bytes
),
182 env
->ReleaseByteArrayElements(content
, content_bytes
, 0);
187 static void SetUploadChannel(JNIEnv
* env
,
189 jlong urlRequestAdapter
,
190 jstring content_type
,
191 jlong content_length
) {
192 URLRequestAdapter
* request
=
193 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
194 SetPostContentType(env
, request
, content_type
);
196 request
->SetUploadChannel(env
, content_length
);
201 static void Start(JNIEnv
* env
, jobject object
, jlong urlRequestAdapter
) {
202 URLRequestAdapter
* request
=
203 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
204 if (request
!= NULL
) {
210 static void DestroyRequestAdapter(JNIEnv
* env
,
212 jlong urlRequestAdapter
) {
213 URLRequestAdapter
* request
=
214 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
215 if (request
!= NULL
) {
221 static void Cancel(JNIEnv
* env
, jobject object
, jlong urlRequestAdapter
) {
222 URLRequestAdapter
* request
=
223 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
224 if (request
!= NULL
) {
229 static jint
GetErrorCode(JNIEnv
* env
, jobject object
, jlong urlRequestAdapter
) {
230 URLRequestAdapter
* request
=
231 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
232 int error_code
= request
->error_code();
233 switch (error_code
) {
234 // TODO(mef): Investigate returning success on positive values, too, as
235 // they technically indicate success.
237 return REQUEST_ERROR_SUCCESS
;
239 // TODO(mef): Investigate this. The fact is that Chrome does not do this,
240 // and this library is not just being used for downloads.
242 // Comment from src/content/browser/download/download_resource_handler.cc:
243 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
244 // allowed since a number of servers in the wild close the connection too
245 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
246 // treat downloads as complete in both cases, so we follow their lead.
247 case net::ERR_CONTENT_LENGTH_MISMATCH
:
248 case net::ERR_INCOMPLETE_CHUNKED_ENCODING
:
249 return REQUEST_ERROR_SUCCESS
;
251 case net::ERR_INVALID_URL
:
252 case net::ERR_DISALLOWED_URL_SCHEME
:
253 case net::ERR_UNKNOWN_URL_SCHEME
:
254 return REQUEST_ERROR_MALFORMED_URL
;
256 case net::ERR_CONNECTION_TIMED_OUT
:
257 return REQUEST_ERROR_CONNECTION_TIMED_OUT
;
259 case net::ERR_NAME_NOT_RESOLVED
:
260 return REQUEST_ERROR_UNKNOWN_HOST
;
262 return REQUEST_ERROR_UNKNOWN
;
265 static jstring
GetErrorString(JNIEnv
* env
,
267 jlong urlRequestAdapter
) {
268 URLRequestAdapter
* request
=
269 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
270 int error_code
= request
->error_code();
272 std::string error_string
= net::ErrorToString(error_code
);
275 "System error: %s(%d)",
276 error_string
.c_str(),
278 return ConvertUTF8ToJavaString(env
, buffer
).Release();
281 static jint
GetHttpStatusCode(JNIEnv
* env
,
283 jlong urlRequestAdapter
) {
284 URLRequestAdapter
* request
=
285 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
286 return request
->http_status_code();
289 static jstring
GetContentType(JNIEnv
* env
,
291 jlong urlRequestAdapter
) {
292 URLRequestAdapter
* request
=
293 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
294 if (request
== NULL
) {
297 std::string type
= request
->content_type();
299 return ConvertUTF8ToJavaString(env
, type
.c_str()).Release();
305 static jlong
GetContentLength(JNIEnv
* env
,
307 jlong urlRequestAdapter
) {
308 URLRequestAdapter
* request
=
309 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
310 if (request
== NULL
) {
313 return request
->content_length();
316 static jstring
GetHeader(JNIEnv
* env
,
318 jlong urlRequestAdapter
,
320 URLRequestAdapter
* request
=
321 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
322 if (request
== NULL
) {
326 std::string name_string
= base::android::ConvertJavaStringToUTF8(env
, name
);
327 std::string value
= request
->GetHeader(name_string
);
328 if (!value
.empty()) {
329 return ConvertUTF8ToJavaString(env
, value
.c_str()).Release();
335 static void GetAllHeaders(JNIEnv
* env
,
337 jlong urlRequestAdapter
,
338 jobject headersMap
) {
339 URLRequestAdapter
* request
=
340 reinterpret_cast<URLRequestAdapter
*>(urlRequestAdapter
);
344 net::HttpResponseHeaders
* headers
= request
->GetResponseHeaders();
349 std::string header_name
;
350 std::string header_value
;
351 while (headers
->EnumerateHeaderLines(&iter
, &header_name
, &header_value
)) {
352 ScopedJavaLocalRef
<jstring
> name
=
353 ConvertUTF8ToJavaString(env
, header_name
);
354 ScopedJavaLocalRef
<jstring
> value
=
355 ConvertUTF8ToJavaString(env
, header_value
);
356 Java_UrlRequest_onAppendResponseHeader(
357 env
, object
, headersMap
, name
.Release(), value
.Release());
360 // Some implementations (notably HttpURLConnection) include a mapping for the
361 // null key; in HTTP's case, this maps to the HTTP status line.
362 ScopedJavaLocalRef
<jstring
> status_line
=
363 ConvertUTF8ToJavaString(env
, headers
->GetStatusLine());
364 Java_UrlRequest_onAppendResponseHeader(
365 env
, object
, headersMap
, NULL
, status_line
.Release());
368 } // namespace cronet