working with jetty and with netty

317 views
Skip to first unread message

Henry Story

unread,
Oct 22, 2011, 3:10:29 PM10/22/11
to unfilter...@googlegroups.com
As I wrote up in a post yesterday, I found that TLS renegotiation is easy to do with netty, and probably impossible with jetty as it stands now - and furthermore the netty code is also elegant. So I want to transition from using jetty to netty, and if possible be able to develop for both simultaneously. So I set out to do this today, and it worked, but I am wondering if it could be done better.

First we have a very simple intent that is jetty/netty independent (the line numbers point to the source in the rich version, otherwise see [1])

trait X509view[A,B]  {
    40    implicit def wc: WebCache
    41    implicit def manif: Manifest[A]
    43     def intent: Cycle.Intent[A, B] =  {
    44       case req @ Path(path) if path startsWith "/test/auth/x509" =>
    45         Ok ~> ContentType("text/html") ~> Html(
    46           <html><head><title>Authentication Page</title></head>
    47         { req match {
    48           case X509Claim(xclaim) => <body>
    49             <h1>Authentication Info received</h1>
    50             <p>You were identified with the following WebIDs</p>
    51              <ul>{xclaim.webidclaims.filter(cl=>cl.verified).map(p=> <li>{p.webId}</li>)}</ul>
    52             <p>You sent the following certificate</p>
    53             <pre>{xclaim.cert.toString}</pre>
    54           </body>
    55           case _ => <body><p>We received no Authentication information</p></body>
    56         }
    57           }</html>)
    59       }
    61 }

The X509View is parameterized on types [A,B] where these are
 - for jetty and other servlet containers: A=HttpServletRequest, B=HttpServletResponse
 - for netty A=ReceivedMessage B=HttpResponse
Those types are needed only when code requires a requests' underlying object. There is no direct call to those classes visible here.

But in fact hidden behind the X509Claim(xclaim) on line 48 there is a request to a Certs class that does require access to the underlying components. I could not write overloaded functions on the parameterized types due to type erasure: scala and java just see them as the same. So I had to use the Manifest technique:

    33 object Certs {
    36     def unapplySeq[T](r: HttpRequest[T]) (implicit m: Manifest[T]) : Option[IndexedSeq[Certificate]] =  {
    37       if (m <:< manifest[HttpServletRequest]) unapplyServletRequest(r.asInstanceOf[HttpRequest[HttpServletRequest]])
    38       else if (m <:< manifest[ReceivedMessage]) unapplyReceivedMessage(r.asInstanceOf[HttpRequest[ReceivedMessage]])
    39       else None //todo: should perhaps throw an exception here.
    40     }

That really does not look that beautiful. It's what I managed to come up with first. Essentially we use the manifest of the Types passed in order to decide which implementation to use. This is why we needed the Manifest to be passed in the previously discussed X509View class (line 41).

This is the used in the jetty server like this:

   103     // configures and launches a Jetty server
   104     service.filter(new FilterLogger(logger)).
   105       context("/public"){ ctx:ContextBuilder =>
   106         ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
   107     }.
   108       filter(app.plan).
   109       filter(Planify(x509v.intent)).
   110       filter(new EchoPlan().plan).run()
   112   }
   114   object x509v extends X509view[HttpServletRequest,HttpServletResponse] {
   115     def wc = webCache
   116     def manif = manifest[HttpServletRequest]
   117   }

The X509v object extends the X509view and sets the implicit manifest and cache parameters, which are then passed to the filter.

The netty server does something similar, but requires to be composed with a few more traits Notice here the X509view parameters are specified with the ReceivedMessage and HttpResonse classes.

    65      // configures and launches a Netty server
    66      service.plan( x509v ).run()
    68    }
    70   object x509v extends  cycle.Plan  with cycle.ThreadPool with ServerErrorResponse with X509view[ReceivedMessage,HttpResponse] {
    71     def wc = webCache
    72     def manif = manifest[ReceivedMessage]
    73   }


Perhaps someone knows of a better way of doing this, and otherwise it may be helpful to other people coming to unfiltered at some later time.




PS. On IRC I had a conversation on unfiltered. Perhaps this will be helpful to someone here. I am not exactly sure what dobblego was driving at.

dobblego:you shouldn't be losing types by writing appropriate abstractions
[11:08am]dobblego:if you are, then you aren't writing appropriate abstractions, therefore you're not
[11:10am]dobblego:I can see lots of opportunity for combinators there, some of which should be compiler-generated :(
[11:10am]dobblego:shame about the Iterators
[11:12amlopex left the chat room.
[11:14am]bblfish:well that's cool. I think it's worth looking at their framework. Perhaps an opportunity to improve it
[11:15am]bblfish:it would be fun it you put together an idea of how to do that an put it forward to their mailing list
[11:15am]dobblego:just to demonstrate the point, you could write this function: mapParameterValues[A](r: HttpRequest[A], f: String => String): HttpRequest[A]
[11:15am]dobblego:map that function across the result of calling parameterValues and copy all the other fields
[11:16am]dobblego:there are zillions of opportunities there, but alas, it falls down quite a lot with the unfortunate choice of data structures
[11:16am]bblfish:I think like all good ideas it starts with a bit of code, then one abstracts and makes it better
[11:17am]bblfish:they probably wanted to get something going quickly to see if it would work
[11:17am]bblfish:then they added pieces
[11:17am]bblfish:It would be great if you put a few of these thoughts to the group https://groups.google.com/forum/#!forum/unfiltered-scala
[11:17am]dobblego:right, but some of the properties of this structure make abstraction difficult/impossible
[11:17am]bblfish:either they improve the next version or perhaps someone will
[11:17am]dobblego:nevertheless, you still shouldn't be pattern-matching
[11:18am]dobblego:if I had the time to fix it, I'd just write it


Social Web Architect
http://bblfish.net/

n8han

unread,
Oct 23, 2011, 8:39:47 PM10/23/11
to unfilter...@googlegroups.com


On Saturday, October 22, 2011 3:10:29 PM UTC-4, Henry Story wrote:
As I wrote up in a post yesterday, I found that TLS renegotiation is easy to do with netty, and probably impossible with jetty as it stands now - and furthermore the netty code is also elegant. So I want to transition from using jetty to netty, and if possible be able to develop for both simultaneously. So I set out to do this today, and it worked, but I am wondering if it could be done better.
 
This is very interesting to the project and something I think we would be very happy to include when it's finished.

First we have a very simple intent that is jetty/netty independent (the line numbers point to the source in the rich version, otherwise see [1])
 
[...]

But in fact hidden behind the X509Claim(xclaim) on line 48 there is a request to a Certs class that does require access to the underlying components. I could not write overloaded functions on the parameterized types due to type erasure: scala and java just see them as the same. So I had to use the Manifest technique:

In this case I think we do not want to work around erasure even though we can. The intent is typed as if it can work with any server binding, but as you say there is hidden internal code that does depend on a known binding. It would be better to have two versions of this intent typed for each server binding, so that later when a mina or grizzly binding becomes available it is clear to the programmer and the compiler that the intent will not be compatible. It should still be possible to share code between the subtypes of the intent.
 
PS. On IRC I had a conversation on unfiltered. Perhaps this will be helpful to someone here. I am not exactly sure what dobblego was driving at.

The interface uses iterators for parameters because they are iterators in the servlet spec and accessing them has side effects. We have kept the binding interfaces fairly close to what they are wrapping, and use the request extractors to supply preferred data structures.

Nathan

Doug Tangren

unread,
Oct 23, 2011, 8:48:07 PM10/23/11
to unfilter...@googlegroups.com
On Sun, Oct 23, 2011 at 8:39 PM, n8han <nat...@technically.us> wrote:


On Saturday, October 22, 2011 3:10:29 PM UTC-4, Henry Story wrote:
As I wrote up in a post yesterday, I found that TLS renegotiation is easy to do with netty, and probably impossible with jetty as it stands now - and furthermore the netty code is also elegant.

Please let us know what your specific pain points are. The Jetty version was simpler to author since it had a build in ssl connector but adds an extra layer for you do dig into and the Netty one was more difficult author because I had to hand author it with a side benefit of having no extra layer for you to dig into. If it's possible maybe we should roll whats common into the jetty version. It may be duplicating support that jetty has built in though.

Henry Story

unread,
Oct 24, 2011, 3:17:53 AM10/24/11
to Peter Williams, unfilter...@googlegroups.com

On 24 Oct 2011, at 02:36, Peter Williams wrote:

im not aware of joining this list. Is google violating my privacy (again)? Is it acting without authority (again)?

No, Peter. It's just a weird way e-mail works. You got it from the WebID mailing list. Initially I had just
re-sent it to that list, then I changed the text and forgot to copy and paste the text into a new e-mail
Here you can see it:


So it's one of those weird threading issues you usually don't care too much about.

Henry

Henry Story

unread,
Oct 24, 2011, 9:55:48 AM10/24/11
to unfilter...@googlegroups.com
On 24 Oct 2011, at 02:48, Doug Tangren wrote:

On Sun, Oct 23, 2011 at 8:39 PM, n8han <nat...@technically.us> wrote:


On Saturday, October 22, 2011 3:10:29 PM UTC-4, Henry Story wrote:
As I wrote up in a post yesterday, I found that TLS renegotiation is easy to do with netty, and probably impossible with jetty as it stands now - and furthermore the netty code is also elegant.

Please let us know what your specific pain points are.

I think that it is not really possible to do TLS renegotiation with jetty. 
I asked on the jetty mailing list in August


but I did not recognise an answer. It may just be that I did not look in the right place at the time, as it was the first time I was trying to do it. Perhaps I would find the answer immediately looking at  it now?

The Jetty version was simpler to author since it had a build in ssl connector but adds an extra layer for you do dig into and the Netty one was more difficult author because I had to hand author it with a side benefit of having no extra layer for you to dig into. If it's possible maybe we should roll whats common into the jetty version. It may be duplicating support that jetty has built in though.

I suppose the first thing to work out is if one can do TLS renegotiation programatically in jetty. If that is possible then that would be very cool, and then one can start looking at what building a common API would look like. Having a common API would then remove the need for me to genericise the intents as I am forced to do now whenever I want to do deeper TLS layered work. 

So again this is how I could request a TLS renegotiation in netty.  It is pretty cool to tell the truth, and using it I feel quite in control. 

    58        req.underlying.context.getPipeline.get(classOf[org.jboss.netty.handler.ssl.SslHandler])  match {
    59           case sslh: SslHandler => {
    60             sslh.setEnableRenegotiation(true)
    61             sslh.getEngine.setWantClientAuth(true)
    62             val future = sslh.handshake()
    63             future.await(5000)
    64             val res = if (future.isDone) {
    65               var r ="We are in login & we have an https handler! "
    66               if (future.isSuccess)
    67                 r +=  "\r\n"+"SSL handchake Successful. Did we get the certificate? \r\n\r\n"+certAvailable(sslh)
    68               else {
    69                 r += "\r\n handshake failed. Cause \r\n" +future.getCause
    70               }
    71               r
    72             } else {
    73               "Still waiting for requested certificate"
    74             }
    75             ResponseString(res)
    76            }
    77           case _ =>ResponseString("We are in login but no https handler!")
    78        }


There is another way of doing that with RESTlets as you can see from my code in the session renegotiation directory here

Henry Story

unread,
Oct 24, 2011, 10:22:40 AM10/24/11
to unfilter...@googlegroups.com
On 24 Oct 2011, at 02:39, n8han wrote:



On Saturday, October 22, 2011 3:10:29 PM UTC-4, Henry Story wrote:
As I wrote up in a post yesterday, I found that TLS renegotiation is easy to do with netty, and probably impossible with jetty as it stands now - and furthermore the netty code is also elegant. So I want to transition from using jetty to netty, and if possible be able to develop for both simultaneously. So I set out to do this today, and it worked, but I am wondering if it could be done better.
 
This is very interesting to the project and something I think we would be very happy to include when it's finished.

that would be great :-)


First we have a very simple intent that is jetty/netty independent (the line numbers point to the source in the rich version, otherwise see [1])
 
[...]

But in fact hidden behind the X509Claim(xclaim) on line 48 there is a request to a Certs class that does require access to the underlying components. I could not write overloaded functions on the parameterized types due to type erasure: scala and java just see them as the same. So I had to use the Manifest technique:

In this case I think we do not want to work around erasure even though we can. The intent is typed as if it can work with any server binding, but as you say there is hidden internal code that does depend on a known binding. It would be better to have two versions of this intent typed for each server binding, so that later when a mina or grizzly binding becomes available it is clear to the programmer and the compiler that the intent will not be compatible. It should still be possible to share code between the subtypes of the intent.

yes, but then I'd have to write intents twice, which is ugly and waste of code. I know the current solution is a bit of a hack, but it is less of one than writing intents twice. As it stands currently I can run the same tests on the same intents. (btw. why are these called intents? ).

So it is nice that one can do this at all. It shows the flexibility of the framework. But I would not be surprised if it turns out in scala that there are a few other better solutions that we have not thought of.  Or perhaps a future version of Scala will simply solve the problem...

 
PS. On IRC I had a conversation on unfiltered. Perhaps this will be helpful to someone here. I am not exactly sure what dobblego was driving at.

The interface uses iterators for parameters because they are iterators in the servlet spec and accessing them has side effects. We have kept the binding interfaces fairly close to what they are wrapping, and use the request extractors to supply preferred data structures.

Nathan

Nathan Hamblen

unread,
Oct 24, 2011, 10:34:33 AM10/24/11
to unfilter...@googlegroups.com
On 10/24/2011 10:22 AM, Henry Story wrote:
>
> yes, but then I'd have to write intents twice, which is ugly and waste
> of code. I know the current solution is a bit of a hack, but it is
> less of one than writing intents twice.

It would require different objects but it should be possible to share
most of the code between them. When you are finished with the
implementation I'll take a crack at that, unless someone else will.

> As it stands currently I can run the same tests on the same intents.
> (btw. why are these called intents? ).

The thinking is that an "intent" is the precursor to a "plan".

Nathan

Henry Story

unread,
Nov 8, 2011, 11:43:23 AM11/8/11
to unfilter...@googlegroups.com
I was away for a week, so sorry for the late reply.


On Monday, 24 October 2011 16:34:33 UTC+2, n8han wrote:
On 10/24/2011 10:22 AM, Henry Story wrote:
>
> yes, but then I'd have to write intents twice, which is ugly and waste
> of code. I know the current solution is a bit of a hack, but it is
> less of one than writing intents twice.

It would require different objects but it should be possible to share
most of the code between them. When you are finished with the
implementation I'll take a crack at that, unless someone else will.

Yes, the implementation I have is stable now, and the test suite even works. :-)

It is in 


in the "webid" branch.

 

> As it stands currently I can run the same tests on the same intents.
> (btw. why are these called intents? ).

The thinking is that an "intent" is the precursor to a "plan".

Intent also suggests self awareness, and belief/desire psychology - often called BDI for that reason. 
But well, naming is a really difficult art. I would not know what to call it either at this point. :-)
 

Nathan

Reply all
Reply to author
Forward
0 new messages