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

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

Issue 2903193002: [Cronet] Fix two races/leaks in JavaUrlRequest (Closed)
Patch Set: whoops, put back JavaUrlRequest.java Created 3 years, 6 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
« no previous file with comments | « no previous file | components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.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 2015 The Chromium Authors. All rights reserved. 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 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.impl; 5 package org.chromium.net.impl;
6 6
7 import android.annotation.SuppressLint; 7 import android.annotation.SuppressLint;
8 import android.annotation.TargetApi; 8 import android.annotation.TargetApi;
9 import android.net.TrafficStats; 9 import android.net.TrafficStats;
10 import android.os.Build; 10 import android.os.Build;
11
12 import android.util.Log; 11 import android.util.Log;
13 12
14 import org.chromium.net.CronetException; 13 import org.chromium.net.CronetException;
15 import org.chromium.net.InlineExecutionProhibitedException; 14 import org.chromium.net.InlineExecutionProhibitedException;
16 import org.chromium.net.UploadDataProvider; 15 import org.chromium.net.UploadDataProvider;
17 import org.chromium.net.UploadDataSink; 16 import org.chromium.net.UploadDataSink;
18 import org.chromium.net.UrlResponseInfo; 17 import org.chromium.net.UrlResponseInfo;
19 18
20 import java.io.Closeable;
21 import java.io.IOException; 19 import java.io.IOException;
22 import java.io.OutputStream; 20 import java.io.OutputStream;
23 import java.net.HttpURLConnection; 21 import java.net.HttpURLConnection;
24 import java.net.URI; 22 import java.net.URI;
25 import java.net.URL; 23 import java.net.URL;
26 import java.nio.ByteBuffer; 24 import java.nio.ByteBuffer;
27 import java.nio.channels.Channels; 25 import java.nio.channels.Channels;
28 import java.nio.channels.ReadableByteChannel; 26 import java.nio.channels.ReadableByteChannel;
29 import java.nio.channels.WritableByteChannel; 27 import java.nio.channels.WritableByteChannel;
30 import java.util.AbstractMap.SimpleEntry; 28 import java.util.AbstractMap.SimpleEntry;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 * 89 *
92 * <p>Concurrency notes - this value is not atomically updated with mState, so there is some 90 * <p>Concurrency notes - this value is not atomically updated with mState, so there is some
93 * risk that we'd get an inconsistent snapshot of both - however, it also ha ppens that this 91 * risk that we'd get an inconsistent snapshot of both - however, it also ha ppens that this
94 * value is only used with the STARTED state, so it's inconsequential. 92 * value is only used with the STARTED state, so it's inconsequential.
95 */ 93 */
96 @StatusValues 94 @StatusValues
97 private volatile int mAdditionalStatusDetails = Status.INVALID; 95 private volatile int mAdditionalStatusDetails = Status.INVALID;
98 96
99 /* These change with redirects. */ 97 /* These change with redirects. */
100 private String mCurrentUrl; 98 private String mCurrentUrl;
101 private ReadableByteChannel mResponseChannel; 99 private ReadableByteChannel mResponseChannel; // Only accessed on mExecutor.
102 private UrlResponseInfoImpl mUrlResponseInfo; 100 private UrlResponseInfoImpl mUrlResponseInfo;
103 private String mPendingRedirectUrl; 101 private String mPendingRedirectUrl;
104 /** 102 private HttpURLConnection mCurrentUrlConnection; // Only accessed on mExecut or.
105 * The happens-before edges created by the executor submission and AtomicRef erence setting are
106 * sufficient to guarantee the correct behavior of this field; however, this is an
107 * AtomicReference so that we can cleanly dispose of a new connection if we' re cancelled during
108 * a redirect, which requires get-and-set semantics.
109 * */
110 private final AtomicReference<HttpURLConnection> mCurrentUrlConnection =
111 new AtomicReference<>();
112 103
113 /** 104 /**
114 * /- AWAITING_FOLLOW_REDIRECT <- REDIRECT_RECEIVED <-\ /- READING <--\ 105 * /- AWAITING_FOLLOW_REDIRECT <- REDIRECT_RECEIVED <-\ /- READING <--\
115 * | | | | 106 * | | | |
116 * \ / \ / 107 * \ / \ /
117 * NOT_STARTED ---> STARTED ----> AW AITING_READ ---> 108 * NOT_STARTED ---> STARTED ----> AW AITING_READ --->
118 * COMPLETE 109 * COMPLETE
119 */ 110 */
120 private enum State { 111 private enum State {
121 NOT_STARTED, 112 NOT_STARTED,
(...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 fireOpenConnection(); 569 fireOpenConnection();
579 } 570 }
580 }); 571 });
581 } 572 }
582 573
583 private void fireGetHeaders() { 574 private void fireGetHeaders() {
584 mAdditionalStatusDetails = Status.WAITING_FOR_RESPONSE; 575 mAdditionalStatusDetails = Status.WAITING_FOR_RESPONSE;
585 mExecutor.execute(errorSetting(new CheckedRunnable() { 576 mExecutor.execute(errorSetting(new CheckedRunnable() {
586 @Override 577 @Override
587 public void run() throws Exception { 578 public void run() throws Exception {
588 HttpURLConnection connection = mCurrentUrlConnection.get(); 579 if (mCurrentUrlConnection == null) {
589 if (connection == null) {
590 return; // We've been cancelled 580 return; // We've been cancelled
591 } 581 }
592 final List<Map.Entry<String, String>> headerList = new ArrayList <>(); 582 final List<Map.Entry<String, String>> headerList = new ArrayList <>();
593 String selectedTransport = "http/1.1"; 583 String selectedTransport = "http/1.1";
594 String headerKey; 584 String headerKey;
595 for (int i = 0; (headerKey = connection.getHeaderFieldKey(i)) != null; i++) { 585 for (int i = 0; (headerKey = mCurrentUrlConnection.getHeaderFiel dKey(i)) != null;
586 i++) {
596 if (X_ANDROID_SELECTED_TRANSPORT.equalsIgnoreCase(headerKey) ) { 587 if (X_ANDROID_SELECTED_TRANSPORT.equalsIgnoreCase(headerKey) ) {
597 selectedTransport = connection.getHeaderField(i); 588 selectedTransport = mCurrentUrlConnection.getHeaderField (i);
598 } 589 }
599 if (!headerKey.startsWith(X_ANDROID)) { 590 if (!headerKey.startsWith(X_ANDROID)) {
600 headerList.add(new SimpleEntry<>(headerKey, connection.g etHeaderField(i))); 591 headerList.add(new SimpleEntry<>(
592 headerKey, mCurrentUrlConnection.getHeaderField( i)));
601 } 593 }
602 } 594 }
603 595
604 int responseCode = connection.getResponseCode(); 596 int responseCode = mCurrentUrlConnection.getResponseCode();
605 // Important to copy the list here, because although we never co ncurrently modify 597 // Important to copy the list here, because although we never co ncurrently modify
606 // the list ourselves, user code might iterate over it while we' re redirecting, and 598 // the list ourselves, user code might iterate over it while we' re redirecting, and
607 // that would throw ConcurrentModificationException. 599 // that would throw ConcurrentModificationException.
608 mUrlResponseInfo = new UrlResponseInfoImpl(new ArrayList<>(mUrlC hain), responseCode, 600 mUrlResponseInfo = new UrlResponseInfoImpl(new ArrayList<>(mUrlC hain), responseCode,
609 connection.getResponseMessage(), Collections.unmodifiabl eList(headerList), 601 mCurrentUrlConnection.getResponseMessage(),
610 false, selectedTransport, ""); 602 Collections.unmodifiableList(headerList), false, selecte dTransport, "");
611 // TODO(clm) actual redirect handling? post -> get and whatnot? 603 // TODO(clm) actual redirect handling? post -> get and whatnot?
612 if (responseCode >= 300 && responseCode < 400) { 604 if (responseCode >= 300 && responseCode < 400) {
613 fireRedirectReceived(mUrlResponseInfo.getAllHeaders()); 605 fireRedirectReceived(mUrlResponseInfo.getAllHeaders());
614 return; 606 return;
615 } 607 }
616 fireCloseUploadDataProvider(); 608 fireCloseUploadDataProvider();
617 if (responseCode >= 400) { 609 if (responseCode >= 400) {
618 mResponseChannel = InputStreamChannel.wrap(connection.getErr orStream()); 610 mResponseChannel =
611 InputStreamChannel.wrap(mCurrentUrlConnection.getErr orStream());
619 mCallbackAsync.onResponseStarted(mUrlResponseInfo); 612 mCallbackAsync.onResponseStarted(mUrlResponseInfo);
620 } else { 613 } else {
621 mResponseChannel = InputStreamChannel.wrap(connection.getInp utStream()); 614 mResponseChannel =
615 InputStreamChannel.wrap(mCurrentUrlConnection.getInp utStream());
622 mCallbackAsync.onResponseStarted(mUrlResponseInfo); 616 mCallbackAsync.onResponseStarted(mUrlResponseInfo);
623 } 617 }
624 } 618 }
625 })); 619 }));
626 } 620 }
627 621
628 private void fireCloseUploadDataProvider() { 622 private void fireCloseUploadDataProvider() {
629 if (mUploadDataProvider != null && mUploadProviderClosed.compareAndSet(f alse, true)) { 623 if (mUploadDataProvider != null && mUploadProviderClosed.compareAndSet(f alse, true)) {
630 try { 624 try {
631 mUploadExecutor.execute(uploadErrorSetting(new CheckedRunnable() { 625 mUploadExecutor.execute(uploadErrorSetting(new CheckedRunnable() {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
664 mExecutor.execute(errorSetting(new CheckedRunnable() { 658 mExecutor.execute(errorSetting(new CheckedRunnable() {
665 @Override 659 @Override
666 public void run() throws Exception { 660 public void run() throws Exception {
667 // If we're cancelled, then our old connection will be disconnec ted for us and 661 // If we're cancelled, then our old connection will be disconnec ted for us and
668 // we shouldn't open a new one. 662 // we shouldn't open a new one.
669 if (mState.get() == State.CANCELLED) { 663 if (mState.get() == State.CANCELLED) {
670 return; 664 return;
671 } 665 }
672 666
673 final URL url = new URL(mCurrentUrl); 667 final URL url = new URL(mCurrentUrl);
674 HttpURLConnection newConnection = (HttpURLConnection) url.openCo nnection(); 668 if (mCurrentUrlConnection != null) {
675 HttpURLConnection oldConnection = mCurrentUrlConnection.getAndSe t(newConnection); 669 mCurrentUrlConnection.disconnect();
676 if (oldConnection != null) { 670 mCurrentUrlConnection = null;
677 oldConnection.disconnect();
678 } 671 }
679 newConnection.setInstanceFollowRedirects(false); 672 mCurrentUrlConnection = (HttpURLConnection) url.openConnection() ;
673 mCurrentUrlConnection.setInstanceFollowRedirects(false);
680 if (!mRequestHeaders.containsKey(USER_AGENT)) { 674 if (!mRequestHeaders.containsKey(USER_AGENT)) {
681 mRequestHeaders.put(USER_AGENT, mUserAgent); 675 mRequestHeaders.put(USER_AGENT, mUserAgent);
682 } 676 }
683 for (Map.Entry<String, String> entry : mRequestHeaders.entrySet( )) { 677 for (Map.Entry<String, String> entry : mRequestHeaders.entrySet( )) {
684 newConnection.setRequestProperty(entry.getKey(), entry.getVa lue()); 678 mCurrentUrlConnection.setRequestProperty(entry.getKey(), ent ry.getValue());
685 } 679 }
686 if (mInitialMethod == null) { 680 if (mInitialMethod == null) {
687 mInitialMethod = "GET"; 681 mInitialMethod = "GET";
688 } 682 }
689 newConnection.setRequestMethod(mInitialMethod); 683 mCurrentUrlConnection.setRequestMethod(mInitialMethod);
690 if (mUploadDataProvider != null) { 684 if (mUploadDataProvider != null) {
691 OutputStreamDataSink dataSink = new OutputStreamDataSink( 685 OutputStreamDataSink dataSink = new OutputStreamDataSink(
692 mUploadExecutor, mExecutor, newConnection, mUploadDa taProvider); 686 mUploadExecutor, mExecutor, mCurrentUrlConnection, m UploadDataProvider);
693 dataSink.start(mUrlChain.size() == 1); 687 dataSink.start(mUrlChain.size() == 1);
694 } else { 688 } else {
695 mAdditionalStatusDetails = Status.CONNECTING; 689 mAdditionalStatusDetails = Status.CONNECTING;
696 newConnection.connect(); 690 mCurrentUrlConnection.connect();
697 fireGetHeaders(); 691 fireGetHeaders();
698 } 692 }
699 } 693 }
700 })); 694 }));
701 } 695 }
702 696
703 private Runnable errorSetting(final CheckedRunnable delegate) { 697 private Runnable errorSetting(final CheckedRunnable delegate) {
704 return new Runnable() { 698 return new Runnable() {
705 @Override 699 @Override
706 public void run() { 700 public void run() {
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
765 } else { 759 } else {
766 mResponseChannel.close(); 760 mResponseChannel.close();
767 if (mState.compareAndSet(State.READING, State.COMPLETE)) { 761 if (mState.compareAndSet(State.READING, State.COMPLETE)) {
768 fireDisconnect(); 762 fireDisconnect();
769 mCallbackAsync.onSucceeded(mUrlResponseInfo); 763 mCallbackAsync.onSucceeded(mUrlResponseInfo);
770 } 764 }
771 } 765 }
772 } 766 }
773 767
774 private void fireDisconnect() { 768 private void fireDisconnect() {
775 final HttpURLConnection connection = mCurrentUrlConnection.getAndSet(nul l); 769 mExecutor.execute(new Runnable() {
776 if (connection != null) { 770 @Override
777 mExecutor.execute(new Runnable() { 771 public void run() {
778 @Override 772 if (mCurrentUrlConnection != null) {
779 public void run() { 773 mCurrentUrlConnection.disconnect();
780 connection.disconnect(); 774 mCurrentUrlConnection = null;
781 } 775 }
782 }); 776 }
783 } 777 });
784 } 778 }
785 779
786 @Override 780 @Override
787 public void cancel() { 781 public void cancel() {
788 State oldState = mState.getAndSet(State.CANCELLED); 782 State oldState = mState.getAndSet(State.CANCELLED);
789 switch (oldState) { 783 switch (oldState) {
790 // We've just scheduled some user code to run. When they perform the ir next operation, 784 // We've just scheduled some user code to run. When they perform the ir next operation,
791 // they'll observe it and fail. However, if user code is cancelling in response to one 785 // they'll observe it and fail. However, if user code is cancelling in response to one
792 // of these callbacks, we'll never actually cancel! 786 // of these callbacks, we'll never actually cancel!
793 // TODO(clm) figure out if it's possible to avoid concurrency in use r callbacks. 787 // TODO(clm) figure out if it's possible to avoid concurrency in use r callbacks.
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
958 mUserExecutor.execute(runnable); 952 mUserExecutor.execute(runnable);
959 } catch (InlineExecutionProhibitedException wasDirect) { 953 } catch (InlineExecutionProhibitedException wasDirect) {
960 if (mFallbackExecutor != null) { 954 if (mFallbackExecutor != null) {
961 mFallbackExecutor.execute(runnable); 955 mFallbackExecutor.execute(runnable);
962 } 956 }
963 } 957 }
964 } 958 }
965 } 959 }
966 960
967 private void closeResponseChannel() { 961 private void closeResponseChannel() {
968 final Closeable closeable = mResponseChannel;
969 if (closeable == null) {
970 return;
971 }
972 mResponseChannel = null;
973 mExecutor.execute(new Runnable() { 962 mExecutor.execute(new Runnable() {
974 @Override 963 @Override
975 public void run() { 964 public void run() {
976 try { 965 if (mResponseChannel != null) {
977 closeable.close(); 966 try {
978 } catch (IOException e) { 967 mResponseChannel.close();
979 e.printStackTrace(); 968 } catch (IOException e) {
969 e.printStackTrace();
970 }
971 mResponseChannel = null;
980 } 972 }
981 } 973 }
982 }); 974 });
983 } 975 }
984 976
985 /** 977 /**
986 * Executor that detects and throws if its mDelegate runs a submitted runnab le inline. 978 * Executor that detects and throws if its mDelegate runs a submitted runnab le inline.
987 */ 979 */
988 static final class DirectPreventingExecutor implements Executor { 980 static final class DirectPreventingExecutor implements Executor {
989 private final Executor mDelegate; 981 private final Executor mDelegate;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1025 // Can't throw directly from here, since the delegate execut or could catch this 1017 // Can't throw directly from here, since the delegate execut or could catch this
1026 // exception. 1018 // exception.
1027 mExecutedInline = new InlineExecutionProhibitedException(); 1019 mExecutedInline = new InlineExecutionProhibitedException();
1028 return; 1020 return;
1029 } 1021 }
1030 mCommand.run(); 1022 mCommand.run();
1031 } 1023 }
1032 } 1024 }
1033 } 1025 }
1034 } 1026 }
OLDNEW
« no previous file with comments | « no previous file | components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698