diff --git a/config_example.yml b/config_example.yml index 338bfd2..2b55294 100644 --- a/config_example.yml +++ b/config_example.yml @@ -21,4 +21,13 @@ BOOTSTRAP_TOKEN: # GUA_HOST: 127.0.0.1 # Guacamole Server 端口号,默认4822 -# GUA_PORT: 4822 \ No newline at end of file +# GUA_PORT: 4822 + +# 会话共享使用的类型 [local, redis], 默认local +# SHARE_ROOM_TYPE: local + +# Redis配置 +# REDIS_HOST: 127.0.0.1 +# REDIS_PORT: 6379 +# REDIS_PASSWORD: +# REDIS_DB_ROOM: \ No newline at end of file diff --git a/go.mod b/go.mod index 2a357e4..787f58b 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/gin-contrib/sessions v0.0.3 github.com/gin-gonic/gin v1.6.3 + github.com/go-redis/redis/v8 v8.8.2 github.com/gofrs/uuid v4.0.0+incompatible github.com/gorilla/websocket v1.4.2 github.com/satori/go.uuid v1.2.0 // indirect github.com/shirou/gopsutil/v3 v3.21.3 github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.7.1 - github.com/stretchr/testify v1.7.0 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 diff --git a/go.sum b/go.sum index 282bdcc..6388fa0 100644 --- a/go.sum +++ b/go.sum @@ -49,7 +49,10 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -62,10 +65,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sessions v0.0.3 h1:PoBXki+44XdJdlgDqDrY5nDVe3Wk7wDV/UCOuLP6fBI= github.com/gin-contrib/sessions v0.0.3/go.mod h1:8C/J6cad3Il1mWYYgtw0w+hqasmpvy25mPkXdOgeB9I= @@ -91,6 +98,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-redis/redis/v8 v8.8.2 h1:O/NcHqobw7SEptA0yA6up6spZVFtwE06SXM8rgLtsP8= +github.com/go-redis/redis/v8 v8.8.2/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -106,11 +115,22 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -155,6 +175,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -210,7 +231,17 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4= +github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= +github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -277,9 +308,18 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= +go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= +go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= +go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= +go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= +go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= +go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= +go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -288,6 +328,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -308,8 +349,10 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -323,8 +366,12 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -333,9 +380,11 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -348,12 +397,17 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341 h1:2/QtM1mL37YmcsT8HaDNHDgTqqFVw+zr8UzMiBVLzYU= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -382,7 +436,13 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -404,6 +464,13 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -411,6 +478,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= @@ -418,6 +486,8 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/twindagger/httpsig.v1 v1.2.0 h1:GHT8oYp1sdRKr89MYwpixUcDOx4iEY5EO/Rk+A5FenY= gopkg.in/twindagger/httpsig.v1 v1.2.0/go.mod h1:J1gOUnY2juidmnrHbYPnCoTacqx3oIAUsyKfASUXlU8= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -427,6 +497,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index bf0c751..f18d495 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "net/http/pprof" "os" "path/filepath" + "strconv" "strings" "time" @@ -58,8 +59,8 @@ func main() { jmsService := MustJMService() bootstrap(jmsService) tunnelService := tunnel.GuacamoleTunnelServer{ - Cache: &tunnel.GuaTunnelCache{ - Tunnels: make(map[string]*tunnel.Connection), + Cache: &tunnel.GuaTunnelCacheManager{ + GuaTunnelCache: NewGuaTunnelCache(), }, SessCache: &tunnel.SessionCache{ Sessions: make(map[string]*session.TunnelSession), @@ -76,12 +77,26 @@ func main() { logger.Fatal(http.ListenAndServe(addr, eng)) } -func runHeartTask(jmsService *service.JMService, tunnelCache *tunnel.GuaTunnelCache) { +func NewGuaTunnelCache() tunnel.GuaTunnelCache { + switch strings.ToLower(config.GlobalConfig.ShareRoomType) { + case config.ShareTypeRedis: + return tunnel.NewGuaTunnelRedisCache(tunnel.Config{ + Addr: net.JoinHostPort(config.GlobalConfig.RedisHost, + strconv.Itoa(config.GlobalConfig.RedisPort)), + Password: config.GlobalConfig.RedisPassword, + DBIndex: config.GlobalConfig.RedisDBIndex, + }) + default: + return tunnel.NewLocalTunnelLocalCache() + } +} + +func runHeartTask(jmsService *service.JMService, tunnelCache *tunnel.GuaTunnelCacheManager) { // default 30s beatTicker := time.NewTicker(time.Second * 30) defer beatTicker.Stop() for range beatTicker.C { - sids := tunnelCache.Range() + sids := tunnelCache.RangeActiveSessionIds() tasks, err := jmsService.TerminalHeartBeat(sids) if err != nil { logger.Error(err) @@ -103,7 +118,7 @@ func runHeartTask(jmsService *service.JMService, tunnelCache *tunnel.GuaTunnelCa } } -func runCleanDriverDisk(tunnelCache *tunnel.GuaTunnelCache) { +func runCleanDriverDisk(tunnelCache *tunnel.GuaTunnelCacheManager) { // default 1 hour cleanDriveTicker := time.NewTicker(time.Hour) defer cleanDriveTicker.Stop() @@ -114,7 +129,7 @@ func runCleanDriverDisk(tunnelCache *tunnel.GuaTunnelCache) { logger.Error(err) continue } - currentOnlineUserIds := tunnelCache.RangeUserIds() + currentOnlineUserIds := tunnelCache.RangeActiveUserIds() for i := range folders { if _, ok := currentOnlineUserIds[folders[i].Name()]; ok { continue @@ -234,6 +249,7 @@ func uploadRemainReplay(jmsService *service.JMService, remainFiles map[string]st logger.Errorf("Upload replay failed: %s", err) continue } + logger.Infof("Upload remain session replay %s success", absGzPath) // 上传成功删除文件 _ = os.Remove(absGzPath) if err = jmsService.FinishReply(sid); err != nil { diff --git a/pkg/config/config.go b/pkg/config/config.go index f636c5e..de6bd36 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -31,6 +31,12 @@ type Config struct { DisableAllUpDownload bool `mapstructure:"JUMPSERVER_DISABLE_ALL_UPLOAD_DOWNLOAD"` EnableRemoteAppUpDownLoad bool `mapstructure:"JUMPSERVER_REMOTE_APP_UPLOAD_DOWNLOAD_ENABLE"` EnableRemoteAPPCopyPaste bool `mapstructure:"JUMPSERVER_REMOTE_APP_COPY_PASTE_ENABLE"` + + ShareRoomType string `mapstructure:"SHARE_ROOM_TYPE"` + RedisHost string `mapstructure:"REDIS_HOST"` + RedisPort int `mapstructure:"REDIS_PORT"` + RedisPassword string `mapstructure:"REDIS_PASSWORD"` + RedisDBIndex int `mapstructure:"REDIS_DB_ROOM"` } func Setup(configPath string) { diff --git a/pkg/config/const.go b/pkg/config/const.go index 62366a3..088068d 100644 --- a/pkg/config/const.go +++ b/pkg/config/const.go @@ -8,3 +8,8 @@ const ( GinSessionName = "session-Lion" GinSessionKey = "SESSION" ) + +const ( + ShareTypeRedis = "redis" + ShareTypeLocal = "local" +) diff --git a/pkg/jms-sdk-go/service/jms.go b/pkg/jms-sdk-go/service/jms.go index b3fe8b3..bf3cf48 100644 --- a/pkg/jms-sdk-go/service/jms.go +++ b/pkg/jms-sdk-go/service/jms.go @@ -15,7 +15,12 @@ var AccessKeyUnauthorized = errors.New("access key unauthorized") var ConnectErr = errors.New("api connect err") -const minTimeOut = time.Second * 30 +const ( + minTimeOut = time.Second * 30 + + orgHeaderKey = "X-JMS-ORG" + orgHeaderValue = "ROOT" +) func NewAuthJMService(opts ...Option) (*JMService, error) { opt := option{ @@ -35,6 +40,7 @@ func NewAuthJMService(opts ...Option) (*JMService, error) { if opt.sign != nil { httpClient.SetAuthSign(opt.sign) } + httpClient.SetHeader(orgHeaderKey, orgHeaderValue) return &JMService{authClient: httpClient}, nil } diff --git a/pkg/session/server.go b/pkg/session/server.go index fbe800c..533375a 100644 --- a/pkg/session/server.go +++ b/pkg/session/server.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "time" "github.com/gin-gonic/gin" @@ -230,7 +231,9 @@ func (s *Server) RegisterFinishReplayCallback(tunnel TunnelSession) func() error // 压缩完成则删除源文件 defer os.Remove(originReplayFilePath) if replayStorage := storage.NewReplayStorage(replayConfig); replayStorage != nil { - err = replayStorage.Upload(dstReplayFilePath, tunnel.Created.Format(recordDirTimeFormat)) + targetName := strings.Join([]string{tunnel.Created.Format(recordDirTimeFormat), + tunnel.ID + ReplayFileNameSuffix}, "/") + err = replayStorage.Upload(dstReplayFilePath, targetName) } else { err = s.JmsService.Upload(tunnel.ID, dstReplayFilePath) } diff --git a/pkg/tunnel/cache.go b/pkg/tunnel/cache.go index 12ddb4c..33e862b 100644 --- a/pkg/tunnel/cache.go +++ b/pkg/tunnel/cache.go @@ -3,62 +3,34 @@ package tunnel import ( "sync" + "lion/pkg/guacd" "lion/pkg/session" ) -type GuaTunnelCache struct { - sync.Mutex - Tunnels map[string]*Connection -} - -func (g *GuaTunnelCache) Add(t *Connection) { - g.Lock() - defer g.Unlock() - g.Tunnels[t.guacdTunnel.UUID] = t +type Tunneler interface { + WriteAndFlush(p []byte) (int, error) + ReadInstruction() (guacd.Instruction, error) + Close() error } -func (g *GuaTunnelCache) Delete(t *Connection) { - g.Lock() - defer g.Unlock() - delete(g.Tunnels, t.guacdTunnel.UUID) +type GuaTunnelCache interface { + Add(*Connection) + Delete(*Connection) + Get(string) *Connection + RangeActiveSessionIds() []string + RangeActiveUserIds() map[string]struct{} + GetBySessionId(sid string) *Connection + GetMonitorTunnelerBySessionId(sid string) Tunneler + RemoveMonitorTunneler(sid string, monitorTunnel Tunneler) } -func (g *GuaTunnelCache) Get(tid string) *Connection { - g.Lock() - defer g.Unlock() - return g.Tunnels[tid] -} - -func (g *GuaTunnelCache) Range() []string { - g.Lock() - ret := make([]string, 0, len(g.Tunnels)) - for i := range g.Tunnels { - ret = append(ret, g.Tunnels[i].Sess.ID) - } - g.Unlock() - return ret -} - -func (g *GuaTunnelCache) RangeUserIds() map[string]struct{} { - g.Lock() - ret := make(map[string]struct{}) - for i := range g.Tunnels { - currentUser := g.Tunnels[i].Sess.User - ret[currentUser.ID] = struct{}{} - } - g.Unlock() - return ret -} +var ( + _ GuaTunnelCache = (*GuaTunnelLocalCache)(nil) + _ GuaTunnelCache = (*GuaTunnelRedisCache)(nil) +) -func (g *GuaTunnelCache) GetBySessionId(sid string) *Connection { - g.Lock() - defer g.Unlock() - for i := range g.Tunnels { - if sid == g.Tunnels[i].Sess.ID { - return g.Tunnels[i] - } - } - return nil +type GuaTunnelCacheManager struct { + GuaTunnelCache } type SessionCache struct { diff --git a/pkg/tunnel/cache_local.go b/pkg/tunnel/cache_local.go new file mode 100644 index 0000000..05ba63d --- /dev/null +++ b/pkg/tunnel/cache_local.go @@ -0,0 +1,88 @@ +package tunnel + +import ( + "sync" + + "lion/pkg/guacd" + "lion/pkg/logger" +) + +func NewLocalTunnelLocalCache() *GuaTunnelLocalCache { + return &GuaTunnelLocalCache{ + Tunnels: make(map[string]*Connection), + } +} + +type GuaTunnelLocalCache struct { + sync.Mutex + Tunnels map[string]*Connection +} + +func (g *GuaTunnelLocalCache) Add(t *Connection) { + g.Lock() + defer g.Unlock() + g.Tunnels[t.guacdTunnel.UUID] = t +} + +func (g *GuaTunnelLocalCache) Delete(t *Connection) { + g.Lock() + defer g.Unlock() + delete(g.Tunnels, t.guacdTunnel.UUID) +} + +func (g *GuaTunnelLocalCache) Get(tid string) *Connection { + g.Lock() + defer g.Unlock() + return g.Tunnels[tid] +} + +func (g *GuaTunnelLocalCache) RangeActiveSessionIds() []string { + g.Lock() + ret := make([]string, 0, len(g.Tunnels)) + for i := range g.Tunnels { + ret = append(ret, g.Tunnels[i].Sess.ID) + } + g.Unlock() + return ret +} + +func (g *GuaTunnelLocalCache) RangeActiveUserIds() map[string]struct{} { + g.Lock() + ret := make(map[string]struct{}) + for i := range g.Tunnels { + currentUser := g.Tunnels[i].Sess.User + ret[currentUser.ID] = struct{}{} + } + g.Unlock() + return ret +} + +func (g *GuaTunnelLocalCache) GetBySessionId(sid string) *Connection { + g.Lock() + defer g.Unlock() + for i := range g.Tunnels { + if sid == g.Tunnels[i].Sess.ID { + return g.Tunnels[i] + } + } + return nil +} + +func (g *GuaTunnelLocalCache) GetMonitorTunnelerBySessionId(sid string) Tunneler { + if conn := g.GetBySessionId(sid); conn != nil { + if guacdTunnel, err := conn.CloneMonitorTunnel(); err == nil { + return guacdTunnel + } else { + logger.Error(err) + } + } + return nil +} + +func (g *GuaTunnelLocalCache) RemoveMonitorTunneler(sid string, monitorTunnel Tunneler) { + if conn := g.GetBySessionId(sid); conn != nil { + if tunnel, ok := monitorTunnel.(*guacd.Tunnel); ok { + conn.unTraceMonitorTunnel(tunnel) + } + } +} diff --git a/pkg/tunnel/cache_remote.go b/pkg/tunnel/cache_remote.go new file mode 100644 index 0000000..3bd993c --- /dev/null +++ b/pkg/tunnel/cache_remote.go @@ -0,0 +1,479 @@ +package tunnel + +import ( + "context" + "encoding/json" + "fmt" + "io" + "sync" + "time" + + "github.com/go-redis/redis/v8" + + "lion/pkg/common" + "lion/pkg/guacd" + "lion/pkg/logger" +) + +const ( + eventsChannel = "JUMPSERVER:LION:EVENTS:CHANNEL" + + resultsChannel = "JUMPSERVER:LION:EVENTS:RESULT" + + sessionsChannelPrefix = "JUMPSERVER:LION:SESSIONS" +) + +type Config struct { + // Addr of a single redis server instance. + // Defaults to "127.0.0.1:6379". + Addr string + + Password string + + DBIndex int +} + +func NewGuaTunnelRedisCache(conf Config) *GuaTunnelRedisCache { + if conf.Addr == "" { + conf.Addr = "127.0.0.1:6379" + } + rdb := redis.NewClient(&redis.Options{ + Addr: conf.Addr, + Password: conf.Password, + DB: conf.DBIndex, + }) + if _, err := rdb.Ping(context.TODO()).Result(); err != nil { + logger.Fatalf("Redis ping err: %s", err) + } + + cache := GuaTunnelRedisCache{ + ID: common.UUID(), + rdb: rdb, + requestChan: make(chan *subscribeRequest), + responseChan: make(chan chan *subscribeResponse), + reqCancelChan: make(chan *subscribeRequest), + localProxyExitSignalChan: make(chan string, 100), + GuaTunnelLocalCache: NewLocalTunnelLocalCache(), + } + go cache.run() + return &cache +} + +type GuaTunnelRedisCache struct { + *GuaTunnelLocalCache + + ID string + rdb *redis.Client + + requestChan chan *subscribeRequest + responseChan chan chan *subscribeResponse + reqCancelChan chan *subscribeRequest + + localProxyExitSignalChan chan string +} + +func (r *GuaTunnelRedisCache) GetMonitorTunnelerBySessionId(sid string) Tunneler { + tunneler := r.GuaTunnelLocalCache.GetMonitorTunnelerBySessionId(sid) + if tunneler != nil { + return tunneler + } + return r.requestRemoteTunnelerBySessionId(sid) +} + +func (r *GuaTunnelRedisCache) requestRemoteTunnelerBySessionId(sid string) Tunneler { + /* + 1. 发布请求 + 2. 收到Tunneler结果 + */ + req := r.createEventRequest(sid, channelEventJoin) + res, err := r.sendRequest(&req) + if err != nil { + logger.Error(err) + return nil + } + return res.conn +} + +func (r *GuaTunnelRedisCache) sendRequest(req *subscribeRequest) (*subscribeResponse, error) { + ctx, cancelFunc := context.WithTimeout(context.Background(), 20*time.Second) + defer cancelFunc() + r.requestChan <- req + resultChan := <-r.responseChan + select { + case <-ctx.Done(): + select { + case r.reqCancelChan <- req: + + case res := <-resultChan: + return res, res.err + } + case res := <-resultChan: + return res, res.err + } + return nil, fmt.Errorf("Redis cache send request event %s time out ", req.Event) +} + +func (r *GuaTunnelRedisCache) createEventRequest(sid, event string) subscribeRequest { + reqId := r.uniqueReqId(sid) + return subscribeRequest{ + ReqId: reqId, + SessionId: sid, + Event: event, + Prefix: reqId, + Channel: eventsChannel, + } +} + +func (r *GuaTunnelRedisCache) createResultRequest(reqId, roomId, event string) subscribeRequest { + return subscribeRequest{ + ReqId: reqId, + SessionId: roomId, + Event: event, + Prefix: reqId, + Channel: resultsChannel, + } +} + +/* +(确保每次都是唯一的) +prefix: sessionsChannelPrefix:uuid:reqId:sessionId + +*/ + +func (r *GuaTunnelRedisCache) uniqueReqId(sid string) string { + return fmt.Sprintf("%s:%s:%s:%s", + sessionsChannelPrefix, + common.UUID(), + r.ID, + sid) +} + +func (r *GuaTunnelRedisCache) publishRequest(req *subscribeRequest) error { + body, _ := json.Marshal(req) + return r.publishCommand(req.Channel, body) +} + +func (r *GuaTunnelRedisCache) publishCommand(channel string, p []byte) (err error) { + _, err = r.rdb.Publish(context.TODO(), channel, p).Result() + return +} + +func (r *GuaTunnelRedisCache) proxyTunnel(tunnelProxy *RedisGuacProxy) { + defer func() { + _ = tunnelProxy.Close() + r.GuaTunnelLocalCache.RemoveMonitorTunneler(tunnelProxy.sessionId, tunnelProxy.tunnel) + }() + logger.Infof("Redis guacd proxy %s tunnel start", tunnelProxy.reqId) + for { + ins, err := tunnelProxy.ReadInstruction() + if err != nil { + logger.Errorf("Redis guacd proxy %s tunnel exit", tunnelProxy.reqId) + return + } + if err = r.publishCommand(tunnelProxy.writeChannelName, []byte(ins.String())); err != nil { + logger.Errorf("Redis guacd proxy %s pubSub message err: %s", tunnelProxy.reqId, err) + } + } +} + +func (r *GuaTunnelRedisCache) run() { + innerPubSub := r.rdb.Subscribe(context.TODO(), eventsChannel, resultsChannel) + subscribeEventsMsgCh := innerPubSub.Channel() + requestsMap := make(map[string]chan *subscribeResponse) + proxyConnMap := make(map[string]*RedisGuacProxy) + for { + select { + case redisMsg := <-subscribeEventsMsgCh: + var req subscribeRequest + if err := json.Unmarshal([]byte(redisMsg.Payload), &req); err != nil { + logger.Errorf("Redis cache unmarshal request msg err: %s", err) + continue + } + logger.Infof("Redis channel %s recv request event %s", + redisMsg.Channel, req.Event) + + switch redisMsg.Channel { + case eventsChannel: + if _, ok := requestsMap[req.ReqId]; ok { + logger.Infof("Redis cache ignore self request %s", req.ReqId) + continue + } + // 创建result channel的req + switch req.Event { + case channelEventJoin: + successReq := r.createResultRequest(req.ReqId, req.SessionId, + channelEventJoinSuccess) + if conn := r.GuaTunnelLocalCache.GetBySessionId(req.SessionId); conn != nil { + guacdTunnel, err := conn.CloneMonitorTunnel() + if err != nil { + logger.Errorf("Redis cache create monitor tunneler for request %s: %s", + req.ReqId, err) + continue + } + err = r.publishRequest(&successReq) + if err != nil { + _ = guacdTunnel.Close() + logger.Errorf("Redis cache reply request %s join event err %s", req.ReqId, err) + continue + } + logger.Infof("Redis cache reply request %s join event", req.ReqId) + writeChannel := fmt.Sprintf("%s.read", req.Prefix) + readChannel := fmt.Sprintf("%s.write", req.Prefix) + pubSub := r.rdb.Subscribe(context.TODO(), readChannel) + proxyConn := RedisGuacProxy{ + reqId: req.ReqId, + sessionId: req.SessionId, + readChannelName: readChannel, + writeChannelName: writeChannel, + pubSub: pubSub, + cache: r, + done: make(chan struct{}), + tunnel: guacdTunnel, + } + proxyConnMap[req.ReqId] = &proxyConn + go proxyConn.run() + go r.proxyTunnel(&proxyConn) + } + + case channelEventExit: + if proxyConn, ok := proxyConnMap[req.ReqId]; ok { + logger.Infof("Redis cache reply %s exit event", req.ReqId) + delete(proxyConnMap, req.ReqId) + successReq := r.createResultRequest(req.ReqId, req.SessionId, + channelEventExitSuccess) + err := r.publishRequest(&successReq) + if err != nil { + logger.Errorf("Redis cache reply request %s exit event err %s", req.ReqId, err) + continue + } + _ = proxyConn.Close() + } + } + + case resultsChannel: + responseChan, ok := requestsMap[req.ReqId] + if !ok { + logger.Debugf("Redis cache ignore not self result request %s", req.ReqId) + continue + } + logger.Infof("Redis cache request %s receive result event %s", req.ReqId, req.Event) + // 请求结束,移除缓存, 返回请求的结果 + delete(requestsMap, req.ReqId) + switch req.Event { + case channelEventJoinSuccess: + var res subscribeResponse + res.Req = &req + writeChannel := fmt.Sprintf("%s.write", req.Prefix) + readChannel := fmt.Sprintf("%s.read", req.Prefix) + pubSub := r.rdb.Subscribe(context.TODO(), readChannel) + conn := RedisConn{ + reqId: req.ReqId, + sessionId: req.SessionId, + readChannelName: readChannel, + writeChannelName: writeChannel, + instructionChan: make(chan guacd.Instruction, 100), + cache: r, + pubSub: pubSub, + done: make(chan struct{}), + } + res.conn = &conn + go conn.run() + responseChan <- &res + case channelEventExitSuccess: + var res subscribeResponse + res.Req = &req + responseChan <- &res + } + default: + continue + } + case req := <-r.requestChan: + logger.Debugf("Redis cache publish request %s event %s", req.ReqId, req.Event) + responseChan := make(chan *subscribeResponse, 1) + r.responseChan <- responseChan + if err := r.publishRequest(req); err != nil { + logger.Errorf("Redis cache publish channel request err: %s", err) + delete(requestsMap, req.ReqId) + responseChan <- &subscribeResponse{ + Req: req, + err: err, + conn: nil, + } + continue + } + logger.Infof("Redis cache publish request %s event %s success", req.ReqId, req.Event) + requestsMap[req.ReqId] = responseChan + + case req := <-r.reqCancelChan: + delete(requestsMap, req.ReqId) + logger.Debugf("Redis cache cancel request: %s", req.ReqId) + + case reqId := <-r.localProxyExitSignalChan: + if _, ok := proxyConnMap[reqId]; ok { + logger.Infof("Redis cache recv proxy con %s exit signal", reqId) + delete(proxyConnMap, reqId) + } + } + } + +} + +type RedisConn struct { + reqId string + sessionId string + + readChannelName string + writeChannelName string + instructionChan chan guacd.Instruction + cache *GuaTunnelRedisCache + once sync.Once + pubSub *redis.PubSub + + done chan struct{} +} + +func (r *RedisConn) run() { + logger.Infof("Redis Conn %s pubSub run", r.reqId) + messageChan := r.pubSub.Channel() + defer close(r.instructionChan) + detectTicker := time.NewTicker(time.Minute) + defer detectTicker.Stop() + activeTime := time.Now() + for { + select { + case detectTime := <-detectTicker.C: + if detectTime.After(activeTime.Add(5 * time.Minute)) { + logger.Errorf("Redis Conn %s time out after 5 minute and exit.", r.reqId) + return + } + continue + case <-r.done: + return + case msg, ok := <-messageChan: + if !ok { + logger.Infof("Redis Conn %s pubSub exit", r.reqId) + return + } + switch msg.Channel { + case r.readChannelName: + if ret, err := guacd.ParseInstructionString(msg.Payload); err == nil { + select { + case <-r.done: + return + case r.instructionChan <- ret: + } + } else { + logger.Errorf("Redis Conn %s parse instruction err: %+v", r.reqId, err) + } + } + } + activeTime = time.Now() + } +} + +func (r *RedisConn) WriteAndFlush(p []byte) (int, error) { + if err := r.cache.publishCommand(r.writeChannelName, p); err != nil { + return 0, err + } + return len(p), nil +} + +func (r *RedisConn) ReadInstruction() (guacd.Instruction, error) { + if instruction, ok := <-r.instructionChan; ok { + return instruction, nil + } + return guacd.Instruction{}, io.EOF +} + +func (r *RedisConn) Close() error { + var err error + r.once.Do(func() { + logger.Debugf("Redis conn %s close", r.reqId) + err = r.pubSub.Close() + _, err := r.cache.sendRequest(&subscribeRequest{ + ReqId: r.reqId, + SessionId: r.sessionId, + Event: channelEventExit, + Prefix: r.reqId, + Channel: eventsChannel, + }) + if err != nil { + logger.Errorf("Redis conn %s send exit event err: %s", r.reqId, err) + } + + }) + return err +} + +const ( + channelEventJoin = "Join" + channelEventExit = "Exit" + channelEventJoinSuccess = "JoinSuccess" + channelEventExitSuccess = "ExitSuccess" +) + +type subscribeRequest struct { + ReqId string `json:"req_id"` + SessionId string `json:"session_id"` + Event string `json:"event"` + Prefix string `json:"prefix"` + Channel string `json:"-"` +} + +type subscribeResponse struct { + Req *subscribeRequest + err error + conn *RedisConn +} + +type RedisGuacProxy struct { + reqId string + sessionId string + + readChannelName string + writeChannelName string + pubSub *redis.PubSub + + cache *GuaTunnelRedisCache + + done chan struct{} + + tunnel *guacd.Tunnel + + once sync.Once +} + +func (r *RedisGuacProxy) run() { + logger.Infof("Redis guacd proxy %s pubSub run", r.reqId) + redisMsgChan := r.pubSub.Channel() + defer func() { + r.tunnel.Close() + r.cache.localProxyExitSignalChan <- r.reqId + }() + for { + select { + case redisMsg, ok := <-redisMsgChan: + if !ok { + logger.Infof("Redis guacd proxy %s pubSub exit", r.reqId) + return + } + _, _ = r.tunnel.WriteAndFlush([]byte(redisMsg.Payload)) + case <-r.done: + return + } + } +} + +func (r *RedisGuacProxy) ReadInstruction() (guacd.Instruction, error) { + return r.tunnel.ReadInstruction() +} + +func (r *RedisGuacProxy) Close() error { + var err error + r.once.Do(func() { + err = r.pubSub.Close() + close(r.done) + logger.Infof("Redis guacd proxy %s close", r.reqId) + }) + return err +} diff --git a/pkg/tunnel/conn.go b/pkg/tunnel/conn.go index 7b91ad9..4cbe857 100644 --- a/pkg/tunnel/conn.go +++ b/pkg/tunnel/conn.go @@ -1,6 +1,7 @@ package tunnel import ( + "net" "sort" "sync" "time" @@ -8,6 +9,7 @@ import ( "github.com/gin-gonic/gin" "github.com/gorilla/websocket" + "lion/pkg/config" "lion/pkg/guacd" "lion/pkg/logger" "lion/pkg/session" @@ -47,6 +49,11 @@ type Connection struct { outputFilter *OutputStreamInterceptingFilter inputFilter *InputStreamInterceptingFilter + + done chan struct{} + + traceLock sync.Mutex + traceMap map[*guacd.Tunnel]struct{} } func (t *Connection) SendWsMessage(msg guacd.Instruction) error { @@ -95,6 +102,7 @@ func (t *Connection) readTunnelInstruction() (*guacd.Instruction, error) { } func (t *Connection) Run(ctx *gin.Context) (err error) { + defer t.releaseMonitorTunnel() // 需要发送 uuid 返回给 guacamole tunnel err = t.SendWsMessage(guacd.NewInstruction( INTERNALDATAOPCODE, t.guacdTunnel.UUID)) @@ -148,8 +156,8 @@ func (t *Connection) Run(ctx *gin.Context) (err error) { switch ret.Opcode { case guacd.InstructionClientSync, - guacd.InstructionClientNop, - guacd.InstructionStreamingAck: + guacd.InstructionClientNop, + guacd.InstructionStreamingAck: case guacd.InstructionClientDisconnect: logger.Errorf("Session[%s] receive web client disconnect opcode", t) @@ -212,3 +220,47 @@ func (t *Connection) Terminate() { func (t *Connection) String() string { return t.Sess.ID } + +func (t *Connection) CloneMonitorTunnel() (*guacd.Tunnel, error) { + info := guacd.NewClientInformation() + conf := guacd.NewConfiguration() + conf.ConnectionID = t.guacdTunnel.UUID + guacdAddr := net.JoinHostPort(config.GlobalConfig.GuaHost, + config.GlobalConfig.GuaPort) + monitorTunnel, err := guacd.NewTunnel(guacdAddr, conf, info) + if err != nil { + return nil, err + } + t.traceMonitorTunnel(monitorTunnel) + return monitorTunnel, err +} + +func (t *Connection) traceMonitorTunnel(monitorTunnel *guacd.Tunnel) { + t.traceLock.Lock() + defer t.traceLock.Unlock() + if t.traceMap == nil { + t.traceMap = make(map[*guacd.Tunnel]struct{}) + } + t.traceMap[monitorTunnel] = struct{}{} +} + +func (t *Connection) releaseMonitorTunnel() { + t.traceLock.Lock() + defer t.traceLock.Unlock() + if t.traceMap == nil { + return + } + for tunneler := range t.traceMap { + _ = tunneler.Close() + } + +} + +func (t *Connection) unTraceMonitorTunnel(monitorTunnel *guacd.Tunnel) { + t.traceLock.Lock() + defer t.traceLock.Unlock() + if t.traceMap == nil { + return + } + delete(t.traceMap, monitorTunnel) +} \ No newline at end of file diff --git a/pkg/tunnel/monitor.go b/pkg/tunnel/monitor.go new file mode 100644 index 0000000..632c7e6 --- /dev/null +++ b/pkg/tunnel/monitor.go @@ -0,0 +1,106 @@ +package tunnel + +import ( + "context" + "sync" + + "github.com/gorilla/websocket" + + "lion/pkg/guacd" + "lion/pkg/logger" +) + +type MonitorCon struct { + guacdTunnel Tunneler + + ws *websocket.Conn + + wsLock sync.Mutex + guacdLock sync.Mutex +} + +func (m *MonitorCon) SendWsMessage(msg guacd.Instruction) error { + return m.writeWsMessage([]byte(msg.String())) +} + +func (m *MonitorCon) writeWsMessage(p []byte) error { + m.wsLock.Lock() + defer m.wsLock.Unlock() + return m.ws.WriteMessage(websocket.TextMessage, p) +} + +func (m *MonitorCon) WriteTunnelMessage(msg guacd.Instruction) (err error) { + _, err = m.writeTunnelMessage([]byte(msg.String())) + return err +} + +func (m *MonitorCon) writeTunnelMessage(p []byte) (int, error) { + m.guacdLock.Lock() + defer m.guacdLock.Unlock() + return m.guacdTunnel.WriteAndFlush(p) +} +func (m *MonitorCon) readTunnelInstruction() (*guacd.Instruction, error) { + instruction, err := m.guacdTunnel.ReadInstruction() + if err != nil { + return nil, err + } + return &instruction, nil +} + +func (m *MonitorCon) Run(ctx context.Context) (err error) { + exit := make(chan error, 2) + go func(t *MonitorCon) { + for { + instruction, err := t.readTunnelInstruction() + if err != nil { + logger.Error(err) + exit <- err + break + } + if err = t.writeWsMessage([]byte(instruction.String())); err != nil { + logger.Error(err) + exit <- err + break + } + } + _ = t.ws.Close() + }(m) + + go func(t *MonitorCon) { + for { + _, message, err := t.ws.ReadMessage() + if err != nil { + exit <- err + break + } + + if ret, err := guacd.ParseInstructionString(string(message)); err == nil { + if ret.Opcode == INTERNALDATAOPCODE && len(ret.Args) >= 2 && ret.Args[0] == PINGOPCODE { + if err := t.SendWsMessage(guacd.NewInstruction(INTERNALDATAOPCODE, PINGOPCODE)); err != nil { + logger.Error(err) + } + continue + } + } else { + logger.Errorf("Parse instruction err %s", err) + } + _, err = t.writeTunnelMessage(message) + if err != nil { + logger.Errorf("Guacamole server write err: %+v", err) + exit <- err + break + } + } + }(m) + + for { + select { + case err = <-exit: + return err + case <-ctx.Done(): + _ = m.ws.Close() + _ = m.guacdTunnel.Close() + return nil + } + } +} diff --git a/pkg/tunnel/server.go b/pkg/tunnel/server.go index a19f743..8389100 100644 --- a/pkg/tunnel/server.go +++ b/pkg/tunnel/server.go @@ -35,7 +35,7 @@ var upGrader = websocket.Upgrader{ type GuacamoleTunnelServer struct { JmsService *service.JMService - Cache *GuaTunnelCache + Cache *GuaTunnelCacheManager SessCache *SessionCache SessionService *session.Server } @@ -78,6 +78,7 @@ func (g *GuacamoleTunnelServer) Connect(ctx *gin.Context) { ctx.AbortWithStatus(http.StatusBadRequest) return } + defer ws.Close() sessionId, ok := ctx.GetQuery("SESSION_ID") if !ok { data := guacd.NewInstruction( @@ -158,6 +159,7 @@ func (g *GuacamoleTunnelServer) Connect(ctx *gin.Context) { Sess: tunnelSession, guacdTunnel: tunnel, ws: ws, + done: make(chan struct{}), } outFilter := OutputStreamInterceptingFilter{ acknowledgeBlobs: true, @@ -170,6 +172,7 @@ func (g *GuacamoleTunnelServer) Connect(ctx *gin.Context) { } conn.outputFilter = &outFilter conn.inputFilter = &inputFilter + logger.Infof("Session[%s] connect success", sessionId) g.Cache.Add(&conn) _ = conn.Run(ctx) g.Cache.Delete(&conn) @@ -340,6 +343,7 @@ func (g *GuacamoleTunnelServer) Monitor(ctx *gin.Context) { ctx.AbortWithStatus(http.StatusBadRequest) return } + defer ws.Close() userItem, ok := ctx.Get(config.GinCtxUserKey) if !ok { data := guacd.NewInstruction( @@ -361,30 +365,18 @@ func (g *GuacamoleTunnelServer) Monitor(ctx *gin.Context) { _ = ws.WriteMessage(websocket.TextMessage, []byte(data.String())) return } - info := g.getClientInfo(ctx) - tunnelCon := g.Cache.GetBySessionId(sessionId) + tunnelCon := g.Cache.GetMonitorTunnelerBySessionId(sessionId) if tunnelCon == nil { data := guacd.NewInstruction( guacd.InstructionServerError, "no found tunnel", "504") _ = ws.WriteMessage(websocket.TextMessage, []byte(data.String())) return } - guacdConnectID := tunnelCon.guacdTunnel.UUID - conf := tunnelCon.Sess.GuaConfiguration() - conf.ConnectionID = guacdConnectID - guacdAddr := net.JoinHostPort(config.GlobalConfig.GuaHost, config.GlobalConfig.GuaPort) - tunnel, err := guacd.NewTunnel(guacdAddr, conf, info) - if err != nil { - logger.Errorf("Connect tunnel err: %+v", err) - data := guacd.NewInstruction( - guacd.InstructionServerError, err.Error(), "504") - _ = ws.WriteMessage(websocket.TextMessage, []byte(data.String())) - return - } - conn := Connection{ - Sess: tunnelCon.Sess, - guacdTunnel: tunnel, + defer tunnelCon.Close() + conn := MonitorCon{ + guacdTunnel: tunnelCon, ws: ws, } - _ = conn.Run(ctx) + _ = conn.Run(ctx.Request.Context()) + g.Cache.RemoveMonitorTunneler(sessionId, tunnelCon) } diff --git a/pkg/tunnel/stream_input.go b/pkg/tunnel/stream_input.go index 3413d14..8743e1a 100644 --- a/pkg/tunnel/stream_input.go +++ b/pkg/tunnel/stream_input.go @@ -86,9 +86,9 @@ func (filter *InputStreamInterceptingFilter) addInputStream(stream *InputStreamR // 上传文件的对象 type InputStreamResource struct { streamIndex string - mediaType string // application/octet-stream - reader io.ReadCloser - done chan struct{} + //mediaType string // application/octet-stream + reader io.ReadCloser + done chan struct{} err error } diff --git a/pkg/tunnel/ws_error.go b/pkg/tunnel/ws_error.go new file mode 100644 index 0000000..e348cbc --- /dev/null +++ b/pkg/tunnel/ws_error.go @@ -0,0 +1,19 @@ +package tunnel + +import ( + "strconv" + + "lion/pkg/guacd" +) + +type GuacamoleServerError struct { + err error + code int +} + +func (g GuacamoleServerError) String() guacd.Instruction { + return guacd.NewInstruction( + guacd.InstructionServerError, + g.err.Error(), + strconv.Itoa(g.code)) +}