Home | History | Annotate | Download | only in android
      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 
     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   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.
     55 class 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.
    108 bool ChromiumUrlRequestRegisterJni(JNIEnv* env) {
    109   return RegisterNativesImpl(env);
    110 }
    111 
    112 static 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
    135 static 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 
    150 static 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 
    164 static 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 
    184 static 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 
    196 static 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 
    207 static 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 */
    223 static 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 */
    232 static 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 */
    243 static 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 
    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.
    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 
    287 static 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 
    303 static 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 
    311 static 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 
    327 static 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 
    338 static 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 
    357 static 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 
    390 static 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