Permalink
Browse files

Created ProxyCache to support locally caching remote URLs

Added event.link to simplify linking to paths or URLs
Added ByMultiple Selector implementation to support multiple selectors
  • Loading branch information...
darkfrog26 committed Jun 11, 2018
1 parent 5251302 commit 6b084fcc5165d2ac9b3fafbedb01d5f159794f0c
@@ -29,6 +29,7 @@ class Header extends Container {
size.height := 117.0
position.left := 25.0
position.top := 4.0
event.link("/ui-examples.html")
}
val title: HTMLTextView = new HTMLTextView {
@@ -3,8 +3,9 @@ package io.youi.example
import io.youi.app._
import io.youi.http._
import io.youi.net._
import io.youi.server.handler.CachingManager
import io.youi.server.handler.{CachingManager, ProxyCache}
import io.youi.server.dsl._
import io.youi.stream._
object ServerExampleApplication extends ExampleApplication with ServerApplication {
val generalPages: Page = page(GeneralPages)
@@ -13,22 +14,28 @@ object ServerExampleApplication extends ExampleApplication with ServerApplicatio
override protected def applicationJSBasePath = s"/app/example"
override def start(): Unit = {
// TODO: add support to `Connection` to add deltas so they may all be processed at the end
proxies += ProxyHandler(path.exact("/proxy.html")) { url =>
URL("http://google.com").copy(path = url.path)
}
handler(
filters(
path"/hello.txt" /
CachingManager.MaxAge(120L) /
"Hello, World!".withContentType(ContentType.`text/plain`),
path"/hello.txt" / CachingManager.MaxAge(120L) / "Hello, World!".withContentType(ContentType.`text/plain`),
combined.any(
path.matches("/examples/.*[.]html"),
path.exact("/ui-examples.html")
) / Application / ServerApplication.AppTemplate,
path"/cookies.html" /
CookiesExample,
path"/session.html" /
SessionExample,
) / Application / Delta.Process(
selector = ByTag("script"),
replace = true,
onlyOpenTag = true,
processor = (tag, content) => {
scribe.info(s"Process: $content")
content
}
) / ServerApplication.AppTemplate,
path"/cookies.html" / CookiesExample,
path"/session.html" / SessionExample,
path"/cache" / ProxyCache(),
ClassLoaderPath(pathTransform = (path: String) => s"content$path") / CachingManager.LastModified(),
path.startsWith("/app") / ClassLoaderPath()
)
@@ -8,7 +8,7 @@ import io.youi.net.{ContentType, IP, Path, URLMatcher}
import io.youi.server.handler._
import io.youi.server.rest.Restful
import io.youi.server.validation.{ValidationResult, Validator}
import io.youi.stream.{Delta, HTMLParser, Selector}
import io.youi.stream.{Delta, HTMLParser, Selector, StreamableHTML}
import scala.language.implicitConversions
import scala.xml.Elem
@@ -37,14 +37,17 @@ package object dsl {
implicit class DeltasFilter(val deltas: List[Delta]) extends ActionFilter(processDeltas(_, deltas))
implicit class DeltaFilter(delta: Delta) extends ActionFilter(processDeltas(_, List(delta)))
implicit class StringFilter(val s: String) extends ConnectionFilter {
override def filter(connection: HttpConnection): Option[HttpConnection] = PathPart.take(connection, s)
}
private[server] def processDeltas(connection: HttpConnection, deltas: List[Delta] = Nil): Unit = {
scribe.info(s"processing deltas: $deltas")
connection.response.content match {
case Some(content) => {
val stream = content match {
val stream: StreamableHTML = content match {
case c: FileContent => HTMLParser.cache(c.file)
case c: URLContent => HTMLParser.cache(c.url)
case c: StringContent => HTMLParser.cache(c.value)
@@ -59,7 +62,11 @@ package object dsl {
}
}
}
case None => // No content
case None => { // No content
if (deltas.nonEmpty) {
scribe.warn(s"Not content set for processing of deltas: $deltas")
}
}
}
}
@@ -0,0 +1,31 @@
package io.youi.server.handler
import java.net.{URL, URLEncoder}
import java.nio.file.{Path, Paths}
import io.youi.http.{Content, HttpConnection, HttpStatus}
import io.youi.net.ContentType
import org.powerscala.io._
case class ProxyCache(directory: Path = Paths.get(System.getProperty("java.io.tmpdir"))) extends HttpHandler {
override def handle(connection: HttpConnection): Unit = connection.request.url.param("url") match {
case Some(cacheURL) => {
val url = new URL(cacheURL)
val fileName = URLEncoder.encode(cacheURL, "UTF-8")
val file = directory.resolve(fileName).toAbsolutePath.toFile
if (!file.exists()) {
scribe.info(s"Downloading $cacheURL...")
val tmp = directory.resolve(s"$fileName.tmp").toAbsolutePath.toFile
IO.stream(url, tmp)
IO.stream(tmp, file)
if (!tmp.delete()) {
tmp.deleteOnExit()
}
} else {
scribe.info(s"$cacheURL already exists")
}
connection.update(_.withContent(Content.file(file)).withStatus(HttpStatus.OK))
}
case None => connection.update(_.withStatus(HttpStatus.BadRequest).withContent(Content.string("Must include `url` as GET parameter", ContentType.`text/plain`)))
}
}
@@ -16,6 +16,10 @@ case class ByTag(tagName: String) extends Selector {
override def lookup(streamable: StreamableHTML): Set[OpenTag] = streamable.byTag.getOrElse(tagName, Set.empty)
}
case class ByMultiple(selectors: Selector*) extends Selector {
override def lookup(streamable: StreamableHTML): Set[OpenTag] = selectors.flatMap(_.lookup(streamable)).toSet
}
object Selector {
def parse(value: String): Selector = if (value.startsWith("#")) {
ById(value.substring(1))
@@ -2,6 +2,7 @@ package io.youi.event
import io.youi._
import io.youi.component.Component
import io.youi.net.URL
import org.scalajs.dom.html
import org.scalajs.{dom => jsdom}
import reactify.{Channel, Val, Var}
@@ -68,6 +69,13 @@ trait EventSupport {
protected def events[E <: jsdom.Event](eventType: String, stopPropagation: Boolean = false): Channel[E]
protected def pointerChannel(`type`: PointerEvent.Type): Channel[PointerEvent]
protected def wheelChannel(): Channel[WheelEvent]
def link(url: URL): Unit = {
component.cursor := Cursor.Pointer
click.on(History.push(url))
}
def link(path: String): Unit = link(History.url().withPart(path))
}
class HTMLEvents(override protected val component: Component, element: html.Element) extends EventSupport {

0 comments on commit 6b084fc

Please sign in to comment.