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