Where communities thrive

  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
Repo info
    I added ImageIO code, so png files are loaded correctly now. I will use 12 Monkeys imageio library, tomorrow, to load the favicon.ico file. I'll keep you posted.
    Well I added the twelve monkeys dependency, which provides extended ImageIO support for ico and the like. So, now, I no longer get a NPE; yet the ico is not loaded by the browser correctly. I'm on an Apple M1. Here's the code:
    import cask.main.Routes
    import cask.model.{Request, Response}
    import com.typesafe.scalalogging.LazyLogging
    import java.awt.image.BufferedImage
    import java.io.ByteArrayOutputStream
    import javax.imageio.ImageIO
    import scala.io.{Codec, Source}
    import scala.util.{Try, Using}
    trait Resources(val basePath: String) extends LazyLogging:
      val utf8 = Codec.UTF8.name
      val contentType = "Content-Type"
      val indexHtml = loadResource("index.html")
      val indexHtmlHeader = contentType -> "text/html; charset=UTF-8"
      def toContentType(resource: String): String = resource.split('.').last
      def toPath(resource: String): String = s"$basePath$resource"
      def toHeader(resource: String): (String, String) =
        logger.debug(s"*** to header: ${toContentType(resource)}")
        toContentType(resource) match
          case "css"  => contentType -> "text/css"
          case "ico"  => contentType -> "image/x-icon"
          case "png"  => contentType -> "image/png"
          case "js"   => contentType -> "text/javascript"
          case "map"  => contentType -> "application/json"
          case "html" => indexHtmlHeader
          case _      => contentType -> "text/plain"
      def isImage(resource: String): Boolean =
        toContentType(resource) match
          case "ico" | "png"  => true
          case _              => false
      def loadResource(resource: String): Array[Byte] =
        val path = toPath(resource)
        logger.debug(s"*** load resource: $path")
        Using( Source.fromInputStream(getClass.getResourceAsStream(path), utf8) ) {
          source => source.mkString.getBytes
      def loadImage(resource: String): Array[Byte] =
        val path = toPath(resource)
        logger.debug(s"*** load image: $path")
        val url = getClass.getResource(path)
        val image = ImageIO.read(url)
        val baos = new ByteArrayOutputStream()
        val contentType = toContentType(resource)
        ImageIO.write(image, contentType, baos)
    class Router(dispatcher: Dispatcher) extends Routes with LazyLogging with Resources("/public/"):
      def index() = Response(indexHtml, 200, Seq(indexHtmlHeader))
      @cask.get(basePath, subpath = true)
      def resources(request: Request) =
        val resource = request.remainingPathSegments.head
        val headers = Seq(toHeader(resource))
        if isImage(resource) then Response(loadImage(resource), 200, headers)
        else Response(loadResource(resource), 200, headers)
    That's effectively what I was hoping cask.staticResources would do for me. Cheers!
    I corrected the favicon.ico loading error by replacing the true ico file with a png file --- still named favicon.ico, of course. :)
    FWIW, this project contains, among other examples, an updated Resources and ResourceRouter: https://github.com/objektwerks/cask
    Stewart Stewart

    Suppose I wanted to use a custom type in a path, like

    def user(id: java.util.UUID)

    Would it be appropriate to implement a custom cask.endpoints.PathReader[UUID]?
    Or is it better to parse an id string in the body and return a 400?

    Li Haoyi
    either should work
    FYI, I've built a CorsHandler that works in initial tests. See:
    Naturally, this can be built multiple ways. Note how a CorsHandler is initialized in Server, which extends cask.Main.
    I've posted the above info to cask/github #31 should someone want to run with this initial effort. Cheers!
    the autowire project seemed a little bit awesome. Is there some way to get that kind of strong typing with cask?
    Hello there!
    do you happen to have good resources/idiomatic examples showing how to manage users, authentication and sessions with cask?
    Lucas Della Bella
    Hey there! I'm looking for a web server to serve a sangria (graphql) api from. I like the simplicity of cask but I'm not really sure what trade-offs I'd need to be considering. Does anyone have thoughts or opinions?
    Li Haoyi
    Cask is pretty bare bones; it basically only does routing and not much else. That can be good or bad depending on what you want
    @lihaoyi: I'm a bit wary to roll my own authentication and session management, I don't trust myself to do that safely. Could that eventually be absorbed in the framework's features set as an optional thing?
    An unrelated question, has anyone attempted to compile a cask app to native (via GraalVM's native-image)? That would be super interesting for me
    1 reply
    Li Haoyi
    it could, if someone contributes. But right now, Cask only knows about requests, responses, headers and cookies. Higher level concepts like auth or sessions would have to be built on top
    and that requires state, hence persistence, hence caching and loads of opinionated ways to do it :)
    I’m testing cask from my iPad+pi and sbt, is there a way to keep the app running(It auto closed after hitting sbt run), or I necessarily need to use mill?
    Raul Rodriguez

    Hello guys, do you know how can I run a cask project with sbt.

    object RadarApp extends cask.Main:
      val allRoutes: Seq[ApiRoutes] = Seq(ApiRoutes())
      override def port: Int = 8888
      override def main(args: Array[String]): Unit = super.main(args)

    When running it as

    sbt "run com.example.RadarApp"

    I get the following message:

    [info] running com.example.RadarApp com.example.RadarApp
    Jun 10, 2022 8:36:28 PM org.jboss.threads.Version <clinit>
    INFO: JBoss Threads version 3.1.0.Final
    [success] Total time: 1 s, completed Jun 10, 2022, 8:36:28 PM
    Raul Rodriguez
    running sbt "~run com.example.RadarApp" keeps the server running, though any change in the source code throws an address error

    Sorry to post the same question again (didn't know this channel exists).

    I am learning to use cask (https://com-lihaoyi.github.io/cask/#getting-started). But I don't use mill as build tool. Instead I use sbt. In that case, after typing sbt run in the command line, following messages are returned. However accessing to http://localhost:8080 shows Unable to connect, unless run sbt process first, and then type run or runMain .... How can I launch the server without executing sbt process? Thanks

    info] running metricsapp.App 
    Jun 24, 2022 3:42:34 PM org.jboss.threads.Version <clinit>
    INFO: JBoss Threads version 3.1.0.Final
    [success] Total time: 1 s, completed Jun 24, 2022, 3:42:34 PM
    Siddhartha Gadgil

    I was trying to upgrade scala from 2.13.3 to 2.13.8 and various dependencies, and I am getting a strange error because of a change in behaviour for static resources, which I am using for css and javascript (the latter built with mill and copied).

    • when I go to the path to my css file, http://localhost:8080/public/css/bootstrap.min.css the browser insists on downloading the file, not showing it.
    • it seems that (probably for the same reason) bootstrap is not loading in the page calling it.

    The code with the static resource is:

      def staticResourceRoutes() = "."

    The strangest thing is I got this error before I updated cask (but updated other stuff) and also after. The changes are in scalaVersion, scalaJSVersion, os-lib, upickle and scalatags.

    It seems to me that I should set some header, but I cannot figure out what. Any help is appreciated.

    Siddhartha Gadgil

    Further, when I try to change the code to:

          headers = Seq("Content-Type" -> "text/plain"))
      def staticResourceRoutes() = "."

    I get the error:

    [error] Error while emitting Server.scala
    [error] value x$3
    Siddhartha Gadgil
    Actually, I redid things in a cleaner way, so the above does not matter (explicitly returning content types after reading resources).