Initial commit

This commit is contained in:
Alexey Khit
2022-08-18 09:19:00 +03:00
commit 3e77835583
65 changed files with 6372 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
package api
import (
"encoding/json"
"github.com/AlexxIT/go2rtc/cmd/app"
"github.com/AlexxIT/go2rtc/cmd/streams"
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/gorilla/websocket"
"github.com/rs/zerolog"
"net"
"net/http"
)
func Init() {
var cfg struct {
Mod struct {
Listen string `yaml:"listen"`
BasePath string `yaml:"base_path"`
StaticDir string `yaml:"static_dir"`
} `yaml:"api"`
}
// default config
cfg.Mod.Listen = ":3000"
cfg.Mod.StaticDir = "www"
// load config from YAML
app.LoadConfig(&cfg)
if cfg.Mod.Listen == "" {
return
}
basePath = cfg.Mod.BasePath
log = app.GetLogger("api")
if cfg.Mod.StaticDir != "" {
fileServer = http.FileServer(http.Dir(cfg.Mod.StaticDir))
HandleFunc("/", fileServerHandlder)
}
HandleFunc("/api/stack", stackHandler)
HandleFunc("/api/stats", statsHandler)
HandleFunc("/api/ws", apiWS)
// ensure we can listen without errors
listener, err := net.Listen("tcp", cfg.Mod.Listen)
if err != nil {
log.Fatal().Err(err).Msg("[api] listen")
}
log.Info().Str("addr", cfg.Mod.Listen).Msg("[api] listen")
go func() {
s := http.Server{}
if err = s.Serve(listener); err != nil {
log.Fatal().Err(err).Msg("[api] Serve")
}
}()
}
func HandleFunc(pattern string, handler http.HandlerFunc) {
http.HandleFunc(basePath+pattern, handler)
}
func HandleWS(msgType string, handler WSHandler) {
wsHandlers[msgType] = handler
}
var basePath string
var fileServer http.Handler
var log zerolog.Logger
var wsHandlers = make(map[string]WSHandler)
func fileServerHandlder(w http.ResponseWriter, r *http.Request) {
if basePath != "" {
r.URL.Path = r.URL.Path[len(basePath):]
}
fileServer.ServeHTTP(w, r)
}
func statsHandler(w http.ResponseWriter, _ *http.Request) {
v := map[string]interface{}{
"streams": streams.Streams,
}
data, err := json.Marshal(v)
if err != nil {
log.Error().Err(err).Msg("[api.stats] marshal")
}
if _, err = w.Write(data); err != nil {
log.Error().Err(err).Msg("[api.stats] write")
}
}
func apiWS(w http.ResponseWriter, r *http.Request) {
ctx := new(Context)
if err := ctx.Upgrade(w, r); err != nil {
log.Error().Err(err).Msg("[api.ws] upgrade")
return
}
defer ctx.Close()
for {
msg := new(streamer.Message)
if err := ctx.Conn.ReadJSON(msg); err != nil {
if websocket.IsUnexpectedCloseError(
err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure,
) {
log.Error().Err(err).Msg("[api.ws] readJSON")
}
return
}
handler := wsHandlers[msg.Type]
if handler != nil {
handler(ctx, msg)
}
}
}
+52
View File
@@ -0,0 +1,52 @@
package api
import (
"bytes"
"fmt"
"net/http"
"runtime"
)
var stackSkip = [][]byte{
// debug.go
[]byte("github.com/AlexxIT/go2rtc/cmd/debug.handler"),
// cmd.go
[]byte("github.com/AlexxIT/go2rtc/cmd.Run"),
[]byte("created by os/signal.Notify"),
// api.go
[]byte("created by github.com/AlexxIT/go2rtc/cmd/api.Init"),
[]byte("created by net/http.(*connReader).startBackgroundRead"),
[]byte("created by net/http.(*Server).Serve"),
[]byte("created by github.com/AlexxIT/go2rtc/cmd/rtsp.Init"),
}
func stackHandler(w http.ResponseWriter, r *http.Request) {
sep := []byte("\n\n")
buf := make([]byte, 65535)
i := 0
n := runtime.Stack(buf, true)
skipped := 0
for _, item := range bytes.Split(buf[:n], sep) {
for _, skip := range stackSkip {
if bytes.Contains(item, skip) {
item = nil
skipped++
break
}
}
if item != nil {
i += copy(buf[i:], item)
i += copy(buf[i:], sep)
}
}
i += copy(buf[i:], fmt.Sprintf(
"Total: %d, Skipped: %d", runtime.NumGoroutine(), skipped),
)
if _, err := w.Write(buf[:i]); err != nil {
panic(err)
}
}
+67
View File
@@ -0,0 +1,67 @@
package api
import (
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/gorilla/websocket"
"net/http"
"sync"
)
type WSHandler func(ctx *Context, msg *streamer.Message)
var apiWsUp = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 512000,
}
type Context struct {
Conn *websocket.Conn
Request *http.Request
Consumer interface{} // TODO: rewrite
onClose []func()
mu sync.Mutex
}
func (ctx *Context) Upgrade(w http.ResponseWriter, r *http.Request) (err error) {
ctx.Conn, err = apiWsUp.Upgrade(w, r, nil)
ctx.Request = r
return
}
func (ctx *Context) Close() {
for _, f := range ctx.onClose {
f()
}
_ = ctx.Conn.Close()
}
func (ctx *Context) Write(msg interface{}) {
ctx.mu.Lock()
defer ctx.mu.Unlock()
var err error
switch msg := msg.(type) {
case *streamer.Message:
err = ctx.Conn.WriteJSON(msg)
case []byte:
err = ctx.Conn.WriteMessage(websocket.BinaryMessage, msg)
default:
return
}
if err != nil {
//panic(err) // TODO: fix panic
}
}
func (ctx *Context) Error(err error) {
ctx.Write(&streamer.Message{
Type: "error", Value: err.Error(),
})
}
func (ctx *Context) OnClose(f func()) {
ctx.onClose = append(ctx.onClose, f)
}