Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package skinny.micro.request
import java.io.InputStream
import java.net.URI
import java.util.Locale
import javax.servlet.http.HttpServletRequest
import skinny.micro._
import skinny.micro.constant._
import skinny.micro.data.{ AttributesMap, MultiMap, MultiMapHeadView }
import skinny.micro.implicits.RicherStringImplicits
import scala.collection.JavaConverters._
import scala.collection.{ Map => CMap }
import scala.io.Source
/**
* Rich Servlet request.
*/
case class RichRequest(r: HttpServletRequest) extends AttributesMap {
import RichRequest._
import RicherStringImplicits._
/**
* The version of the protocol the client used to send the request.
* Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may
* be used by the application to determine how to treat any HTTP request
* headers.
*/
def serverProtocol: HttpVersion = r.getProtocol match {
case "HTTP/1.1" => Http11
case "HTTP/1.0" => Http10
}
def uri: URI = new URI(r.getRequestURL.toString)
/**
* Http or Https, depending on the request URL.
*/
def urlScheme: Scheme = r.getScheme match {
case "http" => Http
case "https" => Https
}
/**
* The HTTP request method, such as GET or POST
*/
def requestMethod: HttpMethod = HttpMethod(r.getMethod)
/**
* The remainder of the request URL's "path", designating the virtual
* "location" of the request's target within the application. This may be
* an empty string, if the request URL targets the application root and
* does not have a trailing slash.
*/
def pathInfo: String = Option(r.getPathInfo) getOrElse ""
/**
* The initial portion of the request URL's "path" that corresponds to
* the application object, so that the application knows its virtual
* "location". This may be an empty string, if the application corresponds
* to the "root" of the server.
*/
def scriptName: String = r.getServletPath
/**
* The portion of the request URL that follows the ?, if any. May be
* empty, but is always required!
*/
def queryString: String = Option(r.getQueryString) getOrElse ""
/**
* A Map of the parameters of this request. Parameters are contained in
* the query string or posted form data.
*/
def multiParameters: MultiParams = {
val bodyParams: Map[String, Seq[String]] = {
if (r.getParameterMap.isEmpty
&& r.getMethod != null
&& !HttpMethod(r.getMethod).isSafe
&& r.getHeader("Content-Type") != null
&& r.getHeader("Content-Type").split(";")(0).trim.equalsIgnoreCase("APPLICATION/X-WWW-FORM-URLENCODED")) {
rl.MapQueryString.parseString(body)
} else {
Map.empty
}
}
// At the very least in jetty 8 we see problems under load related to this
if (r.getQueryString.nonBlank && r.getParameterMap.isEmpty) {
val queryStringParams: Map[String, Seq[String]] = rl.MapQueryString.parseString(r.getQueryString)
queryStringParams ++ bodyParams
} else {
val paramMap = r.getParameterMap.asScala.toMap.transform { (_, v) => v.toIndexedSeq }
paramMap ++ bodyParams
}
}
object parameters extends MultiMapHeadView[String, String] {
protected def multiMap: MultiParams = multiParameters
}
/**
* A map of headers. Multiple header values are separated by a ','
* character. The keys of this map are case-insensitive.
*/
val headers = new RequestHeaders(r)
def header(name: String): Option[String] = Option(r.getHeader(name))
def headers(name: String): Seq[String] = Option(r.getHeaders(name)).map(_.asScala.toSeq).getOrElse(Seq.empty)
def headerNames: Seq[String] = Option(r.getHeaderNames).map(_.asScala.toSeq).getOrElse(Seq.empty)
/**
* Returns the name of the character encoding of the body, or None if no
* character encoding is specified.
*/
def characterEncoding: Option[String] = Option(r.getCharacterEncoding)
def characterEncoding_=(encoding: Option[String]): Unit = {
r.setCharacterEncoding(encoding getOrElse null)
}
/**
* The content of the Content-Type header, or None if absent.
*/
def contentType: Option[String] = Option(r.getContentType)
/**
* Returns the length, in bytes, of the body, or None if not known.
*/
def contentLength: Option[Long] = r.getContentLength match {
case -1 => None
case length => Some(length)
}
/**
* When combined with scriptName, pathInfo, and serverPort, can be used to
* complete the URL. Note, however, that the "Host" header, if present,
* should be used in preference to serverName for reconstructing the request
* URL.
*/
def serverName: String = r.getServerName
/**
* When combined with scriptName, pathInfo, and serverName, can be used to
* complete the URL. See serverName for more details.
*/
def serverPort: Int = r.getServerPort
/**
* Optionally returns the HTTP referrer.
*
* @return the `Referer` header, or None if not set
*/
def referrer: Option[String] = r.getHeader("Referer") match {
case s: String => Some(s)
case null => None
}
def referer: Option[String] = referrer
/**
* Optionally returns User-Agent header.
*
* @return the `User-Agent` header, or None if not set
*/
def userAgent: Option[String] = {
Option(r.getHeader("User-Agent")).filterNot(_.isEmpty).orElse {
// Tomcat accepts only "user-agent"
Option(r.getHeader("user-agent")).filterNot(_.isEmpty)
}
}
/**
* Caches and returns the body of the response. The method is idempotent
* for any given request. The result is cached in memory regardless of size,
* so be careful. Calling this method consumes the request's input stream.
*
* @return the message body as a string according to the request's encoding
* (defult ISO-8859-1).
*/
def body: String = {
cachedBody getOrElse {
val encoding = r.getCharacterEncoding
val enc = if (encoding == null || encoding.trim.length == 0) {
if (contentType.exists(_ equalsIgnoreCase "application/json")) "UTF-8" else "ISO-8859-1"
} else encoding
val body: String = try {
Source.fromInputStream(r.getInputStream, enc).mkString
} catch { case e: java.io.IOException => "" }
update(cachedBodyKey, body)
body
}
}
private def cachedBody: Option[String] =
get(cachedBodyKey).flatMap(_.asInstanceOf[String].blankOption)
def isHttps: Boolean = {
// also respect load balancer version of the protocol
val h = r.getHeader("X-Forwarded-Proto").blankOption
r.isSecure || (h.isDefined && h.forall(_ equalsIgnoreCase "HTTPS"))
}
/**
* Returns true if the request is an AJAX request
*/
def isAjax: Boolean = r.getHeader("X-Requested-With") != null
/**
* Returns true if the request's method is not "safe" per RFC 2616.
*/
def isWrite: Boolean = !HttpMethod(r.getMethod).isSafe
/**
* Returns a map of cookie names to lists of their values.
* The default value of the map is the empty sequence.
*/
def multiCookies: MultiMap = {
val rr = Option(r.getCookies).getOrElse(Array()).toSeq.
groupBy { _.getName }.
transform { case (k, v) => v map { _.getValue } }.
withDefaultValue(Seq.empty)
MultiMap(rr)
}
/**
* Returns values of the request cookie.
*/
def multiCookie(name: String): Seq[String] = {
multiCookies.find { case (key, _) => key == name }.
map { case (_, value) => value }.
getOrElse(Seq.empty)
}
/**
* Returns a map of cookie names to values. If multiple values are present
* for a given cookie, the value is the first cookie of that name.
*/
def cookies: CMap[String, String] = {
new MultiMapHeadView[String, String] {
override protected def multiMap = multiCookies
}
}
/**
* Returns a value of the request cookie.
*/
def cookie(name: String): Option[String] = {
cookies.
find { case (key, _) => key == name }.
map { case (_, value) => value }
}
protected[skinny] def attributes: HttpServletRequest = r
/**
* The input stream is an InputStream which contains the raw HTTP POST
* data. The caller should not close this stream.
*
* In contrast to Rack, this stream is not rewindable.
*/
def inputStream: InputStream = r.getInputStream
/**
* The remote address the client is connected from.
* This takes the load balancing header X-Forwarded-For into account
* @return the client ip address
*/
def remoteAddress: String = header("X-FORWARDED-FOR").flatMap(_.blankOption) getOrElse r.getRemoteAddr
def locale: Locale = r.getLocale
def locales: Seq[Locale] = {
// Although javadoc says "If the client request doesn't provide an Accept-Language header,
// this method returns an Enumeration containing one Locale, the default locale for the server.",
// just to be safe, care about null value here
Option(r.getLocales).map(_.asScala.toSeq).getOrElse(Nil)
}
}
object RichRequest {
private val cachedBodyKey: String = "skinny.micro.RichRequest.cachedBody"
}