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 |