Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

filling out NettyHttpResponse

- keep alive connections
- cookies
- headers
  • Loading branch information...
commit 189d8e57ebc7b4561cadb4dfae28e7b2e178ef90 1 parent cd63189
Jordan West authored
3  src/main/scala/com/something/lift/LiftChannelHandler.scala
@@ -51,6 +51,7 @@ class LiftChannelHandler(val nettyContext: HTTPNettyContext,
51 51
52 52 override def messageReceived(ctx: ChannelHandlerContext, e: MessageEvent) {
53 53 val request = e.getMessage.asInstanceOf[HttpRequest]
  54 + val keepAlive = HttpHeaders.isKeepAlive(request)
54 55
55 56 if (HttpHeaders.is100ContinueExpected(request)) {
56 57 send100Continue(e);
@@ -65,7 +66,7 @@ class LiftChannelHandler(val nettyContext: HTTPNettyContext,
65 66
66 67
67 68 val httpRequest: HTTPRequest = new NettyHttpRequest(request, ctx, nettyContext, this)
68   - val httpResponse = new NettyHttpResponse(ctx)
  69 + val httpResponse = new NettyHttpResponse(ctx, keepAlive)
69 70
70 71 handleLoanWrappers(service(httpRequest, httpResponse) {
71 72 doNotHandled()
70 src/main/scala/com/something/lift/NettyHttpResponse.scala
... ... @@ -1,10 +1,11 @@
1 1 package com.something.lift
2 2
3   -import org.jboss.netty.handler.codec.http.{HttpResponseStatus, HttpVersion, DefaultHttpResponse, HttpResponse}
4 3 import org.jboss.netty.buffer.ChannelBuffers
5 4 import net.liftweb.http.provider.{HTTPParam, HTTPCookie, HTTPResponse}
6 5 import java.io.OutputStream
7 6 import org.jboss.netty.channel.{ChannelHandlerContext, ChannelFutureListener}
  7 +import org.jboss.netty.handler.codec.http._
  8 +import net.liftweb.http.LiftRules
8 9
9 10 /**
10 11 * Created by IntelliJ IDEA.
@@ -13,40 +14,83 @@ import org.jboss.netty.channel.{ChannelHandlerContext, ChannelFutureListener}
13 14 * Time: 8:41 PM
14 15 */
15 16
16   -class NettyHttpResponse(ctx: ChannelHandlerContext) extends HTTPResponse {
  17 +/**
  18 + * Representation of the HTTPResponseStatus
  19 + *
  20 + * @param ctx - the netty channel handler context
  21 + * @param keepAlive - if true the channel's connection will remain open after response is written, otherwise it will be closed.
  22 + *
  23 + */
  24 +class NettyHttpResponse(ctx: ChannelHandlerContext, keepAlive: Boolean) extends HTTPResponse {
17 25
18 26 lazy val nettyResponse: HttpResponse = {
19 27 val r = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
20 28 r.setContent(ChannelBuffers.dynamicBuffer(1024)) // TODO this is just some random choice, do something more intelligent?
21 29 r
22 30 }
  31 +
  32 + private var cookies = List[HTTPCookie]()
23 33
24   - // FIXME actually add cookies to the response
25   - def addCookies(cookies: List[HTTPCookie]) {}
  34 + def addCookies(cks: List[HTTPCookie]) {
  35 + cookies ++= cks
  36 + }
26 37
27   - // FIXME add session id if/when sessions are supported?
  38 + // FIXME should add session id to url if/when sessions are supported but session id not set in cookies
28 39 def encodeUrl(url: String): String = url
29 40
30   - // FIXME actually set headers
31   - def addHeaders(headers: List[HTTPParam]) {}
  41 + def addHeaders(headers: List[HTTPParam]) {
  42 + val appearOnce = Set(LiftRules.overwrittenReponseHeaders.vend.map(_.toLowerCase):_*)
  43 + for (h <- headers;
  44 + value <- h.values) {
  45 + if (appearOnce.contains(h.name.toLowerCase)) nettyResponse.setHeader(h.name, value)
  46 + else nettyResponse.addHeader(h.name, value)
  47 + }
  48 + }
32 49
33   - def setStatus(status: Int) = nettyResponse.setStatus(HttpResponseStatus.valueOf(status))
  50 + def setStatus(status: Int) {
  51 + nettyResponse.setStatus(HttpResponseStatus.valueOf(status))
  52 + }
34 53
35 54 def getStatus: Int = nettyResponse.getStatus.getCode
36 55
37   - // FIXME
38   - def setStatusWithReason(status: Int, reason: String) = throw new Exception("Implement me")
  56 + // TODO: it is possible to implement this method although netty has no equiv. but more intelligent content buffer management is needed
  57 + def setStatusWithReason(status: Int, reason: String) = throw new Exception("not implemented, there is no equivalent in netty")
39 58
40   - // TODO make better: override other write methods, better flush
  59 + // TODO better flush
41 60 def outputStream: OutputStream = new OutputStream {
42   - // TODO: there is a probably a better impl, by override the other write methods.
  61 +
43 62 def write(i: Int) {
44 63 nettyResponse.getContent.writeByte(i)
45 64 }
46 65
  66 + override def write(bytes: Array[Byte]) {
  67 + nettyResponse.getContent.writeBytes(bytes)
  68 + }
  69 +
  70 + override def write(bytes: Array[Byte], offset: Int, len: Int) {
  71 + nettyResponse.getContent.writeBytes(bytes, offset, len)
  72 + }
  73 +
47 74 override def flush() {
  75 + if (cookies.length > 0) writeCookiesToResponse()
48 76 val future = ctx.getChannel.write(nettyResponse)
49   - future.addListener(ChannelFutureListener.CLOSE)
  77 + if (!keepAlive) future.addListener(ChannelFutureListener.CLOSE)
50 78 }
51 79 }
  80 +
  81 + private def writeCookiesToResponse() {
  82 + val encoder = new CookieEncoder(true)
  83 +
  84 + for (c <- cookies) {
  85 + val cookie = new DefaultCookie(c.name, c.value openOr null)
  86 + c.domain foreach (cookie.setDomain(_))
  87 + c.path foreach (cookie.setPath(_))
  88 + c.maxAge foreach (cookie.setMaxAge(_))
  89 + c.version foreach (cookie.setVersion(_))
  90 + c.secure_? foreach (cookie.setSecure(_))
  91 + c.httpOnly foreach (cookie.setHttpOnly(_))
  92 + }
  93 +
  94 + addHeaders(HTTPParam(HttpHeaders.Names.SET_COOKIE, encoder.encode) :: Nil)
  95 + }
52 96 }

0 comments on commit 189d8e5

Please sign in to comment.
Something went wrong with that request. Please try again.