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

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

Issue 474573003: Catch and report exceptions in CalledByNative java methods. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add test asserts. Created 6 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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; 5 package org.chromium.net;
6 6
7 import android.util.Log;
8
7 import org.apache.http.conn.ConnectTimeoutException; 9 import org.apache.http.conn.ConnectTimeoutException;
8 import org.chromium.base.CalledByNative; 10 import org.chromium.base.CalledByNative;
9 import org.chromium.base.JNINamespace; 11 import org.chromium.base.JNINamespace;
10 12
11 import java.io.IOException; 13 import java.io.IOException;
12 import java.net.MalformedURLException; 14 import java.net.MalformedURLException;
13 import java.net.URL; 15 import java.net.URL;
14 import java.net.UnknownHostException; 16 import java.net.UnknownHostException;
15 import java.nio.ByteBuffer; 17 import java.nio.ByteBuffer;
16 import java.nio.channels.ReadableByteChannel; 18 import java.nio.channels.ReadableByteChannel;
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after
397 399
398 private void validateHeadersAvailable() { 400 private void validateHeadersAvailable() {
399 if (!mHeadersAvailable) { 401 if (!mHeadersAvailable) {
400 throw new IllegalStateException("Response headers not available"); 402 throw new IllegalStateException("Response headers not available");
401 } 403 }
402 } 404 }
403 405
404 // Private methods called by native library. 406 // Private methods called by native library.
405 407
406 /** 408 /**
409 * If @CalledByNative method throws an exception, request gets cancelled
410 * and exception could be retrieved from using getException().
411 */
412 private void onCalledByNativeException(Exception e) {
413 mSinkException = new IOException(
414 "CalledByNative method has thrown an exception", e);
mmenke 2014/08/13 21:15:27 Should these continuation lines all use 8-space in
mef 2014/08/13 21:34:40 Done.
415 Log.e(ChromiumUrlRequestContext.LOG_TAG,
416 "Exception in CalledByNative method", e);
417 try {
418 cancel();
mmenke 2014/08/13 21:15:27 Do we tell the consumer when this happens?
mef 2014/08/13 21:34:40 Consumer can check whether request isCanceled() an
mmenke 2014/08/13 22:18:09 Right, but the consumer isn't notified there was a
mef 2014/08/15 14:58:19 Listener's onRequestComplete is getting called whe
419 } catch (Exception cancel_exception) {
420 Log.e(ChromiumUrlRequestContext.LOG_TAG,
421 "Exception trying to cancel request", cancel_exception);
Charles 2014/08/13 21:17:09 I'm not super comfortable with swallowing all exce
mef 2014/08/13 21:34:40 Um, you do check results of request, right? Would
422 }
423 }
424
425 /**
407 * A callback invoked when the first chunk of the response has arrived. 426 * A callback invoked when the first chunk of the response has arrived.
408 */ 427 */
409 @CalledByNative 428 @CalledByNative
410 private void onResponseStarted() { 429 private void onResponseStarted() {
411 mContentType = nativeGetContentType(mUrlRequestAdapter); 430 try {
412 mContentLength = nativeGetContentLength(mUrlRequestAdapter); 431 mContentType = nativeGetContentType(mUrlRequestAdapter);
413 mHeadersAvailable = true; 432 mContentLength = nativeGetContentLength(mUrlRequestAdapter);
433 mHeadersAvailable = true;
414 434
415 if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit 435 if (mContentLengthLimit > 0 &&
416 && mCancelIfContentLengthOverLimit) { 436 mContentLength > mContentLengthLimit &&
417 onContentLengthOverLimit(); 437 mCancelIfContentLengthOverLimit) {
418 return; 438 onContentLengthOverLimit();
439 return;
440 }
441
442 if (mBufferFullResponse && mContentLength != -1
443 && !mContentLengthOverLimit) {
444 ((ChunkedWritableByteChannel)getSink()).setCapacity(
445 (int)mContentLength);
446 }
447
448 if (mOffset != 0) {
449 // The server may ignore the request for a byte range.
450 if (getHttpStatusCode() == 200) {
451 // TODO(mef): Revisit this logic.
452 if (mContentLength != -1) {
453 mContentLength -= mOffset;
454 }
455 mSkippingToOffset = true;
456 } else {
457 mSize = mOffset;
458 }
459 }
460 mListener.onResponseStarted(this);
461 } catch (Exception e) {
462 onCalledByNativeException(e);
419 } 463 }
420
421 if (mBufferFullResponse && mContentLength != -1
422 && !mContentLengthOverLimit) {
423 ((ChunkedWritableByteChannel)getSink()).setCapacity(
424 (int)mContentLength);
425 }
426
427 if (mOffset != 0) {
428 // The server may ignore the request for a byte range.
429 if (getHttpStatusCode() == 200) {
430 // TODO(mef): Revisit this logic.
431 if (mContentLength != -1) {
432 mContentLength -= mOffset;
433 }
434 mSkippingToOffset = true;
435 } else {
436 mSize = mOffset;
437 }
438 }
439 mListener.onResponseStarted(this);
440 } 464 }
441 465
442 /** 466 /**
443 * Consumes a portion of the response. 467 * Consumes a portion of the response.
444 * 468 *
445 * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and 469 * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and
446 * no references to it may be retained after the method ends, as 470 * no references to it may be retained after the method ends, as
447 * it wraps code managed on the native heap. 471 * it wraps code managed on the native heap.
448 */ 472 */
449 @CalledByNative 473 @CalledByNative
450 private void onBytesRead(ByteBuffer buffer) { 474 private void onBytesRead(ByteBuffer buffer) {
451 if (mContentLengthOverLimit) { 475 try {
452 return; 476 if (mContentLengthOverLimit) {
453 } 477 return;
478 }
454 479
455 int size = buffer.remaining(); 480 int size = buffer.remaining();
456 mSize += size; 481 mSize += size;
457 if (mSkippingToOffset) { 482 if (mSkippingToOffset) {
458 if (mSize <= mOffset) { 483 if (mSize <= mOffset) {
459 return; 484 return;
460 } else { 485 } else {
461 mSkippingToOffset = false; 486 mSkippingToOffset = false;
462 buffer.position((int)(mOffset - (mSize - size))); 487 buffer.position((int)(mOffset - (mSize - size)));
488 }
463 } 489 }
464 }
465 490
466 boolean contentLengthOverLimit = 491 boolean contentLengthOverLimit =
467 (mContentLengthLimit != 0 && mSize > mContentLengthLimit); 492 (mContentLengthLimit != 0 && mSize > mContentLengthLimit);
468 if (contentLengthOverLimit) { 493 if (contentLengthOverLimit) {
469 buffer.limit(size - (int)(mSize - mContentLengthLimit)); 494 buffer.limit(size - (int)(mSize - mContentLengthLimit));
470 } 495 }
471 496
472 try { 497 try {
473 while (buffer.hasRemaining()) { 498 while (buffer.hasRemaining()) {
474 mSink.write(buffer); 499 mSink.write(buffer);
500 }
501 } catch (IOException e) {
502 mSinkException = e;
503 cancel();
475 } 504 }
476 } catch (IOException e) { 505 if (contentLengthOverLimit) {
477 mSinkException = e; 506 onContentLengthOverLimit();
478 cancel(); 507 }
479 } 508 } catch (Exception e) {
480 if (contentLengthOverLimit) { 509 onCalledByNativeException(e);
481 onContentLengthOverLimit();
482 } 510 }
483 } 511 }
484 512
485 /** 513 /**
486 * Notifies the listener, releases native data structures. 514 * Notifies the listener, releases native data structures.
487 */ 515 */
488 @SuppressWarnings("unused") 516 @SuppressWarnings("unused")
489 @CalledByNative 517 @CalledByNative
490 private void finish() { 518 private void finish() {
491 synchronized (mLock) { 519 try {
492 mFinished = true; 520 synchronized (mLock) {
521 mFinished = true;
493 522
494 if (mRecycled) { 523 if (mRecycled) {
495 return; 524 return;
525 }
526 try {
527 mSink.close();
528 } catch (IOException e) {
529 // Ignore
530 }
531 onRequestComplete();
532 nativeDestroyRequestAdapter(mUrlRequestAdapter);
533 mUrlRequestAdapter = 0;
534 mRecycled = true;
496 } 535 }
497 try { 536 } catch (Exception e) {
498 mSink.close(); 537 onCalledByNativeException(e);
499 } catch (IOException e) {
500 // Ignore
501 }
502 onRequestComplete();
503 nativeDestroyRequestAdapter(mUrlRequestAdapter);
504 mUrlRequestAdapter = 0;
505 mRecycled = true;
506 } 538 }
507 } 539 }
508 540
509 /** 541 /**
510 * Appends header |name| with value |value| to |headersMap|. 542 * Appends header |name| with value |value| to |headersMap|.
511 */ 543 */
512 @SuppressWarnings("unused") 544 @SuppressWarnings("unused")
513 @CalledByNative 545 @CalledByNative
514 private void onAppendResponseHeader(ResponseHeadersMap headersMap, 546 private void onAppendResponseHeader(ResponseHeadersMap headersMap,
515 String name, String value) { 547 String name, String value) {
516 if (!headersMap.containsKey(name)) { 548 try {
517 headersMap.put(name, new ArrayList<String>()); 549 if (!headersMap.containsKey(name)) {
550 headersMap.put(name, new ArrayList<String>());
551 }
552 headersMap.get(name).add(value);
553 } catch (Exception e) {
554 onCalledByNativeException(e);
518 } 555 }
519 headersMap.get(name).add(value);
520 } 556 }
521 557
522 /** 558 /**
523 * Reads a sequence of bytes from upload channel into the given buffer. 559 * Reads a sequence of bytes from upload channel into the given buffer.
524 * @param dest The buffer into which bytes are to be transferred. 560 * @param dest The buffer into which bytes are to be transferred.
525 * @return Returns number of bytes read (could be 0) or -1 and closes 561 * @return Returns number of bytes read (could be 0) or -1 and closes
526 * the channel if error occured. 562 * the channel if error occured.
527 */ 563 */
528 @SuppressWarnings("unused") 564 @SuppressWarnings("unused")
529 @CalledByNative 565 @CalledByNative
530 private int readFromUploadChannel(ByteBuffer dest) { 566 private int readFromUploadChannel(ByteBuffer dest) {
531 if (mUploadChannel == null || !mUploadChannel.isOpen())
532 return -1;
533 try { 567 try {
534 int result = mUploadChannel.read(dest); 568 if (mUploadChannel == null || !mUploadChannel.isOpen())
535 if (result < 0) { 569 return -1;
536 mUploadChannel.close(); 570 try {
537 return 0; 571 int result = mUploadChannel.read(dest);
572 if (result < 0) {
573 mUploadChannel.close();
574 return 0;
575 }
576 return result;
577 } catch (IOException e) {
578 mSinkException = e;
579 try {
580 mUploadChannel.close();
581 } catch (IOException ignored) {
582 // Ignore this exception.
583 }
584 cancel();
538 } 585 }
539 return result; 586 } catch (Exception e) {
540 } catch (IOException e) { 587 onCalledByNativeException(e);
541 mSinkException = e;
542 try {
543 mUploadChannel.close();
544 } catch (IOException ignored) {
545 // Ignore this exception.
546 }
547 cancel();
548 return -1;
549 } 588 }
589 return -1;
550 } 590 }
551 591
552 // Native methods are implemented in chromium_url_request.cc. 592 // Native methods are implemented in chromium_url_request.cc.
553 593
554 private native long nativeCreateRequestAdapter( 594 private native long nativeCreateRequestAdapter(
555 long ChromiumUrlRequestContextAdapter, String url, int priority); 595 long ChromiumUrlRequestContextAdapter, String url, int priority);
556 596
557 private native void nativeAddHeader(long urlRequestAdapter, String name, 597 private native void nativeAddHeader(long urlRequestAdapter, String name,
558 String value); 598 String value);
559 599
(...skipping 23 matching lines...) Expand all
583 623
584 private native String nativeGetHeader(long urlRequestAdapter, String name); 624 private native String nativeGetHeader(long urlRequestAdapter, String name);
585 625
586 private native void nativeGetAllHeaders(long urlRequestAdapter, 626 private native void nativeGetAllHeaders(long urlRequestAdapter,
587 ResponseHeadersMap headers); 627 ResponseHeadersMap headers);
588 628
589 // Explicit class to work around JNI-generator generics confusion. 629 // Explicit class to work around JNI-generator generics confusion.
590 private class ResponseHeadersMap extends HashMap<String, List<String>> { 630 private class ResponseHeadersMap extends HashMap<String, List<String>> {
591 } 631 }
592 } 632 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698