001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027package org.apache.http.impl.conn; 028 029import java.io.IOException; 030import java.net.ConnectException; 031import java.net.InetAddress; 032import java.net.InetSocketAddress; 033import java.net.NoRouteToHostException; 034import java.net.Socket; 035import java.net.SocketTimeoutException; 036 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039import org.apache.http.HttpHost; 040import org.apache.http.annotation.Contract; 041import org.apache.http.annotation.ThreadingBehavior; 042import org.apache.http.client.protocol.HttpClientContext; 043import org.apache.http.config.Lookup; 044import org.apache.http.config.SocketConfig; 045import org.apache.http.conn.ConnectTimeoutException; 046import org.apache.http.conn.DnsResolver; 047import org.apache.http.conn.HttpClientConnectionOperator; 048import org.apache.http.conn.HttpHostConnectException; 049import org.apache.http.conn.ManagedHttpClientConnection; 050import org.apache.http.conn.SchemePortResolver; 051import org.apache.http.conn.UnsupportedSchemeException; 052import org.apache.http.conn.socket.ConnectionSocketFactory; 053import org.apache.http.conn.socket.LayeredConnectionSocketFactory; 054import org.apache.http.protocol.HttpContext; 055import org.apache.http.util.Args; 056 057/** 058 * Default implementation of {@link HttpClientConnectionOperator} used as default in Http client, 059 * when no instance provided by user to {@link BasicHttpClientConnectionManager} or {@link 060 * PoolingHttpClientConnectionManager} constructor. 061 * 062 * @since 4.4 063 */ 064@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) 065public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator { 066 067 static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry"; 068 069 private final Log log = LogFactory.getLog(getClass()); 070 071 private final Lookup<ConnectionSocketFactory> socketFactoryRegistry; 072 private final SchemePortResolver schemePortResolver; 073 private final DnsResolver dnsResolver; 074 075 public DefaultHttpClientConnectionOperator( 076 final Lookup<ConnectionSocketFactory> socketFactoryRegistry, 077 final SchemePortResolver schemePortResolver, 078 final DnsResolver dnsResolver) { 079 super(); 080 Args.notNull(socketFactoryRegistry, "Socket factory registry"); 081 this.socketFactoryRegistry = socketFactoryRegistry; 082 this.schemePortResolver = schemePortResolver != null ? schemePortResolver : 083 DefaultSchemePortResolver.INSTANCE; 084 this.dnsResolver = dnsResolver != null ? dnsResolver : 085 SystemDefaultDnsResolver.INSTANCE; 086 } 087 088 @SuppressWarnings("unchecked") 089 private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) { 090 Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute( 091 SOCKET_FACTORY_REGISTRY); 092 if (reg == null) { 093 reg = this.socketFactoryRegistry; 094 } 095 return reg; 096 } 097 098 @Override 099 public void connect( 100 final ManagedHttpClientConnection conn, 101 final HttpHost host, 102 final InetSocketAddress localAddress, 103 final int connectTimeout, 104 final SocketConfig socketConfig, 105 final HttpContext context) throws IOException { 106 final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context); 107 final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); 108 if (sf == null) { 109 throw new UnsupportedSchemeException(host.getSchemeName() + 110 " protocol is not supported"); 111 } 112 final InetAddress[] addresses = host.getAddress() != null ? 113 new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName()); 114 final int port = this.schemePortResolver.resolve(host); 115 for (int i = 0; i < addresses.length; i++) { 116 final InetAddress address = addresses[i]; 117 final boolean last = i == addresses.length - 1; 118 119 Socket sock = sf.createSocket(context); 120 sock.setSoTimeout(socketConfig.getSoTimeout()); 121 sock.setReuseAddress(socketConfig.isSoReuseAddress()); 122 sock.setTcpNoDelay(socketConfig.isTcpNoDelay()); 123 sock.setKeepAlive(socketConfig.isSoKeepAlive()); 124 if (socketConfig.getRcvBufSize() > 0) { 125 sock.setReceiveBufferSize(socketConfig.getRcvBufSize()); 126 } 127 if (socketConfig.getSndBufSize() > 0) { 128 sock.setSendBufferSize(socketConfig.getSndBufSize()); 129 } 130 131 final int linger = socketConfig.getSoLinger(); 132 if (linger >= 0) { 133 sock.setSoLinger(true, linger); 134 } 135 conn.bind(sock); 136 137 final InetSocketAddress remoteAddress = new InetSocketAddress(address, port); 138 if (this.log.isDebugEnabled()) { 139 this.log.debug("Connecting to " + remoteAddress); 140 } 141 try { 142 sock = sf.connectSocket( 143 connectTimeout, sock, host, remoteAddress, localAddress, context); 144 conn.bind(sock); 145 if (this.log.isDebugEnabled()) { 146 this.log.debug("Connection established " + conn); 147 } 148 return; 149 } catch (final SocketTimeoutException ex) { 150 if (last) { 151 throw new ConnectTimeoutException(ex, host, addresses); 152 } 153 } catch (final ConnectException ex) { 154 if (last) { 155 final String msg = ex.getMessage(); 156 if ("Connection timed out".equals(msg)) { 157 throw new ConnectTimeoutException(ex, host, addresses); 158 } else { 159 throw new HttpHostConnectException(ex, host, addresses); 160 } 161 } 162 } catch (final NoRouteToHostException ex) { 163 if (last) { 164 throw ex; 165 } 166 } 167 if (this.log.isDebugEnabled()) { 168 this.log.debug("Connect to " + remoteAddress + " timed out. " + 169 "Connection will be retried using another IP address"); 170 } 171 } 172 } 173 174 @Override 175 public void upgrade( 176 final ManagedHttpClientConnection conn, 177 final HttpHost host, 178 final HttpContext context) throws IOException { 179 final HttpClientContext clientContext = HttpClientContext.adapt(context); 180 final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext); 181 final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); 182 if (sf == null) { 183 throw new UnsupportedSchemeException(host.getSchemeName() + 184 " protocol is not supported"); 185 } 186 if (!(sf instanceof LayeredConnectionSocketFactory)) { 187 throw new UnsupportedSchemeException(host.getSchemeName() + 188 " protocol does not support connection upgrade"); 189 } 190 final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf; 191 Socket sock = conn.getSocket(); 192 final int port = this.schemePortResolver.resolve(host); 193 sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context); 194 conn.bind(sock); 195 } 196 197}