11

My Tomcat instance is listening to multiple IP addresses, but I want to control which source IP address is used when opening a URLConnection.

How can I specify this?

til
  • 205
  • 2
  • 5
stian
  • 2,874
  • 3
  • 24
  • 38
  • 1
    If my tomcat is listening on two IP adresses with different gateways (on different subnets) what will happen if the gateway for the IP address I specify as proxy is temporarily unavailable, will openConnection throw an exception or will it fall back to the the other gateway/ip? – stian Sep 18 '08 at 14:28

4 Answers4

6

This should do the trick:

URL url = new URL(yourUrlHere);
Proxy proxy = new Proxy(Proxy.Type.DIRECT, 
    new InetSocketAddress( 
        InetAddress.getByAddress(
            new byte[]{your, ip, interface, here}), yourTcpPortHere));
URLConnection conn = url.openConnection(proxy);

And you are done. Dont forget to handle exceptions nicely and off course change the values to suit your scenario.

Ah and I omitted the import statements

Javaxpert
  • 2,315
  • 1
  • 13
  • 4
  • 7
    This fails with *"java.lang.IllegalArgumentException: type DIRECT is not compatible with address my.ip.address.here"*. Diving into the source for Proxy, I see that this exception will be thrown *whenever* type DIRECT is attempted. – user359996 Sep 09 '11 at 01:03
  • This doesn't work for me and I can't understand why. Without the proxy it's fine. With the proxy and a virtual IP (second ip address on my localhost) specified I get a `ConnectException`. Testing with a socket instead of `URLConnection` also works fine, using the 4-arg socket constructor where local address is specified. Could someone else please confirm if they've got the above solution working with a second IP on local interface? – KomodoDave May 13 '13 at 17:00
  • What local port should I use? Isn't it supposed to be set by a lower level of the TCP stack to avoid collisions and make ports reusable? – vbence Oct 17 '13 at 13:33
  • 1
    This code can not work. I have reviewed `java.net.Proxy` since JDK 1.5 and always will throw exception at constructor. See first comment. This answer is not correct – pedrofb Aug 06 '16 at 10:51
3

Using the Apache commons HttpClient I have also found the following to work (removed try/catch for clarity):

HostConfiguration hostConfiguration = new HostConfiguration();
byte b[] = new byte[4];
b[0] = new Integer(192).byteValue();
b[1] = new Integer(168).byteValue();
b[2] = new Integer(1).byteValue();
b[3] = new Integer(11).byteValue();

hostConfiguration.setLocalAddress(InetAddress.getByAddress(b));

HttpClient client = new HttpClient();
client.setHostConfiguration(hostConfiguration);
GetMethod method = new GetMethod("http://remoteserver/");
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
    new DefaultHttpMethodRetryHandler(3, false));
int statusCode = client.executeMethod(method);

if (statusCode != HttpStatus.SC_OK) {
    System.err.println("Method failed: " + method.getStatusLine());
}

byte[] responseBody = method.getResponseBody();
System.out.println(new String(responseBody));");

However, I still wonder what would happen if the gateway of the IP is down (192.168.1.11 in this case). Will the next gateway be tried or will it fail?

stian
  • 2,874
  • 3
  • 24
  • 38
  • 1
    With the 4.1 codebase (and perhaps earlier?), you can specify the local IP address in a single line: **object.getParams().setParameter( "http.route.local-address", ipAddress )**, where **object** is either the **HttpClient** or the **HttpRequest**. – user359996 Mar 22 '11 at 05:24
1

The obvious portable way would be to set a Proxy in URL.openConnection. The proxy can be in local host, you can then write a very simple proxy that binds the local address of the client socket.

If you can't modify the source where the URL is connected, you can replace the URLStreamHandler either when calling the URL constructor or globally through URL.setURLStreamHandlerFactory. The URLStreamHandler can then delegate to the default http/https handler, modifying the openConnection call.

A more extreme method would be to completely replace the handler (perhaps extending the implementation in your JRE). Alternatively, alternative (open source) http clients are available.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

Setting manually socket work fine ...

private HttpsURLConnection openConnection(URL src, URL dest, SSLContext sslContext)
        throws IOException, ProtocolException {
    HttpsURLConnection connection = (HttpsURLConnection) dest.openConnection();
    HttpsHostNameVerifier httpsHostNameVerifier = new HttpsHostNameVerifier();
    connection.setHostnameVerifier(httpsHostNameVerifier);
    connection.setConnectTimeout(CONNECT_TIMEOUT);
    connection.setReadTimeout(READ_TIMEOUT);
    connection.setRequestMethod(POST_METHOD);
    connection.setRequestProperty(CONTENT_TYPE, SoapConstants.CONTENT_TYPE_HEADER);
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setSSLSocketFactory(sslContext.getSocketFactory());
    if ( src!=null ) {
        InetAddress inetAddress = InetAddress.getByName(src.getHost());
        int destPort = dest.getPort();
        if ( destPort <=0 ) 
            destPort=SERVER_HTTPS_PORT;
        int srcPort = src.getPort();
        if ( srcPort <=0 ) 
            srcPort=CLIENT_HTTPS_PORT;
         connectionSocket = connection.getSSLSocketFactory().createSocket(dest.getHost(), destPort, inetAddress, srcPort);
    }
    connection.connect();
    return connection;
}    
Enzo
  • 25
  • 2
  • 1
    Just to make sure I understand this correctly (because I have tested it and it worked), there's only the part where you create a socket that I would like to have some clarifications on. The createSocket() method forces the underlying socket factory to use the socket you've just created ? Because I don't see the connectionSocket variable being used anywhere else. The documentation only mentions "Returns a socket layered over an existing socket connected to the named host, at the given port." – schwarz Jan 30 '14 at 19:59