OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "cronet_bidirectional_stream_adapter.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/location.h" | |
12 #include "base/logging.h" | |
13 #include "base/strings/string_number_conversions.h" | |
14 #include "components/cronet/android/cronet_url_request_context_adapter.h" | |
15 #include "jni/CronetBidirectionalStream_jni.h" | |
16 #include "net/base/io_buffer.h" | |
17 #include "net/base/net_errors.h" | |
18 #include "net/base/request_priority.h" | |
19 #include "net/cert/cert_status_flags.h" | |
20 #include "net/http/bidirectional_stream_request_info.h" | |
21 #include "net/http/http_network_session.h" | |
22 #include "net/http/http_response_headers.h" | |
23 #include "net/http/http_status_code.h" | |
24 #include "net/http/http_transaction_factory.h" | |
25 #include "net/http/http_util.h" | |
26 #include "net/spdy/spdy_header_block.h" | |
27 #include "net/ssl/ssl_info.h" | |
28 #include "net/url_request/redirect_info.h" | |
29 #include "net/url_request/url_request_context.h" | |
30 | |
31 using base::android::ConvertUTF8ToJavaString; | |
32 using base::android::ConvertJavaStringToUTF8; | |
33 | |
34 namespace cronet { | |
35 | |
36 // Explicitly register static JNI functions. | |
37 bool CronetBidirectionalStreamAdapterRegisterJni(JNIEnv* env) { | |
38 return RegisterNativesImpl(env); | |
39 } | |
40 | |
41 static jlong CreateBidirectionalStream( | |
42 JNIEnv* env, | |
43 const JavaParamRef<jobject>& jbidi_stream, | |
44 jlong jurl_request_context_adapter) { | |
45 CronetURLRequestContextAdapter* context_adapter = | |
46 reinterpret_cast<CronetURLRequestContextAdapter*>( | |
47 jurl_request_context_adapter); | |
48 DCHECK(context_adapter); | |
49 | |
50 CronetBidirectionalStreamAdapter* adapter = | |
51 new CronetBidirectionalStreamAdapter(context_adapter, env, jbidi_stream); | |
52 | |
53 return reinterpret_cast<jlong>(adapter); | |
54 } | |
55 | |
56 // TODO(mef): Extract this and its original from cronet_url_request_adapter.cc | |
57 // into separate module. | |
58 // net::WrappedIOBuffer subclass for a buffer owned by a Java ByteBuffer. Keeps | |
59 // the ByteBuffer alive until destroyed. Uses WrappedIOBuffer because data() is | |
60 // owned by the embedder. | |
61 class CronetBidirectionalStreamAdapter::IOBufferWithByteBuffer | |
62 : public net::WrappedIOBuffer { | |
63 public: | |
64 // Creates a buffer wrapping the Java ByteBuffer |jbyte_buffer|. |data| points | |
65 // to the memory backed by the ByteBuffer, and position is the location to | |
66 // start writing. | |
67 IOBufferWithByteBuffer(JNIEnv* env, | |
68 const JavaParamRef<jobject>& jbyte_buffer, | |
69 void* data, | |
70 int position, | |
71 int limit) | |
72 : net::WrappedIOBuffer(static_cast<char*>(data) + position), | |
73 initial_position_(position), | |
74 initial_limit_(limit) { | |
75 DCHECK(data); | |
76 DCHECK_EQ(env->GetDirectBufferAddress(jbyte_buffer), data); | |
77 byte_buffer_.Reset(env, jbyte_buffer); | |
pauljensen
2016/01/19 16:03:39
How about moving this up to initializer list like
mef
2016/01/20 15:37:39
Done.
| |
78 } | |
79 | |
80 int initial_position() const { return initial_position_; } | |
81 int initial_limit() const { return initial_limit_; } | |
82 | |
83 jobject byte_buffer() const { return byte_buffer_.obj(); } | |
84 | |
85 private: | |
86 ~IOBufferWithByteBuffer() override {} | |
87 | |
88 base::android::ScopedJavaGlobalRef<jobject> byte_buffer_; | |
89 | |
90 const int initial_position_; | |
91 const int initial_limit_; | |
92 }; | |
93 | |
94 CronetBidirectionalStreamAdapter::CronetBidirectionalStreamAdapter( | |
95 CronetURLRequestContextAdapter* context, | |
96 JNIEnv* env, | |
97 const JavaParamRef<jobject>& jbidi_stream) | |
98 : context_(context) { | |
99 owner_.Reset(env, jbidi_stream); | |
pauljensen
2016/01/19 16:03:39
ditto
mef
2016/01/20 15:37:40
Done.
| |
100 } | |
101 | |
102 CronetBidirectionalStreamAdapter::~CronetBidirectionalStreamAdapter() { | |
103 DCHECK(context_->IsOnNetworkThread()); | |
104 } | |
105 | |
106 jint CronetBidirectionalStreamAdapter::Start( | |
107 JNIEnv* env, | |
108 const JavaParamRef<jobject>& jcaller, | |
109 const JavaParamRef<jstring>& jurl, | |
110 jint jpriority, | |
111 const JavaParamRef<jstring>& jmethod, | |
112 const JavaParamRef<jobjectArray>& jheaders, | |
113 jboolean jend_of_stream) { | |
114 // Prepare request info here to be able to return the error. | |
115 scoped_ptr<net::BidirectionalStreamRequestInfo> request_info( | |
116 new net::BidirectionalStreamRequestInfo()); | |
117 request_info->url = GURL(ConvertJavaStringToUTF8(env, jurl)); | |
118 request_info->priority = static_cast<net::RequestPriority>(jpriority); | |
119 // Http method is a token, just as header name. | |
120 request_info->method = ConvertJavaStringToUTF8(env, jmethod); | |
121 if (!net::HttpUtil::IsValidHeaderName(request_info->method)) | |
122 return -1; | |
123 | |
124 std::vector<std::string> headers; | |
125 base::android::AppendJavaStringArrayToStringVector(env, jheaders, &headers); | |
126 for (size_t i = 0; i < headers.size(); i += 2) { | |
127 std::string name(headers[i]); | |
128 std::string value(headers[i + 1]); | |
129 if (!net::HttpUtil::IsValidHeaderName(name) || | |
130 !net::HttpUtil::IsValidHeaderValue(value)) { | |
131 return i + 1; | |
132 } | |
133 | |
134 request_info->extra_headers.SetHeader(name, value); | |
135 } | |
136 request_info->end_stream_on_headers = jend_of_stream; | |
137 | |
138 context_->PostTaskToNetworkThread( | |
139 FROM_HERE, | |
140 base::Bind(&CronetBidirectionalStreamAdapter::StartOnNetworkThread, | |
141 base::Unretained(this), base::Passed(&request_info))); | |
142 return 0; | |
143 } | |
144 | |
145 void CronetBidirectionalStreamAdapter::StartOnNetworkThread( | |
146 scoped_ptr<net::BidirectionalStreamRequestInfo> request_info) { | |
147 DCHECK(context_->IsOnNetworkThread()); | |
148 | |
149 VLOG(1) << "Starting bidirectional stream: " | |
150 << request_info->url.possibly_invalid_spec().c_str(); | |
151 | |
152 bidi_stream_.reset(new net::BidirectionalStream( | |
153 std::move(request_info), context_->GetURLRequestContext() | |
154 ->http_transaction_factory() | |
155 ->GetSession(), | |
156 this)); | |
157 } | |
158 | |
159 jboolean CronetBidirectionalStreamAdapter::ReadData( | |
160 JNIEnv* env, | |
161 const JavaParamRef<jobject>& jcaller, | |
162 const JavaParamRef<jobject>& jbyte_buffer, | |
163 jint jposition, | |
164 jint jlimit) { | |
165 DCHECK_LT(jposition, jlimit); | |
166 | |
167 void* data = env->GetDirectBufferAddress(jbyte_buffer); | |
168 if (!data) | |
169 return JNI_FALSE; | |
170 | |
171 scoped_refptr<IOBufferWithByteBuffer> read_buffer( | |
172 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition, jlimit)); | |
173 | |
174 int remaining_capacity = jlimit - jposition; | |
175 | |
176 context_->PostTaskToNetworkThread( | |
177 FROM_HERE, | |
178 base::Bind(&CronetBidirectionalStreamAdapter::ReadDataOnNetworkThread, | |
179 base::Unretained(this), read_buffer, remaining_capacity)); | |
180 return JNI_TRUE; | |
181 } | |
182 | |
183 jboolean CronetBidirectionalStreamAdapter::WriteData( | |
184 JNIEnv* env, | |
185 const JavaParamRef<jobject>& jcaller, | |
186 const JavaParamRef<jobject>& jbyte_buffer, | |
187 jint jposition, | |
188 jint jlimit, | |
189 jboolean jend_of_stream) { | |
190 DCHECK_LT(jposition, jlimit); | |
191 | |
192 void* data = env->GetDirectBufferAddress(jbyte_buffer); | |
193 if (!data) | |
194 return JNI_FALSE; | |
195 | |
196 scoped_refptr<IOBufferWithByteBuffer> write_buffer( | |
197 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition, jlimit)); | |
198 | |
199 int remaining_capacity = jlimit - jposition; | |
200 | |
201 context_->PostTaskToNetworkThread( | |
202 FROM_HERE, | |
203 base::Bind(&CronetBidirectionalStreamAdapter::WriteDataOnNetworkThread, | |
204 base::Unretained(this), write_buffer, remaining_capacity, | |
205 jend_of_stream)); | |
206 return JNI_TRUE; | |
207 } | |
208 | |
209 void CronetBidirectionalStreamAdapter::Destroy( | |
210 JNIEnv* env, | |
211 const JavaParamRef<jobject>& jcaller, | |
212 jboolean jsend_on_canceled) { | |
213 // Destroy could be called from any thread, including network thread (if | |
214 // posting task to executor throws an exception), but is posted, so |this| | |
215 // is valid until calling task is complete. Destroy() is always called from | |
216 // within a synchronized java block that guarantees no future posts to the | |
217 // network thread with the adapter pointer. | |
218 context_->PostTaskToNetworkThread( | |
219 FROM_HERE, | |
220 base::Bind(&CronetBidirectionalStreamAdapter::DestroyOnNetworkThread, | |
221 base::Unretained(this), jsend_on_canceled)); | |
222 } | |
223 | |
224 // net::BidirectionalStream::Delegate overrides (called on network thread). | |
225 | |
226 void CronetBidirectionalStreamAdapter::OnHeadersSent() { | |
227 VLOG(1) << "OnHeadersSent"; | |
228 DCHECK(context_->IsOnNetworkThread()); | |
229 JNIEnv* env = base::android::AttachCurrentThread(); | |
230 cronet::Java_CronetBidirectionalStream_onRequestHeadersSent(env, | |
231 owner_.obj()); | |
232 } | |
233 | |
234 void CronetBidirectionalStreamAdapter::OnHeadersReceived( | |
235 const net::SpdyHeaderBlock& response_headers) { | |
236 VLOG(1) << "OnHeadersReceived"; | |
237 DCHECK(context_->IsOnNetworkThread()); | |
238 JNIEnv* env = base::android::AttachCurrentThread(); | |
239 // Get http status code from response headers. | |
240 jint http_status_code = 0; | |
241 const auto http_status_header = response_headers.find(":status"); | |
242 if (http_status_header != response_headers.end()) | |
243 base::StringToInt(http_status_header->second, &http_status_code); | |
244 | |
245 std::string protocol; | |
246 switch (bidi_stream_->GetProtocol()) { | |
247 case net::kProtoHTTP2: | |
248 protocol = "h2"; | |
249 default: | |
250 break; | |
251 } | |
252 | |
253 cronet::Java_CronetBidirectionalStream_onResponseHeadersReceived( | |
pauljensen
2016/01/19 16:03:39
should we pass up the received byte count?
mef
2016/01/20 15:37:39
Done.
| |
254 env, owner_.obj(), http_status_code, | |
255 ConvertUTF8ToJavaString(env, protocol).obj(), | |
256 GetHeadersArray(env, response_headers).obj()); | |
257 } | |
258 | |
259 void CronetBidirectionalStreamAdapter::OnDataRead(int bytes_read) { | |
260 VLOG(1) << "OnDataRead:" << bytes_read; | |
261 DCHECK(context_->IsOnNetworkThread()); | |
262 jlong received_bytes_count = bidi_stream_->GetTotalReceivedBytes(); | |
263 JNIEnv* env = base::android::AttachCurrentThread(); | |
264 cronet::Java_CronetBidirectionalStream_onReadCompleted( | |
265 env, owner_.obj(), read_buffer_->byte_buffer(), bytes_read, | |
266 read_buffer_->initial_position(), read_buffer_->initial_limit(), | |
267 received_bytes_count); | |
pauljensen
2016/01/19 16:03:39
nit: how about "received_bytes_count"->"bidi_strea
mef
2016/01/20 15:37:40
Done.
| |
268 // Free the read buffer. This lets the Java ByteBuffer be freed, if the | |
269 // embedder releases it, too. | |
270 read_buffer_ = nullptr; | |
271 } | |
272 | |
273 void CronetBidirectionalStreamAdapter::OnDataSent() { | |
274 DCHECK(context_->IsOnNetworkThread()); | |
275 JNIEnv* env = base::android::AttachCurrentThread(); | |
276 cronet::Java_CronetBidirectionalStream_onWriteCompleted( | |
277 env, owner_.obj(), write_buffer_->byte_buffer(), | |
278 write_buffer_->initial_position(), write_buffer_->initial_limit()); | |
279 // Free the write buffer. This lets the Java ByteBuffer be freed, if the | |
280 // embedder releases it, too. | |
281 write_buffer_ = nullptr; | |
282 } | |
283 | |
284 void CronetBidirectionalStreamAdapter::OnTrailersReceived( | |
285 const net::SpdyHeaderBlock& response_trailers) { | |
286 DCHECK(context_->IsOnNetworkThread()); | |
287 JNIEnv* env = base::android::AttachCurrentThread(); | |
288 cronet::Java_CronetBidirectionalStream_onResponseTrailersReceived( | |
289 env, owner_.obj(), GetHeadersArray(env, response_trailers).obj()); | |
290 } | |
291 | |
292 void CronetBidirectionalStreamAdapter::OnFailed(int error) { | |
293 DCHECK(context_->IsOnNetworkThread()); | |
294 VLOG(1) << "OnFailed:" << error; | |
295 jlong received_bytes_count = bidi_stream_->GetTotalReceivedBytes(); | |
296 JNIEnv* env = base::android::AttachCurrentThread(); | |
297 cronet::Java_CronetBidirectionalStream_onError( | |
298 env, owner_.obj(), error, | |
299 ConvertUTF8ToJavaString(env, net::ErrorToString(error)).obj(), | |
300 received_bytes_count); | |
pauljensen
2016/01/19 16:03:39
ditto
mef
2016/01/20 15:37:39
Done.
| |
301 } | |
302 | |
303 base::android::ScopedJavaLocalRef<jobjectArray> | |
304 CronetBidirectionalStreamAdapter::GetHeadersArray( | |
305 JNIEnv* env, | |
306 const net::SpdyHeaderBlock& header_block) { | |
307 DCHECK(context_->IsOnNetworkThread()); | |
308 | |
309 std::vector<std::string> headers; | |
310 for (const auto& header : header_block) { | |
311 headers.push_back(header.first.as_string()); | |
312 headers.push_back(header.second.as_string()); | |
313 } | |
314 return base::android::ToJavaArrayOfStrings(env, headers); | |
315 } | |
316 | |
317 void CronetBidirectionalStreamAdapter::ReadDataOnNetworkThread( | |
318 scoped_refptr<IOBufferWithByteBuffer> read_buffer, | |
319 int buffer_size) { | |
320 DCHECK(context_->IsOnNetworkThread()); | |
321 DCHECK(read_buffer); | |
322 DCHECK(!read_buffer_); | |
323 | |
324 read_buffer_ = read_buffer; | |
325 | |
326 int bytes_read = bidi_stream_->ReadData(read_buffer_.get(), buffer_size); | |
327 // If IO is pending, wait for the BidirectionalStream to call OnDataRead. | |
328 if (bytes_read == net::ERR_IO_PENDING) | |
329 return; | |
330 | |
331 if (bytes_read < 0) { | |
332 OnFailed(bytes_read); | |
333 return; | |
334 } | |
335 OnDataRead(bytes_read); | |
336 } | |
337 | |
338 void CronetBidirectionalStreamAdapter::WriteDataOnNetworkThread( | |
339 scoped_refptr<IOBufferWithByteBuffer> write_buffer, | |
340 int buffer_size, | |
341 bool end_of_stream) { | |
342 DCHECK(context_->IsOnNetworkThread()); | |
343 DCHECK(write_buffer); | |
344 DCHECK(!write_buffer_); | |
345 | |
346 write_buffer_ = write_buffer; | |
347 bidi_stream_->SendData(write_buffer_.get(), buffer_size, end_of_stream); | |
348 } | |
349 | |
350 void CronetBidirectionalStreamAdapter::DestroyOnNetworkThread( | |
351 bool send_on_canceled) { | |
352 DCHECK(context_->IsOnNetworkThread()); | |
353 if (send_on_canceled) { | |
354 JNIEnv* env = base::android::AttachCurrentThread(); | |
355 cronet::Java_CronetBidirectionalStream_onCanceled(env, owner_.obj()); | |
356 } | |
357 delete this; | |
358 } | |
359 | |
360 } // namespace cronet | |
OLD | NEW |