From c432174dca4e04e90f800ad50d1739cacb58c581 Mon Sep 17 00:00:00 2001 From: Sergey Petrov Date: Mon, 21 Apr 2025 16:04:12 +0500 Subject: [PATCH] Added TLS. --- reader/cmd/main.go | 39 +- reader/go.mod | 11 + reader/go.sum | 34 ++ reader/internal/config/config.go | 7 +- reader/internal/handlers/handlers.go | 549 ------------------ .../web/https/handlersHTTP/handlers.go | 237 ++++++++ reader/internal/web/quic/quic.go | 59 ++ 7 files changed, 372 insertions(+), 564 deletions(-) delete mode 100644 reader/internal/handlers/handlers.go create mode 100644 reader/internal/web/https/handlersHTTP/handlers.go create mode 100644 reader/internal/web/quic/quic.go diff --git a/reader/cmd/main.go b/reader/cmd/main.go index eefca90..27632d7 100644 --- a/reader/cmd/main.go +++ b/reader/cmd/main.go @@ -1,15 +1,17 @@ package main import ( + "crypto/tls" "fmt" + "log" "net/http" - "os" "git.insit.tech/psa/rtsp_reader-writer/reader/internal/config" logger "git.insit.tech/psa/rtsp_reader-writer/reader/internal/log" "git.insit.tech/psa/rtsp_reader-writer/reader/internal/metrics" "git.insit.tech/psa/rtsp_reader-writer/reader/internal/web/handlers" "git.insit.tech/psa/rtsp_reader-writer/reader/internal/web/middlewares" + "github.com/quic-go/quic-go/http3" ) func main() { @@ -17,10 +19,7 @@ func main() { logger.StartMainLogger(config.Local, "reader") // Check if the data folder in the directory. - homeDir, err := os.UserHomeDir() - if err != nil { - } - config.DirData = fmt.Sprintf("%s/%s/vod", homeDir, config.Local) + config.DirData = fmt.Sprintf("%s/%s/vod", config.HomeDir, config.Local) // //err = unpacker.CreateVideo() //if err != nil { @@ -47,17 +46,31 @@ func main() { //mux.HandleFunc("GET /vods/:id", handlersQUIC.ConfigVodsHandler) mux.Handle("GET /vods", middlewares.AuthMiddleware(handlers.ListVodsHandler())) - http.ListenAndServe(":8080", mux) - // HTTP/1.1 & HTTP/2 go func() { - //http.ListenAndServeTLS(":8080", certFile, keyFile, mux) + http.ListenAndServeTLS(":8080", config.TLSCertFile, config.TLSKeyFile, mux) }() - //server := &http3.Server{ - // Addr: ":8080", - // Handler: mux, - // TLSConfig: quic.GetTLSConfig(), - //} + server := &http3.Server{ + Addr: ":8080", + Handler: mux, + TLSConfig: getTLSConfig(), + } + + log.Fatal(server.ListenAndServe()) +} + +func getTLSConfig() *tls.Config { + // Загрузка сертификата и ключа + cert, err := tls.LoadX509KeyPair(config.TLSCertFile, config.TLSKeyFile) + if err != nil { + log.Fatal("Ошибка загрузки сертификата и ключа:", err) + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + NextProtos: []string{"h3", "h2", "http/1.1"}, + MinVersion: tls.VersionTLS13, + } } diff --git a/reader/go.mod b/reader/go.mod index cedfc6c..831f45f 100644 --- a/reader/go.mod +++ b/reader/go.mod @@ -14,6 +14,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.2 github.com/google/uuid v1.6.0 github.com/prometheus/client_golang v1.22.0 + github.com/quic-go/quic-go v0.51.0 github.com/sirupsen/logrus v1.9.3 go.uber.org/zap v1.27.0 ) @@ -25,13 +26,16 @@ require ( github.com/asticode/go-astits v1.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/snappy v1.0.0 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/grafov/m3u8 v0.12.1 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.15 // indirect github.com/pion/rtp v1.8.13 // indirect @@ -39,13 +43,20 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.63.0 // indirect github.com/prometheus/procfs v0.16.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect + go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.39.0 // indirect + golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/tools v0.22.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/reader/go.sum b/reader/go.sum index 8c2199e..78bf5c6 100644 --- a/reader/go.sum +++ b/reader/go.sum @@ -21,26 +21,38 @@ github.com/bluenviron/mediacommon/v2 v2.0.0 h1:JinZ9v2x6QeAOzA0cDA6aFe8vQuCrU8Oy github.com/bluenviron/mediacommon/v2 v2.0.0/go.mod h1:iHEz1SFIet6zBwAQoh1a92vTQ3dV3LpVFbom6/SLz3k= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gen2brain/aac-go v0.0.0-20230119102159-ef1e76509d21 h1:yfrARW/aVlqKORCdKrYdU0PZUKPqQvYEUQBKfVlNa9Q= github.com/gen2brain/aac-go v0.0.0-20230119102159-ef1e76509d21/go.mod h1:HZqGD/LXHB1VCGUGNzuyxSsD12f3KjbJbvImAmoK/mM= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gofiber/contrib/jwt v1.1.0 h1:ka5WjWsZ2cd0irvfpmH9hIKj+fflvVRzQxJ7Nv1H3tE= github.com/gofiber/contrib/jwt v1.1.0/go.mod h1:CpIwrkUQ3Q6IP8y9n3f0wP9bOnSKx39EDp2fBVgMFVk= github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grafov/m3u8 v0.12.1 h1:DuP1uA1kvRRmGNAZ0m+ObLv1dvrfNO0TPx0c/enNk0s= github.com/grafov/m3u8 v0.12.1/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -58,6 +70,10 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= @@ -78,6 +94,10 @@ github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc= +github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -86,6 +106,7 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -103,17 +124,30 @@ github.com/zaf/g711 v1.4.0 h1:XZYkjjiAg9QTBnHqEg37m2I9q3IIDv5JRYXs2N8ma7c= github.com/zaf/g711 v1.4.0/go.mod h1:eCDXt3dSp/kYYAoooba7ukD/Q75jvAaS4WOMr0l1Roo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/reader/internal/config/config.go b/reader/internal/config/config.go index 6642204..1e629fa 100644 --- a/reader/internal/config/config.go +++ b/reader/internal/config/config.go @@ -2,6 +2,7 @@ package config import ( "github.com/google/uuid" + "os" "sync" "time" ) @@ -15,8 +16,10 @@ var ( Local = "storage" LogsDirectory string DirData string - - Storage AuthStorage + HomeDir, _ = os.UserHomeDir() + TLSCertFile = HomeDir + "/GoProjects/certs/certbundle.pem" + TLSKeyFile = HomeDir + "/GoProjects/certs/server.key" + Storage AuthStorage JwtSecretKey = []byte(uuid.NewString()) SessionStore sync.Map diff --git a/reader/internal/handlers/handlers.go b/reader/internal/handlers/handlers.go deleted file mode 100644 index 00ca4cb..0000000 --- a/reader/internal/handlers/handlers.go +++ /dev/null @@ -1,549 +0,0 @@ -package handlers - -import ( - "encoding/json" - "errors" - "fmt" - "log" - "net/http" - "os" - "strconv" - "strings" - "time" - - "git.insit.tech/psa/rtsp_reader-writer/reader/internal/config" - "git.insit.tech/psa/rtsp_reader-writer/reader/internal/processor" - "git.insit.tech/psa/rtsp_reader-writer/writer/pkg/storage" - jwtware "github.com/gofiber/contrib/jwt" - "github.com/gofiber/fiber/v2" - "github.com/golang-jwt/jwt/v5" - "github.com/sirupsen/logrus" -) - -type VideoRequest struct { - Date string `json:"date"` - StartTime string `json:"start_time"` - EndTime string `json:"end_time"` -} - -type ListVodsResponse struct { - EstimatedCount int `json:"estimated_count"` // Estimated total number of records for the query. - Vods []string `json:"vods"` // List of vods. - Files map[string][]string `json:"files"` // List files and folders of a specific VOD location. -} - -type ConfigVodsResponse struct { - Prefix string `json:"prefix"` // The unique name of VOD location. - AutoMbr bool `json:"auto_mbr"` // Turns on automatic creation of a multi-bitrate HLS playlist from several files with different bitrates. - Disabled bool `json:"disabled"` // Whether this VOD location is disabled. - Protocols struct { // Configuraton of play protocols. - Hls bool `json:"hls"` // Whether to allow or deny an HLS stream playback. - Cmaf bool `json:"cmaf"` // Whether to allow or deny an LL-HLS stream playback. - Dash bool `json:"dash"` // Whether to allow or deny a DASH stream playback. - Player bool `json:"player"` // Whether to allow or deny playback in embed.html. - Mss bool `json:"mss"` // Whether to allow or deny an MSS stream playback. - Rtmp bool `json:"rtmp"` // Whether to allow or deny an RTMP stream playback. - Rtsp bool `json:"rtsp"` // Whether to allow or deny an RTSP stream playback. - M4F bool `json:"m4f"` // Whether to allow or deny an M4F stream playback. - M4S bool `json:"m4s"` // Whether to allow or deny an M4S stream playback. - Mseld bool `json:"mseld"` // Whether to allow or deny an MSE-LD stream playback. - Tshttp bool `json:"tshttp"` // Whether to allow or deny an MPEG-TS stream playback over HTTP(S). - Webrtc bool `json:"webrtc"` // Whether to allow or deny an WebRTC stream playback. - Srt bool `json:"srt"` // Whether to allow or deny an SRT stream playback. - Shoutcast bool `json:"shoutcast"` // Whether to allow or deny a SHOUTcast/Icecast stream playback. - Mp4 bool `json:"mp4"` // Whether to allow or deny an MP4 file download over HTTP(S). - Jpeg bool `json:"jpeg"` // Whether to allow or deny delivering JPEG thumbnails over HTTP(S). - Api bool `json:"api"` // Whether to allow or deny API requests. - } `json:"protocols"` - SegmentDuration int `json:"segment_duration"` // The time, in seconds, of the segment duration. Used for the protocols like HLS or DASH. - AddAudioOnly bool `json:"add_audio_only"` // Whether to add an audio-only version of an HLS stream. Used to create App Store compliant HLS streams to deliver the content to Apple iOS devices. Add audio-only HLS playlist to variant MBR playlist for iOS compliant streaming. - Provider string `json:"provider"` // Human-readable name of the content provider. Applicable to MPEG-TS. -} - -type SingleVodsResponse struct { - Name string `json:"name"` - Prefix string `json:"prefix"` - Url string `json:"url"` - Folder string `json:"folder"` - Bytes int64 `json:"bytes"` - MediaInfo MediaSingleVodsResponse `json:"media_info"` -} - -type MediaSingleVodsResponse struct { - Tracks map[string]string `json:"tracks"` - Duration int `json:"duration"` - Provider string `json:"provider"` - Title string `json:"title"` -} - -// Inactive5Minutes checks if a folder has files that was not created or modified for last 5 minutes. -func Inactive5Minutes(entries []os.DirEntry) (bool, error) { - threshold := time.Now().Add(-5 * time.Minute) - - disabled := true - - for _, entry := range entries { - info, err := entry.Info() - if err != nil { - return true, errors.New("Info error: " + err.Error()) - } - - if info.ModTime().After(threshold) { - disabled = false - return disabled, nil - } - } - - return true, nil -} - -// recDurationMilliseconds calculates the difference between first existing rec file and the last one in milliseconds. -func recDurationMilliseconds(entries []os.DirEntry) (int, error) { - // Find last file and first file; get timestamps. - lastFile := entries[len(entries)-1].Name() - lastTime := strings.Split(lastFile, "_")[0] - - firstFile := entries[0].Name() - firstTime := strings.Split(firstFile, "_")[0] - - // Convert string timestamps to int. - lastTimeInt, err := strconv.Atoi(lastTime) - if err != nil { - return 0, errors.New("convert last time error: " + err.Error()) - } - - firstTimeInt, err := strconv.Atoi(firstTime) - if err != nil { - return 0, errors.New("convert first time error: " + err.Error()) - } - - // Calculate the difference. - difference := lastTimeInt - firstTimeInt - - return difference * 1000, nil -} - -// Download processes Download request. -func Download(w http.ResponseWriter, r *http.Request) { - log.Printf("new download request: %+v\n", r) - - downloadRequest := VideoRequest{} - - err := json.NewDecoder(r.Body).Decode(&downloadRequest) - if err != nil { - log.Printf("json decode error: %v\n", err) - w.WriteHeader(http.StatusBadRequest) - return - } - - pathFileNameRes, err := processor.Process(downloadRequest.Date, downloadRequest.StartTime, downloadRequest.EndTime) - if err != nil { - log.Printf("process error: %v\n", err) - w.WriteHeader(http.StatusBadRequest) - return - } - - w.Header().Set("Content-Type", "video/mp4") - // Разрешаем частичную загрузку (поддержка перемотки) - w.Header().Set("Accept-Ranges", "bytes") - - http.ServeFile(w, r, pathFileNameRes) -} - -// HLS processes Download request. -func HLS(w http.ResponseWriter, r *http.Request) { - log.Printf("new hls request: %+v\n", r) - - path := "/home/psa/GoRepository/data/1280x720/" - - w.Header().Set("Access-Control-Allow-Origin", "*") - http.StripPrefix("/hls", http.FileServer(http.Dir(path))).ServeHTTP(w, r) -} - -// ListVodsHandler returns the list of VOD locations. -// -// This method allows to get the list of all VOD locations. VOD location is a virtual filepath used to place files for -// VOD (Video on Demand) broadcasting. -func ListVodsHandler(c *fiber.Ctx) error { - // Read directory. - entries, err := os.ReadDir(config.DirData) - if err != nil { - log.Println("StatusInternalServerError") - return c.SendStatus(fiber.StatusInternalServerError) - } - - // Filter only folders. - var dirs []string - for _, entry := range entries { - if entry.IsDir() { - dirs = append(dirs, entry.Name()) - } - } - - // Prepare the Response. - VodsRes := ListVodsResponse{ - EstimatedCount: len(dirs), - Vods: dirs, - } - - // Write header and code response. - return c.JSON(VodsRes) -} - -// ConfigVodsHandler returns configuration of the requested VOD location. -// -// This method allows to get a single VOD location. -func ConfigVodsHandler(c *fiber.Ctx) error { - // Read camera id. - id := c.Params("id") - - // Get resolutions. - resolutions, err := storage.GetResolutions(id) - if err != nil { - return c.SendStatus(fiber.StatusBadRequest) - } - - // Calculate response fields. - - // Create path to first resolution. - resDir := fmt.Sprintf("%s/%s/%s", config.DirData, id, resolutions[0]) - - // Read directory. - entries, err := os.ReadDir(resDir) - if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) - } - - // Check if a folder has files that was not created or modified for last 5 minutes. - disabled, err := Inactive5Minutes(entries) - if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) - } - - // Calculate the difference between first existing rec file and the last one in milliseconds. - segmentDuration, err := recDurationMilliseconds(entries) - if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) - } - - // Prepare the Response. - VodsRes := ConfigVodsResponse{ - Prefix: id, - AutoMbr: false, // Always false. Temporary. - Disabled: disabled, - SegmentDuration: segmentDuration, - AddAudioOnly: false, // Always false. Temporary. - Provider: "Insit", - } - - // Write header and code response. - return c.JSON(VodsRes) -} - -// DelVodsHandler delete archive of the requested VOD location. -// -// This method delete a single VOD location by its prefix. -func DelVodsHandler(c *fiber.Ctx) error { - // Read camera id. - id := c.Params("id") - - err := os.Remove(fmt.Sprintf("%s/%s", config.DirData, id)) - if err != nil { - return c.SendStatus(fiber.StatusBadRequest) - } - - return c.SendStatus(fiber.StatusNoContent) -} - -// ListFilesVodsHandler returns the list of all files and folders in archive for a specific VOD location. -// -// This method allows to get the list of all files and folders for a specific VOD location. -func ListFilesVodsHandler(c *fiber.Ctx) error { - // Read camera id. - id := c.Params("id") - - // Create map for response. - files := make(map[string][]string) - - // Get resolutions. - resolutions, err := storage.GetResolutions(id) - if err != nil { - return c.SendStatus(fiber.StatusBadRequest) - } - - for _, resolution := range resolutions { - // Create path to the resolutions. - resDir := fmt.Sprintf("%s/%s/%s", config.DirData, id, resolution) - - // Read directory. - entries, err := os.ReadDir(resDir) - if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) - } - - // Create slice for files in folders. - filesInFolder := make([]string, 0) - - // Add all files to the slice with files. - for _, entry := range entries { - filesInFolder = append(filesInFolder, entry.Name()) - } - - // Add resolution and all files to the response map. - files[resolution] = filesInFolder - } - - // Prepare the Response. - vodsRes := ListVodsResponse{ - EstimatedCount: len(files), - Files: files, - } - - // Write header and code response. - return c.JSON(vodsRes) -} - -// SingleVodsHandler returns a specific file in archive for a specific resolution and VOD location. -// -// This method allows to get a single VOD file. -func SingleVodsHandler(c *fiber.Ctx) error { - // Read camera id, res, filename. - id := c.Params("id") - res := c.Params("res") - file := c.Params("file") - - // Calculate file size in bytes. - FileBytes, err := storage.FileBytes(id, res, file) - if err != nil { - return c.SendStatus(fiber.StatusBadRequest) - } - - dur, tracks, err := storage.GetDurAndTracks(id, res, file) - if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) - } - - media := MediaSingleVodsResponse{ - Tracks: tracks, - Duration: dur, - Provider: "Insit", - Title: id, - } - - // Prepare the Response. - vodsRes := SingleVodsResponse{ - Name: file, - Prefix: id, - Url: c.OriginalURL(), - Folder: res, - Bytes: FileBytes, - MediaInfo: media, - } - - // Write header and code response. - return c.JSON(vodsRes) -} - -// DelSingleVodsHandler deletes a VOD file by its name. -// -// This method deletes a VOD file by its name. -func DelSingleVodsHandler(c *fiber.Ctx) error { - // Read camera id, res, filename. - id := c.Params("id") - res := c.Params("res") - file := c.Params("file") - - if err := os.Remove(fmt.Sprintf("%s/%s/%s/%s", config.DirData, id, res, file)); err != nil { - return c.SendStatus(fiber.StatusNotFound) - } - - return c.SendStatus(fiber.StatusNoContent) -} - -// ////////////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////////////// -const ( - ContextKeyUser = "user" -) - -func Auth() { - app := fiber.New() - - authStorage := &AuthStorage{map[string]User{}} - authHandler := &AuthHandler{Storage: authStorage} - userHandler := &UserHandler{Storage: authStorage} - - // Группа обработчиков, которые доступны неавторизованным пользователям - publicGroup := app.Group("") - publicGroup.Post("/register", authHandler.Register) - publicGroup.Post("/login", authHandler.Login) - - // Группа обработчиков, которые требуют авторизации - authorizedGroup := app.Group("") - authorizedGroup.Use(jwtware.New(jwtware.Config{ - SigningKey: jwtware.SigningKey{ - Key: JwtSecretKey, - }, - ContextKey: ContextKeyUser, - })) - authorizedGroup.Get("/profile", userHandler.Profile) - - logrus.Fatal(app.Listen(":8100")) -} - -type ( - // Обработчик HTTP-запросов на регистрацию и аутентификацию пользователей - AuthHandler struct { - Storage *AuthStorage - } - - // Хранилище зарегистрированных пользователей - // Данные хранятся в оперативной памяти - AuthStorage struct { - Users map[string]User - } - - // Структура данных с информацией о пользователе - User struct { - Email string - Name string - password string - } -) - -// Структура HTTP-запроса на регистрацию пользователя -type RegisterRequest struct { - Email string `json:"email"` - Name string `json:"name"` - Password string `json:"password"` -} - -// Обработчик HTTP-запросов на регистрацию пользователя -func (h *AuthHandler) Register(c *fiber.Ctx) error { - regReq := RegisterRequest{} - if err := c.BodyParser(®Req); err != nil { - return fmt.Errorf("body parser: %w", err) - } - - // Проверяем, что пользователь с таким email еще не зарегистрирован - if _, exists := h.Storage.Users[regReq.Email]; exists { - return errors.New("the user already exists") - } - - // Сохраняем в память нового зарегистрированного пользователя - h.Storage.Users[regReq.Email] = User{ - Email: regReq.Email, - Name: regReq.Name, - password: regReq.Password, - } - - return c.SendStatus(fiber.StatusCreated) -} - -// Структура HTTP-запроса на вход в аккаунт -type LoginRequest struct { - Email string `json:"email"` - Password string `json:"password"` -} - -// Структура HTTP-ответа на вход в аккаунт -// В ответе содержится JWT-токен авторизованного пользователя -type LoginResponse struct { - AccessToken string `json:"access_token"` -} - -var ( - errBadCredentials = errors.New("email or password is incorrect") -) - -// Секретный ключ для подписи JWT-токена -// Необходимо хранить в безопасном месте -var JwtSecretKey = []byte("very-secret-key") - -// Обработчик HTTP-запросов на вход в аккаунт -func (h *AuthHandler) Login(c *fiber.Ctx) error { - regReq := LoginRequest{} - if err := c.BodyParser(®Req); err != nil { - return fmt.Errorf("body parser: %w", err) - } - - // Ищем пользователя в памяти приложения по электронной почте - user, exists := h.Storage.Users[regReq.Email] - // Если пользователь не найден, возвращаем ошибку - if !exists { - return errBadCredentials - } - // Если пользователь найден, но у него другой пароль, возвращаем ошибку - if user.password != regReq.Password { - return errBadCredentials - } - - // Генерируем JWT-токен для пользователя, - // который он будет использовать в будущих HTTP-запросах - - // Генерируем полезные данные, которые будут храниться в токене - payload := jwt.MapClaims{ - "sub": user.Email, - "exp": time.Now().Add(time.Hour * 72).Unix(), - } - - // Создаем новый JWT-токен и подписываем его по алгоритму HS256 - token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload) - - t, err := token.SignedString(JwtSecretKey) - if err != nil { - logrus.WithError(err).Error("JWT token signing") - return c.SendStatus(fiber.StatusInternalServerError) - } - - return c.JSON(LoginResponse{AccessToken: t}) -} - -// Обработчик HTTP-запросов, которые связаны с пользователем -type UserHandler struct { - Storage *AuthStorage -} - -// Структура HTTP-ответа с информацией о пользователе -type ProfileResponse struct { - Email string `json:"email"` - Name string `json:"name"` -} - -func jwtPayloadFromRequest(c *fiber.Ctx) (jwt.MapClaims, bool) { - jwtToken, ok := c.Context().Value(ContextKeyUser).(*jwt.Token) - if !ok { - logrus.WithFields(logrus.Fields{ - "jwt_token_context_value": c.Context().Value(ContextKeyUser), - }).Error("wrong type of JWT token in context") - return nil, false - } - - payload, ok := jwtToken.Claims.(jwt.MapClaims) - if !ok { - logrus.WithFields(logrus.Fields{ - "jwt_token_claims": jwtToken.Claims, - }).Error("wrong type of JWT token claims") - return nil, false - } - - return payload, true -} - -// Обработчик HTTP-запросов на получение информации о пользователе -func (h *UserHandler) Profile(c *fiber.Ctx) error { - jwtPayload, ok := jwtPayloadFromRequest(c) - if !ok { - return c.SendStatus(fiber.StatusUnauthorized) - } - - userInfo, ok := h.Storage.Users[jwtPayload["sub"].(string)] - if !ok { - return errors.New("user not found") - } - - return c.JSON(ProfileResponse{ - Email: userInfo.Email, - Name: userInfo.Name, - }) -} diff --git a/reader/internal/web/https/handlersHTTP/handlers.go b/reader/internal/web/https/handlersHTTP/handlers.go new file mode 100644 index 0000000..edd95e8 --- /dev/null +++ b/reader/internal/web/https/handlersHTTP/handlers.go @@ -0,0 +1,237 @@ +package handlersHTTP + +import ( + "encoding/json" + "fmt" + "git.insit.tech/psa/rtsp_reader-writer/reader/internal/config" + "git.insit.tech/psa/rtsp_reader-writer/reader/internal/processor" + "git.insit.tech/psa/rtsp_reader-writer/reader/internal/web/handlers" + "git.insit.tech/psa/rtsp_reader-writer/writer/pkg/storage" + "github.com/gofiber/fiber/v2" + "log" + "net/http" + "os" +) + +// Download processes Download request. +func Download(w http.ResponseWriter, r *http.Request) { + log.Printf("new download request: %+v\n", r) + + downloadRequest := handlers.VideoRequest{} + + err := json.NewDecoder(r.Body).Decode(&downloadRequest) + if err != nil { + log.Printf("json decode error: %v\n", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + pathFileNameRes, err := processor.Process(downloadRequest.Date, downloadRequest.StartTime, downloadRequest.EndTime) + if err != nil { + log.Printf("process error: %v\n", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + w.Header().Set("Content-Type", "video/mp4") + // Разрешаем частичную загрузку (поддержка перемотки) + w.Header().Set("Accept-Ranges", "bytes") + + http.ServeFile(w, r, pathFileNameRes) +} + +// HLS processes Download request. +func HLS(w http.ResponseWriter, r *http.Request) { + log.Printf("new hls request: %+v\n", r) + + path := "/home/psa/GoRepository/data/1280x720/" + + w.Header().Set("Access-Control-Allow-Origin", "*") + http.StripPrefix("/hls", http.FileServer(http.Dir(path))).ServeHTTP(w, r) +} + +// ConfigVodsHandler returns configuration of the requested VOD location. +// +// This method allows to get a single VOD location. +func ConfigVodsHandler(c *fiber.Ctx) error { + // Read camera id. + id := c.Params("id") + + // Get resolutions. + resolutions, err := storage.GetResolutions(id) + if err != nil { + return c.SendStatus(fiber.StatusBadRequest) + } + + // Calculate response fields. + + // Create path to first resolution. + resDir := fmt.Sprintf("%s/%s/%s", config.DirData, id, resolutions[0]) + + // Read directory. + entries, err := os.ReadDir(resDir) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + // Check if a folder has files that was not created or modified for last 5 minutes. + disabled, err := handlers.Inactive5Minutes(entries) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + // Calculate the difference between first existing rec file and the last one in milliseconds. + segmentDuration, err := handlers.RecDurationMilliseconds(entries) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + // Prepare the Response. + VodsRes := handlers.ConfigVodsResponse{ + Prefix: id, + AutoMbr: false, // Always false. Temporary. + Disabled: disabled, + SegmentDuration: segmentDuration, + AddAudioOnly: false, // Always false. Temporary. + Provider: "Insit", + } + + // Write header and code response. + return c.JSON(VodsRes) +} + +// DelVodsHandler delete archive of the requested VOD location. +// +// This method delete a single VOD location by its prefix. +func DelVodsHandler(c *fiber.Ctx) error { + // Read camera id. + id := c.Params("id") + + err := os.Remove(fmt.Sprintf("%s/%s", config.DirData, id)) + if err != nil { + return c.SendStatus(fiber.StatusBadRequest) + } + + return c.SendStatus(fiber.StatusNoContent) +} + +// ListFilesVodsHandler returns the list of all files and folders in archive for a specific VOD location. +// +// This method allows to get the list of all files and folders for a specific VOD location. +func ListFilesVodsHandler(c *fiber.Ctx) error { + // Read camera id. + id := c.Params("id") + + // Create map for response. + files := make(map[string][]string) + + // Get resolutions. + resolutions, err := storage.GetResolutions(id) + if err != nil { + return c.SendStatus(fiber.StatusBadRequest) + } + + for _, resolution := range resolutions { + // Create path to the resolutions. + resDir := fmt.Sprintf("%s/%s/%s", config.DirData, id, resolution) + + // Read directory. + entries, err := os.ReadDir(resDir) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + // Create slice for files in folders. + filesInFolder := make([]string, 0) + + // Add all files to the slice with files. + for _, entry := range entries { + filesInFolder = append(filesInFolder, entry.Name()) + } + + // Add resolution and all files to the response map. + files[resolution] = filesInFolder + } + + // Prepare the Response. + vodsRes := handlers.ListVodsResponse{ + EstimatedCount: len(files), + Files: files, + } + + // Write header and code response. + return c.JSON(vodsRes) +} + +// SingleVodsHandler returns a specific file in archive for a specific resolution and VOD location. +// +// This method allows to get a single VOD file. +func SingleVodsHandler(c *fiber.Ctx) error { + // Read camera id, res, filename. + id := c.Params("id") + res := c.Params("res") + file := c.Params("file") + + // Calculate file size in bytes. + FileBytes, err := storage.FileBytes(id, res, file) + if err != nil { + return c.SendStatus(fiber.StatusBadRequest) + } + + dur, tracks, err := storage.GetDurAndTracks(id, res, file) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + media := handlers.MediaSingleVodsResponse{ + Tracks: tracks, + Duration: dur, + Provider: "Insit", + Title: id, + } + + // Prepare the Response. + vodsRes := handlers.SingleVodsResponse{ + Name: file, + Prefix: id, + Url: c.OriginalURL(), + Folder: res, + Bytes: FileBytes, + MediaInfo: media, + } + + // Write header and code response. + return c.JSON(vodsRes) +} + +// DelSingleVodsHandler deletes a VOD file by its name. +// +// This method deletes a VOD file by its name. +func DelSingleVodsHandler(c *fiber.Ctx) error { + // Read camera id, res, filename. + id := c.Params("id") + res := c.Params("res") + file := c.Params("file") + + if err := os.Remove(fmt.Sprintf("%s/%s/%s/%s", config.DirData, id, res, file)); err != nil { + return c.SendStatus(fiber.StatusNotFound) + } + + return c.SendStatus(fiber.StatusNoContent) +} + +/* +// FileHandler обработчик для работы с файлами +func FileHandler(c *fiber.Ctx) error { + // Если запрос содержит session_id - получаем файл + sessionData := c.Locals(ContextKeySession).(*SessionData) + + // Логика получения файла + fileContent, err := getFileContent(sessionData.FileName) + if err != nil { + return c.Status(fiber.StatusNotFound).SendString("File not found") + } + + return c.Send(fileContent) +} +*/ diff --git a/reader/internal/web/quic/quic.go b/reader/internal/web/quic/quic.go new file mode 100644 index 0000000..3b6282a --- /dev/null +++ b/reader/internal/web/quic/quic.go @@ -0,0 +1,59 @@ +package quic + +/* +import ( + "crypto/tls" + "log" + "net/http" + + "git.insit.tech/psa/rtsp_reader-writer/reader/internal/web/quic/handlersQUIC" + "github.com/quic-go/quic-go/http3" +) + +// SetupQuicServer starts QUIC protocol using HTTP/3. +func SetupQuicServer() { + mux := http.NewServeMux() + + // Группа обработчиков, которые доступны неавторизованным пользователям + mux.HandleFunc("POST /register", handlersQUIC.Register) + mux.HandleFunc("POST /login", handlersQUIC.Login) + + // Группа обработчиков, которые требуют авторизации + //authorizedGroup := app.Group("") + //authorizedGroup.Use(handlersQUIC.CheckJWT, handlersQUIC.CheckSessionID, handlersQUIC.CreateSessionID) + //mux.HandleFunc("GET /profile", userHandlerQUIC.Profile) + // + //// API Flussonic Media Server adapted handlers. + //mux.HandleFunc("GET /vods/:id/:res/:file", handlersQUIC.SingleVodsHandler) + //mux.HandleFunc("DELETE /vods/:id/:res/:file", handlersQUIC.DelSingleVodsHandler) + //mux.HandleFunc("GET /vods/:id/files", handlersQUIC.ListFilesVodsHandler) + //mux.HandleFunc("DELETE /vods/:id", handlersQUIC.DelVodsHandler) + //mux.HandleFunc("GET /vods/:id", handlersQUIC.ConfigVodsHandler) + mux.HandleFunc("GET /vods", handlersQUIC.ListVodsHandler) + + server := &http3.Server{ + Addr: ":443", + Handler: mux, + } + + http.ListenAndServe("", mux) + + if err := server.ListenAndServeTLS("fullchain.pem", "privkey.pem"); err != nil { + log.Fatal(err) + } +} + +// GetTLSConfig +func GetTLSConfig() *tls.Config { + cert, err := tls.LoadX509KeyPair("fullchain.pem", "privkey.pem") + if err != nil { + log.Fatal(err) + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + NextProtos: []string{"h3", "h2", "http/1.1"}, + MinVersion: tls.VersionTLS13, + } +} +*/