diff --git a/sfu.go b/sfu.go
index 38bffbb..71d45e1 100644
--- a/sfu.go
+++ b/sfu.go
@@ -6,25 +6,14 @@
package main
import (
- "bufio"
- "encoding/json"
- "errors"
"flag"
- "fmt"
- "html"
- "io"
"log"
- "net/http"
"os"
"os/signal"
"path/filepath"
"runtime"
"runtime/pprof"
- "strings"
"syscall"
- "time"
-
- "github.com/gorilla/websocket"
)
var httpAddr string
@@ -94,183 +83,10 @@ func main() {
iceFilename = filepath.Join(staticRoot, "ice-servers.json")
- http.Handle("/", mungeHandler{http.FileServer(http.Dir(staticRoot))})
- http.HandleFunc("/group/",
- func(w http.ResponseWriter, r *http.Request) {
- mungeHeader(w)
- http.ServeFile(w, r, staticRoot+"/sfu.html")
- })
- http.HandleFunc("/ws", wsHandler)
- http.HandleFunc("/public-groups.json", publicHandler)
- http.HandleFunc("/stats", statsHandler)
-
go readPublicGroups()
-
- go func() {
- server := &http.Server{
- Addr: httpAddr,
- ReadTimeout: 60 * time.Second,
- WriteTimeout: 30 * time.Second,
- IdleTimeout: 120 * time.Second,
- }
- var err error
- log.Printf("Listening on %v", httpAddr)
- err = server.ListenAndServeTLS(
- filepath.Join(dataDir, "cert.pem"),
- filepath.Join(dataDir, "key.pem"),
- )
- log.Fatalf("ListenAndServeTLS: %v", err)
- }()
+ webserver()
terminate := make(chan os.Signal, 1)
signal.Notify(terminate, syscall.SIGINT)
<-terminate
}
-
-func mungeHeader(w http.ResponseWriter) {
- w.Header().Add("Content-Security-Policy",
- "connect-src ws: wss: 'self'; img-src data: 'self'; default-src 'self'")
-}
-
-type mungeHandler struct {
- h http.Handler
-}
-
-func (h mungeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- mungeHeader(w)
- h.h.ServeHTTP(w, r)
-}
-
-func publicHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("content-type", "application/json")
- w.Header().Set("cache-control", "no-cache")
-
- if r.Method == "HEAD" {
- return
- }
-
- g := getPublicGroups()
- e := json.NewEncoder(w)
- e.Encode(g)
- return
-}
-
-func getPassword() (string, string, error) {
- f, err := os.Open(filepath.Join(dataDir, "passwd"))
- if err != nil {
- return "", "", err
- }
- defer f.Close()
-
- r := bufio.NewReader(f)
-
- s, err := r.ReadString('\n')
- if err != nil {
- return "", "", err
- }
-
- l := strings.SplitN(strings.TrimSpace(s), ":", 2)
- if len(l) != 2 {
- return "", "", errors.New("couldn't parse passwords")
- }
-
- return l[0], l[1], nil
-}
-
-func statsHandler(w http.ResponseWriter, r *http.Request) {
- bail := func() {
- w.Header().Set("www-authenticate", "basic realm=\"stats\"")
- http.Error(w, "Haha!", http.StatusUnauthorized)
- }
-
- u, p, err := getPassword()
- if err != nil {
- log.Printf("Passwd: %v", err)
- bail()
- return
- }
-
- username, password, ok := r.BasicAuth()
- if !ok || username != u || password != p {
- bail()
- return
- }
-
- w.Header().Set("content-type", "text/html; charset=utf-8")
- w.Header().Set("cache-control", "no-cache")
- if r.Method == "HEAD" {
- return
- }
-
- stats := getGroupStats()
-
- fmt.Fprintf(w, "\n
\n")
- fmt.Fprintf(w, "Stats\n")
- fmt.Fprintf(w, "\n")
-
- printBitrate := func(w io.Writer, rate, maxRate uint64) error {
- var err error
- if maxRate != 0 && maxRate != ^uint64(0) {
- _, err = fmt.Fprintf(w, "%v/%v", rate, maxRate)
- } else {
- _, err = fmt.Fprintf(w, "%v", rate)
- }
- return err
- }
-
- printTrack := func(w io.Writer, t trackStats) {
- fmt.Fprintf(w, " | | | ")
- fmt.Fprintf(w, "")
- printBitrate(w, t.bitrate, t.maxBitrate)
- fmt.Fprintf(w, " | ")
- fmt.Fprintf(w, "%d%% | ",
- t.loss,
- )
- if t.jitter > 0 {
- fmt.Fprintf(w, "%v | ", t.jitter)
- } else {
- fmt.Fprintf(w, " | ")
- }
- fmt.Fprintf(w, "
")
- }
-
- for _, gs := range stats {
- fmt.Fprintf(w, "%v
\n", html.EscapeString(gs.name))
- fmt.Fprintf(w, "")
- for _, cs := range gs.clients {
- fmt.Fprintf(w, "| %v |
\n", cs.id)
- for _, up := range cs.up {
- fmt.Fprintf(w, " | Up | %v |
\n",
- up.id)
- for _, t := range up.tracks {
- printTrack(w, t)
- }
- }
- for _, up := range cs.down {
- fmt.Fprintf(w, " | Down | %v |
\n",
- up.id)
- for _, t := range up.tracks {
- printTrack(w, t)
- }
- }
- }
- fmt.Fprintf(w, "
\n")
- }
- fmt.Fprintf(w, "\n")
-}
-
-var upgrader websocket.Upgrader
-
-func wsHandler(w http.ResponseWriter, r *http.Request) {
- conn, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- log.Printf("Websocket upgrade: %v", err)
- return
- }
- go func() {
- err := startClient(conn)
- if err != nil {
- log.Printf("client: %v", err)
- }
- }()
-}
diff --git a/webserver.go b/webserver.go
new file mode 100644
index 0000000..f9dde98
--- /dev/null
+++ b/webserver.go
@@ -0,0 +1,195 @@
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "html"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+func webserver() {
+ http.Handle("/", mungeHandler{http.FileServer(http.Dir(staticRoot))})
+ http.HandleFunc("/group/",
+ func(w http.ResponseWriter, r *http.Request) {
+ mungeHeader(w)
+ http.ServeFile(w, r, staticRoot+"/sfu.html")
+ })
+ http.HandleFunc("/ws", wsHandler)
+ http.HandleFunc("/public-groups.json", publicHandler)
+ http.HandleFunc("/stats", statsHandler)
+
+ go func() {
+ server := &http.Server{
+ Addr: httpAddr,
+ ReadTimeout: 60 * time.Second,
+ WriteTimeout: 30 * time.Second,
+ IdleTimeout: 120 * time.Second,
+ }
+ var err error
+ err = server.ListenAndServeTLS(
+ filepath.Join(dataDir, "cert.pem"),
+ filepath.Join(dataDir, "key.pem"),
+ )
+ if err != nil {
+ log.Printf("ListenAndServeTLS: %v", err)
+ }
+ }()
+}
+
+func mungeHeader(w http.ResponseWriter) {
+ w.Header().Add("Content-Security-Policy",
+ "connect-src ws: wss: 'self'; img-src data: 'self'; default-src 'self'")
+}
+
+type mungeHandler struct {
+ h http.Handler
+}
+
+func (h mungeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ mungeHeader(w)
+ h.h.ServeHTTP(w, r)
+}
+
+func publicHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("content-type", "application/json")
+ w.Header().Set("cache-control", "no-cache")
+
+ if r.Method == "HEAD" {
+ return
+ }
+
+ g := getPublicGroups()
+ e := json.NewEncoder(w)
+ e.Encode(g)
+ return
+}
+
+func getPassword() (string, string, error) {
+ f, err := os.Open(filepath.Join(dataDir, "passwd"))
+ if err != nil {
+ return "", "", err
+ }
+ defer f.Close()
+
+ r := bufio.NewReader(f)
+
+ s, err := r.ReadString('\n')
+ if err != nil {
+ return "", "", err
+ }
+
+ l := strings.SplitN(strings.TrimSpace(s), ":", 2)
+ if len(l) != 2 {
+ return "", "", errors.New("couldn't parse passwords")
+ }
+
+ return l[0], l[1], nil
+}
+
+func statsHandler(w http.ResponseWriter, r *http.Request) {
+ bail := func() {
+ w.Header().Set("www-authenticate", "basic realm=\"stats\"")
+ http.Error(w, "Haha!", http.StatusUnauthorized)
+ }
+
+ u, p, err := getPassword()
+ if err != nil {
+ log.Printf("Passwd: %v", err)
+ bail()
+ return
+ }
+
+ username, password, ok := r.BasicAuth()
+ if !ok || username != u || password != p {
+ bail()
+ return
+ }
+
+ w.Header().Set("content-type", "text/html; charset=utf-8")
+ w.Header().Set("cache-control", "no-cache")
+ if r.Method == "HEAD" {
+ return
+ }
+
+ stats := getGroupStats()
+
+ fmt.Fprintf(w, "\n\n")
+ fmt.Fprintf(w, "Stats\n")
+ fmt.Fprintf(w, "\n")
+
+ printBitrate := func(w io.Writer, rate, maxRate uint64) error {
+ var err error
+ if maxRate != 0 && maxRate != ^uint64(0) {
+ _, err = fmt.Fprintf(w, "%v/%v", rate, maxRate)
+ } else {
+ _, err = fmt.Fprintf(w, "%v", rate)
+ }
+ return err
+ }
+
+ printTrack := func(w io.Writer, t trackStats) {
+ fmt.Fprintf(w, " | | | ")
+ fmt.Fprintf(w, "")
+ printBitrate(w, t.bitrate, t.maxBitrate)
+ fmt.Fprintf(w, " | ")
+ fmt.Fprintf(w, "%d%% | ",
+ t.loss,
+ )
+ if t.jitter > 0 {
+ fmt.Fprintf(w, "%v | ", t.jitter)
+ } else {
+ fmt.Fprintf(w, " | ")
+ }
+ fmt.Fprintf(w, "
")
+ }
+
+ for _, gs := range stats {
+ fmt.Fprintf(w, "%v
\n", html.EscapeString(gs.name))
+ fmt.Fprintf(w, "")
+ for _, cs := range gs.clients {
+ fmt.Fprintf(w, "| %v |
\n", cs.id)
+ for _, up := range cs.up {
+ fmt.Fprintf(w, " | Up | %v |
\n",
+ up.id)
+ for _, t := range up.tracks {
+ printTrack(w, t)
+ }
+ }
+ for _, up := range cs.down {
+ fmt.Fprintf(w, " | Down | %v |
\n",
+ up.id)
+ for _, t := range up.tracks {
+ printTrack(w, t)
+ }
+ }
+ }
+ fmt.Fprintf(w, "
\n")
+ }
+ fmt.Fprintf(w, "\n")
+}
+
+var upgrader websocket.Upgrader
+
+func wsHandler(w http.ResponseWriter, r *http.Request) {
+ conn, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ log.Printf("Websocket upgrade: %v", err)
+ return
+ }
+ go func() {
+ err := startClient(conn)
+ if err != nil {
+ log.Printf("client: %v", err)
+ }
+ }()
+}