Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.net.impl; | 5 package org.chromium.net.impl; |
| 6 | 6 |
| 7 import org.chromium.base.Log; | 7 import org.chromium.base.Log; |
| 8 import org.chromium.base.VisibleForTesting; | 8 import org.chromium.base.VisibleForTesting; |
| 9 import org.chromium.base.annotations.CalledByNative; | 9 import org.chromium.base.annotations.CalledByNative; |
| 10 import org.chromium.base.annotations.JNINamespace; | 10 import org.chromium.base.annotations.JNINamespace; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 75 WRITING_DONE, | 75 WRITING_DONE, |
| 76 } | 76 } |
| 77 | 77 |
| 78 private final CronetUrlRequestContext mRequestContext; | 78 private final CronetUrlRequestContext mRequestContext; |
| 79 private final Executor mExecutor; | 79 private final Executor mExecutor; |
| 80 private final Callback mCallback; | 80 private final Callback mCallback; |
| 81 private final String mInitialUrl; | 81 private final String mInitialUrl; |
| 82 private final int mInitialPriority; | 82 private final int mInitialPriority; |
| 83 private final String mInitialMethod; | 83 private final String mInitialMethod; |
| 84 private final String mRequestHeaders[]; | 84 private final String mRequestHeaders[]; |
| 85 private final boolean mDisableAutoFlush; | |
| 86 private final boolean mDelayRequestHeadersUntilFirstFlush; | 85 private final boolean mDelayRequestHeadersUntilFirstFlush; |
| 87 | 86 |
| 88 /* | 87 /* |
| 89 * Synchronizes access to mNativeStream, mReadState and mWriteState. | 88 * Synchronizes access to mNativeStream, mReadState and mWriteState. |
| 90 */ | 89 */ |
| 91 private final Object mNativeStreamLock = new Object(); | 90 private final Object mNativeStreamLock = new Object(); |
| 92 | 91 |
| 93 @GuardedBy("mNativeStreamLock") | 92 @GuardedBy("mNativeStreamLock") |
| 94 // Pending write data. | 93 // Pending write data. |
| 95 private LinkedList<ByteBuffer> mPendingData; | 94 private LinkedList<ByteBuffer> mPendingData; |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 212 } | 211 } |
| 213 } catch (Exception e) { | 212 } catch (Exception e) { |
| 214 onCallbackException(e); | 213 onCallbackException(e); |
| 215 } | 214 } |
| 216 } | 215 } |
| 217 } | 216 } |
| 218 | 217 |
| 219 CronetBidirectionalStream(CronetUrlRequestContext requestContext, String url , | 218 CronetBidirectionalStream(CronetUrlRequestContext requestContext, String url , |
| 220 @BidirectionalStream.Builder.StreamPriority int priority, Callback c allback, | 219 @BidirectionalStream.Builder.StreamPriority int priority, Callback c allback, |
| 221 Executor executor, String httpMethod, List<Map.Entry<String, String> > requestHeaders, | 220 Executor executor, String httpMethod, List<Map.Entry<String, String> > requestHeaders, |
| 222 boolean disableAutoFlush, boolean delayRequestHeadersUntilNextFlush) { | 221 boolean delayRequestHeadersUntilNextFlush) { |
| 223 mRequestContext = requestContext; | 222 mRequestContext = requestContext; |
| 224 mInitialUrl = url; | 223 mInitialUrl = url; |
| 225 mInitialPriority = convertStreamPriority(priority); | 224 mInitialPriority = convertStreamPriority(priority); |
| 226 mCallback = callback; | 225 mCallback = callback; |
| 227 mExecutor = executor; | 226 mExecutor = executor; |
| 228 mInitialMethod = httpMethod; | 227 mInitialMethod = httpMethod; |
| 229 mRequestHeaders = stringsFromHeaderList(requestHeaders); | 228 mRequestHeaders = stringsFromHeaderList(requestHeaders); |
| 230 mDisableAutoFlush = disableAutoFlush; | |
| 231 mDelayRequestHeadersUntilFirstFlush = delayRequestHeadersUntilNextFlush; | 229 mDelayRequestHeadersUntilFirstFlush = delayRequestHeadersUntilNextFlush; |
| 232 mPendingData = new LinkedList<>(); | 230 mPendingData = new LinkedList<>(); |
| 233 mFlushData = new LinkedList<>(); | 231 mFlushData = new LinkedList<>(); |
| 234 } | 232 } |
| 235 | 233 |
| 236 @Override | 234 @Override |
| 237 public void start() { | 235 public void start() { |
| 238 synchronized (mNativeStreamLock) { | 236 synchronized (mNativeStreamLock) { |
| 239 if (mReadState != State.NOT_STARTED) { | 237 if (mReadState != State.NOT_STARTED) { |
| 240 throw new IllegalStateException("Stream is already started."); | 238 throw new IllegalStateException("Stream is already started."); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 299 if (mEndOfStreamWritten) { | 297 if (mEndOfStreamWritten) { |
| 300 throw new IllegalArgumentException("Write after writing end of s tream."); | 298 throw new IllegalArgumentException("Write after writing end of s tream."); |
| 301 } | 299 } |
| 302 if (isDoneLocked()) { | 300 if (isDoneLocked()) { |
| 303 return; | 301 return; |
| 304 } | 302 } |
| 305 mPendingData.add(buffer); | 303 mPendingData.add(buffer); |
| 306 if (endOfStream) { | 304 if (endOfStream) { |
| 307 mEndOfStreamWritten = true; | 305 mEndOfStreamWritten = true; |
| 308 } | 306 } |
| 309 if (!mDisableAutoFlush) { | |
| 310 flushLocked(); | |
| 311 } | |
| 312 } | 307 } |
| 313 } | 308 } |
| 314 | 309 |
| 315 @Override | 310 @Override |
| 316 public void flush() { | 311 public void flush() { |
| 317 synchronized (mNativeStreamLock) { | 312 synchronized (mNativeStreamLock) { |
| 318 flushLocked(); | 313 if (isDoneLocked() |
| 314 || (mWriteState != State.WAITING_FOR_FLUSH && mWriteState != State.WRITING)) { | |
| 315 return; | |
| 316 } | |
| 317 if (mPendingData.isEmpty() && mFlushData.isEmpty()) { | |
| 318 // If there is no pending write when flush() is called, see if | |
| 319 // request headers need to be flushed. | |
| 320 if (!mRequestHeadersSent) { | |
| 321 mRequestHeadersSent = true; | |
| 322 nativeSendRequestHeaders(mNativeStream); | |
| 323 if (!doesMethodAllowWriteData(mInitialMethod)) { | |
| 324 mWriteState = State.WRITING_DONE; | |
| 325 } | |
| 326 } | |
| 327 return; | |
| 328 } | |
| 329 | |
| 330 assert !mPendingData.isEmpty() || !mFlushData.isEmpty(); | |
| 331 | |
| 332 // Move buffers from mPendingData to the flushing queue. | |
| 333 if (!mPendingData.isEmpty()) { | |
| 334 mFlushData.addAll(mPendingData); | |
| 335 mPendingData.clear(); | |
| 336 } | |
| 337 | |
| 338 if (mWriteState == State.WRITING) { | |
| 339 // If there is a write already pending, wait until onWritevCompl eted is | |
| 340 // called before pushing data to the native stack. | |
| 341 return; | |
| 342 } | |
| 343 sendFlushDataLocked(); | |
| 319 } | 344 } |
| 320 } | 345 } |
| 321 | 346 |
| 322 @SuppressWarnings("GuardedByChecker") | |
| 323 private void flushLocked() { | |
| 324 if (isDoneLocked() | |
| 325 || (mWriteState != State.WAITING_FOR_FLUSH && mWriteState != Sta te.WRITING)) { | |
| 326 return; | |
| 327 } | |
| 328 if (mPendingData.isEmpty() && mFlushData.isEmpty()) { | |
| 329 // If there is no pending write when flush() is called, see if | |
| 330 // request headers need to be flushed. | |
| 331 if (!mRequestHeadersSent) { | |
| 332 mRequestHeadersSent = true; | |
| 333 nativeSendRequestHeaders(mNativeStream); | |
| 334 if (!doesMethodAllowWriteData(mInitialMethod)) { | |
| 335 mWriteState = State.WRITING_DONE; | |
| 336 } | |
| 337 } | |
| 338 return; | |
| 339 } | |
| 340 | |
| 341 assert !mPendingData.isEmpty() || !mFlushData.isEmpty(); | |
| 342 | |
| 343 // Move buffers from mPendingData to the flushing queue. | |
| 344 if (!mPendingData.isEmpty()) { | |
| 345 mFlushData.addAll(mPendingData); | |
| 346 mPendingData.clear(); | |
| 347 } | |
| 348 | |
| 349 if (mWriteState == State.WRITING) { | |
| 350 // If there is a write already pending, wait until onWritevCompleted is | |
| 351 // called before pushing data to the native stack. | |
| 352 return; | |
| 353 } | |
| 354 sendFlushDataLocked(); | |
| 355 } | |
| 356 | |
| 357 // Helper method to send buffers in mFlushData. Caller needs to acquire | 347 // Helper method to send buffers in mFlushData. Caller needs to acquire |
| 358 // mNativeStreamLock and make sure mWriteState is WAITING_FOR_FLUSH and | 348 // mNativeStreamLock and make sure mWriteState is WAITING_FOR_FLUSH and |
| 359 // mFlushData queue isn't empty. | 349 // mFlushData queue isn't empty. |
| 360 @SuppressWarnings("GuardedByChecker") | 350 @SuppressWarnings("GuardedByChecker") |
| 361 private void sendFlushDataLocked() { | 351 private void sendFlushDataLocked() { |
| 362 assert mWriteState == State.WAITING_FOR_FLUSH; | 352 assert mWriteState == State.WAITING_FOR_FLUSH; |
| 363 int size = mFlushData.size(); | 353 int size = mFlushData.size(); |
| 364 ByteBuffer[] buffers = new ByteBuffer[size]; | 354 ByteBuffer[] buffers = new ByteBuffer[size]; |
| 365 int[] positions = new int[size]; | 355 int[] positions = new int[size]; |
| 366 int[] limits = new int[size]; | 356 int[] limits = new int[size]; |
| 367 for (int i = 0; i < size; i++) { | 357 for (int i = 0; i < size; i++) { |
| 368 ByteBuffer buffer = mFlushData.poll(); | 358 ByteBuffer buffer = mFlushData.poll(); |
| 369 buffers[i] = buffer; | 359 buffers[i] = buffer; |
| 370 positions[i] = buffer.position(); | 360 positions[i] = buffer.position(); |
| 371 limits[i] = buffer.limit(); | 361 limits[i] = buffer.limit(); |
| 372 } | 362 } |
| 373 assert mFlushData.isEmpty(); | 363 assert mFlushData.isEmpty(); |
| 374 assert buffers.length >= 1; | 364 assert buffers.length >= 1; |
| 375 mWriteState = State.WRITING; | 365 mWriteState = State.WRITING; |
| 366 mRequestHeadersSent = true; | |
|
kapishnikov
2016/08/17 22:30:56
Nice catch!
| |
| 376 if (!nativeWritevData(mNativeStream, buffers, positions, limits, | 367 if (!nativeWritevData(mNativeStream, buffers, positions, limits, |
| 377 mEndOfStreamWritten && mPendingData.isEmpty())) { | 368 mEndOfStreamWritten && mPendingData.isEmpty())) { |
| 378 // Still waiting on flush. This is just to have consistent | 369 // Still waiting on flush. This is just to have consistent |
| 379 // behavior with the other error cases. | 370 // behavior with the other error cases. |
| 380 mWriteState = State.WAITING_FOR_FLUSH; | 371 mWriteState = State.WAITING_FOR_FLUSH; |
| 381 throw new IllegalArgumentException("Unable to call native writev."); | 372 throw new IllegalArgumentException("Unable to call native writev."); |
| 382 } | 373 } |
| 383 } | 374 } |
| 384 | 375 |
| 385 /** | 376 /** |
| (...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 766 private native boolean nativeReadData( | 757 private native boolean nativeReadData( |
| 767 long nativePtr, ByteBuffer byteBuffer, int position, int limit); | 758 long nativePtr, ByteBuffer byteBuffer, int position, int limit); |
| 768 | 759 |
| 769 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter") | 760 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter") |
| 770 private native boolean nativeWritevData(long nativePtr, ByteBuffer[] buffers , int[] positions, | 761 private native boolean nativeWritevData(long nativePtr, ByteBuffer[] buffers , int[] positions, |
| 771 int[] limits, boolean endOfStream); | 762 int[] limits, boolean endOfStream); |
| 772 | 763 |
| 773 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter") | 764 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter") |
| 774 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled); | 765 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled); |
| 775 } | 766 } |
| OLD | NEW |