| OLD | NEW |
| (Empty) |
| 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/org_chromium_net_UrlRequest.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/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" | |
| 16 | |
| 17 using base::android::ConvertUTF8ToJavaString; | |
| 18 | |
| 19 namespace cronet { | |
| 20 namespace { | |
| 21 | |
| 22 net::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 | |
| 39 void 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 | |
| 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); | |
| 52 | |
| 53 request->AddHeader(content_type_header, content_type_string); | |
| 54 } | |
| 55 | |
| 56 // A delegate of URLRequestAdapter that delivers callbacks to the Java layer. | |
| 57 class JniURLRequestAdapterDelegate | |
| 58 : public URLRequestAdapter::URLRequestAdapterDelegate { | |
| 59 public: | |
| 60 JniURLRequestAdapterDelegate(JNIEnv* env, jobject owner) { | |
| 61 owner_ = env->NewGlobalRef(owner); | |
| 62 } | |
| 63 | |
| 64 virtual void OnResponseStarted(URLRequestAdapter* request) OVERRIDE { | |
| 65 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 66 cronet::Java_UrlRequest_onResponseStarted(env, owner_); | |
| 67 } | |
| 68 | |
| 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()); | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 virtual void OnRequestFinished(URLRequestAdapter* request) OVERRIDE { | |
| 80 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 81 cronet::Java_UrlRequest_finish(env, owner_); | |
| 82 } | |
| 83 | |
| 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()); | |
| 91 return bytes_read; | |
| 92 } | |
| 93 | |
| 94 protected: | |
| 95 virtual ~JniURLRequestAdapterDelegate() { | |
| 96 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 97 env->DeleteGlobalRef(owner_); | |
| 98 } | |
| 99 | |
| 100 private: | |
| 101 jobject owner_; | |
| 102 | |
| 103 DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate); | |
| 104 }; | |
| 105 | |
| 106 } // namespace | |
| 107 | |
| 108 // Explicitly register static JNI functions. | |
| 109 bool UrlRequestRegisterJni(JNIEnv* env) { return RegisterNativesImpl(env); } | |
| 110 | |
| 111 static jlong CreateRequestAdapter(JNIEnv* env, | |
| 112 jobject object, | |
| 113 jlong urlRequestContextAdapter, | |
| 114 jstring url_string, | |
| 115 jint priority) { | |
| 116 URLRequestContextAdapter* context = | |
| 117 reinterpret_cast<URLRequestContextAdapter*>(urlRequestContextAdapter); | |
| 118 DCHECK(context != NULL); | |
| 119 | |
| 120 const char* url_utf8 = env->GetStringUTFChars(url_string, NULL); | |
| 121 | |
| 122 VLOG(1) << "New chromium network request. URL:" << url_utf8; | |
| 123 | |
| 124 GURL url(url_utf8); | |
| 125 | |
| 126 env->ReleaseStringUTFChars(url_string, url_utf8); | |
| 127 | |
| 128 URLRequestAdapter* adapter = | |
| 129 new URLRequestAdapter(context, | |
| 130 new JniURLRequestAdapterDelegate(env, object), | |
| 131 url, | |
| 132 ConvertRequestPriority(priority)); | |
| 133 | |
| 134 return reinterpret_cast<jlong>(adapter); | |
| 135 } | |
| 136 | |
| 137 // synchronized | |
| 138 static void AddHeader(JNIEnv* env, | |
| 139 jobject object, | |
| 140 jlong urlRequestAdapter, | |
| 141 jstring name, | |
| 142 jstring value) { | |
| 143 URLRequestAdapter* request = | |
| 144 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 145 DCHECK(request); | |
| 146 | |
| 147 std::string name_string(base::android::ConvertJavaStringToUTF8(env, name)); | |
| 148 std::string value_string(base::android::ConvertJavaStringToUTF8(env, value)); | |
| 149 | |
| 150 request->AddHeader(name_string, value_string); | |
| 151 } | |
| 152 | |
| 153 static void SetMethod(JNIEnv* env, | |
| 154 jobject object, | |
| 155 jlong urlRequestAdapter, | |
| 156 jstring method) { | |
| 157 URLRequestAdapter* request = | |
| 158 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 159 DCHECK(request); | |
| 160 | |
| 161 std::string method_string( | |
| 162 base::android::ConvertJavaStringToUTF8(env, method)); | |
| 163 | |
| 164 request->SetMethod(method_string); | |
| 165 } | |
| 166 | |
| 167 static void SetUploadData(JNIEnv* env, | |
| 168 jobject object, | |
| 169 jlong urlRequestAdapter, | |
| 170 jstring content_type, | |
| 171 jbyteArray content) { | |
| 172 URLRequestAdapter* request = | |
| 173 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 174 SetPostContentType(env, request, content_type); | |
| 175 | |
| 176 if (content != NULL) { | |
| 177 jsize size = env->GetArrayLength(content); | |
| 178 if (size > 0) { | |
| 179 jbyte* content_bytes = env->GetByteArrayElements(content, NULL); | |
| 180 request->SetUploadContent(reinterpret_cast<const char*>(content_bytes), | |
| 181 size); | |
| 182 env->ReleaseByteArrayElements(content, content_bytes, 0); | |
| 183 } | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 static void SetUploadChannel(JNIEnv* env, | |
| 188 jobject object, | |
| 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); | |
| 195 | |
| 196 request->SetUploadChannel(env, content_length); | |
| 197 } | |
| 198 | |
| 199 | |
| 200 /* synchronized */ | |
| 201 static void Start(JNIEnv* env, jobject object, jlong urlRequestAdapter) { | |
| 202 URLRequestAdapter* request = | |
| 203 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 204 if (request != NULL) { | |
| 205 request->Start(); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 /* synchronized */ | |
| 210 static void DestroyRequestAdapter(JNIEnv* env, | |
| 211 jobject object, | |
| 212 jlong urlRequestAdapter) { | |
| 213 URLRequestAdapter* request = | |
| 214 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 215 if (request != NULL) { | |
| 216 request->Destroy(); | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 /* synchronized */ | |
| 221 static void Cancel(JNIEnv* env, jobject object, jlong urlRequestAdapter) { | |
| 222 URLRequestAdapter* request = | |
| 223 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 224 if (request != NULL) { | |
| 225 request->Cancel(); | |
| 226 } | |
| 227 } | |
| 228 | |
| 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. | |
| 236 case net::OK: | |
| 237 return REQUEST_ERROR_SUCCESS; | |
| 238 | |
| 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. | |
| 241 | |
| 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; | |
| 250 | |
| 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; | |
| 255 | |
| 256 case net::ERR_CONNECTION_TIMED_OUT: | |
| 257 return REQUEST_ERROR_CONNECTION_TIMED_OUT; | |
| 258 | |
| 259 case net::ERR_NAME_NOT_RESOLVED: | |
| 260 return REQUEST_ERROR_UNKNOWN_HOST; | |
| 261 } | |
| 262 return REQUEST_ERROR_UNKNOWN; | |
| 263 } | |
| 264 | |
| 265 static jstring GetErrorString(JNIEnv* env, | |
| 266 jobject object, | |
| 267 jlong urlRequestAdapter) { | |
| 268 URLRequestAdapter* request = | |
| 269 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 270 int error_code = request->error_code(); | |
| 271 char buffer[200]; | |
| 272 std::string error_string = net::ErrorToString(error_code); | |
| 273 snprintf(buffer, | |
| 274 sizeof(buffer), | |
| 275 "System error: %s(%d)", | |
| 276 error_string.c_str(), | |
| 277 error_code); | |
| 278 return ConvertUTF8ToJavaString(env, buffer).Release(); | |
| 279 } | |
| 280 | |
| 281 static jint GetHttpStatusCode(JNIEnv* env, | |
| 282 jobject object, | |
| 283 jlong urlRequestAdapter) { | |
| 284 URLRequestAdapter* request = | |
| 285 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 286 return request->http_status_code(); | |
| 287 } | |
| 288 | |
| 289 static jstring GetContentType(JNIEnv* env, | |
| 290 jobject object, | |
| 291 jlong urlRequestAdapter) { | |
| 292 URLRequestAdapter* request = | |
| 293 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 294 if (request == NULL) { | |
| 295 return NULL; | |
| 296 } | |
| 297 std::string type = request->content_type(); | |
| 298 if (!type.empty()) { | |
| 299 return ConvertUTF8ToJavaString(env, type.c_str()).Release(); | |
| 300 } else { | |
| 301 return NULL; | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 static jlong GetContentLength(JNIEnv* env, | |
| 306 jobject object, | |
| 307 jlong urlRequestAdapter) { | |
| 308 URLRequestAdapter* request = | |
| 309 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 310 if (request == NULL) { | |
| 311 return 0; | |
| 312 } | |
| 313 return request->content_length(); | |
| 314 } | |
| 315 | |
| 316 static jstring GetHeader(JNIEnv* env, | |
| 317 jobject object, | |
| 318 jlong urlRequestAdapter, | |
| 319 jstring name) { | |
| 320 URLRequestAdapter* request = | |
| 321 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 322 if (request == NULL) { | |
| 323 return NULL; | |
| 324 } | |
| 325 | |
| 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(); | |
| 330 } else { | |
| 331 return NULL; | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 static void GetAllHeaders(JNIEnv* env, | |
| 336 jobject object, | |
| 337 jlong urlRequestAdapter, | |
| 338 jobject headersMap) { | |
| 339 URLRequestAdapter* request = | |
| 340 reinterpret_cast<URLRequestAdapter*>(urlRequestAdapter); | |
| 341 if (request == NULL) | |
| 342 return; | |
| 343 | |
| 344 net::HttpResponseHeaders* headers = request->GetResponseHeaders(); | |
| 345 if (headers == NULL) | |
| 346 return; | |
| 347 | |
| 348 void* iter = NULL; | |
| 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()); | |
| 358 } | |
| 359 | |
| 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()); | |
| 366 } | |
| 367 | |
| 368 } // namespace cronet | |
| OLD | NEW |