users@grizzly.java.net

Re: Implementing a NIO tunnel with Grizzly

From: Sam Crawford <samcrawford_at_gmail.com>
Date: Fri, 25 Sep 2009 15:26:55 +0100

Nevermind, I think I've cracked this one myself.

Because the connected() method is called on the filters within the
transport making the peer connection, we can obtain the context at the
point of connection (before any data is read/written) and then place
the StreamReader and StreamWriter (which have already been wrapped by
SSLFilter at that point) somewhere easily accessible later on.

I'll post some sample code when I'm all done, and hopefully someone
will see what I'm rabbiting on about!

Thanks,

Sam


2009/9/24 samcrawford <samcrawford_at_gmail.com>:
> Alexey (and others!),
>
> I'm currently trying to add in SSL support to my tunneling mechanism.
> The idea is to be able to handle any combination of SSL and plaintext,
> namely:
>
> 1. Plaintext to tunnel, plaintext to peer (as per Alexey's original example)
> 2. Plaintext to tunnel, SSL to peer
> 3. SSL to tunnel, plaintext to peer
> 4. SSL to tunnel, SSL to peer
>
> I'm using the SSLFilter to manage the process of sending/receiving SSL
> traffic, and whilst I have it working in some simple cases, I've run
> into a bit of a design roadblock.
>
> Referring back to Alexey's original tunnel demo code, the
> redirectToPeer method just obtains a StreamReader from the incoming
> connection, and passes that to the writeStream method on the
> StreamWriter of the peer connection. Whilst this is fine in plaintext
> land, it doesn't work for SSL. I believe this to be because I need to
> obtain the StreamReader and StreamWriter from the context (i.e. after
> the SSLFilter has done it's job) rather than the connection - some
> testing suggests this is indeed the case.
>
> Whilst it's easy enough to grab the StreamReader from the context of
> the incoming connection, we don't have a context for the outgoing
> (peer) connection, so can't push that through the SSL filter. This
> creates an issue with cases #2 and #4 above, where writing to
> Connection.getStreamWriter() is insufficient.
>
> I'm not sure if it's valid for me to store a reference to the
> StreamReader & StreamWriter in an attribute (as the current connection
> map is maintained), but even if it were, on the first bit of data sent
> from client > server I wouldn't have a handle to the StreamWriter on
> the peer.
>
> Is there something simple I'm missing here, or have I confused
> everyone completely?
>
> Thanks,
>
> Sam
>
>
>
> 2009/9/1 Sam Crawford <samcrawford_at_gmail.com>:
>> Thanks v much Alexey. I'll proceed along my current course, but will
>> push new connections into a DelayedQueue, and then have a single
>> separate ScheduledExecutor dequeuing items after 5 seconds and
>> checking the handshake status.
>>
>> Rest assured I'll be back with more questions on other matters soon!
>>
>> All the best,
>>
>> Sam
>>
>>
>> 2009/9/1 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
>>> Hi Sam,
>>>
>>> I think you're doing that right way!
>>> Because handshake you're running is a quite complex process (with several
>>> reads/writes as I understand), which is specific to your protocol, so not
>>> sure Grizzly can have some general solution for this.
>>> We have IdleTimeoutFilter implementation, which closes Connection after
>>> detecting that it is idle for some period of time, but it's not applicable
>>> for you, because you want to detect handshake timeouts.
>>>
>>> So, IMHO, what you are proposing is right way to implement the logic you
>>> need.
>>>
>>> Thanks.
>>>
>>> WBR,
>>> Alexey.
>>>
>>>> Adding the following to handleAccept in the filter does what I want,
>>>> but it doesn't seem very modern! I'm going to explore using the
>>>> ScheduledExecutorService, but would still be interested to know if
>>>> there's a proper grizzly way of doing this.
>>>>
>>>> Thanks,
>>>>
>>>> Sam
>>>>
>>>>
>>>> Thread r = new Thread() {
>>>>        public void run() {
>>>>                try {
>>>>                        Thread.sleep(5000);
>>>>                        // ctx.getConnection() is still null in
>>>> handleAccept...
>>>>                        Connection conn =
>>>> ctx.getProcessorRunnable().getConnection();
>>>>                        TunnelHandshake handshake =
>>>> handshakeAttribute.get(conn);
>>>>                        if (handshake == null || !handshake.isComplete()) {
>>>>                                System.out.println("Closing connection");
>>>>                                conn.close();
>>>>                        }
>>>>                } catch (Exception e) {
>>>>                        e.printStackTrace();
>>>>                }
>>>>        }
>>>> };
>>>> r.start();
>>>>
>>>>
>>>>
>>>>
>>>> 2009/9/1 Sam Crawford <samcrawford_at_gmail.com>:
>>>>>
>>>>> Hi Alexey,
>>>>>
>>>>> Thanks, but I recognise already that we don't need to block for
>>>>> operations. I've had great success with getting the non-blocking
>>>>> aspect working well!
>>>>>
>>>>> My question was around creating time limits for logical steps of
>>>>> execution (not physical timeouts when reading from a blocking
>>>>> socket!). So my handshaking process completes in a non-blocking
>>>>> manner, but I want to enforce an absolute time limit of 5 seconds that
>>>>> the handshake can take.
>>>>>
>>>>> To be specific:
>>>>>
>>>>> 1. If the client connects but does not transfer any bytes then the
>>>>> connection should be dropped after 5 seconds
>>>>> 2. If the client connects and starts the handshake, but does not
>>>>> complete it within 5 seconds, then the connection should be dropped
>>>>> 3. If the client connects, completes the handshake within 5 seconds,
>>>>> and establishes the tunnel then there should not be an enforced
>>>>> timeout
>>>>>
>>>>> Thanks,
>>>>>
>>>>> Sam
>>>>>
>>>>>
>>>>> 2009/9/1 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
>>>>>>
>>>>>> Hi Sam,
>>>>>>
>>>>>>> Apologies for the slow reply. There was no issues with the tunneling
>>>>>>> when the exception was thrown, so I'm not concerned about it.
>>>>>>>
>>>>>>> I'm now actively working on porting our project to use Grizzly (and I
>>>>>>> will certainly write an article afterwards to cover my experiences!),
>>>>>>> and have another query...
>>>>>>>
>>>>>>> Our tunneling mechanism uses a handshaking mechanism (not unlike a
>>>>>>> SOCKS proxy with the CONNECT header). We don't want to wait
>>>>>>> indefinitely for the handshake to complete (e.g. 5 seconds is probably
>>>>>>> a sensible maximum). That said, once the tunnel is running, we would
>>>>>>> not want to have any timeouts (i.e. the tunnel should be able to sit
>>>>>>> idle for hours if need be).
>>>>>>>
>>>>>>> In the old Java sockets API I'd set a read timeout on the socket
>>>>>>> before doing the handshaking, and remove it afterwards.
>>>>>>>
>>>>>>> I can't see how to achieve the same (or similar) in Grizzly 2.0. Is
>>>>>>> there a recommended way of doing this?
>>>>>>
>>>>>> By default all the I/O operations on Grizzly 2.0 TCP NIO connections are
>>>>>> non-blocking.
>>>>>> You have Grizzly Connection object, which represents your network
>>>>>> connection
>>>>>> [1]. You can get StreamReader, StreamWriter out of this Connection.
>>>>>>
>>>>>> Using StreamReader [2] you can check number of bytes available for
>>>>>> reading:
>>>>>> StreamReader.availableDataSize() or retrieve/register some notifier,
>>>>>> which
>>>>>> will notify you, when StreamReader state will be changed: new bytes
>>>>>> become
>>>>>> available, connection was closed etc:
>>>>>> Future f = StreamReader.notifyAvailable(numberOfBytes,
>>>>>> [completionHandler]);
>>>>>>
>>>>>> Using StreamWriter [3] you can perform write operations in non-blocking
>>>>>> mode, using StreamWriter.writeXXX(...). By calling
>>>>>> Future f = StreamReader.flush([completionHandler]);
>>>>>> you can register/retrieve notifier, using which you can asynchronously
>>>>>> check
>>>>>> write operation completion state.
>>>>>>
>>>>>> So, really, you don't to block for anything :)
>>>>>>
>>>>>> Please let me know, if you'll have any questions.
>>>>>>
>>>>>> WBR,
>>>>>> Alexey.
>>>>>>
>>>>>>
>>>>>> [1]
>>>>>>
>>>>>> https://grizzly.dev.java.net/source/browse/grizzly/branches/2dot0/code/modules/grizzly/src/main/java/com/sun/grizzly/Connection.java?rev=3266&view=markup
>>>>>> [2]
>>>>>>
>>>>>> https://grizzly.dev.java.net/source/browse/grizzly/branches/2dot0/code/modules/grizzly/src/main/java/com/sun/grizzly/streams/StreamReader.java?rev=3292&view=markup
>>>>>> [3]
>>>>>>
>>>>>> https://grizzly.dev.java.net/source/browse/grizzly/branches/2dot0/code/modules/grizzly/src/main/java/com/sun/grizzly/streams/StreamWriter.java?rev=3496&view=markup
>>>>>>
>>>>>>>
>>>>>>> Thanks again,
>>>>>>>
>>>>>>> Sam
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> 2009/8/18 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
>>>>>>>>
>>>>>>>> Hi Sam,
>>>>>>>>
>>>>>>>>> I've fired up the example you provided and it seems pretty
>>>>>>>>> straightforward and works well.
>>>>>>>>>
>>>>>>>>> However, when doing some quick tests via it I noted the following
>>>>>>>>> exception was printed every so often... Is this something to be
>>>>>>>>> concerned about? I was running this with the latest 2.0 SNAPSHOT.
>>>>>>>>
>>>>>>>> Hmm, I think you can say me, if it's something you have concerns about
>>>>>>>> :))))
>>>>>>>> For me it looks like issue occurs, when Filter decides to close one
>>>>>>>> connection, it also closes a peer connection. And closing of peer
>>>>>>>> connection
>>>>>>>> may cause this exception.
>>>>>>>>
>>>>>>>> Do you see any problem with tunneling functionality, when the
>>>>>>>> exception
>>>>>>>> occurs?
>>>>>>>>
>>>>>>>> I will probably reduce the logging level for the exception.
>>>>>>>>
>>>>>>>> Thank you.
>>>>>>>>
>>>>>>>> WBR,
>>>>>>>> Alexey.
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Thanks
>>>>>>>>>
>>>>>>>>> Sam
>>>>>>>>>
>>>>>>>>> # java -cp "grizzly-framework-2.0.0-SNAPSHOT.jar:."
>>>>>>>>> com.sun.grizzly.samples.tunnel.TunnelServer
>>>>>>>>> Aug 16, 2009 9:33:31 PM com.sun.grizzly.samples.tunnel.TunnelServer
>>>>>>>>> main
>>>>>>>>> INFO: Press any key to stop the server...
>>>>>>>>> Aug 16, 2009 9:33:36 PM com.sun.grizzly.nio.transport.TCPNIOTransport
>>>>>>>>> fireIOEvent
>>>>>>>>> WARNING: Unexpected exception occurred
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> fireIOEvent().connection=com.sun.grizzly.nio.transport.TCPNIOConnection_at_785f8172
>>>>>>>>> event=READ
>>>>>>>>> java.nio.channels.CancelledKeyException
>>>>>>>>>     at
>>>>>>>>> sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:55)
>>>>>>>>>     at
>>>>>>>>> sun.nio.ch.SelectionKeyImpl.interestOps(SelectionKeyImpl.java:64)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.DefaultSelectorHandler.unregisterKey0(DefaultSelectorHandler.java:216)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.DefaultSelectorHandler.unregisterKey(DefaultSelectorHandler.java:104)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.AbstractNIOConnection.disableIOEvent(AbstractNIOConnection.java:271)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.transport.TCPNIOTransport.executeDefaultProcessor(TCPNIOTransport.java:730)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.transport.TCPNIOTransport.processReadIoEvent(TCPNIOTransport.java:703)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:648)
>>>>>>>>>     at
>>>>>>>>> com.sun.grizzly.nio.SelectorRunner.fire(SelectorRunner.java:385)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.SelectorRunner.iterateKeyEvents(SelectorRunner.java:317)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>> com.sun.grizzly.nio.SelectorRunner.iterateKeys(SelectorRunner.java:287)
>>>>>>>>>     at
>>>>>>>>> com.sun.grizzly.nio.SelectorRunner.doSelect(SelectorRunner.java:259)
>>>>>>>>>     at
>>>>>>>>> com.sun.grizzly.nio.SelectorRunner.run(SelectorRunner.java:190)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
>>>>>>>>>     at
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
>>>>>>>>>     at java.lang.Thread.run(Thread.java:619)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> 2009/8/12 Sam Crawford <samcrawford_at_gmail.com>:
>>>>>>>>>>
>>>>>>>>>> Brilliant, thank you very much! Hopefully I'll get started on this
>>>>>>>>>> next month and you'll no doubt see me posting lots of questions
>>>>>>>>>> here.
>>>>>>>>>>
>>>>>>>>>> Thanks,
>>>>>>>>>>
>>>>>>>>>> Sam
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> 2009/8/11 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
>>>>>>>>>>>
>>>>>>>>>>> Hi Sam,
>>>>>>>>>>>
>>>>>>>>>>>> I'm currently looking to rewrite our (blocking) tunneling software
>>>>>>>>>>>> using NIO (in an effort to increase scalability), and Grizzly
>>>>>>>>>>>> seems
>>>>>>>>>>>> like it could save me a considerable amount of time. To be clear,
>>>>>>>>>>>> this
>>>>>>>>>>>> is for tunneling arbitrary TCP traffic (not just HTTP!), over both
>>>>>>>>>>>> SSL
>>>>>>>>>>>> and non-SSL.
>>>>>>>>>>>>
>>>>>>>>>>>> Having searched the archives, I see that someone else two years
>>>>>>>>>>>> was
>>>>>>>>>>>> interested in something similar
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> (https://grizzly.dev.java.net/servlets/ReadMsg?listName=users&msgNo=52).
>>>>>>>>>>>> Are the concepts largely the same nowadays? I know the Grizzly
>>>>>>>>>>>> libraries have progressed a great deal since then, so I didn't
>>>>>>>>>>>> want
>>>>>>>>>>>> to
>>>>>>>>>>>> dive too deeply into that if there were more appropriate ways of
>>>>>>>>>>>> achieving this these days.
>>>>>>>>>>>>
>>>>>>>>>>>> Any other pointers would be most welcome!
>>>>>>>>>>>
>>>>>>>>>>> IMHO it should be pretty easy to write simple tunneling
>>>>>>>>>>> application.
>>>>>>>>>>> All you'll need on server side - is to build filter chain and keep
>>>>>>>>>>> client<->server connection map to redirect data among them.
>>>>>>>>>>>
>>>>>>>>>>> I've just created simple tunnel example for Grizzly 2.0, hope it
>>>>>>>>>>> will
>>>>>>>>>>> help.
>>>>>>>>>>> To build them - please use the latest Grizzly 2.0 snapshot [2]
>>>>>>>>>>>
>>>>>>>>>>> WBR,
>>>>>>>>>>> Alexey.
>>>>>>>>>>>
>>>>>>>>>>> [1]
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> https://grizzly.dev.java.net/source/browse/grizzly/branches/2dot0/code/samples/framework-samples/src/main/java/com/sun/grizzly/samples/tunnel/
>>>>>>>>>>> [2]
>>>>>>>>>>>  <groupId>com.sun.grizzly</groupId>
>>>>>>>>>>>  <artifactId>grizzly-framework</artifactId>
>>>>>>>>>>>  <version>2.0.0-SNAPSHOT</version>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Many thanks,
>>>>>>>>>>>>
>>>>>>>>>>>> Sam
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> ---------------------------------------------------------------------
>>>>>>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>>>>>>>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> ---------------------------------------------------------------------
>>>>>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>>>>>>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> ---------------------------------------------------------------------
>>>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>>>>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> ---------------------------------------------------------------------
>>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>>>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> ---------------------------------------------------------------------
>>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>>>>
>>>>>>
>>>>>>
>>>>>> ---------------------------------------------------------------------
>>>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>>>
>>>>>>
>>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>
>>>
>>
>