naga-3.0+svn80/000077500000000000000000000000001300251770300132325ustar00rootroot00000000000000naga-3.0+svn80/build.properties000066400000000000000000000001701300251770300164450ustar00rootroot00000000000000build.dir=_BUILD build.classes.dir=${build.dir}/classes main.dir=src/main docs.dir=${build.dir}/docs/api dist.dir=_DIST naga-3.0+svn80/build.xml000066400000000000000000000061771300251770300150660ustar00rootroot00000000000000 Naga]]>
naga-3.0+svn80/src/000077500000000000000000000000001300251770300140215ustar00rootroot00000000000000naga-3.0+svn80/src/main/000077500000000000000000000000001300251770300147455ustar00rootroot00000000000000naga-3.0+svn80/src/main/naga/000077500000000000000000000000001300251770300156535ustar00rootroot00000000000000naga-3.0+svn80/src/main/naga/ChannelResponder.java000066400000000000000000000200331300251770300217460ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.net.InetSocketAddress; import java.nio.channels.CancelledKeyException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; /** * The base class for socket and server socket responders. *

* Handles basic functionality such as presenting IP and port, * handling of key and channel as well as closing the socket. * * @author Christoffer Lerno */ abstract class ChannelResponder implements NIOAbstractSocket { private final NIOService m_service; private final String m_ip; private final InetSocketAddress m_address; private final int m_port; private final SelectableChannel m_channel; private volatile boolean m_open; private volatile SelectionKey m_key; private volatile int m_interestOps; private boolean m_observerSet; private Object m_tag; /** * Creates a new channel responder. * * @param service the NIOService this responder belongs to. * @param channel the channel handled by this responder. * @param address the address this channel is associated with. */ protected ChannelResponder(NIOService service, SelectableChannel channel, InetSocketAddress address) { m_channel = channel; m_service = service; m_open = true; m_key = null; m_interestOps = 0; m_observerSet = false; m_address = address; m_ip = address.getAddress().getHostAddress(); m_port = address.getPort(); m_tag = null; } public InetSocketAddress getAddress() { return m_address; } public String getIp() { return m_ip; } public int getPort() { return m_port; } public void setTag(Object tag) { m_tag = tag; } public Object getTag() { return m_tag; } /** * @return the NIOService this responder is connected to. */ protected NIOService getNIOService() { return m_service; } /** * Mark the observer for this responder as set. *

* This does not mean that the observer is really set, but instead that * a set has been queued on the NIOService thread. *

* This call prevents the observer from being set more than once. */ protected void markObserverSet() { synchronized (this) { if (m_observerSet) throw new IllegalStateException("Listener already set."); m_observerSet = true; } } /** * Called by the NIOService when the key has read interest ready. */ void socketReadyForRead() { throw new UnsupportedOperationException(getClass() + " does not support read."); } /** * Called by the NIOService when the key has write interest ready. */ void socketReadyForWrite() { throw new UnsupportedOperationException(getClass() + " does not support write."); } /** * Called by the NIOService when the key has accept interest ready. */ void socketReadyForAccept() { throw new UnsupportedOperationException(getClass() + " does not support accept."); } /** * Called by the NIOService when the key has connect interest ready. */ void socketReadyForConnect() { throw new UnsupportedOperationException(getClass() + " does not support connect."); } /** * Get the channel used by this responder. *

* This method is thread-safe. * * @return the channel used by the responder. */ protected SelectableChannel getChannel() { return m_channel; } /** * Sets the selection key of this responder. *

* This method is called on the NIOService-thread. * * @param key the selection key for this responder. * @throws IllegalStateException if the selection key already is set. */ void setKey(SelectionKey key) { if (m_key != null) throw new IllegalStateException("Tried to set selection key twice"); m_key = key; if (!isOpen()) { // If we closed this before receiving the key, we should cancel the key. NIOUtils.cancelKeySilently(m_key); return; } keyInitialized(); synchronizeKeyInterestOps(); } /** * Returns the selection key or null if the key is not set. *

* This method is thread-safe. * * @return the selection key or nul if the responder is not yet registered. */ protected SelectionKey getKey() { return m_key; } /** * This method is called after the SelectionKey is set. *

* This method is called on the NIOService-thread. */ abstract void keyInitialized(); public boolean isOpen() { return m_open; } public void close() { close(null); } /** * Asynchronously closes the channel with a given exception as a cause. * * @param exception the exception that caused the close, or null if this was * closed by the user of this responder. */ protected void close(Exception exception) { if (isOpen()) { getNIOService().queue(new CloseEvent(this, exception)); } } /** * Synchronizes the desired interest ops with the key interests ops, * if the key is initialized. *

* This method is thread-safe. */ private void synchronizeKeyInterestOps() { if (m_key != null) { try { int oldOps = m_key.interestOps(); if ((m_interestOps & SelectionKey.OP_CONNECT) != 0) { m_key.interestOps(SelectionKey.OP_CONNECT); } else { m_key.interestOps(m_interestOps); } if (m_key.interestOps() != oldOps) { m_service.wakeup(); } } catch (CancelledKeyException e) { // Ignore these. } } } /** * Deleted an interest on a key. *

* This method is called on the NIOService-thread. * * @param interest the interest to delete. */ protected void deleteInterest(int interest) { m_interestOps = m_interestOps & ~interest; synchronizeKeyInterestOps(); } /** * Add an interest to the key, or change the currently pending interest. *

* This method is called on the NIOService-thread. * * @param interest the interest to add. */ protected void addInterest(int interest) { m_interestOps |= interest; synchronizeKeyInterestOps(); } /** * Returns a string on the format [ip]:[port] * This method is thread-safe. * * @return a string on the format IP:port. */ @Override public String toString() { return m_ip + ":" + m_port; } /** * Called once when the responder goes from state open to closed. *

* This method is called on the NIOService thread. * * @param e the exception that caused the shutdown, may be null. */ protected abstract void shutdown(Exception e); /** * A close event sent when close() is called. *

* When run this event sets m_open to false and silently cancels * the key and closes the channel. *

* Finally the responder's shutdown method is called. */ private static class CloseEvent implements Runnable { private final ChannelResponder m_responder; private final Exception m_exception; private CloseEvent(ChannelResponder responder, Exception e) { m_responder = responder; m_exception = e; } public void run() { if (m_responder.isOpen()) { m_responder.m_open = false; NIOUtils.closeKeyAndChannelSilently(m_responder.getKey(), m_responder.getChannel()); m_responder.shutdown(m_exception); } } } } naga-3.0+svn80/src/main/naga/ConnectionAcceptor.java000066400000000000000000000052411300251770300223000ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.net.InetSocketAddress; /** * The ConnectionAcceptor is used by the NIOServerSocket to determine * if a connection should be accepted or refused. *

* This can be used to implement black-listing of certain IP-ranges * or to limit the number of simultaneous connection. However, * in most cases it is enough to use the ConnectorAcceptor.ALLOW which * accepts all incoming connections. *

* Note that a NIOServerSocket defaults to the ConnectorAcceptor.ALLOW * acceptor when it is created. * * * @author Christoffer Lerno */ public interface ConnectionAcceptor { /** * A connection acceptor that refuses all connections. */ ConnectionAcceptor DENY = new ConnectionAcceptor() { public boolean acceptConnection(InetSocketAddress address) { return false; } }; /** * A connection acceptor that accepts all connections. */ ConnectionAcceptor ALLOW = new ConnectionAcceptor() { public boolean acceptConnection(InetSocketAddress address) { return true; } }; /** * Return true if the connection should be accepted, false otherwise. *

* Note: This callback is run on the NIOService thread. This means it will block * all other reads, writes and accepts on the service while it executes. * For this reason it is recommended that this method should return fairly quickly * (i.e. don't make reverse ip lookups or similar - potentially very slow - calls). * * * @param inetSocketAddress the adress the connection came from. * @return true to accept, false to refuse. */ boolean acceptConnection(InetSocketAddress inetSocketAddress); } naga-3.0+svn80/src/main/naga/ExceptionObserver.java000066400000000000000000000031741300251770300221710ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; /** * Implemented by observers of event exceptions. *

* The notifyExceptionThrown will be called synchronously * whn an exception is thrown. * * @author Christoffer Lerno */ public interface ExceptionObserver { ExceptionObserver DEFAULT = new ExceptionObserver() { @SuppressWarnings({"CallToPrintStackTrace"}) public void notifyExceptionThrown(Throwable e) { e.printStackTrace(); } }; /** * Notify the observer that an exception has been thrown. * * @param e the exception that was thrown. */ void notifyExceptionThrown(Throwable e); } naga-3.0+svn80/src/main/naga/NIOAbstractSocket.java000066400000000000000000000047441300251770300220110ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.net.InetSocketAddress; /** * An interface describing methods common to both NIOSocket and NIOServerSocket. * * @author Christoffer Lerno */ public interface NIOAbstractSocket { /** * Closes this socket (the actual disconnect will occur on the NIOService thread) *

* This method is thread-safe. */ void close(); /** * Returns the InetSocketAddress for this socket. *

* This method is thread-safe. * * @return the InetSocketAddress this socket connects to. */ InetSocketAddress getAddress(); /** * Returns the current state of this socket. *

* This method is thread-safe. * * @return true if the connection is socket is open, false if closed. */ boolean isOpen(); /** * Reports the IP used by this socket. *

* This method is thread-safe. * * @return the IP of this socket. */ String getIp(); /** * Returns the port in use by this socket. *

* This method is thread-safe. * * @return the port of this socket. */ int getPort(); /** * Returns the tag for this socket. * * @return the tag or null if no tag has been set. */ Object getTag(); /** * Returns the tag for this socket. A tag is an object * that you can associate with the socket and retrieve later. * * @param tag the new tag for this socket. */ void setTag(Object tag); } naga-3.0+svn80/src/main/naga/NIOServerSocket.java000066400000000000000000000072021300251770300215040ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.net.ServerSocket; /** * Interface for the NIOServerSocket, which is * an asynchronous facade to an underlying ServerSocket. *

* The NIOServerSocket executes callbacks to a ServerSocket observer * to react to new connections and other events. * * @author Christoffer Lerno */ public interface NIOServerSocket extends NIOAbstractSocket { /** * Returns the total number of connections made on this socket since * it opened. *

* This method is thread-safe. * * @return the total number of connections made on this server socket. */ long getTotalConnections(); /** * Returns the total number of refused connections on this socket since * it opened. *

* This method is thread-safe. * * @return the total number of refused connections on this server socket. */ long getTotalRefusedConnections(); /** * Returns the total number of accepted connections on this socket since * it opened. *

* This method is thread-safe. * * @return the total number of accepted connections on this server socket. */ long getTotalAcceptedConnections(); /** * Returns the total number of failed connections on this socket since * it opened. *

* This method is thread-safe. * * @return the total number of failed connections on this server socket. */ long getTotalFailedConnections(); /** * Associates a server socket observer with this server socket and * starts accepting connections. *

* This method is thread-safe, but may only be called once. * * @param observer the observer to receive callbacks from this socket. * @throws NullPointerException if the observer given is null. * @throws IllegalStateException if an observer has already been set. */ void listen(ServerSocketObserver observer); /** * Sets the connection acceptor for this server socket. *

* A connection acceptor determines if a connection should be * disconnected or not after the initial accept is done. *

* For more information, see the documentation for naga.ConnectionAcceptor. *

* This method is thread-safe. * * @param acceptor the acceptor to use, or null to default to * ConnectorAcceptor.DENY. */ void setConnectionAcceptor(ConnectionAcceptor acceptor); /** * Allows access to the underlying server socket. *

* Note calling close and similar functions on this socket * will put the NIOServerSocket in an undefined state * * @return return the underlying server socket. */ ServerSocket socket(); } naga-3.0+svn80/src/main/naga/NIOServerSocketSSL.java000066400000000000000000000025621300251770300220720ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import javax.net.ssl.SSLContext; /** * Interface for SSL Server Sockets * * @author Christoffer Lerno */ public interface NIOServerSocketSSL extends NIOServerSocket { /** * Returns the SSLContext in use. * * @return the SSLContext. */ public SSLContext getSSLContext(); } naga-3.0+svn80/src/main/naga/NIOService.java000066400000000000000000000500401300251770300204630ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; /** * This class forms the basis of the NIO handling in Naga. *

* Common usage is to create a single instance of this service and * then run one other the select methods in a loop. *

* Use {@link naga.NIOService#openSocket(String, int)} to open a socket to a remote server, * and {@link naga.NIOService#openServerSocket(int)} to open a server socket locally. *

* Note that the NIOServerSocket by default opens in a "refuse connections" state. To * start accepting players, the socket's acceptor must first be set to accept connections. * See documentation for openServerSocket for more details. *

* Example use: *

* Using the server socket: *

 * NIOService service = new NIOService;
 * NIOServerSocket serverSocket = service.openServerSocket(1234);
 * serverSocket.setConnectionAcceptor(myAcceptor);
 * serverSocket.listen(myObserver);
 * 
* Using regular sockets: *
 * NIOService service = new NIOService;
 * NIOSocket socket = service.openSocket("www.google.com", 1234);
 * socket.listen(myObserver);
 * // Asynchronous write by default:
 * socket.write("Some message".getBytes());
 * 
* * @author Christoffer Lerno */ public class NIOService { public final static int DEFAULT_IO_BUFFER_SIZE = 64 * 1024; /** The selector used by this service */ private final Selector m_selector; private final Queue m_internalEventQueue; private ByteBuffer m_sharedBuffer; private ExceptionObserver m_exceptionObserver; /** * Create a new nio service with default buffer size (64kb) * * @throws IOException if we failed to open the underlying selector used by the * service. */ public NIOService() throws IOException { this(DEFAULT_IO_BUFFER_SIZE); } /** * Create a new nio service. * * @param ioBufferSize the maximum buffer size. * @throws IOException if we failed to open the underlying selector used by the * service. * @throws IllegalArgumentException if the buffer size is less than 256 bytes. */ public NIOService(int ioBufferSize) throws IOException { m_selector = Selector.open(); m_internalEventQueue = new ConcurrentLinkedQueue(); m_exceptionObserver = ExceptionObserver.DEFAULT; setBufferSize(ioBufferSize); } /** * Run all waiting NIO requests, blocking indefinitely * until at least one request is handled. * * @throws IOException if there is an IO error waiting for requests. * @throws ClosedSelectorException if the underlying selector is closed * (in this case, NIOService#isOpen will return false) */ public synchronized void selectBlocking() throws IOException { executeQueue(); if (m_selector.select() > 0) { handleSelectedKeys(); } executeQueue(); } /** * Run all waiting NIO requests, returning immediately if * no requests are found. * * @throws IOException if there is an IO error waiting for requests. * @throws ClosedSelectorException if the underlying selector is closed. * (in this case, NIOService#isOpen will return false) */ public synchronized void selectNonBlocking() throws IOException { executeQueue(); if (m_selector.selectNow() > 0) { handleSelectedKeys(); } executeQueue(); } /** * Run all waiting NIO requests, blocking until * at least one request is found, or the method has blocked * for the time given by the timeout value, whatever comes first. * * @param timeout the maximum time to wait for requests. * @throws IllegalArgumentException If the value of the timeout argument is negative. * @throws IOException if there is an IO error waiting for requests. * @throws ClosedSelectorException if the underlying selector is closed. * (in this case, NIOService#isOpen will return false) */ public synchronized void selectBlocking(long timeout) throws IOException { executeQueue(); if (m_selector.select(timeout) > 0) { handleSelectedKeys(); } executeQueue(); } /** * Open a normal socket to the host on the given port returning * a NIOSocket. *

* This roughly corresponds to creating a regular socket using new Socket(host, port). *

* This method is thread-safe. * * @param host the host we want to connect to. * @param port the port to use for the connection. * @return a NIOSocket object for asynchronous communication. * @throws IOException if registering the new socket failed. */ public NIOSocket openSocket(String host, int port) throws IOException { return openSocket(InetAddress.getByName(host), port); } /** * Open a socket to the host on the given port returning * a NIOSocketSSL. *

* This roughly corresponds to creating a regular socket using new SSLSocket(host, port). *

* This method is thread-safe. * * @param sslEngine the SSL engine to use for SSL-negotiation. * @param host the host we want to connect to. * @param port the port to use for the connection. * @return a NIOSocket object for asynchronous communication. * @throws IOException if registering the new socket failed. */ public NIOSocket openSSLSocket(SSLEngine sslEngine, String host, int port) throws IOException { return openSSLSocket(sslEngine, InetAddress.getByName(host), port); } /** * Open a normal socket to the host on the given port returning * a NIOSocket. *

* This roughly corresponds to creating a regular socket using new Socket(inetAddress, port). *

* This method is thread-safe. * * @param inetAddress the address we want to connect to. * @param port the port to use for the connection. * @return a NIOSocket object for asynchronous communication. * @throws IOException if registering the new socket failed. */ public NIOSocket openSocket(InetAddress inetAddress, int port) throws IOException { SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); InetSocketAddress address = new InetSocketAddress(inetAddress, port); channel.connect(address); return registerSocketChannel(channel, address); } /** * Open a socket to the host on the given port returning * a NIOSocketSSL. *

* This roughly corresponds to creating a regular socket using new SSLSocket(inetAddress, port). *

* This method is thread-safe. * * @param sslEngine the SSL engine to use for SSL-negotiation. * @param inetAddress the address we want to connect to. * @param port the port to use for the connection. * @return a NIOSocketSSL object for asynchronous communication. * @throws IOException if registering the new socket failed. */ public NIOSocketSSL openSSLSocket(SSLEngine sslEngine, InetAddress inetAddress, int port) throws IOException { SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); InetSocketAddress address = new InetSocketAddress(inetAddress, port); channel.connect(address); return new SSLSocketChannelResponder(this, registerSocketChannel(channel, address), sslEngine, true); } /** * Open a server socket on the given port. *

* This roughly corresponds to using new ServerSocket(port, backlog); *

* This method is thread-safe. * * @param port the port to open. * @param backlog the maximum connection backlog (i.e. connections pending accept) * @return a NIOServerSocket for asynchronous connection to the server socket. * @throws IOException if registering the socket fails. */ public NIOServerSocket openServerSocket(int port, int backlog) throws IOException { return openServerSocket(new InetSocketAddress(port), backlog); } /** * Open an SSL server socket on the given port. *

* This roughly corresponds to using new SSLServerSocket(port, backlog); *

* This method is thread-safe. * * @param sslContext the SSLContext to use for SSL-negotiation. * @param port the port to open. * @param backlog the maximum connection backlog (i.e. connections pending accept) * @return a NIOServerSocket for asynchronous connection to the server socket. * @throws IOException if registering the socket fails. */ public NIOServerSocketSSL openSSLServerSocket(SSLContext sslContext, int port, int backlog) throws IOException { return openSSLServerSocket(sslContext, new InetSocketAddress(port), backlog); } /** * Open a server socket on the given port with the default connection backlog. *

* This roughly corresponds to using new ServerSocket(port); *

* This method is thread-safe. * * @param port the port to open. * @return a NIOServerSocket for asynchronous connection to the server socket. * @throws IOException if registering the socket fails. */ public NIOServerSocket openServerSocket(int port) throws IOException { return openServerSocket(port, -1); } /** * Open an SSL server socket on the given port with the default connection backlog. *

* This roughly corresponds to using new SSLServerSocket(port); *

* This method is thread-safe. * * @param sslContext the SSLContext to use for SSL-negotiation. * @param port the port to open. * @return a NIOServerSocket for asynchronous connection to the server socket. * @throws IOException if registering the socket fails. */ public NIOServerSocketSSL openSSLServerSocket(SSLContext sslContext, int port) throws IOException { return openSSLServerSocket(sslContext, port, -1); } /** * Open an SSL server socket on the address. *

* This method is thread-safe. * * @param sslContext the SSLContext to use for SSL-negotiation. * @param address the address to open. * @param backlog the maximum connection backlog (i.e. connections pending accept) * @return a NIOServerSocket for asynchronous connection to the server socket. * @throws IOException if registering the socket fails. */ public NIOServerSocketSSL openSSLServerSocket(SSLContext sslContext, InetSocketAddress address, int backlog) throws IOException { ServerSocketChannel channel = ServerSocketChannel.open(); channel.socket().setReuseAddress(true); channel.socket().bind(address, backlog); channel.configureBlocking(false); SSLServerSocketChannelResponder channelResponder = new SSLServerSocketChannelResponder(sslContext, this, channel, address); queue(new RegisterChannelEvent(channelResponder)); return channelResponder; } /** * Open a server socket on the address. *

* This method is thread-safe. * * @param address the address to open. * @param backlog the maximum connection backlog (i.e. connections pending accept) * @return a NIOServerSocket for asynchronous connection to the server socket. * @throws IOException if registering the socket fails. */ public NIOServerSocket openServerSocket(InetSocketAddress address, int backlog) throws IOException { ServerSocketChannel channel = ServerSocketChannel.open(); channel.socket().setReuseAddress(true); channel.socket().bind(address, backlog); channel.configureBlocking(false); ServerSocketChannelResponder channelResponder = new ServerSocketChannelResponder(this, channel, address); queue(new RegisterChannelEvent(channelResponder)); return channelResponder; } /** * Internal method to mark a socket channel for pending registration * and create a NIOSocket wrapper around it. *

* This method is thread-safe. * * @param socketChannel the socket channel to wrap. * @param address the address for this socket. * @return the NIOSocket wrapper. * @throws IOException if configuring the channel fails, or the underlying selector is closed. */ NIOSocket registerSocketChannel(SocketChannel socketChannel, InetSocketAddress address) throws IOException { socketChannel.configureBlocking(false); SocketChannelResponder channelResponder = new SocketChannelResponder(this, socketChannel, address); queue(new RegisterChannelEvent(channelResponder)); return channelResponder; } /** * Internal method to execute events on the internal event queue. *

* This method should only ever be called from the NIOService thread. */ private void executeQueue() { Runnable event; while ((event = m_internalEventQueue.poll()) != null) { try { event.run(); } catch (Throwable t) { notifyException(t); } } } /** * Internal method to handle the key set generated by the internal Selector. *

* Will simply remove each entry and handle the key. *

* Called on the NIOService thread. */ private void handleSelectedKeys() { // Loop through all selected keys and handle each key at a time. for (Iterator it = m_selector.selectedKeys().iterator(); it.hasNext();) { // Retrieve the key. SelectionKey key = it.next(); // Remove it from the set so that it is not read again. it.remove(); // Handle actions on this key. try { handleKey(key); } catch (Throwable t) { notifyException(t); } } } /** * Set the new shared buffer size. *

* This method is *not* thread-safe. * @param newBufferSize the new buffer size. * @throws IllegalArgumentException if the new size is less than 256 bytes. */ public void setBufferSize(int newBufferSize) { if (newBufferSize < 256) throw new IllegalArgumentException("The buffer must at least hold 256 bytes"); m_sharedBuffer = ByteBuffer.allocate(newBufferSize); } /** * Returns the new shared buffer size. *

* This method is *not* thread-safe. * * @return the current buffer size, which is the largest packet that can be read. */ public int getBufferSize() { return m_sharedBuffer.capacity(); } /** * Returns the shared byte buffer. This is shared between all users of the service to avoid allocating * a huge number of byte buffers. * * @return the shared byte buffer. */ ByteBuffer getSharedBuffer() { return m_sharedBuffer; } /** * Internal method to handle a SelectionKey that has changed. *

* Will delegate actual actions to the associated ChannelResponder. *

* Called on the NIOService thread. * @param key the key to handle. */ private void handleKey(SelectionKey key) { ChannelResponder responder = (ChannelResponder) key.attachment(); try { if (key.isReadable()) { responder.socketReadyForRead(); } if (key.isWritable()) { responder.socketReadyForWrite(); } if (key.isAcceptable()) { responder.socketReadyForAccept(); } if (key.isConnectable()) { responder.socketReadyForConnect(); } } catch (CancelledKeyException e) { responder.close(e); // The key was cancelled and will automatically be removed next select. } } /** * Close the entire service. *

* This will disconnect all sockets associated with this service. *

* It is not possible to restart the service once closed. *

* This method is thread-safe. */ public void close() { if (!isOpen()) return; queue(new ShutdownEvent()); } /** * Determine if this service is open. *

* This method is thread-safe. * * @return true if the service is open, false otherwise. */ public boolean isOpen() { return m_selector.isOpen(); } /** * Queues an event on the NIOService queue. *

* This method is thread-safe, but should in general not be used by * other applications. * * @param event the event to run on the NIOService-thread. */ public void queue(Runnable event) { m_internalEventQueue.add(event); wakeup(); } /** * Returns a copy of the internal event queue. * * @return a copy of the internal event queue. */ public Queue getQueue() { return new LinkedList(m_internalEventQueue); } /** * Runs wakeup on the selector, causing any blocking select to be released. */ public void wakeup() { m_selector.wakeup(); } /** * Updates the exception observer for the NIOService. * * @param exceptionObserver the new exception observer, if this is null, logging will be directed to stderr. */ public void setExceptionObserver(ExceptionObserver exceptionObserver) { final ExceptionObserver newExceptionObserver = exceptionObserver == null ? ExceptionObserver.DEFAULT : exceptionObserver; queue(new Runnable() { public void run() { m_exceptionObserver = newExceptionObserver; } }); } /** * Logs an exception using the exception observer. This is mainly for use by the Naga classes. *

* Should only be used on the NIOService thread. * * @param t the exception thrown. */ public void notifyException(Throwable t) { try { m_exceptionObserver.notifyExceptionThrown(t); } catch (Exception e) { System.err.println("Failed to log the following exception to the exception observer:"); System.err.println(e); e.printStackTrace(); } } /** * A registration class to let registrations occur on the NIOService thread. */ private class RegisterChannelEvent implements Runnable { private final ChannelResponder m_channelResponder; private RegisterChannelEvent(ChannelResponder channelResponder) { m_channelResponder = channelResponder; } public void run() { try { SelectionKey key = m_channelResponder.getChannel().register(m_selector, m_channelResponder.getChannel().validOps()); m_channelResponder.setKey(key); key.attach(m_channelResponder); } catch (Exception e) { m_channelResponder.close(e); } } @Override public String toString() { return "Register[" + m_channelResponder + "]"; } } /** * Shutdown class to let shutdown happen on the NIOService thread. */ private class ShutdownEvent implements Runnable { public void run() { if (!isOpen()) return; for (SelectionKey key : m_selector.keys()) { try { NIOUtils.cancelKeySilently(key); ((ChannelResponder) key.attachment()).close(); } catch (Exception e) { // Swallow exceptions. } } try { m_selector.close(); } catch (IOException e) { // Swallow exceptions. } } } } naga-3.0+svn80/src/main/naga/NIOSocket.java000066400000000000000000000142431300251770300203200ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.net.Socket; /** * Interface for the NIOSocket, which is * an asynchronous facade to an underlying Socket. *

* The NIOSocket executes callbacks to a Socket observer * to react to incoming packets and other events. * * @author Christoffer Lerno */ public interface NIOSocket extends NIOAbstractSocket { /** * Write a packet of bytes asynchronously on this socket. *

* The bytes will be sent to the PacketWriter belonging to this * socket for dispatch. However, if the queue is full (i.e. the new * queue size would exceed getMaxQueueSize()), * the packet is discarded and the method returns false. *

* This method is thread-safe. * * @param packet the packet to send. * @return true if the packet was queued, false if the queue limit * was reached and the packet was thrown away. */ boolean write(byte[] packet); /** * Write a packet of bytes asynchronously on this socket. *

* The bytes will be sent to the PacketWriter belonging to this * socket for dispatch. However, if the queue is full (i.e. the new * queue size would exceed getMaxQueueSize()), * the packet is discarded and the method returns false. *

* This method is thread-safe. * * @param packet the packet to send. * @param tag an optional tag to tag the packet (used in {@link naga.SocketObserver#packetSent(NIOSocket, Object)}). * @return true if the packet was queued, false if the queue limit * was reached and the packet was thrown away. */ boolean write(byte[] packet, Object tag); /** * Queue a runnable in the packet queue. This runnable will execute * after the latest packet in the queue is sent. *

* This method is thread-safe. * * @param runnable the runnable to queue. */ void queue(Runnable runnable); /** * Return the total number of bytes read on this socket since * it was opened. *

* This method is thread-safe. * * @return the total number of bytes read on this socket. */ long getBytesRead(); /** * Return the total number of bytes written on this socket * since it was opened. *

* This method is thread-safe. * * @return the total number of bytes written on this socket. */ long getBytesWritten(); /** * Return the time this socket has been open. *

* This method is thread-safe. * * @return the time this socket has been open in ms. */ long getTimeOpen(); /** * This method returns the number of bytes queued for dispatch. * This size is compared against the maximum queue size to determine if additional packets * will be refused or not. *

* However, this number does not include the packet currently waiting to be written. *

* This method is thread-safe. * * @return the total size of the packets waiting to be dispatched, exluding the currently * dispatching packet. */ long getWriteQueueSize(); /** * The current maximum queue size in bytes. *

* This method is thread-safe. * * @return the current maximum queue size. */ int getMaxQueueSize(); /** * Sets the maximum number of bytes allowed in the queue for this socket. If this * number is less than 1, the queue is unbounded. *

* This method is thread-safe. * * @param maxQueueSize the new max queue size. A value less than 1 is an unbounded queue. */ void setMaxQueueSize(int maxQueueSize); /** * Sets the packet reader for this socket. * * @param packetReader the packet reader to interpret the incoming byte stream. */ void setPacketReader(PacketReader packetReader); /** * Sets the packet writer for this socket. *

* This method is thread-safe and all packets posted before the writer is * changed is guaranteed to be serialized using the previous writer. * * @param packetWriter the packet writer to interpret the incoming byte stream. */ void setPacketWriter(PacketWriter packetWriter); /** * Opens the socket for reads. *

* The socket observer will receive connects, disconnects and packets. * If the socket was opened or disconnected before the observer was attached, * the socket observer will still receive those callbacks. *

* This method is thread-safe, but may only be called once. * * @param socketObserver the observer to receive packets and be notified of connects/disconnects. * @throws IllegalStateException if the method already has been called. */ void listen(SocketObserver socketObserver); /** * Causes the socket to close after writing the current entries in the queue * (consequent entries will be thrown away). *

* Also see close() if you want to immediately close the socket. *

* This method is thread-safe. */ void closeAfterWrite(); /** * Allows access to the underlying socket. *

* Note that accessing streams or closing the socket will * put this NIOSocket in an undefined state * * @return return the underlying socket. */ Socket socket(); } naga-3.0+svn80/src/main/naga/NIOSocketSSL.java000066400000000000000000000032061300251770300206770ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; /** * Interface for a SSL Socket * * @author Christoffer Lerno */ public interface NIOSocketSSL extends NIOSocket { /** * Returns the SSLEngine in use for this socket. * * @return an SSLEngine. */ SSLEngine getSSLEngine(); /** * Initiates SSL-handshake, starts encrypted communication. */ void beginHandshake() throws SSLException; /** * @return true if handshake is initiated and consequent data will be encrypted. */ boolean isEncrypted(); } naga-3.0+svn80/src/main/naga/NIOUtils.java000066400000000000000000000226461300251770300201760ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.SelectionKey; /** * A collection of utilites used by various classes. * * @author Christoffer Lerno */ public class NIOUtils { NIOUtils() {} /** * Silently close both a key and a channel. * * @param key the key to cancel, may be null. * @param channel the channel to close, may be null. */ public static void closeKeyAndChannelSilently(SelectionKey key, Channel channel) { closeChannelSilently(channel); cancelKeySilently(key); } /** * Encodes a length into byte buffer using * either big or little endian encoding (i.e. biggest or smallest byte first). * * @param byteBuffer the ByteBuffer to use. * @param headerSize the header size in bytes. 1-4. * @param valueToEncode the value to encode, 0 <= value < 2^(headerSize * 8) * @param bigEndian if the encoding is big endian or not. * @throws IllegalArgumentException if the value is out of range for the given header size. */ public static void setPacketSizeInByteBuffer(ByteBuffer byteBuffer, int headerSize, int valueToEncode, boolean bigEndian) { if (valueToEncode < 0) throw new IllegalArgumentException("Payload size is less than 0."); // If header size is 4, we get valueToEncode >> 32, which is defined as valueToEncode >> 0 for int. // Therefore, we handle the that case separately, as any int will fit in 4 bytes. if (headerSize != 4 && valueToEncode >> (headerSize * 8) > 0) { throw new IllegalArgumentException("Payload size cannot be encoded into " + headerSize + " byte(s)."); } for (int i = 0; i < headerSize; i++) { int index = bigEndian ? (headerSize - 1 - i) : i; // We do not need to extend valueToEncode here, since the maximum is valueToEncode >> 24 byteBuffer.put((byte) (valueToEncode >> (8 * index) & 0xFF)); } } /** * Inserts a header in the first bytes of a byte array * in either big or little endian encoding (i.e. biggest or smallest byte first). * * @param buffer the byte array to set the header for * @param headerSize the header size in bytes. 1-4. * @param valueToEncode the value to encode, 0 <= value < 2^(headerSize * 8) * @param bigEndian if the encoding is big endian or not. * @throws IllegalArgumentException if the value is out of range for the given header size. */ public static void setHeaderForPacketSize(byte[] buffer, int headerSize, int valueToEncode, boolean bigEndian) { if (valueToEncode < 0) throw new IllegalArgumentException("Payload size is less than 0."); // If header size is 4, we get valueToEncode >> 32, which is defined as valueToEncode >> 0 for int. // Therefore, we handle the that case separately, as any int will fit in 4 bytes. if (headerSize != 4 && valueToEncode >> (headerSize * 8) > 0) { throw new IllegalArgumentException("Payload size cannot be encoded into " + headerSize + " byte(s)."); } for (int i = 0; i < headerSize; i++) { int index = bigEndian ? (headerSize - 1 - i) : i; // We do not need to extend valueToEncode here, since the maximum is valueToEncode >> 24 buffer[i] = ((byte) (valueToEncode >> (8 * index) & 0xFF)); } } /** * Converts a value in a header buffer encoded in either big or little endian * encoding. *

* Note that trying to decode a value larger than 2^31 - 2 is not supported. * * @param header the header to encode from. * @param size the header size, 1-4. * @param bigEndian if the encoding is big endian or not. * @return the decoded number. */ public static int getPacketSizeFromByteBuffer(ByteBuffer header, int size, boolean bigEndian) { long packetSize = 0; if (bigEndian) { for (int i = 0; i < size; i++) { packetSize <<= 8; packetSize += header.get() & 0xFF; } } else { int shift = 0; for (int i = 0; i < size; i++) { // We do not need to extend valueToEncode here, since the maximum is valueToEncode >> 24 packetSize += (header.get() & 0xFF) << shift; shift += 8; } } return (int) packetSize; } /** * Converts a value in a header byte array encoded in either big or little endian * encoding. *

* Note that trying to decode a value larger than 2^31 - 2 is not supported. * * @param data the data to encode from. * @param length the length of the header. * @param bigEndian if the encoding is big endian or not. * @return the decoded number. */ public static int getPacketSizeFromByteArray(byte[] data, int length, boolean bigEndian) { long packetSize = 0; if (bigEndian) { for (int i = 0; i < length; i++) { packetSize <<= 8; packetSize += data[i] & 0xFF; } } else { int shift = 0; for (int i = 0; i < length; i++) { // We do not need to extend valueToEncode here, since the maximum is valueToEncode >> 24 packetSize += (data[i] & 0xFF) << shift; shift += 8; } } return (int) packetSize; } /** * Silently close a channel. * * @param channel the channel to close, may be null. */ public static void closeChannelSilently(Channel channel) { try { if (channel != null) { channel.close(); } } catch (IOException e) { // Do nothing } } /** * Silently cancel a key. * * @param key the key to cancel, may be null. */ public static void cancelKeySilently(SelectionKey key) { try { if (key != null) key.cancel(); } catch (Exception e) { // Do nothing } } /** * Compacts an array of byte buffers, retaining only the buffers that have remaining data. * * @param buffers the buffers to compact. * @return a compacted ByteBuffer array. */ public static ByteBuffer[] compact(ByteBuffer[] buffers) { for (int i = 0; i < buffers.length; i++) { if (buffers[i].remaining() > 0) { if (i == 0) return buffers; ByteBuffer[] newBuffers = new ByteBuffer[buffers.length - i]; System.arraycopy(buffers, i, newBuffers, 0, buffers.length - i); return newBuffers; } } return null; } public static ByteBuffer[] concat(ByteBuffer[] buffers, ByteBuffer buffer) { return concat(buffers, new ByteBuffer[] { buffer }); } public static ByteBuffer[] concat(ByteBuffer buffer, ByteBuffer[] buffers2) { return concat(new ByteBuffer[] { buffer }, buffers2); } public static ByteBuffer[] concat(ByteBuffer[] buffers1, ByteBuffer[] buffers2) { if (buffers1 == null || buffers1.length == 0) return buffers2; if (buffers2 == null || buffers2.length == 0) return buffers1; ByteBuffer[] newBuffers = new ByteBuffer[buffers1.length + buffers2.length]; System.arraycopy(buffers1, 0, newBuffers, 0, buffers1.length); System.arraycopy(buffers2, 0, newBuffers, buffers1.length, buffers2.length); return newBuffers; } public static ByteBuffer copy(ByteBuffer buffer) { if (buffer == null) return null; ByteBuffer copy = ByteBuffer.allocate(buffer.remaining()); copy.put(buffer); copy.flip(); return copy; } public static long remaining(ByteBuffer[] byteBuffers) { long length = 0; for (ByteBuffer buffer : byteBuffers) length += buffer.remaining(); return length; } public static boolean isEmpty(ByteBuffer[] byteBuffers) { for (ByteBuffer buffer : byteBuffers) { if (buffer.remaining() > 0) return false; } return true; } public static ByteBuffer join(ByteBuffer buffer1, ByteBuffer buffer2) { if (buffer2 == null || buffer2.remaining() == 0) return NIOUtils.copy(buffer1); if (buffer1 == null || buffer1.remaining() == 0) return NIOUtils.copy(buffer2); ByteBuffer buffer = ByteBuffer.allocate(buffer1.remaining() + buffer2.remaining()); buffer.put(buffer1); buffer.put(buffer2); buffer.flip(); return buffer; } }naga-3.0+svn80/src/main/naga/PacketReader.java000066400000000000000000000040301300251770300210450ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import naga.exception.ProtocolViolationException; import java.nio.ByteBuffer; /** * Interface for packet reader plugins to assist a socket in reading. *

* PacketReaders are in general intended to help splitting * * @author Christoffer Lerno */ public interface PacketReader { // Send SKIP_PACKET to cause the returning byte to be discarded, while not stopping the read loop. public static byte[] SKIP_PACKET = new byte[0]; /** * Create a new packet using the ByteBuffer given. *

* If there isn't sufficient data to construct a packet, return null. * * @param byteBuffer the byte buffer to use. * @return the new packet created, or null if no packet could be created. The method will continously * be called until nextPacket returns null. * @throws ProtocolViolationException is there was an error constructing the packet. */ byte[] nextPacket(ByteBuffer byteBuffer) throws ProtocolViolationException; } naga-3.0+svn80/src/main/naga/PacketWriter.java000066400000000000000000000032001300251770300211150ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.nio.ByteBuffer; /** * Interface for classes implementing packet writing strategies. *

* The method {@link naga.PacketWriter#write(ByteBuffer[])} converts an incoming byte array * to an outgoing byte array. * * @author Christoffer Lerno */ public interface PacketWriter { /** * Convert the incoming bytes to the bytes to be serialized. * * @param byteBuffer an array of ByteBuffers containing data the bytes to be written. * @return the resulting array of ByteBuffers. */ ByteBuffer[] write(ByteBuffer[] byteBuffer); } naga-3.0+svn80/src/main/naga/SSLPacketHandler.java000066400000000000000000000236431300251770300216150ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import naga.exception.ProtocolViolationException; import naga.packetreader.RawPacketReader; import naga.packetwriter.RawPacketWriter; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import java.io.EOFException; import java.nio.ByteBuffer; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Undocumented Class * * @author Christoffer Lerno */ public class SSLPacketHandler implements PacketReader, PacketWriter { private final static Executor TASK_HANDLER = Executors.newSingleThreadExecutor(); private final static ThreadLocal SSL_BUFFER = new ThreadLocal() { @Override protected ByteBuffer initialValue() { // Should be plenty. return ByteBuffer.allocate(64 * 1024); } }; private final SSLEngine m_engine; private PacketReader m_reader; private PacketWriter m_writer; private ByteBuffer m_partialIncomingBuffer; private ByteBuffer[] m_initialOutBuffer; private final NIOSocket m_socket; private final SSLSocketChannelResponder m_responder; private boolean m_sslInitiated; public SSLPacketHandler(SSLEngine engine, NIOSocket socket, SSLSocketChannelResponder responder) { m_engine = engine; m_socket = socket; m_partialIncomingBuffer = null; m_writer = RawPacketWriter.INSTANCE; m_reader = RawPacketReader.INSTANCE; m_responder = responder; m_sslInitiated = false; } public PacketReader getReader() { return m_reader; } public void setReader(PacketReader reader) { m_reader = reader; } public PacketWriter getWriter() { return m_writer; } public void setWriter(PacketWriter writer) { m_writer = writer; } private void queueSSLTasks() { if (!m_sslInitiated) return; int tasksScheduled = 0; Runnable task; while ((task = m_engine.getDelegatedTask()) != null) { TASK_HANDLER.execute(task); tasksScheduled++; } if (tasksScheduled == 0) { return; } TASK_HANDLER.execute(new Runnable() { public void run() { m_socket.queue(new Runnable() { public void run() { reactToHandshakeStatus(m_engine.getHandshakeStatus()); } }); } }); } public byte[] nextPacket(ByteBuffer byteBuffer) throws ProtocolViolationException { if (!m_sslInitiated) { return m_reader.nextPacket(byteBuffer); } try { // Retrieve the local buffer. ByteBuffer targetBuffer = SSL_BUFFER.get(); targetBuffer.clear(); // Unwrap the data (both buffers should be sufficiently large) SSLEngineResult result = m_engine.unwrap(byteBuffer, targetBuffer); switch (result.getStatus()) { case BUFFER_UNDERFLOW: // Right, let's wait for more data. return null; case BUFFER_OVERFLOW: // This should never happen as we are ensuring the buffer is large enough. throw new ProtocolViolationException("SSL Buffer Overflow"); case CLOSED: m_responder.connectionBroken(m_socket, new EOFException("SSL Connection closed")); return null; case OK: // Do nothing, just follow the flow. } // We might need to queue tasks or send data as a response to this packet. reactToHandshakeStatus(result.getHandshakeStatus()); return retrieveDecryptedPacket(targetBuffer); } catch (SSLException e) { m_responder.closeDueToSSLException(e); return null; } } private void reactToHandshakeStatus(SSLEngineResult.HandshakeStatus status) { if (!m_sslInitiated) return; switch (status) { case NOT_HANDSHAKING: case NEED_UNWRAP: break; case NEED_TASK: queueSSLTasks(); break; case FINISHED: m_socket.write(new byte[0]); break; case NEED_WRAP: m_socket.write(new byte[0]); break; } } private byte[] retrieveDecryptedPacket(ByteBuffer targetBuffer) throws ProtocolViolationException { // Prepare the buffer for reading. targetBuffer.flip(); // Join the buffer with the partial buffer, this is because we need to internally buffer data that has been decrypted but does not yet form a complete packet. m_partialIncomingBuffer = NIOUtils.join(m_partialIncomingBuffer, targetBuffer); // Skip if the data is empty. This will be the case during handshaking. if (m_partialIncomingBuffer == null || m_partialIncomingBuffer.remaining() == 0) return SKIP_PACKET; // Delegate packet creation to the reader. return m_reader.nextPacket(m_partialIncomingBuffer); } public ByteBuffer[] write(ByteBuffer[] byteBuffers) { if (!m_sslInitiated) { return m_writer.write(byteBuffers); } // Check if we are done handshaking. if (m_engine.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { if (!NIOUtils.isEmpty(byteBuffers)) { // If this is regular data, store this in the initial outbuffer. m_initialOutBuffer = NIOUtils.concat(m_initialOutBuffer, m_writer.write(byteBuffers)); byteBuffers = new ByteBuffer[0]; } // Borrow the shared buffer. ByteBuffer buffer = SSL_BUFFER.get(); ByteBuffer[] buffers = null; try { // Create handshake data. SSLEngineResult result = null; while (m_engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) { buffer.clear(); result = m_engine.wrap(byteBuffers, buffer); buffer.flip(); buffers = NIOUtils.concat(buffers, NIOUtils.copy(buffer)); } // If we for some reason entered here but did not need to wrap anything, exit. if (result == null) return null; if (result.getStatus() != SSLEngineResult.Status.OK) throw new SSLException("Unexpectedly not ok wrapping handshake data, was " + result.getStatus()); reactToHandshakeStatus(result.getHandshakeStatus()); } catch (SSLException e) { // Better error handling required! throw new RuntimeException(e); } return buffers; } // We are not handshaking, so encrypt the data using wrap // Use the shared buffer. ByteBuffer buffer = SSL_BUFFER.get(); buffer.clear(); if (NIOUtils.isEmpty(byteBuffers)) { // Exit early if we have no data to encrypt. if (m_initialOutBuffer == null) return null; } else { // Only convert non-empty buffers byteBuffers = m_writer.write(byteBuffers); } // If we have an initial buffer, send it. if (m_initialOutBuffer != null) { byteBuffers = NIOUtils.concat(m_initialOutBuffer, byteBuffers); m_initialOutBuffer = null; } ByteBuffer[] encrypted = null; // While we have things left to encrypt. while (!NIOUtils.isEmpty(byteBuffers)) { // Clear our huge buffer. buffer.clear(); try { m_engine.wrap(byteBuffers, buffer); } catch (SSLException e) { throw new RuntimeException(e); } buffer.flip(); // Copy the result. encrypted = NIOUtils.concat(encrypted, NIOUtils.copy(buffer)); } // Return our encrypted data. return encrypted; } public SSLEngine getSSLEngine() { return m_engine; } void begin() throws SSLException { m_engine.beginHandshake(); m_sslInitiated = true; reactToHandshakeStatus(m_engine.getHandshakeStatus()); } public void closeEngine() { if (!m_sslInitiated) return; m_engine.closeOutbound(); m_responder.write(new byte[0]); } public boolean isEncrypted() { return m_sslInitiated; } } naga-3.0+svn80/src/main/naga/SSLServerSocketChannelResponder.java000066400000000000000000000042061300251770300246740ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import javax.net.ssl.SSLContext; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; /** * This is a ServerSocketChannel subclass that starts SSL communication on all its sockets. * * @author Christoffer Lerno */ class SSLServerSocketChannelResponder extends ServerSocketChannelResponder implements NIOServerSocketSSL { private final SSLContext m_sslContext; public SSLServerSocketChannelResponder(SSLContext context, NIOService service, ServerSocketChannel channel, InetSocketAddress address) throws IOException { super(service, channel, address); m_sslContext = context; } public SSLContext getSSLContext() { return m_sslContext; } @Override NIOSocket registerSocket(SocketChannel channel, InetSocketAddress address) throws IOException { NIOSocket socket = super.registerSocket(channel, address); return new SSLSocketChannelResponder(getNIOService(), socket, m_sslContext.createSSLEngine(), false); } } naga-3.0+svn80/src/main/naga/SSLSocketChannelResponder.java000066400000000000000000000140311300251770300235020ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import java.net.InetSocketAddress; import java.net.Socket; /** * SSL-implementation on top of NIOSocket, wrapping all calls to the socket. * * @author Christoffer Lerno */ class SSLSocketChannelResponder implements NIOSocketSSL, SocketObserver { private final NIOSocket m_wrappedSocket; private final SSLPacketHandler m_packetHandler; private final NIOService m_nioService; private SocketObserver m_observer; public SSLSocketChannelResponder(NIOService nioService, NIOSocket wrappedSocket, SSLEngine engine, boolean client) throws SSLException { m_nioService = nioService; m_wrappedSocket = wrappedSocket; m_packetHandler = new SSLPacketHandler(engine, m_wrappedSocket, this); m_wrappedSocket.setPacketReader(m_packetHandler); m_wrappedSocket.setPacketWriter(m_packetHandler); engine.setUseClientMode(client); } public void beginHandshake() throws SSLException { if (getSSLEngine().getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) throw new IllegalStateException("Tried to start handshake during handshake."); m_packetHandler.begin(); } public boolean isEncrypted() { return m_packetHandler.isEncrypted(); } public SSLEngine getSSLEngine() { return m_packetHandler.getSSLEngine(); } public boolean write(byte[] packet) { return m_wrappedSocket.write(packet); } public boolean write(byte[] packet, Object tag) { return m_wrappedSocket.write(packet, tag); } public void queue(Runnable runnable) { m_wrappedSocket.queue(runnable); } public long getBytesRead() { return m_wrappedSocket.getBytesRead(); } public long getBytesWritten() { return m_wrappedSocket.getBytesWritten(); } public long getTimeOpen() { return m_wrappedSocket.getTimeOpen(); } public long getWriteQueueSize() { return m_wrappedSocket.getWriteQueueSize(); } public int getMaxQueueSize() { return m_wrappedSocket.getMaxQueueSize(); } public void setMaxQueueSize(int maxQueueSize) { m_wrappedSocket.setMaxQueueSize(maxQueueSize); } public void setPacketReader(PacketReader packetReader) { m_packetHandler.setReader(packetReader); } public void setPacketWriter(final PacketWriter packetWriter) { m_wrappedSocket.queue(new Runnable() { public void run() { m_packetHandler.setWriter(packetWriter); } }); } public void listen(SocketObserver socketObserver) { m_observer = socketObserver; m_wrappedSocket.listen(this); } public void closeAfterWrite() { m_packetHandler.closeEngine(); m_wrappedSocket.closeAfterWrite(); } public Socket socket() { return m_wrappedSocket.socket(); } public void close() { m_wrappedSocket.close(); } public InetSocketAddress getAddress() { return m_wrappedSocket.getAddress(); } public boolean isOpen() { return m_wrappedSocket.isOpen(); } public String getIp() { return m_wrappedSocket.getIp(); } public int getPort() { return m_wrappedSocket.getPort(); } public Object getTag() { return m_wrappedSocket.getTag(); } public void setTag(Object tag) { m_wrappedSocket.setTag(tag); } void closeDueToSSLException(SSLException e) { try { if (m_observer != null) m_observer.connectionBroken(this, e); } catch (Exception ex) { m_nioService.notifyException(e); } m_wrappedSocket.close(); } public void connectionOpened(NIOSocket nioSocket) { try { if (m_observer != null) m_observer.connectionOpened(this); } catch (Exception e) { m_nioService.notifyException(e); } } public void connectionBroken(NIOSocket nioSocket, Exception exception) { try { if (m_observer != null) m_observer.connectionBroken(this, exception); } catch (Exception e) { m_nioService.notifyException(e); } } public void packetReceived(NIOSocket socket, byte[] packet) { try { if (m_observer != null) m_observer.packetReceived(this, packet); } catch (Exception e) { m_nioService.notifyException(e); } } public void packetSent(NIOSocket socket, Object tag) { try { if (m_observer != null) m_observer.packetSent(this, tag); } catch (Exception e) { m_nioService.notifyException(e); } } } naga-3.0+svn80/src/main/naga/ServerSocketChannelResponder.java000066400000000000000000000140211300251770300243060ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; /** * @author Christoffer Lerno */ class ServerSocketChannelResponder extends ChannelResponder implements NIOServerSocket { private long m_totalRefusedConnections; private long m_totalAcceptedConnections; private long m_totalFailedConnections; private long m_totalConnections; private volatile ConnectionAcceptor m_connectionAcceptor; private ServerSocketObserver m_observer; @SuppressWarnings({"ObjectToString"}) public ServerSocketChannelResponder(NIOService service, ServerSocketChannel channel, InetSocketAddress address) throws IOException { super(service, channel, address); m_observer = null; setConnectionAcceptor(ConnectionAcceptor.ALLOW); m_totalRefusedConnections = 0; m_totalAcceptedConnections = 0; m_totalFailedConnections = 0; m_totalConnections = 0; } public void keyInitialized() { addInterest(SelectionKey.OP_ACCEPT); } public ServerSocketChannel getChannel() { return (ServerSocketChannel) super.getChannel(); } /** * Override point for substituting NIOSocket wrappers. * * @param channel the channel to register. * @param address the address associated with the channel. * @return A new NIOSocket * @throws IOException if registration failed. */ NIOSocket registerSocket(SocketChannel channel, InetSocketAddress address) throws IOException { return getNIOService().registerSocketChannel(channel, address); } private void notifyNewConnection(NIOSocket socket) { try { if (m_observer != null) m_observer.newConnection(socket); } catch (Exception e) { getNIOService().notifyException(e); socket.close(); } } private void notifyAcceptFailed(IOException theException) { try { if (m_observer != null) m_observer.acceptFailed(theException); } catch (Exception e) { getNIOService().notifyException(e); } } /** * Callback to tell the object that there is at least one accept that can be done on the server socket. */ public void socketReadyForAccept() { m_totalConnections++; SocketChannel socketChannel = null; try { socketChannel = getChannel().accept(); if (socketChannel == null) { // This means there actually wasn't any connection waiting, // so tick down the number of actual total connections. m_totalConnections--; return; } InetSocketAddress address = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress(); // Is this connection acceptable? if (!m_connectionAcceptor.acceptConnection(address)) { // Connection was refused by the socket owner, so update stats and close connection m_totalRefusedConnections++; NIOUtils.closeChannelSilently(socketChannel); return; } notifyNewConnection(registerSocket(socketChannel, address)); m_totalAcceptedConnections++; } catch (IOException e) { // Close channel in case it opened. NIOUtils.closeChannelSilently(socketChannel); m_totalFailedConnections++; notifyAcceptFailed(e); } } public void notifyWasCancelled() { close(); } public long getTotalRefusedConnections() { return m_totalRefusedConnections; } public long getTotalConnections() { return m_totalConnections; } public long getTotalFailedConnections() { return m_totalFailedConnections; } public long getTotalAcceptedConnections() { return m_totalAcceptedConnections; } public void setConnectionAcceptor(ConnectionAcceptor connectionAcceptor) { m_connectionAcceptor = connectionAcceptor == null ? ConnectionAcceptor.DENY : connectionAcceptor; } private void notifyObserverSocketDied(Exception exception) { try { if (m_observer != null) m_observer.serverSocketDied(exception); } catch (Exception e) { getNIOService().notifyException(e); } } public void listen(ServerSocketObserver observer) { if (observer == null) throw new NullPointerException(); markObserverSet(); getNIOService().queue(new BeginListenEvent(observer)); } private class BeginListenEvent implements Runnable { private final ServerSocketObserver m_newObserver; private BeginListenEvent(ServerSocketObserver socketObserver) { m_newObserver = socketObserver; } public void run() { m_observer = m_newObserver; if (!isOpen()) { notifyObserverSocketDied(null); return; } addInterest(SelectionKey.OP_ACCEPT); } @Override public String toString() { return "BeginListen[" + m_newObserver + "]"; } } protected void shutdown(Exception e) { notifyObserverSocketDied(e); } public ServerSocket socket() { return getChannel().socket(); } } naga-3.0+svn80/src/main/naga/ServerSocketObserver.java000066400000000000000000000065111300251770300226500ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.io.IOException; /** * Implemented by an observer to a server socket. *

* A server socket observer is responsible for handling * incoming connections by implementing a callback * to properly assign a SocketObserver to the new connections. *

* All callbacks will be run on the NIOService-thread, * so callbacks should try to return as quickly as possible * since the callback blocks communication on all sockets * of the service. * * @author Christoffer Lerno */ public interface ServerSocketObserver { /** * Called by the NIOService on the NIO thread when an accept fails on the socket. *

* Note: Since this is a direct callback on the NIO thread, this method will suspend IO on * all other connections until the method returns. It is therefore strongly recommended * that the implementation of this method returns as quickly as possible to avoid blocking IO. * * @param exception the reason for the failure, never null. */ void acceptFailed(IOException exception); /** * Called by the NIOService on the NIO thread when the server socket is closed. *

* Note: Since this is a direct callback on the NIO thread, this method will suspend IO on * all other connections until the method returns. It is therefore strongly recommended * that the implementation of this method returns as quickly as possible to avoid blocking IO. * * @param exception the exception that caused the close, or null if this was * caused by an explicit close() on the NIOServerSocket. */ void serverSocketDied(Exception exception); /** * Called by the NIOService on the NIO thread when a new connection has been accepted by the socket. *

* The normal behaviour would be for the observer to assign a reader and a writer to the socket, * and then finally invoke NIOSocket#listen(SocketObserver) on the socket. *

* Note: Since this is a direct callback on the NIO thread, this method will suspend IO on * all other connections until the method returns. It is therefore strongly recommended * that the implementation of this method returns as quickly as possible to avoid blocking IO. * * @param nioSocket the socket that was accepted. */ void newConnection(NIOSocket nioSocket); } naga-3.0+svn80/src/main/naga/ServerSocketObserverAdapter.java000066400000000000000000000026521300251770300241530ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.io.IOException; /** * Class with null-implementations for all callbacks. * * @author Christoffer Lerno */ public class ServerSocketObserverAdapter implements ServerSocketObserver { public void acceptFailed(IOException exception) { } public void serverSocketDied(Exception e) { } public void newConnection(NIOSocket nioSocket) { } } naga-3.0+svn80/src/main/naga/SocketChannelResponder.java000066400000000000000000000252201300251770300231220ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import naga.packetreader.RawPacketReader; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicLong; /** * @author Christoffer Lerno */ class SocketChannelResponder extends ChannelResponder implements NIOSocket { private int m_maxQueueSize; private long m_timeOpened; private final AtomicLong m_bytesInQueue; private ConcurrentLinkedQueue m_packetQueue; private PacketReader m_packetReader; private volatile SocketObserver m_socketObserver; private final SocketReader m_socketReader; private final SocketWriter m_socketWriter; public SocketChannelResponder(NIOService service, SocketChannel socketChannel, InetSocketAddress address) { super(service, socketChannel, address); m_socketObserver = null; m_maxQueueSize = -1; m_timeOpened = -1; m_packetReader = RawPacketReader.INSTANCE; m_bytesInQueue = new AtomicLong(0L); m_packetQueue = new ConcurrentLinkedQueue(); m_socketReader = new SocketReader(service); m_socketWriter = new SocketWriter(); } void keyInitialized() { if (!isConnected()) { addInterest(SelectionKey.OP_CONNECT); } } public void closeAfterWrite() { queue(new Runnable() { public void run() { m_packetQueue.clear(); close(null); } }); } public void queue(Runnable runnable) { m_packetQueue.offer(runnable); getNIOService().queue(new AddInterestEvent(SelectionKey.OP_WRITE)); } public boolean write(byte[] packet, Object tag) { long currentQueueSize = m_bytesInQueue.addAndGet(packet.length); if (m_maxQueueSize > 0 && currentQueueSize > m_maxQueueSize) { m_bytesInQueue.addAndGet(-packet.length); return false; } // Add the packet. m_packetQueue.offer(tag == null ? packet : new Object[] { packet, tag }); getNIOService().queue(new AddInterestEvent(SelectionKey.OP_WRITE)); return true; } public boolean write(byte[] packet) { return write(packet, null); } public boolean isConnected() { return getChannel().isConnected(); } /** * Notify the observer that the packet is received, will log to the exception observer on NIOService if an error occurs. * * @param packet the packet received. */ private void notifyPacketReceived(byte[] packet) { try { if (m_socketObserver != null) m_socketObserver.packetReceived(this, packet); } catch (Exception e) { getNIOService().notifyException(e); } } /** * Notify the observer that the packet was sent. Will log to the exception observer on NIOService if an error occurs. * * @param tag the optional tag associated with the packet. */ private void notifyPacketSent(Object tag) { try { if (m_socketObserver != null) m_socketObserver.packetSent(this, tag); } catch (Exception e) { getNIOService().notifyException(e); } } public void socketReadyForRead() { if (!isOpen()) return; try { if (!isConnected()) throw new IOException("Channel not connected."); while (m_socketReader.read(getChannel()) > 0) { byte[] packet; ByteBuffer buffer = m_socketReader.getBuffer(); while (buffer.remaining() > 0 && (packet = m_packetReader.nextPacket(buffer)) != null) { if (packet == PacketReader.SKIP_PACKET) continue; notifyPacketReceived(packet); } m_socketReader.compact(); } } catch (Exception e) { close(e); } } private void fillCurrentOutgoingBuffer() throws IOException { if (m_socketWriter.isEmpty()) { // Retrieve next packet from the queue. Object nextPacket = m_packetQueue.poll(); while (nextPacket != null && nextPacket instanceof Runnable) { ((Runnable) nextPacket).run(); nextPacket = m_packetQueue.poll(); } if (nextPacket == null) return; byte[] data; Object tag = null; if (nextPacket instanceof byte[]) { data = (byte[]) nextPacket; } else { data = (byte[])((Object[])nextPacket)[0]; tag = ((Object[])nextPacket)[1]; } m_socketWriter.setPacket(data, tag); // Remove the space reserved in the queue. m_bytesInQueue.addAndGet(-data.length); } } public void socketReadyForWrite() { try { deleteInterest(SelectionKey.OP_WRITE); if (!isOpen()) return; fillCurrentOutgoingBuffer(); // Return if there is nothing in the buffer to send. if (m_socketWriter.isEmpty()) { return; } while (!m_socketWriter.isEmpty()) { boolean bytesWereWritten = m_socketWriter.write(getChannel()); if (!bytesWereWritten) { // Change the interest ops in case we still have things to write. addInterest(SelectionKey.OP_WRITE); return; } if (m_socketWriter.isEmpty()) { notifyPacketSent(m_socketWriter.getTag()); fillCurrentOutgoingBuffer(); } } } catch (Exception e) { close(e); } } public void socketReadyForConnect() { try { if (!isOpen()) return; if (getChannel().finishConnect()) { deleteInterest(SelectionKey.OP_CONNECT); m_timeOpened = System.currentTimeMillis(); notifyObserverOfConnect(); } } catch (Exception e) { close(e); } } public void notifyWasCancelled() { close(); } public Socket getSocket() { return getChannel().socket(); } public long getBytesRead() { return m_socketReader.getBytesRead(); } public long getBytesWritten() { return m_socketWriter.getBytesWritten(); } public long getTimeOpen() { return m_timeOpened > 0 ? System.currentTimeMillis() - m_timeOpened : -1; } public long getWriteQueueSize() { return m_bytesInQueue.get(); } public String toString() { try { return getSocket().toString(); } catch (Exception e) { return "Closed NIO Socket"; } } /** * @return the current maximum queue size. */ public int getMaxQueueSize() { return m_maxQueueSize; } /** * Sets the maximum number of bytes allowed in the queue for this socket. If this * number is less than 1, the queue is unbounded. * * @param maxQueueSize the new max queue size. A value less than 1 is an unbounded queue. */ public void setMaxQueueSize(int maxQueueSize) { m_maxQueueSize = maxQueueSize; } public void listen(SocketObserver socketObserver) { markObserverSet(); getNIOService().queue(new BeginListenEvent(this, socketObserver == null ? SocketObserver.NULL : socketObserver)); } /** * Notify the observer that the socket connected. Will log to the exception observer on NIOService if an error occurs. * */ private void notifyObserverOfConnect() { try { if (m_socketObserver != null) m_socketObserver.connectionOpened(this); } catch (Exception e) { getNIOService().notifyException(e); } } /** * Notify the observer of the disconnect. Will log to the exception observer on NIOService if an error occurs. * * @param exception the exception causing the disconnect, or null if this was a clean close. */ private void notifyObserverOfDisconnect(Exception exception) { try { if (m_socketObserver != null) m_socketObserver.connectionBroken(this, exception); } catch (Exception e) { getNIOService().notifyException(e); } } public void setPacketReader(PacketReader packetReader) { m_packetReader = packetReader; } public void setPacketWriter(final PacketWriter packetWriter) { if (packetWriter == null) throw new NullPointerException(); queue(new Runnable() { public void run() { m_socketWriter.setPacketWriter(packetWriter); } }); } public SocketChannel getChannel() { return (SocketChannel) super.getChannel(); } protected void shutdown(Exception e) { m_timeOpened = -1; m_packetQueue.clear(); m_bytesInQueue.set(0); notifyObserverOfDisconnect(e); } private class AddInterestEvent implements Runnable { private final int m_interest; private AddInterestEvent(int interest) { m_interest = interest; } public void run() { addInterest(m_interest); } } private class BeginListenEvent implements Runnable { private final SocketObserver m_newObserver; private final SocketChannelResponder m_responder; private BeginListenEvent(SocketChannelResponder responder, SocketObserver socketObserver) { m_responder = responder; m_newObserver = socketObserver; } public void run() { m_responder.m_socketObserver = m_newObserver; if (m_responder.isConnected()) { m_responder.notifyObserverOfConnect(); } if (!m_responder.isOpen()) { m_responder.notifyObserverOfDisconnect(null); } m_responder.addInterest(SelectionKey.OP_READ); } @Override public String toString() { return "BeginListen[" + m_newObserver + "]"; } } public Socket socket() { return getChannel().socket(); } } naga-3.0+svn80/src/main/naga/SocketObserver.java000066400000000000000000000076451300251770300214720ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; /** * This interface contains the callbacks used by a NIOSocket * to inform its observer of events. *

* All callbacks will be run on the NIOService-thread, * so callbacks should try to return as quickly as possible * since the callback blocks communication on all sockets * of the service. * * @author Christoffer Lerno */ public interface SocketObserver { /** A null object used as the default observer */ SocketObserver NULL = new SocketObserverAdapter(); /** * Called by the NIOService on the NIO thread when a connection completes on a socket. *

* Note: Since this is a direct callback on the NIO thread, this method will suspend IO on * all other connections until the method returns. It is therefore strongly recommended * that the implementation of this method returns as quickly as possible to avoid blocking IO. * * @param nioSocket the socket that completed its connect. */ void connectionOpened(NIOSocket nioSocket); /** * Called by the NIOService on the NIO thread when a connection is disconnected. *

* This may be sent even if a connectionOpened(NIOSocket) * wasn't ever called, since the connect itself may * fail. *

* Note: Since this is a direct callback on the NIO thread, this method will suspend IO on * all other connections until the method returns. It is therefore strongly recommended * that the implementation of this method returns as quickly as possible to avoid blocking IO. * * @param nioSocket the socket that was disconnected. * @param exception the exception that caused the connection to break, may be null. */ void connectionBroken(NIOSocket nioSocket, Exception exception); /** * Called by the NIOService on the NIO thread when a packet is finished reading. * The byte array contains the packet as parsed by the current PacketReader. *

* Note: Since this is a direct callback on the NIO thread, this method will suspend IO on * all other connections until the method returns. It is therefore strongly recommended * that the implementation of this method returns as quickly as possible to avoid blocking IO. * * @param socket the socket we received a packet on. * @param packet the packet we received. */ void packetReceived(NIOSocket socket, byte[] packet); /** * Called by the NIOService on the NIO thread when a packet has finished writing. *

* Note: Since this is a direct callback on the NIO thread, this method will suspend IO on * all other connections until the method returns. It is therefore strongly recommended * that the implementation of this method returns as quickly as possible to avoid blocking IO. * * @param socket the socket we sent the packet on. * @param tag the (optional) tag associated with the packet. */ void packetSent(NIOSocket socket, Object tag); } naga-3.0+svn80/src/main/naga/SocketObserverAdapter.java000066400000000000000000000030011300251770300227510ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; /** * Class with null-implementation of all SocketObserver callbacks. * * @author Christoffer Lerno */ public class SocketObserverAdapter implements SocketObserver { public void connectionBroken(NIOSocket nioSocket, Exception exception) { } public void packetReceived(NIOSocket socket, byte[] packet) { } public void connectionOpened(NIOSocket nioSocket) { } public void packetSent(NIOSocket socket, Object tag) { } } naga-3.0+svn80/src/main/naga/SocketReader.java000066400000000000000000000075701300251770300211020ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import java.io.EOFException; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** * A Socket reader handles read/writes on a socket. * * @author Christoffer Lerno */ class SocketReader { private final NIOService m_nioService; private ByteBuffer m_previousBytes; private long m_bytesRead; SocketReader(NIOService nioService) { m_nioService = nioService; m_bytesRead = 0; } public int read(SocketChannel channel) throws IOException { // Retrieve the shared buffer. ByteBuffer buffer = getBuffer(); // Clear the buffer. buffer.clear(); // Create an offset if there are unconsumed bytes. if (m_previousBytes != null) { buffer.position(m_previousBytes.remaining()); } // Read data int read = channel.read(buffer); // We might encounter the end of the socket stream here. if (read < 0) throw new EOFException("Buffer read -1"); // If we have no space left in the buffer, we need to throw an exception. if (!buffer.hasRemaining()) throw new BufferOverflowException(); // Increase the bytes read. m_bytesRead += read; // If nothing was read, simply return 0. if (read == 0) return 0; // If we read data, we need to insert the previous bytes. // We could avoid this at the cost of making the "read" method more complex in PacketReader. if (m_previousBytes != null) { // Remember the old position. int position = buffer.position(); // Shift to position 0 buffer.position(0); // Add the bytes. buffer.put(m_previousBytes); // Restore the position. buffer.position(position); // Clear the bytes we kept. m_previousBytes = null; } // Flip the buffer to prepare for reading. buffer.flip(); return read; } /** * Moves any unread bytes to a buffer to be available later. */ public void compact() { // Retrieve our shared buffer. ByteBuffer buffer = getBuffer(); // If there is data remaining, copy that data. if (buffer.remaining() > 0) { m_previousBytes = NIOUtils.copy(buffer); } } /** * Return the number of raw bytes read. * * @return the number of bytes read. */ public long getBytesRead() { return m_bytesRead; } /** * Returns the shared buffer (associated with the NIOService) for read/write. * * @return the shared buffer- */ public ByteBuffer getBuffer() { return m_nioService.getSharedBuffer(); } } naga-3.0+svn80/src/main/naga/SocketWriter.java000066400000000000000000000072001300251770300211420ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga; import naga.packetwriter.RawPacketWriter; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** * A helper class to handle writes on a socket. * * @author Christoffer Lerno */ class SocketWriter { private long m_bytesWritten; private ByteBuffer[] m_writeBuffers; private PacketWriter m_packetWriter; private Object m_tag; private int m_currentBuffer; SocketWriter() { m_bytesWritten = 0; m_writeBuffers = null; m_packetWriter = RawPacketWriter.INSTANCE; } public PacketWriter getPacketWriter() { return m_packetWriter; } public void setPacketWriter(PacketWriter packetWriter) { m_packetWriter = packetWriter; } public boolean isEmpty() { return m_writeBuffers == null; } public void setPacket(byte[] data, Object tag) { if (!isEmpty()) throw new IllegalStateException("This method should only called when m_writeBuffers == null"); // Set the current packet m_writeBuffers = m_packetWriter.write(new ByteBuffer[] { ByteBuffer.wrap(data) }); m_currentBuffer = 0; m_tag = tag; } public boolean write(SocketChannel channel) throws IOException { // If the packet is empty, just clear data and return true if (m_writeBuffers == null || (m_currentBuffer == m_writeBuffers.length - 1 && !m_writeBuffers[m_currentBuffer].hasRemaining())) { m_writeBuffers = null; return true; } // Write as much as possible to the channel. long written = channel.write(m_writeBuffers, m_currentBuffer, m_writeBuffers.length - m_currentBuffer); // If nothing is written, then the buffer is full and writing should end temporarily. if (written == 0) return false; // Add the number of bytes written. m_bytesWritten += written; // Delete written buffers, update currentBuffer for (int i = m_currentBuffer; i < m_writeBuffers.length; i++) { if (m_writeBuffers[i].hasRemaining()) { m_currentBuffer = i; break; } m_writeBuffers[i] = null; } // If the current buffer is empty, clear all. if (m_writeBuffers[m_currentBuffer] == null) { m_writeBuffers = null; } return true; } public long getBytesWritten() { return m_bytesWritten; } public Object getTag() { return m_tag; } } naga-3.0+svn80/src/main/naga/eventmachine/000077500000000000000000000000001300251770300203215ustar00rootroot00000000000000naga-3.0+svn80/src/main/naga/eventmachine/DelayedAction.java000066400000000000000000000060171300251770300236750ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.eventmachine; import java.util.Date; import java.util.concurrent.atomic.AtomicLong; /** * Holds a delayed runnable for the event service. *

* This class implements the DelayedEvent interface and offers * offering the ability to cancel an event before it is executed. * * @author Christoffer Lerno */ class DelayedAction implements Comparable, DelayedEvent { private final static AtomicLong s_nextId = new AtomicLong(0L); private volatile Runnable m_call; private final long m_time; private final long m_id; /** * Creates a new delayed action. * * @param call the Runnable to call at a later point. * @param time the time when the call should execute. */ public DelayedAction(Runnable call, long time) { m_call = call; m_time = time; m_id = s_nextId.getAndIncrement(); } /** * Cancels this delayed action. */ public void cancel() { m_call = null; } void run() { // First extract the runnable and put it in a local // variable since it is possible that m_call is // changed while this method is running. Runnable call = m_call; // If call != null i.e. the action is not cancelled, // execute the encapsulated runnable. if (call != null) call.run(); } /** * Compares one delayed action to another. *

* Comparison is first done by execution time, then on id (i.e. creation order). * * @param o the other delayed action. * @return -1, 0, 1 depending on where this action should be compared to the other action. */ @SuppressWarnings({"AccessingNonPublicFieldOfAnotherObject"}) public int compareTo(DelayedAction o) { if (m_time < o.m_time) return -1; if (m_time > o.m_time) return 1; if (m_id < o.m_id) return -1; return m_id > o.m_id ? 1 : 0; } public Runnable getCall() { return m_call; } public long getTime() { return m_time; } @Override public String toString() { return "DelayedAction @ " + new Date(m_time) + " [" + (m_call == null ? "Cancelled" : m_call) + "]"; } } naga-3.0+svn80/src/main/naga/eventmachine/DelayedEvent.java000066400000000000000000000041431300251770300235370ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.eventmachine; /** * A cancellable, delayed event posted to the event service. * * @author Christoffer Lerno */ public interface DelayedEvent { /** * Cancels this delayed event. *

* Note that cancelling a delayed event is *not* guaranteed to * remove the event from the queue. But it is guaranteed to * clear the reference to the Runnable associated with the event. * The method may be called multiple times with no ill effect. *

* Cancelling an event while it is executing will not prevent it * from executing. *

* This metod is thread-safe. */ void cancel(); /** * Returns the actual Runnable to be executed when this event runs. *

* This will value will be null if this event has been cancelled. * * @return the call to execute with this event runs, or null if this * event is cancelled. */ Runnable getCall(); /** * Returns the time when this event will execute. See Date#getTime(). * * @return a long representing the time when this event will occur. */ long getTime(); } naga-3.0+svn80/src/main/naga/eventmachine/EventMachine.java000066400000000000000000000201251300251770300235320ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.eventmachine; import naga.ExceptionObserver; import naga.NIOService; import java.io.IOException; import java.util.Date; import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.PriorityBlockingQueue; /** * EventMachine is a simple event service for driving asynchronous and delayed tasks * together with the a Naga NIOService. *

* Creating and starting an event machine: *

 * EventMachine em = new EventMachine();
 * // Start our event machine thread:
 * em.start();
 * 
* Delayed execution: *
 * em.executeLater(new Runnable() {
 *   public void run()
 *   {
 *      // Code here will execute after 1 second on the nio thread.
 *   }
 * }, 1000);
 * 
* Asynchronous execution, i.e. putting a task from another thread * to be executed on the EventMachine thread. *
 * em.asyncExecute(new Runnable() {
 *   public void run()
 *   {
 *      // Code here will be executed on the nio thread.
 *   }
 * });
 * 
* It is possible to cancel scheduled tasks: *
 * // Schedule an event
 * DelayedEvent event = em.executeLater(new Runnable()
 *   public void run()
 *   {
 *      // Code to run in 1 minute.
 *   }
 * }, 60000);
 * // Cancel the event before it is executed.
 * event.cancel();
 * 
* * @author Christoffer Lerno */ public class EventMachine { private final NIOService m_service; private final Queue m_queue; private Thread m_runThread; /** * Creates a new EventMachine with an embedded NIOService. * * @throws IOException if we fail to set up the internal NIOService. */ public EventMachine() throws IOException { m_service = new NIOService(); m_queue = new PriorityBlockingQueue(); m_runThread = null; } /** * Execute a runnable on the Event/NIO thread. *

* This method is thread-safe. * * @param runnable the runnable to execute on the server thread as soon as possible, */ public void asyncExecute(Runnable runnable) { executeLater(runnable, 0); } /** * Execute a runnable on the Event/NIO thread after a delay. *

* This is the primary way to execute delayed events, typically time-outs and similar * behaviour. *

* This method is thread-safe. * * @param runnable the runnable to execute after the given delay. * @param msDelay the delay until executing this runnable. * @return the delayed event created to execute later. This can be used * to cancel the event. */ public DelayedEvent executeLater(Runnable runnable, long msDelay) { return queueAction(runnable, msDelay + System.currentTimeMillis()); } /** * Creates and queuest a delayed action for execution at a certain time. * * @param runnable the runnable to execute at the given time. * @param time the time date when this runnable should execute. * @return the delayed action created and queued. */ private DelayedAction queueAction(Runnable runnable, long time) { DelayedAction action = new DelayedAction(runnable, time); m_queue.add(action); m_service.wakeup(); return action; } /** * Execute a runnable on the Event/NIO thread after at a certain time. *

* This is the primary way to execute scheduled events. *

* This method is thread-safe. * * @param runnable the runnable to execute at the given time. * @param date the time date when this runnable should execute. * @return the delayed event created to execute later. This can be used * to cancel the event. */ public DelayedEvent executeAt(Runnable runnable, Date date) { return queueAction(runnable, date.getTime()); } /** * Sets the ExceptionObserver for this service. *

* The observer will receive all exceptions thrown by the underlying NIOService * and by queued events. *

* This method is thread-safe. * * @param observer the observer to use, null will cause exceptions to log to stderr */ public void setObserver(ExceptionObserver observer) { getNIOService().setExceptionObserver(observer); } /** * Returns the time when the next scheduled event will execute. * * @return a long representing the date of the next event, or Long.MAX_VALUE if * no event is scheduled. */ public long timeOfNextEvent() { DelayedAction action = m_queue.peek(); return action == null ? Long.MAX_VALUE : action.getTime(); } /** * Causes the event machine to start running on a separate thread together with the * NIOService. *

* Note that the NIOService should not be called (using {@link naga.NIOService#selectNonBlocking()} and related * functions) on another thread if the EventMachine is used. */ public synchronized void start() { if (m_runThread != null) throw new IllegalStateException("Service already running."); if (!m_service.isOpen()) throw new IllegalStateException("Service has been shut down."); m_runThread = new Thread() { @Override public void run() { while (m_runThread == this) { try { select(); } catch (Throwable e) { if (m_runThread == this) getNIOService().notifyException(e); } } } }; m_runThread.start(); } /** * Stops the event machine thread, it may be restarted using start() */ public synchronized void stop() { if (m_runThread == null) throw new IllegalStateException("Service is not running."); m_runThread = null; m_service.wakeup(); } /** * Stops the event machine and closes the underlying NIO service, it is not possible to * restart the event machine after shutdown. */ public synchronized void shutdown() { if (m_runThread == null) throw new IllegalStateException("The service is not running."); m_service.close(); stop(); } /** * Run all delayed events, then run select on the NIOService. * * @throws Throwable if any exception is thrown while executing events or handling IO. */ private void select() throws Throwable { // Run queued actions to be called while (timeOfNextEvent() <= System.currentTimeMillis()) { try { runNextAction(); } catch (Throwable t) { getNIOService().notifyException(t); } } if (timeOfNextEvent() == Long.MAX_VALUE) { m_service.selectBlocking(); } else { long delay = timeOfNextEvent() - System.currentTimeMillis(); m_service.selectBlocking(Math.max(1, delay)); } } /** * Runs the next action in the queue. */ private void runNextAction() { m_queue.poll().run(); } /** * Returns the NIOService used by this event service. * * @return the NIOService that this event service uses. */ public NIOService getNIOService() { return m_service; } /** * Return the current event service queue. * * @return a copy of the current queue. */ public Queue getQueue() { return new PriorityQueue(m_queue); } /** * Return the current queue size. * * @return the number events in the event queue. */ public int getQueueSize() { return m_queue.size(); } } naga-3.0+svn80/src/main/naga/eventmachine/package-info.java000066400000000000000000000002201300251770300235020ustar00rootroot00000000000000/** * An optional simple service for driving asynchronous and delayed tasks integrated with the Naga NIOService. */ package naga.eventmachine;naga-3.0+svn80/src/main/naga/examples/000077500000000000000000000000001300251770300174715ustar00rootroot00000000000000naga-3.0+svn80/src/main/naga/examples/ChatServer.java000066400000000000000000000157731300251770300224170ustar00rootroot00000000000000/* Copyright (c) 2008-2012 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.examples; import naga.*; import naga.eventmachine.DelayedEvent; import naga.eventmachine.EventMachine; import naga.packetreader.AsciiLinePacketReader; import naga.packetwriter.AsciiLinePacketWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Creates a very simple chat server. *

* Run using {@code java naga.examples.ChatServer [port]} * * @author Christoffer Lerno */ public class ChatServer implements ServerSocketObserver { private final EventMachine m_eventMachine; private final List m_users; ChatServer(EventMachine machine) { m_eventMachine = machine; m_users = new ArrayList(); } public void acceptFailed(IOException exception) { System.out.println("Failed to accept connection: " + exception); } public void serverSocketDied(Exception exception) { // If the server socket dies, we could possibly try to open a new socket. System.out.println("Server socket died."); System.exit(-1); } public void newConnection(NIOSocket nioSocket) { // Create a new user to hande the new connection. System.out.println("New user connected from " + nioSocket.getIp() + "."); m_users.add(new User(this, nioSocket)); } private void removeUser(User user) { System.out.println("Removing user " + user + "."); m_users.remove(user); } public void broadcast(User sender, String string) { // We convert the packet, then send it to all users except the sender. byte[] bytesToSend = string.getBytes(); for (User user : m_users) { if (user != sender) user.sendBroadcast(bytesToSend); } } /** * Runs the echo server. * * @param args command line arguments, assumed to be a 1 length string containing a port. */ public static void main(String... args) { int port = Integer.parseInt(args[0]); try { EventMachine machine = new EventMachine(); NIOServerSocket socket = machine.getNIOService().openServerSocket(port); socket.listen(new ChatServer(machine)); socket.setConnectionAcceptor(ConnectionAcceptor.ALLOW); machine.start(); } catch (IOException e) { e.printStackTrace(); } } public EventMachine getEventMachine() { return m_eventMachine; } private static class User implements SocketObserver { private final static long LOGIN_TIMEOUT = 30 * 1000; private final static long INACTIVITY_TIMEOUT = 5 * 60 * 100; private final ChatServer m_server; private final NIOSocket m_socket; private String m_name; private DelayedEvent m_disconnectEvent; private User(ChatServer server, NIOSocket socket) { m_server = server; m_socket = socket; m_socket.setPacketReader(new AsciiLinePacketReader()); m_socket.setPacketWriter(new AsciiLinePacketWriter()); m_socket.listen(this); m_name = null; } public void connectionOpened(NIOSocket nioSocket) { // We start by scheduling a disconnect event for the login. m_disconnectEvent = m_server.getEventMachine().executeLater(new Runnable() { public void run() { m_socket.write("Disconnecting due to inactivity".getBytes()); m_socket.closeAfterWrite(); } }, LOGIN_TIMEOUT); // Send the request to log in. nioSocket.write("Please enter your name:".getBytes()); } public String toString() { return m_name != null ? m_name + "@" + m_socket.getIp() : "anon@" + m_socket.getIp(); } public void connectionBroken(NIOSocket nioSocket, Exception exception) { // Inform the other users if the user was logged in. if (m_name != null) { m_server.broadcast(this, m_name + " left the chat."); } // Remove the user. m_server.removeUser(this); } private void scheduleInactivityEvent() { // Cancel the last disconnect event, schedule another. if (m_disconnectEvent != null) m_disconnectEvent.cancel(); m_disconnectEvent = m_server.getEventMachine().executeLater(new Runnable() { public void run() { m_socket.write("Disconnected due to inactivity.".getBytes()); m_socket.closeAfterWrite(); } }, INACTIVITY_TIMEOUT); } public void packetReceived(NIOSocket socket, byte[] packet) { // Create the string. For real life scenarios, you'd handle exceptions here. String message = new String(packet).trim(); // Ignore empty lines if (message.length() == 0) return; // Reset inactivity timer. scheduleInactivityEvent(); // In this protocol, the first line entered is the name. if (m_name == null) { // User joined the chat. m_name = message; System.out.println(this + " logged in."); m_server.broadcast(this, m_name + " has joined the chat."); m_socket.write(("Welcome " + m_name + ". There are " + m_server.m_users.size() + " user(s) currently logged in.").getBytes()); return; } m_server.broadcast(this, m_name + ": " + message); } public void packetSent(NIOSocket socket, Object tag) { // No need to handle this case. } public void sendBroadcast(byte[] bytesToSend) { // Only send broadcast to users logged in. if (m_name != null) { m_socket.write(bytesToSend); } } } } naga-3.0+svn80/src/main/naga/examples/EchoServer.java000066400000000000000000000045441300251770300224100ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.examples; import naga.*; import java.io.IOException; /** * Creates a very simple echo server. *

* Run using {@code java naga.examples.EchoServer [port]} * * @author Christoffer Lerno */ public class EchoServer { EchoServer() {} /** * Runs the echo server. * * @param args command line arguments, assumed to be a 1 length string containing a port. */ public static void main(String... args) { int port = Integer.parseInt(args[0]); try { NIOService service = new NIOService(); NIOServerSocket socket = service.openServerSocket(port); socket.listen(new ServerSocketObserverAdapter() { public void newConnection(NIOSocket nioSocket) { System.out.println("Client " + nioSocket.getIp() + " connected."); nioSocket.listen(new SocketObserverAdapter() { public void packetReceived(NIOSocket socket, byte[] packet) { socket.write(packet); } public void connectionBroken(NIOSocket nioSocket, Exception exception) { System.out.println("Client " + nioSocket.getIp() + " disconnected."); } }); } }); socket.setConnectionAcceptor(ConnectionAcceptor.ALLOW); while (true) { service.selectBlocking(); } } catch (IOException e) { e.printStackTrace(); } } } naga-3.0+svn80/src/main/naga/examples/Rot13Server.java000066400000000000000000000075031300251770300224400ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.examples; import naga.*; import naga.packetreader.AsciiLinePacketReader; import naga.packetwriter.AsciiLinePacketWriter; import java.io.IOException; /** * Creates a Rot13Server that takes a line of text and returns the Rot13 version of the * text. *

* Run using {@code java naga.examples.Rot13Server [port]}. * * @author Christoffer Lerno */ public class Rot13Server { Rot13Server() {} /** * Runs the rot13 server. * * @param args command line arguments, assumed to be a 1 length string containing a port. */ public static void main(String... args) { int port = Integer.parseInt(args[0]); try { // Open the service. NIOService service = new NIOService(); NIOServerSocket socket = service.openServerSocket(port); final byte[] welcomeMessage = ("Welcome to the ROT13 server at " + socket.toString() + "!").getBytes(); // Start listening to the server socket. socket.listen(new ServerSocketObserverAdapter() { public void newConnection(NIOSocket nioSocket) { System.out.println("Client " + nioSocket.getIp() + " connected."); nioSocket.setPacketReader(new AsciiLinePacketReader()); nioSocket.setPacketWriter(new AsciiLinePacketWriter()); nioSocket.write(welcomeMessage); nioSocket.listen(new SocketObserverAdapter() { public void packetReceived(NIOSocket socket, byte[] packet) { // Convert the packet to a string and trim non-printables. String line = new String(packet).trim(); // Disconnect on "+++" if (line.equals("+++")) { socket.write("Thank you and good bye.".getBytes()); socket.closeAfterWrite(); return; } // Build our ROT13 version of the incoming string. StringBuilder builder = new StringBuilder(line); for (int i = 0; i < builder.length(); i++) { char c = builder.charAt(i); if (c >= 'a' && c <= 'z') { builder.setCharAt(i, (char) (((c - 'a') + 13) % 26 + 'a')); } if (c >= 'A' && c <= 'Z') { builder.setCharAt(i, (char) (((c - 'A') + 13) % 26 + 'A')); } } // Write the result and append a new line. socket.write(builder.toString().getBytes()); } public void connectionBroken(NIOSocket nioSocket, Exception exception) { System.out.println("Client " + nioSocket.getIp() + " disconnected."); if (exception != null) { exception.printStackTrace(); } } }); } }); // Allow all connections. socket.setConnectionAcceptor(ConnectionAcceptor.ALLOW); // Read IO until process exits. while (true) { service.selectBlocking(); } } catch (IOException e) { e.printStackTrace(); } } } naga-3.0+svn80/src/main/naga/examples/SSLTester.java000066400000000000000000000063641300251770300221750ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.examples; import naga.NIOService; import naga.NIOSocket; import naga.NIOSocketSSL; import naga.SocketObserver; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; /** * Undocumented Class * * @author Christoffer Lerno */ public class SSLTester { public static void main(String... args) { try { NIOService service = new NIOService(); SSLEngine engine = SSLContext.getDefault().createSSLEngine(); NIOSocket socket = service.openSSLSocket(engine, "www.sslshopper.com", 443); socket.listen(new SocketObserver() { public void packetSent(NIOSocket socket, Object tag) { System.out.println("Packet sent"); } public void connectionOpened(NIOSocket nioSocket) { try { ((NIOSocketSSL)nioSocket).beginHandshake(); } catch (SSLException e) { e.printStackTrace(); } System.out.println("*Connection opened"); nioSocket.write("GET /ssl-converter.html HTTP/1.0\r\n\r\n".getBytes()); } public void connectionBroken(NIOSocket nioSocket, Exception exception) { System.out.println("*Connection broken"); if (exception != null) exception.printStackTrace(); System.exit(9); } public void packetReceived(NIOSocket socket, byte[] packet) { System.out.println("*Unencrypted Packet received " + packet.length); System.out.println(new String(packet)); } }); // https://www.sslshopper.com/ssl-converter.html while (true) { service.selectBlocking(); } } catch (Exception e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } } naga-3.0+svn80/src/main/naga/examples/ValidationClient.java000066400000000000000000000073671300251770300236020ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.examples; import naga.NIOService; import naga.NIOSocket; import naga.SocketObserver; import naga.packetreader.RegularPacketReader; import naga.packetwriter.RegularPacketWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; /** * A client for exercising the validation server. *

* Use with {@code java naga.examples.ValidationClient [host] [port] [account] [password]}. * @author Christoffer Lerno */ public class ValidationClient { ValidationClient() {} /** * Make a login request to the server. * * @param args assumed to be 4 strings representing host, port, account and password. */ public static void main(String... args) { try { // Parse arguments. String host = args[0]; int port = Integer.parseInt(args[1]); String account = args[2]; String password = args[3]; // Prepare the login packet, packing two UTF strings together // using a data output stream. ByteArrayOutputStream stream = new ByteArrayOutputStream(); DataOutputStream dataStream = new DataOutputStream(stream); dataStream.writeUTF(account); dataStream.writeUTF(password); dataStream.flush(); final byte[] content = stream.toByteArray(); dataStream.close(); // Start up the service. NIOService service = new NIOService(); // Open our socket. NIOSocket socket = service.openSocket(host, port); // Use regular 1 byte header reader/writer socket.setPacketReader(new RegularPacketReader(1, true)); socket.setPacketWriter(new RegularPacketWriter(1, true)); // Start listening to the socket. socket.listen(new SocketObserver() { public void connectionOpened(NIOSocket nioSocket) { System.out.println("Sending login..."); nioSocket.write(content); } public void packetSent(NIOSocket socket, Object tag) { System.out.println("Packet sent"); } public void packetReceived(NIOSocket socket, byte[] packet) { try { // Read the UTF-reply and print it. String reply = new DataInputStream(new ByteArrayInputStream(packet)).readUTF(); System.out.println("Reply was: " + reply); // Exit the program. System.exit(0); } catch (Exception e) { e.printStackTrace(); } } public void connectionBroken(NIOSocket nioSocket, Exception exception) { System.out.println("Connection failed."); // Exit the program. System.exit(-1); } }); // Read IO until process exits. while (true) { service.selectBlocking(); } } catch (Exception e) { e.printStackTrace(); } } } naga-3.0+svn80/src/main/naga/examples/ValidationServer.java000066400000000000000000000100641300251770300236160ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.examples; import naga.*; import naga.packetreader.RegularPacketReader; import naga.packetwriter.RegularPacketWriter; import java.io.*; import java.util.HashMap; import java.util.Map; /** * An example validation server to validate logins. * * @author Christoffer Lerno */ public class ValidationServer { ValidationServer() { } public static void main(String... args) { int port = Integer.parseInt(args[0]); // Create a map with users and passwords. final Map passwords = new HashMap(); passwords.put("Admin", "password"); passwords.put("Aaron", "AAAAAAAA"); passwords.put("Bob", "QWERTY"); passwords.put("Lisa", "secret"); try { NIOService service = new NIOService(); NIOServerSocket socket = service.openServerSocket(port); socket.listen(new ServerSocketObserverAdapter() { public void newConnection(NIOSocket nioSocket) { System.out.println("Received connection: " + nioSocket); // Set a 1 byte header regular reader. nioSocket.setPacketReader(new RegularPacketReader(1, true)); // Set a 1 byte header regular writer. nioSocket.setPacketWriter(new RegularPacketWriter(1, true)); // Listen on the connection. nioSocket.listen(new SocketObserverAdapter() { public void packetReceived(NIOSocket socket, byte[] packet) { // We received a packet. Should contain two encoded // UTF strings with user and password. System.out.println("Login attempt from " + socket); try { // Let us unpack the bytes by converting the bytes to a stream. DataInputStream stream = new DataInputStream(new ByteArrayInputStream(packet)); // Read the two strings. String user = stream.readUTF(); String password = stream.readUTF(); // Prepare to encode the response. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(byteArrayOutputStream); if (!passwords.containsKey(user)) { System.out.println("Unknown user: " + user); out.writeUTF("NO_SUCH_USER"); } else if (!passwords.get(user).equals(password)) { out.writeUTF("INCORRECT_PASS"); System.out.println("Failed login for: " + user); } else { out.writeUTF("LOGIN_OK"); System.out.println("Successful login for: " + user); } // Create the outgoing packet. out.flush(); socket.write(byteArrayOutputStream.toByteArray()); // Close after the packet has finished writing. socket.closeAfterWrite(); } catch (IOException e) { // No error handling to speak of. socket.close(); } } }); } }); // Allow all logins. socket.setConnectionAcceptor(ConnectionAcceptor.ALLOW); // Keep reading IO forever. while (true) { service.selectBlocking(); } } catch (IOException e) { } } } naga-3.0+svn80/src/main/naga/examples/package-info.java000066400000000000000000000002511300251770300226560ustar00rootroot00000000000000/** * Various examples on how to use Naga. *

* These classes are only available in the debug version of the naga deliverable. */ package naga.examples;naga-3.0+svn80/src/main/naga/exception/000077500000000000000000000000001300251770300176515ustar00rootroot00000000000000naga-3.0+svn80/src/main/naga/exception/ProtocolViolationException.java000066400000000000000000000027631300251770300260710ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.exception; import java.io.IOException; /** * Throw an exception due to unexpected data when reading packets. * * @author Christoffer Lerno */ public class ProtocolViolationException extends IOException { private static final long serialVersionUID = 6869467292395980590L; /** * Create a new exception. * * @param message exception message. */ public ProtocolViolationException(String message) { super(message); } } naga-3.0+svn80/src/main/naga/exception/package-info.java000066400000000000000000000000731300251770300230400ustar00rootroot00000000000000/** * Exceptions used by Naga. */ package naga.exception;naga-3.0+svn80/src/main/naga/package-info.java000066400000000000000000000005501300251770300210420ustar00rootroot00000000000000/** * The main Naga classes. *

* See {@link naga.NIOService} on how to start a new NIOService for asynchronous * socket I/O. *

* The library uses the implementations of {@link naga.NIOSocket} and {@link naga.NIOServerSocket} * as asynchronous counterparts to {@link java.net.Socket} and {@link java.net.ServerSocket}. *

*/ package naga;naga-3.0+svn80/src/main/naga/packetreader/000077500000000000000000000000001300251770300203055ustar00rootroot00000000000000naga-3.0+svn80/src/main/naga/packetreader/AsciiLinePacketReader.java000066400000000000000000000033631300251770300252700ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetreader; /** * Reads a bytestream delimited by '\n'. *

* This can be used for reading lines of ASCII characters. * * @author Christoffer Lerno */ public class AsciiLinePacketReader extends DelimiterPacketReader { /** * Creates a '\n' delimited reader with an unlimited max buffer size. */ public AsciiLinePacketReader() { super((byte) '\n'); } /** * Creates a '\n' delimited reader with the given max line length * and default read buffer size. *

* Exceeding the line length will throw an IOException. * * @param maxLineLength maximum line length. */ public AsciiLinePacketReader(int maxLineLength) { super((byte) '\n', maxLineLength); } } naga-3.0+svn80/src/main/naga/packetreader/CipherPacketReader.java000066400000000000000000000067261300251770300246500ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetreader; import naga.PacketReader; import naga.exception.ProtocolViolationException; import javax.crypto.Cipher; import javax.crypto.ShortBufferException; import java.nio.ByteBuffer; /** * Example filter reader that decrypts the stream before passing it to its underlying reader. * * @author Christoffer Lerno */ public class CipherPacketReader implements PacketReader { private final Cipher m_cipher; private ByteBuffer m_internalBuffer; private PacketReader m_reader; /** * Creates a new CipherPacketReader. * * @param cipher the cipher to use. * @param reader the underlying packet reader we wish to employ. */ public CipherPacketReader(Cipher cipher, PacketReader reader) { m_cipher = cipher; m_reader = reader; } public PacketReader getReader() { return m_reader; } public void setReader(PacketReader reader) { m_reader = reader; } public byte[] nextPacket(ByteBuffer byteBuffer) throws ProtocolViolationException { if (m_internalBuffer == null) { // No buffer, so simply allocate sufficient memory. m_internalBuffer = ByteBuffer.allocate(m_cipher.getOutputSize(byteBuffer.remaining())); } else { // Only create a new buffer if there is new incoming data if (byteBuffer.remaining() > 0) { // Allocate enough memory to hold the new and the already decrypted data. ByteBuffer newBuffer = ByteBuffer.allocate(m_cipher.getOutputSize(byteBuffer.remaining()) + m_internalBuffer.remaining()); // Move the decrypted data to front. newBuffer.put(m_internalBuffer); // Update the internal buffer. m_internalBuffer = newBuffer; } } // Decrypt the new data- if (byteBuffer.remaining() > 0) { try { m_cipher.update(byteBuffer, m_internalBuffer); } catch (ShortBufferException e) { throw new ProtocolViolationException("Short buffer"); } // Prepare the data m_internalBuffer.flip(); } byte[] packet = m_reader.nextPacket(m_internalBuffer); if (m_internalBuffer.remaining() == 0) m_internalBuffer = null; return packet; } } naga-3.0+svn80/src/main/naga/packetreader/DelimiterPacketReader.java000066400000000000000000000074051300251770300253470ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetreader; import naga.PacketReader; import naga.exception.ProtocolViolationException; import java.nio.ByteBuffer; /** * Class to read a byte stream delimited by a byte marking the end of a packet. *

* Since packets read with delimiters may potentially grow unbounded, you can also supply * a maximum buffer size to prevent an attacker from causing an out of memory * by continously sending data without the delimiter. *

* The delimiter will never appear in the packet itself. * * @author Christoffer Lerno */ public class DelimiterPacketReader implements PacketReader { private volatile int m_maxPacketSize; private byte m_delimiter; /** * Create a new reader with the default min buffer size and unlimited max buffer size. * * @param delimiter the byte delimiter to use. */ public DelimiterPacketReader(byte delimiter) { this(delimiter, -1); } /** * Create a new reader with the given min and max buffer size * delimited by the given byte. * * @param delimiter the byte value of the delimiter. * @param maxPacketSize the maximum number of bytes read before throwing an * IOException. -1 means the packet has no size limit. * @throws IllegalArgumentException if maxPacketSize < 1 */ public DelimiterPacketReader(byte delimiter, int maxPacketSize) { if (maxPacketSize < 1 && maxPacketSize != -1) { throw new IllegalArgumentException("Max packet size must be larger that 1, was: " + maxPacketSize); } m_delimiter = delimiter; m_maxPacketSize = maxPacketSize; } /** * Get the current maximum buffer size. * * @return the current maximum size. */ public int getMaxPacketSize() { return m_maxPacketSize; } /** * Set the new maximum packet size. *

* This method is thread-safe, but will not * affect reads in progress. * * @param maxPacketSize the new maximum packet size. */ public void setMaxPacketSize(int maxPacketSize) { m_maxPacketSize = maxPacketSize; } @SuppressWarnings({"ResultOfMethodCallIgnored"}) public byte[] nextPacket(ByteBuffer byteBuffer) throws ProtocolViolationException { byteBuffer.mark(); int bytesRead = 0; while (byteBuffer.remaining() > 0) { int ch = byteBuffer.get(); if (ch == m_delimiter) { byte[] packet = new byte[bytesRead]; byteBuffer.reset(); byteBuffer.get(packet); byteBuffer.get(); return packet; } bytesRead++; if (m_maxPacketSize > 0 && bytesRead > m_maxPacketSize) throw new ProtocolViolationException("Packet exceeds max " + m_maxPacketSize); } byteBuffer.reset(); return null; } }naga-3.0+svn80/src/main/naga/packetreader/RawPacketReader.java000066400000000000000000000033301300251770300241530ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetreader; import naga.PacketReader; import naga.exception.ProtocolViolationException; import java.nio.ByteBuffer; /** * This packet reader reads as many bytes as possible from the stream * and then bundles those bytes into a packet. * * @author Christoffer Lerno */ public class RawPacketReader implements PacketReader { public final static RawPacketReader INSTANCE = new RawPacketReader(); private RawPacketReader() { } public byte[] nextPacket(ByteBuffer byteBuffer) throws ProtocolViolationException { byte[] packet = new byte[byteBuffer.remaining()]; byteBuffer.get(packet); return packet; } } naga-3.0+svn80/src/main/naga/packetreader/RegularPacketReader.java000066400000000000000000000051661300251770300250340ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetreader; import naga.NIOUtils; import naga.PacketReader; import naga.exception.ProtocolViolationException; import java.nio.ByteBuffer; /** * Reads packet of the format *

* * [header 1-4 bytes] => content size *
* [content] => 0-255/0-65535/0-16777215/0-2147483646 *
*

* Note that the maximum size for 4 bytes is a signed 32 bit int, not unsigned. * * @author Christoffer Lerno */ public class RegularPacketReader implements PacketReader { private final boolean m_bigEndian; private final int m_headerSize; /** * Creates a regular packet reader with the given header size. * * @param headerSize the header size, 1 - 4 bytes. * @param bigEndian big endian (largest byte first) or little endian (smallest byte first) */ public RegularPacketReader(int headerSize, boolean bigEndian) { if (headerSize < 1 || headerSize > 4) throw new IllegalArgumentException("Header must be between 1 and 4 bytes long."); m_bigEndian = bigEndian; m_headerSize = headerSize; } public byte[] nextPacket(ByteBuffer byteBuffer) throws ProtocolViolationException { if (byteBuffer.remaining() < m_headerSize) return null; byteBuffer.mark(); int length = NIOUtils.getPacketSizeFromByteBuffer(byteBuffer, m_headerSize, m_bigEndian); if (byteBuffer.remaining() >= length) { byte[] packet = new byte[length]; byteBuffer.get(packet); return packet; } else { byteBuffer.reset(); return null; } } } naga-3.0+svn80/src/main/naga/packetreader/StreamCipherPacketReader.java000066400000000000000000000050361300251770300260150ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetreader; import naga.PacketReader; import naga.exception.ProtocolViolationException; import javax.crypto.Cipher; import javax.crypto.ShortBufferException; import java.nio.ByteBuffer; /** * Undocumented Class * * @author Christoffer Lerno */ public class StreamCipherPacketReader implements PacketReader { private final Cipher m_cipher; private ByteBuffer m_internalBuffer; private PacketReader m_reader; public StreamCipherPacketReader(Cipher cipher, PacketReader reader) { m_cipher = cipher; m_reader = reader; } public byte[] nextPacket(ByteBuffer byteBuffer) throws ProtocolViolationException { if (m_internalBuffer == null) { m_internalBuffer = ByteBuffer.allocate(m_cipher.getOutputSize(byteBuffer.remaining())); } else { ByteBuffer newBuffer = ByteBuffer.allocate(m_cipher.getOutputSize(byteBuffer.remaining()) + m_internalBuffer.remaining()); newBuffer.put(m_internalBuffer); m_internalBuffer = newBuffer; } try { int consumed = m_cipher.update(byteBuffer, m_internalBuffer); } catch (ShortBufferException e) { throw new ProtocolViolationException("Short buffer"); } m_internalBuffer.flip(); byte[] packet = m_reader.nextPacket(m_internalBuffer); if (m_internalBuffer.remaining() == 0) m_internalBuffer = null; return packet; } } naga-3.0+svn80/src/main/naga/packetreader/ZeroDelimitedPacketReader.java000066400000000000000000000033041300251770300261630ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetreader; /** * Reads a bytestream delimited by 0. * * @author Christoffer Lerno */ public class ZeroDelimitedPacketReader extends DelimiterPacketReader { /** * Creates zero delimited reader with an unlimited max packet size. */ public ZeroDelimitedPacketReader() { super((byte) 0); } /** * Creates a zero delimited reader with the given max packet size * and read buffer size. *

* Exceeding the packet size will throw a ProtocolViolationException. * * @param maxPacketSize the maximum packet size to accept. */ public ZeroDelimitedPacketReader(int maxPacketSize) { super((byte)0, maxPacketSize); } } naga-3.0+svn80/src/main/naga/packetreader/package-info.java000066400000000000000000000001631300251770300234740ustar00rootroot00000000000000/** * Package containing various ready-to-use {@code PacketReader} implementations. */ package naga.packetreader;naga-3.0+svn80/src/main/naga/packetwriter/000077500000000000000000000000001300251770300203575ustar00rootroot00000000000000naga-3.0+svn80/src/main/naga/packetwriter/AsciiLinePacketWriter.java000066400000000000000000000024561300251770300254160ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetwriter; /** * Writes a bytestream delimited by '\n'. * * @author Christoffer Lerno */ public class AsciiLinePacketWriter extends DelimiterPacketWriter { public AsciiLinePacketWriter() { super((byte)'\n'); } } naga-3.0+svn80/src/main/naga/packetwriter/CipherPacketWriter.java000066400000000000000000000052071300251770300247650ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetwriter; import naga.PacketWriter; import javax.crypto.Cipher; import java.nio.ByteBuffer; /** * Example Writer that encrypts the outgoing stream using a Cipher object. * * @author Christoffer Lerno */ public class CipherPacketWriter implements PacketWriter { private final Cipher m_cipher; private PacketWriter m_packetWriter; public CipherPacketWriter(Cipher cipher, PacketWriter packetWriter) { m_cipher = cipher; m_packetWriter = packetWriter; } public PacketWriter getPacketWriter() { return m_packetWriter; } public void setPacketWriter(PacketWriter packetWriter) { m_packetWriter = packetWriter; } public ByteBuffer[] write(ByteBuffer[] byteBuffer) { byteBuffer = m_packetWriter.write(byteBuffer); ByteBuffer[] resultBuffer = new ByteBuffer[byteBuffer.length]; try { for (int i = 0; i < byteBuffer.length; i++) { resultBuffer[i] = ByteBuffer.allocate(m_cipher.getOutputSize(byteBuffer[i].remaining())); if (i == byteBuffer.length - 1) { m_cipher.doFinal(byteBuffer[i], resultBuffer[i]); } else { m_cipher.update(byteBuffer[i], resultBuffer[i]); } assert byteBuffer[i].remaining() == 0; resultBuffer[i].flip(); } } catch (Exception e) { throw new RuntimeException(e); } return resultBuffer; } } naga-3.0+svn80/src/main/naga/packetwriter/DelimiterPacketWriter.java000066400000000000000000000032051300251770300254650ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetwriter; import naga.NIOUtils; import naga.PacketWriter; import java.nio.ByteBuffer; /** * Class to write a byte stream delimited by a byte marking the end of a packet. * * @author Christoffer Lerno */ public class DelimiterPacketWriter implements PacketWriter { private ByteBuffer m_endByte; public DelimiterPacketWriter(byte endByte) { m_endByte = ByteBuffer.wrap(new byte[] { endByte }); } public ByteBuffer[] write(ByteBuffer[] byteBuffer) { m_endByte.rewind(); return NIOUtils.concat(byteBuffer, m_endByte); } } naga-3.0+svn80/src/main/naga/packetwriter/RawPacketWriter.java000066400000000000000000000031431300251770300243010ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetwriter; import naga.PacketWriter; import java.nio.ByteBuffer; /** * Writes a byte packet to the stream without doing any changes to it. *

* This is the commonly case when one wants to output text or similarly * delimited data. * * @author Christoffer Lerno */ public class RawPacketWriter implements PacketWriter { public static RawPacketWriter INSTANCE = new RawPacketWriter(); private RawPacketWriter() { } public ByteBuffer[] write(ByteBuffer[] byteBuffers) { return byteBuffers; } } naga-3.0+svn80/src/main/naga/packetwriter/RegularPacketWriter.java000066400000000000000000000052351300251770300251550ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetwriter; import naga.NIOUtils; import naga.PacketWriter; import java.nio.ByteBuffer; /** * Writes packet of the format *

* * [header 1-4 bytes] => content size *
* [content] => 0-255/0-65535/0-16777215/0-2147483646 *
*

* Note that the maximum size for 4 bytes is a signed 32 bit int, not unsigned. *

* The packet writer will not validate outgoing packets, so make sure that * the packet content size will fit in the header. I.e. make sure that if you have * a 1 byte header, you do not send packets larger than 255 bytes, if two bytes, larger than 65535 and * so on. * * @author Christoffer Lerno */ public class RegularPacketWriter implements PacketWriter { private final boolean m_bigEndian; private final ByteBuffer m_header; /** * Creates a regular packet writer with the given header size. * * @param headerSize the header size, 1 - 4 bytes. * @param bigEndian big endian (largest byte first) or little endian (smallest byte first) */ public RegularPacketWriter(int headerSize, boolean bigEndian) { if (headerSize < 1 || headerSize > 4) throw new IllegalArgumentException("Header must be between 1 and 4 bytes long."); m_bigEndian = bigEndian; m_header = ByteBuffer.allocate(headerSize); } public ByteBuffer[] write(ByteBuffer[] byteBuffers) { m_header.clear(); NIOUtils.setPacketSizeInByteBuffer(m_header, m_header.capacity(), (int)NIOUtils.remaining(byteBuffers), m_bigEndian); m_header.flip(); return NIOUtils.concat(m_header, byteBuffers); } } naga-3.0+svn80/src/main/naga/packetwriter/ZeroDelimitedPacketWriter.java000066400000000000000000000024611300251770300263120ustar00rootroot00000000000000/* Copyright (c) 2008-2011 Christoffer Lernö Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package naga.packetwriter; /** * Writes a bytestream delimited by 0. * * @author Christoffer Lerno */ public class ZeroDelimitedPacketWriter extends DelimiterPacketWriter { public ZeroDelimitedPacketWriter() { super((byte) 0); } } naga-3.0+svn80/src/main/naga/packetwriter/package-info.java000066400000000000000000000001631300251770300235460ustar00rootroot00000000000000/** * Package containing various ready-to-use {@code PacketWriter} implementations. */ package naga.packetwriter;naga-3.0+svn80/src/test/000077500000000000000000000000001300251770300150005ustar00rootroot00000000000000naga-3.0+svn80/src/test/naga/000077500000000000000000000000001300251770300157065ustar00rootroot00000000000000naga-3.0+svn80/src/test/naga/NIOServiceTest.java000066400000000000000000000066001300251770300213610ustar00rootroot00000000000000package naga; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import org.easymock.classextension.EasyMock; import java.io.IOException; import java.net.InetSocketAddress; public class NIOServiceTest extends TestCase { NIOService m_service; public void setUp() throws IOException { m_service = new NIOService(); } public void testOpenServerSocket() throws Exception { ConnectionAcceptor acceptor = EasyMock.createMock(ConnectionAcceptor.class); EasyMock.expect(acceptor.acceptConnection((InetSocketAddress) EasyMock.anyObject())).andReturn(true).once(); EasyMock.replay(acceptor); final SocketObserver socketObserverClient = EasyMock.createMock(SocketObserver.class); final SocketObserver socketObserverServer = EasyMock.createMock(SocketObserver.class); ServerSocketObserver serverSocketObserver = new ServerSocketObserverAdapter() { public void newConnection(NIOSocket nioSocket) { nioSocket.listen(socketObserverServer); } }; socketObserverServer.connectionOpened((NIOSocket) EasyMock.anyObject()); EasyMock.expectLastCall().once(); socketObserverClient.connectionOpened((NIOSocket) EasyMock.anyObject()); EasyMock.expectLastCall().once(); EasyMock.replay(socketObserverClient); EasyMock.replay(socketObserverServer); NIOServerSocket serverSocket = m_service.openServerSocket(new InetSocketAddress(3133), 0); NIOSocket socket = m_service.openSocket("localhost", 3133); socket.listen(socketObserverClient); serverSocket.listen(serverSocketObserver); serverSocket.setConnectionAcceptor(acceptor); while (serverSocket.getTotalConnections() == 0) { m_service.selectBlocking(); } m_service.selectNonBlocking(); assertEquals("[]", m_service.getQueue().toString()); EasyMock.verify(socketObserverClient); EasyMock.verify(socketObserverServer); EasyMock.verify(acceptor); assertEquals(socket.getPort(), serverSocket.socket().getLocalPort()); assertEquals(1, serverSocket.getTotalConnections()); assertEquals(1, serverSocket.getTotalAcceptedConnections()); } public void testAcceptRefused() throws Exception { ConnectionAcceptor acceptor = EasyMock.createMock(ConnectionAcceptor.class); EasyMock.expect(acceptor.acceptConnection((InetSocketAddress) EasyMock.anyObject())).andReturn(false).once(); EasyMock.replay(acceptor); ServerSocketObserver serverSocketObserver = EasyMock.createMock(ServerSocketObserver.class); EasyMock.replay(serverSocketObserver); SocketObserver socketOwnerClientSide = EasyMock.createMock(SocketObserver.class); socketOwnerClientSide.connectionOpened((NIOSocket) EasyMock.anyObject()); EasyMock.expectLastCall().once(); socketOwnerClientSide.connectionBroken((NIOSocket) EasyMock.anyObject(), (Exception) EasyMock.anyObject()); EasyMock.expectLastCall().once(); EasyMock.replay(socketOwnerClientSide); NIOServerSocket serverSocket = m_service.openServerSocket(new InetSocketAddress(3134), 0); serverSocket.setConnectionAcceptor(acceptor); serverSocket.listen(serverSocketObserver); NIOSocket socket = m_service.openSocket("localhost", 3134); socket.listen(socketOwnerClientSide); while (socket.isOpen()) { m_service.selectBlocking(); } EasyMock.verify(serverSocketObserver); EasyMock.verify(acceptor); EasyMock.verify(socketOwnerClientSide); assertEquals(1, serverSocket.getTotalConnections()); assertEquals(1, serverSocket.getTotalRefusedConnections()); } }naga-3.0+svn80/src/test/naga/NIOUtilsTest.java000066400000000000000000000105201300251770300210550ustar00rootroot00000000000000package naga; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import org.easymock.classextension.EasyMock; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.SelectionKey; public class NIOUtilsTest extends TestCase { @SuppressWarnings({"InstantiationOfUtilityClass"}) public void testInstance() { new NIOUtils(); } public void testGetPacketSizeFromByteBuffer() throws Exception { assertEquals(0xFF00FE, NIOUtils.getPacketSizeFromByteBuffer(ByteBuffer.wrap(new byte[]{(byte) 0xFF, 0x00, (byte) 0xFE}), 3, true)); assertEquals(0xFF00FE, NIOUtils.getPacketSizeFromByteBuffer(ByteBuffer.wrap(new byte[]{(byte) 0xFE, 0x00, (byte) 0xFF}), 3, false)); } public void testSetPacketSizeInByteBufferTooBig() { ByteBuffer buffer = ByteBuffer.allocate(10); try { NIOUtils.setPacketSizeInByteBuffer(buffer, 3, -1, true); fail(); } catch (IllegalArgumentException e) { assertEquals("Payload size is less than 0.", e.getMessage()); } try { NIOUtils.setPacketSizeInByteBuffer(buffer, 3, 0xFFFFFF + 1, true); fail("Should throw exception"); } catch (IllegalArgumentException e) { assertEquals("Payload size cannot be encoded into 3 byte(s).", e.getMessage()); } try { NIOUtils.setPacketSizeInByteBuffer(buffer, 1, 0xFF + 1, true); fail("Payload size cannot be encoded into 3 byte(s)."); } catch (IllegalArgumentException e) { assertEquals("Payload size cannot be encoded into 1 byte(s).", e.getMessage()); } } public void testSetPacketSizeInByteBuffer() throws Exception { ByteBuffer buffer = ByteBuffer.allocate(10); getByteBufferFromPacketSizeTests(true); getByteBufferFromPacketSizeTests(false); NIOUtils.setPacketSizeInByteBuffer(buffer, 1, 0xFF, true); buffer.flip(); assertEquals(0xFF, NIOUtils.getPacketSizeFromByteBuffer(buffer, 1, false)); buffer.clear(); NIOUtils.setPacketSizeInByteBuffer(buffer, 3, 0x00, false); buffer.flip(); assertEquals(0x00, NIOUtils.getPacketSizeFromByteBuffer(buffer, 3, true)); } private void getByteBufferFromPacketSizeTests(boolean endian) { ByteBuffer buffer = ByteBuffer.allocate(10); NIOUtils.setPacketSizeInByteBuffer(buffer, 3, 0xFFFFFF, endian); buffer.flip(); assertEquals(0xFFFFFF, NIOUtils.getPacketSizeFromByteBuffer(buffer, 3, endian)); buffer.clear(); NIOUtils.setPacketSizeInByteBuffer(buffer, 3, 0x000000, endian); buffer.flip(); assertEquals(0x000000, NIOUtils.getPacketSizeFromByteBuffer(buffer, 3, endian)); buffer.clear(); NIOUtils.setPacketSizeInByteBuffer(buffer, 3, 0xFFFFFE, endian); buffer.flip(); assertEquals(0xFFFFFE, NIOUtils.getPacketSizeFromByteBuffer(buffer, 3, endian)); buffer.clear(); NIOUtils.setPacketSizeInByteBuffer(buffer, 4, Integer.MAX_VALUE, endian); buffer.flip(); assertEquals(Integer.MAX_VALUE, NIOUtils.getPacketSizeFromByteBuffer(buffer, 4, endian)); buffer.clear(); NIOUtils.setPacketSizeInByteBuffer(buffer, 4, 0x00000000, endian); buffer.flip(); assertEquals(0x00000000, NIOUtils.getPacketSizeFromByteBuffer(buffer, 4, endian)); } public void testCancelKeySilently() throws Exception { SelectionKey key = EasyMock.createMock(SelectionKey.class); key.cancel(); EasyMock.expectLastCall().andThrow(new RuntimeException()); EasyMock.replay(key); NIOUtils.cancelKeySilently(key); EasyMock.verify(key); } public void testCloseChannelSilently() throws Exception { Channel channel = EasyMock.createMock(Channel.class); channel.close(); EasyMock.expectLastCall().andThrow(new IOException()); EasyMock.replay(channel); NIOUtils.closeChannelSilently(channel); EasyMock.verify(channel); } }naga-3.0+svn80/src/test/naga/SocketChannelResponderTest.java000066400000000000000000000217601300251770300240220ustar00rootroot00000000000000package naga; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import org.easymock.IAnswer; import org.easymock.classextension.EasyMock; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; @SuppressWarnings({"StaticMethodReferencedViaSubclass"}) public class SocketChannelResponderTest extends TestCase { SocketChannelResponder m_socketChannelResponder; SelectionKey m_key; SocketChannel m_channel; NIOService m_nioService; protected void setUp() throws Exception { m_channel = EasyMock.createMock(SocketChannel.class); m_key = EasyMock.createMock(SelectionKey.class); m_nioService = EasyMock.createMock(NIOService.class); } public void testWriteExceedingMax() { EasyMock.expect(m_channel.isConnected()).andReturn(true).once(); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); EasyMock.expect(m_key.interestOps(0)).andReturn(m_key).once(); replay(); m_socketChannelResponder = new SocketChannelResponder(m_nioService, m_channel, new InetSocketAddress("localhost", 123)); m_socketChannelResponder.setKey(m_key); m_socketChannelResponder.setMaxQueueSize(3); assertEquals(0, m_socketChannelResponder.getBytesWritten()); assertEquals(0, m_socketChannelResponder.getWriteQueueSize()); verify(); reset(); m_nioService.queue((Runnable)EasyMock.anyObject()); EasyMock.expectLastCall(); replay(); // Add a small packet assertEquals(true, m_socketChannelResponder.write("F!".getBytes())); verify(); // This fails because the queue would be too big. assertEquals(false, m_socketChannelResponder.write("OO".getBytes())); } public void testWrite() throws Exception { // Open a writer. PacketWriter writer = EasyMock.createMock(PacketWriter.class); EasyMock.expect(m_channel.isConnected()).andReturn(true).once(); EasyMock.expect(m_key.interestOps(0)).andReturn(m_key).once(); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); m_nioService.queue((Runnable)EasyMock.anyObject()); EasyMock.expectLastCall(); replay(); m_socketChannelResponder = new SocketChannelResponder(m_nioService, m_channel, new InetSocketAddress("localhost", 123)); m_socketChannelResponder.setKey(m_key); m_socketChannelResponder.setPacketWriter(writer); assertEquals(0, m_socketChannelResponder.getBytesWritten()); assertEquals(0, m_socketChannelResponder.getWriteQueueSize()); verify(); reset(); EasyMock.expect(m_key.interestOps()).andReturn(4).atLeastOnce(); EasyMock.expect(m_key.interestOps(0)).andReturn(m_key).atLeastOnce(); EasyMock.replay(writer); replay(); m_socketChannelResponder.socketReadyForWrite(); verify(); reset(); EasyMock.verify(writer); EasyMock.reset(writer); m_nioService.queue((Runnable)EasyMock.anyObject()); EasyMock.expectLastCall(); replay(); // Add a packet byte[] packet = "FOO!".getBytes(); m_socketChannelResponder.write(packet); assertEquals(0, m_socketChannelResponder.getBytesWritten()); assertEquals(4, m_socketChannelResponder.getWriteQueueSize()); verify(); reset(); final ByteBuffer buffer = ByteBuffer.wrap(packet); ByteBuffer[] bufferArray = new ByteBuffer[] { buffer }; // Write nothing of the packet. EasyMock.expect(m_key.interestOps(0)).andReturn(m_key).once(); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); EasyMock.expect(m_key.interestOps(SelectionKey.OP_WRITE)).andReturn(m_key).once(); EasyMock.expect(m_channel.write(bufferArray)).andReturn(0L).once(); EasyMock.expect(writer.write((ByteBuffer[])EasyMock.anyObject())).andReturn(bufferArray).once(); replay(); EasyMock.replay(writer); m_socketChannelResponder.socketReadyForWrite(); assertEquals(0, m_socketChannelResponder.getBytesWritten()); assertEquals(0, m_socketChannelResponder.getWriteQueueSize()); EasyMock.verify(writer); EasyMock.reset(writer); verify(); reset(); // Write part of the packet. EasyMock.expect(m_key.interestOps(0)).andReturn(m_key).once(); EasyMock.expect(m_key.interestOps(SelectionKey.OP_WRITE)).andReturn(m_key).once(); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); EasyMock.expect(m_channel.write(bufferArray, 0, 1)).andAnswer(new IAnswer() { public Long answer() throws Throwable { buffer.position(3); return 3L; } }).once(); EasyMock.expect(m_channel.write(bufferArray, 0, 1)).andReturn(0L).once(); replay(); EasyMock.replay(writer); m_socketChannelResponder.socketReadyForWrite(); assertEquals(3, m_socketChannelResponder.getBytesWritten()); assertEquals(0, m_socketChannelResponder.getWriteQueueSize()); assertEquals(1, buffer.remaining()); EasyMock.verify(writer); EasyMock.reset(writer); verify(); reset(); // Finish writing the packet. EasyMock.expect(m_key.interestOps(0)).andReturn(m_key).once(); EasyMock.expect(m_channel.write(bufferArray, 0, 1)).andAnswer(new IAnswer() { public Long answer() throws Throwable { buffer.position(4); return 1L; } }).once(); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); replay(); EasyMock.replay(writer); m_socketChannelResponder.socketReadyForWrite(); EasyMock.verify(writer); EasyMock.reset(writer); verify(); reset(); assertEquals(4, m_socketChannelResponder.getBytesWritten()); assertEquals(0, m_socketChannelResponder.getWriteQueueSize()); // Test Empty read EasyMock.expect(m_key.interestOps(0)).andReturn(m_key).once(); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); // EasyMock.expect(writer.isEmpty()).andReturn(true).times(2); replay(); EasyMock.replay(writer); m_socketChannelResponder.socketReadyForWrite(); EasyMock.verify(writer); verify(); } private void reset() { EasyMock.reset(m_nioService); EasyMock.reset(m_channel); EasyMock.reset(m_key); } private void verify() { EasyMock.verify(m_nioService); EasyMock.verify(m_channel); EasyMock.verify(m_key); } private void replay() { EasyMock.replay(m_nioService); EasyMock.replay(m_channel); EasyMock.replay(m_key); } public void testFinishConnectThrowsException() throws IOException { NIOService nioService = new NIOService(); EasyMock.expect(m_key.interestOps(SelectionKey.OP_CONNECT)).andReturn(m_key).times(2); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); m_key.cancel(); EasyMock.expectLastCall().once(); replay(); m_socketChannelResponder = new SocketChannelResponder(nioService, SocketChannel.open(), new InetSocketAddress("localhost", 123)); m_socketChannelResponder.setKey(m_key); m_socketChannelResponder.socketReadyForConnect(); assertEquals(true, m_socketChannelResponder.isOpen()); nioService.selectNonBlocking(); assertEquals(false, m_socketChannelResponder.isOpen()); verify(); } public void testCanReadThrowsException() throws IOException { NIOService nioService = new NIOService(); EasyMock.expect(m_key.interestOps(SelectionKey.OP_CONNECT)).andReturn(m_key).times(2); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); m_key.cancel(); EasyMock.expectLastCall().once(); replay(); m_socketChannelResponder = new SocketChannelResponder(nioService, SocketChannel.open(), new InetSocketAddress("localhost", 123)); m_socketChannelResponder.setKey(m_key); m_socketChannelResponder.socketReadyForRead(); assertEquals(true, m_socketChannelResponder.isOpen()); nioService.selectNonBlocking(); assertEquals(false, m_socketChannelResponder.isOpen()); verify(); } public void testCanWriteThrowsException() throws IOException { NIOService nioService = new NIOService(); EasyMock.expect(m_key.interestOps(SelectionKey.OP_CONNECT)).andReturn(m_key).times(4); EasyMock.expect(m_key.interestOps()).andReturn(0).atLeastOnce(); m_key.cancel(); EasyMock.expectLastCall().once(); replay(); m_socketChannelResponder = new SocketChannelResponder(nioService, SocketChannel.open(), new InetSocketAddress("localhost", 123)); m_socketChannelResponder.setKey(m_key); m_socketChannelResponder.write(new byte[] { 0 }); m_socketChannelResponder.socketReadyForWrite(); assertEquals(true, m_socketChannelResponder.isOpen()); nioService.selectNonBlocking(); assertEquals(false, m_socketChannelResponder.isOpen()); verify(); } public void testSetKey() throws Exception { NIOService nioService = new NIOService(); m_key.cancel(); EasyMock.expectLastCall().once(); replay(); m_socketChannelResponder = new SocketChannelResponder(nioService, null, new InetSocketAddress("localhost", 123)); m_socketChannelResponder.close(); nioService.selectNonBlocking(); m_socketChannelResponder.setKey(m_key); verify(); assertEquals(false, m_socketChannelResponder.isOpen()); } } naga-3.0+svn80/src/test/naga/eventmachine/000077500000000000000000000000001300251770300203545ustar00rootroot00000000000000naga-3.0+svn80/src/test/naga/eventmachine/DelayedActionTest.java000066400000000000000000000026571300251770300245760ustar00rootroot00000000000000package naga.eventmachine; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import java.util.Date; public class DelayedActionTest extends TestCase { DelayedAction m_delayedAction; public void testCompare() { DelayedAction action1 = new DelayedAction(null, 3); DelayedAction action2 = new DelayedAction(null, 5); DelayedAction action3 = new DelayedAction(null, 3); DelayedAction action4 = new DelayedAction(null, 2); assertEquals(0, action1.compareTo(action1)); assertEquals(-1, action1.compareTo(action2)); assertEquals(-1, action1.compareTo(action3)); assertEquals(1, action1.compareTo(action4)); assertEquals(1, action2.compareTo(action1)); assertEquals(0, action2.compareTo(action2)); assertEquals(1, action2.compareTo(action3)); assertEquals(1, action2.compareTo(action4)); assertEquals(1, action3.compareTo(action1)); assertEquals(-1, action3.compareTo(action2)); assertEquals(0, action3.compareTo(action3)); assertEquals(1, action3.compareTo(action4)); assertEquals(-1, action4.compareTo(action1)); assertEquals(-1, action4.compareTo(action2)); assertEquals(-1, action4.compareTo(action3)); assertEquals(0, action4.compareTo(action4)); } public void testDelayedActionToString() throws Exception { long time = System.currentTimeMillis(); Date date = new Date(time); assertEquals("DelayedAction @ " + date + " [Cancelled]", new DelayedAction(null, time).toString()); } }naga-3.0+svn80/src/test/naga/eventmachine/EventMachineTest.java000066400000000000000000000121111300251770300244210ustar00rootroot00000000000000package naga.eventmachine; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import naga.ExceptionObserver; import java.util.Date; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class EventMachineTest extends TestCase { EventMachine m_eventMachine; @Override protected void setUp() throws Exception { m_eventMachine = new EventMachine(); } public void testExceptionHandling() throws Exception { final StringBuffer exceptions = new StringBuffer(); try { m_eventMachine.setObserver(null); } catch (NullPointerException e) { } m_eventMachine.start(); m_eventMachine.asyncExecute(new Runnable() { public void run() { throw new RuntimeException("Test1"); } }); Thread.sleep(10); m_eventMachine.setObserver(new ExceptionObserver() { public void notifyExceptionThrown(Throwable e) { exceptions.append(e.getMessage()); } }); m_eventMachine.asyncExecute(new Runnable() { public void run() { throw new RuntimeException("Test2"); } }); Thread.sleep(50); assertEquals("Test2", exceptions.toString()); } public void testExecuteLaterOrdering() throws Exception { Runnable a = new Runnable() { public void run() { } }; Runnable b = new Runnable() { public void run() { } }; Runnable c = new Runnable() { public void run() { } }; m_eventMachine.executeLater(a, 20000); m_eventMachine.executeLater(b, 30000); m_eventMachine.executeLater(c, 10000); Queue actions = m_eventMachine.getQueue(); assertEquals(actions.poll().getCall(), c); assertEquals(actions.poll().getCall(), a); assertEquals(actions.poll().getCall(), b); } public void testExecuteAtOrdering() throws Exception { Runnable a = new Runnable() { public void run() { } }; Runnable b = new Runnable() { public void run() { } }; Runnable c = new Runnable() { public void run() { } }; Date d = new Date(); m_eventMachine.executeAt(a, d); m_eventMachine.executeAt(b, d); m_eventMachine.executeAt(c, d); Queue actions = m_eventMachine.getQueue(); assertEquals(actions.poll().getCall(), a); assertEquals(actions.poll().getCall(), b); assertEquals(actions.poll().getCall(), c); } public void testExecute() throws Exception { final CountDownLatch latch = new CountDownLatch(1); m_eventMachine.asyncExecute(new Runnable() { public void run() { latch.countDown(); } }); assertEquals(false, latch.await(50, TimeUnit.MILLISECONDS)); m_eventMachine.start(); assertEquals(true, latch.await(10, TimeUnit.SECONDS)); m_eventMachine.stop(); } public void testExecuteLater() throws Exception { final CountDownLatch latch = new CountDownLatch(1); m_eventMachine.executeLater(new Runnable() { public void run() { latch.countDown(); } }, 5); assertEquals(false, latch.await(50, TimeUnit.MILLISECONDS)); m_eventMachine.start(); assertEquals(true, latch.await(10, TimeUnit.SECONDS)); m_eventMachine.stop(); } public void testExecuteLater2() throws Exception { final CountDownLatch latch = new CountDownLatch(1); m_eventMachine.executeLater(new Runnable() { public void run() { latch.countDown(); } }, 50); m_eventMachine.start(); assertEquals(false, latch.await(20, TimeUnit.MILLISECONDS)); assertEquals(true, latch.await(50, TimeUnit.SECONDS)); m_eventMachine.stop(); } public void testExecuteLaterWith() throws Exception { final CountDownLatch latch = new CountDownLatch(1); m_eventMachine.executeLater(new Runnable() { public void run() { latch.countDown(); } }, 5); assertEquals(false, latch.await(50, TimeUnit.MILLISECONDS)); m_eventMachine.start(); assertEquals(true, latch.await(10, TimeUnit.SECONDS)); m_eventMachine.stop(); } public void testNIO() { assertNotNull(m_eventMachine.getNIOService()); } public void testStartStop() throws Exception { m_eventMachine.start(); try { m_eventMachine.start(); fail(); } catch (IllegalStateException e) { } m_eventMachine.stop(); try { m_eventMachine.stop(); fail(); } catch (IllegalStateException e) { } final CountDownLatch latch = new CountDownLatch(1); m_eventMachine.start(); m_eventMachine.asyncExecute(new Runnable() { public void run() { latch.countDown(); } }); assertEquals(true, latch.await(10, TimeUnit.MILLISECONDS)); } public void testCancelEvent() throws Exception { final AtomicInteger integer = new AtomicInteger(); m_eventMachine.executeLater(new Runnable() { public void run() { integer.incrementAndGet(); } }, 5); DelayedEvent event = m_eventMachine.executeLater(new Runnable() { public void run() { integer.addAndGet(2); } }, 5); event.cancel(); assertEquals(2, m_eventMachine.getQueueSize()); m_eventMachine.start(); Thread.sleep(20); assertEquals(1, integer.intValue()); m_eventMachine.stop(); assertEquals(0, m_eventMachine.getQueueSize()); } }naga-3.0+svn80/src/test/naga/packetreader/000077500000000000000000000000001300251770300203405ustar00rootroot00000000000000naga-3.0+svn80/src/test/naga/packetreader/AsciiLinePacketReaderTest.java000066400000000000000000000037201300251770300261600ustar00rootroot00000000000000package naga.packetreader; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import naga.exception.ProtocolViolationException; import java.io.IOException; import java.nio.ByteBuffer; public class AsciiLinePacketReaderTest extends TestCase { AsciiLinePacketReader m_asciiLinePacketReader; public void testAsciiLinePacketReader() throws Exception { m_asciiLinePacketReader = new AsciiLinePacketReader(); byte[] notALine = "Foo".getBytes(); ByteBuffer byteBuffer = ByteBuffer.wrap(notALine); assertEquals(3, byteBuffer.remaining()); assertEquals(null, m_asciiLinePacketReader.nextPacket(byteBuffer)); assertEquals(3, byteBuffer.remaining()); byteBuffer = ByteBuffer.wrap("Foo\n".getBytes()); assertEquals("Foo", new String(m_asciiLinePacketReader.nextPacket(byteBuffer))); assertEquals(0, byteBuffer.remaining()); byteBuffer = ByteBuffer.wrap("Foo\n2".getBytes()); assertEquals("Foo", new String(m_asciiLinePacketReader.nextPacket(byteBuffer))); assertEquals(1, byteBuffer.remaining()); byteBuffer = ByteBuffer.wrap("\n\nFoo\n\nBar\nFoobar".getBytes()); assertEquals(0, m_asciiLinePacketReader.nextPacket(byteBuffer).length); assertEquals(0, m_asciiLinePacketReader.nextPacket(byteBuffer).length); assertEquals("Foo", new String(m_asciiLinePacketReader.nextPacket(byteBuffer))); assertEquals(0, m_asciiLinePacketReader.nextPacket(byteBuffer).length); assertEquals("Bar", new String(m_asciiLinePacketReader.nextPacket(byteBuffer))); assertEquals(null, m_asciiLinePacketReader.nextPacket(byteBuffer)); } public void testOverflow() throws ProtocolViolationException { m_asciiLinePacketReader = new AsciiLinePacketReader(3); try { m_asciiLinePacketReader.nextPacket(ByteBuffer.wrap("Foo!\n".getBytes())); fail("Should throw error"); } catch (IOException e) { } } }naga-3.0+svn80/src/test/naga/packetreader/CipherPacketReaderTest.java000066400000000000000000000056521300251770300255400ustar00rootroot00000000000000package naga.packetreader; /** * Undocumented Class * * @author Christoffer Lerno */ import junit.framework.TestCase; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.nio.ByteBuffer; import java.security.Key; public class CipherPacketReaderTest extends TestCase { CipherPacketReader m_cipherPacketReader; public void testNextPacketStreamCipher() throws Exception { Key key = new SecretKeySpec("FOOBAR".getBytes(), "RC4"); Cipher encrypt = Cipher.getInstance("RC4"); encrypt.init(Cipher.ENCRYPT_MODE, key); byte[] bytes = encrypt.doFinal("ABCDEFGHIJK\n Testing\n Testing!\n ABC".getBytes()); Cipher decrypt = Cipher.getInstance("RC4"); decrypt.init(Cipher.DECRYPT_MODE, key); m_cipherPacketReader = new CipherPacketReader(decrypt, new AsciiLinePacketReader()); ByteBuffer buffer1 = ByteBuffer.wrap(bytes, 0, 5); ByteBuffer buffer2 = ByteBuffer.wrap(bytes, 5, 10); ByteBuffer buffer3 = ByteBuffer.wrap(bytes, 15, bytes.length - 15); assertEquals(null, m_cipherPacketReader.nextPacket(buffer1)); assertEquals(0, buffer1.remaining()); assertEquals("ABCDEFGHIJK", new String(m_cipherPacketReader.nextPacket(buffer2))); assertEquals(0, buffer2.remaining()); assertEquals(null, m_cipherPacketReader.nextPacket(buffer2)); assertEquals(" Testing", new String(m_cipherPacketReader.nextPacket(buffer3))); assertEquals(0, buffer3.remaining()); assertEquals(" Testing!", new String(m_cipherPacketReader.nextPacket(buffer3))); assertEquals(null, m_cipherPacketReader.nextPacket(buffer3)); } public void testNextPacketBlockCipher() throws Exception { Key key = new SecretKeySpec("1234567890ABCDEF".getBytes(), "AES"); Cipher encrypt = Cipher.getInstance("AES"); encrypt.init(Cipher.ENCRYPT_MODE, key); byte[] bytes = encrypt.doFinal("ABCDEFGHIJK\n Testing\n Testing!\n ABC".getBytes()); Cipher decrypt = Cipher.getInstance("AES"); decrypt.init(Cipher.DECRYPT_MODE, key); m_cipherPacketReader = new CipherPacketReader(decrypt, new AsciiLinePacketReader()); ByteBuffer buffer1 = ByteBuffer.wrap(bytes, 0, 5); ByteBuffer buffer2 = ByteBuffer.wrap(bytes, 5, 10); ByteBuffer buffer3 = ByteBuffer.wrap(bytes, 15, bytes.length - 15); assertEquals(null, m_cipherPacketReader.nextPacket(buffer1)); assertEquals(0, buffer1.remaining()); assertEquals(null, m_cipherPacketReader.nextPacket(buffer2)); assertEquals(0, buffer2.remaining()); assertEquals("ABCDEFGHIJK", new String(m_cipherPacketReader.nextPacket(buffer3))); assertEquals(" Testing", new String(m_cipherPacketReader.nextPacket(buffer3))); assertEquals(" Testing!", new String(m_cipherPacketReader.nextPacket(buffer3))); assertEquals(null, m_cipherPacketReader.nextPacket(buffer3)); } }naga-3.0+svn80/src/test/naga/packetreader/DelimiterPacketReaderTest.java000066400000000000000000000013061300251770300262340ustar00rootroot00000000000000package naga.packetreader; /** * @author Christoffer Lerno */ import junit.framework.TestCase; public class DelimiterPacketReaderTest extends TestCase { DelimiterPacketReader m_delimiterPacketReader; public void testDelimiterPacketReader() throws Exception { try { new DelimiterPacketReader((byte)0, 0); fail(); } catch (IllegalArgumentException e) { assertEquals("Max packet size must be larger that 1, was: 0", e.getMessage()); } m_delimiterPacketReader = new DelimiterPacketReader((byte)0, 20); assertEquals(20, m_delimiterPacketReader.getMaxPacketSize()); m_delimiterPacketReader.setMaxPacketSize(19); assertEquals(19, m_delimiterPacketReader.getMaxPacketSize()); } }naga-3.0+svn80/src/test/naga/packetreader/RegularPacketReaderTest.java000066400000000000000000000017111300251770300257170ustar00rootroot00000000000000package naga.packetreader; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import java.nio.ByteBuffer; public class RegularPacketReaderTest extends TestCase { RegularPacketReader m_regularPacketReader; public void testRegularPacketReader() throws Exception { m_regularPacketReader = new RegularPacketReader(3, true); ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[] { 0, 0, 3 }); assertEquals(null, m_regularPacketReader.nextPacket(byteBuffer)); assertEquals(3, byteBuffer.remaining()); byteBuffer = ByteBuffer.wrap(new byte[] { 0, 0, 3, 65, 66, 67, 68}); assertEquals("ABC", new String(m_regularPacketReader.nextPacket(byteBuffer))); assertEquals(1, byteBuffer.remaining()); m_regularPacketReader = new RegularPacketReader(3, true); byteBuffer = ByteBuffer.wrap(new byte[] { 0, 0, 0 }); assertEquals("", new String(m_regularPacketReader.nextPacket(byteBuffer))); } }naga-3.0+svn80/src/test/naga/packetwriter/000077500000000000000000000000001300251770300204125ustar00rootroot00000000000000naga-3.0+svn80/src/test/naga/packetwriter/AsciiLinePacketWriterTest.java000066400000000000000000000025451300251770300263100ustar00rootroot00000000000000package naga.packetwriter; /** * Undocumented Class * * @author Christoffer Lerno */ import junit.framework.TestCase; import java.nio.ByteBuffer; public class AsciiLinePacketWriterTest extends TestCase { public void testAsciiLinePacketWriter() throws Exception { AsciiLinePacketWriter writer = new AsciiLinePacketWriter(); ByteBuffer part1 = ByteBuffer.wrap("FOO".getBytes()); ByteBuffer part2 = ByteBuffer.wrap("bar".getBytes()); ByteBuffer[] result = writer.write(new ByteBuffer[] { part1, part2 }); ByteBuffer buffer = ByteBuffer.allocate(100); for (ByteBuffer b : result) { buffer.put(b); } buffer.flip(); byte[] resultByte = new byte[buffer.limit()]; buffer.get(resultByte); assertEquals("FOObar\n", new String(resultByte)); ByteBuffer part3 = ByteBuffer.wrap("BAZ".getBytes()); ByteBuffer part4 = ByteBuffer.wrap("fooo".getBytes()); ByteBuffer[] result2 = writer.write(new ByteBuffer[] { part3, part4 }); ByteBuffer buffer2 = ByteBuffer.allocate(100); for (ByteBuffer b : result2) { buffer2.put(b); } buffer2.flip(); byte[] resultByte2 = new byte[buffer2.limit()]; buffer2.get(resultByte2); assertEquals("BAZfooo\n", new String(resultByte2)); } }naga-3.0+svn80/src/test/naga/packetwriter/CipherPacketWriterTest.java000066400000000000000000000046141300251770300256610ustar00rootroot00000000000000package naga.packetwriter; /** * Undocumented Class * * @author Christoffer Lerno */ import junit.framework.TestCase; import naga.NIOUtils; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.nio.ByteBuffer; import java.security.Key; import java.util.Arrays; public class CipherPacketWriterTest extends TestCase { CipherPacketWriter m_cipherPacketWriter; byte[] merge(ByteBuffer[] buffers) { byte[] total = new byte[(int)NIOUtils.remaining(buffers)]; int pos = 0; for (ByteBuffer buffer : buffers) { int len = buffer.remaining(); buffer.get(total, pos, len); pos += len; } return total; } public void testWriteBlock() throws Exception { Key key = new SecretKeySpec("1234567890ABCDEF".getBytes(), "AES"); Cipher encrypt = Cipher.getInstance("AES"); encrypt.init(Cipher.ENCRYPT_MODE, key); Cipher decrypt = Cipher.getInstance("AES"); decrypt.init(Cipher.DECRYPT_MODE, key); m_cipherPacketWriter = new CipherPacketWriter(encrypt, new RegularPacketWriter(1, true)); ByteBuffer[] result = m_cipherPacketWriter.write(new ByteBuffer[] { ByteBuffer.wrap("ABC".getBytes()) }); assertEquals("[3, 65, 66, 67]", Arrays.toString(decrypt.doFinal(merge(result)))); result = m_cipherPacketWriter.write(new ByteBuffer[] { ByteBuffer.wrap("C".getBytes()), ByteBuffer.wrap("D".getBytes()) }); assertEquals("[2, 67, 68]", Arrays.toString(decrypt.doFinal(merge(result)))); } public void testWriteStream() throws Exception { Key key = new SecretKeySpec("1234567890ABCDEF".getBytes(), "RC4"); Cipher encrypt = Cipher.getInstance("RC4"); encrypt.init(Cipher.ENCRYPT_MODE, key); Cipher decrypt = Cipher.getInstance("RC4"); decrypt.init(Cipher.DECRYPT_MODE, key); m_cipherPacketWriter = new CipherPacketWriter(encrypt, new RegularPacketWriter(1, true)); ByteBuffer[] result = m_cipherPacketWriter.write(new ByteBuffer[] { ByteBuffer.wrap("ABC".getBytes()) }); assertEquals("[3, 65, 66, 67]", Arrays.toString(decrypt.doFinal(merge(result)))); result = m_cipherPacketWriter.write(new ByteBuffer[] { ByteBuffer.wrap("C".getBytes()), ByteBuffer.wrap("D".getBytes()) }); assertEquals("[2, 67, 68]", Arrays.toString(decrypt.doFinal(merge(result)))); } }naga-3.0+svn80/src/test/naga/packetwriter/RegularPacketWriterTest.java000066400000000000000000000026171300251770300260510ustar00rootroot00000000000000package naga.packetwriter; /** * @author Christoffer Lerno */ import junit.framework.TestCase; import java.nio.ByteBuffer; import java.util.Arrays; public class RegularPacketWriterTest extends TestCase { RegularPacketWriter m_regularPacketWriter; public void testRegularPacketWriter() throws Exception { m_regularPacketWriter = new RegularPacketWriter(3, true); ByteBuffer[] result = m_regularPacketWriter.write(new ByteBuffer[] { ByteBuffer.wrap("Foo!".getBytes()) }); assertEquals("[0, 0, 4]", Arrays.toString(result[0].array())); assertEquals("[70, 111, 111, 33]", Arrays.toString(result[1].array())); assertEquals(2, result.length); assertEquals("[0, 0, 0]", Arrays.toString(m_regularPacketWriter.write(new ByteBuffer[] { ByteBuffer.allocate(0) })[0].array())); } // See Bug 5 public void testFourByteHeader() throws Exception { m_regularPacketWriter = new RegularPacketWriter(4, true); ByteBuffer[] result = m_regularPacketWriter.write(new ByteBuffer[] { ByteBuffer.wrap("Foo!".getBytes()) }); assertEquals("[0, 0, 0, 4]", Arrays.toString(result[0].array())); assertEquals("[70, 111, 111, 33]", Arrays.toString(result[1].array())); assertEquals(2, result.length); assertEquals("[0, 0, 0, 0]", Arrays.toString(m_regularPacketWriter.write(new ByteBuffer[] { ByteBuffer.allocate(0) })[0].array())); } }naga-3.0+svn80/src/test/naga/packetwriter/ZeroDelimitedPacketWriterTest.java000066400000000000000000000025571300251770300272130ustar00rootroot00000000000000package naga.packetwriter; /** * Undocumented Class * * @author Christoffer Lerno */ import junit.framework.TestCase; import java.nio.ByteBuffer; public class ZeroDelimitedPacketWriterTest extends TestCase { public void testZeroDelimitedPacketWriter() throws Exception { ZeroDelimitedPacketWriter writer = new ZeroDelimitedPacketWriter(); ByteBuffer part1 = ByteBuffer.wrap("FOO".getBytes()); ByteBuffer part2 = ByteBuffer.wrap("bar".getBytes()); ByteBuffer[] result = writer.write(new ByteBuffer[]{part1, part2}); ByteBuffer buffer = ByteBuffer.allocate(100); for (ByteBuffer b : result) { buffer.put(b); } buffer.flip(); byte[] resultByte = new byte[buffer.limit()]; buffer.get(resultByte); assertEquals("FOObar\0", new String(resultByte)); ByteBuffer part3 = ByteBuffer.wrap("BAZ".getBytes()); ByteBuffer part4 = ByteBuffer.wrap("fooo".getBytes()); ByteBuffer[] result2 = writer.write(new ByteBuffer[]{part3, part4}); ByteBuffer buffer2 = ByteBuffer.allocate(100); for (ByteBuffer b : result2) { buffer2.put(b); } buffer2.flip(); byte[] resultByte2 = new byte[buffer2.limit()]; buffer2.get(resultByte2); assertEquals("BAZfooo\0", new String(resultByte2)); } }naga-3.0+svn80/version.properties000066400000000000000000000001631300251770300170350ustar00rootroot00000000000000#Build Number for ANT. Do not edit! #Wed Jun 13 16:50:00 CEST 2012 build.number=51 major.version=3 minor.version=0