-
-
Notifications
You must be signed in to change notification settings - Fork 296
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Always sort result set from DNS resolver
- Loading branch information
Showing
4 changed files
with
214 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
core/src/main/java/ch/cyberduck/core/http/CustomHttpClientConnectionOperator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package ch.cyberduck.core.http; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.apache.http.HttpHost; | ||
import org.apache.http.client.protocol.HttpClientContext; | ||
import org.apache.http.config.Lookup; | ||
import org.apache.http.config.SocketConfig; | ||
import org.apache.http.conn.ConnectTimeoutException; | ||
import org.apache.http.conn.DnsResolver; | ||
import org.apache.http.conn.HttpClientConnectionOperator; | ||
import org.apache.http.conn.HttpHostConnectException; | ||
import org.apache.http.conn.ManagedHttpClientConnection; | ||
import org.apache.http.conn.SchemePortResolver; | ||
import org.apache.http.conn.UnsupportedSchemeException; | ||
import org.apache.http.conn.socket.ConnectionSocketFactory; | ||
import org.apache.http.conn.socket.LayeredConnectionSocketFactory; | ||
import org.apache.http.impl.conn.DefaultSchemePortResolver; | ||
import org.apache.http.impl.conn.SystemDefaultDnsResolver; | ||
import org.apache.http.protocol.HttpContext; | ||
import org.apache.http.util.Args; | ||
|
||
import java.io.IOException; | ||
import java.net.ConnectException; | ||
import java.net.InetAddress; | ||
import java.net.InetSocketAddress; | ||
import java.net.Socket; | ||
import java.net.SocketException; | ||
import java.net.SocketTimeoutException; | ||
|
||
public class CustomHttpClientConnectionOperator implements HttpClientConnectionOperator { | ||
|
||
static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry"; | ||
|
||
private final Log log = LogFactory.getLog(getClass()); | ||
|
||
private final Lookup<ConnectionSocketFactory> socketFactoryRegistry; | ||
private final SchemePortResolver schemePortResolver; | ||
private final DnsResolver dnsResolver; | ||
|
||
public CustomHttpClientConnectionOperator( | ||
final Lookup<ConnectionSocketFactory> socketFactoryRegistry, | ||
final SchemePortResolver schemePortResolver, | ||
final DnsResolver dnsResolver) { | ||
super(); | ||
Args.notNull(socketFactoryRegistry, "Socket factory registry"); | ||
this.socketFactoryRegistry = socketFactoryRegistry; | ||
this.schemePortResolver = schemePortResolver != null ? schemePortResolver : | ||
DefaultSchemePortResolver.INSTANCE; | ||
this.dnsResolver = dnsResolver != null ? dnsResolver : | ||
SystemDefaultDnsResolver.INSTANCE; | ||
} | ||
|
||
private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) { | ||
Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute( | ||
SOCKET_FACTORY_REGISTRY); | ||
if(reg == null) { | ||
reg = this.socketFactoryRegistry; | ||
} | ||
return reg; | ||
} | ||
|
||
@Override | ||
public void connect( | ||
final ManagedHttpClientConnection conn, | ||
final HttpHost host, | ||
final InetSocketAddress localAddress, | ||
final int connectTimeout, | ||
final SocketConfig socketConfig, | ||
final HttpContext context) throws IOException { | ||
final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context); | ||
final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); | ||
if(sf == null) { | ||
throw new UnsupportedSchemeException(host.getSchemeName() + | ||
" protocol is not supported"); | ||
} | ||
final InetAddress[] addresses = host.getAddress() != null ? | ||
new InetAddress[]{host.getAddress()} : this.dnsResolver.resolve(host.getHostName()); | ||
final int port = this.schemePortResolver.resolve(host); | ||
for(int i = 0; i < addresses.length; i++) { | ||
final InetAddress address = addresses[i]; | ||
final boolean last = i == addresses.length - 1; | ||
|
||
Socket sock = sf.createSocket(context); | ||
sock.setSoTimeout(socketConfig.getSoTimeout()); | ||
sock.setReuseAddress(socketConfig.isSoReuseAddress()); | ||
sock.setTcpNoDelay(socketConfig.isTcpNoDelay()); | ||
sock.setKeepAlive(socketConfig.isSoKeepAlive()); | ||
if(socketConfig.getRcvBufSize() > 0) { | ||
sock.setReceiveBufferSize(socketConfig.getRcvBufSize()); | ||
} | ||
if(socketConfig.getSndBufSize() > 0) { | ||
sock.setSendBufferSize(socketConfig.getSndBufSize()); | ||
} | ||
|
||
final int linger = socketConfig.getSoLinger(); | ||
if(linger >= 0) { | ||
sock.setSoLinger(true, linger); | ||
} | ||
conn.bind(sock); | ||
|
||
final InetSocketAddress remoteAddress = new InetSocketAddress(address, port); | ||
if(this.log.isDebugEnabled()) { | ||
this.log.debug("Connecting to " + remoteAddress); | ||
} | ||
try { | ||
sock = sf.connectSocket( | ||
connectTimeout, sock, host, remoteAddress, localAddress, context); | ||
conn.bind(sock); | ||
if(this.log.isDebugEnabled()) { | ||
this.log.debug("Connection established " + conn); | ||
} | ||
return; | ||
} | ||
catch(final SocketTimeoutException ex) { | ||
if(last) { | ||
throw new ConnectTimeoutException(ex, host, addresses); | ||
} | ||
} | ||
catch(final ConnectException ex) { | ||
if(last) { | ||
final String msg = ex.getMessage(); | ||
throw "Connection timed out".equals(msg) | ||
? new ConnectTimeoutException(ex, host, addresses) | ||
: new HttpHostConnectException(ex, host, addresses); | ||
} | ||
} | ||
catch(final SocketException ex) { | ||
if(last) { | ||
throw ex; | ||
} | ||
} | ||
if(this.log.isDebugEnabled()) { | ||
this.log.debug("Connect to " + remoteAddress + " timed out. " + | ||
"Connection will be retried using another IP address"); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void upgrade( | ||
final ManagedHttpClientConnection conn, | ||
final HttpHost host, | ||
final HttpContext context) throws IOException { | ||
final HttpClientContext clientContext = HttpClientContext.adapt(context); | ||
final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext); | ||
final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); | ||
if(sf == null) { | ||
throw new UnsupportedSchemeException(host.getSchemeName() + | ||
" protocol is not supported"); | ||
} | ||
if(!(sf instanceof LayeredConnectionSocketFactory)) { | ||
throw new UnsupportedSchemeException(host.getSchemeName() + | ||
" protocol does not support connection upgrade"); | ||
} | ||
final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf; | ||
Socket sock = conn.getSocket(); | ||
final int port = this.schemePortResolver.resolve(host); | ||
sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context); | ||
conn.bind(sock); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters