Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 7d89340f0a
Fetching contributors…

Cannot retrieve contributors at this time

1014 lines (850 sloc) 34.19 kB
/*
* Copyright 2007-2011 WorldWide Conferencing, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.liftweb
package http
import xml.{Node, NodeSeq, Group}
import common._
import actor._
import util._
import util.Helpers._
import js._
import auth._
import provider._
import json.JsonAST.JValue
class LiftServlet extends Loggable {
private var servletContext: HTTPContext = null
def this(ctx: HTTPContext) = {
this ()
this.servletContext = ctx
}
def getContext: HTTPContext = servletContext
def destroy = {
try {
LiftRules.ending = true
tryo {
SessionMaster.shutDownAllSessions()
}
val cur = millis
// wait 10 seconds or until the request count is zero
while (LiftRules.reqCnt.get > 0 && (millis - cur) < 10000L) {
Thread.sleep(20)
}
tryo {
Schedule.shutdown
}
tryo {
LAScheduler.shutdown()
}
tryo {
LAPinger.shutdown
}
LiftRules.runUnloadHooks()
logger.debug("Destroyed Lift handler.")
// super.destroy
} catch {
case e => logger.error("Destruction failure", e)
}
}
def init = {
LiftRules.ending = false
}
def getLiftSession(request: Req): LiftSession = LiftRules.getLiftSession(request)
private def wrapState[T](req: Req, session: Box[LiftSession])(f: => T): T = {
session match {
case Full(ses) => S.init(req, ses)(f)
case _ => CurrentReq.doWith(req)(f)
}
}
private def handleGenericContinuation(reqOrg: Req, resp: HTTPResponse, session: Box[LiftSession], func: ((=> LiftResponse) => Unit) => Unit): Boolean = {
val req = if (null eq reqOrg) reqOrg else reqOrg.snapshot
def runFunction(doAnswer: LiftResponse => Unit) {
Schedule.schedule(() => {
val answerFunc: (=> LiftResponse) => Unit = response =>
doAnswer(wrapState(req, session)(response))
func(answerFunc)
}, 5 millis)
}
if (reqOrg.request.suspendResumeSupport_?) {
runFunction(liftResponse => {
// do the actual write on a separate thread
Schedule.schedule(() => {
reqOrg.request.resume(reqOrg, liftResponse)
}, 0 seconds)
})
reqOrg.request.suspend(cometTimeout)
false
} else {
val future = new LAFuture[LiftResponse]
runFunction(answer => future.satisfy(answer))
future.get(cometTimeout) match {
case Full(answer) => sendResponse(answer, resp, req); true
case _ => false
}
}
}
/**
* Processes the HTTP requests
*/
def service(req: Req, resp: HTTPResponse): Boolean = {
try {
def doIt: Boolean = {
if (LiftRules.logServiceRequestTiming) {
logTime {
val ret = doService(req, resp)
val msg = "Service request (" + req.request.method + ") " + req.request.uri + " returned " + resp.getStatus + ","
(msg, ret)
}
} else {
doService(req, resp)
}
}
req.request.resumeInfo match {
case None => doIt
case r if r eq null => doIt
case Some((or: Req, r: LiftResponse)) if (req.path == or.path) => sendResponse(r, resp, req); true
case _ => doIt
}
} catch {
case rest.ContinuationException(theReq, sesBox, func) =>
handleGenericContinuation(theReq, resp, sesBox, func); true // we have to return true to hold onto the request
case e if e.getClass.getName.endsWith("RetryRequest") => throw e
case e => logger.info("Request for " + req.request.uri + " failed " + e.getMessage, e); throw e
}
}
private def flatten(in: List[Any]): List[Any] = in match {
case Nil => Nil
case Some(x: AnyRef) :: xs => x :: flatten(xs)
case Full(x: AnyRef) :: xs => x :: flatten(xs)
case (lst: Iterable[_]) :: xs => lst.toList ::: flatten(xs)
case (x: AnyRef) :: xs => x :: flatten(xs)
case x :: xs => flatten(xs)
}
private def authPassed_?(req: Req): Boolean = {
val checkRoles: (Role, List[Role]) => Boolean = {
case (resRole, roles) => (false /: roles)((l, r) => l || resRole.isChildOf(r.name))
}
val role = NamedPF.applyBox(req, LiftRules.httpAuthProtectedResource.toList)
role.map(_ match {
case Full(r) =>
LiftRules.authentication.verified_?(req) match {
case true => checkRoles(r, userRoles.get)
case _ => false
}
case _ => LiftRules.authentication.verified_?(req)
}) openOr true
}
private val recent: LRUMap[String, Int] = new LRUMap(2000)
private def registerRecentlyChecked(id: String): Unit =
synchronized {
val next = recent.get(id) match {
case Full(x) => x + 1
case _ => 1
}
recent(id) = next
}
private def recentlyChecked(id: Box[String]): Int = synchronized {
id.flatMap(recent.get).openOr(0)
}
/**
* Service the HTTP request
*/
def doService(req: Req, response: HTTPResponse): Boolean = {
var tmpStatelessHolder: Box[Box[LiftResponse]] = Empty
tryo {
LiftRules.onBeginServicing.toList.foreach(_(req))
}
def hasSession(idb: Box[String]): Boolean = {
idb.flatMap {
id =>
registerRecentlyChecked(id)
SessionMaster.getSession(id, Empty)
}.isDefined
}
val wp = req.path.wholePath
val pathLen = wp.length
def isComet: Boolean = {
if (pathLen < 2) false
else {
val kindaComet = wp.head == LiftRules.cometPath
val cometScript = (pathLen >= 3 && kindaComet &&
wp(2) == LiftRules.cometScriptName())
(kindaComet && !cometScript) && req.acceptsJavaScript_?
}
}
def isAjax: Boolean = {
if (pathLen < 2) false
else {
val kindaAjax = wp.head == LiftRules.ajaxPath
val ajaxScript = kindaAjax &&
wp(1) == LiftRules.ajaxScriptName()
(kindaAjax && !ajaxScript) && req.acceptsJavaScript_?
}
}
val sessionIdCalc = new SessionIdCalc(req)
val resp: Box[LiftResponse] = try {
// if the application is shutting down, return a 404
if (LiftRules.ending) {
LiftRules.notFoundOrIgnore(req, Empty)
} else if (!authPassed_?(req)) {
Full(LiftRules.authentication.unauthorizedResponse)
} else if (LiftRules.redirectAsyncOnSessionLoss && !hasSession(sessionIdCalc.id) && (isComet || isAjax)) {
val theId = sessionIdCalc.id
// okay after 2 attempts to redirect, just ignore calls to the
// async URL
if (recentlyChecked(theId) > 1) {
Empty
} else {
val cmd =
if (isComet)
js.JE.JsRaw(LiftRules.noCometSessionCmd.vend.toJsCmd + ";lift_toWatch = {};").cmd
else
js.JE.JsRaw(LiftRules.noAjaxSessionCmd.vend.toJsCmd).cmd
Full(new JsCommands(cmd :: Nil).toResponse)
}
}
// if the request is matched is defined in the stateless table, dispatch
if (S.statelessInit(req) {
tmpStatelessHolder = NamedPF.applyBox(req,
LiftRules.statelessDispatch.toList).map(_.apply() match {
case Full(a) => Full(LiftRules.convertResponse((a, Nil, S.responseCookies, req)))
case r => r
});
tmpStatelessHolder.isDefined
}) {
val f = tmpStatelessHolder.open_!
f match {
case Full(v) => Full(v)
case Empty => LiftRules.notFoundOrIgnore(req, Empty)
case f: Failure => Full(req.createNotFound(f))
}
} else {
// otherwise do a stateful response
val liftSession = getLiftSession(req)
def doSession(r2: Req, s2: LiftSession, continue: Box[() => Nothing]): () => Box[LiftResponse] = {
try {
S.init(r2, s2) {
dispatchStatefulRequest(S.request.open_!, liftSession, r2, continue)
}
} catch {
case cre: ContinueResponseException =>
r2.destroyServletSession()
doSession(r2, getLiftSession(r2), Full(cre.continue))
}
}
val lzy: () => Box[LiftResponse] = doSession(req, liftSession, Empty)
lzy()
}
} catch {
case foc: LiftFlowOfControlException => throw foc
case e: Exception if !e.getClass.getName.endsWith("RetryRequest") => {
val bundle = (Props.mode, req, e)
S.assertExceptionThrown()
NamedPF.applyBox(bundle, LiftRules.exceptionHandler.toList)
}
}
tryo {
LiftRules.onEndServicing.toList.foreach(_(req, resp))
}
resp match {
case Full(EmptyResponse) =>
true
case Full(cresp) =>
sendResponse(cresp, response, req)
true
case _ => {
false
}
}
}
private def dispatchStatefulRequest(req: Req,
liftSession: LiftSession,
originalRequest: Req,
continuation: Box[() => Nothing]): () => Box[LiftResponse] = {
val toMatch = req
val dispatch: (Boolean, Box[LiftResponse]) =
NamedPF.find(toMatch, LiftRules.dispatchTable(req.request)) match {
case Full(pf) =>
LiftSession.onBeginServicing.foreach(_(liftSession, req))
val ret: (Boolean, Box[LiftResponse]) =
try {
try {
// run the continuation in the new session
// if there is a continuation
continuation match {
case Full(func) => {
func()
S.redirectTo("/")
}
case _ => // do nothing
}
liftSession.runParams(req)
S.functionLifespan(true) {
pf(toMatch)() match {
case Full(v) =>
(true, Full(LiftRules.convertResponse((liftSession.checkRedirect(v), Nil,
S.responseCookies, req))))
case Empty =>
(true, LiftRules.notFoundOrIgnore(req, Full(liftSession)))
case f: Failure =>
(true, Full(liftSession.checkRedirect(req.createNotFound(f))))
}
}
} catch {
case ite: java.lang.reflect.InvocationTargetException if (ite.getCause.isInstanceOf[ResponseShortcutException]) =>
(true, Full(liftSession.handleRedirect(ite.getCause.asInstanceOf[ResponseShortcutException], req)))
case rd: net.liftweb.http.ResponseShortcutException => (true, Full(liftSession.handleRedirect(rd, req)))
}
} finally {
if (S.functionMap.size > 0) {
liftSession.updateFunctionMap(S.functionMap, S.renderVersion, millis)
S.clearFunctionMap
}
liftSession.notices = S.getNotices
}
LiftSession.onEndServicing.foreach(_(liftSession, req,
ret._2))
ret
case _ => (false, Empty)
}
val wp = req.path.wholePath
if (LiftRules.enableContainerSessions && !req.stateless_?) {
req.request.session
}
def respToFunc(in: Box[LiftResponse]): () => Box[LiftResponse] = {
val ret = in.map(LiftRules.performTransform)
() => ret
}
val toReturn: () => Box[LiftResponse] =
if (dispatch._1) {
respToFunc(dispatch._2)
} else if (wp.length == 3 && wp.head == LiftRules.cometPath &&
wp(2) == LiftRules.cometScriptName()) {
respToFunc(LiftRules.serveCometScript(liftSession, req))
} else if ((wp.length >= 1) && wp.head == LiftRules.cometPath) {
handleComet(req, liftSession, originalRequest) match {
case Left(x) => respToFunc(x)
case Right(x) => x
}
} else if (wp.length == 2 && wp.head == LiftRules.ajaxPath &&
wp(1) == LiftRules.ajaxScriptName()) {
respToFunc(LiftRules.serveAjaxScript(liftSession, req))
} else if (wp.length >= 1 && wp.head == LiftRules.ajaxPath) {
respToFunc(handleAjax(liftSession, req))
} else {
respToFunc(liftSession.processRequest(req, continuation))
}
toReturn
}
private object AjaxVersions {
def unapply(ajaxPathPart: String) : Option[(String,Int)] = {
val dash = ajaxPathPart.indexOf("-")
if (dash > -1 && ajaxPathPart.length > dash + 1)
Some(
(ajaxPathPart.substring(0, dash),
ajaxPathPart.charAt(dash + 1))
)
else
None
}
}
/**
* Extracts two versions from a given AJAX path:
* - The RenderVersion, which is used for GC purposes.
* - The requestVersion, which lets us determine if this is
* a request we've already dealt with or are currently dealing
* with (so we don't rerun the associated handler). See
* handleVersionedAjax for more.
*
* The requestVersion is passed to the function that is passed in.
*/
private def extractVersions[T](path: List[String])(f: (Box[Int]) => T): T = {
path match {
case first :: AjaxVersions(renderVersion, requestVersion) :: _ =>
RenderVersion.doWith(renderVersion)(f(Full(requestVersion)))
case _ => f(Empty)
}
}
/**
* An actor that manages AJAX continuations from container (Jetty style).
*/
class AjaxContinuationActor(request: Req, session: LiftSession,
onBreakout: Box[LiftResponse] => Unit) extends LiftActor {
private var response: Box[LiftResponse] = Empty
private var done = false
def messageHandler = {
case AjaxResponseComplete(completeResponse) =>
response = completeResponse
LAPinger.schedule(this, BreakOut(), 5 millis)
case BreakOut() if ! done =>
done = true
session.exitComet(this)
onBreakout(response)
case _ =>
}
override def toString = "AJAX Continuation Actor"
}
private case class AjaxResponseComplete(response: Box[LiftResponse])
private lazy val ajaxPostTimeout: Long = LiftRules.ajaxPostTimeout * 1000L
/**
* Runs the actual AJAX processing. This includes handling __lift__GC,
* or running the parameters in the session. onComplete is run when the
* AJAX request has completed with a response that is meant for the
* user. In cases where the request is taking too long to respond,
* an LAFuture may be used to delay the real response (and thus the
* invocation of onComplete) while this function returns an empty
* response.
*/
private def runAjax(requestState: Req,
liftSession: LiftSession,
onCompleteFn: Box[(LiftResponse)=>Unit]): Box[LiftResponse] = {
// We don't want to call the onComplete function if we're returning
// an error response, as if there is a comet timeout while
// processing. This is because the onComplete function is meant to
// indicate a successful completion, and will short-circuit any
// other AJAX requests with the same version with the same
// response. Error responses are specific to the AJAX request,
// rather than to the version of the AJAX request. Successful
// responses are for all AJAX requests with the same version.
var callCompleteFn = true
val ret = try {
requestState.param("__lift__GC") match {
case Full(_) =>
val renderVersion = RenderVersion.get
liftSession.updateFuncByOwner(renderVersion, millis)
Full(JavaScriptResponse(js.JsCmds.Noop))
case _ =>
try {
val what = flatten(try {
liftSession.runParams(requestState)
} catch {
case ResponseShortcutException(_, Full(to), _) =>
import js.JsCmds._
List(RedirectTo(to))
})
def processResponse(response: List[Any]): LiftResponse = {
val what2 = response.flatMap {
case js: JsCmd => List(js)
case jv: JValue => List(jv)
case n: NodeSeq => List(n)
case js: JsCommands => List(js)
case r: LiftResponse => List(r)
// This strange case comes from ajax requests run in
// a comet context that don't complete in the
// cometTimeout time frame. The js is the result of the
// comet processing timeout handler, while the future
// gives us access to the eventual result of the ajax
// request.
case (js, future: LAFuture[List[Any]]) if onCompleteFn.isDefined =>
// Wait for the future on a separate thread.
Schedule.schedule(() => {
val result = flatten(future.get)
onCompleteFn.foreach(_(processResponse(result)))
}, 0 seconds)
// But this request is done for. Return the comet
// processing timeout result, but don't mark the
// request complete; that happens whenever we satisfy
// the future above.
callCompleteFn = false
List(js)
case s => Nil
}
what2 match {
case (json: JsObj) :: Nil => JsonResponse(json)
case (jv: JValue) :: Nil => JsonResponse(jv)
case (js: JsCmd) :: xs => {
(JsCommands(S.noticesToJsCmd :: Nil) &
((js :: xs).flatMap {
case js: JsCmd => List(js)
case _ => Nil
}.reverse) &
S.jsToAppend).toResponse
}
case (n: Node) :: _ => XmlResponse(n)
case (ns: NodeSeq) :: _ => XmlResponse(Group(ns))
case (r: LiftResponse) :: _ => r
case _ => JsCommands(S.noticesToJsCmd :: JsCmds.Noop :: S.jsToAppend).toResponse
}
}
val ret: LiftResponse = processResponse(what)
LiftRules.cometLogger.debug("AJAX Response: " + liftSession.uniqueId + " " + ret)
Full(ret)
} finally {
if (S.functionMap.size > 0) {
liftSession.updateFunctionMap(S.functionMap, RenderVersion.get, millis)
S.clearFunctionMap
}
}
}
} catch {
case foc: LiftFlowOfControlException => throw foc
case e => S.assertExceptionThrown() ; NamedPF.applyBox((Props.mode, requestState, e), LiftRules.exceptionHandler.toList);
}
for {
response <- ret if callCompleteFn
onComplete <- onCompleteFn
} {
onComplete(response)
}
ret
}
/**
* Handles the details of versioned AJAX requests. If the version is
* Empty, runs the request through a normal AJAX flow with no
* continuations or special handling. Repeated calls will cause
* repeated evaluation.
*
* If the version is Full, the request is tracked. Subsequent requests
* for the same version will suspend until a response is available,
* then they will return the response.
*
* cont is the AjaxContinuationActor for this request, and
* suspendRequest is the function that will suspend the request.
* These are present to support non-continuation containers; these
* will present a no-op to suspendRequest.
*/
private def handleVersionedAjax(handlerVersion: Box[Int],
cont: AjaxContinuationActor,
suspendRequest: () => Any,
requestState: Req,
liftSession: LiftSession): Box[LiftResponse] = {
handlerVersion match {
case Full(handlerVersion) =>
val renderVersion = RenderVersion.get
val toReturn: Box[Box[LiftResponse]] =
liftSession.withAjaxRequests { currentAjaxRequests =>
currentAjaxRequests.get(renderVersion).collect {
case AjaxRequestInfo(storedVersion, _, pendingActors, _) if handlerVersion != storedVersion =>
// Break out of any actors for the stale version.
pendingActors.foreach(_ ! BreakOut())
// Evict the older version's info.
currentAjaxRequests +=
(renderVersion ->
AjaxRequestInfo(handlerVersion, Empty, cont :: Nil, millis))
suspendRequest()
Empty // no response available, triggers the actual AJAX computation below
case AjaxRequestInfo(storedVersion, existingResponseBox @ Full(_), _, _) =>
existingResponseBox // return the Full response Box
case AjaxRequestInfo(storedVersion, _, pendingActors, _) =>
currentAjaxRequests +=
(renderVersion ->
AjaxRequestInfo(handlerVersion, Empty, cont :: pendingActors, millis))
suspendRequest()
Full(Full(EmptyResponse))
} openOr {
currentAjaxRequests +=
(renderVersion ->
AjaxRequestInfo(handlerVersion, Empty, cont :: Nil, millis))
suspendRequest()
Empty // no response available, triggers the actual AJAX computation below
}
}
toReturn or {
Full(runAjax(requestState, liftSession, Full((result: LiftResponse) => {
// When we get the response, synchronizedly check that the
// versions are still the same in the map, and, if so, update
// any waiting actors then clear the actor list and update the
// request info to include the response in case any other
// requests come in with this version.
liftSession.withAjaxRequests { currentAjaxRequests =>
currentAjaxRequests.get(renderVersion).collect {
case AjaxRequestInfo(storedVersion, _, pendingActors, _) if storedVersion == handlerVersion =>
pendingActors.foreach(_ ! AjaxResponseComplete(Full(result)))
currentAjaxRequests +=
(renderVersion ->
AjaxRequestInfo(handlerVersion, Full(Full(result)), Nil, millis))
}
}
})))
} openOr Empty
case _ =>
runAjax(requestState, liftSession, Empty)
}
}
/**
* Kick off AJAX handling. Extracts relevant versions and handles the
* begin/end servicing requests, as well as generation of
* ContinuationActors and choosing between continuation and
* continuationless request handling.
*/
private def handleAjax(liftSession: LiftSession,
requestState: Req): Box[LiftResponse] = {
extractVersions(requestState.path.partPath) { handlerVersion =>
LiftRules.cometLogger.debug("AJAX Request: " + liftSession.uniqueId + " " + requestState.params)
tryo {
LiftSession.onBeginServicing.foreach(_(liftSession, requestState))
}
def suspendingActor = {
new AjaxContinuationActor(requestState, liftSession,
response => {
requestState.request.resume((requestState, S.init(requestState, liftSession)
(response.map(LiftRules.performTransform) openOr EmptyResponse)))})
}
def waitingActorForFuture(future: LAFuture[Box[LiftResponse]]) = {
new AjaxContinuationActor(requestState, liftSession,
response => future.satisfy(response))
}
val possibleFuture =
if (requestState.request.suspendResumeSupport_?)
Empty
else
Full(new LAFuture[Box[LiftResponse]])
val (cont, suspendRequest) =
possibleFuture.map { f =>
(waitingActorForFuture(f), () => ())
} openOr {
(suspendingActor, () => requestState.request.suspend(ajaxPostTimeout + 500L))
}
val ret: Box[LiftResponse] = try {
liftSession.enterComet(cont -> requestState)
val result: Box[LiftResponse] =
handleVersionedAjax(handlerVersion, cont, suspendRequest, requestState, liftSession)
possibleFuture.map(_.get(ajaxPostTimeout) match {
case Full(response) => response
case _ => Empty
}) openOr result
} finally {
if (! requestState.request.suspendResumeSupport_?)
liftSession.exitComet(cont)
}
tryo {
LiftSession.onEndServicing.foreach(_(liftSession, requestState, ret))
}
ret
}
}
/**
* An actor that manages comet continuations from container (Jetty style)
*/
class CometContinuationActor(request: Req, session: LiftSession,
actors: List[(LiftCometActor, Long)],
onBreakout: List[AnswerRender] => Unit) extends LiftActor {
private var answers: List[AnswerRender] = Nil
private var done = false
val seqId = Helpers.nextNum
def messageHandler = {
case BeginContinuation =>
val sendItToMe: AnswerRender => Unit = ah => this ! ah
actors.foreach {
case (act, when) => act ! Listen(when, ListenerId(seqId), sendItToMe)
}
case ar: AnswerRender =>
answers = ar :: answers
LAPinger.schedule(this, BreakOut(), 5 millis)
case BreakOut() if !done =>
done = true
session.exitComet(this)
actors.foreach {
case (act, _) => tryo(act ! Unlisten(ListenerId(seqId)))
}
onBreakout(answers)
case _ =>
}
override def toString = "Continuation Actor dude " + seqId
}
private object BeginContinuation
private lazy val cometTimeout: Long = (LiftRules.cometRequestTimeout openOr 120) * 1000L
private def setupCometContinuation(request: Req, session: LiftSession, actors: List[(LiftCometActor, Long)]): Any = {
val cont = new CometContinuationActor(request, session, actors,
answers => request.request.resume(
(request, S.init(request, session)
(LiftRules.performTransform(
convertAnswersToCometResponse(session,
answers.toList, actors))))))
try {
session.enterComet(cont -> request)
LAPinger.schedule(cont, BreakOut(), TimeSpan(cometTimeout))
request.request.suspend(cometTimeout + 2000L)
} finally {
cont ! BeginContinuation
}
}
private def handleComet(requestState: Req, sessionActor: LiftSession, originalRequest: Req): Either[Box[LiftResponse], () => Box[LiftResponse]] = {
val actors: List[(LiftCometActor, Long)] =
requestState.params.toList.flatMap {
case (name, when) =>
sessionActor.getAsyncComponent(name).toList.map(c => (c, toLong(when)))
}
if (actors.isEmpty) Left(Full(new JsCommands(LiftRules.noCometSessionCmd.vend :: js.JE.JsRaw("lift_toWatch = {};").cmd :: Nil).toResponse))
else requestState.request.suspendResumeSupport_? match {
case true => {
setupCometContinuation(requestState, sessionActor, actors)
Left(Full(EmptyResponse))
}
case _ => {
Right(handleNonContinuationComet(requestState, sessionActor, actors, originalRequest))
}
}
}
private def convertAnswersToCometResponse(session: LiftSession, ret: Seq[AnswerRender], actors: List[(LiftCometActor, Long)]): LiftResponse = {
val ret2: List[AnswerRender] = ret.toList
val jsUpdateTime = ret2.map(ar => "if (lift_toWatch['" + ar.who.uniqueId + "'] !== undefined) lift_toWatch['" + ar.who.uniqueId + "'] = '" + ar.when + "';").mkString("\n")
val jsUpdateStuff = ret2.map {
ar => {
val ret = ar.response.toJavaScript(session, ar.displayAll)
if (!S.functionMap.isEmpty) {
session.updateFunctionMap(S.functionMap,
ar.who.uniqueId, ar.when)
S.clearFunctionMap
}
ret
}
}
actors foreach (_._1 ! ClearNotices)
val addl: List[JsCmd] =
(for {
req <- S.request
rendVer <- extractRenderVersion(req.path.partPath)
} yield RenderVersion.doWith(rendVer) {
S.jsToAppend
}) openOr Nil
(new JsCommands(JsCmds.Run(jsUpdateTime) :: jsUpdateStuff ::: addl)).toResponse
}
private def extractRenderVersion(in: List[String]): Box[String] = in match {
case _ :: _ :: _ :: rv :: _ => Full(rv)
case _ => Empty
}
private def handleNonContinuationComet(request: Req, session: LiftSession, actors: List[(LiftCometActor, Long)],
originalRequest: Req): () => Box[LiftResponse] = () => {
val f = new LAFuture[List[AnswerRender]]
val cont = new CometContinuationActor(request, session, actors,
answers => f.satisfy(answers))
try {
cont ! BeginContinuation
session.enterComet(cont -> request)
LAPinger.schedule(cont, BreakOut(), TimeSpan(cometTimeout))
val ret2 = f.get(cometTimeout) openOr Nil
Full(S.init(originalRequest, session) {
convertAnswersToCometResponse(session, ret2, actors)
})
} finally {
session.exitComet(cont)
}
}
val dumpRequestResponse = Props.getBool("dump.request.response", false)
private def logIfDump(request: Req, response: BasicResponse) {
if (dumpRequestResponse) {
val toDump = request.uri + "\n" +
request.params + "\n" +
response.headers + "\n" +
(
response match {
case InMemoryResponse(data, _, _, _) => new String(data, "UTF-8")
case _ => "data"
}
)
logger.trace(toDump)
}
}
/**
* Sends the { @code HTTPResponse } to the browser using data from the
* { @link Response } and { @link Req }.
*/
private[http] def sendResponse(liftResp: LiftResponse, response: HTTPResponse, request: Req) {
def fixHeaders(headers: List[(String, String)]) = headers map ((v) => v match {
case ("Location", uri) =>
val u = request
(v._1, (
(for (
updated <- Full((if (!LiftRules.excludePathFromContextPathRewriting.vend(uri)) u.contextPath else "") + uri).filter(ignore => uri.startsWith("/"));
rwf <- URLRewriter.rewriteFunc) yield rwf(updated)) openOr uri
))
case _ => v
})
def pairFromRequest(req: Req): (Box[Req], Box[String]) = {
val acceptHeader = for (innerReq <- Box.legacyNullTest(req.request);
accept <- innerReq.header("Accept")) yield accept
(Full(req), acceptHeader)
}
val resp = liftResp.toResponse
logIfDump(request, resp)
def insureField(headers: List[(String, String)], toInsure: List[(String, String)]): List[(String, String)] = {
val org = Map(headers: _*)
toInsure.foldLeft(org) {
case (map, (key, value)) =>
if (map.contains(key)) map
else map + (key -> value)
}.toList
}
val len = resp.size
// insure that certain header fields are set
val header = if (resp.code == 304 || resp.code == 303)
fixHeaders(resp.headers)
else
insureField(fixHeaders(resp.headers),
LiftRules.defaultHeaders(NodeSeq.Empty -> request) :::
/* List(("Content-Type",
LiftRules.determineContentType(pairFromRequest(request)))) ::: */
(if (len >= 0) List(("Content-Length", len.toString)) else Nil))
LiftRules.beforeSend.toList.foreach(f => tryo(f(resp, response, header, Full(request))))
// set the cookies
response.addCookies(resp.cookies)
// send the response
response.addHeaders(header.map {
case (name, value) => HTTPParam(name, value)
})
LiftRules.supplimentalHeaders(response)
liftResp match {
case ResponseWithReason(_, reason) => response setStatusWithReason (resp.code, reason)
case _ => response setStatus resp.code
}
try {
resp match {
case EmptyResponse =>
case InMemoryResponse(bytes, _, _, _) =>
response.outputStream.write(bytes)
response.outputStream.flush()
case StreamingResponse(stream, endFunc, _, _, _, _) =>
try {
var len = 0
val ba = new Array[Byte](8192)
val os = response.outputStream
stream match {
case jio: java.io.InputStream => len = jio.read(ba)
case stream => len = stream.read(ba)
}
while (len >= 0) {
if (len > 0) os.write(ba, 0, len)
stream match {
case jio: java.io.InputStream => len = jio.read(ba)
case stream => len = stream.read(ba)
}
}
response.outputStream.flush()
} finally {
endFunc()
}
case OutputStreamResponse(out, _, _, _, _) =>
out(response.outputStream)
response.outputStream.flush()
}
} catch {
case e: java.io.IOException => // ignore IO exceptions... they happen
}
LiftRules.afterSend.toList.foreach(f => tryo(f(resp, response, header, Full(request))))
}
}
import provider.servlet._
class LiftFilter extends ServletFilterProvider
private class SessionIdCalc(req: Req) {
private val CometPath = LiftRules.cometPath
lazy val id: Box[String] = req.request.sessionId match {
case Full(id) => Full(id)
case _ => req.path.wholePath match {
case CometPath :: _ :: id :: _ if id != LiftRules.cometScriptName() => Full(id)
case _ => Empty
}
}
}
Jump to Line
Something went wrong with that request. Please try again.