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/cronet_url_request_adapter.h"
6
7 #include <limits>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "components/cronet/android/cronet_jni_headers/CronetUrlRequest_jni.h"
15 #include "components/cronet/android/cronet_url_request_context_adapter.h"
16 #include "components/cronet/android/io_buffer_with_byte_buffer.h"
17 #include "components/cronet/android/url_request_error.h"
18 #include "components/cronet/metrics_util.h"
19 #include "net/base/idempotency.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/load_states.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/proxy_server.h"
24 #include "net/base/request_priority.h"
25 #include "net/cert/cert_status_flags.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/http/http_status_code.h"
28 #include "net/http/http_util.h"
29 #include "net/ssl/ssl_info.h"
30 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
31 #include "net/url_request/redirect_info.h"
32 #include "net/url_request/url_request_context.h"
33
34 using base::android::ConvertUTF8ToJavaString;
35 using base::android::JavaParamRef;
36
37 namespace {
38
ConvertResponseHeadersToJava(JNIEnv * env,const net::HttpResponseHeaders * headers)39 base::android::ScopedJavaLocalRef<jobjectArray> ConvertResponseHeadersToJava(
40 JNIEnv* env,
41 const net::HttpResponseHeaders* headers) {
42 std::vector<std::string> response_headers;
43 // Returns an empty array if |headers| is nullptr.
44 if (headers != nullptr) {
45 size_t iter = 0;
46 std::string header_name;
47 std::string header_value;
48 while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) {
49 response_headers.push_back(header_name);
50 response_headers.push_back(header_value);
51 }
52 }
53 return base::android::ToJavaArrayOfStrings(env, response_headers);
54 }
55
56 } // namespace
57
58 namespace cronet {
59
JNI_CronetUrlRequest_CreateRequestAdapter(JNIEnv * env,const JavaParamRef<jobject> & jurl_request,jlong jurl_request_context_adapter,const JavaParamRef<jstring> & jurl_string,jint jpriority,jboolean jdisable_cache,jboolean jdisable_connection_migration,jboolean jenable_metrics,jboolean jtraffic_stats_tag_set,jint jtraffic_stats_tag,jboolean jtraffic_stats_uid_set,jint jtraffic_stats_uid,jint jidempotency)60 static jlong JNI_CronetUrlRequest_CreateRequestAdapter(
61 JNIEnv* env,
62 const JavaParamRef<jobject>& jurl_request,
63 jlong jurl_request_context_adapter,
64 const JavaParamRef<jstring>& jurl_string,
65 jint jpriority,
66 jboolean jdisable_cache,
67 jboolean jdisable_connection_migration,
68 jboolean jenable_metrics,
69 jboolean jtraffic_stats_tag_set,
70 jint jtraffic_stats_tag,
71 jboolean jtraffic_stats_uid_set,
72 jint jtraffic_stats_uid,
73 jint jidempotency) {
74 CronetURLRequestContextAdapter* context_adapter =
75 reinterpret_cast<CronetURLRequestContextAdapter*>(
76 jurl_request_context_adapter);
77 DCHECK(context_adapter);
78
79 GURL url(base::android::ConvertJavaStringToUTF8(env, jurl_string));
80
81 VLOG(1) << "New chromium network request_adapter: "
82 << url.possibly_invalid_spec();
83
84 CronetURLRequestAdapter* adapter = new CronetURLRequestAdapter(
85 context_adapter, env, jurl_request, url,
86 static_cast<net::RequestPriority>(jpriority), jdisable_cache,
87 jdisable_connection_migration, jenable_metrics, jtraffic_stats_tag_set,
88 jtraffic_stats_tag, jtraffic_stats_uid_set, jtraffic_stats_uid,
89 static_cast<net::Idempotency>(jidempotency));
90
91 return reinterpret_cast<jlong>(adapter);
92 }
93
CronetURLRequestAdapter(CronetURLRequestContextAdapter * context,JNIEnv * env,jobject jurl_request,const GURL & url,net::RequestPriority priority,jboolean jdisable_cache,jboolean jdisable_connection_migration,jboolean jenable_metrics,jboolean jtraffic_stats_tag_set,jint jtraffic_stats_tag,jboolean jtraffic_stats_uid_set,jint jtraffic_stats_uid,net::Idempotency idempotency)94 CronetURLRequestAdapter::CronetURLRequestAdapter(
95 CronetURLRequestContextAdapter* context,
96 JNIEnv* env,
97 jobject jurl_request,
98 const GURL& url,
99 net::RequestPriority priority,
100 jboolean jdisable_cache,
101 jboolean jdisable_connection_migration,
102 jboolean jenable_metrics,
103 jboolean jtraffic_stats_tag_set,
104 jint jtraffic_stats_tag,
105 jboolean jtraffic_stats_uid_set,
106 jint jtraffic_stats_uid,
107 net::Idempotency idempotency)
108 : request_(
109 new CronetURLRequest(context->cronet_url_request_context(),
110 std::unique_ptr<CronetURLRequestAdapter>(this),
111 url,
112 priority,
113 jdisable_cache == JNI_TRUE,
114 jdisable_connection_migration == JNI_TRUE,
115 jenable_metrics == JNI_TRUE,
116 jtraffic_stats_tag_set == JNI_TRUE,
117 jtraffic_stats_tag,
118 jtraffic_stats_uid_set == JNI_TRUE,
119 jtraffic_stats_uid,
120 idempotency)) {
121 owner_.Reset(env, jurl_request);
122 }
123
~CronetURLRequestAdapter()124 CronetURLRequestAdapter::~CronetURLRequestAdapter() {
125 }
126
SetHttpMethod(JNIEnv * env,const JavaParamRef<jobject> & jcaller,const JavaParamRef<jstring> & jmethod)127 jboolean CronetURLRequestAdapter::SetHttpMethod(
128 JNIEnv* env,
129 const JavaParamRef<jobject>& jcaller,
130 const JavaParamRef<jstring>& jmethod) {
131 std::string method(base::android::ConvertJavaStringToUTF8(env, jmethod));
132 return request_->SetHttpMethod(method) ? JNI_TRUE : JNI_FALSE;
133 }
134
AddRequestHeader(JNIEnv * env,const JavaParamRef<jobject> & jcaller,const JavaParamRef<jstring> & jname,const JavaParamRef<jstring> & jvalue)135 jboolean CronetURLRequestAdapter::AddRequestHeader(
136 JNIEnv* env,
137 const JavaParamRef<jobject>& jcaller,
138 const JavaParamRef<jstring>& jname,
139 const JavaParamRef<jstring>& jvalue) {
140 std::string name(base::android::ConvertJavaStringToUTF8(env, jname));
141 std::string value(base::android::ConvertJavaStringToUTF8(env, jvalue));
142 return request_->AddRequestHeader(name, value) ? JNI_TRUE : JNI_FALSE;
143 }
144
SetUpload(std::unique_ptr<net::UploadDataStream> upload)145 void CronetURLRequestAdapter::SetUpload(
146 std::unique_ptr<net::UploadDataStream> upload) {
147 request_->SetUpload(std::move(upload));
148 }
149
Start(JNIEnv * env,const JavaParamRef<jobject> & jcaller)150 void CronetURLRequestAdapter::Start(JNIEnv* env,
151 const JavaParamRef<jobject>& jcaller) {
152 request_->Start();
153 }
154
GetStatus(JNIEnv * env,const JavaParamRef<jobject> & jcaller,const JavaParamRef<jobject> & jstatus_listener)155 void CronetURLRequestAdapter::GetStatus(
156 JNIEnv* env,
157 const JavaParamRef<jobject>& jcaller,
158 const JavaParamRef<jobject>& jstatus_listener) {
159 base::android::ScopedJavaGlobalRef<jobject> status_listener_ref;
160 status_listener_ref.Reset(env, jstatus_listener);
161 request_->GetStatus(base::BindOnce(&CronetURLRequestAdapter::OnStatus,
162 base::Unretained(this),
163 status_listener_ref));
164 }
165
FollowDeferredRedirect(JNIEnv * env,const JavaParamRef<jobject> & jcaller)166 void CronetURLRequestAdapter::FollowDeferredRedirect(
167 JNIEnv* env,
168 const JavaParamRef<jobject>& jcaller) {
169 request_->FollowDeferredRedirect();
170 }
171
ReadData(JNIEnv * env,const JavaParamRef<jobject> & jcaller,const JavaParamRef<jobject> & jbyte_buffer,jint jposition,jint jlimit)172 jboolean CronetURLRequestAdapter::ReadData(
173 JNIEnv* env,
174 const JavaParamRef<jobject>& jcaller,
175 const JavaParamRef<jobject>& jbyte_buffer,
176 jint jposition,
177 jint jlimit) {
178 DCHECK_LT(jposition, jlimit);
179
180 void* data = env->GetDirectBufferAddress(jbyte_buffer);
181 if (!data)
182 return JNI_FALSE;
183
184 IOBufferWithByteBuffer* read_buffer =
185 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition, jlimit);
186
187 int remaining_capacity = jlimit - jposition;
188 request_->ReadData(read_buffer, remaining_capacity);
189 return JNI_TRUE;
190 }
191
Destroy(JNIEnv * env,const JavaParamRef<jobject> & jcaller,jboolean jsend_on_canceled)192 void CronetURLRequestAdapter::Destroy(JNIEnv* env,
193 const JavaParamRef<jobject>& jcaller,
194 jboolean jsend_on_canceled) {
195 // Destroy could be called from any thread, including network thread (if
196 // posting task to executor throws an exception), but is posted, so |this|
197 // is valid until calling task is complete. Destroy() is always called from
198 // within a synchronized java block that guarantees no future posts to the
199 // network thread with the adapter pointer.
200 request_->Destroy(jsend_on_canceled == JNI_TRUE);
201 }
202
OnReceivedRedirect(const std::string & new_location,int http_status_code,const std::string & http_status_text,const net::HttpResponseHeaders * headers,bool was_cached,const std::string & negotiated_protocol,const std::string & proxy_server,int64_t received_byte_count)203 void CronetURLRequestAdapter::OnReceivedRedirect(
204 const std::string& new_location,
205 int http_status_code,
206 const std::string& http_status_text,
207 const net::HttpResponseHeaders* headers,
208 bool was_cached,
209 const std::string& negotiated_protocol,
210 const std::string& proxy_server,
211 int64_t received_byte_count) {
212 JNIEnv* env = base::android::AttachCurrentThread();
213 cronet::Java_CronetUrlRequest_onRedirectReceived(
214 env, owner_, ConvertUTF8ToJavaString(env, new_location), http_status_code,
215 ConvertUTF8ToJavaString(env, http_status_text),
216 ConvertResponseHeadersToJava(env, headers),
217 was_cached ? JNI_TRUE : JNI_FALSE,
218 ConvertUTF8ToJavaString(env, negotiated_protocol),
219 ConvertUTF8ToJavaString(env, proxy_server), received_byte_count);
220 }
221
OnResponseStarted(int http_status_code,const std::string & http_status_text,const net::HttpResponseHeaders * headers,bool was_cached,const std::string & negotiated_protocol,const std::string & proxy_server,int64_t received_byte_count)222 void CronetURLRequestAdapter::OnResponseStarted(
223 int http_status_code,
224 const std::string& http_status_text,
225 const net::HttpResponseHeaders* headers,
226 bool was_cached,
227 const std::string& negotiated_protocol,
228 const std::string& proxy_server,
229 int64_t received_byte_count) {
230 JNIEnv* env = base::android::AttachCurrentThread();
231 cronet::Java_CronetUrlRequest_onResponseStarted(
232 env, owner_, http_status_code,
233 ConvertUTF8ToJavaString(env, http_status_text),
234 ConvertResponseHeadersToJava(env, headers),
235 was_cached ? JNI_TRUE : JNI_FALSE,
236 ConvertUTF8ToJavaString(env, negotiated_protocol),
237 ConvertUTF8ToJavaString(env, proxy_server), received_byte_count);
238 }
239
OnReadCompleted(scoped_refptr<net::IOBuffer> buffer,int bytes_read,int64_t received_byte_count)240 void CronetURLRequestAdapter::OnReadCompleted(
241 scoped_refptr<net::IOBuffer> buffer,
242 int bytes_read,
243 int64_t received_byte_count) {
244 IOBufferWithByteBuffer* read_buffer =
245 reinterpret_cast<IOBufferWithByteBuffer*>(buffer.get());
246 JNIEnv* env = base::android::AttachCurrentThread();
247 cronet::Java_CronetUrlRequest_onReadCompleted(
248 env, owner_, read_buffer->byte_buffer(), bytes_read,
249 read_buffer->initial_position(), read_buffer->initial_limit(),
250 received_byte_count);
251 }
252
OnSucceeded(int64_t received_byte_count)253 void CronetURLRequestAdapter::OnSucceeded(int64_t received_byte_count) {
254 JNIEnv* env = base::android::AttachCurrentThread();
255 cronet::Java_CronetUrlRequest_onSucceeded(env, owner_, received_byte_count);
256 }
257
OnError(int net_error,int quic_error,const std::string & error_string,int64_t received_byte_count)258 void CronetURLRequestAdapter::OnError(int net_error,
259 int quic_error,
260 const std::string& error_string,
261 int64_t received_byte_count) {
262 JNIEnv* env = base::android::AttachCurrentThread();
263 cronet::Java_CronetUrlRequest_onError(
264 env, owner_, NetErrorToUrlRequestError(net_error), net_error, quic_error,
265 ConvertUTF8ToJavaString(env, error_string), received_byte_count);
266 }
267
OnCanceled()268 void CronetURLRequestAdapter::OnCanceled() {
269 JNIEnv* env = base::android::AttachCurrentThread();
270 cronet::Java_CronetUrlRequest_onCanceled(env, owner_);
271 }
272
OnDestroyed()273 void CronetURLRequestAdapter::OnDestroyed() {
274 JNIEnv* env = base::android::AttachCurrentThread();
275 cronet::Java_CronetUrlRequest_onNativeAdapterDestroyed(env, owner_);
276 // |this| adapter will be destroyed by the owner after return from this call.
277 }
278
OnStatus(const base::android::ScopedJavaGlobalRef<jobject> & status_listener_ref,net::LoadState load_status)279 void CronetURLRequestAdapter::OnStatus(
280 const base::android::ScopedJavaGlobalRef<jobject>& status_listener_ref,
281 net::LoadState load_status) {
282 JNIEnv* env = base::android::AttachCurrentThread();
283 cronet::Java_CronetUrlRequest_onStatus(env, owner_, status_listener_ref,
284 load_status);
285 }
286
OnMetricsCollected(const base::Time & start_time,const base::TimeTicks & start_ticks,const base::TimeTicks & dns_start,const base::TimeTicks & dns_end,const base::TimeTicks & connect_start,const base::TimeTicks & connect_end,const base::TimeTicks & ssl_start,const base::TimeTicks & ssl_end,const base::TimeTicks & send_start,const base::TimeTicks & send_end,const base::TimeTicks & push_start,const base::TimeTicks & push_end,const base::TimeTicks & receive_headers_end,const base::TimeTicks & request_end,bool socket_reused,int64_t sent_bytes_count,int64_t received_bytes_count)287 void CronetURLRequestAdapter::OnMetricsCollected(
288 const base::Time& start_time,
289 const base::TimeTicks& start_ticks,
290 const base::TimeTicks& dns_start,
291 const base::TimeTicks& dns_end,
292 const base::TimeTicks& connect_start,
293 const base::TimeTicks& connect_end,
294 const base::TimeTicks& ssl_start,
295 const base::TimeTicks& ssl_end,
296 const base::TimeTicks& send_start,
297 const base::TimeTicks& send_end,
298 const base::TimeTicks& push_start,
299 const base::TimeTicks& push_end,
300 const base::TimeTicks& receive_headers_end,
301 const base::TimeTicks& request_end,
302 bool socket_reused,
303 int64_t sent_bytes_count,
304 int64_t received_bytes_count) {
305 JNIEnv* env = base::android::AttachCurrentThread();
306 Java_CronetUrlRequest_onMetricsCollected(
307 env, owner_,
308 metrics_util::ConvertTime(start_ticks, start_ticks, start_time),
309 metrics_util::ConvertTime(dns_start, start_ticks, start_time),
310 metrics_util::ConvertTime(dns_end, start_ticks, start_time),
311 metrics_util::ConvertTime(connect_start, start_ticks, start_time),
312 metrics_util::ConvertTime(connect_end, start_ticks, start_time),
313 metrics_util::ConvertTime(ssl_start, start_ticks, start_time),
314 metrics_util::ConvertTime(ssl_end, start_ticks, start_time),
315 metrics_util::ConvertTime(send_start, start_ticks, start_time),
316 metrics_util::ConvertTime(send_end, start_ticks, start_time),
317 metrics_util::ConvertTime(push_start, start_ticks, start_time),
318 metrics_util::ConvertTime(push_end, start_ticks, start_time),
319 metrics_util::ConvertTime(receive_headers_end, start_ticks, start_time),
320 metrics_util::ConvertTime(request_end, start_ticks, start_time),
321 socket_reused ? JNI_TRUE : JNI_FALSE, sent_bytes_count,
322 received_bytes_count);
323 }
324
325 } // namespace cronet
326