Fix ssb-ws connections

The landing path (/) can now also be used as a websocket endpoint.
This enables use of the room server with ssb-browser-demo and similar
applications that can't use TCP connections.

also pin WIP muxrpc branch (fix-stupid-timeout)
This commit is contained in:
Henry 2021-04-05 11:33:28 +02:00
parent 3afe9b3659
commit 940cd2b3d1
11 changed files with 326 additions and 139 deletions

View File

@ -368,6 +368,7 @@ func runroomsrv() error {
var httpHandler http.Handler
httpHandler = httpRateLimiter.RateLimit(webHandler)
httpHandler = secureMiddleware.Handler(httpHandler)
httpHandler = roomsrv.Network.WebsockHandler(httpHandler)
// all init was successfull
level.Info(log).Log(

7
go.mod
View File

@ -14,10 +14,9 @@ require (
github.com/gorilla/securecookie v1.1.1
github.com/gorilla/sessions v1.2.1
github.com/gorilla/websocket v1.4.2
github.com/joefitzgerald/rainbow-reporter v0.1.0 // indirect
github.com/keks/nocomment v0.0.0-20181007001506-30c6dcb4a472
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0 // indirect
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0
github.com/nicksnyder/go-i18n/v2 v2.1.2
github.com/pkg/errors v0.9.1
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351
@ -30,7 +29,7 @@ require (
github.com/volatiletech/sqlboiler-sqlite3 v0.0.0-20210314195744-a1c697a68aef // indirect
github.com/volatiletech/sqlboiler/v4 v4.5.0
github.com/volatiletech/strmangle v0.0.1
go.cryptoscope.co/muxrpc/v2 v2.0.0-beta.1.0.20210308090127-5f1f5f9cbb59
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414085324-530a7df6ea1c
go.cryptoscope.co/netwrap v0.1.1
go.cryptoscope.co/secretstream v1.2.2
go.mindeco.de v1.11.0
@ -39,7 +38,7 @@ require (
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/text v0.3.5
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

29
go.sum
View File

@ -225,7 +225,6 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@ -251,7 +250,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
@ -304,6 +302,7 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nicksnyder/go-i18n/v2 v2.1.2 h1:QHYxcUJnGHBaq7XbvgunmZ2Pn0focXFqTD61CkH146c=
github.com/nicksnyder/go-i18n/v2 v2.1.2/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
@ -319,6 +318,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
@ -382,7 +382,7 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
@ -485,8 +485,18 @@ go.cryptoscope.co/margaret v0.0.5/go.mod h1:W+Q6lvzHIrF8+Yt3dItHHsx2R9/Xvj/NkJGM
go.cryptoscope.co/margaret v0.0.8/go.mod h1:VbP0bqavqW5osTmdNvgukrqtmqvZC5sXyPSBUW5rzv8=
go.cryptoscope.co/margaret v0.0.12-0.20190912103626-34323ad497f4 h1:gLSldWRujtUOfdnpA1XKD71xcCp3Wz1URMnT6xpUPV4=
go.cryptoscope.co/margaret v0.0.12-0.20190912103626-34323ad497f4/go.mod h1:3rt+RmZTFZEgfvFxz0ZPDBIWtLJOouWtzV6YbBl6sek=
go.cryptoscope.co/muxrpc/v2 v2.0.0-beta.1.0.20210308090127-5f1f5f9cbb59 h1:Gv5pKkvHYJNc12uRZ/jMCsR17G7v6oFLLCrGAUVxhvo=
go.cryptoscope.co/muxrpc/v2 v2.0.0-beta.1.0.20210308090127-5f1f5f9cbb59/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.0 h1:Wboamtw7WZPlmEGnGHIPNrScw7HUasXycMmtz+HWXcQ=
go.cryptoscope.co/muxrpc/v2 v2.0.0/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210413143018-bd871b2ec7dd h1:XlPynETXT4tzlzEX8r/V3trDHUDk2ktGol4d3suyC2s=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210413143018-bd871b2ec7dd/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414075132-c1a2adf3d317 h1:in3PvGIazNrexusC+IYXayQ5Ih3CdbWi/oLcArxfnyQ=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414075132-c1a2adf3d317/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414084152-08e3e6acc50d h1:PYOUNvgxKOeJLAF+s+uj31L9xf1xGhqPeHKpORANfTk=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414084152-08e3e6acc50d/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414085324-530a7df6ea1c h1:UQ2PCZ2VDKwvuWXtvyLkCXfdztJPr8tGhTnjsK36JGI=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414085324-530a7df6ea1c/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1 h1:U1dS1dsFk7/LygH8o2qsWy8dHB5g3YeLtm0lsN7c1CI=
go.cryptoscope.co/muxrpc/v2 v2.0.1/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/netwrap v0.1.0/go.mod h1:7zcYswCa4CT+ct54e9uH9+IIbYYETEMHKDNpzl8Ukew=
go.cryptoscope.co/netwrap v0.1.1 h1:JLzzGKEvrUrkKzu3iM0DhpHmt+L/gYqmpcf1lJMUyFs=
go.cryptoscope.co/netwrap v0.1.1/go.mod h1:7zcYswCa4CT+ct54e9uH9+IIbYYETEMHKDNpzl8Ukew=
@ -495,10 +505,6 @@ go.cryptoscope.co/secretstream v1.2.2/go.mod h1:7nRGZ7fTqSgQAnv2Y4m8xQsS3MFxvB7I
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mindeco.de v1.9.0 h1:/xli02DkzpIUZxp/rp1nj8z/OZ9MHvkMIr9TfDVcmBg=
go.mindeco.de v1.9.0/go.mod h1:ePOcyktbpqzhMPRBDv2gUaDd3h8QtT+DUU1DK+VbQZE=
go.mindeco.de v1.10.0 h1:H/bhL+dIgZZnUgBEDlKUJBisTszNiHDONeGZtGdiJJ0=
go.mindeco.de v1.10.0/go.mod h1:ePOcyktbpqzhMPRBDv2gUaDd3h8QtT+DUU1DK+VbQZE=
go.mindeco.de v1.11.0 h1:zrNJFjRr7l+eoRywKxBetB1sgQwnrJ9NA65H/xeusTs=
go.mindeco.de v1.11.0/go.mod h1:ePOcyktbpqzhMPRBDv2gUaDd3h8QtT+DUU1DK+VbQZE=
go.mindeco.de/ssb-refs v0.1.1-0.20210108133850-cf1f44fea870 h1:TCI3AefMAaOYECvppn30+CfEB0Fn8IES1SKvvacc3/c=
@ -546,8 +552,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191116160921-f9c825593386 h1:ktbWvQrW08Txdxno1PiDpSxPXG6ndGsfnJjRRtkM0LQ=
golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs=
@ -612,7 +616,6 @@ golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/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-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY2R5cH/6wL7eIxEmQOMSE=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6 h1:rbvTkL9AkFts1cgI78+gG6Yu1pwaqX6hjSJAatB78E4=
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@ -649,8 +652,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
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=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=

View File

@ -97,8 +97,11 @@ type Network interface {
GetConnTracker() ConnTracker
// websock hack
HandleHTTP(handler http.Handler)
// WebsockHandler returns a "middleware" like thing that is able to upgrade a websocket request to a muxrpc connection and authenticate using shs.
// It calls the next handler if it fails to upgrade the connection to websocket.
// However, it will error on the request and not call the passed handler
// if the websocket upgrade is successfull.
WebsockHandler(next http.Handler) http.Handler
io.Closer
}

View File

@ -43,8 +43,6 @@ type Options struct {
// AfterSecureWrappers are applied afterwards, usefull to debug muxrpc content
AfterSecureWrappers []netwrap.ConnWrapper
WebsocketAddr string
}
type node struct {
@ -110,33 +108,6 @@ func New(opts Options) (Network, error) {
n.log = opts.Logger
// local websocket
wsHandler := websockHandler(n)
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
url := req.URL.String()
if url == "/" {
wsHandler(w, req)
return
}
// n.log.Log("http-url-req", url)
if n.httpHandler != nil {
n.httpHandler.ServeHTTP(w, req)
}
})
if addr := opts.WebsocketAddr; addr != "" {
n.httpLis, err = net.Listen("tcp", addr)
if err != nil {
return nil, err
}
// TODO: move to serve
go func() {
err := http.Serve(n.httpLis, httpHandler)
level.Error(n.log).Log("conn", "ssb-ws :8998 listen exited", "err", err)
}()
}
return n, nil
}

View File

@ -10,128 +10,175 @@ import (
"net/http"
"time"
kitlog "github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/gorilla/websocket"
"go.cryptoscope.co/muxrpc/v2"
)
func websockHandler(n *node) http.HandlerFunc {
// WebsockHandler returns a "middleware" like thing that is able to upgrade a websocket request to a muxrpc connection and authenticate using shs.
// It calls the next handler if it fails to upgrade the connection to websocket.
// However, it will error on the request and not call the passed handler
// if the websocket upgrade is successfull.
func (n *node) WebsockHandler(next http.Handler) http.Handler {
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024 * 4,
WriteBufferSize: 1024 * 4,
CheckOrigin: func(_ *http.Request) bool {
return true
},
// 99% of the traffic will be ciphertext which is impossible to distingish from randomness and thus also hard to compress
EnableCompression: false,
// if upgrading fails, just call the next handler and ignore the error
Error: func(w http.ResponseWriter, req *http.Request, _ int, _ error) {
next.ServeHTTP(w, req)
},
}
return func(w http.ResponseWriter, req *http.Request) {
remoteAddr, err := net.ResolveTCPAddr("tcp", req.RemoteAddr)
if err != nil {
n.log.Log("warning", "failed wrap", "err", err, "remote", remoteAddr)
return
}
wsConn, err := upgrader.Upgrade(w, req, nil)
if err != nil {
n.log.Log("warning", "failed wrap", "err", err, "remote", remoteAddr)
return
}
var wc net.Conn
wc = &wrappedConn{
remote: remoteAddr,
local: &net.TCPAddr{
IP: nil,
Port: 8989,
},
wsc: wsConn,
}
var wsh websocketHandelr
wsh.next = next
wsh.upgrader = &upgrader
wsh.muxnetwork = n
// comment out this block to get `noauth` instead of `shs`
// TODO:
// netwrap.WrapAddr(remoteAddr, secretstream.Addr{
// PubKey: n.opts.KeyPair.Id.ID,
// })
cw := n.secretServer.ConnWrapper()
wc, err = cw(wc)
if err != nil {
level.Error(n.log).Log("warning", "failed to crypt", "err", err, "remote", remoteAddr)
wsConn.Close()
return
}
// debugging copy of all muxrpc frames
// can be handy for reversing applications
// wrapped, err := debug.WrapDump("webmux", cryptoConn)
// if err != nil {
// level.Error(n.log).Log("warning", "failed wrap", "err", err, "remote", remoteAddr)
// wsConn.Close()
// return
// }
pkr := muxrpc.NewPacker(wc)
h, err := n.opts.MakeHandler(wc)
if err != nil {
err = fmt.Errorf("unix sock make handler: %w", err)
level.Error(n.log).Log("warn", err)
wsConn.Close()
return
}
edp := muxrpc.Handle(pkr, h,
muxrpc.WithContext(req.Context()),
muxrpc.WithRemoteAddr(wc.RemoteAddr()))
srv := edp.(muxrpc.Server)
// TODO: bundle root and connection context
if err := srv.Serve(); err != nil {
level.Error(n.log).Log("conn", "serve exited", "err", err, "peer", remoteAddr)
}
wsConn.Close()
}
return wsh
}
type wrappedConn struct {
remote net.Addr
local net.Addr
type websocketHandelr struct {
next http.Handler
muxnetwork *node
upgrader *websocket.Upgrader
}
func (wsh websocketHandelr) ServeHTTP(w http.ResponseWriter, req *http.Request) {
remoteAddrStr := req.Header.Get("X-Forwarded-For")
if remoteAddrStr == "" {
remoteAddrStr = req.RemoteAddr
}
remoteAddr, err := net.ResolveTCPAddr("tcp", remoteAddrStr)
if err != nil {
wsh.next.ServeHTTP(w, req)
return
}
wsConn, err := wsh.upgrader.Upgrade(w, req, nil)
if err != nil {
return
}
errLog := level.Error(wsh.muxnetwork.log)
errLog = kitlog.With(errLog, "remote", remoteAddrStr)
var wc net.Conn
wc = NewWebsockConn(wsConn)
cw := wsh.muxnetwork.secretServer.ConnWrapper()
wc, err = cw(wc)
if err != nil {
errLog.Log("warning", "failed to authenticate", "err", err, "remote", remoteAddr)
wsConn.Close()
return
}
// debugging copy of all muxrpc frames
// can be handy for reversing applications
// feed, err := GetFeedRefFromAddr(wc.RemoteAddr())
// if err != nil {
// errLog.Log("warning", "failed to get feed after auth", "err", err, "remote", remoteAddr)
// wsConn.Close()
// return
// }
// dumpPath := filepath.Join("webmux", base64.URLEncoding.EncodeToString(feed.ID[:10]), remoteAddrStr)
// wc, err = debug.WrapDump(dumpPath, wc)
// if err != nil {
// errLog.Log("warning", "failed wrap", "err", err, "remote", remoteAddr)
// wsConn.Close()
// return
// }
pkr := muxrpc.NewPacker(wc)
h, err := wsh.muxnetwork.opts.MakeHandler(wc)
if err != nil {
err = fmt.Errorf("websocket make handler failed: %w", err)
errLog.Log("warn", err)
wsConn.Close()
return
}
edp := muxrpc.Handle(pkr, h,
muxrpc.WithContext(req.Context()),
muxrpc.WithRemoteAddr(wc.RemoteAddr()))
srv := edp.(muxrpc.Server)
if err := srv.Serve(); err != nil {
errLog.Log("conn", "serve exited", "err", err, "peer", remoteAddr)
}
wsConn.Close()
}
// WebsockConn emulates a normal net.Conn from a websocket connection
type WebsockConn struct {
r io.Reader
wsc *websocket.Conn
}
func (conn *wrappedConn) Read(data []byte) (int, error) {
func NewWebsockConn(wsc *websocket.Conn) *WebsockConn {
return &WebsockConn{
wsc: wsc,
}
}
func (conn *WebsockConn) Read(data []byte) (int, error) {
if conn.r == nil {
if err := conn.renewReader(); err != nil {
return -1, err
}
}
n, err := conn.r.Read(data)
if err == io.EOF {
if err := conn.renewReader(); err != nil {
return -1, err
}
return conn.Read(data)
n, err = conn.Read(data)
if err != nil {
err = fmt.Errorf("wsConn: failed to read after renew(): %w", err)
return -1, err
}
return n, nil
}
return n, err
if err != nil {
return -1, fmt.Errorf("wsConn: read failed: %w", err)
}
return n, nil
}
func (wc *wrappedConn) renewReader() error {
mt, r, err := wc.wsc.NextReader()
func (conn *WebsockConn) renewReader() error {
mt, r, err := conn.wsc.NextReader()
if err != nil {
return fmt.Errorf("wsConn: failed to get reader: %w", err)
err = fmt.Errorf("wsConn: failed to get reader: %w", err)
return err
}
if mt != websocket.BinaryMessage {
return fmt.Errorf("wsConn: not binary message: %v", mt)
}
wc.r = r
conn.r = r
return nil
}
func (conn wrappedConn) Write(data []byte) (int, error) {
func (conn *WebsockConn) Write(data []byte) (int, error) {
writeCloser, err := conn.wsc.NextWriter(websocket.BinaryMessage)
if err != nil {
return -1, fmt.Errorf("wsConn: failed to create Reader: %w", err)
@ -141,21 +188,41 @@ func (conn wrappedConn) Write(data []byte) (int, error) {
if err != nil {
return -1, fmt.Errorf("wsConn: failed to copy data: %w", err)
}
return int(n), writeCloser.Close()
}
func (conn wrappedConn) Close() error {
func (conn *WebsockConn) Close() error {
return conn.wsc.Close()
}
func (c wrappedConn) LocalAddr() net.Addr { return c.local }
func (c wrappedConn) RemoteAddr() net.Addr { return c.remote }
func (c wrappedConn) SetDeadline(t time.Time) error {
return nil // c.conn.SetDeadline(t)
func (conn *WebsockConn) LocalAddr() net.Addr { return conn.wsc.LocalAddr() }
func (conn *WebsockConn) RemoteAddr() net.Addr { return conn.wsc.RemoteAddr() }
func (conn *WebsockConn) SetDeadline(t time.Time) error {
rErr := conn.wsc.SetReadDeadline(t)
wErr := conn.wsc.SetWriteDeadline(t)
var err error
if rErr != nil {
err = fmt.Errorf("websock conn: failed to set read deadline: %w", rErr)
}
if wErr != nil {
wErr = fmt.Errorf("websock conn: failed to set read deadline: %w", wErr)
if err != nil {
err = fmt.Errorf("both faild: %w and %s", err, wErr)
} else {
err = wErr
}
}
return err
}
func (c wrappedConn) SetReadDeadline(t time.Time) error {
return nil // c.conn.SetReadDeadline(t)
func (conn *WebsockConn) SetReadDeadline(t time.Time) error {
return conn.wsc.SetReadDeadline(t)
}
func (c wrappedConn) SetWriteDeadline(t time.Time) error {
return nil // c.conn.SetWriteDeadline(t)
func (conn *WebsockConn) SetWriteDeadline(t time.Time) error {
return conn.wsc.SetWriteDeadline(t)
}

View File

@ -6,6 +6,7 @@ import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/network"
@ -98,23 +99,28 @@ func (h *Handler) endpoints(ctx context.Context, req *muxrpc.Request, snk *muxrp
}
type updateForwarder struct {
mu sync.Mutex // only one caller to forwarder at a time
snk *muxrpc.ByteSink
enc *json.Encoder
}
func newForwarder(snk *muxrpc.ByteSink) updateForwarder {
func newForwarder(snk *muxrpc.ByteSink) *updateForwarder {
enc := json.NewEncoder(snk)
snk.SetEncoding(muxrpc.TypeJSON)
return updateForwarder{
return &updateForwarder{
snk: snk,
enc: enc,
}
}
func (uf updateForwarder) Update(members []string) error {
func (uf *updateForwarder) Update(members []string) error {
uf.mu.Lock()
defer uf.mu.Unlock()
return uf.enc.Encode(members)
}
func (uf updateForwarder) Close() error {
func (uf *updateForwarder) Close() error {
uf.mu.Lock()
defer uf.mu.Unlock()
return uf.snk.Close()
}

View File

@ -0,0 +1,134 @@
package go_test
import (
"context"
"encoding/base64"
"encoding/json"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"testing"
"time"
"go.cryptoscope.co/muxrpc/v2/debug"
"go.mindeco.de/encodedTime"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/require"
"go.cryptoscope.co/muxrpc/v2"
"go.cryptoscope.co/secretstream"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/maybemod/keys"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/network"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
)
func TestWebsocketDialing(t *testing.T) {
r := require.New(t)
testPath := filepath.Join("testrun", t.Name())
os.RemoveAll(testPath)
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
t.Cleanup(cancel)
// create the roomsrv
serverMembers, server := makeNamedTestBot(t, "server", nil)
// open a TCP listener for HTTP
l, err := net.Listen("tcp4", "localhost:0")
r.NoError(err)
var fh = failHandler{t: t}
// serve the websocket handler
handler := server.Network.WebsockHandler(fh)
go http.Serve(l, handler)
// create a fresh keypair for the client
client, err := keys.NewKeyPair(nil)
r.NoError(err)
// add it as a memeber
memberID, err := serverMembers.Add(ctx, client.Feed, roomdb.RoleMember)
r.NoError(err)
t.Log("client member:", memberID)
// construct the websocket http address
var wsURL url.URL
wsURL.Scheme = "ws"
wsURL.Host = l.Addr().String()
wsURL.Path = "/"
// create a websocket connection
conn, resp, err := websocket.DefaultDialer.DialContext(ctx, wsURL.String(), nil)
r.NoError(err)
t.Log(resp.Status)
r.Equal(http.StatusSwitchingProtocols, resp.StatusCode)
// default app key for the secret-handshake connection
ak, err := base64.StdEncoding.DecodeString("1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=")
r.NoError(err)
// create a shs client to authenticate and encrypt the connection
clientSHS, err := secretstream.NewClient(client.Pair, ak)
r.NoError(err)
// the returned wrapper: func(net.Conn) (net.Conn, error)
// returns a new connection that went through shs and does boxstream
connAuther := clientSHS.ConnWrapper(server.Whoami().PubKey())
// turn a websocket conn into a net.Conn
wrappedConn := network.NewWebsockConn(conn)
authedConn, err := connAuther(wrappedConn)
r.NoError(err)
var muxMock muxrpc.FakeHandler
debugConn := debug.Dump(filepath.Join(testPath, "client"), authedConn)
pkr := muxrpc.NewPacker(debugConn)
wsEndpoint := muxrpc.Handle(pkr, &muxMock, muxrpc.WithContext(ctx))
srv := wsEndpoint.(muxrpc.Server)
go func() {
err = srv.Serve()
r.NoError(err)
t.Log("mux server error:", err)
}()
// check we are talking to a room
var yup bool
err = wsEndpoint.Async(ctx, &yup, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "isRoom"})
r.NoError(err)
r.True(yup, "server is not a room?")
// open the gossip.ping channel
src, snk, err := wsEndpoint.Duplex(ctx, muxrpc.TypeJSON, muxrpc.Method{"gossip", "ping"})
r.NoError(err)
pingTS := encodedTime.NewMillisecs(time.Now().Unix())
err = json.NewEncoder(snk).Encode(pingTS)
r.NoError(err)
r.True(src.Next(ctx))
pong, err := src.Bytes()
r.NoError(err)
var pongTS encodedTime.Millisecs
err = json.Unmarshal(pong, &pongTS)
r.NoError(err)
r.False(time.Time(pongTS).IsZero())
}
type failHandler struct {
t *testing.T
}
func (fh failHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
fh.t.Error("next handler called", req.URL.String())
}

View File

@ -60,8 +60,6 @@ func (s *Server) initNetwork() error {
ConnTracker: s.networkConnTracker,
BefreCryptoWrappers: s.preSecureWrappers,
AfterSecureWrappers: s.postSecureWrappers,
WebsocketAddr: s.wsAddr,
}
var err error

View File

@ -32,6 +32,10 @@ const manifest manifestHandler = `
"whoami":"async",
"gossip": {
"ping": "duplex"
},
"room": {
"registerAlias": "async",
"revokeAlias": "async",

View File

@ -110,7 +110,8 @@ func (m *Manager) Remove(who refs.FeedRef) {
m.roomMu.Unlock()
}
// AlreadyAdded returns true if the peer was already added to the room
// AlreadyAdded returns true if the peer was already added to the room.
// if it isn't it will be added.
func (m *Manager) AlreadyAdded(who refs.FeedRef, edp muxrpc.Endpoint) bool {
m.roomMu.Lock()