Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 package org.chromium.net; | |
| 6 | |
| 7 import android.util.Log; | |
|
xunjieli
2015/12/18 19:11:12
we should avoid using android.util.Log in new file
mef
2015/12/29 20:36:54
Done.
| |
| 8 | |
| 9 import org.chromium.base.VisibleForTesting; | |
| 10 import org.chromium.base.annotations.CalledByNative; | |
| 11 import org.chromium.base.annotations.JNINamespace; | |
| 12 import org.chromium.base.annotations.NativeClassQualifiedName; | |
| 13 | |
| 14 import java.nio.ByteBuffer; | |
| 15 import java.util.AbstractMap; | |
| 16 import java.util.ArrayList; | |
| 17 import java.util.List; | |
| 18 import java.util.Map; | |
| 19 import java.util.concurrent.Executor; | |
| 20 import java.util.concurrent.RejectedExecutionException; | |
| 21 | |
| 22 import javax.annotation.concurrent.GuardedBy; | |
| 23 | |
| 24 /** | |
| 25 * BidirectionalStream implementation using Chromium network stack. | |
| 26 * All @CallByNative methods are called on native network thread | |
| 27 * and post tasks with callback calls onto Executor. Upon return from callback n ative | |
| 28 * stream is called on executor thread and posts native tasks to native network thread. | |
| 29 */ | |
| 30 @JNINamespace("cronet") | |
| 31 class CronetBidirectionalStream extends BidirectionalStream { | |
| 32 /** | |
| 33 * States of BidirectionalStream are tracked in mStreamState and mWriteState . | |
| 34 * The write state is separated out as it changes independently of the strea m state. | |
| 35 * There is one initial state - STATE_NOT_STARTED. There is one final state - STATE_SUCCESS, | |
| 36 * reached after STATE_READING_DONE and STATE_WRITING_DONE. There are 2 exce ption states - | |
| 37 * STATE_CANCELED and STATE_ERROR, which can be reached from any other state except | |
| 38 * STATE_SUCCESS. | |
| 39 */ | |
| 40 /* Initial state, stream not started. */ | |
| 41 private static final int STATE_NOT_STARTED = 0; | |
| 42 /* Stream started, request headers are sent. */ | |
| 43 private static final int STATE_STARTED = 1; | |
| 44 /* Waiting for {@code read()} to be called. */ | |
| 45 private static final int STATE_WAITING_ON_READ = 2; | |
| 46 /* Reading from the remote, {@code onReadCompleted()} callback will be calle d when done. */ | |
| 47 private static final int STATE_READING = 3; | |
| 48 /* There is no more data to read and stream is half-closed by the remote sid e. */ | |
| 49 private static final int STATE_READING_DONE = 4; | |
| 50 /* Stream is canceled. */ | |
| 51 private static final int STATE_CANCELED = 5; | |
| 52 /* Error has occured, stream is closed. */ | |
| 53 private static final int STATE_ERROR = 6; | |
| 54 /* Reading and writing is done, and the stream is closed successfully. */ | |
| 55 private static final int STATE_SUCCESS = 7; | |
| 56 /* Waiting for {@code write()} to be called. */ | |
| 57 private static final int STATE_WAITING_ON_WRITE = 10; | |
| 58 /* Writing to the remote, {@code onWriteCompleted()} callback will be called when done. */ | |
| 59 private static final int STATE_WRITING = 11; | |
| 60 /* Writing the last frame, so {@code STATE_WRITING_DONE} will be set upon co mpletion. */ | |
| 61 private static final int STATE_WRITING_END_OF_STREAM = 12; | |
| 62 /* There is no more data to write and stream is half-closed by the local sid e. */ | |
| 63 private static final int STATE_WRITING_DONE = 13; | |
| 64 | |
| 65 /* | |
| 66 * Synchronize access to mNativeStream, mStreamState and mWriteState. | |
| 67 */ | |
| 68 private final Object mNativeStreamLock = new Object(); | |
| 69 private final CronetUrlRequestContext mRequestContext; | |
| 70 private final Executor mExecutor; | |
| 71 private final Callback mCallback; | |
| 72 private final String mInitialUrl; | |
| 73 private final String mInitialMethod; | |
| 74 private final ArrayList<Map.Entry<String, String>> mRequestHeaders; | |
| 75 | |
| 76 /* Native BidirectionalStream object, owned by CronetBidirectionalStream. */ | |
| 77 @GuardedBy("mNativeStreamLock") private long mNativeStream; | |
| 78 | |
| 79 /** | |
| 80 * Stream state is tracking stream and reading flow. | |
| 81 * NOT_STARTED -> STARTED -> WAITING_ON_READ -> READING -> WAITING_ON_READ - > | |
| 82 * READING -> READING_DONE -> SUCCESS | |
| 83 */ | |
| 84 @GuardedBy("mNativeStreamLock") private int mStreamState = STATE_NOT_STARTED ; | |
|
xunjieli
2015/12/18 19:49:25
nit: mReadState?
mef
2015/12/29 20:36:54
I'm not sure. This is both read and common stream
| |
| 85 | |
| 86 /** | |
| 87 * Write state is tracking writing flow. | |
| 88 * NOT_STARTED -> WAITING_ON_WRITE -> WRITING -> WAITING_ON_WRITE -> | |
| 89 * WRITING_END_OF_STREAM -> WRITING_DONE -> SUCCESS | |
| 90 */ | |
| 91 @GuardedBy("mNativeStreamLock") private int mWriteState = STATE_NOT_STARTED; | |
| 92 | |
| 93 private UrlResponseInfo mResponseInfo; | |
| 94 | |
| 95 /* | |
| 96 * OnReadCompleted callback is repeatedly invoked when each read is complete d, so it | |
| 97 * is cached as a member variable. | |
| 98 */ | |
| 99 private OnReadCompletedRunnable mOnReadCompletedTask; | |
| 100 | |
| 101 /* | |
| 102 * OnWriteCompleted callback is repeatedly invoked when each write is comple ted, so it | |
| 103 * is cached as a member variable. | |
| 104 */ | |
| 105 private OnWriteCompletedRunnable mOnWriteCompletedTask; | |
| 106 | |
| 107 private Runnable mOnDestroyedCallbackForTests; | |
|
xunjieli
2015/12/18 19:11:11
nit: maybe using ForTesting. I searched in cronet/
mef
2015/12/29 20:36:54
Done.
| |
| 108 | |
| 109 private final class OnReadCompletedRunnable implements Runnable { | |
| 110 ByteBuffer mByteBuffer; | |
| 111 boolean mEndOfStream; | |
|
xunjieli
2015/12/18 19:11:12
nit: need documentation on these two fields.
mef
2015/12/29 20:36:54
Done.
| |
| 112 | |
| 113 @Override | |
| 114 public void run() { | |
| 115 if (isDone()) { | |
| 116 return; | |
| 117 } | |
| 118 try { | |
| 119 synchronized (mNativeStreamLock) { | |
| 120 if (mNativeStream == 0) { | |
| 121 return; | |
| 122 } | |
| 123 if (mEndOfStream) { | |
| 124 mStreamState = STATE_READING_DONE; | |
| 125 if (maybeSucceeded()) return; | |
| 126 } else { | |
| 127 mStreamState = STATE_WAITING_ON_READ; | |
| 128 } | |
| 129 } | |
| 130 // Null out mByteBuffer, out of paranoia. Has to be done before | |
| 131 // mCallback call, to avoid any race when there are multiple | |
| 132 // executor threads. | |
| 133 ByteBuffer buffer = mByteBuffer; | |
| 134 mByteBuffer = null; | |
| 135 mCallback.onReadCompleted(CronetBidirectionalStream.this, mRespo nseInfo, buffer); | |
| 136 } catch (Exception e) { | |
| 137 onCallbackException(e); | |
| 138 } | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 private final class OnWriteCompletedRunnable implements Runnable { | |
| 143 ByteBuffer mByteBuffer; | |
|
xunjieli
2015/12/18 19:11:12
nit: need doc on this mByteBuffer field.
mef
2015/12/29 20:36:54
Done.
| |
| 144 | |
| 145 @Override | |
| 146 public void run() { | |
| 147 if (isDone()) { | |
| 148 return; | |
| 149 } | |
| 150 try { | |
| 151 synchronized (mNativeStreamLock) { | |
| 152 if (mNativeStream == 0) { | |
| 153 return; | |
| 154 } | |
| 155 if (mWriteState == STATE_WRITING_END_OF_STREAM) { | |
| 156 mWriteState = STATE_WRITING_DONE; | |
| 157 if (maybeSucceeded()) return; | |
| 158 } else { | |
| 159 mWriteState = STATE_WAITING_ON_WRITE; | |
| 160 } | |
| 161 } | |
| 162 // Null out mByteBuffer, out of paranoia. Has to be done before | |
| 163 // mCallback call, to avoid any race when there are multiple | |
| 164 // executor threads. | |
| 165 ByteBuffer buffer = mByteBuffer; | |
| 166 mByteBuffer = null; | |
| 167 mCallback.onWriteCompleted(CronetBidirectionalStream.this, mResp onseInfo, buffer); | |
| 168 } catch (Exception e) { | |
| 169 onCallbackException(e); | |
| 170 } | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 @GuardedBy("nativeStreamLock") | |
| 175 private boolean maybeSucceeded() { | |
| 176 if (mStreamState != STATE_READING_DONE || mWriteState != STATE_WRITING_D ONE) { | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 mStreamState = STATE_SUCCESS; | |
| 181 Runnable task = new Runnable() { | |
| 182 public void run() { | |
| 183 synchronized (mNativeStreamLock) { | |
| 184 if (isDone()) { | |
| 185 return; | |
| 186 } | |
| 187 // Destroy native stream first, so request context could be shut | |
| 188 // down from the listener. | |
| 189 destroyNativeStream(false); | |
| 190 } | |
| 191 try { | |
| 192 mCallback.onSucceeded(CronetBidirectionalStream.this, mRespo nseInfo); | |
| 193 } catch (Exception e) { | |
| 194 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onSucce eded method", e); | |
| 195 } | |
| 196 } | |
| 197 }; | |
| 198 postTaskToExecutor(task); | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 private static boolean doesMethodAllowWriteData(String methodName) { | |
| 203 return !methodName.equals("GET") && !methodName.equals("HEAD"); | |
| 204 } | |
| 205 | |
| 206 CronetBidirectionalStream(CronetUrlRequestContext requestContext, long urlRe questContextAdapter, | |
|
xunjieli
2015/12/18 19:11:11
nit: move constructor to the top. At least before
mef
2015/12/29 20:36:54
Done.
| |
| 207 String url, Callback callback, Executor executor, String httpMethod, | |
| 208 List<Map.Entry<String, String>> requestHeaders) { | |
| 209 mRequestContext = requestContext; | |
| 210 mInitialUrl = url; | |
| 211 mCallback = callback; | |
| 212 mExecutor = executor; | |
| 213 mInitialMethod = httpMethod; | |
| 214 mRequestHeaders = new ArrayList<Map.Entry<String, String>>(requestHeader s); | |
|
xunjieli
2015/12/18 19:11:11
Is this a deep copy?
mef
2015/12/29 20:36:54
It constructs a list containing elements of passed
| |
| 215 } | |
| 216 | |
| 217 @Override | |
| 218 public void start() { | |
| 219 synchronized (mNativeStreamLock) { | |
| 220 if (mStreamState != STATE_NOT_STARTED) { | |
| 221 throw new IllegalStateException("Stream is already started."); | |
| 222 } | |
| 223 try { | |
| 224 mNativeStream = nativeCreateBidirectionalStream( | |
| 225 mRequestContext.getUrlRequestContextAdapter()); | |
| 226 mRequestContext.onRequestStarted(); | |
| 227 String headers[] = stringsFromHeaderList(mRequestHeaders); | |
| 228 // Non-zero startResult means an argument error. | |
| 229 int startResult = nativeStart(mNativeStream, mInitialUrl, mIniti alMethod, headers, | |
| 230 !doesMethodAllowWriteData(mInitialMethod)); | |
| 231 if (startResult == -1) { | |
| 232 throw new IllegalArgumentException("Invalid http method " + mInitialMethod); | |
| 233 } | |
| 234 if (startResult > 0) { | |
| 235 int headerPos = startResult - 1; | |
| 236 throw new IllegalArgumentException( | |
| 237 "Invalid header " + headers[headerPos] + "=" + heade rs[headerPos + 1]); | |
| 238 } | |
| 239 mStreamState = STATE_STARTED; | |
| 240 } catch (RuntimeException e) { | |
| 241 // If there's an exception, cleanup and then throw the | |
| 242 // exception to the caller. | |
| 243 destroyNativeStream(false); | |
| 244 throw e; | |
| 245 } | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 @Override | |
| 250 public void read(ByteBuffer buffer) { | |
| 251 synchronized (mNativeStreamLock) { | |
| 252 if (!buffer.hasRemaining()) { | |
| 253 throw new IllegalArgumentException("ByteBuffer is already full." ); | |
| 254 } | |
| 255 if (mStreamState != STATE_WAITING_ON_READ) { | |
| 256 throw new IllegalStateException("Unexpected read attempt."); | |
| 257 } | |
| 258 if (isDone()) { | |
| 259 return; | |
| 260 } | |
| 261 mStreamState = STATE_READING; | |
| 262 if (!nativeReadData(mNativeStream, buffer, buffer.position(), buffer .limit())) { | |
| 263 // Still waiting on read. This is just to have consistent | |
| 264 // behavior with the other error cases. | |
| 265 mStreamState = STATE_WAITING_ON_READ; | |
| 266 // Since accessing byteBuffer's memory failed, it's presumably | |
| 267 // not a direct ByteBuffer. | |
| 268 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer."); | |
| 269 } | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 @Override | |
| 274 public void write(ByteBuffer buffer, boolean endOfStream) { | |
| 275 synchronized (mNativeStreamLock) { | |
| 276 if (!buffer.hasRemaining() && !endOfStream) { | |
| 277 throw new IllegalArgumentException("Empty buffer before end of s tream."); | |
| 278 } | |
| 279 if (mWriteState != STATE_WAITING_ON_WRITE) { | |
| 280 throw new IllegalStateException("Unexpected write attempt."); | |
| 281 } | |
| 282 if (isDone()) { | |
| 283 return; | |
| 284 } | |
| 285 mWriteState = endOfStream ? STATE_WRITING_END_OF_STREAM : STATE_WRIT ING; | |
| 286 if (!nativeWriteData( | |
| 287 mNativeStream, buffer, buffer.position(), buffer.limit() , endOfStream)) { | |
| 288 // Still waiting on write. This is just to have consistent | |
| 289 // behavior with the other error cases. | |
| 290 mWriteState = STATE_WAITING_ON_WRITE; | |
| 291 // Since accessing byteBuffer's memory failed, it's presumably | |
| 292 // not a direct ByteBuffer. | |
| 293 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer."); | |
| 294 } | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 @Override | |
| 299 public void ping(PingCallback callback, Executor executor) { | |
| 300 // TODO(mef): May be last thing to be implemented on Android. | |
|
xunjieli
2015/12/18 19:11:11
nit: throw UnsupportedOperation with a not yet sup
mef
2015/12/29 20:36:55
Done.
| |
| 301 } | |
| 302 | |
| 303 @Override | |
| 304 public void windowUpdate(int windowSizeIncrement) { | |
| 305 // TODO(mef): Understand the needs and semantics of this method. | |
| 306 } | |
| 307 | |
| 308 /** | |
| 309 * Cancels the stream. Can be called at any time after {@link #start}. | |
| 310 * {@link Callback#onCanceled} will be invoked when cancelation | |
| 311 * is complete and no further callback methods will be invoked. If the | |
| 312 * stream has completed or has not started, calling {@code cancel()} has no | |
| 313 * effect and {@code onCanceled} will not be invoked. If the | |
| 314 * {@link Executor} passed in during {@code BidirectionalStream} constructio n runs | |
| 315 * tasks on a single thread, and {@code cancel()} is called on that thread, | |
| 316 * no listener methods (besides {@code onCanceled()}) will be invoked after | |
| 317 * {@code cancel()} is called. Otherwise, at most one callback method may be | |
| 318 * invoked after {@code cancel()} has completed. | |
| 319 */ | |
| 320 public void cancel() { | |
|
xunjieli
2015/12/18 19:11:11
nit: add @override, and remove documentation.
mef
2015/12/29 20:36:54
Done.
| |
| 321 synchronized (mNativeStreamLock) { | |
| 322 if (isDone() || mStreamState == STATE_NOT_STARTED) { | |
| 323 return; | |
| 324 } | |
| 325 mStreamState = STATE_CANCELED; | |
| 326 destroyNativeStream(true); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 @Override | |
| 331 public boolean isDone() { | |
| 332 synchronized (mNativeStreamLock) { | |
| 333 return mStreamState != STATE_NOT_STARTED && mNativeStream == 0; | |
| 334 } | |
| 335 } | |
| 336 | |
| 337 @SuppressWarnings("unused") | |
| 338 @CalledByNative | |
| 339 private void onRequestHeadersSent() { | |
| 340 Runnable task = new Runnable() { | |
| 341 public void run() { | |
| 342 synchronized (mNativeStreamLock) { | |
| 343 if (isDone()) { | |
| 344 return; | |
| 345 } | |
| 346 if (doesMethodAllowWriteData(mInitialMethod)) { | |
| 347 mWriteState = STATE_WAITING_ON_WRITE; | |
| 348 } else { | |
| 349 mWriteState = STATE_WRITING_DONE; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 try { | |
| 354 mCallback.onRequestHeadersSent(CronetBidirectionalStream.thi s); | |
| 355 } catch (Exception e) { | |
| 356 onCallbackException(e); | |
| 357 } | |
| 358 } | |
| 359 }; | |
| 360 postTaskToExecutor(task); | |
| 361 } | |
| 362 | |
| 363 /** | |
| 364 * Called when the final set of headers, after all redirects, | |
| 365 * is received. Can only be called once for each stream. | |
| 366 */ | |
| 367 @SuppressWarnings("unused") | |
| 368 @CalledByNative | |
| 369 private void onResponseHeadersReceived(int httpStatusCode, String[] headers) { | |
| 370 mResponseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode, heade rs); | |
| 371 Runnable task = new Runnable() { | |
| 372 public void run() { | |
| 373 synchronized (mNativeStreamLock) { | |
| 374 if (isDone()) { | |
| 375 return; | |
| 376 } | |
| 377 mStreamState = STATE_WAITING_ON_READ; | |
| 378 } | |
| 379 | |
| 380 try { | |
| 381 mCallback.onResponseHeadersReceived( | |
| 382 CronetBidirectionalStream.this, mResponseInfo); | |
| 383 } catch (Exception e) { | |
| 384 onCallbackException(e); | |
| 385 } | |
| 386 } | |
| 387 }; | |
| 388 postTaskToExecutor(task); | |
| 389 } | |
| 390 | |
| 391 @SuppressWarnings("unused") | |
| 392 @CalledByNative | |
| 393 private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition, | |
| 394 long receivedBytesCount) { | |
| 395 mResponseInfo.setReceivedBytesCount(receivedBytesCount); | |
| 396 if (byteBuffer.position() != initialPosition) { | |
| 397 failWithException( | |
| 398 new CronetException("ByteBuffer modified externally during r ead", null)); | |
| 399 return; | |
| 400 } | |
| 401 if (bytesRead < 0 || initialPosition + bytesRead > byteBuffer.limit()) { | |
| 402 failWithException(new CronetException("Invalid number of bytes read" , null)); | |
| 403 return; | |
| 404 } | |
| 405 if (mOnReadCompletedTask == null) { | |
| 406 mOnReadCompletedTask = new OnReadCompletedRunnable(); | |
| 407 } | |
| 408 byteBuffer.position(initialPosition + bytesRead); | |
| 409 mOnReadCompletedTask.mByteBuffer = byteBuffer; | |
| 410 mOnReadCompletedTask.mEndOfStream = (bytesRead == 0); | |
| 411 postTaskToExecutor(mOnReadCompletedTask); | |
| 412 } | |
| 413 | |
| 414 @SuppressWarnings("unused") | |
| 415 @CalledByNative | |
| 416 private void onWriteCompleted(final ByteBuffer byteBuffer, int initialPositi on) { | |
| 417 if (byteBuffer.position() != initialPosition) { | |
| 418 failWithException( | |
| 419 new CronetException("ByteBuffer modified externally during w rite", null)); | |
| 420 return; | |
| 421 } | |
| 422 if (mOnWriteCompletedTask == null) { | |
| 423 mOnWriteCompletedTask = new OnWriteCompletedRunnable(); | |
| 424 } | |
| 425 // Current implementation always writes the complete buffer. | |
| 426 byteBuffer.position(byteBuffer.limit()); | |
| 427 mOnWriteCompletedTask.mByteBuffer = byteBuffer; | |
| 428 postTaskToExecutor(mOnWriteCompletedTask); | |
| 429 } | |
| 430 | |
| 431 @SuppressWarnings("unused") | |
| 432 @CalledByNative | |
| 433 private void onResponseTrailersReceived(String[] trailers) { | |
| 434 final UrlResponseInfo.HeaderBlock trailersBlock = | |
| 435 new UrlResponseInfo.HeaderBlock(headersListFromStrings(trailers) ); | |
| 436 Runnable task = new Runnable() { | |
| 437 public void run() { | |
| 438 synchronized (mNativeStreamLock) { | |
| 439 if (isDone()) { | |
| 440 return; | |
| 441 } | |
| 442 } | |
| 443 try { | |
| 444 mCallback.onResponseTrailersReceived( | |
| 445 CronetBidirectionalStream.this, mResponseInfo, trail ersBlock); | |
| 446 } catch (Exception e) { | |
| 447 onCallbackException(e); | |
| 448 } | |
| 449 } | |
| 450 }; | |
| 451 postTaskToExecutor(task); | |
| 452 } | |
| 453 | |
| 454 @SuppressWarnings("unused") | |
| 455 @CalledByNative | |
| 456 private void onError(final int nativeError, final String errorString, long r eceivedBytesCount) { | |
| 457 if (mResponseInfo != null) { | |
| 458 mResponseInfo.setReceivedBytesCount(receivedBytesCount); | |
| 459 } | |
| 460 failWithException(new CronetException( | |
| 461 "Exception in BidirectionalStream: " + errorString, nativeError) ); | |
| 462 } | |
| 463 | |
| 464 /** | |
| 465 * Called when request is canceled, no callbacks will be called afterwards. | |
| 466 */ | |
| 467 @SuppressWarnings("unused") | |
| 468 @CalledByNative | |
| 469 private void onCanceled() { | |
| 470 Runnable task = new Runnable() { | |
| 471 public void run() { | |
| 472 try { | |
| 473 mCallback.onCanceled(CronetBidirectionalStream.this, mRespon seInfo); | |
| 474 } catch (Exception e) { | |
| 475 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onCance led method", e); | |
| 476 } | |
| 477 } | |
| 478 }; | |
| 479 postTaskToExecutor(task); | |
| 480 } | |
| 481 | |
| 482 @VisibleForTesting | |
| 483 public void setOnDestroyedCallbackForTests(Runnable onDestroyedCallbackForTe sts) { | |
|
xunjieli
2015/12/18 19:11:11
nit: ForTests vs ForTesting. Should we standardize
mef
2015/12/29 20:36:54
Yes, and ForTesting is more consistent with @Visib
| |
| 484 mOnDestroyedCallbackForTests = onDestroyedCallbackForTests; | |
| 485 } | |
| 486 | |
| 487 /** | |
| 488 * Posts task to application Executor. Used for callbacks | |
| 489 * and other tasks that should not be executed on network thread. | |
| 490 */ | |
| 491 private void postTaskToExecutor(Runnable task) { | |
| 492 try { | |
| 493 mExecutor.execute(task); | |
| 494 } catch (RejectedExecutionException failException) { | |
| 495 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex ecutor", | |
| 496 failException); | |
| 497 // If posting a task throws an exception, then there is no choice | |
| 498 // but to cancel the stream. | |
| 499 cancel(); | |
| 500 } | |
| 501 } | |
| 502 | |
| 503 private ArrayList<Map.Entry<String, String>> headersListFromStrings(String[] headers) { | |
|
xunjieli
2015/12/18 19:11:12
nit: private static.
mef
2015/12/29 20:36:54
Done.
| |
| 504 ArrayList<Map.Entry<String, String>> headersList = | |
| 505 new ArrayList<Map.Entry<String, String>>(); | |
| 506 for (int i = 0; i < headers.length; i += 2) { | |
| 507 headersList.add(new AbstractMap.SimpleImmutableEntry<String, String> ( | |
| 508 headers[i], headers[i + 1])); | |
| 509 } | |
| 510 return headersList; | |
| 511 } | |
| 512 | |
| 513 private String[] stringsFromHeaderList(ArrayList<Map.Entry<String, String>> headersList) { | |
|
xunjieli
2015/12/18 19:11:12
nit: private static.
mef
2015/12/29 20:36:54
Done.
| |
| 514 String headersArray[] = new String[headersList.size() * 2]; | |
| 515 int i = 0; | |
| 516 for (Map.Entry<String, String> requestHeader : headersList) { | |
| 517 headersArray[i++] = requestHeader.getKey(); | |
| 518 headersArray[i++] = requestHeader.getValue(); | |
| 519 } | |
| 520 return headersArray; | |
| 521 } | |
| 522 | |
| 523 private UrlResponseInfo prepareResponseInfoOnNetworkThread( | |
| 524 int httpStatusCode, String[] headers) { | |
| 525 long nativeStream; | |
| 526 synchronized (mNativeStreamLock) { | |
| 527 if (mNativeStream == 0) { | |
| 528 return null; | |
| 529 } | |
| 530 // This method is running on network thread, so even if | |
| 531 // mUrlRequestAdapter is set to 0 from another thread the actual | |
| 532 // deletion of the adapter is posted to network thread, so it is | |
| 533 // safe to preserve and use urlRequestAdapter outside the lock. | |
|
xunjieli
2015/12/18 19:11:11
nit: need to update comment. s/mUrlRequestAdapter/
mef
2015/12/29 20:36:54
Done.
| |
| 534 nativeStream = mNativeStream; | |
| 535 } | |
| 536 | |
| 537 ArrayList<String> urlChain = new ArrayList<String>(); | |
| 538 urlChain.add(mInitialUrl); | |
| 539 | |
| 540 boolean wasCached = false; | |
| 541 String httpStatusText = ""; | |
| 542 String negotiatedProtocol = nativeGetNegotiatedProtocol(nativeStream); | |
| 543 String proxyServer = null; | |
| 544 | |
| 545 UrlResponseInfo responseInfo = new UrlResponseInfo(urlChain, httpStatusC ode, httpStatusText, | |
| 546 headersListFromStrings(headers), wasCached, negotiatedProtocol, proxyServer); | |
| 547 return responseInfo; | |
| 548 } | |
| 549 | |
| 550 private void destroyNativeStream(boolean sendOnCanceled) { | |
| 551 synchronized (mNativeStreamLock) { | |
| 552 Log.i(CronetUrlRequestContext.LOG_TAG, "destroyNativeStream " + this .toString()); | |
| 553 if (mNativeStream == 0) { | |
| 554 return; | |
| 555 } | |
| 556 nativeDestroy(mNativeStream, sendOnCanceled); | |
| 557 mRequestContext.onRequestDestroyed(); | |
| 558 mNativeStream = 0; | |
| 559 if (mOnDestroyedCallbackForTests != null) { | |
| 560 mOnDestroyedCallbackForTests.run(); | |
| 561 } | |
| 562 } | |
| 563 } | |
| 564 | |
| 565 /** | |
| 566 * If callback method throws an exception, stream gets canceled | |
| 567 * and exception is reported via onFailed callback. | |
| 568 * Only called on the Executor. | |
| 569 */ | |
| 570 private void onCallbackException(Exception e) { | |
| 571 CronetException streamError = | |
| 572 new CronetException("CalledByNative method has thrown an excepti on", e); | |
| 573 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative meth od", e); | |
| 574 // Do not call into listener if request is complete. | |
| 575 synchronized (mNativeStreamLock) { | |
| 576 if (isDone()) { | |
| 577 return; | |
| 578 } | |
| 579 destroyNativeStream(false); | |
| 580 } | |
| 581 try { | |
| 582 mCallback.onFailed(this, mResponseInfo, streamError); | |
| 583 } catch (Exception failException) { | |
| 584 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of faile d request", | |
| 585 failException); | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 /** | |
| 590 * Fails the stream with an exception. Can be called on any thread. | |
| 591 */ | |
| 592 private void failWithException(final CronetException exception) { | |
| 593 Runnable task = new Runnable() { | |
| 594 public void run() { | |
| 595 synchronized (mNativeStreamLock) { | |
| 596 if (isDone()) { | |
| 597 return; | |
| 598 } | |
| 599 mStreamState = STATE_ERROR; | |
| 600 destroyNativeStream(false); | |
| 601 } | |
| 602 try { | |
| 603 mCallback.onFailed(CronetBidirectionalStream.this, mResponse Info, exception); | |
| 604 } catch (Exception e) { | |
| 605 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onError method", e); | |
| 606 } | |
| 607 } | |
| 608 }; | |
| 609 postTaskToExecutor(task); | |
| 610 } | |
| 611 | |
| 612 // Native methods are implemented in cronet_bidirectional_stream.cc. | |
| 613 private native long nativeCreateBidirectionalStream(long urlRequestContextAd apter); | |
| 614 | |
| 615 @NativeClassQualifiedName("CronetBidirectionalStream") | |
| 616 private native int nativeStart( | |
| 617 long nativePtr, String url, String method, String[] headers, boolean endOfStream); | |
| 618 | |
| 619 @NativeClassQualifiedName("CronetBidirectionalStream") | |
| 620 private native boolean nativeReadData( | |
| 621 long nativePtr, ByteBuffer byteBuffer, int position, int capacity); | |
| 622 | |
| 623 @NativeClassQualifiedName("CronetBidirectionalStream") | |
| 624 private native boolean nativeWriteData( | |
| 625 long nativePtr, ByteBuffer byteBuffer, int position, int capacity, b oolean endOfStream); | |
| 626 | |
| 627 @NativeClassQualifiedName("CronetBidirectionalStream") | |
| 628 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled); | |
| 629 | |
| 630 @NativeClassQualifiedName("CronetBidirectionalStream") | |
| 631 private native String nativeGetNegotiatedProtocol(long nativePtr); | |
| 632 } | |
| OLD | NEW |