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

org.xsocket.connection.http.server.HttpServerConnection 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.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.net.SocketTimeoutException;
import java.nio.BufferUnderflowException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;



import org.xsocket.Execution;
import org.xsocket.ILifeCycle;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.http.AbstractHttpConnection;
import org.xsocket.connection.http.BodyDataSink;
import org.xsocket.connection.http.IBodyCompleteListener;
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.HttpRequest;
import org.xsocket.connection.http.HttpRequestHeader;
import org.xsocket.connection.http.HttpResponse;
import org.xsocket.connection.http.HttpResponseHeader;
import org.xsocket.connection.http.IHttpHandler;
import org.xsocket.connection.http.client.IHttpResponseTimeoutHandler;
import org.xsocket.connection.http.server.ServerUtils.HttpHandlerInfo;
import org.xsocket.connection.http.server.ServerUtils.RequestHandlerInfo;
import org.xsocket.connection.spi.DefaultIoProvider;





/**
 * Represents the server side endpoint implementation of a http connection. Typically a
 * HttpServerConnection will be created by the {@link HttpProtocolAdapter}. A HttpServerConnection
 * can also be created manually by passing over a {@link INonBlockingConnection} and the
 * server handler.
 *
 * @author [email protected]
 */
public class HttpServerConnection extends AbstractHttpConnection implements IHttpConnection, IHttpResponseContext {

	private static final Logger LOG = Logger.getLogger(HttpServerConnection.class.getName());

	public static final boolean DEFAULT_REMOVE_REQUEST_CONNECTION_HEADER = false;
	public static final boolean DEFAULT_CLOSE_ON_SENDING_ERROR = true;
	public static final Integer DEFAULT_RECEIVE_TIMEOUT_MILLIS = Integer.MAX_VALUE; 


	
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");

	
	public static final String SHOW_DETAILED_ERROR_KEY = "org.xsocket.connection.http.server.showDetailedError";
	public static final String SHOW_DETAILED_ERROR_DEFAULT = "false";
	private static boolean showDetailedError;
	
	static {
		showDetailedError = Boolean.parseBoolean(System.getProperty(SHOW_DETAILED_ERROR_KEY, SHOW_DETAILED_ERROR_DEFAULT));
	}
	

	// handler support
	private Object handler = null;
	private ConnectHandlerAdapter connectHandlerAdapter = null;
	private DisconnectHandlerAdapter disconnectHandlerAdapter = null;
	private ConnectionTimeoutHandlerAdapter connectionTimeoutHandlerAdapter = null;
	private RequestTimeoutHandlerAdapter requestTimeoutHandlerAdapter = null;
	private boolean invokeOnMessageReceived = false;
	private boolean isMultithreaded = true;
	private final OnRequestCaller onRequestCaller = new OnRequestCaller();
	private final BodyCompleteListener performCallBackOnCompleteListener = new BodyCompleteListener();
	
	// close handling
	private boolean isCloseOnSendingError = DEFAULT_CLOSE_ON_SENDING_ERROR;
	private boolean isCloseAfterResponse = false;


	// request message management
	private final List requestQueue = new ArrayList();
	private int requestQueueVersion = 0;
	private final AtomicInteger openRequests = new AtomicInteger(0);
	

	// auto handle
	boolean isAutohandleExpect100ContinueHeader = DEFAULT_AUTOHANDLE_EXPECT_100CONTINUE_HEADER;


	// timeout support
	private static final long MIN_WATCHDOG_PERIOD_MILLIS = 10 * 1000;
	private TimeoutWatchDogTask watchDogTask = null;
	private long lastTimeMessageReceived = System.currentTimeMillis();
	private Integer requestTimeoutMillis = null;


	// max transaction support
	private Integer maxTransactions = null;
	

	// statistics
	private int countReceivedMessages = 0;
	private int countSendMessages = 0;




	/**
	 * constructor
	 *
	 * @param connection      the underlying tcp connection
	 * @param serverHandler   the server handler (supported: {@link IHttpRequestHandler}, {@link IHttpConnectHandler}, {@link IHttpDisconnectHandler}, {@link IHttpConnectionTimeoutHandler}, {@link IHttpResponseTimeoutHandler}, {@link ILifeCycle})
	 * @throws IOException if an exception occurs
	 */
	public HttpServerConnection(INonBlockingConnection connection, IHttpHandler serverHandler) throws IOException {
		this(connection, ServerUtils.getServerHandlerInfo(serverHandler), ServerUtils.getHttpHandlerInfo(serverHandler), serverHandler, false);
	}


	/**
	 * constructor
	 *
	 * @param connection            the underlying tcp connection
	 * @param serverHandlerInfo     the server handler info
	 * @param httpHandlerInfo       the http handler info
	 * @param serverHandler         the server handler (supported: {@link IHttpRequestHandler}, {@link IHttpConnectHandler}, {@link IHttpDisconnectHandler}, {@link IHttpConnectionTimeoutHandler}, {@link IHttpResponseTimeoutHandler}, {@link ILifeCycle})
	 * @param isCloseOnSendingError true, if the connection should be closed on error
	 * @throws IOException if an exception occurs
	 */
	HttpServerConnection(INonBlockingConnection connection, RequestHandlerInfo serverHandlerInfo, HttpHandlerInfo httpHandlerInfo, IHttpHandler serverHandler, boolean isCloseOnSendingError) throws IOException {
		super(connection);

		setPersistent(true);
		
		setCloseOnSendingError(isCloseOnSendingError);

		this.handler = serverHandler;
		if (httpHandlerInfo.isConnectHandler()) {
			connectHandlerAdapter = new ConnectHandlerAdapter(httpHandlerInfo.isConnectHandlerMultithreaded());
		}

		if (httpHandlerInfo.isDisconnectHandler()) {
			disconnectHandlerAdapter = new DisconnectHandlerAdapter(httpHandlerInfo.isDisconnectHandlerMultithreaded());
		}


		if (httpHandlerInfo.isConnectionTimeoutHandler()) {
			connectionTimeoutHandlerAdapter = new ConnectionTimeoutHandlerAdapter(httpHandlerInfo.isConnectionTimeoutHandlerMultithreaded());
		}

		if (httpHandlerInfo.isRequestTimeoutHandler()) {
			requestTimeoutHandlerAdapter = new RequestTimeoutHandlerAdapter(httpHandlerInfo.isREquestTimeoutHandlerMultithreaded());
		}

		invokeOnMessageReceived = serverHandlerInfo.isInvokeOnMessageReceived();
		isMultithreaded = serverHandlerInfo.isMultithreaded();
		
		onConnect();
		
		if (LOG.isLoggable(Level.FINE)) {
			LOG.fine("[" + getId() + "] http server connection established");
		}
	}
	
	
	public boolean isAutohandleExpect100ContinueHeader() {
		return isAutohandleExpect100ContinueHeader;
	}
	
	public void setAutohandleExpect100ContinueHeader(boolean isAutohandleExpect100ContinueHeader) {
		this.isAutohandleExpect100ContinueHeader = isAutohandleExpect100ContinueHeader;
	}


	private void checkRequestTimeout(long currentTimeMillis) throws SocketTimeoutException {
		if (currentTimeMillis > (lastTimeMessageReceived + requestTimeoutMillis)) {
			terminateWatchDogTask();
			callRequestTimeoutHandler();
		}
	}


	/**
	 * {@inheritDoc}
	 */
	public void setMaxTransactions(int maxTransactions) {
		this.maxTransactions = maxTransactions;
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	public void setRequestTimeoutMillis(int requestTimeoutMillis) {

		if (requestTimeoutMillis <= 0) {
			callRequestTimeoutHandler();
			return;
		}
		
		lastTimeMessageReceived = System.currentTimeMillis();
		
		if ((this.requestTimeoutMillis == null) || (this.requestTimeoutMillis != requestTimeoutMillis)) {
			this.requestTimeoutMillis = requestTimeoutMillis;
			
			long watchdogPeriod = 0; 
			if (requestTimeoutMillis > 100) {
				watchdogPeriod = requestTimeoutMillis / 10;
			} else {
				watchdogPeriod = requestTimeoutMillis;
			}
			
			if (watchdogPeriod > MIN_WATCHDOG_PERIOD_MILLIS) {
				watchdogPeriod = MIN_WATCHDOG_PERIOD_MILLIS;
			}
			
			updateWatchDog(watchdogPeriod);
		}
	}

	

	private synchronized void updateWatchDog(long watchDogPeriod) {
		terminateWatchDogTask();

	    watchDogTask = new TimeoutWatchDogTask(this); 
	    AbstractHttpConnection.schedule(watchDogTask, watchDogPeriod, watchDogPeriod);
	}


	private synchronized void terminateWatchDogTask() {
		if (watchDogTask != null) {
	        watchDogTask.cancel();
	        watchDogTask = null;
	    }
	}

	
	private void callRequestTimeoutHandler() {
		if (requestTimeoutHandlerAdapter != null) {
			requestTimeoutHandlerAdapter.callOnRequestTimeout();
		} else {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("receive timeout for http server connection reached. terminate connection (no timeout handler has been set)");
			}

			closeSilence();
		}	
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void close() throws IOException {
		terminateWatchDogTask();
		super.close();
	};
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void destroy() {
		terminateWatchDogTask();
		super.destroy();
	}
	
	
	/**
	 * returns the message receive timeout
	 * 
	 * @return the message receive timeout
	 */
	public int getRequestTimeoutMillis() {
		return requestTimeoutMillis;
	}	
	

	/**
	 * set is if the connection will closed, if an error message is sent
	 *
	 * @param isCloseOnSendingError if the connection will closed, if an error message is sent
	 */
	public void setCloseOnSendingError(boolean isCloseOnSendingError) {
		this.isCloseOnSendingError = isCloseOnSendingError;
	}


	/**
	 * returns if the connection will closed, if an error message will be sent
	 * @return true, if the connection will closed by sending an error message
	 */
	public boolean isCloseOnSendingError() {
		return isCloseOnSendingError;
	}


	
	
	protected void onMessage(INonBlockingConnection connection) throws BufferUnderflowException, IOException {
	
		// reading the header
		HttpRequestHeader requestHeader = HttpRequestHeader.readFrom(connection, MAX_HEADER_LENGTH);
		HttpRequest request = new HttpRequest(requestHeader);


		// handle connection header
		handleConnectionHeaders(request);

		
		// add body parser (even though for a bodyless message) 
		addFullMessageBodyParser(request, connection);

		
		countReceivedMessages++;		
		if (LOG.isLoggable(Level.FINE)) {
			LOG.fine("[" + connection.getId() + "] request received (" + countReceivedMessages + ". request) " + requestHeader.toString());
		}
		
		
		// handle 100 continue header
		if (isAutohandleExpect100ContinueHeader) {
			if (request.containsHeaderValue("Expect", "100-Continue")) {
				send(new HttpResponse(100));
				return;
			}
		}
		 
		
		// add received message to the queue
		openRequests.incrementAndGet();
		synchronized (requestQueue) {
			requestQueue.add(request);
			requestQueueVersion++;
		}
		

		// mode MESSAGE_RECEIVED			
		if (invokeOnMessageReceived) {
		
			if (request != null) {
				if (request.hasBody()) {
					// add complete listener which will perform the performOnRequestCallBack
					// method if the complete body is received
					request.getNonBlockingBody().addCompleteListener(performCallBackOnCompleteListener);

				} else {
					if (!request.hasBody()) {
						lastTimeMessageReceived = System.currentTimeMillis();
					}
					
					performOnRequestCallBack();
				}
			}
			
		// mode HEADER_RECEIVED			
		} else {
			performOnRequestCallBack();
		}
	}
	
	
	private void performOnRequestCallBack() {
		if (isMultithreaded) {
			processMultiThreaded(onRequestCaller);

		} else {
			processNonThreaded(onRequestCaller);
		}
	}
	
	

	@Override
	protected void onBodyDataReceived() {
		lastTimeMessageReceived = System.currentTimeMillis();
	}




	private void handleConnectionHeaders(HttpRequest request) {


		String keepAliveHeader = request.getHeader("Keep-Alive");
		if (keepAliveHeader != null) {
			String[] tokens = keepAliveHeader.split(",");
			for (String token : tokens) {
				handleKeepAlive(token);
			}
		}


		String connectionHeader = request.getHeader("Connection");
		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. destroying connection");
					}
					isCloseAfterResponse = true;
				}
			}
		}
	}


	

	private void handleKeepAlive(String option) {
		try {
			Integer timeoutSec = null; 
		
			if (option.toUpperCase().startsWith("TIMEOUT=")) {
				timeoutSec = Integer.parseInt(option.substring("TIMEOUT=".length(), option.length()));

			} else {
				timeoutSec = Integer.parseInt(option);
			}
			

			if (timeoutSec != null) {
				if (requestTimeoutMillis != null) {
					int timeoutMillis = timeoutSec * 1000; 
					if (timeoutMillis < requestTimeoutMillis) {
						setRequestTimeoutMillis(timeoutMillis);
					}
				}
			}

		} catch (Exception e) {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("error occured by handling keep alive option " + option + " " + e.toString());
			}
		}
	}


	/**
	 * {@inheritDoc}
	 */
	public final BodyDataSink send(HttpResponseHeader header) throws IOException {
		countSendMessages++;
		
		if (header.getContentLength() < 0) {
			if (header.getTransferEncoding() == null) {
				header.setTransferEncoding("chunked");
			}
		}

		if (header.getStatus() >= 200) {
			openRequests.decrementAndGet();
		}

		enhanceHeader(header);

		BodyDataSink bodyDataSink = newChunkedBody(header);

		if (isCloseAfterResponse) {
			setCloseHttpConnectionAfterWritten(bodyDataSink, true);
		}

		return bodyDataSink;
	}



	/**
	 * {@inheritDoc}
	 */
	public final BodyDataSink send(HttpResponseHeader header, int contentLength) throws IOException {
		countSendMessages++;
		
		header.setContentLength(contentLength);

		if (header.getStatus() >= 200) {
			openRequests.decrementAndGet();
		}

		enhanceHeader(header);

		BodyDataSink bodyDataSink = newBoundBody(header);

		if (isCloseAfterResponse) {
			setCloseHttpConnectionAfterWritten(bodyDataSink, true);
		}

		return bodyDataSink;
	}

	

	private void sendBodyless(HttpResponseHeader responseHeader) throws IOException {
		if (responseHeader.getStatus() >= 200) {
			openRequests.decrementAndGet();
		}

		
		enhanceHeader(responseHeader);

		assert (getUnderlyingConnection().getFlushmode() == FlushMode.ASYNC);

		responseHeader.writeTo(getUnderlyingConnection());
		getUnderlyingConnection().flush();
	}


	/**
	 * {@inheritDoc}
	 */
	public void send(int status) throws IOException {
		send(new HttpResponse(status));
	}
	

	/**
	 * {@inheritDoc}
	 */
	public final void send(HttpResponse response) throws IOException {
		
		countSendMessages++;

		HttpResponseHeader responseHeader = response.getResponseHeader();


		// body less request?
		if (response.getNonBlockingBody() == null) {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("[" + getId() + "] sending (bodyless): " + response.getResponseHeader());
			}

			sendBodyless(response.getResponseHeader());



		// no, request has body
		} else {
			
			
			if (response.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)");
			}

			
			
			
			// is old style headerless response  
			if (responseHeader.getStatus() == 0) {
				responseHeader  = new HttpResponseHeader(200);   // creates a HTTP/1.1 header
				response = new HttpResponse(responseHeader, response.getNonBlockingBody());   
			}
			
			
			
			// connection terminated body, which has already complete received? 
/*			if (hasConnectionTerminatedBody(response) && !response.getNonBlockingBody().isComplete()) {
				
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("try to send an incomplete connection terminated message body. Waiting for body data before sending it");
				}
				
				final HttpResponse res = response;
				IBodyCompleteListener cl = new IBodyCompleteListener() {
					public void onComplete() throws IOException {
						int size = res.getNonBlockingBody().available();
						res.setContentLength(size);
						
						send(res);
					}
				};
				response.getNonBlockingBody().addCompleteListener(cl);
				return;
			}
		*/
			
	

			BodyDataSink bodyDataSink = null;
			if (response.getContentLength() >= 0) {									
				bodyDataSink = send(responseHeader, response.getContentLength());
			} else {
				bodyDataSink = send(responseHeader);
			}


			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("[" + getId() + "] sending (" + bodyDataSink.getClass().getSimpleName() +
						 "): " + response.getResponseHeader());
			}

			sendMessageBody(bodyDataSink, response.getNonBlockingBody());
		}
	}




	/**
	 * {@inheritDoc}
	 */
	public final void sendError(int errorCode) {
		sendError(errorCode, AbstractHttpConnection.getReason(errorCode));
	}


	/**
	 * {@inheritDoc}
	 */
	public final void sendError(int errorCode, String msg) {
		countSendMessages++;
		
		try {
			if (isCloseOnSendingError) {
				isCloseAfterResponse = true;
			}
	
			String txt = "\r\n" +
						 "  \r\n" +
						 "    Error " + errorCode + "\r\n" +
						 "    " +
						 "  \r\n\r\n" +
						 "  \r\n" +
						 "    

ERROR " + errorCode + "

\r\n" + "

" + msg + "

\r\n" + "

" + DATE_FORMAT.format(new Date()) + " xSocket-http (" + ServerUtils.getVersionInfo() + ")

\r\n" + " \r\n" + "\r\n"; byte[] data = txt.getBytes(DEFAULT_BODY_ENCODING); HttpResponseHeader response = new HttpResponseHeader(errorCode, "text/html; charset=" + DEFAULT_BODY_ENCODING); if (isCloseAfterResponse) { response.addHeader("connection", "close"); } BodyDataSink body = send(response, data.length); body.setFlushmode(FlushMode.ASYNC); body.write(data); body.close(); if (isCloseAfterResponse) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] closing http server connection (isCloseAfterResponse=true"); } close(); } } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by sending error " + errorCode + ": " + msg + " " + ioe.toString()); } closeSilence(); } } @Override protected void onProtocolException(Throwable throwable) { onRequestHandlingException(throwable); } private void onRequestHandlingException(Throwable throwable) { isCloseAfterResponse = true; if (openRequests.get() > 0) { if (showDetailedError) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); throwable.printStackTrace(new PrintStream(baos)); String stackTrace = baos.toString(); stackTrace = stackTrace.replace("\r", "
"); sendError(500, stackTrace); } else { sendError(500); } } } /** * {@inheritDoc} */ @Override public String toString() { return "HttpServerConnection (requests=" + countSendMessages + "; responses=" + countReceivedMessages + ") " + super.toString(); } private void enhanceHeader(HttpResponseHeader header) { String server = header.getHeader("SERVER"); if (server == null) { header.setHeader("SERVER", ServerUtils.getComponentInfo()); } int remainingTransactions = 0; if (maxTransactions != null) { remainingTransactions = maxTransactions - countReceivedMessages; if (remainingTransactions <= 0) { isCloseAfterResponse = true; } } if (!isCloseAfterResponse) { if ((maxTransactions != null) || (requestTimeoutMillis != null)) { header.setHeader("Connection", "Keep-Alive"); String keepAliveValue = null; if (maxTransactions != null) { keepAliveValue = "max=" + remainingTransactions; } if (requestTimeoutMillis != null) { if (keepAliveValue == null) { keepAliveValue = "timeout=" + (requestTimeoutMillis / 1000); } else { keepAliveValue += ", timeout=" + (requestTimeoutMillis / 1000); } } header.setHeader("Keep-Alive", keepAliveValue); } } if (isCloseAfterResponse) { header.setHeader("Connection", "close"); } } private void onConnect() throws IOException { if (connectHandlerAdapter != null) { connectHandlerAdapter.callOnConnect(); } } /** * {@inheritDoc} */ @Override protected void onDisconnect() throws IOException { terminateWatchDogTask(); if (disconnectHandlerAdapter != null) { disconnectHandlerAdapter.callOnDisconnect(); } super.onDisconnect(); if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] http server connection destroyed"); } } /** * {@inheritDoc} */ @Override protected void onConnectionTimeout() throws IOException { if (connectionTimeoutHandlerAdapter != null) { connectionTimeoutHandlerAdapter.callOnConnectionTimeout(); } else { super.onConnectionTimeout(); } } 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() { try { ((IHttpConnectHandler) handler).onConnect(HttpServerConnection.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() { try { ((IHttpDisconnectHandler) handler).onDisconnect(HttpServerConnection.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() { try { boolean isHandled = ((IHttpConnectionTimeoutHandler) handler).onConnectionTimeout(HttpServerConnection.this); if (!isHandled) { closeSilence(); } } catch (Exception e) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by calling on connection timeout " + handler + " " + e.toString()); } } } } final class RequestTimeoutHandlerAdapter implements Runnable { private boolean isMultithreaded = true; public RequestTimeoutHandlerAdapter(boolean isMultithreaded) { this.isMultithreaded = isMultithreaded; } private void callOnRequestTimeout() { if (isMultithreaded) { processMultiThreaded(this); } else { processNonThreaded(this); } } public void run() { try { boolean isHandled = ((IHttpRequestTimeoutHandler) handler).onRequestTimeout(HttpServerConnection.this); if (!isHandled) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("receive timeout for http server connection reached. terminate connection (timeout handler returned false)"); } closeSilence(); } } catch (Exception e) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by calling on request timeout " + handler + " " + e.toString()); } } } } private final class BodyCompleteListener implements IBodyCompleteListener { @Execution(Execution.NONTHREADED) public final void onComplete() { assert (DefaultIoProvider.isDispatcherThread()); lastTimeMessageReceived = System.currentTimeMillis(); // call handlers call back method if is HEADER_MESSAGE triggered if (invokeOnMessageReceived) { performOnRequestCallBack(); } } } private final class OnRequestCaller implements Runnable { public void run() { HttpRequest request = null; int currentRequestQueueVersion = 0; do { // get first message of the queue synchronized (requestQueue) { // are messages in the queue? if (requestQueue.isEmpty()) { // .. no, do nothing return; // ... yes, messages are in the queue } else { // get (but do not remove) message from the queue request = requestQueue.get(0); // is message has not been complete received and is mode MESSAGE do nothing if (invokeOnMessageReceived && !isMessageComplete(request)) { return; } // remove the message from the queue and update counters requestQueue.remove(0); requestQueueVersion++; currentRequestQueueVersion = requestQueueVersion; } } // perform call back method try { ((IHttpRequestHandler) handler).onRequest(request, HttpServerConnection.this); } catch (Exception e) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("[" + getId() + "] error occured by calling on request " + handler + " " + e.toString()); } onRequestHandlingException(e); } // if meantime a new request is received -> loop synchronized (requestQueue) { if (currentRequestQueueVersion == requestQueueVersion) { break; } } } while (request != null); } } private boolean isMessageComplete(HttpRequest request) { // does the message contain a body? if (request.hasBody()) { try { return request.getNonBlockingBody().isComplete(); } catch (IOException ioe) { return false; } // ... no it is a bodyless message } else { return true; } } private static final class TimeoutWatchDogTask extends TimerTask { private WeakReference connectionRef = null; public TimeoutWatchDogTask(HttpServerConnection connection) { connectionRef = new WeakReference(connection); } @Override public void run() { try { HttpServerConnection connection = connectionRef.get(); if (connection == null) { this.cancel(); } else { connection.checkRequestTimeout(System.currentTimeMillis()); } } catch (Exception e) { e.printStackTrace(); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy