// ========================================================================
// Copyright 2008-2009 NEXCOM Systems
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.cipango.lab;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.LinkedList;
import javax.servlet.sip.URI;
import org.cipango.server.AbstractSipConnector;
import org.cipango.server.SipConnection;
import org.cipango.server.SipConnector;
import org.cipango.server.SipConnectors;
import org.cipango.server.SipMessage;
import org.cipango.server.SipRequest;
import org.cipango.server.SipResponse;
import org.cipango.sip.*;
import org.cipango.util.HexString;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.View;
import org.eclipse.jetty.util.log.Log;
public class UdpConnector extends AbstractSipConnector
{
public static final int MAX_UDP_SIZE = 65536;
public static final int DEFAULT_PORT = 5060;
public static final boolean RELIABLE = false;
private DatagramPacket[] _packets;
private DatagramSocket _datagramSocket;
private InetAddress _localAddr;
private LinkedList<UdpBuffer> _bufferQueue;
protected void doStart() throws Exception
{
_packets = new DatagramPacket[getAcceptors()];
for (int i = 0; i < getAcceptors(); i++)
{
_packets[i] = new DatagramPacket(new byte[MAX_UDP_SIZE], MAX_UDP_SIZE);
}
_bufferQueue = new LinkedList<UdpBuffer>();
new Thread(new Processor()).start();
new Thread(new Processor()).start();
super.doStart();
}
protected void doStop() throws Exception
{
super.doStop();
}
public void open() throws IOException
{
_datagramSocket = newDatagramSocket();
_localAddr = _datagramSocket.getLocalAddress();
}
public boolean isOpen()
{
return _datagramSocket != null && !_datagramSocket.isClosed();
}
public int getLocalPort()
{
if (_datagramSocket==null || _datagramSocket.isClosed())
return -1;
return _datagramSocket.getLocalPort();
}
public Object getConnection()
{
return _datagramSocket;
}
protected DatagramSocket newDatagramSocket() throws IOException
{
if (getHost() == null)
_datagramSocket = new DatagramSocket(getPort());
else
_datagramSocket = new DatagramSocket(getPort(), InetAddress.getByName(getHost()));
return _datagramSocket;
}
public void close()
{
_datagramSocket.close();
}
public void accept(int acceptorID) throws IOException, InterruptedException
{
DatagramPacket p = _packets[acceptorID];
_datagramSocket.receive(p);
int length = p.getLength();
if (length == 2 || length == 4) return;
byte[] b = new byte[length];
System.arraycopy(p.getData(), 0, b, 0, length);
Buffer buffer = new ByteArrayBuffer(b);
UdpBuffer udpBuffer = new UdpBuffer();
udpBuffer._buffer = buffer;
udpBuffer._address = p.getAddress();
udpBuffer._port = p.getPort();
synchronized (_bufferQueue)
{
if (_bufferQueue.size() < 1024)
{
_bufferQueue.offer(udpBuffer);
_bufferQueue.notifyAll();
}
else
System.out.println("full");
}
}
public SipConnection getConnection(InetAddress address, int port)
{
return new UdpConnection(address, port);
}
public int getDefaultPort()
{
return DEFAULT_PORT;
}
public InetAddress getAddr()
{
return _localAddr;
}
public int getTransportOrdinal()
{
return SipConnectors.UDP_ORDINAL;
}
public boolean isReliable()
{
return RELIABLE;
}
public boolean isSecure()
{
return false;
}
public Buffer newBuffer()
{
return new ByteArrayBuffer(MAX_UDP_SIZE);
}
public SipConnector getConnector()
{
return this;
}
class UdpConnection implements SipConnection
{
private InetAddress _remoteAddr;
private int _remotePort;
public UdpConnection(InetAddress remoteAddr, int remotePort)
{
_remoteAddr = remoteAddr;
_remotePort = remotePort;
}
public SipConnector getConnector()
{
return UdpConnector.this;
}
public InetAddress getLocalAddress()
{
return _localAddr;
}
public int getLocalPort()
{
return UdpConnector.this.getPort();
}
public InetAddress getRemoteAddress()
{
return _remoteAddr;
}
public int getRemotePort()
{
return _remotePort;
}
public void write(Buffer buffer) throws IOException
{
DatagramPacket packet = new DatagramPacket(
buffer.array(),
buffer.length(),
_remoteAddr,
_remotePort);
_datagramSocket.send(packet);
}
public boolean isOpen()
{
return (_datagramSocket != null && !_datagramSocket.isClosed());
}
public String toString()
{
return "udp/" + _remoteAddr.getHostAddress() + ":" + _remotePort;
}
}
class UdpBuffer
{
Buffer _buffer;
InetAddress _address;
int _port;
}
class Processor implements Runnable
{
public void run()
{
UdpBuffer udpBuffer;
try
{
while (isRunning())
{
try
{
synchronized (_bufferQueue)
{
while (_bufferQueue.isEmpty())
_bufferQueue.wait();
udpBuffer = _bufferQueue.poll();
}
Buffer buffer = udpBuffer._buffer;
EventHandler handler = new EventHandler();
SipParser parser = new SipParser(buffer, handler);
try
{
parser.parse();
SipMessage message = handler.getMessage();
message.setConnection(new UdpConnection(udpBuffer._address, udpBuffer._port));
/*
message.set5uple(getTransportOrdinal(), getAddr(), getPort(), p.getAddress(), p.getPort());
if (message.isRequest())
((SipRequest) message).setEndpoint(this);
*/
process(message);
}
catch (Throwable t)
{
Log.warn(t);
//if (handler.hasException())
//Log.warn(handler.getException());
if (Log.isDebugEnabled())
Log.debug("Buffer content: \r\n" + HexString.toDetailedHexString(buffer.array(), buffer.length()));
}
}
catch (Exception e)
{
Log.warn(e);
}
}
}
finally
{
}
}
}
class EventHandler extends SipParser.EventHandler
{
private SipMessage _message;
@Override
public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
{
try
{
URI ruri = URIFactory.parseURI(uri.toString());
SipRequest request = new SipRequest();
request.setMethod(method.toString());
request.setRequestURI(ruri);
_message = request;
}
catch (Exception e)
{
throw new IOException("Parsing error: " + e.getMessage());
}
}
@Override
public void startResponse(Buffer version, int status, Buffer reason) throws IOException
{
SipResponse response = new SipResponse();
response.setStatus(status, reason.toString());
_message = response;
}
public SipMessage getMessage()
{
return _message;
}
@Override
public void header(Buffer name, Buffer value) throws IOException
{
if (_message == null)
throw new IOException("no status line");
if (SipHeaders.getType(name).isList())
{
boolean quote = false;
int start = value.getIndex();
int end = value.putIndex();
byte[] b = value.array();
//if (value == null) value = _value.asArray();
int startValue = start;
int endValue = end;
while (end > start && b[end -1] <= ' ') end--;
for (int i = start; i < end; i++)
{
int c = b[i];
if (c == '"') quote = !quote;
if (c == ',' && !quote)
{
endValue = i;
while (endValue > start && b[endValue -1] <= ' ') endValue--;
while (startValue < endValue && b[startValue] <= ' ') startValue++;
//byte[] bValue = asArray(b, startValue, endValue - startValue);
Buffer buffer = new View(value, startValue, startValue, endValue, Buffer.READONLY);
_message.getFields().addBuffer(name, buffer);
//value = new View(value, i + 1, i + 1, end, Buffer.READONLY);
startValue = i + 1;
}
}
while (startValue < end && b[startValue] <= ' ') startValue++;
value = new View(value, startValue, startValue, end, Buffer.READONLY);
//byte[] bValue = asArray(b, startValue, end - startValue);
_message.getFields().addBuffer(name, value);
//value = new View(value, startValue, startValue, end, Buffer.READONLY);
}
else
{
_message.getFields().addBuffer(name, value);
//msg.getFields().addBuffer(name, new ByteArrayBuffer(value.asArray()));
}
}
@Override
public void content(Buffer buffer) throws IOException
{
if (buffer.length() > 0)
_message.setRawContent(buffer.asArray()); // TODO buffer
}
}
}