org.xsocket.connection.http.client.HttpClientConnection Maven / Gradle / Ivy
/*
* Copyright (c) xsocket.org, 2006 - 2008. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xsocket.org/
*/
package org.xsocket.connection.http.client;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.NonBlockingConnection;
import org.xsocket.connection.http.AbstractHttpConnection;
import org.xsocket.connection.http.BodyDataSink;
import org.xsocket.connection.http.HttpUtils;
import org.xsocket.connection.http.IBodyHandler;
import org.xsocket.connection.http.IHttpConnectHandler;
import org.xsocket.connection.http.IHttpConnection;
import org.xsocket.connection.http.IHttpConnectionTimeoutHandler;
import org.xsocket.connection.http.IHttpDisconnectHandler;
import org.xsocket.connection.http.IHttpHandler;
import org.xsocket.connection.http.IMessage;
import org.xsocket.connection.http.InvokeOn;
import org.xsocket.connection.http.NonBlockingBodyDataSource;
import org.xsocket.connection.http.Request;
import org.xsocket.connection.http.RequestHeader;
import org.xsocket.connection.http.Response;
import org.xsocket.connection.http.ResponseHeader;
import org.xsocket.connection.http.client.ClientUtils.HttpHandlerInfo;
import org.xsocket.connection.http.client.ClientUtils.ResponseHandlerInfo;
import org.xsocket.connection.spi.DefaultIoProvider;
/**
* Represents the client side endpoint implementation of a http connection. The HttpClientConnection supports
* constructors which accepts the remote address or a existing {@link INonBlockingConnection}. A INonBlockingConnection
* can become a HttpClientConnection at any time.
*
* @author [email protected]
*/
public final class HttpClientConnection extends AbstractHttpConnection implements IHttpConnection, IHttpClientEndpoint {
private static final Logger LOG = Logger.getLogger(HttpClientConnection.class.getName());
private static String versionInfo = null;
// timer
private static final Timer TIMER = new Timer("xHttpClientTimer", true);
public static final Integer DEFAULT_RECEIVE_TIMEOUT_MILLIS = Integer.MAX_VALUE;
private int receiveTimeoutMillis = DEFAULT_RECEIVE_TIMEOUT_MILLIS;
private final ArrayList responseHandlers = new ArrayList();
private IHttpHandler handler = null;
private ConnectHandlerAdapter connectHandlerAdapter = null;
private DisconnectHandlerAdapter disconnectHandlerAdapter = null;
private ConnectionTimeoutHandlerAdapter connectionTimeoutHandlerAdapter = null;
private TimerTask watchDogTask = null;
private long watchDogPeriod = Long.MAX_VALUE;
/**
* constructor
*
* @param host the remote host
* @param port the remote port
* @throws IOException if an exception occurs
*/
public HttpClientConnection(String host, int port) throws IOException {
this(new NonBlockingConnection(host, port));
}
/**
* constructor
*
* @param host the remote host
* @param port the remote port
* @param httpHandler the handler (supported: {@link IHttpConnectHandler}, {@link IHttpDisconnectHandler}, {@link IHttpConnectionTimeoutHandler})
* @throws IOException if an exception occurs
*/
public HttpClientConnection(String host, int port, IHttpHandler httpHandler) throws IOException {
this(new NonBlockingConnection(host, port), httpHandler);
}
/**
* constructor
*
* @param connection the underlying tcp connection
* @throws IOException if an exception occurs
*/
public HttpClientConnection(INonBlockingConnection connection) throws IOException {
this(connection, null);
}
/**
* constructor
*
* @param connection the underlying tcp connection
* @param httpHandler the handler (supported: {@link IHttpConnectHandler}, {@link IHttpDisconnectHandler}, {@link IHttpConnectionTimeoutHandler})
* @throws IOException if an exception occurs
*/
public HttpClientConnection(INonBlockingConnection connection, IHttpHandler httpHandler) throws IOException {
super(connection);
HttpHandlerInfo httpHandlerInfo = ClientUtils.getHttpHandlerInfo(httpHandler);
this.handler = httpHandler;
if (httpHandlerInfo.isConnectHandler()) {
connectHandlerAdapter = new ConnectHandlerAdapter(httpHandlerInfo.isConnectHandlerMultithreaded());
}
if (httpHandlerInfo.isDisconnectHandler()) {
disconnectHandlerAdapter = new DisconnectHandlerAdapter(httpHandlerInfo.isDisconnectHandlerMultithreaded());
}
if (httpHandlerInfo.isConnectionTimeoutHandler()) {
connectionTimeoutHandlerAdapter = new ConnectionTimeoutHandlerAdapter(httpHandlerInfo.isConnectionTimeoutHandlerMultithreaded());
}
onConnect();
}
/**
* {@inheritDoc}
*/
public void setResponseTimeoutMillis(int receiveTimeoutMillis) {
if (this.receiveTimeoutMillis != receiveTimeoutMillis) {
this.receiveTimeoutMillis = receiveTimeoutMillis;
if (receiveTimeoutMillis > 100) {
updateTimer(receiveTimeoutMillis / 10);
} else {
updateTimer(receiveTimeoutMillis);
}
}
}
/**
* {@inheritDoc}
*/
public int getResponseTimeoutMillis() {
return receiveTimeoutMillis;
}
/**
* {@inheritDoc}
*/
@Override
protected IMessage readMessageHeader( INonBlockingConnection tcpConnection, int maxHeaderLength) throws BufferUnderflowException, IOException {
ResponseHeader responseHeader = ResponseHeader.readFrom(tcpConnection, maxHeaderLength);
return new Response(responseHeader);
}
/**
* {@inheritDoc}
*/
public Response call(Request request) throws IOException, SocketTimeoutException {
// create response handler
BlockingResponseHandler responseHandler = new BlockingResponseHandler(this, receiveTimeoutMillis);
// send request
send(request, responseHandler);
return responseHandler.getResponse();
}
/**
* {@inheritDoc}
*/
public void send(Request request, IResponseHandler responseHandler) throws IOException {
send(request, new ResponseHandlerAdapter(responseHandler, this, receiveTimeoutMillis));
}
void send(Request request, ResponseHandlerAdapter responseHandlerAdapter) throws IOException {
// body less request?
if (request.getNonBlockingBody() == null) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("sending (bodyless): " + request);
}
sendBodyless(request.getRequestHeader(), responseHandlerAdapter);
// no, request has body
} else {
if (request.getNonBlockingBody().getDataHandler() != null) {
throw new IOException("a body handler is already assigned to the message body. sending such messages is not supported (remove data handler)");
}
LOG.fine("sending (with body): " + request);
BodyDataSink bodyDataSink = null;
// send with plain body
if (request.getRequestHeader().getContentLength() >= 0) {
bodyDataSink = send(request.getRequestHeader(), request.getContentLength(), responseHandlerAdapter);
// send with chunked body
} else {
bodyDataSink = send(request.getRequestHeader(), responseHandlerAdapter);
}
bodyDataSink.setFlushmode(FlushMode.ASYNC);
bodyDataSink.setAutoflush(false);
// is body complete?
if (request.getNonBlockingBody().isComplete()) {
bodyDataSink.write(request.getNonBlockingBody().readAvailableByteBuffer());
bodyDataSink.close();
// .. no
} else {
final BodyDataSink bds = bodyDataSink;
IBodyHandler bodyForwarder = new IBodyHandler() {
@Execution(Execution.Mode.NONTHREADED)
public boolean onData(NonBlockingBodyDataSource bodyDataSource) throws IOException {
bds.write(bodyDataSource.readAvailableByteBuffer());
if (bodyDataSource.isComplete()) {
bds.close();
}
return true;
}
};
request.getNonBlockingBody().setDataHandler(bodyForwarder);
}
}
}
/**
* {@inheritDoc}
*/
public BodyDataSink send(RequestHeader requestHeader, IResponseHandler responseHandler) throws IOException {
return send(requestHeader, new ResponseHandlerAdapter(responseHandler, this, receiveTimeoutMillis));
}
private BodyDataSink send(RequestHeader requestHeader, ResponseHandlerAdapter responseHandlerAdapter) throws IOException {
return sendChunked(requestHeader, responseHandlerAdapter);
}
/**
* {@inheritDoc}
*/
public BodyDataSink send(RequestHeader requestHeader, int contentLength, IResponseHandler responseHandler) throws IOException {
return send(requestHeader, contentLength, new ResponseHandlerAdapter(responseHandler, this, receiveTimeoutMillis));
}
private BodyDataSink send(RequestHeader requestHeader, int contentLength, ResponseHandlerAdapter responseHandlerAdapter) throws IOException {
return sendPlain(requestHeader, contentLength, responseHandlerAdapter);
}
private void sendBodyless(RequestHeader requestHeader, ResponseHandlerAdapter responseHandlerAdapter) throws IOException {
synchronized (responseHandlers) {
responseHandlers.add(responseHandlerAdapter);
}
enhanceHeader(requestHeader);
assert (getUnderlyingConnection().getFlushmode() == FlushMode.ASYNC);
requestHeader.writeTo(getUnderlyingConnection());
getUnderlyingConnection().flush();
}
BodyDataSink sendPlain(RequestHeader requestHeader, int contentLength, ResponseHandlerAdapter responseHandlerAdapter) throws IOException {
synchronized (responseHandlers) {
responseHandlers.add(responseHandlerAdapter);
}
return writePlainRequest(requestHeader, contentLength);
}
BodyDataSink sendChunked(RequestHeader requestHeader, ResponseHandlerAdapter responseHandlerAdapter) throws IOException {
synchronized (responseHandlers) {
responseHandlers.add(responseHandlerAdapter);
}
return writeChunkedRequest(requestHeader);
}
private BodyDataSink writePlainRequest(RequestHeader header, int contentLength) throws IOException {
if (contentLength > 0) {
header.setContentLength(contentLength);
}
enhanceHeader(header);
return writeMessageHeader(header);
}
private BodyDataSink writeChunkedRequest(RequestHeader header) throws IOException {
String transferEncoding = header.getTransferEncoding();
if (transferEncoding == null) {
header.setTransferEncoding("chunked");
}
enhanceHeader(header);
return writeMessageHeader(header);
}
private void enhanceHeader(RequestHeader header) throws IOException {
String host = header.getHost();
if (host == null) {
header.setHost(getRemoteHostInfo());
}
if (header.getScheme() == null) {
if (getUnderlyingConnection().isSecure()) {
header.setScheme("HTTPS");
} else {
header.setScheme("HTTP");
}
}
String userAgent = header.getUserAgent();
if (userAgent == null) {
header.setUserAgent(getVersionInfo());
}
String contentType = header.getContentType();
if (contentType != null) {
String encoding = parseEncoding(contentType);
if (encoding == null) {
header.setContentType(contentType + "; charset=" + header.getCharacterEncoding());
}
}
}
private static String getVersionInfo() {
if (versionInfo == null) {
versionInfo = "xSocket/" + HttpUtils.getVersionInfo();
}
return versionInfo;
}
private String getRemoteHostInfo() throws IOException {
INonBlockingConnection con = getUnderlyingConnection();
InetAddress remoteAddress = con.getRemoteAddress();
int remotePort = con.getRemotePort();
return remoteAddress.getHostName() + ":" + remotePort;
}
/**
* {@inheritDoc}
*/
@Override
protected void onMessageHeaderReceived(IMessage message) throws IOException {
Response response = (Response) message;
ResponseHandlerAdapter responseHandlerAdapter = null;
synchronized (responseHandlers) {
if (responseHandlers.isEmpty()) {
LOG.warning("internal program error occured. a response handler should have been set");
return;
}
responseHandlerAdapter = responseHandlers.remove(0);
}
if (responseHandlerAdapter != null) {
responseHandlerAdapter.process(response);
}
}
private void onConnect() throws IOException {
if (connectHandlerAdapter != null) {
connectHandlerAdapter.callOnConnect();
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
protected void onDisconnect() throws IOException {
ArrayList responseHandlersCopy = null;
synchronized (responseHandlers) {
responseHandlersCopy = (ArrayList) responseHandlers.clone();
}
for (ResponseHandlerAdapter responseHandlerAdapter : responseHandlersCopy) {
if (responseHandlerAdapter.getResponseHandlerInfo().isResponseTimeuotHandler()) {
callResponseTimeout(responseHandlerAdapter);
}
}
if (disconnectHandlerAdapter != null) {
disconnectHandlerAdapter.callOnDisconnect();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onConnectionTimeout() throws IOException {
if (connectionTimeoutHandlerAdapter != null) {
connectionTimeoutHandlerAdapter.callOnConnectionTimeout();
} else {
super.onConnectionTimeout();
}
}
private synchronized void updateTimer(long requiredMinPeriod) {
// if not watch dog already exists and required period is smaller than current one return
if ((watchDogTask != null) && (watchDogPeriod <= requiredMinPeriod)) {
return;
}
// set watch dog period
watchDogPeriod = requiredMinPeriod;
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("update watchdog task " + DataConverter.toFormatedDuration(watchDogPeriod));
}
// if watchdog task task already exits -> terminate it
if (watchDogTask != null) {
watchDogTask.cancel();
}
// create and run new watchdog task
watchDogTask = new TimerTask() {
@Override
public void run() {
checkTimeouts();
}
};
TIMER.schedule(watchDogTask, watchDogPeriod, watchDogPeriod);
}
@SuppressWarnings("unchecked")
private void checkTimeouts() {
List responseHandlersCopy = null;
synchronized (responseHandlers) {
responseHandlersCopy = (List) responseHandlers.clone();
}
long currentTime = System.currentTimeMillis();
for (ResponseHandlerAdapter responseHandlerAdapter : responseHandlersCopy) {
if (currentTime > responseHandlerAdapter.getMaxReceiveTime()) {
callResponseTimeout(responseHandlerAdapter);
}
}
}
private void callResponseTimeout(final ResponseHandlerAdapter responseHandlerAdapter) {
synchronized (responseHandlers) {
if (responseHandlers.contains(responseHandlerAdapter)) {
responseHandlers.remove(responseHandlerAdapter);
} else {
return;
}
}
if (responseHandlerAdapter.getResponseHandlerInfo().isResponseTimeuotHandler()) {
Runnable task = new Runnable() {
public void run() {
try {
IResponseTimeoutHandler responseTimeoutHandler = responseHandlerAdapter.getResponseTmeoutHandler();
boolean handled = responseTimeoutHandler.onResponseTimeout();
if (!handled) {
closeSilence();
}
} catch (IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by calling onResponseTimeout " + ioe.toString());
closeSilence();
}
}
}
};
if (responseHandlerAdapter.getResponseHandlerInfo().getOnResponseTimeoutExecutionMode() == Execution.Mode.MULTITHREADED) {
processMultiThreaded(task);
} else {
processNonThreaded(task);
}
} else {
closeSilence();
}
}
static class ResponseHandlerAdapter implements Runnable, ICompleteListener {
private IResponseHandler responseHandler = null;
private IResponseTimeoutHandler responseTimeoutHandler = null;
private Response response = null;
private ResponseHandlerInfo responseHandlerInfo = null;
private HttpClientConnection httpConnection = null;
private long maxReceiveTime = 0;
private ResponseHandlerAdapter(ResponseHandlerInfo responseHandlerInfo, HttpClientConnection httpConnection, long maxReceiveTime) {
this.responseHandlerInfo = responseHandlerInfo;
this.httpConnection = httpConnection;
this.maxReceiveTime = System.currentTimeMillis() + maxReceiveTime;
}
ResponseHandlerAdapter(IResponseHandler responseHandler, HttpClientConnection httpConnection, long maxReceiveTime) throws IOException {
this(ClientUtils.getResponseHandlerInfo(responseHandler), httpConnection, maxReceiveTime);
this.responseHandler = responseHandler;
if (responseHandlerInfo.isResponseTimeuotHandler()) {
responseTimeoutHandler = (IResponseTimeoutHandler) responseHandler;
}
}
private final ResponseHandlerInfo getResponseHandlerInfo() {
return responseHandlerInfo;
}
private final IResponseTimeoutHandler getResponseTmeoutHandler() {
return responseTimeoutHandler;
}
final HttpClientConnection getHttpConnection() {
return httpConnection;
}
private final long getMaxReceiveTime() {
return maxReceiveTime;
}
final void process(Response response) {
assert (DefaultIoProvider.isDispatcherThread());
this.response = response;
handleConnectionHeaders();
if (response.hasBody()) {
addListener(response.getNonBlockingBody(), this);
} else {
onComplete();
}
// call handlers call back method if is HEADER_RECEIVED triggered
if (responseHandlerInfo.getInvokationMode() == InvokeOn.Mode.HEADER_RECEIVED) {
callOnResponse();
}
}
public final void onComplete() {
assert (DefaultIoProvider.isDispatcherThread());
onMessageCompleteReceived();
if (!httpConnection.isPersistent()) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("closed connection because it is not persistent");
}
destroyConnection();
}
// call handlers call back method if is HEADER_MESSAGE triggered
if (responseHandlerInfo.getInvokationMode() == InvokeOn.Mode.MESSAGE_RECEIVED) {
callOnResponse();
}
}
final void closeConnection() {
if (httpConnection.isPersistent()) {
if (getHttpConnection().isOpen()) {
try {
getHttpConnection().close();
} catch(IOException ioe) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by closing connection " + getHttpConnection().getId()+ " " + ioe.toString());
}
}
}
}
}
final void destroyConnection() {
httpConnection.destroy();
}
private void callOnResponse() {
if (responseHandlerInfo.isResponseHandler()) {
if (responseHandlerInfo.getOnResponseExecutionMode() == Execution.Mode.MULTITHREADED) {
httpConnection.processMultiThreaded(this);
} else {
httpConnection.processNonThreaded(this);
}
} else {
LOG.warning("message received, but response handler is not set");
}
}
public final void run() {
performOnResponse(response);
}
public void performOnResponse(Response response) {
try {
responseHandler.onResponse(response);
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + httpConnection.getId() + "] error occured by calling on response " + responseHandler + " " + response + " " + e.toString());
}
}
}
private void handleConnectionHeaders() {
String keepAliveHeader = response.getKeepAlive();
if (keepAliveHeader != null) {
String[] tokens = keepAliveHeader.split(",");
for (String token : tokens) {
handleKeepAlive(token);
}
}
//check if persistent connection
String connectionHeader = response.getConnection();
if (connectionHeader != null) {
String[] values = connectionHeader.split(",");
for (String value : values) {
value = value.trim();
if (value.equalsIgnoreCase("close")) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("received connection: closed header. set isPersistent=false");
}
httpConnection.setPersistent(false);
}
}
}
}
private void handleKeepAlive(String option) {
try {
if (option.toUpperCase().startsWith("TIMEOUT=")) {
int timeoutSec = Integer.parseInt(option.substring("TIMEOUT=".length(), option.length()));
httpConnection.setIdleTimeoutSec(timeoutSec);
} else {
Integer seconds = Integer.parseInt(option);
httpConnection.setIdleTimeoutSec(seconds);
}
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by handling keep alive option " + option + " " + e.toString());
}
}
}
public void onMessageCompleteReceived() { }
}
static class BlockingResponseHandler extends ResponseHandlerAdapter implements IResponseTimeoutHandler {
private Response response = null;
private final Object readLock = new Object();
private boolean timeoutOccured = false;
private long maxReceiveTime = 0;
public BlockingResponseHandler(HttpClientConnection httpConnection, long maxReceiveTime) {
this(ClientUtils.RESPONSE_HANDLER_INFO_NONTHREADED_HEADER_RECEIVED, httpConnection, maxReceiveTime);
this.maxReceiveTime = maxReceiveTime;
}
public BlockingResponseHandler(ResponseHandlerInfo responseHandlerInfo, HttpClientConnection httpConnection, long maxReceiveTime) {
super(responseHandlerInfo, httpConnection, maxReceiveTime);
super.responseTimeoutHandler = this;
}
public void performOnResponse(Response response) {
this.response = response;
synchronized (readLock) {
readLock.notify();
}
}
public boolean onResponseTimeout() throws IOException {
synchronized (readLock) {
timeoutOccured = true;
readLock.notify();
}
return true;
}
Response getResponse() throws SocketTimeoutException, ClosedChannelException {
do {
synchronized (readLock) {
if (response == null) {
try {
readLock.wait();
} catch (InterruptedException ignore) { }
} else {
return response;
}
}
} while (!timeoutOccured);
if (getHttpConnection().isOpen()) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(maxReceiveTime) + " reached. destroying connection and throwing a timeout exception");
}
destroyConnection();
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(maxReceiveTime) + " reached");
} else {
throw new ClosedChannelException();
}
}
}
final class ConnectHandlerAdapter implements Runnable {
private boolean isMultithreaded = true;
public ConnectHandlerAdapter(boolean isMultithreaded) {
this.isMultithreaded = isMultithreaded;
}
private void callOnConnect() {
if (isMultithreaded) {
processMultiThreaded(this);
} else {
processNonThreaded(this);
}
}
public void run() {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("calling onConnect on " + handler);
}
try {
((IHttpConnectHandler) handler).onConnect(HttpClientConnection.this);
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by calling on connect " + handler + " " + e.toString());
}
}
}
}
final class DisconnectHandlerAdapter implements Runnable {
private boolean isMultithreaded = true;
public DisconnectHandlerAdapter(boolean isMultithreaded) {
this.isMultithreaded = isMultithreaded;
}
private void callOnDisconnect() {
if (isMultithreaded) {
processMultiThreaded(this);
} else {
processNonThreaded(this);
}
}
public void run() {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("calling onDisconnect on " + handler);
}
try {
((IHttpDisconnectHandler) handler).onDisconnect(HttpClientConnection.this);
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by calling on connect " + handler + " " + e.toString());
}
}
}
}
final class ConnectionTimeoutHandlerAdapter implements Runnable {
private boolean isMultithreaded = true;
public ConnectionTimeoutHandlerAdapter(boolean isMultithreaded) {
this.isMultithreaded = isMultithreaded;
}
private void callOnConnectionTimeout() {
if (isMultithreaded) {
processMultiThreaded(this);
} else {
processNonThreaded(this);
}
}
public void run() {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("calling onConnectionTimeout on " + handler);
}
try {
((IHttpConnectionTimeoutHandler) handler).onConnectionTimeout(HttpClientConnection.this);
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("[" + getId() + "] error occured by calling on connection timeout " + handler + " " + e.toString());
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy