"#httpserver/base.spl" import "#httpserver/static.spl" import "#linkedlist.spl" import use net:http:Server 1048576 net:http:server:=bufsize construct Branch { name commit ; construct { this | with name commit this ; name this:=name commit this:=commit this } json { json | properties props-to-json } } construct Branches { main release loader ; construct { this | with dir this ; func read-commit { s | with file ; [ "unzip" "-p" file "commit" ] StreamTypes:cmd:create:read-to-end<32>:to-str } "Loader" dir "/BaseBand-Loader.jar" concat read-commit Branch:new this:=loader "Broadway" dir "/BaseBand-release.jar" concat read-commit Branch:new this:=release "Iceland" dir "/BaseBand-main.jar" concat read-commit Branch:new this:=main this } json { json | properties props-to-json } } construct Event { event-kind data ; construct { this | with kind data this ; kind this:=event-kind data this:=data this } json { json | properties props-to-json } } construct Object404 { error ; construct { this | with this ; "Endpoint not found" this:=error this } json { json | properties props-to-json } } construct Refreshed { ok ; construct { this | with this ; "true" this:=ok this } json { json | properties props-to-json } } func props-to-json { | with props ; props:last:get<1> dup null eq not if { "\"" swap _str :replace<"\\" "\\\\">:replace<"\"" "\\\""> concat "\"" concat 2 stop } pop "{" props :iter :filter<{ b | :to-stack pop with key ; key ":" eq not key ";" eq not and }> :map<{ s | :to-stack with key value ; "\"" key :replace<"\\" "\\\\">:replace<"\"" "\\\""> concat "\": " concat value properties props-to-json concat }> :join<", "> concat "}" concat } func main { exitcode | with args ; def server Server:new<"::0" 40002> =server while { 1 } { server:accept &handle-client fork; } } def clients LinkedList:new =clients func dir { | with client dir id ; def location "/" id concat =location dir "/BaseBand-release.jar" concat location "/download/client/release" concat client:serve-file-cached;<"application/octet-stream"> dir "/BaseBand-main.jar" concat location "/download/client/main" concat client:serve-file-cached;<"application/octet-stream"> dir "/BaseBand-Loader.jar" concat location "/download/loader" concat client:serve-file-cached;<"application/octet-stream"> location "/branches" concat client:path eq if { dir Branches:new:json client:write-ok:write-content-type<"application/json">:write-str-body:finish; } "/refresh" client:path eq if { def event "refresh:" id concat dir Branches:new Event:new:json =event def new-clients LinkedList:new =new-clients clients:foreach<{ | with client ; catch IO { event "\n" concat :to-bytes client:write-exact; client new-clients:push-front; } { pop } }> } } func handle-client { | with client ; client:read; client:path:ends-with<".jar"> if { def path client:path :to-bytes =path path:sub<0 path:len 4 -> :to-str client:=path } "/refresh" client:path eq if { client:server:cached-files if { client:server:cached-files:clear; } } "index.html" "/" client:serve-html-cached; client "rewrite" "1.12.2" dir client "ednieva" "ednieva" dir "/listen" client:path eq if { client :write-ok :write-content-type<"application/x-baseband-events"> :writeln<""> dup:=wrote-body;<1> :writeln<"init" null Event:new:json> :stream dup:flush; clients:push-front; } "/refresh" client:path eq if { Refreshed:new:json client:write-ok:write-content-type<"application/json">:write-str-body:finish; } client:is-open if { Object404:new:json client:write-head<404 "Not Found">:write-content-type<"application/json">:write-str-body:finish; } }