|
Lift Framework example source code file (Req.scala)
The Lift Framework Req.scala source code/* * 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 java.io.{InputStream, ByteArrayInputStream, File, FileInputStream, FileOutputStream} import scala.xml._ import common._ import json._ import util._ import Helpers._ import http.provider._ import sitemap._ object UserAgentCalculator extends Factory { /** * The default regular expression for IE */ val iePattern = """MSIE ([0-9]+)""".r /** * You can change the mechanism by which the user agent for IE * is calculated by doing the Factory thing with this object */ object ieCalcFunction extends FactoryMaker[Box[String] => Box[Double]](defaultIeCalcFunction _) /** * The built-in mechanism for calculating IE */ def defaultIeCalcFunction(userAgent: Box[String]): Box[Double] = for { ua <- userAgent m = iePattern.pattern.matcher(ua) ver <- if (m.find) Helpers.asDouble(m.group(1)) else Empty } yield ver /** * The default regular expression for Safari */ val safariPattern = """Version.([0-9]+)[.0-9]+ ([^S])*Safari\/""".r /** * You can change the mechanism by which the user agent for Safari * is calculated by doing the Factory thing with this object */ object safariCalcFunction extends FactoryMaker[Box[String] => Box[Double]](defaultSafariCalcFunction _) /** * The built-in mechanism for calculating Safari */ def defaultSafariCalcFunction(userAgent: Box[String]): Box[Double] = for { ua <- userAgent m = safariPattern.pattern.matcher(ua) ver <- if (m.find) Helpers.asDouble(m.group(1)) else Empty } yield ver /** * The default regular expression for Firefox */ val firefoxPattern = """Firefox.([1-9][0-9]*\.[0-9])""".r /** * You can change the mechanism by which the user agent for Firefox * is calculated by doing the Factory thing with this object */ object firefoxCalcFunction extends FactoryMaker[Box[String] => Box[Double]](defaultFirefoxCalcFunction _) /** * The built-in mechanism for calculating Firefox */ def defaultFirefoxCalcFunction(userAgent: Box[String]): Box[Double] = for { ua <- userAgent m = firefoxPattern.pattern.matcher(ua) ver <- if (m.find) Helpers.asDouble(m.group(1)) else Empty } yield ver /** * The default regular expression for Chrome */ val chromePattern = """Chrome.([1-9][0-9]*\.[0-9])""".r /** * You can change the mechanism by which the user agent for Chrome * is calculated by doing the Factory thing with this object */ object chromeCalcFunction extends FactoryMaker[Box[String] => Box[Double]](defaultChromeCalcFunction _) /** * You can change the mechanism by which Lift calculates * if the User-Agent represents an iPhone. Put your * special calculation function in here */ object iPhoneCalcFunction extends FactoryMaker[Box[Box[String] => Boolean]](Empty) /** * You can change the mechanism by which Lift calculates * if the User-Agent represents an iPad. Put your * special calculation function in here */ object iPadCalcFunction extends FactoryMaker[Box[Box[String] => Boolean]](Empty) /** * The built-in mechanism for calculating Chrome */ def defaultChromeCalcFunction(userAgent: Box[String]): Box[Double] = for { ua <- userAgent m = chromePattern.pattern.matcher(ua) ver <- if (m.find) Helpers.asDouble(m.group(1)) else Empty } yield ver } trait UserAgentCalculator { lazy val ieVersion: Box[Int] = UserAgentCalculator.ieCalcFunction.vend.apply(userAgent).map(_.toInt) lazy val isIE6: Boolean = ieVersion.map(_ == 6) openOr false lazy val isIE7: Boolean = ieVersion.map(_ == 7) openOr false lazy val isIE8: Boolean = ieVersion.map(_ == 8) openOr false lazy val isIE9: Boolean = ieVersion.map(_ == 9) openOr false lazy val isIE = ieVersion.map(_ >= 6) openOr false lazy val safariVersion: Box[Int] = UserAgentCalculator.safariCalcFunction.vend.apply(userAgent).map(_.toInt) def isSafari2: Boolean = false lazy val isSafari3: Boolean = safariVersion.map(_ == 3) openOr false lazy val isSafari4: Boolean = safariVersion.map(_ == 4) openOr false lazy val isSafari5: Boolean = safariVersion.map(_ == 5) openOr false def isSafari3_+ = safariVersion.map(_ >= 3) openOr false def isSafari = safariVersion.isDefined /** * Is the Req coming from an iPhone */ lazy val isIPhone: Boolean = UserAgentCalculator.iPhoneCalcFunction.vend. map(_.apply(userAgent)) openOr isSafari && (userAgent.map(s => s.indexOf("(iPhone") >= 0) openOr false) /** * Is the Req coming from an iPad */ lazy val isIPad: Boolean = UserAgentCalculator.iPadCalcFunction.vend. map(_.apply(userAgent)) openOr isSafari && (userAgent.map(s => s.indexOf("(iPad") >= 0) openOr false) lazy val firefoxVersion: Box[Double] = UserAgentCalculator.firefoxCalcFunction.vend.apply(userAgent) lazy val isFirefox2: Boolean = firefoxVersion.map(v => v >= 2d && v < 3d) openOr false lazy val isFirefox3: Boolean = firefoxVersion.map(v => v >= 3d && v < 3.5d) openOr false lazy val isFirefox35: Boolean = firefoxVersion.map(v => v >= 3.5d && v < 3.6d) openOr false lazy val isFirefox36: Boolean = firefoxVersion.map(v => v >= 3.6d && v < 4d) openOr false lazy val isFirefox40: Boolean = firefoxVersion.map(v => v >= 4d) openOr false def isFirefox35_+ : Boolean = firefoxVersion.map(_ >= 3.5d) openOr false def isFirefox = firefoxVersion.isDefined lazy val chromeVersion: Box[Double] = UserAgentCalculator.chromeCalcFunction.vend.apply(userAgent) lazy val isChrome2 = chromeVersion.map(v => v >= 2d && v < 3d) openOr false lazy val isChrome3 = chromeVersion.map(v => v >= 3d && v < 4d) openOr false lazy val isChrome4 = chromeVersion.map(v => v >= 4d && v < 5d) openOr false lazy val isChrome5 = chromeVersion.map(v => v >= 5d && v < 6d) openOr false lazy val isChrome6 = chromeVersion.map(v => v >= 6d && v < 7d) openOr false def isChrome3_+ = chromeVersion.map(_ >= 3d) openOr false def isChrome = chromeVersion.isDefined lazy val isOpera9: Boolean = (userAgent.map(s => s.indexOf("Opera/9.") >= 0) openOr false) def isOpera = isOpera9 /** * What's the user agent? */ def userAgent: Box[String] } @serializable sealed trait ParamHolder { def name: String } @serializable final case class NormalParamHolder(name: String, value: String) extends ParamHolder /** * A FileParamHolder contains a file uploaded via a multipart * form. * * @param name The name of the form field for this file * @param mimeType the mime type, as specified in the Content-Type field * @param fileName The local filename on the client */ @serializable abstract class FileParamHolder(val name: String, val mimeType: String, val fileName: String) extends ParamHolder { /** * Returns the contents of the uploaded file as a Byte array. */ def file: Array[Byte] /** * Returns an input stream that can be used to read the * contents of the uploaded file. */ def fileStream: InputStream /** * Returns the length of the uploaded file. */ def length : Long } /** * This FileParamHolder stores the uploaded file directly into memory. * * @param name The name of the form field for this file * @param mimeType the mime type, as specified in the Content-Type field * @param fileName The local filename on the client * @param file The contents of the uploaded file in a byte array */ class InMemFileParamHolder(override val name: String, override val mimeType: String, override val fileName: String, val file: Array[Byte]) extends FileParamHolder(name, mimeType, fileName) { /** * Returns an input stream that can be used to read the * contents of the uploaded file. */ def fileStream: InputStream = new ByteArrayInputStream(file) /** * Returns the length of the uploaded file. */ def length : Long = if (file == null) 0 else file.length } /** * This FileParamHolder stores the uploaded file in a * temporary file on disk. * * @param name The name of the form field for this file * @param mimeType the mime type, as specified in the Content-Type field * @param fileName The local filename on the client * @param localFile The local copy of the uploaded file */ class OnDiskFileParamHolder(override val name: String, override val mimeType: String, override val fileName: String, val localFile: File) extends FileParamHolder(name, mimeType, fileName) { /** * Returns an input stream that can be used to read the * contents of the uploaded file. */ def fileStream: InputStream = new FileInputStream(localFile) /** * Returns the contents of the uploaded file as a Byte array. */ def file: Array[Byte] = Helpers.readWholeStream(fileStream) /** * Returns the length of the uploaded file. */ def length : Long = if (localFile == null) 0 else localFile.length protected override def finalize { tryo(localFile.delete) } } object OnDiskFileParamHolder { def apply(n: String, mt: String, fn: String, inputStream: InputStream): OnDiskFileParamHolder = { val file: File = File.createTempFile("lift_mime", "upload") val fos = new FileOutputStream(file) val ba = new Array[Byte](8192) def doUpload() { inputStream.read(ba) match { case x if x < 0 => case 0 => doUpload() case x => fos.write(ba, 0, x); doUpload() } } doUpload() inputStream.close fos.close new OnDiskFileParamHolder(n, mt, fn, file) } } object FileParamHolder { def apply(n: String, mt: String, fn: String, file: Array[Byte]): FileParamHolder = new InMemFileParamHolder(n, mt, fn, file) def unapply(in: Any): Option[(String, String, String, Array[Byte])] = in match { case f: FileParamHolder => Some((f.name, f.mimeType, f.fileName, f.file)) case _ => None } } private[http] object CurrentReq extends ThreadGlobal[Req] private final case class AvoidGAL(func: () => ParamCalcInfo) { lazy val thunk: ParamCalcInfo = func() } /** * Helper object for constructing Req instances */ object Req { object NilPath extends ParsePath(Nil, "", true, false) private[http] lazy val localHostName = { import java.net._ InetAddress.getLocalHost.getHostName } def apply(original: Req, rewrite: List[LiftRules.RewritePF]): Req = this.apply(original, rewrite, Nil, Nil) def apply(original: Req, rewrite: List[LiftRules.RewritePF], statelessTest: List[LiftRules.StatelessTestPF], otherStatelessTest: List[LiftRules.StatelessReqTestPF]): Req = { def processRewrite(path: ParsePath, params: Map[String, String]): RewriteResponse = NamedPF.applyBox(RewriteRequest(path, original.requestType, original.request), rewrite) match { case Full(resp@RewriteResponse(_, _, true)) => resp case _: EmptyBox => RewriteResponse(path, params) case Full(resp) => processRewrite(resp.path, params ++ resp.params) } val rewritten = processRewrite(original.path, Map.empty) val wholePath = rewritten.path.wholePath val stateless = NamedPF.applyBox(StatelessReqTest(wholePath, original.request), otherStatelessTest) or NamedPF.applyBox(wholePath, statelessTest) new Req(rewritten.path, original.contextPath, original.requestType, original.contentType, original.request, original.nanoStart, original.nanoEnd, stateless openOr original.stateless_?, original.paramCalculator, original.addlParams ++ rewritten.params) } def apply(request: HTTPRequest, rewrite: List[LiftRules.RewritePF], nanoStart: Long): Req = this.apply(request, rewrite, Nil, Nil, nanoStart) def apply(request: HTTPRequest, rewrite: List[LiftRules.RewritePF], statelessTest: List[LiftRules.StatelessTestPF], otherStatelessTest: List[LiftRules.StatelessReqTestPF], nanoStart: Long): Req = { val reqType = RequestType(request) val contextPath = LiftRules.calculateContextPath() openOr request.contextPath val turi = if (request.uri.length >= contextPath.length) request.uri.substring(contextPath.length) else "" val tmpUri = if (turi.length > 0) turi else "/" val tmpPath = parsePath(tmpUri) def processRewrite(path: ParsePath, params: Map[String, String]): RewriteResponse = NamedPF.applyBox(RewriteRequest(path, reqType, request), rewrite) match { case Full(resp@RewriteResponse(_, _, true)) => resp case _: EmptyBox => RewriteResponse(path, params) case Full(resp) => processRewrite(resp.path, params ++ resp.params) } // val (uri, path, localSingleParams) = processRewrite(tmpUri, tmpPath, TreeMap.empty) val rewritten = processRewrite(tmpPath, Map.empty) val localParams: Map[String, List[String]] = Map(rewritten.params.toList.map {case (name, value) => name -> List(value)}: _*) // val session = request.getSession // val body = () val eMap = Map.empty[String, List[String]] val contentType = request.contentType // val (paramNames: List[String], params: Map[String, List[String]], files: List[FileParamHolder], body: Box[Array[Byte]]) = // calculate the query parameters lazy val queryStringParam: (List[String], Map[String, List[String]]) = { val params: List[(String, String)] = for { queryString <- request.queryString.toList nameVal <- queryString.split("&").toList.map(_.trim).filter(_.length > 0) (name, value) <- nameVal.split("=").toList match { case Nil => Empty case n :: v :: _ => Full((urlDecode(n), urlDecode(v))) case n :: _ => Full((urlDecode(n), "")) }} yield (name, value) val names: List[String] = params.map(_._1).distinct val nvp: Map[String, List[String]] = params.foldLeft(Map[String, List[String]]()) { case (map, (name, value)) => map + (name -> (map.getOrElse(name, Nil) ::: List(value))) } (names, nvp) } // make this a thunk so it only gets calculated once val paramCalcInfo: AvoidGAL = new AvoidGAL(() => { // post/put of XML or JSON... eagerly read the stream if ((reqType.post_? || reqType.put_?) && contentType.dmap(false){ _.toLowerCase match { case x => x.startsWith("text/xml") || x.startsWith("application/xml") || x.startsWith("text/json") || x.startsWith("application/json") }}) { ParamCalcInfo(queryStringParam._1, queryStringParam._2 ++ localParams, Nil, Full(BodyOrInputStream(request.inputStream))) // it's multipart } else if (request multipartContent_?) { val allInfo = request extractFiles val normal: List[NormalParamHolder] = allInfo.flatMap { case v: NormalParamHolder => List(v) case _ => Nil} val files: List[FileParamHolder] = allInfo.flatMap { case v: FileParamHolder => List(v) case _ => Nil} val params = normal.foldLeft(eMap)((a, b) => a + (b.name -> (a.getOrElse(b.name, Nil) ::: List(b.value)))) ParamCalcInfo((queryStringParam._1 ::: normal.map(_.name)).distinct, queryStringParam._2 ++ localParams ++ params, files, Empty) // it's a GET } else if (reqType.get_?) { ParamCalcInfo(queryStringParam._1, queryStringParam._2 ++ localParams, Nil, Empty) } else if (contentType.dmap(false)(_.toLowerCase. startsWith("application/x-www-form-urlencoded"))) { val params = localParams ++ (request.params.sortWith {(s1, s2) => s1.name < s2.name}). map(n => (n.name, n.values)) ParamCalcInfo(request paramNames, params, Nil, Empty) } else { ParamCalcInfo(queryStringParam._1, queryStringParam._2 ++ localParams, Nil, Full(BodyOrInputStream(request inputStream))) } }) val paramCalculator: () => ParamCalcInfo = () => { paramCalcInfo.thunk } val wholePath = rewritten.path.wholePath val stateless = NamedPF.applyBox(StatelessReqTest(wholePath, request), otherStatelessTest) or NamedPF.applyBox(wholePath, statelessTest) new Req(rewritten.path, contextPath, reqType, contentType, request, nanoStart, System.nanoTime, stateless openOr false, paramCalculator, Map()) } private def fixURI(uri: String) = uri indexOf ";jsessionid" match { case -1 => uri case x@_ => uri.substring(0, x) } /** * Create a nil request... useful for testing */ def nil = new Req(NilPath, "", GetRequest, Empty, null, System.nanoTime, System.nanoTime, false, () => ParamCalcInfo(Nil, Map.empty, Nil, Empty), Map()) def parsePath(in: String): ParsePath = { val p1 = fixURI((in match {case null => "/"; case s if s.length == 0 => "/"; case s => s}).replaceAll("/+", "/")) val front = p1.startsWith("/") val back = p1.length > 1 && p1.endsWith("/") val orgLst = p1.replaceAll("/$", "/index").split("/"). toList.map(_.trim).filter(_.length > 0) val (lst, suffix) = NamedPF(orgLst, LiftRules.suffixSplitters.toList) ParsePath(lst.map(urlDecode), suffix, front, back) } var fixHref = _fixHref _ private def _fixHref(contextPath: String, v: Seq[Node], fixURL: Boolean, rewrite: Box[String => String]): Text = { val hv = v.text val updated = if (hv.startsWith("/") && !LiftRules.excludePathFromContextPathRewriting.vend(hv)) contextPath + hv else hv Text(if (fixURL && rewrite.isDefined && !updated.startsWith("mailto:") && !updated.startsWith("javascript:") && !updated.startsWith("http://") && !updated.startsWith("https://") && !updated.startsWith("#")) rewrite.open_!.apply(updated) else updated) } /** * Corrects the HTML content,such as applying context path to URI's, session information if cookies are disabled etc. */ def fixHtml(contextPath: String, in: NodeSeq): NodeSeq = { val rewrite = URLRewriter.rewriteFunc def fixAttrs(toFix: String, attrs: MetaData, fixURL: Boolean): MetaData = { if (attrs == Null) Null else if (attrs.key == toFix) { new UnprefixedAttribute(toFix, Req.fixHref(contextPath, attrs.value, fixURL, rewrite), fixAttrs(toFix, attrs.next, fixURL)) } else attrs.copy(fixAttrs(toFix, attrs.next, fixURL)) } def _fixHtml(contextPath: String, in: NodeSeq): NodeSeq = { in.map { v => v match { case Group(nodes) => Group(_fixHtml(contextPath, nodes)) case e: Elem if e.label == "form" => Elem(v.prefix, v.label, fixAttrs("action", v.attributes, true), v.scope, _fixHtml(contextPath, v.child): _*) case e: Elem if e.label == "script" => Elem(v.prefix, v.label, fixAttrs("src", v.attributes, false), v.scope, _fixHtml(contextPath, v.child): _*) case e: Elem if e.label == "a" => Elem(v.prefix, v.label, fixAttrs("href", v.attributes, true), v.scope, _fixHtml(contextPath, v.child): _*) case e: Elem if e.label == "link" => Elem(v.prefix, v.label, fixAttrs("href", v.attributes, false), v.scope, _fixHtml(contextPath, v.child): _*) case e: Elem => Elem(v.prefix, v.label, fixAttrs("src", v.attributes, true), v.scope, _fixHtml(contextPath, v.child): _*) case _ => v } } } _fixHtml(contextPath, in) } private[liftweb] def defaultCreateNotFound(in: Req) = XhtmlResponse((<html> The Requested URL {in.contextPath + in.uri} was not found on this server ), LiftRules.docType.vend(in), List("Content-Type" -> "text/html; charset=utf-8"), Nil, 404, S.ieMode) def unapply(in: Req): Option[(List[String], String, RequestType)] = Some((in.path.partPath, in.path.suffix, in.requestType)) } /** * Holds either the body or the request input stream, depending on which was requested first */ final case class BodyOrInputStream(is: InputStream) { private var calc = false lazy val body: Box[Array[Byte]] = synchronized { if (calc) Empty else { calc = true tryo(readWholeStream(is)) } } lazy val stream: Box[InputStream] = synchronized { if (calc) Empty else { calc = true Full(is) } } } final case class ParamCalcInfo(paramNames: List[String], params: Map[String, List[String]], uploadedFiles: List[FileParamHolder], body: Box[BodyOrInputStream]) /** * Holds information about the content type and subtype including * the q parameter and extension information. */ final case class ContentType(theType: String, subtype: String, order: Int, q: Box[Double], extension: List[(String, String)]) extends Ordered[ContentType] { /** * Compares this to another ContentType instance based on the q * and if the q matches, compare based on specialization (* vs. * explicit and then order. */ def compare(that: ContentType): Int = ((that.q openOr 1d) compare (q openOr 1d)) match { case 0 => def doDefault = { order compare that.order } (theType, that.theType, subtype, that.subtype) match { case ("*", "*", _, _) => doDefault case ("*", _, _, _) => 1 case (_, "*", _, _) => -1 case (_, _, "*", "*") => doDefault case (_, _, "*", _) => 1 case (_, _, _, "*") => -1 case _ => doDefault } case x => x } /** * Does this ContentType match the String including * wildcard support */ def matches(contentType: (String, String)): Boolean = (theType == "*" || (theType == contentType._1)) && (subtype == "*" || subtype == contentType._2) /** * Is it a wildcard */ def wildCard_? = theType == "*" && subtype == "*" } /** * The ContentType companion object that has helper methods * for parsing Accept headers and other things that * contain multiple ContentType information. */ object ContentType { /** * Parse the String into a series of ContentType instances, * returning the multiple ContentType instances */ def parse(str: String): List[ContentType] = (for { (part, index) <- str.charSplit(','). map(_.trim).zipWithIndex // split at comma content <- parseIt(part, index) } yield content).sortWith(_ < _) private object TwoType { def unapply(in: String): Option[(String, String)] = in.charSplit('/') match { case a :: b :: Nil => Some(a -> b) case _ => None } } private object EqualsSplit { private def removeQuotes(s: String) = if (s.startsWith("\"") && s.endsWith("\"")) s.substring(1, s.length - 1) else s def unapply(in: String): Option[(String, String)] = in.roboSplit("=") match { case a :: b :: Nil => Some(a -> removeQuotes(b)) case _ => None } } private def parseIt(content: String, index: Int): Box[ContentType] = content.roboSplit(";") match { case TwoType(typ, subType) :: xs => { val kv = xs.flatMap(EqualsSplit.unapply) // get the key/value pairs val q: Box[Double] = first(kv){ case (k, v) if k == "q" => Helpers.asDouble(v) case _ => Empty } Full(ContentType(typ, subType, index, q, kv.filter{_._1 != "q"})) } case _ => Empty } } /** * Contains request information */ class Req(val path: ParsePath, val contextPath: String, val requestType: RequestType, val contentType: Box[String], val request: HTTPRequest, val nanoStart: Long, val nanoEnd: Long, _stateless_? : Boolean, private[http] val paramCalculator: () => ParamCalcInfo, private[http] val addlParams: Map[String, String]) extends HasParams with UserAgentCalculator { override def toString = "Req(" + paramNames + ", " + params + ", " + path + ", " + contextPath + ", " + requestType + ", " + contentType + ")" def this(_path: ParsePath, _contextPath: String, _requestType: RequestType, _contentType: Box[String], _request: HTTPRequest, _nanoStart: Long, _nanoEnd: Long, _paramCalculator: () => ParamCalcInfo, _addlParams: Map[String, String]) = this(_path, _contextPath, _requestType, _contentType, _request, _nanoStart, _nanoEnd, false, _paramCalculator, _addlParams) /** * Build a new Req, except it has a different path. * Useful for creating Reqs with sub-paths */ def withNewPath(newPath: ParsePath): Req = { val outer = this new Req(newPath, contextPath, requestType, contentType, request, nanoStart, nanoEnd, _stateless_?, paramCalculator, addlParams) { override lazy val json: Box[JsonAST.JValue] = outer.json override lazy val xml: Box[Elem] = outer.xml override lazy val location: Box[Loc[_]] = outer.location override lazy val buildMenu: CompleteMenu = outer.buildMenu /** * the accept header */ override lazy val accepts: Box[String] = outer.accepts /** * What is the content type in order of preference by the requestor * calculated via the Accept header */ override lazy val weightedAccept: List[ContentType] = outer.weightedAccept /** * Returns true if the request accepts XML */ override lazy val acceptsXml_? = outer.acceptsXml_? /** * Returns true if the request accepts JSON */ override lazy val acceptsJson_? = outer.acceptsJson_? /** * Is the Accepts Header * / * */ override lazy val acceptsStarStar : Boolean = outer.acceptsStarStar /** * Returns true if the request accepts JavaScript */ override lazy val acceptsJavaScript_? = outer.acceptsJavaScript_? } } /** * Should the request be treated as stateless (no session created for it)? */ lazy val stateless_? = { val ret = _stateless_? || (location.map(_.stateless_?) openOr false) ret } /** * Returns true if the content-type is text/xml or application/xml */ def xml_? = contentType != null && contentType.dmap(false){ _.toLowerCase match { case x if x.startsWith("text/xml") => true case x if x.startsWith("application/xml") => true case _ => false } } /** * Returns true if the content-type is text/json or application/json */ def json_? = contentType != null && contentType.dmap(false){ _.toLowerCase match { case x if x.startsWith("text/json") => true case x if x.startsWith("application/json") => true case _ => false } } /** * Make the servlet session go away */ def destroyServletSession() { for { httpReq <- Box !! request } httpReq.destroyServletSession() } def snapshot = { val paramCalc = paramCalculator() paramCalc.body.map(_.body) // make sure we grab the body new Req(path, contextPath, requestType, contentType, request.snapshot, nanoStart, nanoEnd, stateless_?, () => paramCalc, addlParams) } val section = path(0) match {case null => "default"; case s => s} val view = path(1) match {case null => "index"; case s@_ => s} val id = pathParam(0) def pathParam(n: Int) = path.wholePath.drop(n + 2).headOption getOrElse "" def path(n: Int): String = path.wholePath.drop(n).headOption getOrElse "" def param(n: String): Box[String] = params.get(n) match { case Some(s :: _) => Full(s) case _ => Empty } lazy val headers: List[(String, String)] = for (h <- request.headers; p <- h.values ) yield (h name, p) def headers(name: String): List[String] = headers.filter(_._1.equalsIgnoreCase(name)).map(_._2) def header(name: String): Box[String] = headers(name) match { case x :: _ => Full(x) case _ => Empty } /** * Get the name of the params */ def paramNames: List[String] = _paramNames /** * the raw parameters, use params */ def _params: Map[String, List[String]] = __params /** * The uploaded files */ def uploadedFiles: List[FileParamHolder] = _uploadedFiles /** * The POST or PUT body. This will be empty if the content * type is application/x-www-form-urlencoded or a multipart mime. * It will also be empty if rawInputStream is accessed */ def body: Box[Array[Byte]] = _body.flatMap(_.body) /** * The raw input stream of a POST or PUT that is not * application/x-www-form-urlencoded or a multipart mime * and if this method is called before the body method. * Remember to close the stream when done. */ def rawInputStream: Box[InputStream] = _body.flatMap(_.stream) private lazy val ParamCalcInfo(_paramNames /*: List[String]*/, __params /*: Map[String, List[String]]*/, _uploadedFiles /*: List[FileParamHolder]*/, _body /*: Box[BodyOrInputStream]*/) = { val ret = paramCalculator() ret } lazy val params: Map[String, List[String]] = addlParams.foldLeft(_params){ case (map, (key, value)) => map + (key -> (value :: map.getOrElse(key, Nil))) } lazy val cookies = request.cookies match { case null => Nil case ca => ca.toList } /** * Get the session ID if there is one without creating on */ def sessionId: Box[String] = for { httpRequest <- Box !! request sid <- httpRequest.sessionId } yield sid lazy val json: Box[JsonAST.JValue] = if (!json_?) Empty else try { import java.io._ def r = """; *charset=(.*)""".r def r2 = """[^=]*$""".r def charSet: String = contentType.flatMap(ct => r.findFirstIn(ct).flatMap(r2.findFirstIn)).getOrElse("UTF-8") body.map(b => JsonParser.parse(new InputStreamReader(new ByteArrayInputStream(b), charSet))) } catch { case e: LiftFlowOfControlException => throw e case e: Exception => Failure(e.getMessage, Full(e), Empty) } private def containerRequest = Box !! request /** * The hostname to which the request was sent. This is taken from the "Host" HTTP header, or if that * does not exist, the DNS name or IP address of the server. */ lazy val hostName: String = containerRequest.map(_.serverName) openOr Req.localHostName /** * The host and path of the request up to and including the context path. This does * not include the template path or query string. */ lazy val hostAndPath: String = containerRequest.map(r => (r.scheme, r.serverPort) match { case ("http", 80) => "http://" + r.serverName + contextPath case ("https", 443) => "https://" + r.serverName + contextPath case (sch, port) => sch + "://" + r.serverName + ":" + port + contextPath }) openOr "" lazy val xml: Box[Elem] = if (!xml_?) Empty else try { import java.io._ body.map(b => XML.load(new ByteArrayInputStream(b))) } catch { case e: LiftFlowOfControlException => throw e case e: Exception => Failure(e.getMessage, Full(e), Empty) } /** * The SiteMap Loc associated with this Req */ lazy val location: Box[Loc[_]] = LiftRules.siteMap.flatMap(_.findLoc(this)) /** * Test the current SiteMap Loc for access control to insure * that this Req is allowed to access the page */ def testLocation: Either[Boolean, Box[LiftResponse]] = { if (LiftRules.siteMap.isEmpty) Left(true) else location.map(_.testAccess) match { case Full(Left(true)) => Left(true) case Full(Right(Full(resp))) => object theResp extends RequestVar(resp.apply()) Right(Full(theResp.is)) case _ => Right(Empty) } } lazy val buildMenu: CompleteMenu = location.map(_.buildMenu) openOr CompleteMenu(Nil) private def initIfUnitted[T](f: T): T = S.session match { case Full(_) => f case _ => S.statelessInit(this)(f) } /** * Computer the Not Found via a Template */ private def notFoundViaTemplate(path: ParsePath): LiftResponse = { this.initIfUnitted { (for { session <- S.session template = Templates(path.partPath) resp <- session.processTemplate(template, this, path, 404) } yield resp) match { case Full(resp) => resp case _ => Req.defaultCreateNotFound(this) } } } def createNotFound: LiftResponse = NamedPF((this, Empty), LiftRules.uriNotFound.toList) match { case DefaultNotFound => Req.defaultCreateNotFound(this) case NotFoundAsTemplate(path) => notFoundViaTemplate(path) case NotFoundAsResponse(resp) => resp case NotFoundAsNode(node) => LiftRules.convertResponse((node, 404), S.getHeaders(LiftRules.defaultHeaders((node, this))), S.responseCookies, this) } def createNotFound(f: Failure): LiftResponse = NamedPF((this, Full(f)), LiftRules.uriNotFound.toList) match { case DefaultNotFound => Req.defaultCreateNotFound(this) case NotFoundAsTemplate(path) => notFoundViaTemplate(path) case NotFoundAsResponse(resp) => resp case NotFoundAsNode(node) => LiftRules.convertResponse((node, 404), S.getHeaders(LiftRules.defaultHeaders((node, this))), S.responseCookies, this) } private[http] def createNotFound(f: (ParsePath) => Box[LiftResponse]): Box[LiftResponse] = NamedPF((this, Empty), LiftRules.uriNotFound.toList) match { case DefaultNotFound => Full(Req.defaultCreateNotFound(this)) case NotFoundAsResponse(resp) => Full(resp) case NotFoundAsTemplate(path) => val newReq = new Req(path, this.contextPath, this.requestType, this.contentType, this.request, this.nanoStart, this.nanoEnd, true, this.paramCalculator, this.addlParams) S.withReq(newReq) { f(path) } case NotFoundAsNode(node) => Full(LiftRules.convertResponse((node, 404), S.getHeaders(LiftRules.defaultHeaders((node, this))), S.responseCookies, this)) } def post_? = requestType.post_? def get_? = requestType.get_? def put_? = requestType.put_? def fixHtml(in: NodeSeq): NodeSeq = Req.fixHtml(contextPath, in) lazy val uri: String = request match { case null => "Outside HTTP Request (e.g., on Actor)" case request => val ret = for {uri <- Box.legacyNullTest(request.uri) cp <- Box.legacyNullTest(contextPath) part <- (request.uri.length >= cp.length) match { case true => Full(request.uri.substring(cp.length)) case _ => Empty}} yield { part match { case "" => "/" case x => Req.fixURI(x) } } ret openOr "/" } /** * The IP address of the request */ def remoteAddr: String = request.remoteAddress /** * Parse the if-modified-since header and return the milliseconds since epoch * of the parsed date. */ lazy val ifModifiedSince: Box[java.util.Date] = for{req <- Box !! request ims <- req.header("if-modified-since") id <- boxParseInternetDate(ims) } yield id def testIfModifiedSince(when: Long): Boolean = (when == 0L) || ((when / 1000L) > ((ifModifiedSince.map(_.getTime) openOr 0L) / 1000L)) def testFor304(lastModified: Long, headers: (String, String)*): Box[LiftResponse] = if (!testIfModifiedSince(lastModified)) Full(InMemoryResponse(new Array[Byte](0), headers.toList, Nil, 304)) else Empty /** * The user agent of the browser that sent the request */ lazy val userAgent: Box[String] = for (r <- Box.legacyNullTest(request); uah <- request.header("User-Agent")) yield uah /** * the accept header */ lazy val accepts: Box[String] = { request.headers.toList. filter(_.name equalsIgnoreCase "accept").flatMap(_.values) match { case Nil => Empty case xs => Full(xs.mkString(", ")) } } /** * What is the content type in order of preference by the requestor * calculated via the Accept header */ lazy val weightedAccept: List[ContentType] = accepts match { case Full(a) => ContentType.parse(a) case _ => Nil } /** * Returns true if the request accepts XML */ lazy val acceptsXml_? = (weightedAccept.find(_.matches("text" -> "xml")) orElse weightedAccept.find(_.matches("application" -> "xml"))).isDefined /** * Returns true if the request accepts JSON */ lazy val acceptsJson_? = (weightedAccept.find(_.matches("text" -> "json")) orElse weightedAccept.find(_.matches("application" -> "json"))).isDefined /** * Is the Accepts Header * / * */ lazy val acceptsStarStar : Boolean = accepts.map(_ == "*/*") openOr false /** * Returns true if the request accepts JavaScript */ lazy val acceptsJavaScript_? = (weightedAccept.find(_.matches("text" -> "javascript")) orElse weightedAccept.find(_.matches("application" -> "javascript"))). isDefined def updateWithContextPath(uri: String): String = if (uri.startsWith("/")) contextPath + uri else uri } /** * This case class is used for pattern matching. See LiftRules.statelessRewrite and LiftRules.statefulRewrite */ final case class RewriteRequest(path: ParsePath, requestType: RequestType, httpRequest: HTTPRequest) /** * The representation of an URI path */ @serializable case class ParsePath(partPath: List[String], suffix: String, absolute: Boolean, endSlash: Boolean) { def drop(cnt: Int) = ParsePath(partPath.drop(cnt), suffix, absolute, endSlash) lazy val wholePath = if (suffix.length > 0) { partPath.dropRight(1) ::: List((partPath match { case Nil => "" case xs => xs.last}) + "." + suffix) } else { partPath } } final case class RewriteResponse(path: ParsePath, params: Map[String, String], stopRewriting: Boolean) /** * Maintains the context of resolving the URL when cookies are disabled from container. It maintains * low coupling such as code within request processing is not aware of the actual response that * ancodes the URL. */ object RewriteResponse { def apply(path: List[String], params: Map[String, String]) = new RewriteResponse(ParsePath(path, "", true, false), params, false) def apply(path: List[String]) = new RewriteResponse(ParsePath(path, "", true, false), Map.empty, false) def apply(path: List[String], stopRewriting: Boolean) = new RewriteResponse(ParsePath(path, "", true, false), Map.empty, stopRewriting) def apply(path: List[String], suffix: String) = new RewriteResponse(ParsePath(path, suffix, true, false), Map.empty, false) def apply(path: ParsePath, params: Map[String, String]) = new RewriteResponse(path, params, false) } object URLRewriter { private val funcHolder = new ThreadGlobal[(String) => String] def doWith[R](f: (String) => String)(block: => R): R = { funcHolder.doWith(f) { block } } def rewriteFunc: Box[(String) => String] = Box.legacyNullTest(funcHolder value) } Other Lift Framework examples (source code examples)Here is a short list of links related to this Lift Framework Req.scala source code file: |
... this post is sponsored by my books ... | |
![]() #1 New Release! |
![]() FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.