commit e2d0b51a50d879814428227cc7c4f128cc4634a2 Author: kayos@tcp.direct Date: Wed May 3 23:22:21 2023 -0700 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f11b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dfc3f88 --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module git.tcp.direct/kayos/fla5h + +go 1.20 + +require ( + git.tcp.direct/kayos/common v0.8.3 + github.com/fasthttp/websocket v1.5.2 + github.com/icza/bitio v1.1.0 + github.com/rs/zerolog v1.29.1 + github.com/valyala/fasthttp v1.47.0 +) + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/klauspost/compress v1.16.3 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + golang.org/x/sys v0.7.0 // indirect + nullprogram.com/x/rng v1.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..15808b4 --- /dev/null +++ b/go.sum @@ -0,0 +1,34 @@ +git.tcp.direct/kayos/common v0.8.3 h1:W2iDrrQ7GN9cIuJTcAtmxnWIyBRtDrTsmgQzzuZMTM8= +git.tcp.direct/kayos/common v0.8.3/go.mod h1:7hOMiW1jK55PcvEuerOUKac3MjOAexaLYluYqFL+2Ws= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/fasthttp/websocket v1.5.2 h1:KdCb0EpLpdJpfE3IPA5YLK/aYBO3dhZcvwxz6tXe2LQ= +github.com/fasthttp/websocket v1.5.2/go.mod h1:S0KC1VBlx1SaXGXq7yi1wKz4jMub58qEnHQG9oHuqBw= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0= +github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= +github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= +github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= +github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +nullprogram.com/x/rng v1.1.0 h1:SMU7DHaQSWtKJNTpNFIFt8Wd/KSmOuSDPXrMFp/UMro= +nullprogram.com/x/rng v1.1.0/go.mod h1:glGw6V87vyfawxCzqOABL3WfL95G65az9Z2JZCylCkg= diff --git a/pkg/bitstream/bits.go b/pkg/bitstream/bits.go new file mode 100644 index 0000000..41d33a4 --- /dev/null +++ b/pkg/bitstream/bits.go @@ -0,0 +1,18 @@ +package bitstream + +import ( + _ "github.com/icza/bitio" +) + +func PackColorLossy(r, g, b int) byte { + sum := (r*7/255)<<5 + (g*7/255)<<2 + (b * 3 / 255) + return byte(sum) +} + +func UnpackColorLossy(packed byte) (r, g, b int) { + sum := int(packed) + r = sum >> 5 * 255 / 7 + g = ((sum >> 2) & 0x07) * 255 / 7 + b = sum & 3 * 255 / 3 + return +} diff --git a/pkg/bitstream/bits_test.go b/pkg/bitstream/bits_test.go new file mode 100644 index 0000000..8a7aed1 --- /dev/null +++ b/pkg/bitstream/bits_test.go @@ -0,0 +1,57 @@ +package bitstream + +import ( + "testing" + + "git.tcp.direct/kayos/common/entropy" +) + +func randomRGB() (r, g, b int) { + r = entropy.RNG(255) + g = entropy.RNG(255) + b = entropy.RNG(255) + return +} + +func TestPackColorLossy(t *testing.T) { + r := 0 + g := 204 + b := 255 + check(r, g, b, t) + for i := 0; i < 100; i++ { + r, g, b = randomRGB() + check(r, g, b, t) + } +} +func check(r, g, b int, t *testing.T) { + t.Logf("Original: R:%d G:%d B:%d", r, g, b) + packed := PackColorLossy(r, g, b) + t.Logf("Packed: %d", packed) + + rn, gn, bn := UnpackColorLossy(packed) + t.Logf("Unpacked: R:%d G:%d B:%d", rn, gn, bn) + comp := map[string]map[int]int{ + "r": {r: rn}, + "g": {g: gn}, + "b": {b: bn}, + } + totalDiff := 0 + for k, v := range comp { + col := k + for in, out := range v { + var l, s = in, out + if in < out { + l = out + } + diff := l - s + totalDiff += diff + if diff > 85 { + t.Errorf("[%s] lossy beyond tolerace: %d -> %d", col, in, out) + } + } + } + if totalDiff > 150 { + t.Errorf("lossy beyond tolerace: %d", totalDiff) + } + t.Logf("TotalDiff: %d", totalDiff) +} diff --git a/pkg/logger/console.go b/pkg/logger/console.go new file mode 100644 index 0000000..8506f10 --- /dev/null +++ b/pkg/logger/console.go @@ -0,0 +1,26 @@ +package logger + +import ( + "time" + + "github.com/rs/zerolog" +) + +var l *zerolog.Logger + +func setup() { + cl := zerolog.NewConsoleWriter( + func(w *zerolog.ConsoleWriter) { + w.TimeFormat = time.RFC822 + }, + ) + ll := zerolog.New(cl).With().Timestamp().Logger() + l = &ll +} + +func Get() *zerolog.Logger { + if l == nil { + setup() + } + return l +} diff --git a/pkg/pools/buffers.go b/pkg/pools/buffers.go new file mode 100644 index 0000000..dc5dcdb --- /dev/null +++ b/pkg/pools/buffers.go @@ -0,0 +1,16 @@ +package pools + +import ( + "sync" + + "git.tcp.direct/kayos/common/pool" +) + +var ( + Strings = pool.NewStringFactory() + FastHTTPBuffers = &sync.Pool{ + New: func() interface{} { + return make([]byte, 32*1024) + }, + } +) diff --git a/pkg/resp/rgb.go b/pkg/resp/rgb.go new file mode 100644 index 0000000..29cd62c --- /dev/null +++ b/pkg/resp/rgb.go @@ -0,0 +1 @@ +package resp diff --git a/pkg/ws/params.go b/pkg/ws/params.go new file mode 100644 index 0000000..5f3af4f --- /dev/null +++ b/pkg/ws/params.go @@ -0,0 +1,65 @@ +package ws + +import ( + "crypto/tls" + "strconv" + + "git.tcp.direct/kayos/fla5h/pkg/pools" +) + +type SSLParams struct { + tlsConfig *tls.Config + Enabled bool +} + +type WebsocketServerParams struct { + Addr string + Port string + SSLParams SSLParams +} + +func (wsp *WebsocketServerParams) GetAddr() string { + s := pools.Strings.Get() + s.MustWriteString(wsp.Addr) + s.MustWriteString(":") + s.MustWriteString(wsp.Port) + addr := s.String() + pools.Strings.MustPut(s) + return addr + +} + +func (wsp *WebsocketServerParams) GetTLSConfig() *tls.Config { + if !wsp.SSLParams.Enabled { + return nil + } + return wsp.SSLParams.tlsConfig +} + +func NewWebsocketServerParams(addr string, port int) *WebsocketServerParams { + return &WebsocketServerParams{ + Addr: addr, + Port: strconv.Itoa(port), + SSLParams: SSLParams{Enabled: false}, + } +} + +func NewWebsocketServerParamsWithSSL(addr string, port int, cert, key []byte) *WebsocketServerParams { + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{ + tls.Certificate{ + Certificate: [][]byte{cert}, + PrivateKey: key, + }, + }, + } + + return &WebsocketServerParams{ + Addr: addr, + Port: strconv.Itoa(port), + SSLParams: SSLParams{ + Enabled: true, + tlsConfig: tlsConfig, + }, + } +} diff --git a/pkg/ws/server.go b/pkg/ws/server.go new file mode 100644 index 0000000..16cdc64 --- /dev/null +++ b/pkg/ws/server.go @@ -0,0 +1,72 @@ +package ws + +import ( + "io" + "time" + + "github.com/fasthttp/websocket" + "github.com/valyala/fasthttp" + + "git.tcp.direct/kayos/fla5h/pkg/logger" + "git.tcp.direct/kayos/fla5h/pkg/pools" +) + +var upgrader = websocket.FastHTTPUpgrader{ + HandshakeTimeout: time.Duration(5) * time.Second, + WriteBufferPool: pools.FastHTTPBuffers, + Subprotocols: []string{"fla5h"}, + EnableCompression: true, +} + +func Serve(params WebsocketServerParams) error { + srv := &fasthttp.Server{ + Name: "fla5h/0.1", + ReadTimeout: time.Duration(5) * time.Second, + WriteTimeout: time.Duration(5) * time.Second, + IdleTimeout: time.Duration(55) * time.Second, + // NoDefaultDate: true, + // NoDefaultContentType: false, + KeepHijackedConns: true, + CloseOnShutdown: true, + StreamRequestBody: true, + Logger: logger.Get(), + TLSConfig: params.GetTLSConfig(), + } + srv.Handler = func(ctx *fasthttp.RequestCtx) { + if string(ctx.Path()) == "/ws" { + Upgrade(ctx) + } + } + return srv.ListenAndServe(params.GetAddr()) +} + +func Upgrade(ctx *fasthttp.RequestCtx) (*websocket.Conn, error) { + var wsConn *websocket.Conn + err := upgrader.Upgrade(ctx, func(wsc *websocket.Conn) { + wsConn = wsc + }) + return wsConn, err +} + +func closer(ioc io.Closer) { + if err := ioc.Close(); err != nil { + logger.Get().Error().Msgf("io.Closer error: %v", err) + } +} + +func wsHandler(wsc *websocket.Conn) { + wb := func(dat []byte) { + if err := wsc.WriteMessage(websocket.BinaryMessage, dat); err != nil { + logger.Get().Error().Msgf("WriteMessage error: %v", err) + } + } + ws := func(dat []byte) { + if err := wsc.WriteMessage(websocket.TextMessage, dat); err != nil { + logger.Get().Error().Msgf("WriteMessage error: %v", err) + } + } + defer closer(wsc) + for { + + } +}