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.
5 #include "cronet_url_request_adapter.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "components/cronet/android/cronet_url_request_context_adapter.h"
11 #include "jni/CronetUrlRequest_jni.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/load_flags.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/request_priority.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/http/http_status_code.h"
18 #include "net/http/http_util.h"
19 #include "net/url_request/redirect_info.h"
20 #include "net/url_request/url_request_context.h"
22 using base::android::ConvertUTF8ToJavaString
;
26 static const int kReadBufferSize
= 32768;
28 // Explicitly register static JNI functions.
29 bool CronetUrlRequestAdapterRegisterJni(JNIEnv
* env
) {
30 return RegisterNativesImpl(env
);
33 static jlong
CreateRequestAdapter(JNIEnv
* env
,
35 jlong jurl_request_context_adapter
,
38 CronetURLRequestContextAdapter
* context_adapter
=
39 reinterpret_cast<CronetURLRequestContextAdapter
*>(
40 jurl_request_context_adapter
);
41 DCHECK(context_adapter
);
43 GURL
url(base::android::ConvertJavaStringToUTF8(env
, jurl_string
));
45 VLOG(1) << "New chromium network request_adapter: "
46 << url
.possibly_invalid_spec();
48 CronetURLRequestAdapter
* adapter
=
49 new CronetURLRequestAdapter(context_adapter
, env
, jurl_request
, url
,
50 static_cast<net::RequestPriority
>(jpriority
));
52 return reinterpret_cast<jlong
>(adapter
);
55 CronetURLRequestAdapter::CronetURLRequestAdapter(
56 CronetURLRequestContextAdapter
* context
,
60 net::RequestPriority priority
)
63 initial_priority_(priority
),
64 initial_method_("GET"),
65 load_flags_(context
->default_load_flags()) {
66 DCHECK(!context_
->IsOnNetworkThread());
67 owner_
.Reset(env
, jurl_request
);
70 CronetURLRequestAdapter::~CronetURLRequestAdapter() {
71 DCHECK(context_
->IsOnNetworkThread());
74 jboolean
CronetURLRequestAdapter::SetHttpMethod(JNIEnv
* env
,
77 DCHECK(!context_
->IsOnNetworkThread());
78 std::string
method(base::android::ConvertJavaStringToUTF8(env
, jmethod
));
79 // Http method is a token, just as header name.
80 if (!net::HttpUtil::IsValidHeaderName(method
))
82 initial_method_
= method
;
86 jboolean
CronetURLRequestAdapter::AddRequestHeader(JNIEnv
* env
,
90 DCHECK(!context_
->IsOnNetworkThread());
91 std::string
name(base::android::ConvertJavaStringToUTF8(env
, jname
));
92 std::string
value(base::android::ConvertJavaStringToUTF8(env
, jvalue
));
93 if (!net::HttpUtil::IsValidHeaderName(name
) ||
94 !net::HttpUtil::IsValidHeaderValue(value
)) {
97 initial_request_headers_
.SetHeader(name
, value
);
101 void CronetURLRequestAdapter::DisableCache(JNIEnv
* env
, jobject jcaller
) {
102 DCHECK(!context_
->IsOnNetworkThread());
103 load_flags_
|= net::LOAD_DISABLE_CACHE
;
106 void CronetURLRequestAdapter::SetUpload(
107 scoped_ptr
<net::UploadDataStream
> upload
) {
108 DCHECK(!context_
->IsOnNetworkThread());
110 upload_
= upload
.Pass();
113 void CronetURLRequestAdapter::Start(JNIEnv
* env
, jobject jcaller
) {
114 DCHECK(!context_
->IsOnNetworkThread());
115 context_
->PostTaskToNetworkThread(
116 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::StartOnNetworkThread
,
117 base::Unretained(this)));
120 void CronetURLRequestAdapter::FollowDeferredRedirect(JNIEnv
* env
,
122 DCHECK(!context_
->IsOnNetworkThread());
123 context_
->PostTaskToNetworkThread(
126 &CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread
,
127 base::Unretained(this)));
130 void CronetURLRequestAdapter::ReadData(JNIEnv
* env
, jobject jcaller
) {
131 DCHECK(!context_
->IsOnNetworkThread());
132 context_
->PostTaskToNetworkThread(
133 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::ReadDataOnNetworkThread
,
134 base::Unretained(this)));
137 void CronetURLRequestAdapter::Destroy(JNIEnv
* env
, jobject jcaller
) {
138 DCHECK(!context_
->IsOnNetworkThread());
139 context_
->PostTaskToNetworkThread(
140 FROM_HERE
, base::Bind(&CronetURLRequestAdapter::DestroyOnNetworkThread
,
141 base::Unretained(this)));
144 void CronetURLRequestAdapter::PopulateResponseHeaders(JNIEnv
* env
,
145 jobject jurl_request
,
146 jobject jheaders_list
) {
147 DCHECK(context_
->IsOnNetworkThread());
148 const net::HttpResponseHeaders
* headers
= url_request_
->response_headers();
149 if (headers
== nullptr)
152 void* iter
= nullptr;
153 std::string header_name
;
154 std::string header_value
;
155 while (headers
->EnumerateHeaderLines(&iter
, &header_name
, &header_value
)) {
156 base::android::ScopedJavaLocalRef
<jstring
> name
=
157 ConvertUTF8ToJavaString(env
, header_name
);
158 base::android::ScopedJavaLocalRef
<jstring
> value
=
159 ConvertUTF8ToJavaString(env
, header_value
);
160 Java_CronetUrlRequest_onAppendResponseHeader(
161 env
, jurl_request
, jheaders_list
, name
.obj(), value
.obj());
165 base::android::ScopedJavaLocalRef
<jstring
>
166 CronetURLRequestAdapter::GetHttpStatusText(JNIEnv
* env
, jobject jcaller
) const {
167 DCHECK(context_
->IsOnNetworkThread());
168 const net::HttpResponseHeaders
* headers
= url_request_
->response_headers();
169 return ConvertUTF8ToJavaString(env
, headers
->GetStatusText());
172 base::android::ScopedJavaLocalRef
<jstring
>
173 CronetURLRequestAdapter::GetNegotiatedProtocol(JNIEnv
* env
,
174 jobject jcaller
) const {
175 DCHECK(context_
->IsOnNetworkThread());
176 return ConvertUTF8ToJavaString(
177 env
, url_request_
->response_info().npn_negotiated_protocol
);
180 jboolean
CronetURLRequestAdapter::GetWasCached(JNIEnv
* env
,
181 jobject jcaller
) const {
182 DCHECK(context_
->IsOnNetworkThread());
183 return url_request_
->response_info().was_cached
;
186 int64
CronetURLRequestAdapter::GetTotalReceivedBytes(JNIEnv
* env
,
187 jobject jcaller
) const {
188 DCHECK(context_
->IsOnNetworkThread());
189 return url_request_
->GetTotalReceivedBytes();
192 // net::URLRequest::Delegate overrides (called on network thread).
194 void CronetURLRequestAdapter::OnReceivedRedirect(
195 net::URLRequest
* request
,
196 const net::RedirectInfo
& redirect_info
,
197 bool* defer_redirect
) {
198 DCHECK(context_
->IsOnNetworkThread());
199 DCHECK(request
->status().is_success());
200 JNIEnv
* env
= base::android::AttachCurrentThread();
201 cronet::Java_CronetUrlRequest_onRedirect(
203 ConvertUTF8ToJavaString(env
, redirect_info
.new_url
.spec()).obj(),
204 redirect_info
.status_code
);
205 *defer_redirect
= true;
208 void CronetURLRequestAdapter::OnResponseStarted(net::URLRequest
* request
) {
209 DCHECK(context_
->IsOnNetworkThread());
210 if (MaybeReportError(request
))
212 JNIEnv
* env
= base::android::AttachCurrentThread();
213 cronet::Java_CronetUrlRequest_onResponseStarted(env
, owner_
.obj(),
214 request
->GetResponseCode());
217 void CronetURLRequestAdapter::OnReadCompleted(net::URLRequest
* request
,
219 DCHECK(context_
->IsOnNetworkThread());
220 if (MaybeReportError(request
))
222 if (bytes_read
!= 0) {
223 JNIEnv
* env
= base::android::AttachCurrentThread();
224 base::android::ScopedJavaLocalRef
<jobject
> java_buffer(
225 env
, env
->NewDirectByteBuffer(read_buffer_
->data(), bytes_read
));
226 cronet::Java_CronetUrlRequest_onDataReceived(env
, owner_
.obj(),
229 JNIEnv
* env
= base::android::AttachCurrentThread();
230 cronet::Java_CronetUrlRequest_onSucceeded(env
, owner_
.obj());
234 void CronetURLRequestAdapter::StartOnNetworkThread() {
235 DCHECK(context_
->IsOnNetworkThread());
236 VLOG(1) << "Starting chromium request: "
237 << initial_url_
.possibly_invalid_spec().c_str()
238 << " priority: " << RequestPriorityToString(initial_priority_
);
239 url_request_
= context_
->GetURLRequestContext()->CreateRequest(
240 initial_url_
, net::DEFAULT_PRIORITY
, this);
241 url_request_
->SetLoadFlags(load_flags_
);
242 url_request_
->set_method(initial_method_
);
243 url_request_
->SetExtraRequestHeaders(initial_request_headers_
);
244 url_request_
->SetPriority(initial_priority_
);
246 url_request_
->set_upload(upload_
.Pass());
247 url_request_
->Start();
250 void CronetURLRequestAdapter::FollowDeferredRedirectOnNetworkThread() {
251 DCHECK(context_
->IsOnNetworkThread());
252 url_request_
->FollowDeferredRedirect();
255 void CronetURLRequestAdapter::ReadDataOnNetworkThread() {
256 DCHECK(context_
->IsOnNetworkThread());
257 if (!read_buffer_
.get())
258 read_buffer_
= new net::IOBufferWithSize(kReadBufferSize
);
261 url_request_
->Read(read_buffer_
.get(), read_buffer_
->size(), &bytes_read
);
262 // If IO is pending, wait for the URLRequest to call OnReadCompleted.
263 if (url_request_
->status().is_io_pending())
266 OnReadCompleted(url_request_
.get(), bytes_read
);
269 void CronetURLRequestAdapter::DestroyOnNetworkThread() {
270 DCHECK(context_
->IsOnNetworkThread());
274 bool CronetURLRequestAdapter::MaybeReportError(net::URLRequest
* request
) const {
275 DCHECK_NE(net::URLRequestStatus::IO_PENDING
, url_request_
->status().status());
276 DCHECK_EQ(request
, url_request_
);
277 if (url_request_
->status().is_success())
279 int net_error
= url_request_
->status().error();
280 VLOG(1) << "Error " << net::ErrorToString(net_error
)
281 << " on chromium request: " << initial_url_
.possibly_invalid_spec();
282 JNIEnv
* env
= base::android::AttachCurrentThread();
283 cronet::Java_CronetUrlRequest_onError(
284 env
, owner_
.obj(), net_error
,
285 ConvertUTF8ToJavaString(env
, net::ErrorToString(net_error
)).obj());
289 } // namespace cronet