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

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: Destroy the native adapter instead of cancel if can't post task to executor. 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.
pauljensen 2016/01/19 16:03:40 BidirectionalStream ->{@link BidirectionalStream}
mef 2016/01/20 15:37:40 Done.
25 * All @CallByNative methods are called on native network thread
pauljensen 2016/01/19 16:03:40 native->the native
pauljensen 2016/01/19 16:03:40 CallByNative->CalledByNative
mef 2016/01/20 15:37:40 Done.
mef 2016/01/20 15:37:40 Done.
26 * and post tasks with callback calls onto Executor. Upon return from callback n ative
pauljensen 2016/01/19 16:03:40 callback->callback,
mef 2016/01/20 15:37:40 Hrm. I think done, but I'm not sure.
27 * stream is called on executor thread and posts native tasks to native network thread.
pauljensen 2016/01/19 16:03:40 native->the native
pauljensen 2016/01/19 16:03:40 executor->Executor
mef 2016/01/20 15:37:40 Done.
mef 2016/01/20 15:37:40 Done.
28 */
29 @JNINamespace("cronet")
30 class CronetBidirectionalStream extends BidirectionalStream {
31 /**
32 * States of BidirectionalStream are tracked in mReadState and mWriteState.
33 * The write state is separated out as it changes independently of the strea m state.
pauljensen 2016/01/19 16:03:40 nit: might want to update reference to "stream sta
mef 2016/01/20 15:37:40 Done.
34 * There is one initial state - State.NOT_STARTED. There is one final state - State.SUCCESS,
pauljensen 2016/01/19 16:03:40 Might want to mention that CANCELED and ERROR stat
mef 2016/01/20 15:37:40 Done.
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 private enum State {
40 /* Initial state, stream not started. */
41 NOT_STARTED,
42 /* Stream started, request headers are sent. */
pauljensen 2016/01/19 16:03:40 sent->being sent
mef 2016/01/20 15:37:40 Done.
43 STARTED,
44 /* Waiting for {@code read()} to be called. */
45 WAITING_ON_READ,
46 /* Reading from the remote, {@code onReadCompleted()} callback will be c alled when done. */
47 READING,
48 /* There is no more data to read and stream is half-closed by the remote side. */
49 READING_DONE,
50 /* Stream is canceled. */
51 CANCELED,
52 /* Error has occured, stream is closed. */
53 ERROR,
54 /* Reading and writing is done, and the stream is closed successfully. * /
55 SUCCESS,
56 /* Waiting for {@code write()} to be called. */
57 WAITING_ON_WRITE,
58 /* Writing to the remote, {@code onWriteCompleted()} callback will be ca lled when done. */
59 WRITING,
60 /* Writing the last frame, so {@code State.WRITING_DONE} will be set upo n completion. */
61 WRITING_END_OF_STREAM,
62 /* There is no more data to write and stream is half-closed by the local side. */
63 WRITING_DONE,
64 }
65
66 private final CronetUrlRequestContext mRequestContext;
67 private final Executor mExecutor;
68 private final Callback mCallback;
69 private final String mInitialUrl;
70 private final int mInitialPriority;
71 private final String mInitialMethod;
72 private final ArrayList<Map.Entry<String, String>> mRequestHeaders;
73
74 /*
75 * Synchronize access to mNativeStream, mReadState and mWriteState.
76 */
77 private final Object mNativeStreamLock = new Object();
78
79 /* Native BidirectionalStream object, owned by CronetBidirectionalStream. */
80 @GuardedBy("mNativeStreamLock") private long mNativeStream;
81
82 /**
83 * Read state is tracking reading flow.
84 * / <--- READING <--- \
85 * | |
86 * \ /
87 * NOT_STARTED -> STARTED --> WAITING_ON_READ -> READING_DONE -> SUCCESS
88 */
89 @GuardedBy("mNativeStreamLock") private State mReadState = State.NOT_STARTED ;
90
91 /**
92 * Write state is tracking writing flow.
93 * / <--- WRITING <--- \
94 * | |
95 * \ /
96 * NOT_STARTED -> STARTED --> WAITING_ON_WRITE -> WRITING_END_OF_STREAM -> W RITING_DONE ->
97 * SUCCESS
98 */
99 @GuardedBy("mNativeStreamLock") private State mWriteState = State.NOT_STARTE D;
100
101 private UrlResponseInfo mResponseInfo;
102
103 /*
104 * OnReadCompleted callback is repeatedly invoked when each read is complete d, so it
105 * is cached as a member variable.
106 */
107 private OnReadCompletedRunnable mOnReadCompletedTask;
108
109 /*
110 * OnWriteCompleted callback is repeatedly invoked when each write is comple ted, so it
111 * is cached as a member variable.
112 */
113 private OnWriteCompletedRunnable mOnWriteCompletedTask;
114
115 private Runnable mOnDestroyedCallbackForTesting;
116
117 private final class OnReadCompletedRunnable implements Runnable {
118 // Buffer passed back from current invocation of onReadCompleted.
119 ByteBuffer mByteBuffer;
120 // End of stream flag from current invocation of onReadCompleted.
121 boolean mEndOfStream;
122
123 @Override
124 public void run() {
125 try {
126 // Null out mByteBuffer, to pass buffer ownership to callback or release if done.
127 ByteBuffer buffer = mByteBuffer;
128 mByteBuffer = null;
129 synchronized (mNativeStreamLock) {
130 if (isDoneLocked()) {
131 return;
132 }
133 if (mEndOfStream) {
134 mReadState = State.READING_DONE;
135 if (maybeSucceeded()) {
136 return;
137 }
138 } else {
139 mReadState = State.WAITING_ON_READ;
140 }
141 }
142 mCallback.onReadCompleted(CronetBidirectionalStream.this, mRespo nseInfo, buffer);
143 } catch (Exception e) {
144 onCallbackException(e);
145 }
146 }
147 }
148
149 private final class OnWriteCompletedRunnable implements Runnable {
150 // Buffer passed back from current invocation of onWriteCompleted.
151 ByteBuffer mByteBuffer;
152
153 @Override
154 public void run() {
155 try {
156 // Null out mByteBuffer, to pass buffer ownership to callback or release if done.
157 ByteBuffer buffer = mByteBuffer;
158 mByteBuffer = null;
159 synchronized (mNativeStreamLock) {
160 if (isDoneLocked()) {
161 return;
162 }
163 if (mWriteState == State.WRITING_END_OF_STREAM) {
164 mWriteState = State.WRITING_DONE;
165 if (maybeSucceeded()) {
166 return;
167 }
168 } else {
169 mWriteState = State.WAITING_ON_WRITE;
170 }
171 }
172 mCallback.onWriteCompleted(CronetBidirectionalStream.this, mResp onseInfo, buffer);
173 } catch (Exception e) {
174 onCallbackException(e);
175 }
176 }
177 }
178
179 CronetBidirectionalStream(CronetUrlRequestContext requestContext, long urlRe questContextAdapter,
pauljensen 2016/01/19 16:03:40 remove unused urlRequestContextAdapater argument
mef 2016/01/20 15:37:40 Done.
180 String url, @BidirectionalStream.Builder.StreamPriority int priority , Callback callback,
181 Executor executor, String httpMethod, List<Map.Entry<String, String> > requestHeaders) {
182 mRequestContext = requestContext;
183 mInitialUrl = url;
184 mInitialPriority = convertStreamPriority(priority);
185 mCallback = callback;
186 mExecutor = executor;
187 mInitialMethod = httpMethod;
188 mRequestHeaders = new ArrayList<Map.Entry<String, String>>(requestHeader s);
189 }
190
191 @GuardedBy("mNativeStreamLock")
192 private boolean maybeSucceeded() {
pauljensen 2016/01/19 16:03:40 nit: add "Locked" suffix to maybeSucceeded
mef 2016/01/20 15:37:40 Done.
193 if (mReadState != State.READING_DONE || mWriteState != State.WRITING_DON E) {
194 return false;
195 }
196
197 mReadState = mWriteState = State.SUCCESS;
198 Runnable task = new Runnable() {
199 public void run() {
200 synchronized (mNativeStreamLock) {
201 if (isDoneLocked()) {
202 return;
203 }
204 // Destroy native stream first, so request context could be shut
205 // down from the listener.
206 destroyNativeStream(false);
207 }
208 try {
209 mCallback.onSucceeded(CronetBidirectionalStream.this, mRespo nseInfo);
210 } catch (Exception e) {
211 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onSucce eded method", e);
212 }
213 }
214 };
215 postTaskToExecutor(task);
216 return true;
217 }
218
219 private static boolean doesMethodAllowWriteData(String methodName) {
220 return !methodName.equals("GET") && !methodName.equals("HEAD");
221 }
222
223 @Override
224 public void start() {
225 synchronized (mNativeStreamLock) {
226 if (mReadState != State.NOT_STARTED) {
227 throw new IllegalStateException("Stream is already started.");
228 }
229 try {
230 mNativeStream = nativeCreateBidirectionalStream(
231 mRequestContext.getUrlRequestContextAdapter());
232 mRequestContext.onRequestStarted();
233 String headers[] = stringsFromHeaderList(mRequestHeaders);
234 // Non-zero startResult means an argument error.
235 int startResult = nativeStart(mNativeStream, mInitialUrl, mIniti alPriority,
236 mInitialMethod, headers, !doesMethodAllowWriteData(mInit ialMethod));
237 if (startResult == -1) {
238 throw new IllegalArgumentException("Invalid http method " + mInitialMethod);
239 }
240 if (startResult > 0) {
241 int headerPos = startResult - 1;
242 throw new IllegalArgumentException(
243 "Invalid header " + headers[headerPos] + "=" + heade rs[headerPos + 1]);
244 }
245 mReadState = mWriteState = State.STARTED;
246 } catch (RuntimeException e) {
247 // If there's an exception, cleanup and then throw the
248 // exception to the caller.
249 destroyNativeStream(false);
250 throw e;
251 }
252 }
253 }
254
255 @Override
256 public void read(ByteBuffer buffer) {
257 synchronized (mNativeStreamLock) {
258 if (!buffer.hasRemaining()) {
pauljensen 2016/01/19 16:03:40 replace this with Preconditions.checkHasRemaining(
mef 2016/01/20 15:37:40 Done.
259 throw new IllegalArgumentException("ByteBuffer is already full." );
260 }
pauljensen 2016/01/19 16:03:40 add Preconditions.checkDirect(buffer);
mef 2016/01/20 15:37:40 Done.
261 if (mReadState != State.WAITING_ON_READ) {
262 throw new IllegalStateException("Unexpected read attempt.");
263 }
264 if (isDoneLocked()) {
265 return;
266 }
267 mReadState = State.READING;
268 if (!nativeReadData(mNativeStream, buffer, buffer.position(), buffer .limit())) {
269 // Still waiting on read. This is just to have consistent
270 // behavior with the other error cases.
271 mReadState = State.WAITING_ON_READ;
272 // Since accessing byteBuffer's memory failed, it's presumably
273 // not a direct ByteBuffer.
274 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
275 }
276 }
277 }
278
279 @Override
280 public void write(ByteBuffer buffer, boolean endOfStream) {
281 synchronized (mNativeStreamLock) {
282 if (!buffer.hasRemaining() && !endOfStream) {
283 throw new IllegalArgumentException("Empty buffer before end of s tream.");
284 }
pauljensen 2016/01/19 16:03:40 add Preconditions.checkDirect(buffer);
mef 2016/01/20 15:37:40 Done.
285 if (mWriteState != State.WAITING_ON_WRITE) {
286 throw new IllegalStateException("Unexpected write attempt.");
287 }
288 if (isDoneLocked()) {
289 return;
290 }
291 mWriteState = endOfStream ? State.WRITING_END_OF_STREAM : State.WRIT ING;
292 if (!nativeWriteData(
293 mNativeStream, buffer, buffer.position(), buffer.limit() , endOfStream)) {
294 // Still waiting on write. This is just to have consistent
295 // behavior with the other error cases.
296 mWriteState = State.WAITING_ON_WRITE;
297 // Since accessing byteBuffer's memory failed, it's presumably
298 // not a direct ByteBuffer.
299 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
300 }
301 }
302 }
303
304 @Override
305 public void ping(PingCallback callback, Executor executor) {
306 // TODO(mef): May be last thing to be implemented on Android.
307 throw new UnsupportedOperationException("ping is not supported yet.");
308 }
309
310 @Override
311 public void windowUpdate(int windowSizeIncrement) {
312 // TODO(mef): Understand the needs and semantics of this method.
313 throw new UnsupportedOperationException("windowUpdate is not supported y et.");
314 }
315
316 @Override
317 public void cancel() {
318 synchronized (mNativeStreamLock) {
319 if (isDoneLocked() || mReadState == State.NOT_STARTED) {
320 return;
321 }
322 mReadState = mWriteState = State.CANCELED;
323 destroyNativeStream(true);
324 }
325 }
326
327 @Override
328 public boolean isDone() {
329 synchronized (mNativeStreamLock) {
330 return isDoneLocked();
331 }
332 }
333
334 @GuardedBy("mNativeStreamLock")
335 private boolean isDoneLocked() {
336 return mReadState != State.NOT_STARTED && mNativeStream == 0;
337 }
338
339 @SuppressWarnings("unused")
340 @CalledByNative
341 private void onRequestHeadersSent() {
342 Runnable task = new Runnable() {
343 public void run() {
344 synchronized (mNativeStreamLock) {
345 if (isDoneLocked()) {
346 return;
347 }
348 if (doesMethodAllowWriteData(mInitialMethod)) {
349 mWriteState = State.WAITING_ON_WRITE;
350 } else {
351 mWriteState = State.WRITING_DONE;
352 }
353 }
354
355 try {
356 mCallback.onRequestHeadersSent(CronetBidirectionalStream.thi s);
357 } catch (Exception e) {
358 onCallbackException(e);
359 }
360 }
361 };
362 postTaskToExecutor(task);
363 }
364
365 /**
366 * Called when the final set of headers, after all redirects,
367 * is received. Can only be called once for each stream.
368 */
369 @SuppressWarnings("unused")
370 @CalledByNative
371 private void onResponseHeadersReceived(
372 int httpStatusCode, String negotiatedProtocol, String[] headers) {
373 mResponseInfo =
374 prepareResponseInfoOnNetworkThread(httpStatusCode, negotiatedPro tocol, headers);
375 Runnable task = new Runnable() {
376 public void run() {
377 synchronized (mNativeStreamLock) {
378 if (isDoneLocked()) {
379 return;
380 }
381 mReadState = State.WAITING_ON_READ;
382 }
383
384 try {
385 mCallback.onResponseHeadersReceived(
386 CronetBidirectionalStream.this, mResponseInfo);
387 } catch (Exception e) {
388 onCallbackException(e);
389 }
390 }
391 };
392 postTaskToExecutor(task);
393 }
394
395 @SuppressWarnings("unused")
396 @CalledByNative
397 private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
398 int initialLimit, long receivedBytesCount) {
399 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
400 if (byteBuffer.position() != initialPosition || byteBuffer.limit() != in itialLimit) {
401 failWithException(
402 new CronetException("ByteBuffer modified externally during r ead", null));
403 return;
404 }
405 if (bytesRead < 0 || initialPosition + bytesRead > byteBuffer.limit()) {
pauljensen 2016/01/19 16:03:40 do one of these for consistency: 1. initialPositio
mef 2016/01/20 15:37:40 Done.
406 failWithException(new CronetException("Invalid number of bytes read" , null));
407 return;
408 }
409 if (mOnReadCompletedTask == null) {
410 mOnReadCompletedTask = new OnReadCompletedRunnable();
411 }
412 byteBuffer.position(initialPosition + bytesRead);
413 mOnReadCompletedTask.mByteBuffer = byteBuffer;
414 mOnReadCompletedTask.mEndOfStream = (bytesRead == 0);
pauljensen 2016/01/19 16:03:40 no action required nit: it'd be a performance boos
mef 2016/01/20 15:37:40 Acknowledged. This is also current contract with u
415 postTaskToExecutor(mOnReadCompletedTask);
416 }
417
418 @SuppressWarnings("unused")
419 @CalledByNative
420 private void onWriteCompleted(
421 final ByteBuffer byteBuffer, int initialPosition, int initialLimit) {
422 if (byteBuffer.position() != initialPosition || byteBuffer.limit() != in itialLimit) {
423 failWithException(
424 new CronetException("ByteBuffer modified externally during w rite", null));
425 return;
426 }
427 if (mOnWriteCompletedTask == null) {
428 mOnWriteCompletedTask = new OnWriteCompletedRunnable();
429 }
430 // Current implementation always writes the complete buffer.
431 byteBuffer.position(byteBuffer.limit());
432 mOnWriteCompletedTask.mByteBuffer = byteBuffer;
433 postTaskToExecutor(mOnWriteCompletedTask);
434 }
435
436 @SuppressWarnings("unused")
437 @CalledByNative
438 private void onResponseTrailersReceived(String[] trailers) {
439 final UrlResponseInfo.HeaderBlock trailersBlock =
440 new UrlResponseInfo.HeaderBlock(headersListFromStrings(trailers) );
441 Runnable task = new Runnable() {
442 public void run() {
443 synchronized (mNativeStreamLock) {
444 if (isDoneLocked()) {
445 return;
446 }
447 }
448 try {
449 mCallback.onResponseTrailersReceived(
450 CronetBidirectionalStream.this, mResponseInfo, trail ersBlock);
451 } catch (Exception e) {
452 onCallbackException(e);
453 }
454 }
455 };
456 postTaskToExecutor(task);
pauljensen 2016/01/19 16:03:40 nit: any particular reason we do this: Runnable
mef 2016/01/20 15:37:40 Historical reasons. :) Done.
457 }
458
459 @SuppressWarnings("unused")
460 @CalledByNative
461 private void onError(final int nativeError, final String errorString, long r eceivedBytesCount) {
462 if (mResponseInfo != null) {
463 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
464 }
465 failWithException(new CronetException(
466 "Exception in BidirectionalStream: " + errorString, nativeError) );
467 }
468
469 /**
470 * Called when request is canceled, no callbacks will be called afterwards.
471 */
472 @SuppressWarnings("unused")
473 @CalledByNative
474 private void onCanceled() {
475 Runnable task = new Runnable() {
476 public void run() {
477 try {
478 mCallback.onCanceled(CronetBidirectionalStream.this, mRespon seInfo);
479 } catch (Exception e) {
480 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onCance led method", e);
481 }
482 }
483 };
484 postTaskToExecutor(task);
485 }
486
487 @VisibleForTesting
488 public void setOnDestroyedCallbackForTesting(Runnable onDestroyedCallbackFor Testing) {
489 mOnDestroyedCallbackForTesting = onDestroyedCallbackForTesting;
490 }
491
492 /**
493 * Posts task to application Executor. Used for callbacks
494 * and other tasks that should not be executed on network thread.
495 */
496 private void postTaskToExecutor(Runnable task) {
497 try {
498 mExecutor.execute(task);
499 } catch (RejectedExecutionException failException) {
500 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex ecutor",
501 failException);
502 // If posting a task throws an exception, then there is no choice
503 // but to destroy the stream without invoking the callback.
504 mReadState = mWriteState = State.ERROR;
pauljensen 2016/01/19 16:03:40 seems like this is unlocked access to mReadState a
mef 2016/01/20 15:37:40 Done.
505 destroyNativeStream(false);
506 }
507 }
508
509 private static ArrayList<Map.Entry<String, String>> headersListFromStrings(S tring[] headers) {
510 ArrayList<Map.Entry<String, String>> headersList =
511 new ArrayList<Map.Entry<String, String>>(headers.length / 2);
512 for (int i = 0; i < headers.length; i += 2) {
513 headersList.add(new AbstractMap.SimpleImmutableEntry<String, String> (
514 headers[i], headers[i + 1]));
515 }
516 return headersList;
517 }
518
519 private static String[] stringsFromHeaderList(
520 ArrayList<Map.Entry<String, String>> headersList) {
521 String headersArray[] = new String[headersList.size() * 2];
522 int i = 0;
523 for (Map.Entry<String, String> requestHeader : headersList) {
524 headersArray[i++] = requestHeader.getKey();
525 headersArray[i++] = requestHeader.getValue();
526 }
527 return headersArray;
528 }
529
530 private UrlResponseInfo prepareResponseInfoOnNetworkThread(
531 int httpStatusCode, String negotiatedProtocol, String[] headers) {
532 long nativeStream;
533 synchronized (mNativeStreamLock) {
534 if (mNativeStream == 0) {
535 return null;
536 }
537 // This method is running on network thread, so even if
538 // mNativeStream is set to 0 from another thread the actual
539 // deletion of the adapter is posted to network thread, so it is
540 // safe to preserve and use nativeStream outside the lock.
541 nativeStream = mNativeStream;
542 }
543
544 ArrayList<String> urlChain = new ArrayList<String>();
545 urlChain.add(mInitialUrl);
546
547 boolean wasCached = false;
548 String httpStatusText = "";
pauljensen 2016/01/19 16:03:40 there's no status line for BiDiStreams? I would h
mef 2016/01/20 15:37:40 Correct. Http2 doesn't have special header for htt
549 String proxyServer = null;
550
551 UrlResponseInfo responseInfo = new UrlResponseInfo(urlChain, httpStatusC ode, httpStatusText,
552 headersListFromStrings(headers), wasCached, negotiatedProtocol, proxyServer);
553 return responseInfo;
554 }
555
556 private static int convertStreamPriority(
557 @BidirectionalStream.Builder.StreamPriority int priority) {
558 switch (priority) {
559 case Builder.STREAM_PRIORITY_IDLE:
560 return RequestPriority.IDLE;
561 case Builder.STREAM_PRIORITY_LOWEST:
562 return RequestPriority.LOWEST;
563 case Builder.STREAM_PRIORITY_LOW:
564 return RequestPriority.LOW;
565 case Builder.STREAM_PRIORITY_MEDIUM:
566 return RequestPriority.MEDIUM;
567 case Builder.STREAM_PRIORITY_HIGHEST:
568 return RequestPriority.HIGHEST;
569 default:
570 return RequestPriority.MEDIUM;
pauljensen 2016/01/19 16:03:40 shouldn't this throw IllegalArgumentException?
mef 2016/01/20 15:37:40 Done.
571 }
572 }
573
574 private void destroyNativeStream(boolean sendOnCanceled) {
575 synchronized (mNativeStreamLock) {
576 Log.i(CronetUrlRequestContext.LOG_TAG, "destroyNativeStream " + this .toString());
577 if (mNativeStream == 0) {
578 return;
579 }
580 nativeDestroy(mNativeStream, sendOnCanceled);
581 mNativeStream = 0;
582 mRequestContext.onRequestDestroyed();
583 if (mOnDestroyedCallbackForTesting != null) {
584 mOnDestroyedCallbackForTesting.run();
585 }
586 }
587 }
588
589 /**
590 * Fails the stream with an exception. Only called on the Executor.
591 */
592 private void failWithExceptionOnExecutor(CronetException e) {
593 // Do not call into listener if request is complete.
594 synchronized (mNativeStreamLock) {
595 if (isDoneLocked()) {
596 return;
597 }
598 mReadState = mWriteState = State.ERROR;
599 destroyNativeStream(false);
600 }
601 try {
602 mCallback.onFailed(this, mResponseInfo, e);
603 } catch (Exception failException) {
604 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of faile d request",
605 failException);
606 }
607 }
608
609 /**
610 * If callback method throws an exception, stream gets canceled
611 * and exception is reported via onFailed callback.
612 * Only called on the Executor.
613 */
614 private void onCallbackException(Exception e) {
615 CronetException streamError =
616 new CronetException("CalledByNative method has thrown an excepti on", e);
617 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative meth od", e);
618 failWithExceptionOnExecutor(streamError);
619 }
620
621 /**
622 * Fails the stream with an exception. Can be called on any thread.
623 */
624 private void failWithException(final CronetException exception) {
625 Runnable task = new Runnable() {
626 public void run() {
627 failWithExceptionOnExecutor(exception);
628 }
629 };
630 postTaskToExecutor(task);
631 }
632
633 // Native methods are implemented in cronet_bidirectional_stream_adapter.cc.
634 private native long nativeCreateBidirectionalStream(long urlRequestContextAd apter);
pauljensen 2016/01/19 16:03:40 static?
mef 2016/01/20 15:37:40 Why? It gets |this| and stores as java owner for c
635
636 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
637 private native int nativeStart(long nativePtr, String url, int priority, Str ing method,
638 String[] headers, boolean endOfStream);
639
640 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
641 private native boolean nativeReadData(
642 long nativePtr, ByteBuffer byteBuffer, int position, int limit);
643
644 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
645 private native boolean nativeWriteData(
646 long nativePtr, ByteBuffer byteBuffer, int position, int limit, bool ean endOfStream);
647
648 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
649 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled);
650 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698