Skip to content

Commit 2515d28

Browse files
committed
Support multiple addresses when resolving names, close #968
1 parent 6ca14a1 commit 2515d28

File tree

15 files changed

+343
-169
lines changed

15 files changed

+343
-169
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.asynchttpclient.channel;
14+
15+
import static org.asynchttpclient.util.AsyncHttpProviderUtils.getExplicitPort;
16+
import static org.asynchttpclient.util.ProxyUtils.avoidProxy;
17+
18+
import java.net.InetSocketAddress;
19+
import java.net.UnknownHostException;
20+
21+
import org.asynchttpclient.AsyncHandler;
22+
import org.asynchttpclient.Request;
23+
import org.asynchttpclient.handler.AsyncHandlerExtensions;
24+
import org.asynchttpclient.proxy.ProxyServer;
25+
import org.asynchttpclient.uri.Uri;
26+
27+
public abstract class ChannelConnector {
28+
29+
protected final AsyncHandlerExtensions asyncHandlerExtensions;
30+
protected final InetSocketAddress localAddress;
31+
protected final InetSocketAddress[] remoteAddresses;
32+
protected volatile int i = 0;
33+
34+
public ChannelConnector(Request request, ProxyServer proxy, boolean useProxy, AsyncHandler<?> asyncHandler) throws UnknownHostException {
35+
36+
this.asyncHandlerExtensions = asyncHandler instanceof AsyncHandlerExtensions ? (AsyncHandlerExtensions) asyncHandler : null;
37+
NameResolution[] resolutions;
38+
Uri uri = request.getUri();
39+
int port = getExplicitPort(uri);
40+
41+
if (request.getInetAddress() != null) {
42+
resolutions = new NameResolution[] { new NameResolution(request.getInetAddress()) };
43+
44+
} else if (!useProxy || avoidProxy(proxy, uri.getHost())) {
45+
resolutions = request.getNameResolver().resolve(uri.getHost());
46+
47+
} else {
48+
resolutions = request.getNameResolver().resolve(proxy.getHost());
49+
port = proxy.getPort();
50+
}
51+
52+
if (asyncHandlerExtensions != null)
53+
asyncHandlerExtensions.onDnsResolved(resolutions);
54+
55+
remoteAddresses = new InetSocketAddress[resolutions.length];
56+
for (int i = 0; i < resolutions.length; i ++) {
57+
remoteAddresses[i] = new InetSocketAddress(resolutions[i].address, port);
58+
}
59+
60+
if (request.getLocalAddress() != null) {
61+
localAddress = new InetSocketAddress(request.getLocalAddress(), 0);
62+
63+
} else {
64+
localAddress = null;
65+
}
66+
}
67+
68+
protected boolean pickNextRemoteAddress() {
69+
i++;
70+
return i < remoteAddresses.length;
71+
}
72+
}
Lines changed: 32 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.asynchttpclient.channel;
14+
15+
import java.net.InetAddress;
16+
17+
public class NameResolution {
18+
19+
public static final long UNKNOWN_EXPIRATION = 0;
20+
21+
public final InetAddress address;
22+
public final long expiration;
23+
24+
public NameResolution(InetAddress address) {
25+
this(address, UNKNOWN_EXPIRATION);
26+
}
27+
28+
public NameResolution(InetAddress address, long expiration) {
29+
this.address = address;
30+
this.expiration = expiration;
31+
}
32+
}

api/src/main/java/org/asynchttpclient/channel/NameResolver.java

Lines changed: 7 additions & 3 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -17,15 +17,19 @@
17

17

18
public interface NameResolver {
18
public interface NameResolver {
19

19

20-
InetAddress resolve(String name) throws UnknownHostException;
20+
NameResolution[] resolve(String name) throws UnknownHostException;
21

21

22
enum JdkNameResolver implements NameResolver {
22
enum JdkNameResolver implements NameResolver {
23

23

24
INSTANCE;
24
INSTANCE;
25

25

26
@Override
26
@Override
27-
public InetAddress resolve(String name) throws UnknownHostException {
27+
public NameResolution[] resolve(String name) throws UnknownHostException {
28-
return InetAddress.getByName(name);
28+
InetAddress[] addresses = InetAddress.getAllByName(name);
29+
NameResolution[] resolutions = new NameResolution[addresses.length];
30+
for (int i = 0; i < addresses.length; i++)
31+
resolutions[i] = new NameResolution(addresses[i]);
32+
return resolutions;
29
}
33
}
30
}
34
}
31
}
35
}

api/src/main/java/org/asynchttpclient/handler/AsyncHandlerExtensions.java

Lines changed: 20 additions & 9 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -15,6 +15,7 @@
15
import java.net.InetAddress;
15
import java.net.InetAddress;
16

16

17
import org.asynchttpclient.AsyncHandler;
17
import org.asynchttpclient.AsyncHandler;
18+
import org.asynchttpclient.channel.NameResolution;
18

19

19
/**
20
/**
20
* This interface hosts new low level callback methods on {@link AsyncHandler}.
21
* This interface hosts new low level callback methods on {@link AsyncHandler}.
@@ -30,11 +31,28 @@ public interface AsyncHandlerExtensions {
30
void onConnectionOpen();
31
void onConnectionOpen();
31

32

32
/**
33
/**
33-
* Notify the callback when a new connection was successfully opened.
34+
* Notify the callback after DNS resolution has completed.
35+
*
36+
* @param addresses the resolved addresses
37+
*/
38+
void onDnsResolved(NameResolution[] addresses);
39+
40+
/**
41+
* Notify the callback after a successful connect
34
*
42
*
35
* @param connection the connection
43
* @param connection the connection
44+
* @param address the connected addresses
36
*/
45
*/
37-
void onConnectionOpened(Object connection);
46+
void onConnectionSuccess(Object connection, InetAddress address);
47+
48+
/**
49+
* Notify the callback after a failed connect.
50+
* Might be called several times, or be followed by onConnectionSuccess
51+
* when the name was resolved to multiple addresses.
52+
*
53+
* @param address the tentative addresses
54+
*/
55+
void onConnectionFailure(InetAddress address);
38

56

39
/**
57
/**
40
* Notify the callback when trying to fetch a connection from the pool.
58
* Notify the callback when trying to fetch a connection from the pool.
@@ -70,13 +88,6 @@ public interface AsyncHandlerExtensions {
70
*/
88
*/
71
void onRetry();
89
void onRetry();
72

90

73-
/**
74-
* Notify the callback after DNS resolution has completed.
75-
*
76-
* @param address the resolved address
77-
*/
78-
void onDnsResolved(InetAddress address);
79-
80
/**
91
/**
81
* Notify the callback when the SSL handshake performed to establish an
92
* Notify the callback when the SSL handshake performed to establish an
82
* HTTPS connection has been completed.
93
* HTTPS connection has been completed.

api/src/test/java/org/asynchttpclient/AsyncProvidersBasicTest.java

Lines changed: 15 additions & 15 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -16,6 +16,7 @@
16
package org.asynchttpclient;
16
package org.asynchttpclient;
17

17

18
import static java.nio.charset.StandardCharsets.UTF_8;
18
import static java.nio.charset.StandardCharsets.UTF_8;
19+
import static org.asynchttpclient.test.EventCollectingHandler.*;
19
import static org.asynchttpclient.test.TestUtils.TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET;
20
import static org.asynchttpclient.test.TestUtils.TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET;
20
import static org.asynchttpclient.test.TestUtils.findFreePort;
21
import static org.asynchttpclient.test.TestUtils.findFreePort;
21
import static org.asynchttpclient.util.DateUtils.millisTime;
22
import static org.asynchttpclient.util.DateUtils.millisTime;
@@ -46,8 +47,6 @@
46
import java.util.concurrent.atomic.AtomicInteger;
47
import java.util.concurrent.atomic.AtomicInteger;
47
import java.util.concurrent.atomic.AtomicReference;
48
import java.util.concurrent.atomic.AtomicReference;
48

49

49-
import org.asynchttpclient.AsyncHttpClient;
50-
import org.asynchttpclient.AsyncHttpProviderConfig;
51
import org.asynchttpclient.AsyncHttpClientConfig.Builder;
50
import org.asynchttpclient.AsyncHttpClientConfig.Builder;
52
import org.asynchttpclient.config.AsyncHttpClientConfigBean;
51
import org.asynchttpclient.config.AsyncHttpClientConfigBean;
53
import org.asynchttpclient.cookie.Cookie;
52
import org.asynchttpclient.cookie.Cookie;
@@ -1410,19 +1409,20 @@ public void testNewConnectionEventsFired() throws Exception {
1410
client.executeRequest(request, handler).get(3, TimeUnit.SECONDS);
1409
client.executeRequest(request, handler).get(3, TimeUnit.SECONDS);
1411
handler.waitForCompletion(3, TimeUnit.SECONDS);
1410
handler.waitForCompletion(3, TimeUnit.SECONDS);
1412

1411

1413-
List<String> expectedEvents = Arrays.asList(
1412+
Object[] expectedEvents = new Object[] {
1414-
"ConnectionPool",
1413+
CONNECTION_POOL_EVENT,
1415-
"ConnectionOpen",
1414+
CONNECTION_OPEN_EVENT,
1416-
"DnsResolved",
1415+
DNS_RESOLVED_EVENT,
1417-
"ConnectionOpened",
1416+
CONNECTION_SUCCESS_EVENT,
1418-
"RequestSend",
1417+
REQUEST_SEND_EVENT,
1419-
"HeadersWritten",
1418+
HEADERS_WRITTEN_EVENT,
1420-
"StatusReceived",
1419+
STATUS_RECEIVED_EVENT,
1421-
"HeadersReceived",
1420+
HEADERS_RECEIVED_EVENT,
1422-
"ConnectionOffer",
1421+
CONNECTION_OFFER_EVENT,
1423-
"Completed");
1422+
COMPLETED_EVENT
1424-
1423+
};
1425-
assertEquals(handler.firedEvents, expectedEvents, "Got " + Arrays.toString(handler.firedEvents.toArray()));
1424+
1425+
assertEquals(handler.firedEvents.toArray(), expectedEvents, "Got " + Arrays.toString(handler.firedEvents.toArray()));
1426
}
1426
}
1427
}
1427
}
1428
}
1428
}

api/src/test/java/org/asynchttpclient/BasicHttpsTest.java

Lines changed: 15 additions & 15 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -15,6 +15,7 @@
15
*/
15
*/
16
package org.asynchttpclient;
16
package org.asynchttpclient;
17

17

18+
import static org.asynchttpclient.test.EventCollectingHandler.*;
18
import static org.asynchttpclient.test.TestUtils.SIMPLE_TEXT_FILE;
19
import static org.asynchttpclient.test.TestUtils.SIMPLE_TEXT_FILE;
19
import static org.asynchttpclient.test.TestUtils.SIMPLE_TEXT_FILE_STRING;
20
import static org.asynchttpclient.test.TestUtils.SIMPLE_TEXT_FILE_STRING;
20
import static org.asynchttpclient.test.TestUtils.createSSLContext;
21
import static org.asynchttpclient.test.TestUtils.createSSLContext;
@@ -28,7 +29,6 @@
28
import javax.servlet.http.HttpServletResponse;
29
import javax.servlet.http.HttpServletResponse;
29

30

30
import java.util.Arrays;
31
import java.util.Arrays;
31-
import java.util.List;
32
import java.util.concurrent.ExecutionException;
32
import java.util.concurrent.ExecutionException;
33
import java.util.concurrent.TimeUnit;
33
import java.util.concurrent.TimeUnit;
34
import java.util.concurrent.TimeoutException;
34
import java.util.concurrent.TimeoutException;
@@ -125,20 +125,20 @@ public void testNormalEventsFired() throws InterruptedException, TimeoutExceptio
125
client.preparePost(getTargetUrl()).setBody("whatever").execute(handler).get(3, TimeUnit.SECONDS);
125
client.preparePost(getTargetUrl()).setBody("whatever").execute(handler).get(3, TimeUnit.SECONDS);
126
handler.waitForCompletion(3, TimeUnit.SECONDS);
126
handler.waitForCompletion(3, TimeUnit.SECONDS);
127

127

128-
List<String> expectedEvents = Arrays.asList(
128+
Object[] expectedEvents = new Object[] {
129-
"ConnectionPool",
129+
CONNECTION_POOL_EVENT,
130-
"ConnectionOpen",
130+
CONNECTION_OPEN_EVENT,
131-
"DnsResolved",
131+
DNS_RESOLVED_EVENT,
132-
"SslHandshakeCompleted",
132+
SSL_HANDSHAKE_COMPLETED_EVENT,
133-
"ConnectionOpened",
133+
CONNECTION_SUCCESS_EVENT,
134-
"RequestSend",
134+
REQUEST_SEND_EVENT,
135-
"HeadersWritten",
135+
HEADERS_WRITTEN_EVENT,
136-
"StatusReceived",
136+
STATUS_RECEIVED_EVENT,
137-
"HeadersReceived",
137+
HEADERS_RECEIVED_EVENT,
138-
"ConnectionOffer",
138+
CONNECTION_OFFER_EVENT,
139-
"Completed");
139+
COMPLETED_EVENT};
140-
140+
141-
assertEquals(handler.firedEvents, expectedEvents, "Got " + Arrays.toString(handler.firedEvents.toArray()));
141+
assertEquals(handler.firedEvents.toArray(), expectedEvents, "Got " + Arrays.toString(handler.firedEvents.toArray()));
142
}
142
}
143
}
143
}
144
}
144
}

api/src/test/java/org/asynchttpclient/channel/pool/ConnectionPoolTest.java

Lines changed: 12 additions & 11 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -19,6 +19,7 @@
19
import static org.testng.Assert.assertNotNull;
19
import static org.testng.Assert.assertNotNull;
20
import static org.testng.Assert.assertNull;
20
import static org.testng.Assert.assertNull;
21
import static org.testng.Assert.fail;
21
import static org.testng.Assert.fail;
22+
import static org.asynchttpclient.test.EventCollectingHandler.*;
22

23

23
import java.io.IOException;
24
import java.io.IOException;
24
import java.util.ArrayList;
25
import java.util.ArrayList;
@@ -285,17 +286,17 @@ public void testPooledEventsFired() throws Exception {
285
client.executeRequest(request, secondHandler).get(3, TimeUnit.SECONDS);
286
client.executeRequest(request, secondHandler).get(3, TimeUnit.SECONDS);
286
secondHandler.waitForCompletion(3, TimeUnit.SECONDS);
287
secondHandler.waitForCompletion(3, TimeUnit.SECONDS);
287

288

288-
List<String> expectedEvents = Arrays.asList(
289+
Object[] expectedEvents = new Object[] {
289-
"ConnectionPool",
290+
CONNECTION_POOL_EVENT,
290-
"ConnectionPooled",
291+
CONNECTION_POOLED_EVENT,
291-
"RequestSend",
292+
REQUEST_SEND_EVENT,
292-
"HeadersWritten",
293+
HEADERS_WRITTEN_EVENT,
293-
"StatusReceived",
294+
STATUS_RECEIVED_EVENT,
294-
"HeadersReceived",
295+
HEADERS_RECEIVED_EVENT,
295-
"ConnectionOffer",
296+
CONNECTION_OFFER_EVENT,
296-
"Completed");
297+
COMPLETED_EVENT};
297-
298+
298-
assertEquals(secondHandler.firedEvents, expectedEvents, "Got " + Arrays.toString(secondHandler.firedEvents.toArray()));
299+
assertEquals(secondHandler.firedEvents.toArray(), expectedEvents, "Got " + Arrays.toString(secondHandler.firedEvents.toArray()));
299
}
300
}
300
}
301
}
301
}
302
}

0 commit comments

Comments
 (0)