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

View File

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

View File

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

View File

@ -28,9 +28,10 @@ public interface ClientTransportFactory extends Closeable {
* *
* @param serverAddress the address that the transport is connected to * @param serverAddress the address that the transport is connected to
* @param authority the HTTP/2 authority of the server * @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, 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 * 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 int port;
private final Resource<ScheduledExecutorService> timerServiceResource; private final Resource<ScheduledExecutorService> timerServiceResource;
private final Resource<ExecutorService> executorResource; private final Resource<ExecutorService> executorResource;
private final ProxyDetector proxyDetector;
@GuardedBy("this") @GuardedBy("this")
private boolean shutdown; private boolean shutdown;
@GuardedBy("this") @GuardedBy("this")
@ -84,7 +85,8 @@ final class DnsNameResolver extends NameResolver {
DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params, DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params,
Resource<ScheduledExecutorService> timerServiceResource, Resource<ScheduledExecutorService> timerServiceResource,
Resource<ExecutorService> executorResource) { Resource<ExecutorService> executorResource,
ProxyDetector proxyDetector) {
// TODO: if a DNS server is provided as nsAuthority, use it. // TODO: if a DNS server is provided as nsAuthority, use it.
// https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java // https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java
this.timerServiceResource = timerServiceResource; this.timerServiceResource = timerServiceResource;
@ -106,6 +108,7 @@ final class DnsNameResolver extends NameResolver {
} else { } else {
port = nameUri.getPort(); port = nameUri.getPort();
} }
this.proxyDetector = proxyDetector;
} }
@Override @Override
@ -145,9 +148,10 @@ final class DnsNameResolver extends NameResolver {
resolving = true; resolving = true;
} }
try { try {
if (System.getenv("GRPC_PROXY_EXP") != null) { InetSocketAddress destination = InetSocketAddress.createUnresolved(host, port);
EquivalentAddressGroup server = ProxyParameters proxy = proxyDetector.proxyFor(destination);
new EquivalentAddressGroup(InetSocketAddress.createUnresolved(host, port)); if (proxy != null) {
EquivalentAddressGroup server = new EquivalentAddressGroup(destination);
savedListener.onAddresses(Collections.singletonList(server), Attributes.EMPTY); savedListener.onAddresses(Collections.singletonList(server), Attributes.EMPTY);
return; 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); "the path component (%s) of the target (%s) must start with '/'", targetPath, targetUri);
String name = targetPath.substring(1); String name = targetPath.substring(1);
return new DnsNameResolver(targetUri.getAuthority(), name, params, GrpcUtil.TIMER_SERVICE, return new DnsNameResolver(targetUri.getAuthority(), name, params, GrpcUtil.TIMER_SERVICE,
GrpcUtil.SHARED_CHANNEL_EXECUTOR); GrpcUtil.SHARED_CHANNEL_EXECUTOR, ProxyDetector.DEFAULT_INSTANCE);
} else { } else {
return null; return null;
} }

View File

@ -39,8 +39,10 @@ import io.grpc.internal.SharedResourceHolder.Resource;
import io.grpc.internal.StreamListener.MessageProducer; import io.grpc.internal.StreamListener.MessageProducer;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.charset.Charset; 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, * 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 * 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") @GuardedBy("lock")
private ConnectivityStateInfo state = ConnectivityStateInfo.forNonError(IDLE); private ConnectivityStateInfo state = ConnectivityStateInfo.forNonError(IDLE);
private final ProxyDetector proxyDetector;
@GuardedBy("lock") @GuardedBy("lock")
private Status shutdownReason; private Status shutdownReason;
InternalSubchannel(EquivalentAddressGroup addressGroup, String authority, String userAgent, InternalSubchannel(EquivalentAddressGroup addressGroup, String authority, String userAgent,
BackoffPolicy.Provider backoffPolicyProvider, BackoffPolicy.Provider backoffPolicyProvider,
ClientTransportFactory transportFactory, ScheduledExecutorService scheduledExecutor, 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.addressGroup = Preconditions.checkNotNull(addressGroup, "addressGroup");
this.authority = authority; this.authority = authority;
this.userAgent = userAgent; this.userAgent = userAgent;
@ -153,6 +157,7 @@ final class InternalSubchannel implements WithLogId {
this.connectingTimer = stopwatchSupplier.get(); this.connectingTimer = stopwatchSupplier.get();
this.channelExecutor = channelExecutor; this.channelExecutor = channelExecutor;
this.callback = callback; this.callback = callback;
this.proxyDetector = proxyDetector;
} }
/** /**
@ -194,8 +199,9 @@ final class InternalSubchannel implements WithLogId {
List<SocketAddress> addrs = addressGroup.getAddresses(); List<SocketAddress> addrs = addressGroup.getAddresses();
final SocketAddress address = addrs.get(addressIndex); final SocketAddress address = addrs.get(addressIndex);
ProxyParameters proxy = proxyDetector.proxyFor(address);
ConnectionClientTransport transport = ConnectionClientTransport transport =
transportFactory.newClientTransport(address, authority, userAgent); transportFactory.newClientTransport(address, authority, userAgent, proxy);
if (log.isLoggable(Level.FINE)) { if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "[{0}] Created {1} for {2}", log.log(Level.FINE, "[{0}] Created {1} for {2}",
new Object[] {logId, transport.getLogId(), address}); 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. // Only null after channel is terminated. Must be assigned from the channelExecutor.
private NameResolver nameResolver; private NameResolver nameResolver;
private final ProxyDetector proxyDetector;
// null when channel is in idle mode. Must be assigned from channelExecutor. // null when channel is in idle mode. Must be assigned from channelExecutor.
@Nullable @Nullable
private LbHelperImpl lbHelper; private LbHelperImpl lbHelper;
@ -386,7 +388,8 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI
BackoffPolicy.Provider backoffPolicyProvider, BackoffPolicy.Provider backoffPolicyProvider,
ObjectPool<? extends Executor> oobExecutorPool, ObjectPool<? extends Executor> oobExecutorPool,
Supplier<Stopwatch> stopwatchSupplier, Supplier<Stopwatch> stopwatchSupplier,
List<ClientInterceptor> interceptors) { List<ClientInterceptor> interceptors,
ProxyDetector proxyDetector) {
this.target = checkNotNull(builder.target, "target"); this.target = checkNotNull(builder.target, "target");
this.nameResolverFactory = builder.getNameResolverFactory(); this.nameResolverFactory = builder.getNameResolverFactory();
this.nameResolverParams = checkNotNull(builder.getNameResolverParams(), "nameResolverParams"); 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.decompressorRegistry = checkNotNull(builder.decompressorRegistry, "decompressorRegistry");
this.compressorRegistry = checkNotNull(builder.compressorRegistry, "compressorRegistry"); this.compressorRegistry = checkNotNull(builder.compressorRegistry, "compressorRegistry");
this.userAgent = builder.userAgent; this.userAgent = builder.userAgent;
this.proxyDetector = proxyDetector;
phantom = new ManagedChannelReference(this); phantom = new ManagedChannelReference(this);
logger.log(Level.FINE, "[{0}] Created with target {1}", new Object[] {getLogId(), target}); 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) { void onNotInUse(InternalSubchannel is) {
inUseStateAggregator.updateObjectInUse(is, false); inUseStateAggregator.updateObjectInUse(is, false);
} }
}); },
proxyDetector);
subchannel.subchannel = internalSubchannel; subchannel.subchannel = internalSubchannel;
logger.log(Level.FINE, "[{0}] {1} created for {2}", logger.log(Level.FINE, "[{0}] {1} created for {2}",
new Object[] {getLogId(), internalSubchannel.getLogId(), addressGroup}); new Object[] {getLogId(), internalSubchannel.getLogId(), addressGroup});
@ -754,7 +759,8 @@ public final class ManagedChannelImpl extends ManagedChannel implements WithLogI
void onStateChange(InternalSubchannel is, ConnectivityStateInfo newState) { void onStateChange(InternalSubchannel is, ConnectivityStateInfo newState) {
oobChannel.handleSubchannelStateChange(newState); oobChannel.handleSubchannelStateChange(newState);
} }
}); },
proxyDetector);
oobChannel.setSubchannel(internalSubchannel); oobChannel.setSubchannel(internalSubchannel);
runSerialized(new Runnable() { runSerialized(new Runnable() {
@Override @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 AUTHORITY = "testauthority";
private static final String USER_AGENT = "testuseragent"; 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 Attributes.Key<String> ATTR_KEY = Attributes.Key.of("somekey");
private static final String ATTR_VALUE = "somevalue"; private static final String ATTR_VALUE = "somevalue";
private static final MethodDescriptor<String, Integer> method = private static final MethodDescriptor<String, Integer> method =
@ -99,16 +100,16 @@ public class CallCredentialsApplyingTest {
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
origHeaders.put(ORIG_HEADER_KEY, ORIG_HEADER_VALUE); 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); .thenReturn(mockTransport);
when(mockTransport.newStream(same(method), any(Metadata.class), any(CallOptions.class))) when(mockTransport.newStream(same(method), any(Metadata.class), any(CallOptions.class)))
.thenReturn(mockStream); .thenReturn(mockStream);
ClientTransportFactory transportFactory = new CallCredentialsApplyingTransportFactory( ClientTransportFactory transportFactory = new CallCredentialsApplyingTransportFactory(
mockTransportFactory, mockExecutor); mockTransportFactory, mockExecutor);
transport = (ForwardingConnectionClientTransport) transportFactory.newClientTransport( transport = (ForwardingConnectionClientTransport) transportFactory.newClientTransport(
address, AUTHORITY, USER_AGENT); address, AUTHORITY, USER_AGENT, NO_PROXY);
callOptions = CallOptions.DEFAULT.withCallCredentials(mockCreds); 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()); 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.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Matchers.any; 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.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -37,6 +41,7 @@ import io.grpc.internal.DnsNameResolver.ResolutionResults;
import io.grpc.internal.SharedResourceHolder.Resource; import io.grpc.internal.SharedResourceHolder.Resource;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI; import java.net.URI;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
@ -107,13 +112,22 @@ public class DnsNameResolverTest {
private ArgumentCaptor<Status> statusCaptor; private ArgumentCaptor<Status> statusCaptor;
private DnsNameResolver newResolver(String name, int port) { 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( DnsNameResolver dnsResolver = new DnsNameResolver(
null, null,
name, name,
Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(), Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(),
fakeTimerServiceResource, fakeTimerServiceResource,
fakeExecutorResource); fakeExecutorResource,
dnsResolver.setDelegateResolver(mockResolver); proxyDetector);
dnsResolver.setDelegateResolver(delegateResolver);
return dnsResolver; return dnsResolver;
} }
@ -335,6 +349,32 @@ public class DnsNameResolverTest {
assertThat(results.txtRecords).isEmpty(); 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) { private void testInvalidUri(URI uri) {
try { try {
provider.newNameResolver(uri, NAME_RESOLVER_PARAMS); 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.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same; import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; 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.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import io.grpc.ConnectivityState;
import io.grpc.ConnectivityStateInfo; import io.grpc.ConnectivityStateInfo;
import io.grpc.EquivalentAddressGroup; import io.grpc.EquivalentAddressGroup;
import io.grpc.Status; import io.grpc.Status;
import io.grpc.internal.TestUtils.MockClientTransportInfo; import io.grpc.internal.TestUtils.MockClientTransportInfo;
import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -61,6 +65,7 @@ public class InternalSubchannelTest {
private static final String AUTHORITY = "fakeauthority"; private static final String AUTHORITY = "fakeauthority";
private static final String USER_AGENT = "mosaic"; private static final String USER_AGENT = "mosaic";
private static final ProxyParameters NO_PROXY = null;
private static final ConnectivityStateInfo UNAVAILABLE_STATE = private static final ConnectivityStateInfo UNAVAILABLE_STATE =
ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE); ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE);
private static final ConnectivityStateInfo RESOURCE_EXHAUSTED_STATE = private static final ConnectivityStateInfo RESOURCE_EXHAUSTED_STATE =
@ -145,7 +150,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsCreated)) 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. // Fail this one. Because there is only one address to try, enter TRANSIENT_FAILURE.
assertNoCallbackInvoke(); assertNoCallbackInvoke();
@ -161,7 +166,7 @@ public class InternalSubchannelTest {
fakeClock.forwardNanos(9); fakeClock.forwardNanos(9);
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(transportsCreated)) verify(mockTransportFactory, times(transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState()); assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke(); assertNoCallbackInvoke();
@ -169,7 +174,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsCreated)) verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too // Fail this one too
assertNoCallbackInvoke(); assertNoCallbackInvoke();
// Here we use a different status from the first failure, and verify that it's passed to // 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); fakeClock.forwardNanos(99);
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(transportsCreated)) verify(mockTransportFactory, times(transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState()); assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke(); assertNoCallbackInvoke();
fakeClock.forwardNanos(1); fakeClock.forwardNanos(1);
@ -194,7 +199,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(++transportsCreated)) verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Let this one succeed, will enter READY state. // Let this one succeed, will enter READY state.
assertNoCallbackInvoke(); assertNoCallbackInvoke();
transports.peek().listener.transportReady(); transports.peek().listener.transportReady();
@ -214,7 +219,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockBackoffPolicyProvider, times(backoffReset)).get(); verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsCreated)) verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Final checks for consultations on back-off policies // Final checks for consultations on back-off policies
verify(mockBackoffPolicy1, times(backoff1Consulted)).nextBackoffNanos(); verify(mockBackoffPolicy1, times(backoff1Consulted)).nextBackoffNanos();
@ -240,7 +245,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1)) verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Let this one fail without success // Let this one fail without success
transports.poll().listener.transportShutdown(Status.UNAVAILABLE); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
@ -252,7 +257,7 @@ public class InternalSubchannelTest {
// Second attempt will start immediately. Still no back-off policy. // Second attempt will start immediately. Still no back-off policy.
verify(mockBackoffPolicyProvider, times(backoffReset)).get(); verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr2)) verify(mockTransportFactory, times(++transportsAddr2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT); .newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
// Fail this one too // Fail this one too
assertNoCallbackInvoke(); assertNoCallbackInvoke();
@ -272,14 +277,14 @@ public class InternalSubchannelTest {
// Third attempt is the first address, thus controlled by the first back-off interval. // Third attempt is the first address, thus controlled by the first back-off interval.
fakeClock.forwardNanos(9); fakeClock.forwardNanos(9);
verify(mockTransportFactory, times(transportsAddr1)) verify(mockTransportFactory, times(transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState()); assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke(); assertNoCallbackInvoke();
fakeClock.forwardNanos(1); fakeClock.forwardNanos(1);
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1)) verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too // Fail this one too
transports.poll().listener.transportShutdown(Status.UNAVAILABLE); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
@ -289,7 +294,7 @@ public class InternalSubchannelTest {
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockBackoffPolicyProvider, times(backoffReset)).get(); verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr2)) verify(mockTransportFactory, times(++transportsAddr2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT); .newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too // Fail this one too
assertNoCallbackInvoke(); assertNoCallbackInvoke();
transports.poll().listener.transportShutdown(Status.RESOURCE_EXHAUSTED); transports.poll().listener.transportShutdown(Status.RESOURCE_EXHAUSTED);
@ -304,14 +309,14 @@ public class InternalSubchannelTest {
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState()); assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
fakeClock.forwardNanos(99); fakeClock.forwardNanos(99);
verify(mockTransportFactory, times(transportsAddr1)) verify(mockTransportFactory, times(transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState()); assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke(); assertNoCallbackInvoke();
fakeClock.forwardNanos(1); fakeClock.forwardNanos(1);
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1)) verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Let it through // Let it through
assertNoCallbackInvoke(); assertNoCallbackInvoke();
transports.peek().listener.transportReady(); transports.peek().listener.transportReady();
@ -332,7 +337,7 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockBackoffPolicyProvider, times(backoffReset)).get(); verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr1)) verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail the transport // Fail the transport
transports.poll().listener.transportShutdown(Status.UNAVAILABLE); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
@ -340,7 +345,7 @@ public class InternalSubchannelTest {
// Second attempt will start immediately. Still no new back-off policy. // Second attempt will start immediately. Still no new back-off policy.
verify(mockBackoffPolicyProvider, times(backoffReset)).get(); verify(mockBackoffPolicyProvider, times(backoffReset)).get();
verify(mockTransportFactory, times(++transportsAddr2)) verify(mockTransportFactory, times(++transportsAddr2))
.newClientTransport(addr2, AUTHORITY, USER_AGENT); .newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one too // Fail this one too
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
transports.poll().listener.transportShutdown(Status.UNAVAILABLE); 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. // Third attempt is the first address, thus controlled by the first back-off interval.
fakeClock.forwardNanos(9); fakeClock.forwardNanos(9);
verify(mockTransportFactory, times(transportsAddr1)) verify(mockTransportFactory, times(transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState()); assertEquals(TRANSIENT_FAILURE, internalSubchannel.getState());
assertNoCallbackInvoke(); assertNoCallbackInvoke();
fakeClock.forwardNanos(1); fakeClock.forwardNanos(1);
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
verify(mockTransportFactory, times(++transportsAddr1)) verify(mockTransportFactory, times(++transportsAddr1))
.newClientTransport(addr1, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
// Final checks on invocations on back-off policies // Final checks on invocations on back-off policies
verify(mockBackoffPolicy1, times(backoff1Consulted)).nextBackoffNanos(); verify(mockBackoffPolicy1, times(backoff1Consulted)).nextBackoffNanos();
@ -379,12 +384,12 @@ public class InternalSubchannelTest {
// First address fails // First address fails
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING"); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connects // Second address connects
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT); verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
transports.peek().listener.transportReady(); transports.peek().listener.transportReady();
assertExactCallbackInvokes("onStateChange:READY"); assertExactCallbackInvokes("onStateChange:READY");
assertEquals(READY, internalSubchannel.getState()); assertEquals(READY, internalSubchannel.getState());
@ -402,9 +407,10 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks()); 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); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory); verifyNoMoreInteractions(mockTransportFactory);
@ -421,12 +427,12 @@ public class InternalSubchannelTest {
// First address fails // First address fails
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING"); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connecting // Second address connecting
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT); verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
assertNoCallbackInvoke(); assertNoCallbackInvoke();
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
@ -445,9 +451,10 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks()); 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); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory); verifyNoMoreInteractions(mockTransportFactory);
@ -462,8 +469,10 @@ public class InternalSubchannelTest {
internalSubchannel.updateAddresses(new EquivalentAddressGroup(addr2)); internalSubchannel.updateAddresses(new EquivalentAddressGroup(addr2));
// Nothing happened on address update // Nothing happened on address update
verify(mockTransportFactory, never()).newClientTransport(addr1, AUTHORITY, USER_AGENT); verify(mockTransportFactory, never())
verify(mockTransportFactory, never()).newClientTransport(addr2, AUTHORITY, USER_AGENT); .newClientTransport(addr1, AUTHORITY, USER_AGENT, NO_PROXY);
verify(mockTransportFactory, never())
.newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
verifyNoMoreInteractions(mockTransportFactory); verifyNoMoreInteractions(mockTransportFactory);
assertNoCallbackInvoke(); assertNoCallbackInvoke();
assertEquals(IDLE, internalSubchannel.getState()); assertEquals(IDLE, internalSubchannel.getState());
@ -471,7 +480,7 @@ public class InternalSubchannelTest {
// But new address chosen when connecting // But new address chosen when connecting
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT); verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
// And no other addresses attempted // And no other addresses attempted
assertEquals(0, fakeClock.numPendingTasks()); assertEquals(0, fakeClock.numPendingTasks());
@ -494,12 +503,12 @@ public class InternalSubchannelTest {
// First address fails // First address fails
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING"); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connects // Second address connects
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT); verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
transports.peek().listener.transportReady(); transports.peek().listener.transportReady();
assertExactCallbackInvokes("onStateChange:READY"); assertExactCallbackInvokes("onStateChange:READY");
assertEquals(READY, internalSubchannel.getState()); assertEquals(READY, internalSubchannel.getState());
@ -517,9 +526,9 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks()); 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); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory); verifyNoMoreInteractions(mockTransportFactory);
@ -537,12 +546,12 @@ public class InternalSubchannelTest {
// First address fails // First address fails
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertExactCallbackInvokes("onStateChange:CONNECTING"); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
// Second address connecting // Second address connecting
verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT); verify(mockTransportFactory).newClientTransport(addr2, AUTHORITY, USER_AGENT, NO_PROXY);
assertNoCallbackInvoke(); assertNoCallbackInvoke();
assertEquals(CONNECTING, internalSubchannel.getState()); assertEquals(CONNECTING, internalSubchannel.getState());
@ -558,9 +567,9 @@ public class InternalSubchannelTest {
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
assertEquals(0, fakeClock.numPendingTasks()); 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); 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); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
verifyNoMoreInteractions(mockTransportFactory); verifyNoMoreInteractions(mockTransportFactory);
@ -577,13 +586,13 @@ public class InternalSubchannelTest {
// Won't connect until requested // Won't connect until requested
verify(mockTransportFactory, times(transportsCreated)) verify(mockTransportFactory, times(transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// First attempt // First attempt
internalSubchannel.obtainActiveTransport(); internalSubchannel.obtainActiveTransport();
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory, times(++transportsCreated)) verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one // Fail this one
transports.poll().listener.transportShutdown(Status.UNAVAILABLE); transports.poll().listener.transportShutdown(Status.UNAVAILABLE);
@ -593,7 +602,7 @@ public class InternalSubchannelTest {
fakeClock.forwardNanos(10); fakeClock.forwardNanos(10);
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory, times(++transportsCreated)) verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Make this one proceed // Make this one proceed
transports.peek().listener.transportReady(); transports.peek().listener.transportReady();
@ -610,7 +619,7 @@ public class InternalSubchannelTest {
internalSubchannel.obtainActiveTransport(); internalSubchannel.obtainActiveTransport();
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory, times(++transportsCreated)) verify(mockTransportFactory, times(++transportsCreated))
.newClientTransport(addr, AUTHORITY, USER_AGENT); .newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
} }
@Test @Test
@ -640,7 +649,7 @@ public class InternalSubchannelTest {
// First transport is created immediately // First transport is created immediately
internalSubchannel.obtainActiveTransport(); internalSubchannel.obtainActiveTransport();
assertExactCallbackInvokes("onStateChange:CONNECTING"); assertExactCallbackInvokes("onStateChange:CONNECTING");
verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT); verify(mockTransportFactory).newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
// Fail this one // Fail this one
MockClientTransportInfo transportInfo = transports.poll(); MockClientTransportInfo transportInfo = transports.poll();
@ -740,7 +749,8 @@ public class InternalSubchannelTest {
assertExactCallbackInvokes("onStateChange:SHUTDOWN", "onTerminated"); assertExactCallbackInvokes("onStateChange:SHUTDOWN", "onTerminated");
assertEquals(SHUTDOWN, internalSubchannel.getState()); assertEquals(SHUTDOWN, internalSubchannel.getState());
assertNull(internalSubchannel.obtainActiveTransport()); assertNull(internalSubchannel.obtainActiveTransport());
verify(mockTransportFactory, times(0)).newClientTransport(addr, AUTHORITY, USER_AGENT); verify(mockTransportFactory, times(0))
.newClientTransport(addr, AUTHORITY, USER_AGENT, NO_PROXY);
assertNoCallbackInvoke(); assertNoCallbackInvoke();
assertEquals(SHUTDOWN, internalSubchannel.getState()); assertEquals(SHUTDOWN, internalSubchannel.getState());
} }
@ -848,11 +858,43 @@ public class InternalSubchannelTest {
assertEquals(3, runnableInvokes.get()); 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) { private void createInternalSubchannel(SocketAddress ... addrs) {
createInternalSubChannelWithProxy(ProxyDetector.NOOP_INSTANCE, addrs);
}
private void createInternalSubChannelWithProxy(
ProxyDetector proxyDetector, SocketAddress ... addrs) {
addressGroup = new EquivalentAddressGroup(Arrays.asList(addrs)); addressGroup = new EquivalentAddressGroup(Arrays.asList(addrs));
internalSubchannel = new InternalSubchannel(addressGroup, AUTHORITY, USER_AGENT, internalSubchannel = new InternalSubchannel(addressGroup, AUTHORITY, USER_AGENT,
mockBackoffPolicyProvider, mockTransportFactory, fakeClock.getScheduledExecutorService(), mockBackoffPolicyProvider, mockTransportFactory, fakeClock.getScheduledExecutorService(),
fakeClock.getStopwatchSupplier(), channelExecutor, mockInternalSubchannelCallback); fakeClock.getStopwatchSupplier(), channelExecutor, mockInternalSubchannelCallback,
proxyDetector);
} }
private void assertNoCallbackInvoke() { private void assertNoCallbackInvoke() {

View File

@ -80,6 +80,7 @@ public class ManagedChannelImplIdlenessTest {
private final FakeClock oobExecutor = new FakeClock(); private final FakeClock oobExecutor = new FakeClock();
private static final String AUTHORITY = "fakeauthority"; private static final String AUTHORITY = "fakeauthority";
private static final String USER_AGENT = "fakeagent"; private static final String USER_AGENT = "fakeagent";
private static final ProxyParameters NO_PROXY = null;
private static final long IDLE_TIMEOUT_SECONDS = 30; private static final long IDLE_TIMEOUT_SECONDS = 30;
private ManagedChannelImpl channel; private ManagedChannelImpl channel;
@ -141,7 +142,8 @@ public class ManagedChannelImplIdlenessTest {
channel = new ManagedChannelImpl( channel = new ManagedChannelImpl(
builder, mockTransportFactory, new FakeBackoffPolicyProvider(), builder, mockTransportFactory, new FakeBackoffPolicyProvider(),
oobExecutorPool, timer.getStopwatchSupplier(), oobExecutorPool, timer.getStopwatchSupplier(),
Collections.<ClientInterceptor>emptyList()); Collections.<ClientInterceptor>emptyList(),
ProxyDetector.NOOP_INSTANCE);
newTransports = TestUtils.captureTransports(mockTransportFactory); newTransports = TestUtils.captureTransports(mockTransportFactory);
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
@ -155,7 +157,7 @@ public class ManagedChannelImplIdlenessTest {
// Verify the initial idleness // Verify the initial idleness
verify(mockLoadBalancerFactory, never()).newLoadBalancer(any(Helper.class)); verify(mockLoadBalancerFactory, never()).newLoadBalancer(any(Helper.class));
verify(mockTransportFactory, never()).newClientTransport( 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)); verify(mockNameResolver, never()).start(any(NameResolver.Listener.class));
} }
@ -352,11 +354,13 @@ public class ManagedChannelImplIdlenessTest {
// Now make an RPC on an OOB channel // Now make an RPC on an OOB channel
ManagedChannel oob = helper.createOobChannel(servers.get(0), "oobauthority"); ManagedChannel oob = helper.createOobChannel(servers.get(0), "oobauthority");
verify(mockTransportFactory, never()) 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); ClientCall<String, Integer> oobCall = oob.newCall(method, CallOptions.DEFAULT);
oobCall.start(mockCallListener2, new Metadata()); oobCall.start(mockCallListener2, new Metadata());
verify(mockTransportFactory) 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(); MockClientTransportInfo oobTransportInfo = newTransports.poll();
assertEquals(0, newTransports.size()); assertEquals(0, newTransports.size());
// The OOB transport reports in-use state // 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 serviceName = "fake.example.com";
private final String authority = serviceName; private final String authority = serviceName;
private final String userAgent = "userAgent"; private final String userAgent = "userAgent";
private final ProxyParameters noProxy = null;
private final String target = "fake://" + serviceName; private final String target = "fake://" + serviceName;
private URI expectedUri; private URI expectedUri;
private final SocketAddress socketAddress = new SocketAddress() {}; private final SocketAddress socketAddress = new SocketAddress() {};
@ -213,7 +214,7 @@ public class ManagedChannelImplTest {
checkState(channel == null); checkState(channel == null);
channel = new ManagedChannelImpl( channel = new ManagedChannelImpl(
builder, mockTransportFactory, new FakeBackoffPolicyProvider(), builder, mockTransportFactory, new FakeBackoffPolicyProvider(),
oobExecutorPool, timer.getStopwatchSupplier(), interceptors); oobExecutorPool, timer.getStopwatchSupplier(), interceptors, ProxyDetector.NOOP_INSTANCE);
if (requestConnection) { if (requestConnection) {
// Force-exit the initial idle-mode // Force-exit the initial idle-mode
@ -328,7 +329,7 @@ public class ManagedChannelImplTest {
Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY); Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY);
subchannel.requestConnection(); subchannel.requestConnection();
verify(mockTransportFactory).newClientTransport( 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(); MockClientTransportInfo transportInfo = transports.poll();
ConnectionClientTransport mockTransport = transportInfo.transport; ConnectionClientTransport mockTransport = transportInfo.transport;
verify(mockTransport).start(any(ManagedClientTransport.Listener.class)); verify(mockTransport).start(any(ManagedClientTransport.Listener.class));
@ -349,7 +350,7 @@ public class ManagedChannelImplTest {
// First RPC, will be pending // First RPC, will be pending
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT); ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
verify(mockTransportFactory).newClientTransport( 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); call.start(mockCallListener, headers);
verify(mockTransport, never()) verify(mockTransport, never())
@ -437,7 +438,7 @@ public class ManagedChannelImplTest {
verifyNoMoreInteractions(oobExecutorPool); verifyNoMoreInteractions(oobExecutorPool);
verify(mockTransportFactory).newClientTransport( 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(mockTransportFactory).close();
verify(mockTransport, atLeast(0)).getLogId(); verify(mockTransport, atLeast(0)).getLogId();
verifyNoMoreInteractions(mockTransport); verifyNoMoreInteractions(mockTransport);
@ -459,7 +460,7 @@ public class ManagedChannelImplTest {
subchannel1.requestConnection(); subchannel1.requestConnection();
subchannel2.requestConnection(); subchannel2.requestConnection();
verify(mockTransportFactory, times(2)).newClientTransport( 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 transportInfo1 = transports.poll();
MockClientTransportInfo transportInfo2 = transports.poll(); MockClientTransportInfo transportInfo2 = transports.poll();
@ -522,10 +523,10 @@ public class ManagedChannelImplTest {
// Make the transport available // Make the transport available
Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY); Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY);
verify(mockTransportFactory, never()).newClientTransport( 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(); subchannel.requestConnection();
verify(mockTransportFactory).newClientTransport( 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(); MockClientTransportInfo transportInfo = transports.poll();
ConnectionClientTransport mockTransport = transportInfo.transport; ConnectionClientTransport mockTransport = transportInfo.transport;
ManagedClientTransport.Listener transportListener = transportInfo.listener; ManagedClientTransport.Listener transportListener = transportInfo.listener;
@ -665,9 +666,11 @@ public class ManagedChannelImplTest {
// The channel will starts with the first address (badAddress) // The channel will starts with the first address (badAddress)
verify(mockTransportFactory) 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)) 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(); MockClientTransportInfo badTransportInfo = transports.poll();
// Which failed to connect // Which failed to connect
@ -676,7 +679,8 @@ public class ManagedChannelImplTest {
// The channel then try the second address (goodAddress) // The channel then try the second address (goodAddress)
verify(mockTransportFactory) 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(); MockClientTransportInfo goodTransportInfo = transports.poll();
when(goodTransportInfo.transport.newStream( when(goodTransportInfo.transport.newStream(
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class))) any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class)))
@ -807,15 +811,18 @@ public class ManagedChannelImplTest {
// Connecting to server1, which will fail // Connecting to server1, which will fail
verify(mockTransportFactory) 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)) 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(); MockClientTransportInfo transportInfo1 = transports.poll();
transportInfo1.listener.transportShutdown(Status.UNAVAILABLE); transportInfo1.listener.transportShutdown(Status.UNAVAILABLE);
// Connecting to server2, which will fail too // Connecting to server2, which will fail too
verify(mockTransportFactory) 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(); MockClientTransportInfo transportInfo2 = transports.poll();
Status server2Error = Status.UNAVAILABLE.withDescription("Server2 failed to connect"); Status server2Error = Status.UNAVAILABLE.withDescription("Server2 failed to connect");
transportInfo2.listener.transportShutdown(server2Error); transportInfo2.listener.transportShutdown(server2Error);
@ -863,20 +870,22 @@ public class ManagedChannelImplTest {
// requestConnection() // requestConnection()
verify(mockTransportFactory, never()).newClientTransport( 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(); sub1.requestConnection();
verify(mockTransportFactory).newClientTransport(socketAddress, authority, userAgent); verify(mockTransportFactory).newClientTransport(socketAddress, authority, userAgent, noProxy);
MockClientTransportInfo transportInfo1 = transports.poll(); MockClientTransportInfo transportInfo1 = transports.poll();
assertNotNull(transportInfo1); assertNotNull(transportInfo1);
sub2.requestConnection(); sub2.requestConnection();
verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent); verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent,
noProxy);
MockClientTransportInfo transportInfo2 = transports.poll(); MockClientTransportInfo transportInfo2 = transports.poll();
assertNotNull(transportInfo2); assertNotNull(transportInfo2);
sub1.requestConnection(); sub1.requestConnection();
sub2.requestConnection(); sub2.requestConnection();
verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent); verify(mockTransportFactory, times(2)).newClientTransport(socketAddress, authority, userAgent,
noProxy);
// shutdown() has a delay // shutdown() has a delay
sub1.shutdown(); sub1.shutdown();
@ -944,7 +953,7 @@ public class ManagedChannelImplTest {
sub2.shutdown(); sub2.shutdown();
assertTrue(channel.isTerminated()); assertTrue(channel.isTerminated());
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(), verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString()); anyString(), any(ProxyParameters.class));
} }
@Test @Test
@ -959,7 +968,7 @@ public class ManagedChannelImplTest {
// Therefore, channel is terminated without relying on LoadBalancer to shutdown subchannels. // Therefore, channel is terminated without relying on LoadBalancer to shutdown subchannels.
assertTrue(channel.isTerminated()); assertTrue(channel.isTerminated());
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(), verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString()); anyString(), any(ProxyParameters.class));
} }
@Test @Test
@ -977,7 +986,8 @@ public class ManagedChannelImplTest {
Metadata headers = new Metadata(); Metadata headers = new Metadata();
ClientCall<String, Integer> call = oob1.newCall(method, CallOptions.DEFAULT); ClientCall<String, Integer> call = oob1.newCall(method, CallOptions.DEFAULT);
call.start(mockCallListener, headers); call.start(mockCallListener, headers);
verify(mockTransportFactory).newClientTransport(socketAddress, "oob1authority", userAgent); verify(mockTransportFactory).newClientTransport(socketAddress, "oob1authority", userAgent,
noProxy);
MockClientTransportInfo transportInfo = transports.poll(); MockClientTransportInfo transportInfo = transports.poll();
assertNotNull(transportInfo); assertNotNull(transportInfo);
@ -998,7 +1008,7 @@ public class ManagedChannelImplTest {
oob1.newCall(method, CallOptions.DEFAULT.withWaitForReady()); oob1.newCall(method, CallOptions.DEFAULT.withWaitForReady());
call3.start(mockCallListener3, headers); call3.start(mockCallListener3, headers);
verify(mockTransportFactory, times(2)).newClientTransport( verify(mockTransportFactory, times(2)).newClientTransport(
socketAddress, "oob1authority", userAgent); socketAddress, "oob1authority", userAgent, noProxy);
transportInfo = transports.poll(); transportInfo = transports.poll();
assertNotNull(transportInfo); assertNotNull(transportInfo);
@ -1103,7 +1113,7 @@ public class ManagedChannelImplTest {
assertTrue(oob2.isTerminated()); assertTrue(oob2.isTerminated());
assertTrue(channel.isTerminated()); assertTrue(channel.isTerminated());
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(), verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString()); anyString(), any(ProxyParameters.class));
} }
@Test @Test
@ -1118,7 +1128,7 @@ public class ManagedChannelImplTest {
// Channel's shutdownNow() will call shutdownNow() on all subchannels and oobchannels. // Channel's shutdownNow() will call shutdownNow() on all subchannels and oobchannels.
// Therefore, channel is terminated without relying on LoadBalancer to shutdown oobchannels. // Therefore, channel is terminated without relying on LoadBalancer to shutdown oobchannels.
verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(), verify(mockTransportFactory, never()).newClientTransport(any(SocketAddress.class), anyString(),
anyString()); anyString(), any(ProxyParameters.class));
} }
@Test @Test
@ -1168,7 +1178,7 @@ public class ManagedChannelImplTest {
Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY); Subchannel subchannel = helper.createSubchannel(addressGroup, Attributes.EMPTY);
subchannel.requestConnection(); subchannel.requestConnection();
verify(mockTransportFactory).newClientTransport( verify(mockTransportFactory).newClientTransport(
same(socketAddress), eq(authority), eq(userAgent)); same(socketAddress), eq(authority), eq(userAgent), eq(noProxy));
MockClientTransportInfo transportInfo = transports.poll(); MockClientTransportInfo transportInfo = transports.poll();
final ConnectionClientTransport transport = transportInfo.transport; final ConnectionClientTransport transport = transportInfo.transport;
when(transport.getAttributes()).thenReturn(Attributes.EMPTY); when(transport.getAttributes()).thenReturn(Attributes.EMPTY);

View File

@ -89,7 +89,8 @@ final class TestUtils {
return mockTransport; return mockTransport;
} }
}).when(mockTransportFactory) }).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; return captor;
} }

View File

@ -17,6 +17,7 @@
package io.grpc.netty; package io.grpc.netty;
import io.grpc.Internal; import io.grpc.Internal;
import io.grpc.internal.ProxyParameters;
import java.net.SocketAddress; import java.net.SocketAddress;
/** /**
@ -44,7 +45,8 @@ public final class InternalNettyChannelBuilder {
extends NettyChannelBuilder.TransportCreationParamsFilterFactory { extends NettyChannelBuilder.TransportCreationParamsFilterFactory {
@Override @Override
TransportCreationParamsFilter create( 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.ConnectionClientTransport;
import io.grpc.internal.GrpcUtil; import io.grpc.internal.GrpcUtil;
import io.grpc.internal.KeepAliveManager; import io.grpc.internal.KeepAliveManager;
import io.grpc.internal.ProxyParameters;
import io.grpc.internal.SharedResourceHolder; import io.grpc.internal.SharedResourceHolder;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
@ -341,18 +342,13 @@ public final class NettyChannelBuilder
static ProtocolNegotiator createProtocolNegotiator( static ProtocolNegotiator createProtocolNegotiator(
String authority, String authority,
NegotiationType negotiationType, NegotiationType negotiationType,
SslContext sslContext) { SslContext sslContext,
ProxyParameters proxy) {
ProtocolNegotiator negotiator = ProtocolNegotiator negotiator =
createProtocolNegotiatorByType(authority, negotiationType, sslContext); createProtocolNegotiatorByType(authority, negotiationType, sslContext);
String proxy = System.getenv("GRPC_PROXY_EXP");
if (proxy != null) { if (proxy != null) {
String[] parts = proxy.split(":", 2); negotiator = ProtocolNegotiators.httpProxy(
int port = 80; proxy.proxyAddress, proxy.username, proxy.password, negotiator);
if (parts.length > 1) {
port = Integer.parseInt(parts[1]);
}
InetSocketAddress proxyAddress = new InetSocketAddress(parts[0], port);
negotiator = ProtocolNegotiators.httpProxy(proxyAddress, null, null, negotiator);
} }
return negotiator; return negotiator;
} }
@ -406,7 +402,10 @@ public final class NettyChannelBuilder
interface TransportCreationParamsFilterFactory { interface TransportCreationParamsFilterFactory {
@CheckReturnValue @CheckReturnValue
TransportCreationParamsFilter create( TransportCreationParamsFilter create(
SocketAddress targetServerAddress, String authority, @Nullable String userAgent); SocketAddress targetServerAddress,
String authority,
@Nullable String userAgent,
@Nullable ProxyParameters proxy);
} }
@CheckReturnValue @CheckReturnValue
@ -472,11 +471,12 @@ public final class NettyChannelBuilder
@Override @Override
public ConnectionClientTransport newClientTransport( 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."); checkState(!closed, "The transport factory is closed.");
TransportCreationParamsFilter dparams = TransportCreationParamsFilter dparams =
transportCreationParamsFilterFactory.create(serverAddress, authority, userAgent); transportCreationParamsFilterFactory.create(serverAddress, authority, userAgent, proxy);
final AtomicBackoff.State keepAliveTimeNanosState = keepAliveTimeNanos.getState(); final AtomicBackoff.State keepAliveTimeNanosState = keepAliveTimeNanos.getState();
Runnable tooManyPingsRunnable = new Runnable() { Runnable tooManyPingsRunnable = new Runnable() {
@ -528,8 +528,12 @@ public final class NettyChannelBuilder
@Override @Override
public TransportCreationParamsFilter create( public TransportCreationParamsFilter create(
SocketAddress targetServerAddress, String authority, String userAgent) { SocketAddress targetServerAddress,
return new DynamicNettyTransportParams(targetServerAddress, authority, userAgent); String authority,
String userAgent,
ProxyParameters proxyParams) {
return new DynamicNettyTransportParams(
targetServerAddress, authority, userAgent, proxyParams);
} }
@CheckReturnValue @CheckReturnValue
@ -538,12 +542,17 @@ public final class NettyChannelBuilder
private final SocketAddress targetServerAddress; private final SocketAddress targetServerAddress;
private final String authority; private final String authority;
@Nullable private final String userAgent; @Nullable private final String userAgent;
private ProxyParameters proxyParams;
private DynamicNettyTransportParams( private DynamicNettyTransportParams(
SocketAddress targetServerAddress, String authority, String userAgent) { SocketAddress targetServerAddress,
String authority,
String userAgent,
ProxyParameters proxyParams) {
this.targetServerAddress = targetServerAddress; this.targetServerAddress = targetServerAddress;
this.authority = authority; this.authority = authority;
this.userAgent = userAgent; this.userAgent = userAgent;
this.proxyParams = proxyParams;
} }
@Override @Override
@ -563,7 +572,7 @@ public final class NettyChannelBuilder
@Override @Override
public ProtocolNegotiator getProtocolNegotiator() { 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 static org.mockito.Mockito.mock;
import io.grpc.ManagedChannel; import io.grpc.ManagedChannel;
import io.grpc.internal.ProxyParameters;
import io.grpc.netty.InternalNettyChannelBuilder.OverrideAuthorityChecker; import io.grpc.netty.InternalNettyChannelBuilder.OverrideAuthorityChecker;
import io.grpc.netty.ProtocolNegotiators.TlsNegotiator; import io.grpc.netty.ProtocolNegotiators.TlsNegotiator;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
@ -39,6 +40,7 @@ public class NettyChannelBuilderTest {
@Rule public final ExpectedException thrown = ExpectedException.none(); @Rule public final ExpectedException thrown = ExpectedException.none();
private final SslContext noSslContext = null; private final SslContext noSslContext = null;
private final ProxyParameters noProxy = null;
private void shutdown(ManagedChannel mc) throws Exception { private void shutdown(ManagedChannel mc) throws Exception {
mc.shutdownNow(); mc.shutdownNow();
@ -141,7 +143,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator( ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"authority", "authority",
NegotiationType.PLAINTEXT, NegotiationType.PLAINTEXT,
noSslContext); noSslContext,
noProxy);
// just check that the classes are the same, and that negotiator is not null. // just check that the classes are the same, and that negotiator is not null.
assertTrue(negotiator instanceof ProtocolNegotiators.PlaintextNegotiator); assertTrue(negotiator instanceof ProtocolNegotiators.PlaintextNegotiator);
} }
@ -151,7 +154,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator( ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"authority", "authority",
NegotiationType.PLAINTEXT_UPGRADE, NegotiationType.PLAINTEXT_UPGRADE,
noSslContext); noSslContext,
noProxy);
// just check that the classes are the same, and that negotiator is not null. // just check that the classes are the same, and that negotiator is not null.
assertTrue(negotiator instanceof ProtocolNegotiators.PlaintextUpgradeNegotiator); assertTrue(negotiator instanceof ProtocolNegotiators.PlaintextUpgradeNegotiator);
} }
@ -162,7 +166,8 @@ public class NettyChannelBuilderTest {
NettyChannelBuilder.createProtocolNegotiator( NettyChannelBuilder.createProtocolNegotiator(
"authority:1234", "authority:1234",
NegotiationType.TLS, NegotiationType.TLS,
noSslContext); noSslContext,
noProxy);
} }
@Test @Test
@ -170,7 +175,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator( ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"authority:1234", "authority:1234",
NegotiationType.TLS, NegotiationType.TLS,
GrpcSslContexts.forClient().build()); GrpcSslContexts.forClient().build(),
noProxy);
assertTrue(negotiator instanceof ProtocolNegotiators.TlsNegotiator); assertTrue(negotiator instanceof ProtocolNegotiators.TlsNegotiator);
ProtocolNegotiators.TlsNegotiator n = (TlsNegotiator) negotiator; ProtocolNegotiators.TlsNegotiator n = (TlsNegotiator) negotiator;
@ -184,7 +190,8 @@ public class NettyChannelBuilderTest {
ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator( ProtocolNegotiator negotiator = NettyChannelBuilder.createProtocolNegotiator(
"bad_authority", "bad_authority",
NegotiationType.TLS, NegotiationType.TLS,
GrpcSslContexts.forClient().build()); GrpcSslContexts.forClient().build(),
noProxy);
assertTrue(negotiator instanceof ProtocolNegotiators.TlsNegotiator); assertTrue(negotiator instanceof ProtocolNegotiators.TlsNegotiator);
ProtocolNegotiators.TlsNegotiator n = (TlsNegotiator) negotiator; ProtocolNegotiators.TlsNegotiator n = (TlsNegotiator) negotiator;

View File

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

View File

@ -35,6 +35,7 @@ import io.grpc.internal.ClientTransportFactory;
import io.grpc.internal.ConnectionClientTransport; import io.grpc.internal.ConnectionClientTransport;
import io.grpc.internal.GrpcUtil; import io.grpc.internal.GrpcUtil;
import io.grpc.internal.KeepAliveManager; import io.grpc.internal.KeepAliveManager;
import io.grpc.internal.ProxyParameters;
import io.grpc.internal.SharedResourceHolder; import io.grpc.internal.SharedResourceHolder;
import io.grpc.internal.SharedResourceHolder.Resource; import io.grpc.internal.SharedResourceHolder.Resource;
import io.grpc.okhttp.internal.Platform; import io.grpc.okhttp.internal.Platform;
@ -403,20 +404,11 @@ public class OkHttpChannelBuilder extends
@Override @Override
public ConnectionClientTransport newClientTransport( public ConnectionClientTransport newClientTransport(
SocketAddress addr, String authority, @Nullable String userAgent) { SocketAddress addr, String authority, @Nullable String userAgent,
@Nullable ProxyParameters proxy) {
if (closed) { if (closed) {
throw new IllegalStateException("The transport factory is 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(); final AtomicBackoff.State keepAliveTimeNanosState = keepAliveTimeNanos.getState();
Runnable tooManyPingsRunnable = new Runnable() { Runnable tooManyPingsRunnable = new Runnable() {
@Override @Override
@ -425,9 +417,19 @@ public class OkHttpChannelBuilder extends
} }
}; };
InetSocketAddress inetSocketAddr = (InetSocketAddress) addr; InetSocketAddress inetSocketAddr = (InetSocketAddress) addr;
OkHttpClientTransport transport = new OkHttpClientTransport(inetSocketAddr, authority, OkHttpClientTransport transport = new OkHttpClientTransport(
userAgent, executor, socketFactory, hostnameVerifier, Utils.convertSpec(connectionSpec), inetSocketAddr,
maxMessageSize, proxyAddress, null, null, tooManyPingsRunnable); 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) { if (enableKeepAlive) {
transport.enableKeepAlive( transport.enableKeepAlive(
true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls); 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() { public void usePlaintext_newClientTransportAllowed() {
OkHttpChannelBuilder builder = OkHttpChannelBuilder.forAddress("host", 1234).usePlaintext(true); OkHttpChannelBuilder builder = OkHttpChannelBuilder.forAddress("host", 1234).usePlaintext(true);
builder.buildTransportFactory().newClientTransport(new InetSocketAddress(5678), builder.buildTransportFactory().newClientTransport(new InetSocketAddress(5678),
"dummy_authority", "dummy_userAgent"); "dummy_authority", "dummy_userAgent", null /* proxy */);
} }
@Test @Test

View File

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

View File

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