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

Side by Side Diff: components/cronet/android/java/src/org/chromium/net/CronetBidirectionalStream.java

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 package org.chromium.net;
6
7 import org.chromium.base.Log;
8 import org.chromium.base.VisibleForTesting;
9 import org.chromium.base.annotations.CalledByNative;
10 import org.chromium.base.annotations.JNINamespace;
11 import org.chromium.base.annotations.NativeClassQualifiedName;
12
13 import java.nio.ByteBuffer;
14 import java.util.AbstractMap;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.concurrent.Executor;
19 import java.util.concurrent.RejectedExecutionException;
20
21 import javax.annotation.concurrent.GuardedBy;
22
23 /**
24 * BidirectionalStream implementation using Chromium network stack.
25 * All @CallByNative methods are called on native network thread
26 * and post tasks with callback calls onto Executor. Upon return from callback n ative
27 * stream is called on executor thread and posts native tasks to native network thread.
28 */
29 @JNINamespace("cronet")
30 class CronetBidirectionalStream extends BidirectionalStream {
31 /**
32 * States of BidirectionalStream are tracked in mStreamState and mWriteState .
33 * The write state is separated out as it changes independently of the strea m state.
34 * There is one initial state - STATE_NOT_STARTED. There is one final state - STATE_SUCCESS,
35 * reached after STATE_READING_DONE and STATE_WRITING_DONE. There are 2 exce ption states -
36 * STATE_CANCELED and STATE_ERROR, which can be reached from any other state except
37 * STATE_SUCCESS.
38 */
39 /* Initial state, stream not started. */
40 private static final int STATE_NOT_STARTED = 0;
pauljensen 2016/01/06 17:18:33 let's change these states to an enum (they're allo
mef 2016/01/06 21:28:30 Done.
41 /* Stream started, request headers are sent. */
42 private static final int STATE_STARTED = 1;
43 /* Waiting for {@code read()} to be called. */
44 private static final int STATE_WAITING_ON_READ = 2;
45 /* Reading from the remote, {@code onReadCompleted()} callback will be calle d when done. */
46 private static final int STATE_READING = 3;
47 /* There is no more data to read and stream is half-closed by the remote sid e. */
48 private static final int STATE_READING_DONE = 4;
49 /* Stream is canceled. */
50 private static final int STATE_CANCELED = 5;
pauljensen 2016/01/04 19:56:00 no action required side-note: this state is set bu
mef 2016/01/04 22:27:07 Good point, not sure where to check them. I think
pauljensen 2016/01/06 17:18:33 I don't think we need to take any action, or at le
51 /* Error has occured, stream is closed. */
52 private static final int STATE_ERROR = 6;
53 /* Reading and writing is done, and the stream is closed successfully. */
54 private static final int STATE_SUCCESS = 7;
55 /* Waiting for {@code write()} to be called. */
56 private static final int STATE_WAITING_ON_WRITE = 10;
57 /* Writing to the remote, {@code onWriteCompleted()} callback will be called when done. */
58 private static final int STATE_WRITING = 11;
59 /* Writing the last frame, so {@code STATE_WRITING_DONE} will be set upon co mpletion. */
60 private static final int STATE_WRITING_END_OF_STREAM = 12;
61 /* There is no more data to write and stream is half-closed by the local sid e. */
62 private static final int STATE_WRITING_DONE = 13;
63
64 /*
pauljensen 2016/01/06 17:18:33 can we move these four lines down to line 75 so th
mef 2016/01/06 21:28:30 Done.
65 * Synchronize access to mNativeStream, mStreamState and mWriteState.
66 */
67 private final Object mNativeStreamLock = new Object();
68 private final CronetUrlRequestContext mRequestContext;
69 private final Executor mExecutor;
70 private final Callback mCallback;
71 private final String mInitialUrl;
72 private final int mInitialPriority;
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 ;
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
pauljensen 2016/01/04 19:56:00 does mWriteState ever proceed to SUCCESS?
mef 2016/01/04 22:27:07 Currently it stops at WRITING_DONE. I could fix th
pauljensen 2016/01/06 17:18:33 As we discussed offline, I think for consistency a
mef 2016/01/06 21:28:30 Done. I wonder whether we need all 3 - Stream Stat
90 */
91 @GuardedBy("mNativeStreamLock") private int mWriteState = STATE_NOT_STARTED;
92
93 private UrlResponseInfo mResponseInfo;
pauljensen 2016/01/06 17:18:33 should this be marked volatile? it's written on o
mef 2016/01/06 21:28:30 mResponseInfo doesn't change after creation in onR
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 mOnDestroyedCallbackForTesting;
108
109 private final class OnReadCompletedRunnable implements Runnable {
110 // Buffer passed back from current invocation of onReadCompleted.
111 ByteBuffer mByteBuffer;
112 // End of stream flag from current invocation of onReadCompleted.
113 boolean mEndOfStream;
114
115 @Override
116 public void run() {
117 if (isDone()) {
118 return;
119 }
120 try {
121 synchronized (mNativeStreamLock) {
122 if (mNativeStream == 0) {
123 return;
124 }
125 if (mEndOfStream) {
126 mStreamState = STATE_READING_DONE;
127 if (maybeSucceeded()) return;
128 } else {
129 mStreamState = STATE_WAITING_ON_READ;
130 }
131 }
132 // Null out mByteBuffer, out of paranoia. Has to be done before
pauljensen 2016/01/04 19:56:00 nit: "out of paranoia" seems more like to facilita
mef 2016/01/04 22:27:07 Good point. The comment hasn't changed since times
mef 2016/01/06 21:28:30 Done.
133 // mCallback call, to avoid any race when there are multiple
134 // executor threads.
135 ByteBuffer buffer = mByteBuffer;
136 mByteBuffer = null;
137 mCallback.onReadCompleted(CronetBidirectionalStream.this, mRespo nseInfo, buffer);
138 } catch (Exception e) {
139 onCallbackException(e);
140 }
141 }
142 }
143
144 private final class OnWriteCompletedRunnable implements Runnable {
145 // Buffer passed back from current invocation of onWriteCompleted.
146 ByteBuffer mByteBuffer;
147
148 @Override
149 public void run() {
150 if (isDone()) {
151 return;
152 }
153 try {
154 synchronized (mNativeStreamLock) {
155 if (mNativeStream == 0) {
156 return;
157 }
158 if (mWriteState == STATE_WRITING_END_OF_STREAM) {
159 mWriteState = STATE_WRITING_DONE;
160 if (maybeSucceeded()) return;
161 } else {
162 mWriteState = STATE_WAITING_ON_WRITE;
163 }
164 }
165 // Null out mByteBuffer, out of paranoia. Has to be done before
166 // mCallback call, to avoid any race when there are multiple
167 // executor threads.
168 ByteBuffer buffer = mByteBuffer;
169 mByteBuffer = null;
170 mCallback.onWriteCompleted(CronetBidirectionalStream.this, mResp onseInfo, buffer);
171 } catch (Exception e) {
172 onCallbackException(e);
173 }
174 }
175 }
176
177 CronetBidirectionalStream(CronetUrlRequestContext requestContext, long urlRe questContextAdapter,
178 String url, @BidirectionalStream.Builder.StreamPriority int priority , Callback callback,
179 Executor executor, String httpMethod, List<Map.Entry<String, String> > requestHeaders) {
180 mRequestContext = requestContext;
181 mInitialUrl = url;
182 mInitialPriority = convertStreamPriority(priority);
183 mCallback = callback;
184 mExecutor = executor;
185 mInitialMethod = httpMethod;
186 mRequestHeaders = new ArrayList<Map.Entry<String, String>>(requestHeader s);
187 }
188
189 @GuardedBy("nativeStreamLock")
190 private boolean maybeSucceeded() {
191 if (mStreamState != STATE_READING_DONE || mWriteState != STATE_WRITING_D ONE) {
192 return false;
193 }
194
195 mStreamState = STATE_SUCCESS;
196 Runnable task = new Runnable() {
197 public void run() {
198 synchronized (mNativeStreamLock) {
199 if (isDone()) {
200 return;
201 }
202 // Destroy native stream first, so request context could be shut
203 // down from the listener.
204 destroyNativeStream(false);
205 }
206 try {
207 mCallback.onSucceeded(CronetBidirectionalStream.this, mRespo nseInfo);
208 } catch (Exception e) {
209 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onSucce eded method", e);
210 }
211 }
212 };
213 postTaskToExecutor(task);
214 return true;
215 }
216
217 private static boolean doesMethodAllowWriteData(String methodName) {
218 return !methodName.equals("GET") && !methodName.equals("HEAD");
219 }
220
221 @Override
222 public void start() {
223 synchronized (mNativeStreamLock) {
224 if (mStreamState != STATE_NOT_STARTED) {
225 throw new IllegalStateException("Stream is already started.");
226 }
227 try {
228 mNativeStream = nativeCreateBidirectionalStream(
229 mRequestContext.getUrlRequestContextAdapter());
230 mRequestContext.onRequestStarted();
231 String headers[] = stringsFromHeaderList(mRequestHeaders);
pauljensen 2016/01/04 19:56:00 no action required side-note: interesting that yo
mef 2016/01/04 22:27:07 She didn't change request headers because we didn'
232 // Non-zero startResult means an argument error.
233 int startResult = nativeStart(mNativeStream, mInitialUrl, mIniti alPriority,
234 mInitialMethod, headers, !doesMethodAllowWriteData(mInit ialMethod));
pauljensen 2016/01/06 17:18:33 Can we combine nativeStart() and nativeCreateBidir
mef 2016/01/06 21:28:30 We could, but then how do we report back faulty me
pauljensen 2016/01/07 02:56:34 Yuck. JNI is awful sometimes. All we need is a p
mef 2016/01/07 03:22:15 Acknowledged.
pauljensen 2016/01/11 20:05:45 Another idea is to throw the exception from native
mef 2016/01/11 23:22:50 Would it make sense to add a @CalledByNative metod
pauljensen 2016/01/12 16:55:41 I don't think there is any need to involve an Exec
mef 2016/01/14 21:07:54 sg, let's keep it this way and refactor both UrlRe
235 if (startResult == -1) {
236 throw new IllegalArgumentException("Invalid http method " + mInitialMethod);
237 }
238 if (startResult > 0) {
239 int headerPos = startResult - 1;
240 throw new IllegalArgumentException(
241 "Invalid header " + headers[headerPos] + "=" + heade rs[headerPos + 1]);
242 }
243 mStreamState = STATE_STARTED;
244 } catch (RuntimeException e) {
245 // If there's an exception, cleanup and then throw the
246 // exception to the caller.
247 destroyNativeStream(false);
248 throw e;
249 }
250 }
251 }
252
253 @Override
254 public void read(ByteBuffer buffer) {
255 synchronized (mNativeStreamLock) {
256 if (!buffer.hasRemaining()) {
257 throw new IllegalArgumentException("ByteBuffer is already full." );
258 }
259 if (mStreamState != STATE_WAITING_ON_READ) {
260 throw new IllegalStateException("Unexpected read attempt.");
261 }
262 if (isDone()) {
263 return;
264 }
265 mStreamState = STATE_READING;
266 if (!nativeReadData(mNativeStream, buffer, buffer.position(), buffer .limit())) {
267 // Still waiting on read. This is just to have consistent
268 // behavior with the other error cases.
269 mStreamState = STATE_WAITING_ON_READ;
270 // Since accessing byteBuffer's memory failed, it's presumably
271 // not a direct ByteBuffer.
272 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
273 }
274 }
275 }
276
277 @Override
278 public void write(ByteBuffer buffer, boolean endOfStream) {
279 synchronized (mNativeStreamLock) {
280 if (!buffer.hasRemaining() && !endOfStream) {
281 throw new IllegalArgumentException("Empty buffer before end of s tream.");
282 }
283 if (mWriteState != STATE_WAITING_ON_WRITE) {
284 throw new IllegalStateException("Unexpected write attempt.");
285 }
286 if (isDone()) {
287 return;
288 }
289 mWriteState = endOfStream ? STATE_WRITING_END_OF_STREAM : STATE_WRIT ING;
290 if (!nativeWriteData(
291 mNativeStream, buffer, buffer.position(), buffer.limit() , endOfStream)) {
292 // Still waiting on write. This is just to have consistent
293 // behavior with the other error cases.
294 mWriteState = STATE_WAITING_ON_WRITE;
295 // Since accessing byteBuffer's memory failed, it's presumably
296 // not a direct ByteBuffer.
297 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
298 }
299 }
300 }
301
302 @Override
303 public void ping(PingCallback callback, Executor executor) {
304 // TODO(mef): May be last thing to be implemented on Android.
305 throw new UnsupportedOperationException("ping is not supported yet.");
306 }
307
308 @Override
309 public void windowUpdate(int windowSizeIncrement) {
310 // TODO(mef): Understand the needs and semantics of this method.
311 throw new UnsupportedOperationException("windowUpdate is not supported y et.");
312 }
313
314 @Override
315 public void cancel() {
316 synchronized (mNativeStreamLock) {
317 if (isDone() || mStreamState == STATE_NOT_STARTED) {
318 return;
319 }
320 mStreamState = STATE_CANCELED;
321 destroyNativeStream(true);
322 }
323 }
324
325 @Override
326 public boolean isDone() {
pauljensen 2016/01/06 17:18:33 This function is only called from within "synchron
mef 2016/01/06 21:28:30 It is also public API, so it has to be synchronize
pauljensen 2016/01/07 02:56:34 how about: isDone() { synchronized (mNativeStrea
mef 2016/01/07 03:22:15 I think it is micro-optimization when lock is alre
327 synchronized (mNativeStreamLock) {
328 return mStreamState != STATE_NOT_STARTED && mNativeStream == 0;
329 }
330 }
331
332 @SuppressWarnings("unused")
333 @CalledByNative
334 private void onRequestHeadersSent() {
335 Runnable task = new Runnable() {
336 public void run() {
337 synchronized (mNativeStreamLock) {
338 if (isDone()) {
339 return;
340 }
341 if (doesMethodAllowWriteData(mInitialMethod)) {
342 mWriteState = STATE_WAITING_ON_WRITE;
343 } else {
344 mWriteState = STATE_WRITING_DONE;
345 }
346 }
347
348 try {
349 mCallback.onRequestHeadersSent(CronetBidirectionalStream.thi s);
350 } catch (Exception e) {
351 onCallbackException(e);
352 }
353 }
354 };
355 postTaskToExecutor(task);
356 }
357
358 /**
359 * Called when the final set of headers, after all redirects,
360 * is received. Can only be called once for each stream.
361 */
362 @SuppressWarnings("unused")
363 @CalledByNative
364 private void onResponseHeadersReceived(int httpStatusCode, String[] headers) {
365 mResponseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode, heade rs);
366 Runnable task = new Runnable() {
367 public void run() {
368 synchronized (mNativeStreamLock) {
369 if (isDone()) {
370 return;
371 }
372 mStreamState = STATE_WAITING_ON_READ;
373 }
374
375 try {
376 mCallback.onResponseHeadersReceived(
377 CronetBidirectionalStream.this, mResponseInfo);
378 } catch (Exception e) {
379 onCallbackException(e);
380 }
381 }
382 };
383 postTaskToExecutor(task);
384 }
385
386 @SuppressWarnings("unused")
387 @CalledByNative
388 private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
389 long receivedBytesCount) {
390 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
391 if (byteBuffer.position() != initialPosition) {
392 failWithException(
393 new CronetException("ByteBuffer modified externally during r ead", null));
394 return;
395 }
396 if (bytesRead < 0 || initialPosition + bytesRead > byteBuffer.limit()) {
397 failWithException(new CronetException("Invalid number of bytes read" , null));
398 return;
399 }
400 if (mOnReadCompletedTask == null) {
401 mOnReadCompletedTask = new OnReadCompletedRunnable();
402 }
403 byteBuffer.position(initialPosition + bytesRead);
404 mOnReadCompletedTask.mByteBuffer = byteBuffer;
405 mOnReadCompletedTask.mEndOfStream = (bytesRead == 0);
406 postTaskToExecutor(mOnReadCompletedTask);
407 }
408
409 @SuppressWarnings("unused")
410 @CalledByNative
411 private void onWriteCompleted(final ByteBuffer byteBuffer, int initialPositi on) {
412 if (byteBuffer.position() != initialPosition) {
pauljensen 2016/01/04 19:56:00 no action required side-note: Kinda weird to me t
mef 2016/01/04 22:27:07 Good point, I will preserve and check limits as we
mef 2016/01/06 21:28:30 Done.
413 failWithException(
414 new CronetException("ByteBuffer modified externally during w rite", null));
415 return;
416 }
417 if (mOnWriteCompletedTask == null) {
418 mOnWriteCompletedTask = new OnWriteCompletedRunnable();
419 }
420 // Current implementation always writes the complete buffer.
421 byteBuffer.position(byteBuffer.limit());
422 mOnWriteCompletedTask.mByteBuffer = byteBuffer;
423 postTaskToExecutor(mOnWriteCompletedTask);
424 }
425
426 @SuppressWarnings("unused")
427 @CalledByNative
428 private void onResponseTrailersReceived(String[] trailers) {
429 final UrlResponseInfo.HeaderBlock trailersBlock =
430 new UrlResponseInfo.HeaderBlock(headersListFromStrings(trailers) );
431 Runnable task = new Runnable() {
432 public void run() {
433 synchronized (mNativeStreamLock) {
434 if (isDone()) {
435 return;
436 }
437 }
438 try {
439 mCallback.onResponseTrailersReceived(
440 CronetBidirectionalStream.this, mResponseInfo, trail ersBlock);
441 } catch (Exception e) {
442 onCallbackException(e);
443 }
444 }
445 };
446 postTaskToExecutor(task);
447 }
448
449 @SuppressWarnings("unused")
450 @CalledByNative
451 private void onError(final int nativeError, final String errorString, long r eceivedBytesCount) {
452 if (mResponseInfo != null) {
453 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
454 }
455 failWithException(new CronetException(
456 "Exception in BidirectionalStream: " + errorString, nativeError) );
457 }
458
459 /**
460 * Called when request is canceled, no callbacks will be called afterwards.
461 */
462 @SuppressWarnings("unused")
463 @CalledByNative
464 private void onCanceled() {
465 Runnable task = new Runnable() {
466 public void run() {
467 try {
468 mCallback.onCanceled(CronetBidirectionalStream.this, mRespon seInfo);
469 } catch (Exception e) {
470 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onCance led method", e);
471 }
472 }
473 };
474 postTaskToExecutor(task);
475 }
476
477 @VisibleForTesting
478 public void setOnDestroyedCallbackForTesting(Runnable onDestroyedCallbackFor Testing) {
479 mOnDestroyedCallbackForTesting = onDestroyedCallbackForTesting;
480 }
481
482 /**
483 * Posts task to application Executor. Used for callbacks
484 * and other tasks that should not be executed on network thread.
485 */
486 private void postTaskToExecutor(Runnable task) {
487 try {
488 mExecutor.execute(task);
489 } catch (RejectedExecutionException failException) {
490 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex ecutor",
491 failException);
492 // If posting a task throws an exception, then there is no choice
493 // but to cancel the stream.
494 cancel();
495 }
496 }
497
498 private static ArrayList<Map.Entry<String, String>> headersListFromStrings(S tring[] headers) {
499 ArrayList<Map.Entry<String, String>> headersList =
500 new ArrayList<Map.Entry<String, String>>();
pauljensen 2016/01/06 17:18:33 ()->(headers.length/2)
mef 2016/01/06 21:28:30 Done.
501 for (int i = 0; i < headers.length; i += 2) {
502 headersList.add(new AbstractMap.SimpleImmutableEntry<String, String> (
503 headers[i], headers[i + 1]));
504 }
505 return headersList;
506 }
507
508 private static String[] stringsFromHeaderList(
509 ArrayList<Map.Entry<String, String>> headersList) {
510 String headersArray[] = new String[headersList.size() * 2];
511 int i = 0;
512 for (Map.Entry<String, String> requestHeader : headersList) {
513 headersArray[i++] = requestHeader.getKey();
514 headersArray[i++] = requestHeader.getValue();
515 }
516 return headersArray;
517 }
518
519 private UrlResponseInfo prepareResponseInfoOnNetworkThread(
520 int httpStatusCode, String[] headers) {
521 long nativeStream;
522 synchronized (mNativeStreamLock) {
523 if (mNativeStream == 0) {
524 return null;
525 }
526 // This method is running on network thread, so even if
527 // mNativeStream is set to 0 from another thread the actual
528 // deletion of the adapter is posted to network thread, so it is
529 // safe to preserve and use nativeStream outside the lock.
530 nativeStream = mNativeStream;
531 }
532
533 ArrayList<String> urlChain = new ArrayList<String>();
534 urlChain.add(mInitialUrl);
535
536 boolean wasCached = false;
537 String httpStatusText = "";
538 String negotiatedProtocol = nativeGetNegotiatedProtocol(nativeStream);
539 String proxyServer = null;
540
541 UrlResponseInfo responseInfo = new UrlResponseInfo(urlChain, httpStatusC ode, httpStatusText,
542 headersListFromStrings(headers), wasCached, negotiatedProtocol, proxyServer);
543 return responseInfo;
544 }
545
546 private static int convertStreamPriority(
547 @BidirectionalStream.Builder.StreamPriority int priority) {
548 switch (priority) {
549 case Builder.STREAM_PRIORITY_IDLE:
550 return RequestPriority.IDLE;
551 case Builder.STREAM_PRIORITY_LOWEST:
552 return RequestPriority.LOWEST;
553 case Builder.STREAM_PRIORITY_LOW:
554 return RequestPriority.LOW;
555 case Builder.STREAM_PRIORITY_MEDIUM:
556 return RequestPriority.MEDIUM;
557 case Builder.STREAM_PRIORITY_HIGHEST:
558 return RequestPriority.HIGHEST;
559 default:
560 return RequestPriority.MEDIUM;
561 }
562 }
563
564 private void destroyNativeStream(boolean sendOnCanceled) {
565 synchronized (mNativeStreamLock) {
566 Log.i(CronetUrlRequestContext.LOG_TAG, "destroyNativeStream " + this .toString());
567 if (mNativeStream == 0) {
568 return;
569 }
570 nativeDestroy(mNativeStream, sendOnCanceled);
571 mRequestContext.onRequestDestroyed();
572 mNativeStream = 0;
pauljensen 2016/01/06 17:18:33 can we swap this line with the line above? I'd ra
mef 2016/01/06 21:28:30 Done.
573 if (mOnDestroyedCallbackForTesting != null) {
574 mOnDestroyedCallbackForTesting.run();
575 }
576 }
577 }
578
579 /**
580 * If callback method throws an exception, stream gets canceled
581 * and exception is reported via onFailed callback.
582 * Only called on the Executor.
583 */
584 private void onCallbackException(Exception e) {
585 CronetException streamError =
586 new CronetException("CalledByNative method has thrown an excepti on", e);
587 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative meth od", e);
588 // Do not call into listener if request is complete.
589 synchronized (mNativeStreamLock) {
590 if (isDone()) {
591 return;
592 }
593 destroyNativeStream(false);
594 }
595 try {
596 mCallback.onFailed(this, mResponseInfo, streamError);
597 } catch (Exception failException) {
598 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of faile d request",
599 failException);
600 }
601 }
602
603 /**
604 * Fails the stream with an exception. Can be called on any thread.
605 */
606 private void failWithException(final CronetException exception) {
607 Runnable task = new Runnable() {
608 public void run() {
609 synchronized (mNativeStreamLock) {
610 if (isDone()) {
611 return;
612 }
613 mStreamState = STATE_ERROR;
614 destroyNativeStream(false);
615 }
616 try {
617 mCallback.onFailed(CronetBidirectionalStream.this, mResponse Info, exception);
618 } catch (Exception e) {
619 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onError method", e);
620 }
621 }
622 };
623 postTaskToExecutor(task);
624 }
625
626 // Native methods are implemented in cronet_bidirectional_stream.cc.
627 private native long nativeCreateBidirectionalStream(long urlRequestContextAd apter);
628
629 @NativeClassQualifiedName("CronetBidirectionalStream")
630 private native int nativeStart(long nativePtr, String url, int priority, Str ing method,
631 String[] headers, boolean endOfStream);
632
633 @NativeClassQualifiedName("CronetBidirectionalStream")
634 private native boolean nativeReadData(
635 long nativePtr, ByteBuffer byteBuffer, int position, int capacity);
636
637 @NativeClassQualifiedName("CronetBidirectionalStream")
638 private native boolean nativeWriteData(
639 long nativePtr, ByteBuffer byteBuffer, int position, int capacity, b oolean endOfStream);
640
641 @NativeClassQualifiedName("CronetBidirectionalStream")
642 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled);
643
644 @NativeClassQualifiedName("CronetBidirectionalStream")
645 private native String nativeGetNegotiatedProtocol(long nativePtr);
646 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698