• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19760

02 Apr 2025 10:54AM UTC coverage: 88.566% (-0.02%) from 88.586%
#19760

push

github

web-flow
core: Delete the long-deprecated GRPC_PROXY_EXP (#11988)

"EXP" stood for experimental and all documentation that referenced it made it clear it was experimental. It's been some years since we started logging a message when it was used to say it will be deleted. There's no time like the present to delete it.

34672 of 39148 relevant lines covered (88.57%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.76
/../core/src/main/java/io/grpc/internal/ProxyDetectorImpl.java
1
/*
2
 * Copyright 2017 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.internal;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20

21
import com.google.common.annotations.VisibleForTesting;
22
import com.google.common.base.Supplier;
23
import io.grpc.HttpConnectProxiedSocketAddress;
24
import io.grpc.ProxiedSocketAddress;
25
import io.grpc.ProxyDetector;
26
import java.io.IOException;
27
import java.net.Authenticator;
28
import java.net.InetAddress;
29
import java.net.InetSocketAddress;
30
import java.net.MalformedURLException;
31
import java.net.PasswordAuthentication;
32
import java.net.Proxy;
33
import java.net.ProxySelector;
34
import java.net.SocketAddress;
35
import java.net.URI;
36
import java.net.URISyntaxException;
37
import java.net.URL;
38
import java.util.List;
39
import java.util.logging.Level;
40
import java.util.logging.Logger;
41
import javax.annotation.Nullable;
42

43
/**
44
 * A utility class that detects proxies using {@link ProxySelector} and detects authentication
45
 * credentials using {@link Authenticator}.
46
 *
47
 */
48
class ProxyDetectorImpl implements ProxyDetector {
49
  // To validate this code: set up a local squid proxy instance, and
50
  // try to communicate with grpc-test.sandbox.googleapis.com:443.
51
  // The endpoint runs an instance of TestServiceGrpc, see
52
  // AbstractInteropTest for an example how to run a
53
  // TestService.EmptyCall RPC.
54
  //
55
  // The instructions below assume Squid 3.5.23 and a recent
56
  // version of Debian.
57
  //
58
  // Set the contents of /etc/squid/squid.conf to be:
59
  // WARNING: THESE CONFIGS HAVE NOT BEEN REVIEWED FOR SECURITY, DO
60
  // NOT USE OUTSIDE OF TESTING. COMMENT OUT THIS WARNING TO
61
  // UNBREAK THE CONFIG FILE.
62
  // acl SSL_ports port 443
63
  // acl Safe_ports port 80
64
  // acl Safe_ports port 21
65
  // acl Safe_ports port 443
66
  // acl Safe_ports port 70
67
  // acl Safe_ports port 210
68
  // acl Safe_ports port 1025-65535
69
  // acl Safe_ports port 280
70
  // acl Safe_ports port 488
71
  // acl Safe_ports port 591
72
  // acl Safe_ports port 777
73
  // acl CONNECT method CONNECT
74
  // http_access deny !Safe_ports
75
  // http_access deny CONNECT !SSL_ports
76
  // http_access allow localhost manager
77
  // http_access deny manager
78
  // http_access allow localhost
79
  // http_access deny all
80
  // http_port 3128
81
  // coredump_dir /var/spool/squid
82
  // refresh_pattern ^ftp: 1440 20% 10080
83
  // refresh_pattern ^gopher: 1440 0% 1440
84
  // refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
85
  // refresh_pattern . 0 20% 4320
86
  //
87
  // Restart squid:
88
  // $ sudo /etc/init.d/squid restart
89
  //
90
  // To test with passwords:
91
  //
92
  // Run this command and follow the instructions to set up a user/pass:
93
  // $ sudo htpasswd -c /etc/squid/passwd myuser1
94
  //
95
  // Make the file readable to squid:
96
  // $ sudo chmod 644 /etc/squid/passwd
97
  //
98
  // Validate the username and password, you should see OK printed:
99
  // $ /usr/lib/squid3/basic_ncsa_auth /etc/squid/passwd
100
  // myuser1 <your password here>
101
  //
102
  // Add these additional lines to the beginning of squid.conf (the ordering matters):
103
  // auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/passwd
104
  // auth_param basic children 5
105
  // auth_param basic realm Squid proxy-caching web server
106
  // auth_param basic credentialsttl 2 hours
107
  // acl ncsa_users proxy_auth REQUIRED
108
  // http_access allow ncsa_users
109
  //
110
  // Restart squid:
111
  // $ sudo /etc/init.d/squid restart
112
  //
113
  // In both cases, start the JVM with -Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=3128 to
114
  // configure the proxy. For passwords, use java.net.Authenticator.setDefault().
115
  //
116
  // Testing with curl, no password:
117
  // $ curl -U myuser1:pass1 -x http://localhost:3128 -L grpc.io
118
  // Testing with curl, with password:
119
  // $ curl -U myuser1:pass1 -x http://localhost:3128 -L grpc.io
120
  //
121
  // It may be helpful to monitor the squid access logs:
122
  // $ sudo tail -f /var/log/squid/access.log
123

124
  private static final Logger log = Logger.getLogger(ProxyDetectorImpl.class.getName());
1✔
125
  private static final AuthenticationProvider DEFAULT_AUTHENTICATOR = new AuthenticationProvider() {
1✔
126
    @Override
127
    public PasswordAuthentication requestPasswordAuthentication(
128
        String host, InetAddress addr, int port, String protocol, String prompt, String scheme) {
129
      URL url = null;
×
130
      try {
131
        url = new URL(protocol, host, port, "");
×
132
      } catch (MalformedURLException e) {
×
133
        // let url be null
134
        log.log(
×
135
            Level.WARNING,
136
            "failed to create URL for Authenticator: {0} {1}", new Object[] {protocol, host});
137
      }
×
138
      return Authenticator.requestPasswordAuthentication(
×
139
          host, addr, port, protocol, prompt, scheme, url, Authenticator.RequestorType.PROXY);
140
    }
141
  };
142
  private static final Supplier<ProxySelector> DEFAULT_PROXY_SELECTOR =
1✔
143
      new Supplier<ProxySelector>() {
1✔
144
        @Override
145
        public ProxySelector get() {
146
          return ProxySelector.getDefault();
1✔
147
        }
148
      };
149

150
  // Do not hard code a ProxySelector because the global default ProxySelector can change
151
  private final Supplier<ProxySelector> proxySelector;
152
  private final AuthenticationProvider authenticationProvider;
153

154
  // We want an HTTPS proxy, which operates on the entire data stream (See IETF rfc2817).
155
  static final String PROXY_SCHEME = "https";
156

157
  /**
158
   * A proxy selector that uses the global {@link ProxySelector#getDefault()} and
159
   * {@link ProxyDetectorImpl.AuthenticationProvider} to detect proxy parameters.
160
   */
161
  public ProxyDetectorImpl() {
162
    this(DEFAULT_PROXY_SELECTOR, DEFAULT_AUTHENTICATOR);
1✔
163
  }
1✔
164

165
  @VisibleForTesting
166
  ProxyDetectorImpl(
167
      Supplier<ProxySelector> proxySelector,
168
      AuthenticationProvider authenticationProvider) {
1✔
169
    this.proxySelector = checkNotNull(proxySelector);
1✔
170
    this.authenticationProvider = checkNotNull(authenticationProvider);
1✔
171
  }
1✔
172

173
  @Nullable
174
  @Override
175
  public ProxiedSocketAddress proxyFor(SocketAddress targetServerAddress) throws IOException {
176
    if (!(targetServerAddress instanceof InetSocketAddress)) {
1✔
177
      return null;
1✔
178
    }
179
    return detectProxy((InetSocketAddress) targetServerAddress);
1✔
180
  }
181

182
  private ProxiedSocketAddress detectProxy(InetSocketAddress targetAddr) throws IOException {
183
    URI uri;
184
    String host = targetAddr.getHostString();
1✔
185
    try {
186
      uri =
1✔
187
          new URI(
188
              PROXY_SCHEME,
189
              null, /* userInfo */
190
              host,
191
              targetAddr.getPort(),
1✔
192
              null, /* path */
193
              null, /* query */
194
              null /* fragment */);
195
    } catch (final URISyntaxException e) {
×
196
      log.log(
×
197
          Level.WARNING,
198
          "Failed to construct URI for proxy lookup, proceeding without proxy",
199
          e);
200
      return null;
×
201
    }
1✔
202

203
    ProxySelector proxySelector = this.proxySelector.get();
1✔
204
    if (proxySelector == null) {
1✔
205
      log.log(Level.FINE, "proxy selector is null, so continuing without proxy lookup");
1✔
206
      return null;
1✔
207
    }
208

209
    List<Proxy> proxies = proxySelector.select(uri);
1✔
210
    if (proxies.size() > 1) {
1✔
211
      log.warning("More than 1 proxy detected, gRPC will select the first one");
1✔
212
    }
213
    Proxy proxy = proxies.get(0);
1✔
214

215
    if (proxy.type() == Proxy.Type.DIRECT) {
1✔
216
      return null;
1✔
217
    }
218
    InetSocketAddress proxyAddr = (InetSocketAddress) proxy.address();
1✔
219
    // The prompt string should be the realm as returned by the server.
220
    // We don't have it because we are avoiding the full handshake.
221
    String promptString = "";
1✔
222
    PasswordAuthentication auth =
1✔
223
        authenticationProvider.requestPasswordAuthentication(
1✔
224
            proxyAddr.getHostString(),
1✔
225
            proxyAddr.getAddress(),
1✔
226
            proxyAddr.getPort(),
1✔
227
            PROXY_SCHEME,
228
            promptString,
229
            null);
230

231
    final InetSocketAddress resolvedProxyAddr;
232
    if (proxyAddr.isUnresolved()) {
1✔
233
      InetAddress resolvedAddress = InetAddress.getByName(proxyAddr.getHostName());
1✔
234
      resolvedProxyAddr = new InetSocketAddress(resolvedAddress, proxyAddr.getPort());
1✔
235
    } else {
1✔
236
      resolvedProxyAddr = proxyAddr;
×
237
    }
238

239
    HttpConnectProxiedSocketAddress.Builder builder =
240
        HttpConnectProxiedSocketAddress.newBuilder()
1✔
241
        .setTargetAddress(targetAddr)
1✔
242
        .setProxyAddress(resolvedProxyAddr);
1✔
243

244
    if (auth == null) {
1✔
245
      return builder.build();
1✔
246
    }
247

248
    return builder
1✔
249
        .setUsername(auth.getUserName())
1✔
250
        .setPassword(auth.getPassword() == null ? null : new String(auth.getPassword()))
1✔
251
        .build();
1✔
252
  }
253

254
  /**
255
   * This interface makes unit testing easier by avoiding direct calls to static methods.
256
   */
257
  interface AuthenticationProvider {
258
    PasswordAuthentication requestPasswordAuthentication(
259
        String host,
260
        InetAddress addr,
261
        int port,
262
        String protocol,
263
        String prompt,
264
        String scheme);
265
  }
266
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc