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