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

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

Powered by Google App Engine
This is Rietveld 408576698