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

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

Issue 1412243012: Initial implementation of CronetBidirectionalStream. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sync Created 5 years 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.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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698