Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / cronet / android / chromium_url_request.cc
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