| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |