Permalink
Browse files

Continued work on DSL for server

  • Loading branch information...
darkfrog26 committed Mar 30, 2018
1 parent 4b5433c commit 54e9e677e60557cb391d1c312f60e417273956af
@@ -1,7 +1,7 @@
sudo: false
language: scala
scala:
- 2.12.3
- 2.12.5
jdk:
- oraclejdk8
install:
@@ -2,8 +2,9 @@ package io.youi.example
import io.youi.app._
import io.youi.http._
import io.youi.net.{ContentType, URL}
import io.youi.net._
import io.youi.server.handler.CachingManager
import io.youi.server.dsl._
object ServerExampleApplication extends ExampleApplication with ServerApplication {
val uiExamples: Page = page(UIExamples)
@@ -13,18 +14,40 @@ object ServerExampleApplication extends ExampleApplication with ServerApplicatio
override protected def applicationJSBasePath = s"/app/example"
override def start(): Unit = {
handler.matcher(path.exact("/hello.txt")).caching(CachingManager.MaxAge(120L)).resource {
Content.string("Hello World!", ContentType.`text/plain`)
}
handler.matcher(path.exact("/bootstrap.html")).bootstrap()
handler.matcher(path.exact("/cookies.html")).wrap(CookiesExample)
handler.matcher(path.exact("/web-socket-example")).wrap(WebSocketExample)
proxies += ProxyHandler(path.exact("/proxy.html")) { url =>
URL("http://google.com").copy(path = url.path)
}
handler.matcher(path.exact("/session.html")).wrap(SessionExample)
handler.caching(CachingManager.LastModified()).classLoader("", (path: String) => s"content$path")
handler.caching(CachingManager.LastModified()).matcher(path.startsWith("/app")).classLoader()
handler(
filters(
path"/hello.txt" ->
CachingManager.MaxAge(120L) ->
"Hello, World!".withContentType(ContentType.`text/plain`),
path"/bootstrap.html" ->
Application ->
ServerApplication.BootstrapTemplate,
path"/cookies.html" ->
CookiesExample,
path"/web-socket-example" ->
WebSocketExample,
path"/session.html" ->
SessionExample,
ClassLoaderPath(pathTransform = (path: String) => s"content$path") -> CachingManager.LastModified(),
path.startsWith("/app") -> ClassLoaderPath()
)
)
// handler.matcher(path.exact("/hello.txt")).caching(CachingManager.MaxAge(120L)).resource {
// Content.string("Hello World!", ContentType.`text/plain`)
// }
// handler.matcher(path.exact("/bootstrap.html")).bootstrap()
// handler.matcher(path.exact("/cookies.html")).wrap(CookiesExample)
// handler.matcher(path.exact("/web-socket-example")).wrap(WebSocketExample)
// proxies += ProxyHandler(path.exact("/proxy.html")) { url =>
// URL("http://google.com").copy(path = url.path)
// }
// handler.matcher(path.exact("/session.html")).wrap(SessionExample)
// handler.caching(CachingManager.LastModified()).classLoader("", (path: String) => s"content$path")
// handler.caching(CachingManager.LastModified()).matcher(path.startsWith("/app")).classLoader()
super.start()
}

This file was deleted.

Oops, something went wrong.
@@ -8,4 +8,9 @@ addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.22")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.3")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.3")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.3")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1")
addSbtPlugin("com.codacy" % "sbt-codacy-coverage" % "1.3.12")
@@ -1,14 +1,35 @@
package io.youi.server
import io.youi.http.{Content, HttpConnection, Method, Status}
import io.youi.net.{ContentType, IP, Path}
import io.youi.server.handler.{ContentHandler, HttpHandler}
import java.io.File
import io.youi.http.{Content, FileContent, HttpConnection, Method, Status, StringContent, URLContent}
import io.youi.net.{ContentType, IP, Path, URLMatcher}
import io.youi.server.handler.{CachingManager, ContentHandler, HttpHandler, SenderHandler}
import io.youi.stream.{ByTag, Delta, HTMLParser, Selector}
import scala.language.implicitConversions
import scala.util.matching.Regex
import scala.xml.Elem
package object dsl {
private val DeltaKey: String = "deltas"
protected def applicationBasePath: String = "app/application"
private val fullOpt = s"$applicationBasePath.js"
private val fastOpt = s"$applicationBasePath-fastopt.js"
private val fullOptMap = s"$fullOpt.map"
private val fastOptMap = s"$fastOpt.map"
private val jsDeps = s"$applicationBasePath-jsdeps.js"
protected def applicationJSBasePath: String = "/app/application"
def applicationJSPath: String = s"$applicationJSBasePath.js"
def applicationJSMapPath: String = s"$applicationJSPath.map"
def applicationJSDepsPath: String = s"$applicationJSBasePath-jsdeps.js"
lazy val applicationJSContent: Content = Content.classPathOption(fullOpt).getOrElse(Content.classPath(fastOpt))
lazy val applicationJSMapContent: Content = Content.classPathOption(fullOptMap).getOrElse(Content.classPath(fastOptMap))
lazy val applicationJSDepsContent: Option[Content] = Content.classPathOption(jsDeps)
implicit class HttpConnectionConnectionFilter(val connection: HttpConnection) extends ConnectionFilter {
override def filter(connection: HttpConnection): Option[HttpConnection] = Some(connection)
}
@@ -28,13 +49,103 @@ package object dsl {
}
}
implicit class CachingManagerFilter(val caching: CachingManager) extends ConnectionFilter {
override def filter(connection: HttpConnection): Option[HttpConnection] = {
caching.handle(connection)
Some(connection)
}
}
implicit class DeltasFilter(val deltas: List[Delta]) extends ConnectionFilter {
override def filter(connection: HttpConnection): Option[HttpConnection] = {
connection.response.content match {
case Some(content) => {
val stream = content match {
case c: FileContent => HTMLParser.cache(c.file)
case c: URLContent => HTMLParser.cache(c.url)
case c: StringContent => HTMLParser.cache(c.value)
}
val deltasList = connection.store.getOrElse[List[Delta]](DeltaKey, Nil) ::: deltas
val selector = connection.request.url.param("selector").map(Selector.parse)
val streamed = stream.stream(deltasList, selector)
connection.update { response =>
response.withContent(Content.string(streamed, content.contentType))
}
}
case None => // No content
}
Some(connection)
}
}
implicit class URLMatcherFilter(val matcher: URLMatcher) extends ConnectionFilter {
override def filter(connection: HttpConnection): Option[HttpConnection] = {
if (matcher.matches(connection.request.url)) {
Some(connection)
} else {
None
}
}
}
case class ClassLoaderPath(directory: String = "", pathTransform: String => String = (s: String) => s) extends ConnectionFilter {
private val dir = if (directory.endsWith("/")) {
directory.substring(directory.length - 1)
} else {
directory
}
override def filter(connection: HttpConnection): Option[HttpConnection] = {
val path = pathTransform(connection.request.url.path.decoded)
val resourcePath = s"$dir$path" match {
case s if s.startsWith("/") => s.substring(1)
case s => s
}
Option(getClass.getClassLoader.getResource(resourcePath))
.map(url => new File(url.getFile))
.filterNot(_.isDirectory)
.map { file =>
SenderHandler(Content.file(file)).handle(connection)
connection
}
}
}
object Application extends ConnectionFilter {
override def filter(connection: HttpConnection): Option[HttpConnection] = {
val jsDeps = if (applicationJSDepsContent.nonEmpty) {
s"""<script src="$applicationJSDepsPath"></script>"""
} else {
""
}
val applicationDeltas = List(
Delta.InsertLastChild(ByTag("body"),
s"""
|$jsDeps
|<script src="$applicationJSPath"></script>
|<script>
| application();
|</script>
""".stripMargin
)
)
val deltas = connection.store.getOrElse[List[Delta]](DeltaKey, Nil) ::: applicationDeltas
connection.store(DeltaKey) = deltas
Some(connection)
}
}
implicit def content2Filter(content: Content): ConnectionFilter = {
new HttpHandlerFilter(ContentHandler(content, Status.OK))
}
implicit def path2AllowFilter(path: Path): ConnectionFilter = PathFilter(path)
def filters(filters: ConnectionFilter*): ConnectionFilter = ListConnectionFilter(filters.toList)
def allow(ips: IP*): ConnectionFilter = IPAddressFilter(allow = ips.toList)
def allow(path: Regex): ConnectionFilter = PathRegexFilter(path)
def allow(path: Path): ConnectionFilter = PathFilter(path)
def respond(content: Content, status: Status = Status.OK): ContentHandler = {

0 comments on commit 54e9e67

Please sign in to comment.