All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xsocket.connection.http.client.HttpClientConnection Maven / Gradle / Ivy

There is a newer version: 2.0-beta-1
Show newest version
/*
 *  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