core,netty,okhttp: detect proxy via ProxySelector (#3021)

This lets us specify the proxy using `-Dhttps.proxyHost=host -Dhttps.proxyPort=port`
along with auth info like username and password.
This commit is contained in:
zpencer 2017-10-17 19:26:11 -07:00 committed by GitHub
parent b9f6590084
commit 7df9ae9753
27 changed files with 775 additions and 132 deletions

View File

@ -23,6 +23,7 @@ import io.grpc.internal.AbstractManagedChannelImplBuilder;
import io.grpc.internal.ClientTransportFactory;
import io.grpc.internal.ConnectionClientTransport;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.ProxyParameters;
import io.grpc.internal.SharedResourceHolder;
import java.net.SocketAddress;
import java.util.concurrent.ScheduledExecutorService;
@ -128,7 +129,7 @@ public final class InProcessChannelBuilder extends
@Override
public ConnectionClientTransport newClientTransport(
SocketAddress addr, String authority, String userAgent) {
SocketAddress addr, String authority, String userAgent, ProxyParameters proxy) {
if (closed) {
throw new IllegalStateException("The transport factory is closed.");
}

View File

@ -116,6 +116,8 @@ public abstract class AbstractManagedChannelImplBuilder
String authorityOverride;
private ProxyDetector proxyDetector = ProxyDetector.DEFAULT_INSTANCE;
LoadBalancer.Factory loadBalancerFactory = DEFAULT_LOAD_BALANCER_FACTORY;
boolean fullStreamDecompression;
@ -335,7 +337,8 @@ public abstract class AbstractManagedChannelImplBuilder
new ExponentialBackoffPolicy.Provider(),
SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
GrpcUtil.STOPWATCH_SUPPLIER,
getEffectiveInterceptors());
getEffectiveInterceptors(),
proxyDetector);
}
@VisibleForTesting

View File

@ -42,9 +42,10 @@ final class CallCredentialsApplyingTransportFactory implements ClientTransportFa
@Override
public ConnectionClientTransport newClientTransport(
SocketAddress serverAddress, String authority, @Nullable String userAgent) {
SocketAddress serverAddress, String authority, @Nullable String userAgent,
@Nullable ProxyParameters proxy) {
return new CallCredentialsApplyingTransport(
delegate.newClientTransport(serverAddress, authority, userAgent), authority);
delegate.newClientTransport(serverAddress, authority, userAgent, proxy), authority);
}
@Override

View File

@ -28,9 +28,10 @@ public interface ClientTransportFactory extends Closeable {
*
* @param serverAddress the address that the transport is connected to
* @param authority the HTTP/2 authority of the server
* @param proxy the proxy that should be used to connect to serverAddress
*/
ConnectionClientTransport newClientTransport(SocketAddress serverAddress, String authority,
@Nullable String userAgent);
@Nullable String userAgent, @Nullable ProxyParameters proxy);
/**
* Returns an executor for scheduling provided by the transport. The service should be configured

View File

@ -69,6 +69,7 @@ final class DnsNameResolver extends NameResolver {
private final int port;
private final Resource<ScheduledExecutorService> timerServiceResource;
private final Resource<ExecutorService> executorResource;
private final ProxyDetector proxyDetector;
@GuardedBy("this")
private boolean shutdown;
@GuardedBy("this")
@ -84,7 +85,8 @@ final class DnsNameResolver extends NameResolver {
DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params,
Resource<ScheduledExecutorService> timerServiceResource,
Resource<ExecutorService> executorResource) {
Resource<ExecutorService> executorResource,
ProxyDetector proxyDetector) {
// TODO: if a DNS server is provided as nsAuthority, use it.
// https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java
this.timerServiceResource = timerServiceResource;
@ -106,6 +108,7 @@ final class DnsNameResolver extends NameResolver {
} else {
port = nameUri.getPort();
}
this.proxyDetector = proxyDetector;
}
@Override
@ -145,9 +148,10 @@ final class DnsNameResolver extends NameResolver {
resolving = true;
}
try {
if (System.getenv("GRPC_PROXY_EXP") != null) {
EquivalentAddressGroup server =
new EquivalentAddressGroup(InetSocketAddress.createUnresolved(host, port));
InetSocketAddress destination = InetSocketAddress.createUnresolved(host, port);
ProxyParameters proxy = proxyDetector.proxyFor(destination);
if (proxy != null) {
EquivalentAddressGroup server = new EquivalentAddressGroup(destination);
savedListener.onAddresses(Collections.singletonList(server), Attributes.EMPTY);
return;
}

View File

@ -48,7 +48,7 @@ public final class DnsNameResolverProvider extends NameResolverProvider {
"the path component (%s) of the target (%s) must start with '/'", targetPath, targetUri);
String name = targetPath.substring(1);
return new DnsNameResolver(targetUri.getAuthority(), name, params, GrpcUtil.TIMER_SERVICE,
GrpcUtil.SHARED_CHANNEL_EXECUTOR);
GrpcUtil.SHARED_CHANNEL_EXECUTOR, ProxyDetector.DEFAULT_INSTANCE);
} else {
return null;
}

View File

@ -39,8 +39,10 @@ import io.grpc.internal.SharedResourceHolder.Resource;
import io.grpc.internal.StreamListener.MessageProducer;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
@ -534,6 +536,25 @@ public final class GrpcUtil {
}
};
/**
* Returns the host via {@link InetSocketAddress#getHostString} if it is possible,
* i.e. in jdk >= 7.
* Otherwise, return it via {@link InetSocketAddress#getHostName} which may incur a DNS lookup.
*/
public static String getHost(InetSocketAddress addr) {
try {
Method getHostStringMethod = InetSocketAddress.class.getMethod("getHostString");
return (String) getHostStringMethod.invoke(addr);
} catch (NoSuchMethodException e) {
// noop
} catch (IllegalAccessException e) {
// noop
} catch (InvocationTargetException e) {
// noop
}
return addr.getHostName();
}
/**
* Marshals a nanoseconds representation of the timeout to and from a string representation,
* consisting of an ASCII decimal representation of a number with at most 8 digits, followed by a

View File

@ -137,13 +137,17 @@ final class InternalSubchannel implements WithLogId {
@GuardedBy("lock")
private ConnectivityStateInfo state = ConnectivityStateInfo.forNonError(IDLE);
private final ProxyDetector proxyDetector;
@GuardedBy("lock")
private Status shutdownReason;
InternalSubchannel(EquivalentAddressGroup addressGroup, String authority, String userAgent,
BackoffPolicy.Provider backoffPolicyProvider,
ClientTransportFactory transportFactory, ScheduledExecutorService scheduledExecutor,
Supplier<Stopwatch> stopwatchSupplier, ChannelExecutor channelExecutor, Callback callback) {
Supplier<Stopwatch> stopwatchSupplier, ChannelExecutor channelExecutor, Callback callback,
ProxyDetector proxyDetector) {
this.addressGroup = Preconditions.checkNotNull(addressGroup, "addressGroup");
this.authority = authority;
this.userAgent = userAgent;
@ -153,6 +157,7 @@ final class InternalSubchannel implements WithLogId {
this.connectingTimer = stopwatchSupplier.get();
this.channelExecutor = channelExecutor;
this.callback = callback;
this.proxyDetector = proxyDetector;
}
/**
@ -194,8 +199,9 @@ final class InternalSubchannel implements WithLogId {
List<SocketAddress> addrs = addressGroup.getAddresses();
final SocketAddress address = addrs.get(addressIndex);
ProxyParameters proxy = proxyDetector.proxyFor(address);
ConnectionClientTransport transport =
transportFactory.newClientTransport(address, authority, userAgent);
transportFactory.newClientTransport(address, authority, userAgent, proxy);
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "[{0}] Created {1} for {2}",
new Object[] {logId, transport.getLogId(), address});

View File

@ -133,6 +133,8 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI
// Only null after channel is terminated. Must be assigned from the channelExecutor.
private NameResolver nameResolver;
private final ProxyDetector proxyDetector;
// null when channel is in idle mode. Must be assigned from channelExecutor.
@Nullable
private LbHelperImpl lbHelper;
@ -386,7 +388,8 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI
BackoffPolicy.Provider backoffPolicyProvider,
ObjectPool<? extends Executor> oobExecutorPool,
Supplier<Stopwatch> stopwatchSupplier,
List<ClientInterceptor> interceptors) {
List<ClientInterceptor> interceptors,
ProxyDetector proxyDetector) {
this.target = checkNotNull(builder.target, "target");
this.nameResolverFactory = builder.getNameResolverFactory();
this.nameResolverParams = checkNotNull(builder.getNameResolverParams(), "nameResolverParams");
@ -416,6 +419,7 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI
this.decompressorRegistry = checkNotNull(builder.decompressorRegistry, "decompressorRegistry");
this.compressorRegistry = checkNotNull(builder.compressorRegistry, "compressorRegistry");
this.userAgent = builder.userAgent;
this.proxyDetector = proxyDetector;
phantom = new ManagedChannelReference(this);
logger.log(Level.FINE, "[{0}] Created with target {1}", new Object[] {getLogId(), target});
@ -674,7 +678,8 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI
void onNotInUse(InternalSubchannel is) {
inUseStateAggregator.updateObjectInUse(is, false);
}
});
},
proxyDetector);
subchannel.subchannel = internalSubchannel;
logger.log(Level.FINE, "[{0}] {1} created for {2}",
new Object[] {getLogId(), internalSubchannel.getLogId(), addressGroup});
@ -754,7 +759,8 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI
void onStateChange(InternalSubchannel is, ConnectivityStateInfo newState) {
oobChannel.handleSubchannelStateChange(newState);
}
});
},
proxyDetector);
oobChannel.setSubchannel(internalSubchannel);
runSerialized(new Runnable() {
@Override

View File

@ -0,0 +1,44 @@
/*
* Copyright 2017, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.internal;
import java.net.SocketAddress;
import javax.annotation.Nullable;
/**
* A utility class to detect which proxy, if any, should be used for a given
* {@link java.net.SocketAddress}.
*/
public interface ProxyDetector {
ProxyDetector DEFAULT_INSTANCE = new ProxyDetectorImpl();
/** A proxy detector that always claims no proxy is needed, for unit test convenience. */
ProxyDetector NOOP_INSTANCE = new ProxyDetector() {
@Nullable
@Override
public ProxyParameters proxyFor(SocketAddress targetServerAddress) {
return null;
}
};
/**
* Given a target address, returns which proxy address should be used. If no proxy should be
* used, then return value will be null.
*/
@Nullable
ProxyParameters proxyFor(SocketAddress targetServerAddress);
}

View File

@ -0,0 +1,202 @@
/*
* Copyright 2017, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import java.net.Authenticator;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* A utility class that detects proxies using {@link ProxySelector} and detects authentication
* credentials using {@link Authenticator}.
*
*/
class ProxyDetectorImpl implements ProxyDetector {
private static final Logger log = Logger.getLogger(ProxyDetectorImpl.class.getName());
private static final AuthenticationProvider DEFAULT_AUTHENTICATOR = new AuthenticationProvider() {
@Override
public PasswordAuthentication requestPasswordAuthentication(
String host, InetAddress addr, int port, String protocol, String prompt, String scheme) {
URL url = null;
try {
url = new URL(protocol, host, "");
} catch (MalformedURLException e) {
// let url be null
log.log(
Level.WARNING,
String.format("failed to create URL for Authenticator: %s %s", protocol, host));
}
// TODO(spencerfang): consider using java.security.AccessController here
return Authenticator.requestPasswordAuthentication(
host, addr, port, protocol, prompt, scheme, url, Authenticator.RequestorType.PROXY);
}
};
private static final Supplier<ProxySelector> DEFAULT_PROXY_SELECTOR =
new Supplier<ProxySelector>() {
@Override
public ProxySelector get() {
// TODO(spencerfang): consider using java.security.AccessController here
return ProxySelector.getDefault();
}
};
/**
* @deprecated Use the standard Java proxy configuration instead with flags such as:
* -Dhttps.proxyHost=HOST -Dhttps.proxyPort=PORT
*/
@Deprecated
private static final String GRPC_PROXY_ENV_VAR = "GRPC_PROXY_EXP";
// Do not hard code a ProxySelector because the global default ProxySelector can change
private final Supplier<ProxySelector> proxySelector;
private final AuthenticationProvider authenticationProvider;
private final ProxyParameters override;
// We want an HTTPS proxy, which operates on the entire data stream (See IETF rfc2817).
static final String PROXY_SCHEME = "https";
/**
* A proxy selector that uses the global {@link ProxySelector#getDefault()} and
* {@link ProxyDetectorImpl.AuthenticationProvider} to detect proxy parameters.
*/
public ProxyDetectorImpl() {
this(DEFAULT_PROXY_SELECTOR, DEFAULT_AUTHENTICATOR, System.getenv(GRPC_PROXY_ENV_VAR));
}
@VisibleForTesting
ProxyDetectorImpl(
Supplier<ProxySelector> proxySelector,
AuthenticationProvider authenticationProvider,
@Nullable String proxyEnvString) {
this.proxySelector = checkNotNull(proxySelector);
this.authenticationProvider = checkNotNull(authenticationProvider);
if (proxyEnvString != null) {
override = new ProxyParameters(overrideProxy(proxyEnvString), null, null);
} else {
override = null;
}
}
@Nullable
@Override
public ProxyParameters proxyFor(SocketAddress targetServerAddress) {
if (override != null) {
return override;
}
if (!(targetServerAddress instanceof InetSocketAddress)) {
return null;
}
return detectProxy((InetSocketAddress) targetServerAddress);
}
private ProxyParameters detectProxy(InetSocketAddress targetAddr) {
URI uri;
try {
uri = new URI(
PROXY_SCHEME,
null, /* userInfo */
targetAddr.getHostName(),
targetAddr.getPort(),
null, /* path */
null, /* query */
null /* fragment */);
} catch (final URISyntaxException e) {
log.log(
Level.WARNING,
"Failed to construct URI for proxy lookup, proceeding without proxy",
e);
return null;
}
List<Proxy> proxies = proxySelector.get().select(uri);
if (proxies.size() > 1) {
log.warning("More than 1 proxy detected, gRPC will select the first one");
}
Proxy proxy = proxies.get(0);
if (proxy.type() == Proxy.Type.DIRECT) {
return null;
}
InetSocketAddress proxyAddr = (InetSocketAddress) proxy.address();
// The prompt string should be the realm as returned by the server.
// We don't have it because we are avoiding the full handshake.
String promptString = "";
PasswordAuthentication auth = authenticationProvider.requestPasswordAuthentication(
GrpcUtil.getHost(proxyAddr),
proxyAddr.getAddress(),
proxyAddr.getPort(),
PROXY_SCHEME,
promptString,
null);
if (auth == null) {
return new ProxyParameters(proxyAddr, null, null);
}
// TODO(spencerfang): users of ProxyParameters should clear the password when done
return new ProxyParameters(proxyAddr, auth.getUserName(), new String(auth.getPassword()));
}
/**
* GRPC_PROXY_EXP is deprecated but let's maintain compatibility for now.
*/
private static InetSocketAddress overrideProxy(String proxyHostPort) {
if (proxyHostPort == null) {
return null;
}
String[] parts = proxyHostPort.split(":", 2);
int port = 80;
if (parts.length > 1) {
port = Integer.parseInt(parts[1]);
}
log.warning(
"Detected GRPC_PROXY_EXP and will honor it, but this feature will "
+ "be removed in a future release. Use the JVM flags "
+ "\"-Dhttps.proxyHost=HOST -Dhttps.proxyPort=PORT\" to set the https proxy for "
+ "this JVM.");
return new InetSocketAddress(parts[0], port);
}
/**
* This interface makes unit testing easier by avoiding direct calls to static methods.
*/
interface AuthenticationProvider {
PasswordAuthentication requestPasswordAuthentication(
String host,
InetAddress addr,
int port,
String protocol,
String prompt,
String scheme);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2017, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.internal;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import java.net.InetSocketAddress;
import javax.annotation.Nullable;
/**
* Used to express the result of a proxy lookup.
*/
public final class ProxyParameters {
public final InetSocketAddress proxyAddress;
@Nullable public final String username;
@Nullable public final String password;
ProxyParameters(
InetSocketAddress proxyAddress,
@Nullable String username,
@Nullable String password) {
this.proxyAddress = Preconditions.checkNotNull(proxyAddress);
this.username = username;
this.password = password;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ProxyParameters)) {
return false;
}
ProxyParameters that = (ProxyParameters) o;
return Objects.equal(proxyAddress, that.proxyAddress)
&& Objects.equal(username, that.username)
&& Objects.equal(password, that.password);
}
@Override
public int hashCode() {
return Objects.hashCode(proxyAddress, username, password);
}
}

View File

@ -75,6 +75,7 @@ public class CallCredentialsApplyingTest {
private static final String AUTHORITY = "testauthority";
private static final String USER_AGENT = "testuseragent";
private static final ProxyParameters NO_PROXY = null;
private static final Attributes.Key<String> ATTR_KEY = Attributes.Key.of("somekey");
private static final String ATTR_VALUE = "somevalue";
private static final MethodDescriptor<String, Integer> method =
@ -99,16 +100,16 @@ public class CallCredentialsApplyingTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
origHeaders.put(ORIG_HEADER_KEY, ORIG_HEADER_VALUE);
when(mockTransportFactory.newClientTransport(address, AUTHORITY, USER_AGENT))
when(mockTransportFactory.newClientTransport(address, AUTHORITY, USER_AGENT, NO_PROXY))
.thenReturn(mockTransport);
when(mockTransport.newStream(same(method), any(Metadata.class), any(CallOptions.class)))
.thenReturn(mockStream);
ClientTransportFactory transportFactory = new CallCredentialsApplyingTransportFactory(
mockTransportFactory, mockExecutor);
transport = (ForwardingConnectionClientTransport) transportFactory.newClientTransport(
address, AUTHORITY, USER_AGENT);
address, AUTHORITY, USER_AGENT, NO_PROXY);
callOptions = CallOptions.DEFAULT.withCallCredentials(mockCreds);
verify(mockTransportFactory).newClientTransport(address, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(address, AUTHORITY, USER_AGENT, NO_PROXY);
assertSame(mockTransport, transport.delegate());
}

View File

@ -20,11 +20,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Iterables;
@ -37,6 +41,7 @@ import io.grpc.internal.DnsNameResolver.ResolutionResults;
import io.grpc.internal.SharedResourceHolder.Resource;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
@ -107,13 +112,22 @@ public class DnsNameResolverTest {
private ArgumentCaptor<Status> statusCaptor;
private DnsNameResolver newResolver(String name, int port) {
return newResolver(name, port, mockResolver, ProxyDetector.NOOP_INSTANCE);
}
private DnsNameResolver newResolver(
String name,
int port,
DelegateResolver delegateResolver,
ProxyDetector proxyDetector) {
DnsNameResolver dnsResolver = new DnsNameResolver(
null,
name,
Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(),
fakeTimerServiceResource,
fakeExecutorResource);
dnsResolver.setDelegateResolver(mockResolver);
fakeExecutorResource,
proxyDetector);
dnsResolver.setDelegateResolver(delegateResolver);
return dnsResolver;
}
@ -335,6 +349,32 @@ public class DnsNameResolverTest {
assertThat(results.txtRecords).isEmpty();
}
@Test
public void doNotResolveWhenProxyDetected() throws Exception {
final String name = "foo.googleapis.com";
final int port = 81;
ProxyDetector alwaysDetectProxy = mock(ProxyDetector.class);
ProxyParameters proxyParameters = new ProxyParameters(
InetSocketAddress.createUnresolved("proxy.example.com", 1000),
"username",
"password");
when(alwaysDetectProxy.proxyFor(any(SocketAddress.class)))
.thenReturn(proxyParameters);
DelegateResolver unusedResolver = mock(DelegateResolver.class);
DnsNameResolver resolver = newResolver(name, port, unusedResolver, alwaysDetectProxy);
resolver.start(mockListener);
assertEquals(1, fakeExecutor.runDueTasks());
verify(unusedResolver, never()).resolve(any(String.class));
verify(mockListener).onAddresses(resultCaptor.capture(), any(Attributes.class));
List<EquivalentAddressGroup> result = resultCaptor.getValue();
assertThat(result).hasSize(1);
EquivalentAddressGroup eag = result.get(0);
assertThat(eag.getAddresses()).hasSize(1);
SocketAddress socketAddress = eag.getAddresses().get(0);
assertTrue(((InetSocketAddress) socketAddress).isUnresolved());
}
private void testInvalidUri(URI uri) {
try {
provider.newNameResolver(uri, NAME_RESOLVER_PARAMS);

View File

@ -28,6 +28,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@ -36,15 +37,18 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import io.grpc.ConnectivityState;
import io.grpc.ConnectivityStateInfo;
import io.grpc.EquivalentAddressGroup;
import io.grpc.Status;
import io.grpc.internal.TestUtils.MockClientTransportInfo;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -61,6 +65,7 @@ public class InternalSubchannelTest {
private static final String AUTHORITY = "fakeauthority";
private static final String USER_AGENT = "mosaic";
private static final ProxyParameters NO_PROXY = null;
private static final ConnectivityStateInfo UNAVAILABLE_STATE =
ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE);
private static final ConnectivityStateInfo RESOURCE_EXHAUSTED_STATE =
@ -145,7 +150,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one. Because there is only one address to try, enter TRANSIENT_FAILURE.
assertNoCallbackInvoke();
@ -161,7 +166,7 @@ public class InternalSubchannelTest {
fakeClock.forwardNanos(9);
assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke();
@ -169,7 +174,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too
assertNoCallbackInvoke();
// Here we use a different status from the first failure, and verify that it's passed to
@ -186,7 +191,7 @@ public class InternalSubchannelTest {
fakeClock.forwardNanos(99);
assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke();
fakeClock.forwardNanos(1);
@ -194,7 +199,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Let this one succeed, will enter READY state.
assertNoCallbackInvoke();
transports.peek().listener.transportReady();
@ -214,7 +219,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Final checks for consultations on back-off policies
verify(mockBackoffPolicy1, times(backoff1Consulted)).nextBackoffNanos();
@ -240,7 +245,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Let this one fail without success
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
@ -252,7 +257,7 @@ public class InternalSubchannelTest {
// Second attempt will start immediately. Still no back-off policy.
verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT);
.newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
assertNull(internalSubchannel.obtainActiveTransport());
// Fail this one too
assertNoCallbackInvoke();
@ -272,14 +277,14 @@ public class InternalSubchannelTest {
// Third attempt is the first address, thus controlled by the first back-off interval.
fakeClock.forwardNanos(9);
verify(mockTransportFactory, times(transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke();
fakeClock.forwardNanos(1);
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState());
@ -289,7 +294,7 @@ public class InternalSubchannelTest {
assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT);
.newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too
assertNoCallbackInvoke();
transports.poll().listener.transportShutdown(Status.RESOURCE_EXHAUSTED);
@ -304,14 +309,14 @@ public class InternalSubchannelTest {
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
fakeClock.forwardNanos(99);
verify(mockTransportFactory, times(transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke();
fakeClock.forwardNanos(1);
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Let it through
assertNoCallbackInvoke();
transports.peek().listener.transportReady();
@ -332,7 +337,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail the transport
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState());
@ -340,7 +345,7 @@ public class InternalSubchannelTest {
// Second attempt will start immediately. Still no new back-off policy.
verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT);
.newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too
assertEquals(CONNECTING, internalSubchannel.getState());
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
@ -354,14 +359,14 @@ public class InternalSubchannelTest {
// Third attempt is the first address, thus controlled by the first back-off interval.
fakeClock.forwardNanos(9);
verify(mockTransportFactory, times(transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke();
fakeClock.forwardNanos(1);
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT);
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Final checks on invocations on back-off policies
verify(mockBackoffPolicy1, times(backoff1Consulted)).nextBackoffNanos();
@ -379,12 +384,12 @@ public class InternalSubchannelTest {
// First address fails
assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connects
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
transports.peek().listener.transportReady();
assertExactCallbackInvokes("onStateChange:READY");
assertEquals(READY, internalSubchannel.getState());
@ -402,9 +407,10 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks());
verify(mockTransportFactory, times(2)).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory, times(2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory);
@ -421,12 +427,12 @@ public class InternalSubchannelTest {
// First address fails
assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connecting
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
assertNoCallbackInvoke();
assertEquals(CONNECTING, internalSubchannel.getState());
@ -445,9 +451,10 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks());
verify(mockTransportFactory, times(2)).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory, times(2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory);
@ -462,8 +469,10 @@ public class InternalSubchannelTest {
internalSubchannel.updateAddresses(new EquivalentAddressGroup(addr2));
// Nothing happened on address update
verify(mockTransportFactory, never()).newClientTransport(addr1, AUTHORITY, USER_AGENT);
verify(mockTransportFactory, never()).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory, never())
.newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
verify(mockTransportFactory, never())
.newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
verifyNoMoreInteractions(mockTransportFactory);
assertNoCallbackInvoke();
assertEquals(IDLE, internalSubchannel.getState());
@ -471,7 +480,7 @@ public class InternalSubchannelTest {
// But new address chosen when connecting
assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
// And no other addresses attempted
assertEquals(0, fakeClock.numPendingTasks());
@ -494,12 +503,12 @@ public class InternalSubchannelTest {
// First address fails
assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connects
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
transports.peek().listener.transportReady();
assertExactCallbackInvokes("onStateChange:READY");
assertEquals(READY, internalSubchannel.getState());
@ -517,9 +526,9 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks());
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verify(mockTransportFactory).newClientTransport(addr4, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr4, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory);
@ -537,12 +546,12 @@ public class InternalSubchannelTest {
// First address fails
assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connecting
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
assertNoCallbackInvoke();
assertEquals(CONNECTING, internalSubchannel.getState());
@ -558,9 +567,9 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks());
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr3, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verify(mockTransportFactory).newClientTransport(addr4, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr4, AUTHORITY, USER_AGENT, NO_PROXY);
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory);
@ -577,13 +586,13 @@ public class InternalSubchannelTest {
// Won't connect until requested
verify(mockTransportFactory, times(transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// First attempt
internalSubchannel.obtainActiveTransport();
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one
transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
@ -593,7 +602,7 @@ public class InternalSubchannelTest {
fakeClock.forwardNanos(10);
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Make this one proceed
transports.peek().listener.transportReady();
@ -610,7 +619,7 @@ public class InternalSubchannelTest {
internalSubchannel.obtainActiveTransport();
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT);
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
}
@Test
@ -640,7 +649,7 @@ public class InternalSubchannelTest {
// First transport is created immediately
internalSubchannel.obtainActiveTransport();
assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT);
verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one
MockClientTransportInfo transportInfo = transports.poll();
@ -740,7 +749,8 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:SHUTDOWN", "onTerminated");
assertEquals(SHUTDOWN, internalSubchannel.getState());
assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(0)).newClientTransport(addr, AUTHORITY, USER_AGENT);
verify(mockTransportFactory, times(0))
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
assertNoCallbackInvoke();
assertEquals(SHUTDOWN, internalSubchannel.getState());
}
@ -848,11 +858,43 @@ public class InternalSubchannelTest {
assertEquals(3, runnableInvokes.get());
}
@Test
public void proxyTest() {
final SocketAddress addr1 = mock(SocketAddress.class);
final ProxyParameters proxy = new ProxyParameters(
InetSocketAddress.createUnresolved("proxy.example.com", 1000), "username", "password");
ProxyDetector proxyDetector = new ProxyDetector() {
@Nullable
@Override
public ProxyParameters proxyFor(SocketAddress targetServerAddress) {
if (targetServerAddress == addr1) {
return proxy;
} else {
return null;
}
}
};
createInternalSubChannelWithProxy(proxyDetector, addr1);
assertEquals(ConnectivityState.IDLE, internalSubchannel.getState());
assertNoCallbackInvoke();
assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(ConnectivityState.CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory).newClientTransport(
eq(addr1), eq(AUTHORITY), eq(USER_AGENT), eq(proxy));
}
private void createInternalSubchannel(SocketAddress ... addrs) {
createInternalSubChannelWithProxy(ProxyDetector.NOOP_INSTANCE, addrs);
}
private void createInternalSubChannelWithProxy(
ProxyDetector proxyDetector, SocketAddress ... addrs) {
addressGroup = new EquivalentAddressGroup(Arrays.asList(addrs));
internalSubchannel = new InternalSubchannel(addressGroup, AUTHORITY, USER_AGENT,
mockBackoffPolicyProvider, mockTransportFactory, fakeClock.getScheduledExecutorService(),
fakeClock.getStopwatchSupplier(), channelExecutor, mockInternalSubchannelCallback);
fakeClock.getStopwatchSupplier(), channelExecutor, mockInternalSubchannelCallback,
proxyDetector);
}
private void assertNoCallbackInvoke() {

View File

@ -80,6 +80,7 @@ public class ManagedChannelImplIdlenessTest {
private final FakeClock oobExecutor = new FakeClock();
private static final String AUTHORITY = "fakeauthority";
private static final String USER_AGENT = "fakeagent";
private static final ProxyParameters NO_PROXY = null;
private static final long IDLE_TIMEOUT_SECONDS = 30;
private ManagedChannelImpl channel;
@ -141,7 +142,8 @@ public class ManagedChannelImplIdlenessTest {
channel = new ManagedChannelImpl(
builder, mockTransportFactory, new FakeBackoffPolicyProvider(),
oobExecutorPool, timer.getStopwatchSupplier(),
Collections.<ClientInterceptor>emptyList());
Collections.<ClientInterceptor>emptyList(),
ProxyDetector.NOOP_INSTANCE);
newTransports = TestUtils.captureTransports(mockTransportFactory);
for (int i = 0; i < 2; i++) {
@ -155,7 +157,7 @@ public class ManagedChannelImplIdlenessTest {
// Verify the initial idleness
verify(mockLoadBalancerFactory, never()).newLoadBalancer(any(Helper.class));
verify(mockTransportFactory, never()).newClientTransport(
any(SocketAddress.class), anyString(), anyString());
any(SocketAddress.class), anyString(), anyString(), any(ProxyParameters.class));
verify(mockNameResolver, never()).start(any(NameResolver.Listener.class));
}
@ -352,11 +354,13 @@ public class ManagedChannelImplIdlenessTest {
// Now make an RPC on an OOB channel
ManagedChannel oob = helper.createOobChannel(servers.get(0), "oobauthority");
verify(mockTransportFactory, never())
.newClientTransport(any(SocketAddress.class), same("oobauthority"), same(USER_AGENT));
.newClientTransport(any(SocketAddress.class), same("oobauthority"), same(USER_AGENT),
same(NO_PROXY));
ClientCall<String, Integer> oobCall = oob.newCall(method, CallOptions.DEFAULT);
oobCall.start(mockCallListener2, new Metadata());
verify(mockTransportFactory)
.newClientTransport(any(SocketAddress.class), same("oobauthority"), same(USER_AGENT));
.newClientTransport(any(SocketAddress.class), same("oobauthority"), same(USER_AGENT),
same(NO_PROXY));
MockClientTransportInfo oobTransportInfo = newTransports.poll();
assertEquals(0, newTransports.size());
// The OOB transport reports in-use state

View File

@ -128,6 +128,7 @@ public class ManagedChannelImplTest {
private final String serviceName = "fake.example.com";
private final String authority = serviceName;
private final String userAgent = "userAgent";
private final ProxyParameters noProxy = null;
private final String target = "fake://" + serviceName;
private URI expectedUri;
private final SocketAddress socketAddress = new SocketAddress() {};
@ -213,7 +214,7 @@ public class ManagedChannelImplTest {
checkState(channel == null);
channel = new ManagedChannelImpl(
builder, mockTransportFactory, new FakeBackoffPolicyProvider(),
oobExecutorPool, timer.getStopwatchSupplier(), interceptors);
oobExecutorPool, timer.getStopwatchSupplier(), interceptors, ProxyDetector.NOOP_INSTANCE);
if (requestConnection) {
// Force-exit the initial idle-mode
@ -328,7 +329,7 @@ public class ManagedChannelImplTest {
Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY);
subchannel.requestConnection();
verify(mockTransportFactory).newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class));
any(SocketAddress.class), any(String.class), any(String.class), any(ProxyParameters.class));
MockClientTransportInfo transportInfo = transports.poll();
ConnectionClientTransport mockTransport = transportInfo.transport;
verify(mockTransport).start(any(ManagedClientTransport.Listener.class));
@ -349,7 +350,7 @@ public class ManagedChannelImplTest {
// First RPC, will be pending
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
verify(mockTransportFactory).newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class));
any(SocketAddress.class), any(String.class), any(String.class), any(ProxyParameters.class));
call.start(mockCallListener, headers);
verify(mockTransport, never())
@ -437,7 +438,7 @@ public class ManagedChannelImplTest {
verifyNoMoreInteractions(oobExecutorPool);
verify(mockTransportFactory).newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class));
any(SocketAddress.class), any(String.class), any(String.class), any(ProxyParameters.class));
verify(mockTransportFactory).close();
verify(mockTransport, atLeast(0)).getLogId();
verifyNoMoreInteractions(mockTransport);
@ -459,7 +460,7 @@ public class ManagedChannelImplTest {
subchannel1.requestConnection();
subchannel2.requestConnection();
verify(mockTransportFactory, times(2)).newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class));
any(SocketAddress.class), any(String.class), any(String.class), any(ProxyParameters.class));
MockClientTransportInfo transportInfo1 = transports.poll();
MockClientTransportInfo transportInfo2 = transports.poll();
@ -522,10 +523,10 @@ public class ManagedChannelImplTest {
// Make the transport available
Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY);
verify(mockTransportFactory, never()).newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class));
any(SocketAddress.class), any(String.class), any(String.class), any(ProxyParameters.class));
subchannel.requestConnection();
verify(mockTransportFactory).newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class));
any(SocketAddress.class), any(String.class), any(String.class), any(ProxyParameters.class));
MockClientTransportInfo transportInfo = transports.poll();
ConnectionClientTransport mockTransport = transportInfo.transport;
ManagedClientTransport.Listener transportListener = transportInfo.listener;
@ -665,9 +666,11 @@ public class ManagedChannelImplTest {
// The channel will starts with the first address (badAddress)
verify(mockTransportFactory)
.newClientTransport(same(badAddress), any(String.class), any(String.class));
.newClientTransport(same(badAddress), any(String.class), any(String.class),
any(ProxyParameters.class));
verify(mockTransportFactory, times(0))
.newClientTransport(same(goodAddress), any(String.class), any(String.class));
.newClientTransport(same(goodAddress), any(String.class), any(String.class),
any(ProxyParameters.class));
MockClientTransportInfo badTransportInfo = transports.poll();
// Which failed to connect
@ -676,7 +679,8 @@ public class ManagedChannelImplTest {
// The channel then try the second address (goodAddress)
verify(mockTransportFactory)
.newClientTransport(same(goodAddress), any(String.class), any(String.class));
.newClientTransport(same(goodAddress), any(String.class), any(String.class),
any(ProxyParameters.class));
MockClientTransportInfo goodTransportInfo = transports.poll();
when(goodTransportInfo.transport.newStream(
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class)))
@ -807,15 +811,18 @@ public class ManagedChannelImplTest {
// Connecting to server1, which will fail
verify(mockTransportFactory)
.newClientTransport(same(addr1), any(String.class), any(String.class));
.newClientTransport(same(addr1), any(String.class), any(String.class),
any(ProxyParameters.class));
verify(mockTransportFactory, times(0))
.newClientTransport(same(addr2), any(String.class), any(String.class));
.newClientTransport(same(addr2), any(String.class), any(String.class),
any(ProxyParameters.class));
MockClientTransportInfo transportInfo1 = transports.poll();
transportInfo1.listener.transportShutdown(Status.UNAVAILABLE);
// Connecting to server2, which will fail too
verify(mockTransportFactory)
.newClientTransport(same(addr2), any(String.class), any(String.class));
.newClientTransport(same(addr2), any(String.class), any(String.class),
any(ProxyParameters.class));
MockClientTransportInfo transportInfo2 = transports.poll();
Status server2Error = Status.UNAVAILABLE.withDescription("Server2 failed to connect");
transportInfo2.listener.transportShutdown(server2Error);
@ -863,20 +870,22 @@ public class ManagedChannelImplTest {
// requestConnection()
verify(mockTransportFactory, never()).newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class));
any(SocketAddress.class), any(String.class), any(String.class), any(ProxyParameters.class));
sub1.requestConnection();
verify(mockTransportFactory).newClientTransport(socketAddress, authority, userAgent);
verify(mockTransportFactory).newClientTransport(socketAddress, authority, userAgent, noProxy);
MockClientTransportInfo transportInfo1 = transports.poll();
assertNotNull(transportInfo1);
sub2.requestConnection();
verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent);
verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent,
noProxy);
MockClientTransportInfo transportInfo2 = transports.poll();
assertNotNull(transportInfo2);
sub1.requestConnection();
sub2.requestConnection();
verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent);
verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent,
noProxy);
// shutdown() has a delay
sub1.shutdown();
@ -944,7 +953,7 @@ public class ManagedChannelImplTest {
sub2.shutdown();
assertTrue(channel.isTerminated());
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString());
anyString(), any(ProxyParameters.class));
}
@Test
@ -959,7 +968,7 @@ public class ManagedChannelImplTest {
// Therefore, channel is terminated without relying on LoadBalancer to shutdown subchannels.
assertTrue(channel.isTerminated());
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString());
anyString(), any(ProxyParameters.class));
}
@Test
@ -977,7 +986,8 @@ public class ManagedChannelImplTest {
Metadata headers = new Metadata();
ClientCall<String, Integer> call = oob1.newCall(method, CallOptions.DEFAULT);
call.start(mockCallListener, headers);
verify(mockTransportFactory).newClientTransport(socketAddress, "oob1authority", userAgent);
verify(mockTransportFactory).newClientTransport(socketAddress, "oob1authority", userAgent,
noProxy);
MockClientTransportInfo transportInfo = transports.poll();
assertNotNull(transportInfo);
@ -998,7 +1008,7 @@ public class ManagedChannelImplTest {
oob1.newCall(method, CallOptions.DEFAULT.withWaitForReady());
call3.start(mockCallListener3, headers);
verify(mockTransportFactory, times(2)).newClientTransport(
socketAddress, "oob1authority", userAgent);
socketAddress, "oob1authority", userAgent, noProxy);
transportInfo = transports.poll();
assertNotNull(transportInfo);
@ -1103,7 +1113,7 @@ public class ManagedChannelImplTest {
assertTrue(oob2.isTerminated());
assertTrue(channel.isTerminated());
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString());
anyString(), any(ProxyParameters.class));
}
@Test
@ -1118,7 +1128,7 @@ public class ManagedChannelImplTest {
// Channel's shutdownNow() will call shutdownNow() on all subchannels and oobchannels.
// Therefore, channel is terminated without relying on LoadBalancer to shutdown oobchannels.
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString());
anyString(), any(ProxyParameters.class));
}
@Test
@ -1168,7 +1178,7 @@ public class ManagedChannelImplTest {
Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY);
subchannel.requestConnection();
verify(mockTransportFactory).newClientTransport(
same(socketAddress), eq(authority), eq(userAgent));
same(socketAddress), eq(authority), eq(userAgent), eq(noProxy));
MockClientTransportInfo transportInfo = transports.poll();
final ConnectionClientTransport transport = transportInfo.transport;
when(transport.getAttributes()).thenReturn(Attributes.EMPTY);

View File

@ -89,7 +89,8 @@ final class TestUtils {
return mockTransport;
}
}).when(mockTransportFactory)
.newClientTransport(any(SocketAddress.class), any(String.class), any(String.class));
.newClientTransport(any(SocketAddress.class), any(String.class), any(String.class),
any(ProxyParameters.class));
return captor;
}

View File

@ -17,6 +17,7 @@
package io.grpc.netty;
import io.grpc.Internal;
import io.grpc.internal.ProxyParameters;
import java.net.SocketAddress;
/**
@ -44,7 +45,8 @@ public final class InternalNettyChannelBuilder {
extends NettyChannelBuilder.TransportCreationParamsFilterFactory {
@Override
TransportCreationParamsFilter create(
SocketAddress targetServerAddress, String authority, String userAgent);
SocketAddress targetServerAddress, String authority, String userAgent,
ProxyParameters proxy);
}
/**

View File

@ -36,6 +36,7 @@ import io.grpc.internal.ClientTransportFactory;
import io.grpc.internal.ConnectionClientTransport;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.KeepAliveManager;
import io.grpc.internal.ProxyParameters;
import io.grpc.internal.SharedResourceHolder;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
@ -341,18 +342,13 @@ public final class NettyChannelBuilder
static ProtocolNegotiator createProtocolNegotiator(
String authority,
NegotiationType negotiationType,
SslContext sslContext) {
SslContext sslContext,
ProxyParameters proxy) {
ProtocolNegotiator negotiator =
createProtocolNegotiatorByType(authority, negotiationType, sslContext);
String proxy = System.getenv("GRPC_PROXY_EXP");
if (proxy != null) {
String[] parts = proxy.split(":", 2);
int port = 80;
if (parts.length > 1) {
port = Integer.parseInt(parts[1]);
}
InetSocketAddress proxyAddress = new InetSocketAddress(parts[0], port);
negotiator = ProtocolNegotiators.httpProxy(proxyAddress, null, null, negotiator);
negotiator = ProtocolNegotiators.httpProxy(
proxy.proxyAddress, proxy.username, proxy.password, negotiator);
}
return negotiator;
}
@ -406,7 +402,10 @@ public final class NettyChannelBuilder
interface TransportCreationParamsFilterFactory {
@CheckReturnValue
TransportCreationParamsFilter create(
SocketAddress targetServerAddress, String authority, @Nullable String userAgent);
SocketAddress targetServerAddress,
String authority,
@Nullable String userAgent,
@Nullable ProxyParameters proxy);
}
@CheckReturnValue
@ -472,11 +471,12 @@ public final class NettyChannelBuilder
@Override
public ConnectionClientTransport newClientTransport(
SocketAddress serverAddress, String authority, @Nullable String userAgent) {
SocketAddress serverAddress, String authority, @Nullable String userAgent,
@Nullable ProxyParameters proxy) {
checkState(!closed, "The transport factory is closed.");
TransportCreationParamsFilter dparams =
transportCreationParamsFilterFactory.create(serverAddress, authority, userAgent);
transportCreationParamsFilterFactory.create(serverAddress, authority, userAgent, proxy);
final AtomicBackoff.State keepAliveTimeNanosState = keepAliveTimeNanos.getState();
Runnable tooManyPingsRunnable = new Runnable() {
@ -528,8 +528,12 @@ public final class NettyChannelBuilder
@Override
public TransportCreationParamsFilter create(
SocketAddress targetServerAddress, String authority, String userAgent) {
return new DynamicNettyTransportParams(targetServerAddress, authority, userAgent);
SocketAddress targetServerAddress,
String authority,
String userAgent,
ProxyParameters proxyParams) {
return new DynamicNettyTransportParams(
targetServerAddress, authority, userAgent, proxyParams);
}
@CheckReturnValue
@ -538,12 +542,17 @@ public final class NettyChannelBuilder
private final SocketAddress targetServerAddress;
private final String authority;
@Nullable private final String userAgent;
private ProxyParameters proxyParams;
private DynamicNettyTransportParams(
SocketAddress targetServerAddress, String authority, String userAgent) {
SocketAddress targetServerAddress,
String authority,
String userAgent,
ProxyParameters proxyParams) {
this.targetServerAddress = targetServerAddress;
this.authority = authority;
this.userAgent = userAgent;
this.proxyParams = proxyParams;
}
@Override
@ -563,7 +572,7 @@ public final class NettyChannelBuilder
@Override
public ProtocolNegotiator getProtocolNegotiator() {
return createProtocolNegotiator(authority, negotiationType, sslContext);
return createProtocolNegotiator(authority, negotiationType, sslContext, proxyParams);
}
}
}

View File

@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import io.grpc.ManagedChannel;
import io.grpc.internal.ProxyParameters;
import io.grpc.netty.InternalNettyChannelBuilder.OverrideAuthorityChecker;
import io.grpc.netty.ProtocolNegotiators.TlsNegotiator;
import io.netty.handler.ssl.SslContext;
@ -39,6 +40,7 @@ public class NettyChannelBuilderTest {
@Rule public final ExpectedException thrown = ExpectedException.none();
private final SslContext noSslContext = null;
private final ProxyParameters noProxy = null;
private void shutdown(ManagedChannel mc) throws Exception {
mc.shutdownNow();
@ -141,7 +143,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"authority",
NegotiationType.PLAINTEXT,
noSslContext);
noSslContext,
noProxy);
// just check that the classes are the same, and that negotiator is not null.
assertTrue(negotiator instanceof ProtocolNegotiators.PlaintextNegotiator);
}
@ -151,7 +154,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"authority",
NegotiationType.PLAINTEXT_UPGRADE,
noSslContext);
noSslContext,
noProxy);
// just check that the classes are the same, and that negotiator is not null.
assertTrue(negotiator instanceof ProtocolNegotiators.PlaintextUpgradeNegotiator);
}
@ -162,7 +166,8 @@ public class NettyChannelBuilderTest {
NettyChannelBuilder.createProtocolNegotiator(
"authority:1234",
NegotiationType.TLS,
noSslContext);
noSslContext,
noProxy);
}
@Test
@ -170,7 +175,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"authority:1234",
NegotiationType.TLS,
GrpcSslContexts.forClient().build());
GrpcSslContexts.forClient().build(),
noProxy);
assertTrue(negotiator instanceof ProtocolNegotiators.TlsNegotiator);
ProtocolNegotiators.TlsNegotiator n = (TlsNegotiator) negotiator;
@ -184,7 +190,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"bad_authority",
NegotiationType.TLS,
GrpcSslContexts.forClient().build());
GrpcSslContexts.forClient().build(),
noProxy);
assertTrue(negotiator instanceof ProtocolNegotiators.TlsNegotiator);
ProtocolNegotiators.TlsNegotiator n = (TlsNegotiator) negotiator;

View File

@ -76,7 +76,8 @@ public class NettyTransportTest extends AbstractTransportTest {
return clientFactory.newClientTransport(
new InetSocketAddress("localhost", port),
testAuthority(server),
null /* agent */);
null /* agent */,
null /* proxy */);
}
@Test

View File

@ -35,6 +35,7 @@ import io.grpc.internal.ClientTransportFactory;
import io.grpc.internal.ConnectionClientTransport;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.KeepAliveManager;
import io.grpc.internal.ProxyParameters;
import io.grpc.internal.SharedResourceHolder;
import io.grpc.internal.SharedResourceHolder.Resource;
import io.grpc.okhttp.internal.Platform;
@ -403,20 +404,11 @@ public class OkHttpChannelBuilder extends
@Override
public ConnectionClientTransport newClientTransport(
SocketAddress addr, String authority, @Nullable String userAgent) {
SocketAddress addr, String authority, @Nullable String userAgent,
@Nullable ProxyParameters proxy) {
if (closed) {
throw new IllegalStateException("The transport factory is closed.");
}
InetSocketAddress proxyAddress = null;
String proxy = System.getenv("GRPC_PROXY_EXP");
if (proxy != null) {
String[] parts = proxy.split(":", 2);
int port = 80;
if (parts.length > 1) {
port = Integer.parseInt(parts[1]);
}
proxyAddress = new InetSocketAddress(parts[0], port);
}
final AtomicBackoff.State keepAliveTimeNanosState = keepAliveTimeNanos.getState();
Runnable tooManyPingsRunnable = new Runnable() {
@Override
@ -425,9 +417,19 @@ public class OkHttpChannelBuilder extends
}
};
InetSocketAddress inetSocketAddr = (InetSocketAddress) addr;
OkHttpClientTransport transport = new OkHttpClientTransport(inetSocketAddr, authority,
userAgent, executor, socketFactory, hostnameVerifier, Utils.convertSpec(connectionSpec),
maxMessageSize, proxyAddress, null, null, tooManyPingsRunnable);
OkHttpClientTransport transport = new OkHttpClientTransport(
inetSocketAddr,
authority,
userAgent,
executor,
socketFactory,
hostnameVerifier,
Utils.convertSpec(connectionSpec),
maxMessageSize,
proxy == null ? null : proxy.proxyAddress,
proxy == null ? null : proxy.username,
proxy == null ? null : proxy.password,
tooManyPingsRunnable);
if (enableKeepAlive) {
transport.enableKeepAlive(
true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls);

View File

@ -0,0 +1,178 @@
/*
* Copyright 2017, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.internal;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.net.HostAndPort;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(JUnit4.class)
public class ProxyDetectorImplTest {
private InetSocketAddress destination = InetSocketAddress.createUnresolved(
"destination",
5678
);
@Mock private ProxySelector proxySelector;
@Mock private ProxyDetectorImpl.AuthenticationProvider authenticator;
private Supplier<ProxySelector> proxySelectorSupplier;
private ProxyDetector proxyDetector;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
proxySelectorSupplier = new Supplier<ProxySelector>() {
@Override
public ProxySelector get() {
return proxySelector;
}
};
proxyDetector = new ProxyDetectorImpl(proxySelectorSupplier, authenticator, null);
}
@Test
public void override_hostPort() throws Exception {
final String overrideHost = "override";
final int overridePort = 1234;
HostAndPort hostPort = HostAndPort.fromParts(overrideHost, overridePort);
ProxyDetectorImpl proxyDetector = new ProxyDetectorImpl(
proxySelectorSupplier,
authenticator,
hostPort.toString());
ProxyParameters detected = proxyDetector.proxyFor(destination);
assertNotNull(detected);
assertEquals(
new ProxyParameters(
InetSocketAddress.createUnresolved(overrideHost, overridePort), null, null),
detected);
}
@Test
public void override_hostOnly() throws Exception {
final String overrideHostWithoutPort = "override";
final int defaultPort = 80;
ProxyDetectorImpl proxyDetector = new ProxyDetectorImpl(
proxySelectorSupplier,
authenticator,
overrideHostWithoutPort);
ProxyParameters detected = proxyDetector.proxyFor(destination);
assertNotNull(detected);
assertEquals(
new ProxyParameters(
InetSocketAddress.createUnresolved(overrideHostWithoutPort, defaultPort), null, null),
detected);
}
@Test
public void returnNullWhenNoProxy() throws Exception {
when(proxySelector.select(any(URI.class)))
.thenReturn(ImmutableList.of(java.net.Proxy.NO_PROXY));
assertNull(proxyDetector.proxyFor(destination));
}
@Test
public void detectProxyForUnresolved() throws Exception {
final InetSocketAddress proxyAddress = InetSocketAddress.createUnresolved("proxy", 1234);
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress);
when(proxySelector.select(any(URI.class))).thenReturn(ImmutableList.of(proxy));
ProxyParameters detected = proxyDetector.proxyFor(destination);
assertNotNull(detected);
assertEquals(new ProxyParameters(proxyAddress, null, null), detected);
}
@Test
public void detectProxyForResolved() throws Exception {
InetSocketAddress resolved =
new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 0, 0, 1}), 10);
assertFalse(resolved.isUnresolved());
destination = resolved;
final InetSocketAddress proxyAddress = InetSocketAddress.createUnresolved("proxy", 1234);
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress);
when(proxySelector.select(any(URI.class))).thenReturn(ImmutableList.of(proxy));
ProxyParameters detected = proxyDetector.proxyFor(destination);
assertNotNull(detected);
assertEquals(new ProxyParameters(proxyAddress, null, null), detected);
}
@Test
public void pickFirstHttpProxy() throws Exception {
final InetSocketAddress proxyAddress = InetSocketAddress.createUnresolved("proxy1", 1111);
InetSocketAddress otherProxy = InetSocketAddress.createUnresolved("proxy2", 2222);
Proxy proxy1 = new java.net.Proxy(java.net.Proxy.Type.HTTP, proxyAddress);
Proxy proxy2 = new java.net.Proxy(java.net.Proxy.Type.HTTP, otherProxy);
when(proxySelector.select(any(URI.class))).thenReturn(ImmutableList.of(proxy1, proxy2));
ProxyParameters detected = proxyDetector.proxyFor(destination);
assertNotNull(detected);
assertEquals(new ProxyParameters(proxyAddress, null, null), detected);
}
// Mainly for InProcessSocketAddress
@Test
public void noProxyForNonInetSocket() throws Exception {
assertNull(proxyDetector.proxyFor(mock(SocketAddress.class)));
}
@Test
public void authRequired() throws Exception {
final String proxyHost = "proxyhost";
final int proxyPort = 1234;
final InetSocketAddress proxyAddress = InetSocketAddress.createUnresolved(proxyHost, proxyPort);
Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, proxyAddress);
final String proxyUser = "testuser";
final String proxyPassword = "testpassword";
PasswordAuthentication auth = new PasswordAuthentication(
proxyUser,
proxyPassword.toCharArray());
when(authenticator.requestPasswordAuthentication(
any(String.class),
any(InetAddress.class),
any(Integer.class),
any(String.class),
any(String.class),
any(String.class))).thenReturn(auth);
when(proxySelector.select(any(URI.class))).thenReturn(ImmutableList.of(proxy));
ProxyParameters detected = proxyDetector.proxyFor(destination);
assertNotNull(detected);
assertEquals(new ProxyParameters(proxyAddress, proxyUser, proxyPassword), detected);
}
}

View File

@ -110,7 +110,7 @@ public class OkHttpChannelBuilderTest {
public void usePlaintext_newClientTransportAllowed() {
OkHttpChannelBuilder builder = OkHttpChannelBuilder.forAddress("host", 1234).usePlaintext(true);
builder.buildTransportFactory().newClientTransport(new InetSocketAddress(5678),
"dummy_authority", "dummy_userAgent");
"dummy_authority", "dummy_userAgent", null /* proxy */);
}
@Test

View File

@ -76,7 +76,8 @@ public class OkHttpTransportTest extends AbstractTransportTest {
return clientFactory.newClientTransport(
new InetSocketAddress("::1", port),
testAuthority(server),
null /* agent */);
null /* agent */,
null /* proxy */);
}
// TODO(ejona): Flaky/Broken

View File

@ -40,6 +40,6 @@ public abstract class AbstractClientTransportFactoryTest {
ClientTransportFactory transportFactory = newClientTransportFactory();
transportFactory.close();
transportFactory.newClientTransport(
new InetSocketAddress("localhost", 12345), "localhost:" + 12345, "agent");
new InetSocketAddress("localhost", 12345), "localhost:" + 12345, "agent", null);
}
}