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

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

Powered by Google App Engine
This is Rietveld 408576698