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.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 CronetBidirectionalStreamRegisterJni(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 CronetBidirectionalStream* adapter = | |
51 new CronetBidirectionalStream(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. | |
pauljensen
2016/01/06 17:18:33
when do you think de-duping IOBufferWithByteBuffer
mef
2016/01/06 21:28:30
In a separate CL as I've just added an initial_lim
pauljensen
2016/01/07 02:56:34
when do you think such a CL would land?
mef
2016/01/07 03:22:15
Soon after. I can create it now, I just don't want
pauljensen
2016/01/11 20:05:45
I agree with not wanting it in this CL...I just wa
mef
2016/01/11 23:22:50
Acknowledged.
| |
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 CronetBidirectionalStream::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 : net::WrappedIOBuffer(static_cast<char*>(data) + position), | |
72 initial_position_(position) { | |
73 DCHECK(data); | |
74 DCHECK_EQ(env->GetDirectBufferAddress(jbyte_buffer), data); | |
75 byte_buffer_.Reset(env, jbyte_buffer); | |
76 } | |
77 | |
78 int initial_position() const { return initial_position_; } | |
79 | |
80 jobject byte_buffer() const { return byte_buffer_.obj(); } | |
81 | |
82 private: | |
83 ~IOBufferWithByteBuffer() override {} | |
84 | |
85 base::android::ScopedJavaGlobalRef<jobject> byte_buffer_; | |
86 | |
87 const int initial_position_; | |
88 }; | |
89 | |
90 CronetBidirectionalStream::CronetBidirectionalStream( | |
91 CronetURLRequestContextAdapter* context, | |
92 JNIEnv* env, | |
93 const JavaParamRef<jobject>& jbidi_stream) | |
94 : context_(context) { | |
95 DCHECK(!context_->IsOnNetworkThread()); | |
96 owner_.Reset(env, jbidi_stream); | |
97 } | |
98 | |
99 CronetBidirectionalStream::~CronetBidirectionalStream() { | |
100 DCHECK(context_->IsOnNetworkThread()); | |
101 } | |
102 | |
103 jint CronetBidirectionalStream::Start(JNIEnv* env, | |
104 const JavaParamRef<jobject>& jcaller, | |
105 const JavaParamRef<jstring>& jurl, | |
106 jint jpriority, | |
107 const JavaParamRef<jstring>& jmethod, | |
108 const JavaParamRef<jobjectArray> jheaders, | |
109 jboolean jend_of_stream) { | |
110 DCHECK(!context_->IsOnNetworkThread()); | |
111 // Prepare request info here to be able to return the error. | |
112 scoped_ptr<net::BidirectionalStreamRequestInfo> request_info( | |
113 new net::BidirectionalStreamRequestInfo()); | |
114 request_info->url = GURL(ConvertJavaStringToUTF8(env, jurl)); | |
115 request_info->priority = static_cast<net::RequestPriority>(jpriority); | |
116 // Http method is a token, just as header name. | |
117 request_info->method = ConvertJavaStringToUTF8(env, jmethod); | |
118 if (!net::HttpUtil::IsValidHeaderName(request_info->method)) | |
119 return -1; | |
120 | |
121 std::vector<std::string> headers; | |
122 base::android::AppendJavaStringArrayToStringVector(env, jheaders, &headers); | |
123 for (size_t i = 0; i < headers.size(); i += 2) { | |
124 std::string name(headers[i]); | |
125 std::string value(headers[i + 1]); | |
126 if (!net::HttpUtil::IsValidHeaderName(name) || | |
127 !net::HttpUtil::IsValidHeaderValue(value)) { | |
128 return i + 1; | |
129 } | |
130 | |
131 request_info->extra_headers.SetHeader(name, value); | |
132 } | |
133 request_info->end_stream_on_headers = jend_of_stream; | |
134 | |
135 context_->PostTaskToNetworkThread( | |
136 FROM_HERE, | |
137 base::Bind(&CronetBidirectionalStream::StartOnNetworkThread, | |
138 base::Unretained(this), base::Passed(&request_info))); | |
139 return 0; | |
140 } | |
141 | |
142 void CronetBidirectionalStream::StartOnNetworkThread( | |
143 scoped_ptr<net::BidirectionalStreamRequestInfo> request_info) { | |
144 DCHECK(context_->IsOnNetworkThread()); | |
145 | |
146 VLOG(1) << "Starting bidirectional stream: " | |
147 << request_info->url.possibly_invalid_spec().c_str(); | |
148 | |
149 bidi_stream_.reset(new net::BidirectionalStream( | |
150 std::move(request_info), context_->GetURLRequestContext() | |
151 ->http_transaction_factory() | |
152 ->GetSession(), | |
153 this)); | |
154 } | |
155 | |
156 jboolean CronetBidirectionalStream::ReadData( | |
157 JNIEnv* env, | |
158 const JavaParamRef<jobject>& jcaller, | |
pauljensen
2016/01/06 17:18:33
how come this definition is "const JavaParamRef<jo
mef
2016/01/06 21:28:29
Done.
| |
159 const JavaParamRef<jobject>& jbyte_buffer, | |
160 jint jposition, | |
161 jint jcapacity) { | |
pauljensen
2016/01/06 17:18:33
jcapacity->jlimit
capacity has a very different co
mef
2016/01/06 21:28:29
Done.
| |
162 DCHECK(!context_->IsOnNetworkThread()); | |
163 DCHECK_LT(jposition, jcapacity); | |
164 | |
165 void* data = env->GetDirectBufferAddress(jbyte_buffer); | |
166 if (!data) | |
167 return JNI_FALSE; | |
168 | |
169 scoped_refptr<IOBufferWithByteBuffer> read_buffer( | |
170 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition)); | |
171 | |
172 int remaining_capacity = jcapacity - jposition; | |
173 | |
174 context_->PostTaskToNetworkThread( | |
175 FROM_HERE, | |
176 base::Bind(&CronetBidirectionalStream::ReadDataOnNetworkThread, | |
177 base::Unretained(this), read_buffer, remaining_capacity)); | |
178 return JNI_TRUE; | |
179 } | |
180 | |
181 jboolean CronetBidirectionalStream::WriteData( | |
182 JNIEnv* env, | |
183 const JavaParamRef<jobject>& jcaller, | |
184 const JavaParamRef<jobject>& jbyte_buffer, | |
185 jint jposition, | |
186 jint jcapacity, | |
pauljensen
2016/01/06 17:18:33
jcapacity->jlimit
capacity has a very different co
mef
2016/01/06 21:28:29
Done.
| |
187 jboolean jend_of_stream) { | |
188 DCHECK(!context_->IsOnNetworkThread()); | |
189 DCHECK_LT(jposition, jcapacity); | |
190 | |
191 void* data = env->GetDirectBufferAddress(jbyte_buffer); | |
192 if (!data) | |
193 return JNI_FALSE; | |
194 | |
195 scoped_refptr<IOBufferWithByteBuffer> write_buffer( | |
196 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition)); | |
197 | |
198 int remaining_capacity = jcapacity - jposition; | |
199 | |
200 context_->PostTaskToNetworkThread( | |
201 FROM_HERE, | |
202 base::Bind(&CronetBidirectionalStream::WriteDataOnNetworkThread, | |
203 base::Unretained(this), write_buffer, remaining_capacity, | |
204 jend_of_stream)); | |
205 return JNI_TRUE; | |
206 } | |
207 | |
208 void CronetBidirectionalStream::Destroy(JNIEnv* env, | |
209 const JavaParamRef<jobject>& jcaller, | |
210 jboolean jsend_on_canceled) { | |
211 // Destroy could be called from any thread, including network thread (if | |
212 // posting task to executor throws an exception), but is posted, so |this| | |
213 // is valid until calling task is complete. Destroy() is always called from | |
214 // within a synchronized java block that guarantees no future posts to the | |
215 // network thread with the adapter pointer. | |
216 context_->PostTaskToNetworkThread( | |
217 FROM_HERE, base::Bind(&CronetBidirectionalStream::DestroyOnNetworkThread, | |
218 base::Unretained(this), jsend_on_canceled)); | |
219 } | |
220 | |
221 base::android::ScopedJavaLocalRef<jstring> | |
222 CronetBidirectionalStream::GetNegotiatedProtocol( | |
223 JNIEnv* env, | |
224 const JavaParamRef<jobject>& jcaller) const { | |
225 DCHECK(context_->IsOnNetworkThread()); | |
226 switch (bidi_stream_->GetProtocol()) { | |
227 case net::kProtoHTTP2: | |
228 return ConvertUTF8ToJavaString(env, "h2"); | |
229 default: | |
230 break; | |
231 } | |
232 NOTREACHED(); | |
233 return ConvertUTF8ToJavaString(env, ""); | |
234 } | |
235 | |
236 // net::BidirectionalStream::Delegate overrides (called on network thread). | |
237 | |
238 void CronetBidirectionalStream::OnHeadersSent() { | |
239 VLOG(1) << "OnHeadersSent"; | |
240 DCHECK(context_->IsOnNetworkThread()); | |
241 JNIEnv* env = base::android::AttachCurrentThread(); | |
242 cronet::Java_CronetBidirectionalStream_onRequestHeadersSent(env, | |
243 owner_.obj()); | |
244 } | |
245 | |
246 void CronetBidirectionalStream::OnHeadersReceived( | |
247 const net::SpdyHeaderBlock& response_headers) { | |
248 VLOG(1) << "OnHeadersReceived"; | |
249 DCHECK(context_->IsOnNetworkThread()); | |
250 JNIEnv* env = base::android::AttachCurrentThread(); | |
251 // Get http status code from response headers. | |
252 jint http_status_code = 0; | |
253 const auto http_status_header = response_headers.find(":status"); | |
254 if (http_status_header != response_headers.end()) | |
255 base::StringToInt(http_status_header->second, &http_status_code); | |
256 | |
257 cronet::Java_CronetBidirectionalStream_onResponseHeadersReceived( | |
258 env, owner_.obj(), http_status_code, | |
259 GetHeadersArray(env, response_headers).obj()); | |
260 } | |
261 | |
262 void CronetBidirectionalStream::OnDataRead(int bytes_read) { | |
263 VLOG(1) << "OnDataRead:" << bytes_read; | |
264 DCHECK(context_->IsOnNetworkThread()); | |
265 jlong received_bytes_count = bidi_stream_->GetTotalReceivedBytes(); | |
266 JNIEnv* env = base::android::AttachCurrentThread(); | |
267 cronet::Java_CronetBidirectionalStream_onReadCompleted( | |
268 env, owner_.obj(), read_buffer_->byte_buffer(), bytes_read, | |
269 read_buffer_->initial_position(), received_bytes_count); | |
270 // Free the read buffer. This lets the Java ByteBuffer be freed, if the | |
271 // embedder releases it, too. | |
272 read_buffer_ = nullptr; | |
273 } | |
274 | |
275 void CronetBidirectionalStream::OnDataSent() { | |
276 DCHECK(context_->IsOnNetworkThread()); | |
277 JNIEnv* env = base::android::AttachCurrentThread(); | |
278 cronet::Java_CronetBidirectionalStream_onWriteCompleted( | |
279 env, owner_.obj(), write_buffer_->byte_buffer(), | |
280 write_buffer_->initial_position()); | |
281 // Free the write buffer. This lets the Java ByteBuffer be freed, if the | |
282 // embedder releases it, too. | |
283 write_buffer_ = nullptr; | |
284 } | |
285 | |
286 void CronetBidirectionalStream::OnTrailersReceived( | |
287 const net::SpdyHeaderBlock& response_trailers) { | |
288 DCHECK(context_->IsOnNetworkThread()); | |
289 JNIEnv* env = base::android::AttachCurrentThread(); | |
290 cronet::Java_CronetBidirectionalStream_onResponseTrailersReceived( | |
291 env, owner_.obj(), GetHeadersArray(env, response_trailers).obj()); | |
292 } | |
293 | |
294 void CronetBidirectionalStream::OnFailed(int error) { | |
295 DCHECK(context_->IsOnNetworkThread()); | |
296 VLOG(1) << "OnFailed:" << error; | |
297 jlong received_bytes_count = bidi_stream_->GetTotalReceivedBytes(); | |
298 JNIEnv* env = base::android::AttachCurrentThread(); | |
299 cronet::Java_CronetBidirectionalStream_onError( | |
300 env, owner_.obj(), error, | |
301 ConvertUTF8ToJavaString(env, net::ErrorToString(error)).obj(), | |
302 received_bytes_count); | |
303 } | |
304 | |
305 base::android::ScopedJavaLocalRef<jobjectArray> | |
306 CronetBidirectionalStream::GetHeadersArray( | |
307 JNIEnv* env, | |
308 const net::SpdyHeaderBlock& header_block) { | |
309 DCHECK(context_->IsOnNetworkThread()); | |
310 | |
311 std::vector<std::string> headers; | |
312 for (const auto& header : header_block) { | |
313 headers.push_back(header.first.as_string()); | |
314 headers.push_back(header.second.as_string()); | |
315 } | |
316 return base::android::ToJavaArrayOfStrings(env, headers); | |
317 } | |
318 | |
319 void CronetBidirectionalStream::ReadDataOnNetworkThread( | |
320 scoped_refptr<IOBufferWithByteBuffer> read_buffer, | |
321 int buffer_size) { | |
322 DCHECK(context_->IsOnNetworkThread()); | |
323 DCHECK(read_buffer); | |
324 DCHECK(!read_buffer_); | |
325 | |
326 read_buffer_ = read_buffer; | |
327 | |
328 int bytes_read = bidi_stream_->ReadData(read_buffer_.get(), buffer_size); | |
329 // If IO is pending, wait for the BidirectionalStream to call OnDataRead. | |
330 if (bytes_read == net::ERR_IO_PENDING) | |
331 return; | |
332 | |
333 if (bytes_read < 0) { | |
334 OnFailed(bytes_read); | |
335 return; | |
336 } | |
337 OnDataRead(bytes_read); | |
338 } | |
339 | |
340 void CronetBidirectionalStream::WriteDataOnNetworkThread( | |
341 scoped_refptr<IOBufferWithByteBuffer> write_buffer, | |
342 int buffer_size, | |
343 bool end_of_stream) { | |
344 DCHECK(context_->IsOnNetworkThread()); | |
345 DCHECK(write_buffer); | |
346 DCHECK(!write_buffer_); | |
347 | |
348 write_buffer_ = write_buffer; | |
349 bidi_stream_->SendData(write_buffer_.get(), buffer_size, end_of_stream); | |
350 } | |
351 | |
352 void CronetBidirectionalStream::DestroyOnNetworkThread(bool send_on_canceled) { | |
353 DCHECK(context_->IsOnNetworkThread()); | |
354 if (send_on_canceled) { | |
355 JNIEnv* env = base::android::AttachCurrentThread(); | |
356 cronet::Java_CronetBidirectionalStream_onCanceled(env, owner_.obj()); | |
357 } | |
358 delete this; | |
359 } | |
360 | |
361 } // namespace cronet | |
OLD | NEW |