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

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: Close mUploadChannel in finish, and don't cancel again if exception is caught in finish. 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
« no previous file with comments | « no previous file | components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 377 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 396
395 private void validateHeadersAvailable() { 397 private void validateHeadersAvailable() {
396 if (!mHeadersAvailable) { 398 if (!mHeadersAvailable) {
397 throw new IllegalStateException("Response headers not available"); 399 throw new IllegalStateException("Response headers not available");
398 } 400 }
399 } 401 }
400 402
401 // Private methods called by native library. 403 // Private methods called by native library.
402 404
403 /** 405 /**
406 * If @CalledByNative method throws an exception, request gets cancelled
407 * and exception could be retrieved from request using getException().
408 */
409 private void onCalledByNativeException(Exception e) {
410 mSinkException = new IOException(
411 "CalledByNative method has thrown an exception", e);
412 Log.e(ChromiumUrlRequestContext.LOG_TAG,
413 "Exception in CalledByNative method", e);
414 try {
415 cancel();
416 } catch (Exception cancel_exception) {
417 Log.e(ChromiumUrlRequestContext.LOG_TAG,
418 "Exception trying to cancel request", cancel_exception);
419 }
420 }
421
422 /**
404 * A callback invoked when the first chunk of the response has arrived. 423 * A callback invoked when the first chunk of the response has arrived.
405 */ 424 */
406 @CalledByNative 425 @CalledByNative
407 private void onResponseStarted() { 426 private void onResponseStarted() {
408 mContentType = nativeGetContentType(mUrlRequestAdapter); 427 try {
409 mContentLength = nativeGetContentLength(mUrlRequestAdapter); 428 mContentType = nativeGetContentType(mUrlRequestAdapter);
410 mHeadersAvailable = true; 429 mContentLength = nativeGetContentLength(mUrlRequestAdapter);
430 mHeadersAvailable = true;
411 431
412 if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit 432 if (mContentLengthLimit > 0 &&
413 && mCancelIfContentLengthOverLimit) { 433 mContentLength > mContentLengthLimit &&
414 onContentLengthOverLimit(); 434 mCancelIfContentLengthOverLimit) {
415 return; 435 onContentLengthOverLimit();
436 return;
437 }
438
439 if (mBufferFullResponse && mContentLength != -1
440 && !mContentLengthOverLimit) {
441 ((ChunkedWritableByteChannel)getSink()).setCapacity(
442 (int)mContentLength);
443 }
444
445 if (mOffset != 0) {
446 // The server may ignore the request for a byte range, in which case
447 // status code will be 200, instead of 206. Note that we cannot call
448 // getHttpStatusCode as it rewrites 206 into 200.
449 if (nativeGetHttpStatusCode(mUrlRequestAdapter) == 200) {
450 // TODO(mef): Revisit this logic.
451 if (mContentLength != -1) {
452 mContentLength -= mOffset;
453 }
454 mSkippingToOffset = true;
455 } else {
456 mSize = mOffset;
457 }
458 }
459 mListener.onResponseStarted(this);
460 } catch (Exception e) {
461 onCalledByNativeException(e);
416 } 462 }
417
418 if (mBufferFullResponse && mContentLength != -1
419 && !mContentLengthOverLimit) {
420 ((ChunkedWritableByteChannel)getSink()).setCapacity(
421 (int)mContentLength);
422 }
423
424 if (mOffset != 0) {
425 // The server may ignore the request for a byte range, in which case
426 // status code will be 200, instead of 206. Note that we cannot call
427 // getHttpStatusCode as it rewrites 206 into 200.
428 if (nativeGetHttpStatusCode(mUrlRequestAdapter) == 200) {
429 // TODO(mef): Revisit this logic.
430 if (mContentLength != -1) {
431 mContentLength -= mOffset;
432 }
433 mSkippingToOffset = true;
434 } else {
435 mSize = mOffset;
436 }
437 }
438 mListener.onResponseStarted(this);
439 } 463 }
440 464
441 /** 465 /**
442 * Consumes a portion of the response. 466 * Consumes a portion of the response.
443 * 467 *
444 * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and 468 * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and
445 * no references to it may be retained after the method ends, as 469 * no references to it may be retained after the method ends, as
446 * it wraps code managed on the native heap. 470 * it wraps code managed on the native heap.
447 */ 471 */
448 @CalledByNative 472 @CalledByNative
449 private void onBytesRead(ByteBuffer buffer) { 473 private void onBytesRead(ByteBuffer buffer) {
450 if (mContentLengthOverLimit) { 474 try {
451 return; 475 if (mContentLengthOverLimit) {
452 } 476 return;
477 }
453 478
454 int size = buffer.remaining(); 479 int size = buffer.remaining();
455 mSize += size; 480 mSize += size;
456 if (mSkippingToOffset) { 481 if (mSkippingToOffset) {
457 if (mSize <= mOffset) { 482 if (mSize <= mOffset) {
458 return; 483 return;
459 } else { 484 } else {
460 mSkippingToOffset = false; 485 mSkippingToOffset = false;
461 buffer.position((int)(mOffset - (mSize - size))); 486 buffer.position((int)(mOffset - (mSize - size)));
487 }
462 } 488 }
463 }
464 489
465 boolean contentLengthOverLimit = 490 boolean contentLengthOverLimit =
466 (mContentLengthLimit != 0 && mSize > mContentLengthLimit); 491 (mContentLengthLimit != 0 && mSize > mContentLengthLimit);
467 if (contentLengthOverLimit) { 492 if (contentLengthOverLimit) {
468 buffer.limit(size - (int)(mSize - mContentLengthLimit)); 493 buffer.limit(size - (int)(mSize - mContentLengthLimit));
469 } 494 }
470 495
471 try {
472 while (buffer.hasRemaining()) { 496 while (buffer.hasRemaining()) {
473 mSink.write(buffer); 497 mSink.write(buffer);
474 } 498 }
475 } catch (IOException e) { 499 if (contentLengthOverLimit) {
476 mSinkException = e; 500 onContentLengthOverLimit();
477 cancel(); 501 }
478 } 502 } catch (Exception e) {
479 if (contentLengthOverLimit) { 503 onCalledByNativeException(e);
480 onContentLengthOverLimit();
481 } 504 }
482 } 505 }
483 506
484 /** 507 /**
485 * Notifies the listener, releases native data structures. 508 * Notifies the listener, releases native data structures.
486 */ 509 */
487 @SuppressWarnings("unused") 510 @SuppressWarnings("unused")
488 @CalledByNative 511 @CalledByNative
489 private void finish() { 512 private void finish() {
490 synchronized (mLock) { 513 try {
491 mFinished = true; 514 synchronized (mLock) {
515 mFinished = true;
492 516
493 if (mRecycled) { 517 if (mRecycled) {
494 return; 518 return;
519 }
520 try {
521 mSink.close();
522 if (mUploadChannel != null && mUploadChannel.isOpen()) {
523 mUploadChannel.close();
524 }
525 } catch (IOException e) {
526 // Ignore
527 }
528 onRequestComplete();
529 nativeDestroyRequestAdapter(mUrlRequestAdapter);
530 mUrlRequestAdapter = 0;
531 mRecycled = true;
495 } 532 }
496 try { 533 } catch (Exception e) {
497 mSink.close(); 534 mSinkException = new IOException("Exception in finish", e);
mef 2014/08/15 17:15:01 Don't call onCalledByNativeException, as request i
498 } catch (IOException e) {
499 // Ignore
500 }
501 onRequestComplete();
502 nativeDestroyRequestAdapter(mUrlRequestAdapter);
503 mUrlRequestAdapter = 0;
504 mRecycled = true;
505 } 535 }
506 } 536 }
507 537
508 /** 538 /**
509 * Appends header |name| with value |value| to |headersMap|. 539 * Appends header |name| with value |value| to |headersMap|.
510 */ 540 */
511 @SuppressWarnings("unused") 541 @SuppressWarnings("unused")
512 @CalledByNative 542 @CalledByNative
513 private void onAppendResponseHeader(ResponseHeadersMap headersMap, 543 private void onAppendResponseHeader(ResponseHeadersMap headersMap,
514 String name, String value) { 544 String name, String value) {
515 if (!headersMap.containsKey(name)) { 545 try {
516 headersMap.put(name, new ArrayList<String>()); 546 if (!headersMap.containsKey(name)) {
547 headersMap.put(name, new ArrayList<String>());
548 }
549 headersMap.get(name).add(value);
550 } catch (Exception e) {
551 onCalledByNativeException(e);
517 } 552 }
518 headersMap.get(name).add(value);
519 } 553 }
520 554
521 /** 555 /**
522 * Reads a sequence of bytes from upload channel into the given buffer. 556 * Reads a sequence of bytes from upload channel into the given buffer.
523 * @param dest The buffer into which bytes are to be transferred. 557 * @param dest The buffer into which bytes are to be transferred.
524 * @return Returns number of bytes read (could be 0) or -1 and closes 558 * @return Returns number of bytes read (could be 0) or -1 and closes
525 * the channel if error occured. 559 * the channel if error occured.
526 */ 560 */
527 @SuppressWarnings("unused") 561 @SuppressWarnings("unused")
528 @CalledByNative 562 @CalledByNative
529 private int readFromUploadChannel(ByteBuffer dest) { 563 private int readFromUploadChannel(ByteBuffer dest) {
530 if (mUploadChannel == null || !mUploadChannel.isOpen())
531 return -1;
532 try { 564 try {
565 if (mUploadChannel == null || !mUploadChannel.isOpen())
566 return -1;
533 int result = mUploadChannel.read(dest); 567 int result = mUploadChannel.read(dest);
534 if (result < 0) { 568 if (result < 0) {
535 mUploadChannel.close(); 569 mUploadChannel.close();
536 return 0; 570 return 0;
537 } 571 }
538 return result; 572 return result;
539 } catch (IOException e) { 573 } catch (Exception e) {
540 mSinkException = e; 574 onCalledByNativeException(e);
541 try {
542 mUploadChannel.close();
543 } catch (IOException ignored) {
544 // Ignore this exception.
545 }
546 cancel();
547 return -1;
548 } 575 }
576 return -1;
549 } 577 }
550 578
551 // Native methods are implemented in chromium_url_request.cc. 579 // Native methods are implemented in chromium_url_request.cc.
552 580
553 private native long nativeCreateRequestAdapter( 581 private native long nativeCreateRequestAdapter(
554 long ChromiumUrlRequestContextAdapter, String url, int priority); 582 long ChromiumUrlRequestContextAdapter, String url, int priority);
555 583
556 private native void nativeAddHeader(long urlRequestAdapter, String name, 584 private native void nativeAddHeader(long urlRequestAdapter, String name,
557 String value); 585 String value);
558 586
(...skipping 23 matching lines...) Expand all
582 610
583 private native String nativeGetHeader(long urlRequestAdapter, String name); 611 private native String nativeGetHeader(long urlRequestAdapter, String name);
584 612
585 private native void nativeGetAllHeaders(long urlRequestAdapter, 613 private native void nativeGetAllHeaders(long urlRequestAdapter,
586 ResponseHeadersMap headers); 614 ResponseHeadersMap headers);
587 615
588 // Explicit class to work around JNI-generator generics confusion. 616 // Explicit class to work around JNI-generator generics confusion.
589 private class ResponseHeadersMap extends HashMap<String, List<String>> { 617 private class ResponseHeadersMap extends HashMap<String, List<String>> {
590 } 618 }
591 } 619 }
OLDNEW
« no previous file with comments | « no previous file | components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698