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. 4 5#include "components/cronet/android/chromium_url_request.h" 6 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" 16 17using base::android::ConvertUTF8ToJavaString; 18 19namespace cronet { 20namespace { 21 22net::RequestPriority ConvertRequestPriority(jint request_priority) { 23 switch (request_priority) { 24 case REQUEST_PRIORITY_IDLE: 25 return net::IDLE; 26 case REQUEST_PRIORITY_LOWEST: 27 return net::LOWEST; 28 case REQUEST_PRIORITY_LOW: 29 return net::LOW; 30 case REQUEST_PRIORITY_MEDIUM: 31 return net::MEDIUM; 32 case REQUEST_PRIORITY_HIGHEST: 33 return net::HIGHEST; 34 default: 35 return net::LOWEST; 36 } 37} 38 39void SetPostContentType(JNIEnv* env, 40 URLRequestAdapter* request, 41 jstring content_type) { 42 DCHECK(request != NULL); 43 44 std::string method_post("POST"); 45 request->SetMethod(method_post); 46 47 std::string content_type_header("Content-Type"); 48 std::string content_type_string( 49 base::android::ConvertJavaStringToUTF8(env, content_type)); 50 51 request->AddHeader(content_type_header, content_type_string); 52} 53 54// A delegate of URLRequestAdapter that delivers callbacks to the Java layer. 55class JniURLRequestAdapterDelegate 56 : public URLRequestAdapter::URLRequestAdapterDelegate { 57 public: 58 JniURLRequestAdapterDelegate(JNIEnv* env, jobject owner) { 59 owner_ = env->NewGlobalRef(owner); 60 } 61 62 virtual void OnResponseStarted(URLRequestAdapter* request) OVERRIDE { 63 JNIEnv* env = base::android::AttachCurrentThread(); 64 cronet::Java_ChromiumUrlRequest_onResponseStarted(env, owner_); 65 } 66 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()); 75 } 76 } 77 78 virtual void OnRequestFinished(URLRequestAdapter* request) OVERRIDE { 79 JNIEnv* env = base::android::AttachCurrentThread(); 80 cronet::Java_ChromiumUrlRequest_finish(env, owner_); 81 } 82 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()); 90 return bytes_read; 91 } 92 93 protected: 94 virtual ~JniURLRequestAdapterDelegate() { 95 JNIEnv* env = base::android::AttachCurrentThread(); 96 env->DeleteGlobalRef(owner_); 97 } 98 99 private: 100 jobject owner_; 101 102 DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate); 103}; 104 105} // namespace 106 107// Explicitly register static JNI functions. 108bool ChromiumUrlRequestRegisterJni(JNIEnv* env) { 109 return RegisterNativesImpl(env); 110} 111 112static jlong CreateRequestAdapter(JNIEnv* env, 113 jobject object, 114 jlong urlRequestContextAdapter, 115 jstring url_string, 116 jint priority) { 117 URLRequestContextAdapter* context = 118 reinterpret_cast<URLRequestContextAdapter*>(urlRequestContextAdapter); 119 DCHECK(context != NULL); 120 121 GURL url(base::android::ConvertJavaStringToUTF8(env, url_string)); 122 123 VLOG(1) << "New chromium network request: " << url.possibly_invalid_spec(); 124 125 URLRequestAdapter* adapter = 126 new URLRequestAdapter(context, 127 new JniURLRequestAdapterDelegate(env, object), 128 url, 129 ConvertRequestPriority(priority)); 130 131 return reinterpret_cast<jlong>(adapter); 132} 133 134// synchronized 135static void AddHeader(JNIEnv* env, 136 jobject object, 137 jlong urlRequestAdapter, 138 jstring name, 139 jstring value) { 140 URLRequestAdapter* request = 141 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 142 DCHECK(request); 143 144 std::string name_string(base::android::ConvertJavaStringToUTF8(env, name)); 145 std::string value_string(base::android::ConvertJavaStringToUTF8(env, value)); 146 147 request->AddHeader(name_string, value_string); 148} 149 150static void SetMethod(JNIEnv* env, 151 jobject object, 152 jlong urlRequestAdapter, 153 jstring method) { 154 URLRequestAdapter* request = 155 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 156 DCHECK(request); 157 158 std::string method_string( 159 base::android::ConvertJavaStringToUTF8(env, method)); 160 161 request->SetMethod(method_string); 162} 163 164static void SetUploadData(JNIEnv* env, 165 jobject object, 166 jlong urlRequestAdapter, 167 jstring content_type, 168 jbyteArray content) { 169 URLRequestAdapter* request = 170 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 171 SetPostContentType(env, request, content_type); 172 173 if (content != NULL) { 174 jsize size = env->GetArrayLength(content); 175 if (size > 0) { 176 jbyte* content_bytes = env->GetByteArrayElements(content, NULL); 177 request->SetUploadContent(reinterpret_cast<const char*>(content_bytes), 178 size); 179 env->ReleaseByteArrayElements(content, content_bytes, 0); 180 } 181 } 182} 183 184static void SetUploadChannel(JNIEnv* env, 185 jobject object, 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); 192 193 request->SetUploadChannel(env, content_length); 194} 195 196static void EnableChunkedUpload(JNIEnv* env, 197 jobject object, 198 jlong urlRequestAdapter, 199 jstring content_type) { 200 URLRequestAdapter* request = 201 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 202 SetPostContentType(env, request, content_type); 203 204 request->EnableChunkedUpload(); 205} 206 207static void AppendChunk(JNIEnv* env, 208 jobject object, 209 jlong urlRequestAdapter, 210 jobject chunk_byte_buffer, 211 jint chunk_size, 212 jboolean is_last_chunk) { 213 URLRequestAdapter* request = 214 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 215 DCHECK(chunk_byte_buffer != NULL); 216 217 void* chunk = env->GetDirectBufferAddress(chunk_byte_buffer); 218 request->AppendChunk( 219 reinterpret_cast<const char*>(chunk), chunk_size, is_last_chunk); 220} 221 222/* synchronized */ 223static void Start(JNIEnv* env, jobject object, jlong urlRequestAdapter) { 224 URLRequestAdapter* request = 225 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 226 if (request != NULL) { 227 request->Start(); 228 } 229} 230 231/* synchronized */ 232static void DestroyRequestAdapter(JNIEnv* env, 233 jobject object, 234 jlong urlRequestAdapter) { 235 URLRequestAdapter* request = 236 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 237 if (request != NULL) { 238 request->Destroy(); 239 } 240} 241 242/* synchronized */ 243static void Cancel(JNIEnv* env, jobject object, jlong urlRequestAdapter) { 244 URLRequestAdapter* request = 245 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 246 if (request != NULL) { 247 request->Cancel(); 248 } 249} 250 251static 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. 258 case net::OK: 259 return REQUEST_ERROR_SUCCESS; 260 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. 263 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; 272 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; 277 278 case net::ERR_CONNECTION_TIMED_OUT: 279 return REQUEST_ERROR_CONNECTION_TIMED_OUT; 280 281 case net::ERR_NAME_NOT_RESOLVED: 282 return REQUEST_ERROR_UNKNOWN_HOST; 283 } 284 return REQUEST_ERROR_UNKNOWN; 285} 286 287static jstring GetErrorString(JNIEnv* env, 288 jobject object, 289 jlong urlRequestAdapter) { 290 URLRequestAdapter* request = 291 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 292 int error_code = request->error_code(); 293 char buffer[200]; 294 std::string error_string = net::ErrorToString(error_code); 295 snprintf(buffer, 296 sizeof(buffer), 297 "System error: %s(%d)", 298 error_string.c_str(), 299 error_code); 300 return ConvertUTF8ToJavaString(env, buffer).Release(); 301} 302 303static jint GetHttpStatusCode(JNIEnv* env, 304 jobject object, 305 jlong urlRequestAdapter) { 306 URLRequestAdapter* request = 307 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 308 return request->http_status_code(); 309} 310 311static jstring GetContentType(JNIEnv* env, 312 jobject object, 313 jlong urlRequestAdapter) { 314 URLRequestAdapter* request = 315 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 316 if (request == NULL) { 317 return NULL; 318 } 319 std::string type = request->content_type(); 320 if (!type.empty()) { 321 return ConvertUTF8ToJavaString(env, type.c_str()).Release(); 322 } else { 323 return NULL; 324 } 325} 326 327static jlong GetContentLength(JNIEnv* env, 328 jobject object, 329 jlong urlRequestAdapter) { 330 URLRequestAdapter* request = 331 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 332 if (request == NULL) { 333 return 0; 334 } 335 return request->content_length(); 336} 337 338static jstring GetHeader(JNIEnv* env, 339 jobject object, 340 jlong urlRequestAdapter, 341 jstring name) { 342 URLRequestAdapter* request = 343 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 344 if (request == NULL) { 345 return NULL; 346 } 347 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(); 352 } else { 353 return NULL; 354 } 355} 356 357static void GetAllHeaders(JNIEnv* env, 358 jobject object, 359 jlong urlRequestAdapter, 360 jobject headersMap) { 361 URLRequestAdapter* request = 362 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 363 if (request == NULL) 364 return; 365 366 net::HttpResponseHeaders* headers = request->GetResponseHeaders(); 367 if (headers == NULL) 368 return; 369 370 void* iter = NULL; 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()); 380 } 381 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()); 388} 389 390static jstring GetNegotiatedProtocol(JNIEnv* env, 391 jobject object, 392 jlong urlRequestAdapter) { 393 URLRequestAdapter* request = 394 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); 395 if (request == NULL) 396 return ConvertUTF8ToJavaString(env, "").Release(); 397 398 std::string negotiated_protocol = request->GetNegotiatedProtocol(); 399 return ConvertUTF8ToJavaString(env, negotiated_protocol.c_str()).Release(); 400} 401 402} // namespace cronet 403