| Index: components/cronet/android/cronet_bidirectional_stream_adapter.cc
|
| diff --git a/components/cronet/android/cronet_bidirectional_stream_adapter.cc b/components/cronet/android/cronet_bidirectional_stream_adapter.cc
|
| index a33e4e3627e91f4a9ac45f1e68353c9413b96ae9..84ab85dacb426a7d514dcbe09d6d30108a8ce8e9 100644
|
| --- a/components/cronet/android/cronet_bidirectional_stream_adapter.cc
|
| +++ b/components/cronet/android/cronet_bidirectional_stream_adapter.cc
|
| @@ -34,17 +34,34 @@ using base::android::ConvertJavaStringToUTF8;
|
|
|
| namespace cronet {
|
|
|
| +namespace {
|
| +
|
| +// As |GetArrayLength| makes no guarantees about the returned value (e.g., it
|
| +// may be -1 if |array| is not a valid Java array), provide a safe wrapper
|
| +// that always returns a valid, non-negative size.
|
| +template <typename JavaArrayType>
|
| +size_t SafeGetArrayLength(JNIEnv* env, JavaArrayType jarray) {
|
| + DCHECK(jarray);
|
| + jsize length = env->GetArrayLength(jarray);
|
| + DCHECK_GE(length, 0) << "Invalid array length: " << length;
|
| + return static_cast<size_t>(std::max(0, length));
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| static jlong CreateBidirectionalStream(
|
| JNIEnv* env,
|
| - const JavaParamRef<jobject>& jbidi_stream,
|
| - jlong jurl_request_context_adapter) {
|
| + const base::android::JavaParamRef<jobject>& jbidi_stream,
|
| + jlong jurl_request_context_adapter,
|
| + jboolean jdisable_auto_flush) {
|
| CronetURLRequestContextAdapter* context_adapter =
|
| reinterpret_cast<CronetURLRequestContextAdapter*>(
|
| jurl_request_context_adapter);
|
| DCHECK(context_adapter);
|
|
|
| CronetBidirectionalStreamAdapter* adapter =
|
| - new CronetBidirectionalStreamAdapter(context_adapter, env, jbidi_stream);
|
| + new CronetBidirectionalStreamAdapter(context_adapter, env, jbidi_stream,
|
| + jdisable_auto_flush);
|
|
|
| return reinterpret_cast<jlong>(adapter);
|
| }
|
| @@ -57,8 +74,13 @@ bool CronetBidirectionalStreamAdapter::RegisterJni(JNIEnv* env) {
|
| CronetBidirectionalStreamAdapter::CronetBidirectionalStreamAdapter(
|
| CronetURLRequestContextAdapter* context,
|
| JNIEnv* env,
|
| - const JavaParamRef<jobject>& jbidi_stream)
|
| - : context_(context), owner_(env, jbidi_stream), stream_failed_(false) {}
|
| + const base::android::JavaParamRef<jobject>& jbidi_stream,
|
| + bool disable_auto_flush)
|
| + : context_(context),
|
| + owner_(env, jbidi_stream),
|
| + disable_auto_flush_(disable_auto_flush),
|
| + write_end_of_stream_(false),
|
| + stream_failed_(false) {}
|
|
|
| CronetBidirectionalStreamAdapter::~CronetBidirectionalStreamAdapter() {
|
| DCHECK(context_->IsOnNetworkThread());
|
| @@ -66,11 +88,11 @@ CronetBidirectionalStreamAdapter::~CronetBidirectionalStreamAdapter() {
|
|
|
| jint CronetBidirectionalStreamAdapter::Start(
|
| JNIEnv* env,
|
| - const JavaParamRef<jobject>& jcaller,
|
| - const JavaParamRef<jstring>& jurl,
|
| + const base::android::JavaParamRef<jobject>& jcaller,
|
| + const base::android::JavaParamRef<jstring>& jurl,
|
| jint jpriority,
|
| - const JavaParamRef<jstring>& jmethod,
|
| - const JavaParamRef<jobjectArray>& jheaders,
|
| + const base::android::JavaParamRef<jstring>& jmethod,
|
| + const base::android::JavaParamRef<jobjectArray>& jheaders,
|
| jboolean jend_of_stream) {
|
| // Prepare request info here to be able to return the error.
|
| std::unique_ptr<net::BidirectionalStreamRequestInfo> request_info(
|
| @@ -104,8 +126,8 @@ jint CronetBidirectionalStreamAdapter::Start(
|
|
|
| jboolean CronetBidirectionalStreamAdapter::ReadData(
|
| JNIEnv* env,
|
| - const JavaParamRef<jobject>& jcaller,
|
| - const JavaParamRef<jobject>& jbyte_buffer,
|
| + const base::android::JavaParamRef<jobject>& jcaller,
|
| + const base::android::JavaParamRef<jobject>& jbyte_buffer,
|
| jint jposition,
|
| jint jlimit) {
|
| DCHECK_LT(jposition, jlimit);
|
| @@ -126,35 +148,51 @@ jboolean CronetBidirectionalStreamAdapter::ReadData(
|
| return JNI_TRUE;
|
| }
|
|
|
| -jboolean CronetBidirectionalStreamAdapter::WriteData(
|
| +jboolean CronetBidirectionalStreamAdapter::WritevData(
|
| JNIEnv* env,
|
| - const JavaParamRef<jobject>& jcaller,
|
| - const JavaParamRef<jobject>& jbyte_buffer,
|
| - jint jposition,
|
| - jint jlimit,
|
| + const base::android::JavaParamRef<jobject>& jcaller,
|
| + const base::android::JavaParamRef<jobjectArray>& jbyte_buffers,
|
| + const base::android::JavaParamRef<jintArray>& jbyte_buffers_pos,
|
| + const base::android::JavaParamRef<jintArray>& jbyte_buffers_limit,
|
| jboolean jend_of_stream) {
|
| - DCHECK_LE(jposition, jlimit);
|
| -
|
| - void* data = env->GetDirectBufferAddress(jbyte_buffer);
|
| - if (!data)
|
| + size_t buffers_array_size = SafeGetArrayLength(env, jbyte_buffers.obj());
|
| + size_t pos_array_size = SafeGetArrayLength(env, jbyte_buffers.obj());
|
| + size_t limit_array_size = SafeGetArrayLength(env, jbyte_buffers.obj());
|
| + if (buffers_array_size != pos_array_size ||
|
| + buffers_array_size != limit_array_size) {
|
| + DLOG(ERROR) << "Illegal arguments.";
|
| return JNI_FALSE;
|
| + }
|
|
|
| - scoped_refptr<IOBufferWithByteBuffer> write_buffer(
|
| - new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition, jlimit));
|
| -
|
| - int remaining_capacity = jlimit - jposition;
|
| -
|
| + IOByteBufferList buffers;
|
| + for (size_t i = 0; i < buffers_array_size; ++i) {
|
| + ScopedJavaLocalRef<jobject> jbuffer(
|
| + env, env->GetObjectArrayElement(jbyte_buffers, i));
|
| + void* data = env->GetDirectBufferAddress(jbuffer.obj());
|
| + if (!data)
|
| + return JNI_FALSE;
|
| + jint pos;
|
| + env->GetIntArrayRegion(jbyte_buffers_pos.obj(), i, 1, &pos);
|
| + jint limit;
|
| + env->GetIntArrayRegion(jbyte_buffers_limit.obj(), i, 1, &limit);
|
| + DCHECK_LE(pos, limit);
|
| + scoped_refptr<IOBufferWithByteBuffer> write_buffer(
|
| + new IOBufferWithByteBuffer(
|
| + env, base::android::JavaParamRef<jobject>(env, jbuffer.Release()),
|
| + data, pos, limit));
|
| + buffers.push_back(write_buffer);
|
| + }
|
| context_->PostTaskToNetworkThread(
|
| FROM_HERE,
|
| - base::Bind(&CronetBidirectionalStreamAdapter::WriteDataOnNetworkThread,
|
| - base::Unretained(this), write_buffer, remaining_capacity,
|
| - jend_of_stream));
|
| + base::Bind(&CronetBidirectionalStreamAdapter::WritevDataOnNetworkThread,
|
| + base::Unretained(this), buffers, jend_of_stream));
|
| +
|
| return JNI_TRUE;
|
| }
|
|
|
| void CronetBidirectionalStreamAdapter::Destroy(
|
| JNIEnv* env,
|
| - const JavaParamRef<jobject>& jcaller,
|
| + const base::android::JavaParamRef<jobject>& jcaller,
|
| jboolean jsend_on_canceled) {
|
| // Destroy could be called from any thread, including network thread (if
|
| // posting task to executor throws an exception), but is posted, so |this|
|
| @@ -167,11 +205,10 @@ void CronetBidirectionalStreamAdapter::Destroy(
|
| base::Unretained(this), jsend_on_canceled));
|
| }
|
|
|
| -void CronetBidirectionalStreamAdapter::OnHeadersSent() {
|
| +void CronetBidirectionalStreamAdapter::OnStreamReady() {
|
| DCHECK(context_->IsOnNetworkThread());
|
| JNIEnv* env = base::android::AttachCurrentThread();
|
| - cronet::Java_CronetBidirectionalStream_onRequestHeadersSent(env,
|
| - owner_.obj());
|
| + cronet::Java_CronetBidirectionalStream_onStreamReady(env, owner_.obj());
|
| }
|
|
|
| void CronetBidirectionalStreamAdapter::OnHeadersReceived(
|
| @@ -217,13 +254,34 @@ void CronetBidirectionalStreamAdapter::OnDataRead(int bytes_read) {
|
|
|
| void CronetBidirectionalStreamAdapter::OnDataSent() {
|
| DCHECK(context_->IsOnNetworkThread());
|
| + DCHECK(!write_buffer_list_.empty());
|
| +
|
| JNIEnv* env = base::android::AttachCurrentThread();
|
| - cronet::Java_CronetBidirectionalStream_onWriteCompleted(
|
| - env, owner_.obj(), write_buffer_->byte_buffer(),
|
| - write_buffer_->initial_position(), write_buffer_->initial_limit());
|
| - // Free the write buffer. This lets the Java ByteBuffer be freed, if the
|
| + base::android::ScopedJavaLocalRef<jclass> byte_buffer_clazz(
|
| + env, env->GetObjectClass(write_buffer_list_[0]->byte_buffer()));
|
| + size_t size = write_buffer_list_.size();
|
| + jobjectArray jbuffers =
|
| + env->NewObjectArray(size, byte_buffer_clazz.obj(), NULL);
|
| + base::android::CheckException(env);
|
| + std::vector<int> initial_positions;
|
| + std::vector<int> initial_limits;
|
| + for (size_t i = 0; i < size; ++i) {
|
| + env->SetObjectArrayElement(jbuffers, i,
|
| + write_buffer_list_[i]->byte_buffer());
|
| + initial_positions.push_back(write_buffer_list_[i]->initial_position());
|
| + initial_limits.push_back(write_buffer_list_[i]->initial_limit());
|
| + }
|
| + ScopedJavaLocalRef<jintArray> jinitial_positions =
|
| + base::android::ToJavaIntArray(env, initial_positions);
|
| + ScopedJavaLocalRef<jintArray> jinitial_limits =
|
| + base::android::ToJavaIntArray(env, initial_limits);
|
| + // Call into Java.
|
| + cronet::Java_CronetBidirectionalStream_onWritevCompleted(
|
| + env, owner_.obj(), jbuffers, jinitial_positions.obj(),
|
| + jinitial_limits.obj(), write_end_of_stream_);
|
| + // Free the write buffers. This lets the Java ByteBuffer be freed, if the
|
| // embedder releases it, too.
|
| - write_buffer_ = nullptr;
|
| + write_buffer_list_.clear();
|
| }
|
|
|
| void CronetBidirectionalStreamAdapter::OnTrailersReceived(
|
| @@ -256,7 +314,7 @@ void CronetBidirectionalStreamAdapter::StartOnNetworkThread(
|
| std::move(request_info), context_->GetURLRequestContext()
|
| ->http_transaction_factory()
|
| ->GetSession(),
|
| - this));
|
| + disable_auto_flush_, this));
|
| }
|
|
|
| void CronetBidirectionalStreamAdapter::ReadDataOnNetworkThread(
|
| @@ -280,13 +338,13 @@ void CronetBidirectionalStreamAdapter::ReadDataOnNetworkThread(
|
| OnDataRead(bytes_read);
|
| }
|
|
|
| -void CronetBidirectionalStreamAdapter::WriteDataOnNetworkThread(
|
| - scoped_refptr<IOBufferWithByteBuffer> write_buffer,
|
| - int buffer_size,
|
| +void CronetBidirectionalStreamAdapter::WritevDataOnNetworkThread(
|
| + const IOByteBufferList& write_buffer_list,
|
| bool end_of_stream) {
|
| DCHECK(context_->IsOnNetworkThread());
|
| - DCHECK(write_buffer);
|
| - DCHECK(!write_buffer_);
|
| + DCHECK(write_buffer_list_.empty());
|
| + DCHECK(!write_buffer_list.empty());
|
| + DCHECK(!write_end_of_stream_);
|
|
|
| if (stream_failed_) {
|
| // If stream failed between the time when WriteData is invoked and
|
| @@ -296,8 +354,20 @@ void CronetBidirectionalStreamAdapter::WriteDataOnNetworkThread(
|
| // set to true.
|
| return;
|
| }
|
| - write_buffer_ = write_buffer;
|
| - bidi_stream_->SendData(write_buffer_.get(), buffer_size, end_of_stream);
|
| +
|
| + write_end_of_stream_ = end_of_stream;
|
| + std::vector<net::IOBuffer*> buffers;
|
| + std::vector<int> lengths;
|
| + for (const auto& buffer : write_buffer_list) {
|
| + write_buffer_list_.push_back(buffer);
|
| + buffers.push_back(buffer.get());
|
| + lengths.push_back(buffer->initial_limit() - buffer->initial_position());
|
| + }
|
| + if (buffers.size() == 1) {
|
| + bidi_stream_->SendData(buffers[0], lengths[0], end_of_stream);
|
| + } else {
|
| + bidi_stream_->SendvData(buffers, lengths, end_of_stream);
|
| + }
|
| }
|
|
|
| void CronetBidirectionalStreamAdapter::DestroyOnNetworkThread(
|
|
|