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 DCHECK(request_adapter
);
45 std::string
method_post("POST");
46 request_adapter
->SetMethod(method_post
);
48 std::string
content_type_header("Content-Type");
49 std::string
content_type_string(ConvertJavaStringToUTF8(env
, content_type
));
51 request_adapter
->AddHeader(content_type_header
, content_type_string
);
54 // A delegate of URLRequestAdapter that delivers callbacks to the Java layer.
55 class JniURLRequestAdapterDelegate
56 : public URLRequestAdapter::URLRequestAdapterDelegate
{
58 JniURLRequestAdapterDelegate(JNIEnv
* env
, jobject owner
) {
59 owner_
= env
->NewGlobalRef(owner
);
62 void OnResponseStarted(URLRequestAdapter
* request_adapter
) override
{
63 JNIEnv
* env
= base::android::AttachCurrentThread();
64 cronet::Java_ChromiumUrlRequest_onResponseStarted(env
, owner_
);
67 void OnBytesRead(URLRequestAdapter
* request_adapter
) override
{
68 int bytes_read
= request_adapter
->bytes_read();
69 if (bytes_read
!= 0) {
70 JNIEnv
* env
= base::android::AttachCurrentThread();
71 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
72 env
, env
->NewDirectByteBuffer(request_adapter
->Data(), bytes_read
));
73 cronet::Java_ChromiumUrlRequest_onBytesRead(
74 env
, owner_
, java_buffer
.obj());
78 void OnRequestFinished(URLRequestAdapter
* request_adapter
) override
{
79 JNIEnv
* env
= base::android::AttachCurrentThread();
80 cronet::Java_ChromiumUrlRequest_finish(env
, owner_
);
83 int ReadFromUploadChannel(net::IOBuffer
* buf
, int buf_length
) override
{
84 JNIEnv
* env
= base::android::AttachCurrentThread();
85 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
86 env
, env
->NewDirectByteBuffer(buf
->data(), buf_length
));
87 jint bytes_read
= cronet::Java_ChromiumUrlRequest_readFromUploadChannel(
88 env
, owner_
, java_buffer
.obj());
93 ~JniURLRequestAdapterDelegate() override
{
94 JNIEnv
* env
= base::android::AttachCurrentThread();
95 env
->DeleteGlobalRef(owner_
);
101 DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate
);
106 // Explicitly register static JNI functions.
107 bool ChromiumUrlRequestRegisterJni(JNIEnv
* env
) {
108 return RegisterNativesImpl(env
);
111 static jlong
CreateRequestAdapter(JNIEnv
* env
,
113 jlong jurl_request_context_adapter
,
115 jint jrequest_priority
) {
116 URLRequestContextAdapter
* context_adapter
=
117 reinterpret_cast<URLRequestContextAdapter
*>(jurl_request_context_adapter
);
118 DCHECK(context_adapter
);
120 GURL
url(ConvertJavaStringToUTF8(env
, jurl
));
122 VLOG(1) << "New chromium network request: " << url
.possibly_invalid_spec();
124 URLRequestAdapter
* adapter
= new URLRequestAdapter(
125 context_adapter
, new JniURLRequestAdapterDelegate(env
, jcaller
), url
,
126 ConvertRequestPriority(jrequest_priority
));
128 return reinterpret_cast<jlong
>(adapter
);
132 static void AddHeader(JNIEnv
* env
,
134 jlong jurl_request_adapter
,
135 jstring jheader_name
,
136 jstring jheader_value
) {
137 URLRequestAdapter
* request_adapter
=
138 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
139 DCHECK(request_adapter
);
141 std::string
header_name(ConvertJavaStringToUTF8(env
, jheader_name
));
142 std::string
header_value(ConvertJavaStringToUTF8(env
, jheader_value
));
144 request_adapter
->AddHeader(header_name
, header_value
);
147 static void SetMethod(JNIEnv
* env
,
149 jlong jurl_request_adapter
,
151 URLRequestAdapter
* request_adapter
=
152 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
153 DCHECK(request_adapter
);
155 std::string
method(ConvertJavaStringToUTF8(env
, jmethod
));
157 request_adapter
->SetMethod(method
);
160 static void SetUploadData(JNIEnv
* env
,
162 jlong jurl_request_adapter
,
163 jstring jcontent_type
,
164 jbyteArray jcontent
) {
165 URLRequestAdapter
* request_adapter
=
166 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
167 SetPostContentType(env
, request_adapter
, jcontent_type
);
169 if (jcontent
!= NULL
) {
170 jsize size
= env
->GetArrayLength(jcontent
);
172 jbyte
* content_bytes
= env
->GetByteArrayElements(jcontent
, NULL
);
173 request_adapter
->SetUploadContent(
174 reinterpret_cast<const char*>(content_bytes
), size
);
175 env
->ReleaseByteArrayElements(jcontent
, content_bytes
, 0);
180 static void SetUploadChannel(JNIEnv
* env
,
182 jlong jurl_request_adapter
,
183 jstring jcontent_type
,
184 jlong jcontent_length
) {
185 URLRequestAdapter
* request_adapter
=
186 reinterpret_cast<URLRequestAdapter
*>(jurl_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 SetPostContentType(env
, request_adapter
, jcontent_type
);
200 request_adapter
->EnableChunkedUpload();
203 static void AppendChunk(JNIEnv
* env
,
205 jlong jurl_request_adapter
,
206 jobject jchunk_byte_buffer
,
208 jboolean jis_last_chunk
) {
209 URLRequestAdapter
* request_adapter
=
210 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
211 DCHECK(jchunk_byte_buffer
);
213 void* chunk
= env
->GetDirectBufferAddress(jchunk_byte_buffer
);
214 request_adapter
->AppendChunk(reinterpret_cast<const char*>(chunk
),
215 jchunk_size
, jis_last_chunk
);
219 static void Start(JNIEnv
* env
, jobject jcaller
, jlong jurl_request_adapter
) {
220 URLRequestAdapter
* request_adapter
=
221 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
222 if (request_adapter
!= NULL
)
223 request_adapter
->Start();
227 static void DestroyRequestAdapter(JNIEnv
* env
,
229 jlong jurl_request_adapter
) {
230 URLRequestAdapter
* request_adapter
=
231 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
232 if (request_adapter
!= NULL
)
233 request_adapter
->Destroy();
237 static void Cancel(JNIEnv
* env
, jobject jcaller
, jlong jurl_request_adapter
) {
238 URLRequestAdapter
* request_adapter
=
239 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
240 if (request_adapter
!= NULL
)
241 request_adapter
->Cancel();
244 static jint
GetErrorCode(JNIEnv
* env
,
246 jlong jurl_request_adapter
) {
247 URLRequestAdapter
* request_adapter
=
248 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
249 int error_code
= request_adapter
->error_code();
250 switch (error_code
) {
251 // TODO(mef): Investigate returning success on positive values, too, as
252 // they technically indicate success.
254 return REQUEST_ERROR_SUCCESS
;
256 // TODO(mef): Investigate this. The fact is that Chrome does not do this,
257 // and this library is not just being used for downloads.
259 // Comment from src/content/browser/download/download_resource_handler.cc:
260 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
261 // allowed since a number of servers in the wild close the connection too
262 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
263 // treat downloads as complete in both cases, so we follow their lead.
264 case net::ERR_CONTENT_LENGTH_MISMATCH
:
265 case net::ERR_INCOMPLETE_CHUNKED_ENCODING
:
266 return REQUEST_ERROR_SUCCESS
;
268 case net::ERR_INVALID_URL
:
269 case net::ERR_DISALLOWED_URL_SCHEME
:
270 case net::ERR_UNKNOWN_URL_SCHEME
:
271 return REQUEST_ERROR_MALFORMED_URL
;
273 case net::ERR_CONNECTION_TIMED_OUT
:
274 return REQUEST_ERROR_CONNECTION_TIMED_OUT
;
276 case net::ERR_NAME_NOT_RESOLVED
:
277 return REQUEST_ERROR_UNKNOWN_HOST
;
278 case net::ERR_TOO_MANY_REDIRECTS
:
279 return REQUEST_ERROR_TOO_MANY_REDIRECTS
;
281 return REQUEST_ERROR_UNKNOWN
;
284 static jstring
GetErrorString(JNIEnv
* env
,
286 jlong jurl_request_adapter
) {
287 URLRequestAdapter
* request_adapter
=
288 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
289 int error_code
= request_adapter
->error_code();
291 std::string error_string
= net::ErrorToString(error_code
);
294 "System error: %s(%d)",
295 error_string
.c_str(),
297 return ConvertUTF8ToJavaString(env
, buffer
).Release();
300 static jint
GetHttpStatusCode(JNIEnv
* env
,
302 jlong jurl_request_adapter
) {
303 URLRequestAdapter
* request_adapter
=
304 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
305 return request_adapter
->http_status_code();
308 static jstring
GetHttpStatusText(JNIEnv
* env
,
310 jlong jurl_request_adapter
) {
311 URLRequestAdapter
* request_adapter
=
312 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
313 return ConvertUTF8ToJavaString(env
, request_adapter
->http_status_text())
317 static jstring
GetContentType(JNIEnv
* env
,
319 jlong jurl_request_adapter
) {
320 URLRequestAdapter
* request_adapter
=
321 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
322 if (request_adapter
== NULL
)
324 std::string type
= request_adapter
->content_type();
326 return ConvertUTF8ToJavaString(env
, type
.c_str()).Release();
332 static jlong
GetContentLength(JNIEnv
* env
,
334 jlong jurl_request_adapter
) {
335 URLRequestAdapter
* request_adapter
=
336 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
337 if (request_adapter
== NULL
)
339 return request_adapter
->content_length();
342 static jstring
GetHeader(JNIEnv
* env
,
344 jlong jurl_request_adapter
,
345 jstring jheader_name
) {
346 URLRequestAdapter
* request_adapter
=
347 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
348 if (request_adapter
== NULL
)
350 std::string header_name
= ConvertJavaStringToUTF8(env
, jheader_name
);
351 std::string header_value
= request_adapter
->GetHeader(header_name
);
352 if (!header_value
.empty()) {
353 return ConvertUTF8ToJavaString(env
, header_value
.c_str()).Release();
359 static void GetAllHeaders(JNIEnv
* env
,
361 jlong jurl_request_adapter
,
362 jobject jheaders_map
) {
363 URLRequestAdapter
* request_adapter
=
364 reinterpret_cast<URLRequestAdapter
*>(jurl_request_adapter
);
365 if (request_adapter
== NULL
)
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 if (request_adapter
== NULL
)
398 return ConvertUTF8ToJavaString(env
, "").Release();
400 std::string negotiated_protocol
= request_adapter
->GetNegotiatedProtocol();
401 return ConvertUTF8ToJavaString(env
, negotiated_protocol
.c_str()).Release();
404 static void DisableRedirects(JNIEnv
* env
, jobject jcaller
,
405 jlong jrequest_adapter
) {
406 URLRequestAdapter
* request_adapter
=
407 reinterpret_cast<URLRequestAdapter
*>(jrequest_adapter
);
408 if (request_adapter
!= NULL
)
409 request_adapter
->DisableRedirects();
412 } // namespace cronet