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");
48 std::string content_type_string(
49 base::android::ConvertJavaStringToUTF8(env, content_type));
51 request->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 virtual void OnResponseStarted(URLRequestAdapter* request) OVERRIDE {
63 JNIEnv* env = base::android::AttachCurrentThread();
64 cronet::Java_ChromiumUrlRequest_onResponseStarted(env, owner_);
67 virtual void OnBytesRead(URLRequestAdapter* request) OVERRIDE {
68 int bytes_read = request->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->Data(), bytes_read));
73 cronet::Java_ChromiumUrlRequest_onBytesRead(
74 env, owner_, java_buffer.obj());
78 virtual void OnRequestFinished(URLRequestAdapter* request) OVERRIDE {
79 JNIEnv* env = base::android::AttachCurrentThread();
80 cronet::Java_ChromiumUrlRequest_finish(env, owner_);
83 virtual int ReadFromUploadChannel(net::IOBuffer* buf,
84 int buf_length) OVERRIDE {
85 JNIEnv* env = base::android::AttachCurrentThread();
86 base::android::ScopedJavaLocalRef<jobject> java_buffer(
87 env, env->NewDirectByteBuffer(buf->data(), buf_length));
88 jint bytes_read = cronet::Java_ChromiumUrlRequest_readFromUploadChannel(
89 env, owner_, java_buffer.obj());
94 virtual ~JniURLRequestAdapterDelegate() {
95 JNIEnv* env = base::android::AttachCurrentThread();
96 env->DeleteGlobalRef(owner_);
102 DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate);
107 // Explicitly register static JNI functions.
108 bool ChromiumUrlRequestRegisterJni(JNIEnv* env) {
109 return RegisterNativesImpl(env);
112 static jlong CreateRequestAdapter(JNIEnv* env,
114 jlong urlRequestContextAdapter,
117 URLRequestContextAdapter* context =
118 reinterpret_cast<URLRequestContextAdapter*>(urlRequestContextAdapter);
119 DCHECK(context != NULL);
121 GURL url(base::android::ConvertJavaStringToUTF8(env, url_string));
123 VLOG(1) << "New chromium network request: " << url.possibly_invalid_spec();
125 URLRequestAdapter* adapter =
126 new URLRequestAdapter(context,
127 new JniURLRequestAdapterDelegate(env, object),
129 ConvertRequestPriority(priority));
131 return reinterpret_cast<jlong>(adapter);
135 static void AddHeader(JNIEnv* env,
137 jlong urlRequestAdapter,
140 URLRequestAdapter* request =
141 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
144 std::string name_string(base::android::ConvertJavaStringToUTF8(env, name));
145 std::string value_string(base::android::ConvertJavaStringToUTF8(env, value));
147 request->AddHeader(name_string, value_string);
150 static void SetMethod(JNIEnv* env,
152 jlong urlRequestAdapter,
154 URLRequestAdapter* request =
155 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
158 std::string method_string(
159 base::android::ConvertJavaStringToUTF8(env, method));
161 request->SetMethod(method_string);
164 static void SetUploadData(JNIEnv* env,
166 jlong urlRequestAdapter,
167 jstring content_type,
168 jbyteArray content) {
169 URLRequestAdapter* request =
170 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
171 SetPostContentType(env, request, content_type);
173 if (content != NULL) {
174 jsize size = env->GetArrayLength(content);
176 jbyte* content_bytes = env->GetByteArrayElements(content, NULL);
177 request->SetUploadContent(reinterpret_cast<const char*>(content_bytes),
179 env->ReleaseByteArrayElements(content, content_bytes, 0);
184 static void SetUploadChannel(JNIEnv* env,
186 jlong urlRequestAdapter,
187 jstring content_type,
188 jlong content_length) {
189 URLRequestAdapter* request =
190 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
191 SetPostContentType(env, request, content_type);
193 request->SetUploadChannel(env, content_length);
196 static void EnableChunkedUpload(JNIEnv* env,
198 jlong urlRequestAdapter,
199 jstring content_type) {
200 URLRequestAdapter* request =
201 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
202 SetPostContentType(env, request, content_type);
204 request->EnableChunkedUpload();
207 static void AppendChunk(JNIEnv* env,
209 jlong urlRequestAdapter,
210 jobject chunk_byte_buffer,
212 jboolean is_last_chunk) {
213 URLRequestAdapter* request =
214 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
215 DCHECK(chunk_byte_buffer != NULL);
217 void* chunk = env->GetDirectBufferAddress(chunk_byte_buffer);
218 request->AppendChunk(
219 reinterpret_cast<const char*>(chunk), chunk_size, is_last_chunk);
223 static void Start(JNIEnv* env, jobject object, jlong urlRequestAdapter) {
224 URLRequestAdapter* request =
225 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
226 if (request != NULL) {
232 static void DestroyRequestAdapter(JNIEnv* env,
234 jlong urlRequestAdapter) {
235 URLRequestAdapter* request =
236 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
237 if (request != NULL) {
243 static void Cancel(JNIEnv* env, jobject object, jlong urlRequestAdapter) {
244 URLRequestAdapter* request =
245 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
246 if (request != NULL) {
251 static jint GetErrorCode(JNIEnv* env, jobject object, jlong urlRequestAdapter) {
252 URLRequestAdapter* request =
253 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
254 int error_code = request->error_code();
255 switch (error_code) {
256 // TODO(mef): Investigate returning success on positive values, too, as
257 // they technically indicate success.
259 return REQUEST_ERROR_SUCCESS;
261 // TODO(mef): Investigate this. The fact is that Chrome does not do this,
262 // and this library is not just being used for downloads.
264 // Comment from src/content/browser/download/download_resource_handler.cc:
265 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
266 // allowed since a number of servers in the wild close the connection too
267 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
268 // treat downloads as complete in both cases, so we follow their lead.
269 case net::ERR_CONTENT_LENGTH_MISMATCH:
270 case net::ERR_INCOMPLETE_CHUNKED_ENCODING:
271 return REQUEST_ERROR_SUCCESS;
273 case net::ERR_INVALID_URL:
274 case net::ERR_DISALLOWED_URL_SCHEME:
275 case net::ERR_UNKNOWN_URL_SCHEME:
276 return REQUEST_ERROR_MALFORMED_URL;
278 case net::ERR_CONNECTION_TIMED_OUT:
279 return REQUEST_ERROR_CONNECTION_TIMED_OUT;
281 case net::ERR_NAME_NOT_RESOLVED:
282 return REQUEST_ERROR_UNKNOWN_HOST;
284 return REQUEST_ERROR_UNKNOWN;
287 static jstring GetErrorString(JNIEnv* env,
289 jlong urlRequestAdapter) {
290 URLRequestAdapter* request =
291 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
292 int error_code = request->error_code();
294 std::string error_string = net::ErrorToString(error_code);
297 "System error: %s(%d)",
298 error_string.c_str(),
300 return ConvertUTF8ToJavaString(env, buffer).Release();
303 static jint GetHttpStatusCode(JNIEnv* env,
305 jlong urlRequestAdapter) {
306 URLRequestAdapter* request =
307 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
308 return request->http_status_code();
311 static jstring GetContentType(JNIEnv* env,
313 jlong urlRequestAdapter) {
314 URLRequestAdapter* request =
315 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
316 if (request == NULL) {
319 std::string type = request->content_type();
321 return ConvertUTF8ToJavaString(env, type.c_str()).Release();
327 static jlong GetContentLength(JNIEnv* env,
329 jlong urlRequestAdapter) {
330 URLRequestAdapter* request =
331 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
332 if (request == NULL) {
335 return request->content_length();
338 static jstring GetHeader(JNIEnv* env,
340 jlong urlRequestAdapter,
342 URLRequestAdapter* request =
343 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
344 if (request == NULL) {
348 std::string name_string = base::android::ConvertJavaStringToUTF8(env, name);
349 std::string value = request->GetHeader(name_string);
350 if (!value.empty()) {
351 return ConvertUTF8ToJavaString(env, value.c_str()).Release();
357 static void GetAllHeaders(JNIEnv* env,
359 jlong urlRequestAdapter,
360 jobject headersMap) {
361 URLRequestAdapter* request =
362 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
366 net::HttpResponseHeaders* headers = request->GetResponseHeaders();
371 std::string header_name;
372 std::string header_value;
373 while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) {
374 ScopedJavaLocalRef<jstring> name =
375 ConvertUTF8ToJavaString(env, header_name);
376 ScopedJavaLocalRef<jstring> value =
377 ConvertUTF8ToJavaString(env, header_value);
378 Java_ChromiumUrlRequest_onAppendResponseHeader(
379 env, object, headersMap, name.Release(), value.Release());
382 // Some implementations (notably HttpURLConnection) include a mapping for the
383 // null key; in HTTP's case, this maps to the HTTP status line.
384 ScopedJavaLocalRef<jstring> status_line =
385 ConvertUTF8ToJavaString(env, headers->GetStatusLine());
386 Java_ChromiumUrlRequest_onAppendResponseHeader(
387 env, object, headersMap, NULL, status_line.Release());
390 static jstring GetNegotiatedProtocol(JNIEnv* env,
392 jlong urlRequestAdapter) {
393 URLRequestAdapter* request =
394 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter);
396 return ConvertUTF8ToJavaString(env, "").Release();
398 std::string negotiated_protocol = request->GetNegotiatedProtocol();
399 return ConvertUTF8ToJavaString(env, negotiated_protocol.c_str()).Release();
402 } // namespace cronet