lappend auto_path "./vendor" package require websocket proc handleConnect {chan addr port} { fileevent $chan readable [list handleRead $chan $addr $port] } proc htmlEscape {s} { string map {& "&" < "<" > ">" "\"" """} $s } proc readFile {filename contentTypeVar} { upvar $contentTypeVar contentType set fd [open $filename r] fconfigure $fd -encoding binary -translation binary set response [read $fd]; close $fd; return $response } proc getDotAsPdf {dot contentTypeVar} { upvar $contentTypeVar contentType set contentType "application/pdf" set fd [open |[list dot -Tpdf <<$dot] r] fconfigure $fd -encoding binary -translation binary set response [read $fd]; close $fd; return $response } proc handlePage {path httpStatusVar contentTypeVar} { upvar $contentTypeVar contentType switch -exact -- $path { "/" { set l [list] dict for {id stmt} [Statements::all] { lappend l [subst {
[htmlEscape [statement clause $stmt]]
[htmlEscape [lindex $program 1]]
}} }] "\n"]
}
}
"/timings" {
set totalTimes [list]
dict for {body totalTime} $Evaluator::totalTimesMap {
dict with totalTime {
lappend totalTimes $body [expr {$loadTime + $runTime + $unloadTime}]
}
}
set totalTimes [lsort -integer -stride 2 -index 1 $totalTimes]
set totalFrameTime 0
set l [list]
foreach {body totalTime} $totalTimes {
set runs [dict get $Evaluator::runsMap $body]
set totalFrameTime [expr {$totalFrameTime + $totalTime/$::stepCount}]
lappend l [subst {
[htmlEscape $body]($runs runs): [dict get $Evaluator::totalTimesMap $body]: $totalTime microseconds total ([expr {$totalTime/$::stepCount}] us per frame), $runs runs ([expr {$totalTime/$runs}] us per run; [expr {$runs/$::stepCount}] runs per frame)
[htmlEscape $e]: [htmlEscape $::errorInfo]}] set response [dict create statusAndHeaders "HTTP/1.1 500 Internal Server Error\nConnection: close\nContent-Type: $contentType\n\n" body $body] } puts -nonewline $chan [dict get $response statusAndHeaders] if {[dict exists $response body]} { chan configure $chan -encoding binary -translation binary puts -nonewline $chan [dict get $response body] } close $chan } elseif {[::websocket::test $::serverSock $chan "/ws" $headers]} { # puts "WS: $chan $addr $port" ::websocket::upgrade $chan # from now the handleWS will be called (not anymore handleRead). } else { puts "Closing: $chan $addr $port $headers"; close $chan } } proc handleWS {chan type msg} { if {$type eq "connect" || $type eq "ping" || $type eq "pong"} { # puts "Event $type from chan $chan" } elseif {$type eq "text"} { eval $msg } elseif {$type eq "disconnect"} { Commit $chan statements {} foreach peerNs [namespace children ::Peers] { apply [list {disconnectedChan} { variable chan if {$chan eq $disconnectedChan} { namespace delete [namespace current] } } $peerNs] $chan } } else { puts "$::thisProcess: Unhandled WS event $type on $chan ($msg)" } } if {[catch {set ::serverSock [socket -server handleConnect 4273]}] == 1} { error "There's already a Web-capable Folk node running on this machine." } ::websocket::server $::serverSock ::websocket::live $::serverSock /ws handleWS