Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
28 package ch.boye.httpclientandroidlib.impl.conn;
30 import java.io.IOException;
31 import java.net.ConnectException;
32 import java.net.InetSocketAddress;
33 import java.net.Socket;
34 import java.net.InetAddress;
35 import java.net.UnknownHostException;
37 import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
38 /* LogFactory removed by HttpClient for Android script. */
39 import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
41 import ch.boye.httpclientandroidlib.HttpHost;
42 import ch.boye.httpclientandroidlib.params.HttpParams;
43 import ch.boye.httpclientandroidlib.params.HttpConnectionParams;
44 import ch.boye.httpclientandroidlib.protocol.HttpContext;
46 import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException;
47 import ch.boye.httpclientandroidlib.conn.HttpHostConnectException;
48 import ch.boye.httpclientandroidlib.conn.OperatedClientConnection;
49 import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator;
50 import ch.boye.httpclientandroidlib.conn.scheme.LayeredSchemeSocketFactory;
51 import ch.boye.httpclientandroidlib.conn.scheme.Scheme;
52 import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry;
53 import ch.boye.httpclientandroidlib.conn.scheme.SchemeSocketFactory;
55 /**
56 * Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry}
57 * to look up {@link SchemeSocketFactory} objects.
58 * <p>
59 * This connection operator is multihome network aware and will attempt to retry failed connects
60 * against all known IP addresses sequentially until the connect is successful or all known
61 * addresses fail to respond. Please note the same
62 * {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT} value will be used
63 * for each connection attempt, so in the worst case the total elapsed time before timeout
64 * can be <code>CONNECTION_TIMEOUT * n</code> where <code>n</code> is the number of IP addresses
65 * of the given host. One can disable multihome support by overriding
66 * the {@link #resolveHostname(String)} method and returning only one IP address for the given
67 * host name.
68 * <p>
69 * The following parameters can be used to customize the behavior of this
70 * class:
71 * <ul>
72 * <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
73 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}</li>
74 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}</li>
75 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}</li>
76 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}</li>
77 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li>
78 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li>
79 * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
80 * </ul>
81 *
82 * @since 4.0
83 */
84 @ThreadSafe
85 public class DefaultClientConnectionOperator implements ClientConnectionOperator {
87 public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
89 /** The scheme registry for looking up socket factories. */
90 protected final SchemeRegistry schemeRegistry; // @ThreadSafe
92 /**
93 * Creates a new client connection operator for the given scheme registry.
94 *
95 * @param schemes the scheme registry
96 */
97 public DefaultClientConnectionOperator(final SchemeRegistry schemes) {
98 if (schemes == null) {
99 throw new IllegalArgumentException("Scheme registry amy not be null");
100 }
101 this.schemeRegistry = schemes;
102 }
104 public OperatedClientConnection createConnection() {
105 return new DefaultClientConnection();
106 }
108 public void openConnection(
109 final OperatedClientConnection conn,
110 final HttpHost target,
111 final InetAddress local,
112 final HttpContext context,
113 final HttpParams params) throws IOException {
114 if (conn == null) {
115 throw new IllegalArgumentException("Connection may not be null");
116 }
117 if (target == null) {
118 throw new IllegalArgumentException("Target host may not be null");
119 }
120 if (params == null) {
121 throw new IllegalArgumentException("Parameters may not be null");
122 }
123 if (conn.isOpen()) {
124 throw new IllegalStateException("Connection must not be open");
125 }
127 Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
128 SchemeSocketFactory sf = schm.getSchemeSocketFactory();
130 InetAddress[] addresses = resolveHostname(target.getHostName());
131 int port = schm.resolvePort(target.getPort());
132 for (int i = 0; i < addresses.length; i++) {
133 InetAddress address = addresses[i];
134 boolean last = i == addresses.length - 1;
136 Socket sock = sf.createSocket(params);
137 conn.opening(sock, target);
139 InetSocketAddress remoteAddress = new HttpInetSocketAddress(target, address, port);
140 InetSocketAddress localAddress = null;
141 if (local != null) {
142 localAddress = new InetSocketAddress(local, 0);
143 }
144 if (this.log.isDebugEnabled()) {
145 this.log.debug("Connecting to " + remoteAddress);
146 }
147 try {
148 Socket connsock = sf.connectSocket(sock, remoteAddress, localAddress, params);
149 if (sock != connsock) {
150 sock = connsock;
151 conn.opening(sock, target);
152 }
153 prepareSocket(sock, context, params);
154 conn.openCompleted(sf.isSecure(sock), params);
155 return;
156 } catch (ConnectException ex) {
157 if (last) {
158 throw new HttpHostConnectException(target, ex);
159 }
160 } catch (ConnectTimeoutException ex) {
161 if (last) {
162 throw ex;
163 }
164 }
165 if (this.log.isDebugEnabled()) {
166 this.log.debug("Connect to " + remoteAddress + " timed out. " +
167 "Connection will be retried using another IP address");
168 }
169 }
170 }
172 public void updateSecureConnection(
173 final OperatedClientConnection conn,
174 final HttpHost target,
175 final HttpContext context,
176 final HttpParams params) throws IOException {
177 if (conn == null) {
178 throw new IllegalArgumentException("Connection may not be null");
179 }
180 if (target == null) {
181 throw new IllegalArgumentException("Target host may not be null");
182 }
183 if (params == null) {
184 throw new IllegalArgumentException("Parameters may not be null");
185 }
186 if (!conn.isOpen()) {
187 throw new IllegalStateException("Connection must be open");
188 }
190 final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
191 if (!(schm.getSchemeSocketFactory() instanceof LayeredSchemeSocketFactory)) {
192 throw new IllegalArgumentException
193 ("Target scheme (" + schm.getName() +
194 ") must have layered socket factory.");
195 }
197 LayeredSchemeSocketFactory lsf = (LayeredSchemeSocketFactory) schm.getSchemeSocketFactory();
198 Socket sock;
199 try {
200 sock = lsf.createLayeredSocket(
201 conn.getSocket(), target.getHostName(), target.getPort(), true);
202 } catch (ConnectException ex) {
203 throw new HttpHostConnectException(target, ex);
204 }
205 prepareSocket(sock, context, params);
206 conn.update(sock, target, lsf.isSecure(sock), params);
207 }
209 /**
210 * Performs standard initializations on a newly created socket.
211 *
212 * @param sock the socket to prepare
213 * @param context the context for the connection
214 * @param params the parameters from which to prepare the socket
215 *
216 * @throws IOException in case of an IO problem
217 */
218 protected void prepareSocket(
219 final Socket sock,
220 final HttpContext context,
221 final HttpParams params) throws IOException {
222 sock.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
223 sock.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
225 int linger = HttpConnectionParams.getLinger(params);
226 if (linger >= 0) {
227 sock.setSoLinger(linger > 0, linger);
228 }
229 }
231 /**
232 * Resolves the given host name to an array of corresponding IP addresses, based on the
233 * configured name service on the system.
234 *
235 * @param host host name to resolve
236 * @return array of IP addresses
237 * @exception UnknownHostException if no IP address for the host could be determined.
238 *
239 * @since 4.1
240 */
241 protected InetAddress[] resolveHostname(final String host) throws UnknownHostException {
242 return InetAddress.getAllByName(host);
243 }
245 }