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

org.xsocket.connection.http.client.HttpClient 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.Closeable;
import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.ClosedChannelException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.SSLContext;


import org.xsocket.ILifeCycle;
import org.xsocket.connection.IConnectionPool;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.NonBlockingConnection;
import org.xsocket.connection.NonBlockingConnectionPool;
import org.xsocket.connection.http.BodyDataSink;
import org.xsocket.connection.http.NonBlockingBodyDataSource;
import org.xsocket.connection.http.HttpRequest;
import org.xsocket.connection.http.HttpRequestHeader;
import org.xsocket.connection.http.HttpResponse;
import org.xsocket.connection.http.HttpResponseHeader;
import org.xsocket.connection.http.client.HttpClientConnection.BlockingResponseHandler;
import org.xsocket.connection.http.client.HttpClientConnection.ResponseHandlerAdapter;



/**
 * Higher level client-side abstraction of the client side endpoint. The HttpClient uses am internal pool 
 * of {@link HttpClientConnection} to perform the requests. 
 *
 * @author [email protected]
 */
public final class HttpClient implements IHttpClientEndpoint, IConnectionPool, Closeable {
	
	private static final Logger LOG = Logger.getLogger(HttpClient.class.getName());
	
	public static final int DEFAULT_POOLED_IDLE_TIMEOUT_MILLIS = 5 * 1000;
	public static final int DEFAULT_POOLED_LIFE_TIMEOUT_MILLIS = 10 * 1000;
	
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
	
	private static final boolean DEFAULT_IS_TRANSACTION_LOG = true;
	public static final int DEFAULT_MAX_REDIRECTS = 5;
	public static final boolean DEFAULT_TREAT_302_REDIRECT_AS_303 = false;
	public static final Integer DEFAULT_RECEIVE_TIMEOUT_MILLIS = Integer.MAX_VALUE;
	
	private int maxRedirects = DEFAULT_MAX_REDIRECTS;
	private boolean isTreat302RedirectAs303 = DEFAULT_TREAT_302_REDIRECT_AS_303;
	private int receiveTimeoutMillis = DEFAULT_RECEIVE_TIMEOUT_MILLIS;
	private boolean isTransactionLog =  DEFAULT_IS_TRANSACTION_LOG;

	private SSLContext sslCtx = null;
	private NonBlockingConnectionPool pool = null;
	
	private boolean isPooled = true;
	private boolean isSSLSupported = false;
	
	
	// auto handle
	boolean isAutohandle100ContinueResponse = DEFAULT_AUTOHANDLE_100CONTINUE_RESPONSE;
	
	
	// statistics
	private TransactionLog transactionLog = new TransactionLog(20);
	
	

	/**
	 * constructor 
	 */
	public HttpClient() {
		this(null);
	}
	

	
	/**
	 * constructor 
	 * 
	 * @param sslCtx   the ssl context to use
	 */
	public HttpClient(SSLContext sslCtx) {
		if (sslCtx != null) {
			this.sslCtx = sslCtx;
			pool = new NonBlockingConnectionPool(sslCtx);
			isSSLSupported = true;
		} else {
			pool = new NonBlockingConnectionPool();
			isSSLSupported = false;
		}	
		
		pool.setPooledIdleTimeoutMillis(DEFAULT_IDLE_TIMEOUT_MILLIS);
		pool.setPooledLifeTimeoutMillis(DEFAULT_LIFE_TIMEOUT_MILLIS);
	}
	
	

	public final void setAutohandle100ContinueResponse(boolean isAutohandle100ContinueResponse) {
		this.isAutohandle100ContinueResponse = isAutohandle100ContinueResponse;
	}
	
	
	public boolean isAutohandle100ContinueResponse() {
		return isAutohandle100ContinueResponse;
	}
	


	/**
	 * set the max redirects
	 * 
	 * @param maxRedirects  the max redirects 
	 */
	public void setMaxRedirects(int maxRedirects) {
		this.maxRedirects = maxRedirects;
	}
	
	
	/**
	 * get the max redirects 
	 * 
	 * @return the max redirects
	 */
	public int getMaxRedirects() {
		return maxRedirects;
	}
	
	
	/**
	 * sets if a 302 response should be treat as a 303 response
	 *  
	 * @param isTreat303RedirectAs302 true, if a 303 response should be treat a a 303 response 
	 */
	public void setTreat302RedirectAs303(boolean isTreat303RedirectAs302) {
		this.isTreat302RedirectAs303 = isTreat303RedirectAs302; 
	}
	
	
	/**
	 * gets if a 302 response should be treat as a 303 response
	 * 
	 * @return true, if a 302 response should be treat as a 303 response
	 */
	public boolean isTreat302RedirectAs303() {
		return isTreat302RedirectAs303;
	}
	
	
	/**
	 * activates the transaction log
	 *  
	 * @param isTransactionLog true, if transaction log sholud be activated 
	 */
	void setTransactionLog(boolean isTransactionLog) {
		this.isTransactionLog = isTransactionLog;
	}
	
	/**
	 * returns if the transaction log is activated 
	 * 
	 * @return true, if the transaction log is activated
	 */
	boolean isTransactionLog() {
		return isTransactionLog;
	}
	
	
	/**
	 * get the max size of the transaction log
	 * 
	 * @return the max size of the transaction log
	 */
	int getTransactionLogMaxSize() {
		return transactionLog.getMaxSize();
	}
	
	
	/**
	 * sets the max size of the transaction log
	 * 
	 * @param maxSize the max size of the transaction log
	 */
	void setTransactionLogMaxSize(int maxSize) {
		transactionLog.setMaxSize(maxSize);
	}

	
	/**
	 * set the worker pool which will be assigned to the connections for call back handling
	 * @param workerpool the worker pool
	 */
	public void setWorkerpool(Executor workerpool) {
		pool.setWorkerpool(workerpool);
	}

	
	/**
	 * returns is pooling is used
	 *  
	 * @return true, if pooling is used 
	 */
	public boolean isPooled() {
		return isPooled;
	}
	
	
	/**
	 * sets if pooling is used
	 * 
	 * @param isPooled true, if pooling is used
	 */
	public void setPooled(boolean isPooled) {
		this.isPooled = isPooled;
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	public void setResponseTimeoutMillis(int receiveTimeoutMillis) {
		this.receiveTimeoutMillis = receiveTimeoutMillis;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getResponseTimeoutMillis() {
		return receiveTimeoutMillis;
	}
	
	

	/**
	 * {@inheritDoc}
	 */
	public void close() throws IOException {
		pool.close();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public boolean isOpen() {
		return pool.isOpen();
	}
	
	
	/**
	 * returns a unique id 
	 * 
	 * @return the id 
	 */
	public String getId() {
		return Integer.toString(this.hashCode());
	}
	

	/**
	 * {@inheritDoc}
	 */
	public void addListener(ILifeCycle listener) {
		pool.addListener(listener);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public boolean removeListener(ILifeCycle listener) {
		return pool.removeListener(listener);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public void setPooledIdleTimeoutMillis(int idleTimeoutMillis) {
		pool.setPooledIdleTimeoutMillis(idleTimeoutMillis);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getPooledIdleTimeoutMillis() {
		return pool.getPooledIdleTimeoutMillis();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public void setPooledLifeTimeoutMillis(int lifeTimeoutMillis) {
		pool.setPooledLifeTimeoutMillis(lifeTimeoutMillis);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getPooledLifeTimeoutMillis() {
		return pool.getPooledLifeTimeoutMillis();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public long getCreationMaxWaitMillis() {
		return pool.getCreationMaxWaitMillis();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public void setCreationMaxWaitMillis(long maxWaitMillis) {
		pool.setCreationMaxWaitMillis(maxWaitMillis);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public void setMaxIdlePooled(int maxIdle) {
		pool.setMaxIdlePooled(maxIdle);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getMaxIdlePooled() {
		return pool.getMaxIdlePooled();
	}

	/**
	 * {@inheritDoc}
	 */
	public void setMaxActivePooled(int maxActive) {
		pool.setMaxActivePooled(maxActive);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getMaxActivePooled() {
		return pool.getMaxActivePooled();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getNumPooledActive() {
		return pool.getNumPooledActive();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getNumPooledIdle() {
		return pool.getNumPooledIdle();
	}
	
	/**
	 * {@inheritDoc}
	 */
	int getNumPendingGet() {
		return pool.getNumPendingGet();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getNumCreated() {
		return pool.getNumCreated();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public int getNumDestroyed() {
		return pool.getNumDestroyed();
	}
	
    /**
	 * {@inheritDoc}
	 */
    public int getNumTimeoutPooledIdle() {
    	return pool.getNumTimeoutPooledIdle();
    }

    /**
	 * {@inheritDoc}
	 */
    public int getNumTimeoutPooledLifetime() {
    	return pool.getNumTimeoutPooledLifetime();
    }
    
	
    /**
	 * {@inheritDoc}
	 */
	public List getActiveConnectionInfos() {
		return pool.getActiveConnectionInfos();
	}
	
	/**
	 * {@inheritDoc}
	 */
	public List getIdleConnectionInfos() {
		return pool.getIdleConnectionInfos();
	}


	/**
	 * returns the transaction log 
	 * @return the transaction log
	 */
	List getTransactionInfos() {
		return transactionLog.getTransactions();
	}
	
	

	
	
	/**
	 * {@inheritDoc}
	 */
	public HttpResponse call(HttpRequest request) throws IOException, SocketTimeoutException {
			
		// get connection
		URI targetURI = request.getTargetURI();
		HttpClientConnection con = getConnection(request.isSecure(), targetURI.getHost(), targetURI.getPort(), targetURI.getScheme());
	
		// create a response handler 
		BlockingResponseHandler responseHandler = new BlockingResponseHandler(con, getResponseTimeoutMillis());
		

		// send the request and wait for the response
		con.send(request, responseHandler);
		HttpResponse response = responseHandler.getResponse();
		
		addTransactionInfo(request, response);
		return response;
	}

	

	/**
	 * performs a request, by handling redirects 
	 * 
	 * @param request  the request 
	 * @return the response 
	 * @throws IOException   if an exception occurs 
	 * @throws ConnectException if an error occurred while attempting to connect to a remote address and port. 
	 * @throws SocketTimeoutException if the received timeout is exceed
	 */
	public HttpResponse callFollowRedirects(HttpRequest request) throws IOException, ConnectException, SocketTimeoutException {
		return callFollowRedirects(request, 0);
	}
	

	private HttpResponse callFollowRedirects(HttpRequest request, int redirectCounter) throws IOException, ConnectException, SocketTimeoutException {

		if (redirectCounter > maxRedirects) {
			throw new IOException("max redirects " + maxRedirects + " reached");
		}
		
		HttpResponse response = null;
		

		// duplicate body is redirect is activated
		NonBlockingBodyDataSource copiedBody = null;
		if (request.hasBody()) {
			copiedBody = request.getNonBlockingBody().duplicate();
		}

		
		// get connection and perform call 
		URI targetURI = request.getTargetURI(); 
		HttpClientConnection con = getConnection(request.isSecure(), targetURI.getHost(), targetURI.getPort(), targetURI.getScheme());
		response = con.call(request);
		

		addTransactionInfo(request, response);

		if (isRedirectResponse(request.getRequestHeader(), response.getResponseHeader())) {
			URL newLocation = getRedirectURI(response, request.isSecure(), targetURI.getHost(), targetURI.getPort());
				
			try {
				HttpRequestHeader newRequestHeader = new HttpRequestHeader(request.getMethod(), newLocation.toExternalForm());
				newRequestHeader.copyHeaderFrom(request.getRequestHeader(), "HOST", "CONTENT-LENGTH");
				
				HttpRequest newRequest = null;
				if ((response.getStatus() == 303) || ((response.getStatus() == 302) && isTreat302RedirectAs303)) {
					newRequest = new HttpRequest(newRequestHeader);
					newRequest.setMethod("GET");
					
				} else {
					newRequest = new HttpRequest(newRequestHeader, copiedBody); 
				}
					
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("Sending redirect ");
				}
				response = callFollowRedirects(newRequest, ++redirectCounter);
			} catch (URISyntaxException use) {
				throw new IOException(use.toString());
			}
		}
		
		
		return response;
	}
	
	
	
	
	
	/**
	 * {@inheritDoc}
	 */
	public void send(HttpRequest request, IHttpResponseHandler responseHandler) throws IOException, ConnectException {

		// get connection
		URI targetURI = request.getTargetURI();
		HttpClientConnection con = getConnection(request.isSecure(), targetURI.getHost(), targetURI.getPort(), targetURI.getScheme());
		
		// perform call
		ResponseHandlerAdapter responseHandlerAdapter = new ResponseHandlerAdapter(responseHandler, con, getResponseTimeoutMillis()); 
		con.send(request, responseHandlerAdapter);
	}
	

	
	/**
	 * {@inheritDoc}
	 */
	public BodyDataSink send(HttpRequestHeader requestHeader, int contentLength, IHttpResponseHandler responseHandler) throws IOException, ConnectException {
		
		// get connection
		URI targetURI = requestHeader.getTargetURI();
		HttpClientConnection con = getConnection(requestHeader.isSecure(), targetURI.getHost(), targetURI.getPort(), targetURI.getScheme());
		
		// perform call
		ResponseHandlerAdapter responseHandlerAdapter = new ResponseHandlerAdapter(responseHandler, con, getResponseTimeoutMillis());
		return con.sendPlain(requestHeader, contentLength, responseHandlerAdapter);

	}
	
	
	/**
	 * {@inheritDoc}
	 */
	public BodyDataSink send(HttpRequestHeader requestHeader, IHttpResponseHandler responseHandler) throws IOException, ConnectException {
	
		// get connection
		URI targetURI = requestHeader.getTargetURI();
		HttpClientConnection con = getConnection(requestHeader.isSecure(), targetURI.getHost(), targetURI.getPort(), targetURI.getScheme());
		
		// perform call
		return con.sendChunked(requestHeader, new ResponseHandlerAdapter(responseHandler, con, getResponseTimeoutMillis()));
	}
	
	
	

	/**
	 * sends a request, by following redirects 
	 * 
	 * @param request           the request 
	 * @param responseHandler   the response handler
	 * @throws IOException if an exception occurs
	 * @throws ConnectException if an error occurred while attempting to connect to a remote address and port. 
	 */
	public void sendFollowRedirects(HttpRequest request, IHttpResponseHandler responseHandler) throws IOException, ConnectException {
		sendFollowRedirects(request, responseHandler, 0);
	}
	
	
	private void sendFollowRedirects(HttpRequest request, IHttpResponseHandler responseHandler, int redirectCounter) throws IOException, ConnectException {

		// duplicate body and create RedirectAdapter is redirect is activated
		NonBlockingBodyDataSource copiedBody = null;
		if (request.hasBody()) {
			copiedBody = request.getNonBlockingBody().duplicate();
		}
	
		
		// get connection
		URI targetURI = request.getTargetURI();
		HttpClientConnection con = getConnection(request.isSecure(), targetURI.getHost(), targetURI.getPort(), targetURI.getScheme());

		
		// perform call
		RedirectResponseHandlerAdapter responseHandlerAdapter = new RedirectResponseHandlerAdapter(responseHandler, con, request.getRequestHeader(), copiedBody, redirectCounter, getResponseTimeoutMillis()); 
		con.send(request, responseHandlerAdapter);
	}
	
	
	
	
	

	
	private void addTransactionInfo(HttpRequest request, HttpResponse response) {
		
		if (isTransactionLog) {
			String info = null;
			if (request.getQueryString() != null) {
				info = "[" + DATE_FORMAT.format(new Date()) + "] " + request.getRemoteHost() + ":" + request.getRemotePort() + 
				       " " + request.getMethod() + " " + request.getRequestURI() + request.getQueryString() + 
				       " -> " + response.getStatus() + " " + response.getReason();
			} else {
				info = "[" + DATE_FORMAT.format(new Date()) + "] " + request.getRemoteHost() + ":" + request.getRemotePort() +
				       " " + request.getMethod() + " " + request.getRequestURI() +
				       " -> " + response.getStatus() + " " + response.getReason();
			}
			
			
			if (response.containsHeader("connection")) {
				info = info + " (connection: " + response.getHeader("connection") + ")";
			}
			
			
			transactionLog.add(info);
		}
	}
	


	

	
	private HttpClientConnection getConnection(boolean isSSL, String host, int port, String scheme) throws IOException, ConnectException {
		
		if (port == -1) {
			if (scheme.equalsIgnoreCase("HTTP")) {
				port = 80;
			} else if (scheme.equalsIgnoreCase("HTTPS")) {
				port = 443;
			} else {
				throw new IOException("wrong address host=" + host + " port=" + port + " scheme=" + scheme);
			}
		}
		
		
		
		if ((isSSL == true) && !isSSLSupported) {
			throw new IOException("ssl connection are not supported (use pool sslContext parameter constructor)");
		}
		
		INonBlockingConnection tcpConnection = null;
		
		if (isPooled) {
			try {
				tcpConnection = pool.getNonBlockingConnection(host, port, isSSL);
				
				if (!tcpConnection.isOpen()) {
					System.out.println("Stop");
				}
			} catch (IOException ioe) {
				throw new ConnectException("could not connect to " + host + ":" + port + " (" + ioe.toString() + ")");
			}

				
		} else {
			try {
				if (sslCtx != null) {
					tcpConnection = new NonBlockingConnection(host, port, sslCtx, true);
					((NonBlockingConnection) tcpConnection).setWorkerpool(pool.getWorkerpool());
				} else {
					tcpConnection = new NonBlockingConnection(host, port);
					((NonBlockingConnection) tcpConnection).setWorkerpool(pool.getWorkerpool());
				}
			} catch (IOException ioe) {
				throw new ConnectException("could not establish new tcp connection to " + host + ":" + port + " reason: " + ioe.toString());
			}
		}
		
		HttpClientConnection httpConnection = new HttpClientConnection(tcpConnection);
		httpConnection.setResponseTimeoutMillis(receiveTimeoutMillis);
		httpConnection.setAutohandle100ContinueResponse(isAutohandle100ContinueResponse);
		httpConnection.setCloseAfterResponse(true);
		
		return httpConnection;
	}
	
	

	private boolean isRedirectResponse(HttpRequestHeader requestHeader, HttpResponseHeader responseHeader) {
		
		switch (responseHeader.getStatus()) {
		
		
		// 300 Multiple choices
		case 300:
			return false;
			
		// 301 Moved permanently
		case 301:
			if (requestHeader.getMethod().equalsIgnoreCase("GET") || requestHeader.getMethod().equalsIgnoreCase("HEAD")) {
				return true;
			}
			
			return false;

			
		// 302 found
		case 302:
			if (isTreat302RedirectAs303) {
				return true;
			}
			
			if (requestHeader.getMethod().equalsIgnoreCase("GET") || requestHeader.getMethod().equalsIgnoreCase("HEAD")) {
				return true;
			}
			
			return false;
		
		// 303 See other
		case 303:
			return true;
		
			
		// 304 Not modified
		case 304:
			return false;

		// 305 Use proxy
		case 305:
			return false;

		// 306 (unused)
		case 306:
			return false;

		// 307 temporary redirect
		case 307:
			return false;

			
		default:
			return false;
		}
	}
	
	
	
	private static final URL getRedirectURI(HttpResponse response, boolean isSSL, String originalHost, int originalPort) {
		if (response.getStatus() == 302) {
			String location = response.getHeader("Location");
			
			// absolute URL? 
			try {
				return new URL(location);
				
			} catch (MalformedURLException mue) {
				// no
				try {
					if (isSSL) {
						if (originalPort == -1) {
							return new URL("https://" + originalHost + location);
						} else {
							return new URL("https://" + originalHost + ":" + originalPort + location);
						}
					} else {
						if (originalPort == -1) {
							return new URL("http://" + originalHost + location);
						} else { 
							return new URL("http://" + originalHost + ":" + originalPort + location);
						}
					}
				} catch (MalformedURLException e) {
					if (LOG.isLoggable(Level.FINE)) {
						LOG.fine("could not create relocation url . reason " + e.toString());
					}
				}
			}			
		}
	
		return null;
	}
	
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(super.toString());
		
		sb.append("\r\nactive connections:");
		for (String connectionInfo : getActiveConnectionInfos()) {
			sb.append("\r\n " + connectionInfo);
		}
		
		sb.append("\r\nidle connections:");
		for (String connectionInfo : getIdleConnectionInfos()) {
			sb.append("\r\n " + connectionInfo);
		}

		
		sb.append("\r\ntransaction log:");
		for (String transactionInfo : getTransactionInfos()) {
			sb.append("\r\n " + transactionInfo);
		}
		
		return sb.toString();
	}
	
	
	
	
	
	class RedirectResponseHandlerAdapter extends ResponseHandlerAdapter {
			
		private IHttpResponseHandler responseHandler = null;
		
		// redirect support
		private int currentRedirects = 0;
		private HttpRequestHeader originalRequestHeader = null;
		private NonBlockingBodyDataSource originalRequestBody = null;
		
		
		public RedirectResponseHandlerAdapter(IHttpResponseHandler responseHandler, HttpClientConnection httpConnection, HttpRequestHeader originalRequestHeader, NonBlockingBodyDataSource originalRequestBody, int currentRedirects, int receiveTimeoutMillis) throws IOException {
			super(responseHandler, httpConnection, receiveTimeoutMillis);		
			this.responseHandler = responseHandler;
			this.originalRequestHeader = originalRequestHeader;
			this.originalRequestBody = originalRequestBody;
			this.currentRedirects = currentRedirects;
		}
		
		
		@Override
		public void performOnResponse(HttpResponse response) {
			
			try {
				// redirect handling
				if (isRedirectResponse(originalRequestHeader, response.getResponseHeader())) {
					
					try {
						URL newLocation = getRedirectURI(response, originalRequestHeader.isSecure(), originalRequestHeader.getRemoteHost(), originalRequestHeader.getRemotePort());
							
						HttpRequestHeader newRequestHeader = new HttpRequestHeader(originalRequestHeader.getMethod(), newLocation.toExternalForm());
						newRequestHeader.copyHeaderFrom(originalRequestHeader, "HOST", "CONTENT-LENGTH");
								
						HttpRequest newRequest = null;
						if ((response.getStatus() == 303) || ((response.getStatus() == 302) && isTreat302RedirectAs303)) {
							newRequest = new HttpRequest(newRequestHeader);
							newRequest.setMethod("GET");
							
						} else {
							newRequest = new HttpRequest(newRequestHeader, originalRequestBody); 
						}
							
						sendFollowRedirects(newRequest, responseHandler, ++currentRedirects);
					} catch (URISyntaxException use) {
						throw new IOException(use.toString());
					}
						
				} else {
					super.performOnResponse(response);
				}
				
			} catch (IOException ioe) {
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("error occured by calling on response " + ioe.toString());
				}
			}
		}
	}
	
	
	
	
	
	@SuppressWarnings("unchecked")
	private static final class TransactionLog {
		
		private LinkedList transactions = new LinkedList();
		private int maxSize = 0;
		
		TransactionLog(int maxSize) {
			this.maxSize = maxSize;
		}
		
		void setMaxSize(int maxSize) {
			this.maxSize = maxSize;
		}
		
		int getMaxSize() {
			return maxSize;
		}
		
		void add(String transactionInfo) {
			transactions.add(transactionInfo);
			if (transactions.size() > maxSize) {
				try {
					transactions.removeFirst();
				} catch (Exception e) {
					if (LOG.isLoggable(Level.FINE)) {
						LOG.fine("error occured by removing list entry " + e.toString());
					}
				}
			}
		}
		
		
		public List getTransactions() {
			return (List) transactions.clone();
		}

	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy