/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.container;
import com.ericsson.ssa.container.startup.SipMonitoring;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.Layer;
import com.ericsson.ssa.sip.LayerHelper;
import com.ericsson.ssa.sip.SipParser;
import com.ericsson.ssa.sip.SipServletMessageImpl;
//import com.ericsson.ssa.sip.SipServletMessageImpl.SipMessageType;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetResolver;
import com.ericsson.ssa.sip.dns.TargetTuple;
import com.ericsson.ssa.utils.ByteBufferPool;
import com.sun.grizzly.Context;
import com.sun.grizzly.Controller.Protocol;
import com.sun.grizzly.ProtocolFilter;
import com.sun.grizzly.SelectorHandler;
import com.sun.grizzly.TCPSelectorHandler;
import com.sun.grizzly.async.AsyncWriteCallbackHandler;
import com.sun.grizzly.filter.ReadFilter;
import com.sun.grizzly.util.ByteBufferInputStream;
import com.sun.grizzly.util.WorkerThread;
import com.sun.grizzly.util.ThreadAttachment;
import com.sun.grizzly.util.ThreadAttachment.Mode;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.util.logging.Logger;
/**
*
* @author ekrigro
*/
public class MessageProcessorFilter implements ProtocolFilter, Callable {
private final static Logger logger = LogUtil.SIP_LOGGER.getLogger();
private final Layer _networkHandler;
/**
* Should use a attribute from request-processing rather
* than a system property. default is false.
*/
private static final boolean finalRequestOnContainerThreadPool =
Boolean.getBoolean("org.jvnet.glassfish.comms.finalRequestOnContainerThreadPool");
private int requestTimeOut;
private boolean isRequestBlocked = false;
/** Bytebuffer pool instead of static GrizzlyReference. */
private ByteBufferPool _bbPool;
private AsyncWriteCallbackHandler _asyncWriteCallbackHandler;
/** Creates a new instance of MessageProcessorFilter */
public MessageProcessorFilter(Layer layer, int arequestTimeout,
boolean disablerequests, ByteBufferPool bbPool, AsyncWriteCallbackHandler asyncWriteCallbackHandler) {
_networkHandler = layer;
requestTimeOut = arequestTimeout;
isRequestBlocked = disablerequests;
_bbPool = bbPool;
_asyncWriteCallbackHandler = asyncWriteCallbackHandler;
}
public MessageProcessorFilter(Layer layer, int arequestTimeout, ByteBufferPool bbPool, AsyncWriteCallbackHandler asyncWriteCallbackHandler) {
this(layer, arequestTimeout, false, bbPool, asyncWriteCallbackHandler);
}
public boolean execute(final Context ctx) throws IOException {
SipServletMessageImpl parsedMessage = null;
Socket s = null;
InetSocketAddress remoteAddress = null;
SipServletMessageImpl _message = null;
boolean invokeNextFilter = true;
final WorkerThread workerThread =
((WorkerThread) Thread.currentThread());
ByteBuffer buffer = workerThread.getByteBuffer();
SipParser _parser = SipParser.getInstance();
final Protocol prot = ctx.getProtocol();
final SelectionKey key = ctx.getSelectionKey();
final SelectorHandler handler = ctx.getSelectorHandler();
final ThreadAttachment currentTA = workerThread.getAttachment();
TargetTuple remote = null;
InetSocketAddress local = null;
X509Certificate[] x509Certs = null;
switch (prot) {
case TCP:
s = ((SocketChannel) key.channel()).socket();
remoteAddress = (InetSocketAddress) s.getRemoteSocketAddress();
remote = new TargetTuple(SipTransports.TCP_PROT, remoteAddress);
local = (InetSocketAddress) s.getLocalSocketAddress();
break;
case UDP:
if (!GrizzlyNetworkManager.useDefaultUDPSelectorHandler){
buffer = (ByteBuffer) ctx.removeAttribute
(GrizzlyNetworkManager.UDP_BUFFER);
} else {
ctx.setKeyRegistrationState(Context.KeyRegistrationState.NONE);
handler.register(key, SelectionKey.OP_READ);
}
DatagramSocket d = ((DatagramChannel) key.channel()).socket();
remoteAddress =
(InetSocketAddress) ctx.getAttribute(ReadFilter.UDP_SOCKETADDRESS);
remote = new TargetTuple(SipTransports.UDP_PROT, remoteAddress);
local = (InetSocketAddress) d.getLocalSocketAddress();
break;
case TLS:
s = ((SocketChannel) key.channel()).socket();
remoteAddress = (InetSocketAddress) s.getRemoteSocketAddress();
remote = new TargetTuple(SipTransports.TLS_PROT, remoteAddress);
local = (InetSocketAddress) s.getLocalSocketAddress();
Object[] certs =
(Object[]) ctx.removeAttribute(GrizzlyNetworkManager.SIP_CERTS);
if ((certs != null) && (certs.length > 0)) {
ArrayList<X509Certificate> al =
new ArrayList<X509Certificate>();
for (int i = 0; i < certs.length; i++) {
if (certs[i] instanceof X509Certificate) {
al.add((X509Certificate) certs[i]);
} else {
logger.log(Level.WARNING,
"sip.network.grizzly.wrong.certs",
new Object[]{certs[i].getClass()});
}
}
x509Certs = al.toArray(new X509Certificate[al.size()]);
}
break;
}
try {
int initialSize = 0;
buffer.flip();
int remaining = buffer.remaining();
while (((remaining > 0) && (initialSize != remaining)) ||
(parsedMessage == null)) {
initialSize = remaining;
if (_message == null) {
skipNewLines(buffer);
}
if (!buffer.hasRemaining()) {
return invokeNextFilter;
}
parsedMessage = _parser.parseMessage(_message, buffer, local,
remote, null);
remaining = buffer.remaining();
if ((parsedMessage != null) &&
parsedMessage.isMessageComplete() && remaining > 0) {
if (isRequestBlocked) {
processMessageBlockedState(parsedMessage);
continue;
}
final SipServletMessageImpl msg = parsedMessage;
parsedMessage = null;
_message = null;
final InetSocketAddress _remoteAddress = remoteAddress;
msg.setCertificate(x509Certs);
SipContainerThreadPool.getInstance().execute(new Callable() {
public Object call() throws Exception {
if (prot == Protocol.TLS) {
if (currentTA != null) {
((WorkerThread) Thread.currentThread()).
setSSLEngine(currentTA.getSSLEngine());
((WorkerThread) Thread.currentThread()).
updateAttachment(Mode.SSL_ENGINE);
} else {
logger.log(Level.WARNING,
"Thread attachment is null");
return null;
}
}
processMessage(msg, key, handler, _remoteAddress,
prot);
return null;
}
});
continue;
}
if ((parsedMessage == null) ||
!parsedMessage.isMessageComplete()) {
// UDP packet are *always* read using a single read, hence
// no need to try another read using a temporary Selector.
if (prot != Protocol.UDP) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST,
"sip.network.grizzly.incomplete.tcp.request",
new Object[]{buffer.position()});
}
initialSize = remaining;
if (buffer.hasRemaining()) {
buffer.compact();
} else {
buffer.clear();
}
// The thread might block when reading more bytes using
// a temporary Selector.
ByteBufferInputStream inputStream =
new ByteBufferInputStream();
inputStream.setSecure((prot == Protocol.TLS));
inputStream.setSelectionKey(ctx.getSelectionKey());
inputStream.setReadTimeout(requestTimeOut * 1000);
int nRead = inputStream.read(buffer);
if (nRead <= 0) {
logger.log(Level.SEVERE,
"sip.network.grizzly.readtimeout",
new Object[]{requestTimeOut, nRead, buffer});
if (logger.isLoggable(Level.FINE)) {
SocketChannel channel = (SocketChannel) ctx.getSelectionKey().
channel();
logger.log(Level.FINE,
"sip.network.grizzly.readtimeout.channel" +
channel);
}
if (parsedMessage != null) {
logger.log(Level.SEVERE,
"sip.network.grizzly.readtimeout.drop",
new Object[]{parsedMessage.toString()});
} // Do not invoke the next ProtocolFilter, if any.
return false;
}
remaining = buffer.remaining();
_message = parsedMessage;
} else {
return true;
}
}
}
if (parsedMessage != null) {
if (isRequestBlocked) {
processMessageBlockedState(parsedMessage);
return false;
}
parsedMessage.setCertificate(x509Certs);
if (prot == Protocol.UDP) {
processMessage(parsedMessage, key, handler, remoteAddress,
prot);
} else {
if (!finalRequestOnContainerThreadPool) {
/**
* The key has to be registered back here for TCP, because
* the request will be processesd in the same worker thread
* and further requests cannot be read until this request
* is processed by the app.
*/
ctx.setKeyRegistrationState(Context.KeyRegistrationState.NONE);
handler.register(key, SelectionKey.OP_READ);
processMessage(parsedMessage, key, handler,
remoteAddress, prot);
} else {
/**
* This is necessary if we have to ensure (mitigate
* ) that the messages are processed in same order. * TODO : To register the key, the buffer
* has to be copied into another buffer because this buffer
* will be reused when this worker thread is used for processing.
*/
final SipServletMessageImpl finalProcessedMessage =
parsedMessage;
final InetSocketAddress finalRemoteAddress =
remoteAddress;
SipContainerThreadPool.getInstance().execute(new Callable() {
public Object call() throws Exception {
if (currentTA != null) {
((WorkerThread) Thread.currentThread()).
setSSLEngine(currentTA.getSSLEngine());
((WorkerThread) Thread.currentThread()).
updateAttachment(Mode.SSL_ENGINE);
} else {
logger.log(Level.WARNING,
"Thread attachment is null");
return null;
}
processMessage(finalProcessedMessage,
key, handler, finalRemoteAddress, prot);
return null;
}
});
}
}
}
} catch (Throwable t) {
invokeNextFilter = false;
if (t instanceof Exception) {
logger.log(Level.SEVERE,
"sip.network.grizzly.request.exception", t);
} else {
// FIXME alarm for e.g. OutOfMemoryError
logger.log(Level.SEVERE, "sip.network.grizzly.request.exception",
t);
}
if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
if (((TCPSelectorHandler) handler).getInet() != null) {
((NetworkManager) _networkHandler).incrEasInvalidSipMessages(
((TCPSelectorHandler) handler).getInet().getHostAddress(),
((TCPSelectorHandler) handler).getPort());
} else {
((NetworkManager) _networkHandler).incrEasInvalidSipMessages(
local.getAddress().getHostAddress(), -1);
}
}
} finally {
buffer.clear();
if ((prot == Protocol.UDP) &&
(!GrizzlyNetworkManager.useDefaultUDPSelectorHandler)){
_bbPool.releaseBuffer(buffer);
}
_message = null;
}
return invokeNextFilter;
}
public boolean postExecute(Context context) throws IOException {
return true;
}
private void processMessage(final SipServletMessageImpl message,
SelectionKey key, SelectorHandler handler,
SocketAddress remoteAdress, Protocol prot) {
if (message.getMessageType() ==
SipServletMessageImpl.SipMessageType.SipRequest) {
SipServletRequestImpl req = (SipServletRequestImpl) message;
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST,
"sip.network.grizzly.in.request",
new Object[]{req.getMethod(), req.toString()});
}
if (hasBodyWithoutContentType(message)) {
return;
}
req.pushTransactionDispatcher(_networkHandler);
ResponseDispatcher rd = null;
if (prot == Protocol.TCP) {
rd = new StreamResponseDispatcher(handler, key, _bbPool, _asyncWriteCallbackHandler);
}
else if (prot == Protocol.TLS) {
rd = new TLSResponseDispatcher(handler, key, _bbPool, _asyncWriteCallbackHandler);
}
if (rd != null)
req.pushTransactionDispatcher(rd); //Could be the same channel
req.pushApplicationDispatcher(_networkHandler);
LayerHelper.next(req, _networkHandler, _networkHandler);
if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
if (((TCPSelectorHandler)handler).getInet() != null){
((NetworkManager) _networkHandler).incrEasReceivedSipRequests(
((TCPSelectorHandler)handler).getInet().getHostAddress(),
((TCPSelectorHandler)handler).getPort());
} else {
((NetworkManager) _networkHandler).incrEasReceivedSipRequests(
message.getLocal().getAddress().getHostAddress(), -1);
}
((NetworkManager) _networkHandler).incrReqInMethodCounter(req.getMethod());
}
} else {
SipServletResponseImpl resp = (SipServletResponseImpl) message;
Header cseq = resp.getRawHeader(Header.CSEQ);
/*
* this should never be null
*/
if ((cseq == null) || (cseq.getValue() == null)) {
logger.log(Level.SEVERE, "sip.network.grizzlu.request.cseq.null");
if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
if (((TCPSelectorHandler) handler).getInet() != null){
((NetworkManager) _networkHandler).incrEasInvalidSipMessages(
((TCPSelectorHandler) handler).getInet().getHostAddress(),
((TCPSelectorHandler) handler).getPort());
} else {
((NetworkManager) _networkHandler).incrEasReceivedSipRequests(
message.getLocal().getAddress().getHostAddress(), -1);
}
}
return;
}
String c = cseq.getValue();
int index = c.indexOf(' ');
resp.setMethod(c.substring(index + 1));
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "sip.network.grizzly.in.response",
new Object[]{resp.getStatus(), resp.getMethod(),
resp.toString()
});
}
//_networkHandler.next(resp);
LayerHelper.next(resp, _networkHandler, _networkHandler);
if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
if (((TCPSelectorHandler)handler).getInet() != null){
((NetworkManager) _networkHandler).incrEasReceivedSipResponses(((TCPSelectorHandler)handler).getInet().getHostAddress(),
((TCPSelectorHandler)handler).getPort());
} else {
((NetworkManager) _networkHandler).incrEasReceivedSipResponses(
message.getLocal().getAddress().getHostAddress(), -1);
}
((NetworkManager) _networkHandler).incrRespInStatCodeCounter(resp.getStatus());
}
}
}
public boolean hasBodyWithoutContentType(SipServletMessageImpl message) {
// The SIP-stack should refuse all request with body and without
// Content-Type header field.
if (message.hasBody() && (message.getContentType() == null)) {
try {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST,
"sip.network.grizzly.missing.content.type");
}
SipServletRequestImpl req = (SipServletRequestImpl) message;
String phraze = "Missing Content-Type header field";
SipServletResponseImpl resp = req.createTerminatingResponse(400,
phraze);
sendErrorResponse(resp);
} catch (Exception ignore) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "unexpected exception: ", ignore);
}
}
return true;
}
return false;
}
// TODO
public Object call() throws Exception {
return null;
}
private void skipNewLines(ByteBuffer buffer) {
int position = buffer.position();
while (buffer.remaining() == 2) {
if (buffer.get(position) == '\r' && buffer.get(position + 1) == '\n') {
position += 2;
buffer.position(position);
} else {
break;
}
}
}
private void processMessageBlockedState(SipServletMessageImpl parsedmessage) {
if ((parsedmessage.getMessageType() ==
SipServletMessageImpl.SipMessageType.SipRequest) && (!parsedmessage.getMethod().
equals("ACK"))) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "sip.network.grizzly.reject.request",
new Object[]{parsedmessage});
}
/**
* Add some monitoring info here.
*/
SipServletRequestImpl req =
(SipServletRequestImpl) parsedmessage;
SipServletResponseImpl resp = req.createTerminatingResponse(500);
sendErrorResponse(resp);
}
}
private void sendErrorResponse(SipServletResponseImpl resp) {
while (resp.popDispatcher() != null) {
}
TargetResolver tr = TargetResolver.getInstance();
TargetTuple tt = null;
try {
tt = tr.resolveResponse(resp);
} catch (Exception e) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST,
"sip.network.grizzly.failed.resolution");
}
}
if (tt != null) {
resp.setRemote(tt);
_networkHandler.dispatch(resp);
} else if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST,
"sip.network.grizzly.failed.resolution");
}
}
}