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

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: Address Helen'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.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 CronetBidirectionalStreamRegisterJni(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 CronetBidirectionalStream* adapter =
51 new CronetBidirectionalStream(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.
pauljensen 2016/01/06 17:18:33 when do you think de-duping IOBufferWithByteBuffer
mef 2016/01/06 21:28:30 In a separate CL as I've just added an initial_lim
pauljensen 2016/01/07 02:56:34 when do you think such a CL would land?
mef 2016/01/07 03:22:15 Soon after. I can create it now, I just don't want
pauljensen 2016/01/11 20:05:45 I agree with not wanting it in this CL...I just wa
mef 2016/01/11 23:22:50 Acknowledged.
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 CronetBidirectionalStream::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 : net::WrappedIOBuffer(static_cast<char*>(data) + position),
72 initial_position_(position) {
73 DCHECK(data);
74 DCHECK_EQ(env->GetDirectBufferAddress(jbyte_buffer), data);
75 byte_buffer_.Reset(env, jbyte_buffer);
76 }
77
78 int initial_position() const { return initial_position_; }
79
80 jobject byte_buffer() const { return byte_buffer_.obj(); }
81
82 private:
83 ~IOBufferWithByteBuffer() override {}
84
85 base::android::ScopedJavaGlobalRef<jobject> byte_buffer_;
86
87 const int initial_position_;
88 };
89
90 CronetBidirectionalStream::CronetBidirectionalStream(
91 CronetURLRequestContextAdapter* context,
92 JNIEnv* env,
93 const JavaParamRef<jobject>& jbidi_stream)
94 : context_(context) {
95 DCHECK(!context_->IsOnNetworkThread());
96 owner_.Reset(env, jbidi_stream);
97 }
98
99 CronetBidirectionalStream::~CronetBidirectionalStream() {
100 DCHECK(context_->IsOnNetworkThread());
101 }
102
103 jint CronetBidirectionalStream::Start(JNIEnv* env,
104 const JavaParamRef<jobject>& jcaller,
105 const JavaParamRef<jstring>& jurl,
106 jint jpriority,
107 const JavaParamRef<jstring>& jmethod,
108 const JavaParamRef<jobjectArray> jheaders,
109 jboolean jend_of_stream) {
110 DCHECK(!context_->IsOnNetworkThread());
111 // Prepare request info here to be able to return the error.
112 scoped_ptr<net::BidirectionalStreamRequestInfo> request_info(
113 new net::BidirectionalStreamRequestInfo());
114 request_info->url = GURL(ConvertJavaStringToUTF8(env, jurl));
115 request_info->priority = static_cast<net::RequestPriority>(jpriority);
116 // Http method is a token, just as header name.
117 request_info->method = ConvertJavaStringToUTF8(env, jmethod);
118 if (!net::HttpUtil::IsValidHeaderName(request_info->method))
119 return -1;
120
121 std::vector<std::string> headers;
122 base::android::AppendJavaStringArrayToStringVector(env, jheaders, &headers);
123 for (size_t i = 0; i < headers.size(); i += 2) {
124 std::string name(headers[i]);
125 std::string value(headers[i + 1]);
126 if (!net::HttpUtil::IsValidHeaderName(name) ||
127 !net::HttpUtil::IsValidHeaderValue(value)) {
128 return i + 1;
129 }
130
131 request_info->extra_headers.SetHeader(name, value);
132 }
133 request_info->end_stream_on_headers = jend_of_stream;
134
135 context_->PostTaskToNetworkThread(
136 FROM_HERE,
137 base::Bind(&CronetBidirectionalStream::StartOnNetworkThread,
138 base::Unretained(this), base::Passed(&request_info)));
139 return 0;
140 }
141
142 void CronetBidirectionalStream::StartOnNetworkThread(
143 scoped_ptr<net::BidirectionalStreamRequestInfo> request_info) {
144 DCHECK(context_->IsOnNetworkThread());
145
146 VLOG(1) << "Starting bidirectional stream: "
147 << request_info->url.possibly_invalid_spec().c_str();
148
149 bidi_stream_.reset(new net::BidirectionalStream(
150 std::move(request_info), context_->GetURLRequestContext()
151 ->http_transaction_factory()
152 ->GetSession(),
153 this));
154 }
155
156 jboolean CronetBidirectionalStream::ReadData(
157 JNIEnv* env,
158 const JavaParamRef<jobject>& jcaller,
pauljensen 2016/01/06 17:18:33 how come this definition is "const JavaParamRef<jo
mef 2016/01/06 21:28:29 Done.
159 const JavaParamRef<jobject>& jbyte_buffer,
160 jint jposition,
161 jint jcapacity) {
pauljensen 2016/01/06 17:18:33 jcapacity->jlimit capacity has a very different co
mef 2016/01/06 21:28:29 Done.
162 DCHECK(!context_->IsOnNetworkThread());
163 DCHECK_LT(jposition, jcapacity);
164
165 void* data = env->GetDirectBufferAddress(jbyte_buffer);
166 if (!data)
167 return JNI_FALSE;
168
169 scoped_refptr<IOBufferWithByteBuffer> read_buffer(
170 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition));
171
172 int remaining_capacity = jcapacity - jposition;
173
174 context_->PostTaskToNetworkThread(
175 FROM_HERE,
176 base::Bind(&CronetBidirectionalStream::ReadDataOnNetworkThread,
177 base::Unretained(this), read_buffer, remaining_capacity));
178 return JNI_TRUE;
179 }
180
181 jboolean CronetBidirectionalStream::WriteData(
182 JNIEnv* env,
183 const JavaParamRef<jobject>& jcaller,
184 const JavaParamRef<jobject>& jbyte_buffer,
185 jint jposition,
186 jint jcapacity,
pauljensen 2016/01/06 17:18:33 jcapacity->jlimit capacity has a very different co
mef 2016/01/06 21:28:29 Done.
187 jboolean jend_of_stream) {
188 DCHECK(!context_->IsOnNetworkThread());
189 DCHECK_LT(jposition, jcapacity);
190
191 void* data = env->GetDirectBufferAddress(jbyte_buffer);
192 if (!data)
193 return JNI_FALSE;
194
195 scoped_refptr<IOBufferWithByteBuffer> write_buffer(
196 new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition));
197
198 int remaining_capacity = jcapacity - jposition;
199
200 context_->PostTaskToNetworkThread(
201 FROM_HERE,
202 base::Bind(&CronetBidirectionalStream::WriteDataOnNetworkThread,
203 base::Unretained(this), write_buffer, remaining_capacity,
204 jend_of_stream));
205 return JNI_TRUE;
206 }
207
208 void CronetBidirectionalStream::Destroy(JNIEnv* env,
209 const JavaParamRef<jobject>& jcaller,
210 jboolean jsend_on_canceled) {
211 // Destroy could be called from any thread, including network thread (if
212 // posting task to executor throws an exception), but is posted, so |this|
213 // is valid until calling task is complete. Destroy() is always called from
214 // within a synchronized java block that guarantees no future posts to the
215 // network thread with the adapter pointer.
216 context_->PostTaskToNetworkThread(
217 FROM_HERE, base::Bind(&CronetBidirectionalStream::DestroyOnNetworkThread,
218 base::Unretained(this), jsend_on_canceled));
219 }
220
221 base::android::ScopedJavaLocalRef<jstring>
222 CronetBidirectionalStream::GetNegotiatedProtocol(
223 JNIEnv* env,
224 const JavaParamRef<jobject>& jcaller) const {
225 DCHECK(context_->IsOnNetworkThread());
226 switch (bidi_stream_->GetProtocol()) {
227 case net::kProtoHTTP2:
228 return ConvertUTF8ToJavaString(env, "h2");
229 default:
230 break;
231 }
232 NOTREACHED();
233 return ConvertUTF8ToJavaString(env, "");
234 }
235
236 // net::BidirectionalStream::Delegate overrides (called on network thread).
237
238 void CronetBidirectionalStream::OnHeadersSent() {
239 VLOG(1) << "OnHeadersSent";
240 DCHECK(context_->IsOnNetworkThread());
241 JNIEnv* env = base::android::AttachCurrentThread();
242 cronet::Java_CronetBidirectionalStream_onRequestHeadersSent(env,
243 owner_.obj());
244 }
245
246 void CronetBidirectionalStream::OnHeadersReceived(
247 const net::SpdyHeaderBlock& response_headers) {
248 VLOG(1) << "OnHeadersReceived";
249 DCHECK(context_->IsOnNetworkThread());
250 JNIEnv* env = base::android::AttachCurrentThread();
251 // Get http status code from response headers.
252 jint http_status_code = 0;
253 const auto http_status_header = response_headers.find(":status");
254 if (http_status_header != response_headers.end())
255 base::StringToInt(http_status_header->second, &http_status_code);
256
257 cronet::Java_CronetBidirectionalStream_onResponseHeadersReceived(
258 env, owner_.obj(), http_status_code,
259 GetHeadersArray(env, response_headers).obj());
260 }
261
262 void CronetBidirectionalStream::OnDataRead(int bytes_read) {
263 VLOG(1) << "OnDataRead:" << bytes_read;
264 DCHECK(context_->IsOnNetworkThread());
265 jlong received_bytes_count = bidi_stream_->GetTotalReceivedBytes();
266 JNIEnv* env = base::android::AttachCurrentThread();
267 cronet::Java_CronetBidirectionalStream_onReadCompleted(
268 env, owner_.obj(), read_buffer_->byte_buffer(), bytes_read,
269 read_buffer_->initial_position(), received_bytes_count);
270 // Free the read buffer. This lets the Java ByteBuffer be freed, if the
271 // embedder releases it, too.
272 read_buffer_ = nullptr;
273 }
274
275 void CronetBidirectionalStream::OnDataSent() {
276 DCHECK(context_->IsOnNetworkThread());
277 JNIEnv* env = base::android::AttachCurrentThread();
278 cronet::Java_CronetBidirectionalStream_onWriteCompleted(
279 env, owner_.obj(), write_buffer_->byte_buffer(),
280 write_buffer_->initial_position());
281 // Free the write buffer. This lets the Java ByteBuffer be freed, if the
282 // embedder releases it, too.
283 write_buffer_ = nullptr;
284 }
285
286 void CronetBidirectionalStream::OnTrailersReceived(
287 const net::SpdyHeaderBlock& response_trailers) {
288 DCHECK(context_->IsOnNetworkThread());
289 JNIEnv* env = base::android::AttachCurrentThread();
290 cronet::Java_CronetBidirectionalStream_onResponseTrailersReceived(
291 env, owner_.obj(), GetHeadersArray(env, response_trailers).obj());
292 }
293
294 void CronetBidirectionalStream::OnFailed(int error) {
295 DCHECK(context_->IsOnNetworkThread());
296 VLOG(1) << "OnFailed:" << error;
297 jlong received_bytes_count = bidi_stream_->GetTotalReceivedBytes();
298 JNIEnv* env = base::android::AttachCurrentThread();
299 cronet::Java_CronetBidirectionalStream_onError(
300 env, owner_.obj(), error,
301 ConvertUTF8ToJavaString(env, net::ErrorToString(error)).obj(),
302 received_bytes_count);
303 }
304
305 base::android::ScopedJavaLocalRef<jobjectArray>
306 CronetBidirectionalStream::GetHeadersArray(
307 JNIEnv* env,
308 const net::SpdyHeaderBlock& header_block) {
309 DCHECK(context_->IsOnNetworkThread());
310
311 std::vector<std::string> headers;
312 for (const auto& header : header_block) {
313 headers.push_back(header.first.as_string());
314 headers.push_back(header.second.as_string());
315 }
316 return base::android::ToJavaArrayOfStrings(env, headers);
317 }
318
319 void CronetBidirectionalStream::ReadDataOnNetworkThread(
320 scoped_refptr<IOBufferWithByteBuffer> read_buffer,
321 int buffer_size) {
322 DCHECK(context_->IsOnNetworkThread());
323 DCHECK(read_buffer);
324 DCHECK(!read_buffer_);
325
326 read_buffer_ = read_buffer;
327
328 int bytes_read = bidi_stream_->ReadData(read_buffer_.get(), buffer_size);
329 // If IO is pending, wait for the BidirectionalStream to call OnDataRead.
330 if (bytes_read == net::ERR_IO_PENDING)
331 return;
332
333 if (bytes_read < 0) {
334 OnFailed(bytes_read);
335 return;
336 }
337 OnDataRead(bytes_read);
338 }
339
340 void CronetBidirectionalStream::WriteDataOnNetworkThread(
341 scoped_refptr<IOBufferWithByteBuffer> write_buffer,
342 int buffer_size,
343 bool end_of_stream) {
344 DCHECK(context_->IsOnNetworkThread());
345 DCHECK(write_buffer);
346 DCHECK(!write_buffer_);
347
348 write_buffer_ = write_buffer;
349 bidi_stream_->SendData(write_buffer_.get(), buffer_size, end_of_stream);
350 }
351
352 void CronetBidirectionalStream::DestroyOnNetworkThread(bool send_on_canceled) {
353 DCHECK(context_->IsOnNetworkThread());
354 if (send_on_canceled) {
355 JNIEnv* env = base::android::AttachCurrentThread();
356 cronet::Java_CronetBidirectionalStream_onCanceled(env, owner_.obj());
357 }
358 delete this;
359 }
360
361 } // namespace cronet
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698