summaryrefslogtreecommitdiff
path: root/package.scala
blob: cee84bcc40538064fca340b8372dde7bb29490ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package scala.js

import akka.actor.{Props, ActorRef, Actor, ActorSystem}
import akka.io
import akka.util.ByteString
import play.api.libs.json.JsArray

import play.api.libs.json.Json
import spray.can.Http
import spray.can.server.websockets.model.Frame
import spray.can.server.websockets.model.OpCode
import spray.can.server.websockets.Sockets
import sbt._
import Keys._

import com.typesafe.config.ConfigFactory
import scala.collection.mutable
import akka.io.Tcp
import spray.http.{StatusCodes, HttpResponse, HttpRequest}
import spray.http.HttpHeaders.Connection
import spray.can.server.websockets.model.OpCode.Text

package object workbench extends sbt.Plugin {
  val refreshBrowsers = taskKey[Unit]("Sends a message to all connected web pages asking them to refresh the page")
  val generateClient = taskKey[File]("generates a .js file that can be embedded in your web page")
  val localUrl = settingKey[(String, Int)]("localUrl")
  val server = settingKey[ActorRef]("local websocket server")
  val fileName = settingKey[String]("name of the generated javascript file")

  implicit val system = ActorSystem(
    "SystemLol",
    config = ConfigFactory.load(ActorSystem.getClass.getClassLoader),
    classLoader = ActorSystem.getClass.getClassLoader
  )

  implicit class pimpedActor(server: ActorRef){
    def send(x: JsArray) = {
      server ! Frame(
        opcode = OpCode.Text,
        data = ByteString(x.toString())
      )
    }
  }

  val buildSettingsX = Seq(
    localUrl := ("localhost", 12345),
    fileName := "workbench.js",
    server := {
      implicit val server = system.actorOf(Props(new SocketServer))
      val host = localUrl.value
      io.IO(Sockets) ! Http.Bind(server, host._1, host._2)
      server
    },

    extraLoggers := {
      val clientLogger = FullLogger{
        new Logger {
          def log(level: Level.Value, message: => String): Unit =
            if(level >= Level.Info) server.value.send(Json.arr("print", level.toString(), message))
          def success(message: => String): Unit = server.value.send(Json.arr("print", message))
          def trace(t: => Throwable): Unit = server.value.send(Json.arr("print", t.toString))
        }
      }
      val currentFunction = extraLoggers.value
      (key: ScopedKey[_]) => clientLogger +: currentFunction(key)
    },
    refreshBrowsers := {
      streams.value.log("Reloading Pages...")
      server.value.send(Json.arr("reload"))
    },
    generateClient := {
      val clientTemplate = IO.readStream(getClass.getClassLoader.getResourceAsStream("workbench_template.js"))
      val transformed = clientTemplate.replace("<host>", localUrl.value._1).replace("<port>", localUrl.value._2.toString)
      val outputFile = (crossTarget in Compile).value / fileName.value
      IO.write(outputFile, transformed)
      outputFile
    }
  )

  class SocketServer extends Actor{
    val sockets: mutable.Set[ActorRef] = mutable.Set.empty
    def receive = {
      case x: Tcp.Connected => sender ! Tcp.Register(self) // normal Http server init

      case req: HttpRequest =>
        // Upgrade the connection to websockets if you think the incoming
        // request looks good
        if (req.headers.contains(Connection("Upgrade"))){
          sender ! Sockets.UpgradeServer(Sockets.acceptAllFunction(req), self)
        }else{
          sender ! HttpResponse(
            StatusCodes.OK,
            entity="i am a cow"
          )
        }

      case Sockets.Upgraded =>
        sockets.add(sender)
        println("Browser Open n=" + sockets.size)

      case f @ Frame(fin, rsv, Text, maskingKey, data) =>
        sockets.foreach(_ ! f.copy(maskingKey=None))

      case _: Tcp.ConnectionClosed =>
        if (sockets.contains(sender)) println("Browser Closed n=" + sockets.size )
        sockets.remove(sender)


      case x =>
    }
  }
}