|
17 | 17 | import static feign.Util.CONTENT_LENGTH; |
18 | 18 | import static feign.Util.ENCODING_DEFLATE; |
19 | 19 | import static feign.Util.ENCODING_GZIP; |
| 20 | +import static feign.Util.checkArgument; |
| 21 | +import static feign.Util.checkNotNull; |
| 22 | +import static feign.Util.isNotBlank; |
20 | 23 | import static java.lang.String.format; |
21 | 24 |
|
22 | 25 | import feign.Request.Options; |
23 | 26 | import java.io.IOException; |
24 | 27 | import java.io.InputStream; |
25 | 28 | import java.io.OutputStream; |
26 | 29 | import java.net.HttpURLConnection; |
| 30 | +import java.net.Proxy; |
27 | 31 | import java.net.URL; |
| 32 | +import java.nio.charset.StandardCharsets; |
| 33 | +import java.util.Base64; |
28 | 34 | import java.util.Collection; |
29 | 35 | import java.util.LinkedHashMap; |
30 | 36 | import java.util.List; |
@@ -65,9 +71,51 @@ public Response execute(Request request, Options options) throws IOException { |
65 | 71 | return convertResponse(connection, request); |
66 | 72 | } |
67 | 73 |
|
| 74 | + Response convertResponse(HttpURLConnection connection, Request request) throws IOException { |
| 75 | + int status = connection.getResponseCode(); |
| 76 | + String reason = connection.getResponseMessage(); |
| 77 | + |
| 78 | + if (status < 0) { |
| 79 | + throw new IOException( |
| 80 | + format( |
| 81 | + "Invalid status(%s) executing %s %s", |
| 82 | + status, connection.getRequestMethod(), connection.getURL())); |
| 83 | + } |
| 84 | + |
| 85 | + Map<String, Collection<String>> headers = new LinkedHashMap<>(); |
| 86 | + for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) { |
| 87 | + // response message |
| 88 | + if (field.getKey() != null) { |
| 89 | + headers.put(field.getKey(), field.getValue()); |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + Integer length = connection.getContentLength(); |
| 94 | + if (length == -1) { |
| 95 | + length = null; |
| 96 | + } |
| 97 | + InputStream stream; |
| 98 | + if (status >= 400) { |
| 99 | + stream = connection.getErrorStream(); |
| 100 | + } else { |
| 101 | + stream = connection.getInputStream(); |
| 102 | + } |
| 103 | + return Response.builder() |
| 104 | + .status(status) |
| 105 | + .reason(reason) |
| 106 | + .headers(headers) |
| 107 | + .request(request) |
| 108 | + .body(stream, length) |
| 109 | + .build(); |
| 110 | + } |
| 111 | + |
| 112 | + public HttpURLConnection getConnection(final URL url) throws IOException { |
| 113 | + return (HttpURLConnection) url.openConnection(); |
| 114 | + } |
| 115 | + |
68 | 116 | HttpURLConnection convertAndSend(Request request, Options options) throws IOException { |
69 | | - final HttpURLConnection connection = |
70 | | - (HttpURLConnection) new URL(request.url()).openConnection(); |
| 117 | + final URL url = new URL(request.url()); |
| 118 | + final HttpURLConnection connection = this.getConnection(url); |
71 | 119 | if (connection instanceof HttpsURLConnection) { |
72 | 120 | HttpsURLConnection sslCon = (HttpsURLConnection) connection; |
73 | 121 | if (sslContextFactory != null) { |
@@ -135,43 +183,52 @@ HttpURLConnection convertAndSend(Request request, Options options) throws IOExce |
135 | 183 | } |
136 | 184 | return connection; |
137 | 185 | } |
| 186 | + } |
| 187 | + |
| 188 | + /** Client that supports a {@link java.net.Proxy}. */ |
| 189 | + class Proxied extends Default { |
| 190 | + |
| 191 | + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; |
| 192 | + private final Proxy proxy; |
| 193 | + private String credentials; |
| 194 | + |
| 195 | + public Proxied( |
| 196 | + SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier, Proxy proxy) { |
| 197 | + super(sslContextFactory, hostnameVerifier); |
| 198 | + checkNotNull(proxy, "a proxy is required."); |
| 199 | + this.proxy = proxy; |
| 200 | + } |
138 | 201 |
|
139 | | - Response convertResponse(HttpURLConnection connection, Request request) throws IOException { |
140 | | - int status = connection.getResponseCode(); |
141 | | - String reason = connection.getResponseMessage(); |
| 202 | + public Proxied( |
| 203 | + SSLSocketFactory sslContextFactory, |
| 204 | + HostnameVerifier hostnameVerifier, |
| 205 | + Proxy proxy, |
| 206 | + String proxyUser, |
| 207 | + String proxyPassword) { |
| 208 | + this(sslContextFactory, hostnameVerifier, proxy); |
| 209 | + checkArgument(isNotBlank(proxyUser), "proxy user is required."); |
| 210 | + checkArgument(isNotBlank(proxyPassword), "proxy password is required."); |
| 211 | + this.credentials = basic(proxyUser, proxyPassword); |
| 212 | + } |
142 | 213 |
|
143 | | - if (status < 0) { |
144 | | - throw new IOException( |
145 | | - format( |
146 | | - "Invalid status(%s) executing %s %s", |
147 | | - status, connection.getRequestMethod(), connection.getURL())); |
| 214 | + @Override |
| 215 | + public HttpURLConnection getConnection(URL url) throws IOException { |
| 216 | + HttpURLConnection connection = (HttpURLConnection) url.openConnection(this.proxy); |
| 217 | + if (isNotBlank(this.credentials)) { |
| 218 | + connection.addRequestProperty(PROXY_AUTHORIZATION, this.credentials); |
148 | 219 | } |
| 220 | + return connection; |
| 221 | + } |
149 | 222 |
|
150 | | - Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>(); |
151 | | - for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) { |
152 | | - // response message |
153 | | - if (field.getKey() != null) { |
154 | | - headers.put(field.getKey(), field.getValue()); |
155 | | - } |
156 | | - } |
| 223 | + public String getCredentials() { |
| 224 | + return this.credentials; |
| 225 | + } |
157 | 226 |
|
158 | | - Integer length = connection.getContentLength(); |
159 | | - if (length == -1) { |
160 | | - length = null; |
161 | | - } |
162 | | - InputStream stream; |
163 | | - if (status >= 400) { |
164 | | - stream = connection.getErrorStream(); |
165 | | - } else { |
166 | | - stream = connection.getInputStream(); |
167 | | - } |
168 | | - return Response.builder() |
169 | | - .status(status) |
170 | | - .reason(reason) |
171 | | - .headers(headers) |
172 | | - .request(request) |
173 | | - .body(stream, length) |
174 | | - .build(); |
| 227 | + private String basic(String username, String password) { |
| 228 | + String token = username + ":" + password; |
| 229 | + byte[] bytes = token.getBytes(StandardCharsets.ISO_8859_1); |
| 230 | + String encoded = Base64.getEncoder().encodeToString(bytes); |
| 231 | + return "Basic " + encoded; |
175 | 232 | } |
176 | 233 | } |
177 | 234 | } |
0 commit comments