Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: components/cronet/android/cronet_bidirectional_stream_adapter.cc

Issue 1412243012: Initial implementation of CronetBidirectionalStream. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Destroy the native adapter instead of cancel if can't post task to executor. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698