Merge pull request #141 from ssb-ngi-pointer/fix-websocket
Fix websocket
This commit is contained in:
commit
a872ddf2c0
|
@ -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(
|
||||
|
|
|
@ -17,3 +17,20 @@ This implementation is compliant with [SSB HTTP Authentication](https://github.c
|
|||
A summary can be seen in the following chart:
|
||||
|
||||
![Chart](./login-chart.png)
|
||||
|
||||
# HTTP Hosting
|
||||
|
||||
We currently assume a standard HTTPS server in front of go-ssb-room to facilitate TLS termination and certificate management. This should be possible with most modern HTTP servers since it's a pretty standard practice, known as [reverse proxying](https://en.wikipedia.org/wiki/Reverse_proxy).
|
||||
|
||||
Two bits of rationale:
|
||||
|
||||
1) People usually want to have more than one site on their server. Put differently, we could have [LetsEncrypt](https://letsencrypt.org/) inside the go-ssb-room server but it would have to listen on port :443—blocking the use of other domains on the same IP.
|
||||
2) Listening on :443 can be pretty annoying (you might need root privileges or similar capabilities).
|
||||
|
||||
go-ssb-room needs three headers to function properly, which need to be forwarded by the webserver.
|
||||
|
||||
* `X-Forwarded-Host` as which domain name the room is running (enforce strict TLS checking)
|
||||
* `X-Forwarded-Proto` to ensure that TLS is used (and redirect if necessary)
|
||||
* `X-Forwarded-For` the remote TCP/IP address of the client accessing the room (used for rate limiting)
|
||||
|
||||
[nginx-example.conf](./nginx-example.conf) contains an [nginx](https://nginx.org) config that we use for [hermies.club](https://hermies.club). To get a wildcard TLS certificate you can follow the steps in [this article](https://medium.com/@alitou/getting-a-wildcard-ssl-certificate-using-certbot-and-deploy-on-nginx-15b8ffa34157), which uses the [certbot](https://certbot.eff.org/) utility.
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
server {
|
||||
server_name hermies.club;
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/hermies.club/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/hermies.club/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8899;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# for websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
|
||||
# TODO: https://blog.tarq.io/nginx-catch-all-error-pages/
|
||||
}
|
||||
|
||||
# this server uses the (same) wildcard cert as the one above but uses a regular expression on the hostname
|
||||
# which extracts the first subdomain which holds the alias and forwards that to the prox_pass server
|
||||
server {
|
||||
server_name "~^(?<alias>\w+)\.hermies\.club$";
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/hermies.club/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/hermies.club/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
location = / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://localhost:8899/alias/$alias;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://localhost:8899;
|
||||
}
|
||||
|
||||
# TODO: https://blog.tarq.io/nginx-catch-all-error-pages/
|
||||
}
|
||||
|
||||
server {
|
||||
if ($host ~ hermies.club$ ) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name hermies.club;
|
||||
return 404; # managed by Certbot
|
||||
}
|
7
go.mod
7
go.mod
|
@ -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
29
go.sum
|
@ -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=
|
||||
|
|
|
@ -97,8 +97,12 @@ 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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -10,128 +10,177 @@ 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 distinguish
|
||||
// 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 +190,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)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ package gossip
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
|
@ -16,10 +15,11 @@ import (
|
|||
// Ping implements the server side of gossip.ping.
|
||||
// it's idea is mentioned here https://github.com/ssbc/ssb-gossip/#ping-duplex
|
||||
// and implemented by https://github.com/dominictarr/pull-ping/
|
||||
//
|
||||
func Ping(ctx context.Context, req *muxrpc.Request, peerSrc *muxrpc.ByteSource, peerSnk *muxrpc.ByteSink) error {
|
||||
type arg struct {
|
||||
Timeout int
|
||||
// The only argument is the delay between two pings.
|
||||
// the Javascript code calls this "timeout", tho.
|
||||
Delay int `json:"timeout"`
|
||||
}
|
||||
|
||||
var args []arg
|
||||
|
@ -33,6 +33,17 @@ func Ping(ctx context.Context, req *muxrpc.Request, peerSrc *muxrpc.ByteSource,
|
|||
// timeout = time.Minute * time.Duration(args[0].Timeout/(60*1000))
|
||||
// }
|
||||
|
||||
// return sillyPingPong(ctx, peerSrc, peerSnk)
|
||||
return actualPingPong(ctx, peerSrc, peerSnk)
|
||||
}
|
||||
|
||||
// actually just read and write whenever...
|
||||
func sillyPingPong(ctx context.Context, peerSrc *muxrpc.ByteSource, peerSnk *muxrpc.ByteSink) error {
|
||||
var (
|
||||
sendErr = make(chan error)
|
||||
receiveErr = make(chan error)
|
||||
)
|
||||
|
||||
go func() {
|
||||
peerSnk.SetEncoding(muxrpc.TypeJSON)
|
||||
enc := json.NewEncoder(peerSnk)
|
||||
|
@ -40,6 +51,8 @@ func Ping(ctx context.Context, req *muxrpc.Request, peerSrc *muxrpc.ByteSource,
|
|||
tick := time.NewTicker(5 * time.Second)
|
||||
defer tick.Stop()
|
||||
|
||||
defer close(sendErr)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
@ -48,27 +61,40 @@ func Ping(ctx context.Context, req *muxrpc.Request, peerSrc *muxrpc.ByteSource,
|
|||
}
|
||||
|
||||
var pong = encodedTime.Millisecs(time.Now())
|
||||
err = enc.Encode(pong)
|
||||
if err != nil {
|
||||
|
||||
if err := enc.Encode(pong); err != nil {
|
||||
sendErr <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for peerSrc.Next(ctx) {
|
||||
var ping encodedTime.Millisecs
|
||||
err := peerSrc.Reader(func(rd io.Reader) error {
|
||||
return json.NewDecoder(rd).Decode(&ping)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
go func() {
|
||||
defer close(receiveErr)
|
||||
|
||||
for peerSrc.Next(ctx) {
|
||||
var ping encodedTime.Millisecs
|
||||
err := peerSrc.Reader(func(rd io.Reader) error {
|
||||
return json.NewDecoder(rd).Decode(&ping)
|
||||
})
|
||||
if err != nil {
|
||||
receiveErr <- err
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// when := time.Time(ping)
|
||||
// fmt.Printf("got ping: %s - age: %s\n", when.String(), time.Since(when))
|
||||
}
|
||||
return
|
||||
}()
|
||||
|
||||
return nil
|
||||
select {
|
||||
case e := <-sendErr:
|
||||
return e
|
||||
case e := <-receiveErr:
|
||||
return e
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// this is how it should work, i think, but it leads to disconnects...
|
||||
|
@ -87,8 +113,8 @@ func actualPingPong(ctx context.Context, peerSrc *muxrpc.ByteSource, peerSnk *mu
|
|||
return err
|
||||
}
|
||||
|
||||
when := time.Time(ping)
|
||||
fmt.Printf("got ping: %s - age: %s\n", when.String(), time.Since(when))
|
||||
//when := time.Time(ping)
|
||||
//fmt.Printf("got ping: %s - age: %s\n", when.String(), time.Since(when))
|
||||
|
||||
pong := encodedTime.Millisecs(time.Now())
|
||||
err = enc.Encode(pong)
|
||||
|
|
|
@ -8,19 +8,22 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
"go.cryptoscope.co/muxrpc/v2"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/internal/network"
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
)
|
||||
|
||||
type connectArg struct {
|
||||
Portal refs.FeedRef `json:"portal"`
|
||||
Target refs.FeedRef `json:"target"`
|
||||
Portal refs.FeedRef `json:"portal"` // the room server
|
||||
Target refs.FeedRef `json:"target"` // which peer the initiator/caller wants to be tunneld to
|
||||
}
|
||||
|
||||
type connectWithOriginArg struct {
|
||||
connectArg
|
||||
Origin refs.FeedRef `json:"origin"` // this should be clear from the shs session already
|
||||
Origin refs.FeedRef `json:"origin"` // who started the call
|
||||
}
|
||||
|
||||
func (h *Handler) connect(ctx context.Context, req *muxrpc.Request, peerSrc *muxrpc.ByteSource, peerSnk *muxrpc.ByteSink) error {
|
||||
|
@ -36,6 +39,16 @@ func (h *Handler) connect(ctx context.Context, req *muxrpc.Request, peerSrc *mux
|
|||
}
|
||||
arg := args[0]
|
||||
|
||||
if !arg.Portal.Equal(&h.self) {
|
||||
return fmt.Errorf("talking to the wrong room")
|
||||
}
|
||||
|
||||
// who made the call
|
||||
caller, err := network.GetFeedRefFromAddr(req.RemoteAddr())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// see if we have and endpoint for the target
|
||||
|
||||
edp, has := h.state.Has(arg.Target)
|
||||
|
@ -46,20 +59,16 @@ func (h *Handler) connect(ctx context.Context, req *muxrpc.Request, peerSrc *mux
|
|||
// call connect on them
|
||||
var argWorigin connectWithOriginArg
|
||||
argWorigin.connectArg = arg
|
||||
argWorigin.Origin = h.self
|
||||
argWorigin.Origin = *caller
|
||||
|
||||
targetSrc, targetSnk, err := edp.Duplex(ctx, muxrpc.TypeBinary, muxrpc.Method{"tunnel", "connect"}, argWorigin)
|
||||
if err != nil {
|
||||
h.state.Remove(arg.Target)
|
||||
// TODO: the call could fail because of an error with the caller, too.
|
||||
// if we remove the wrong one, tho others might get confused
|
||||
// h.state.Remove(caller)
|
||||
|
||||
return fmt.Errorf("failed to init connect call with target: %w", err)
|
||||
}
|
||||
|
||||
// pipe data
|
||||
// pipe data between caller and target
|
||||
var cpy muxrpcDuplexCopy
|
||||
cpy.logger = kitlog.With(h.logger, "caller", caller.ShortRef(), "target", arg.Target.ShortRef())
|
||||
cpy.ctx, cpy.cancel = context.WithCancel(ctx)
|
||||
|
||||
go cpy.do(targetSnk, peerSrc)
|
||||
|
@ -71,6 +80,8 @@ func (h *Handler) connect(ctx context.Context, req *muxrpc.Request, peerSrc *mux
|
|||
type muxrpcDuplexCopy struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
logger kitlog.Logger
|
||||
}
|
||||
|
||||
func (mdc muxrpcDuplexCopy) do(w *muxrpc.ByteSink, r *muxrpc.ByteSource) {
|
||||
|
@ -80,14 +91,15 @@ func (mdc muxrpcDuplexCopy) do(w *muxrpc.ByteSink, r *muxrpc.ByteSource) {
|
|||
return err
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("read failed:", err)
|
||||
level.Warn(mdc.logger).Log("event", "read failed", "err", err)
|
||||
w.CloseWithError(err)
|
||||
mdc.cancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := r.Err(); err != nil {
|
||||
fmt.Println("src errored:", err)
|
||||
level.Warn(mdc.logger).Log("event", "source errored", "err", err)
|
||||
// TODO: remove reading side from state?!
|
||||
w.CloseWithError(err)
|
||||
mdc.cancel()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
|
@ -60,8 +60,6 @@ func (s *Server) initNetwork() error {
|
|||
ConnTracker: s.networkConnTracker,
|
||||
BefreCryptoWrappers: s.preSecureWrappers,
|
||||
AfterSecureWrappers: s.postSecureWrappers,
|
||||
|
||||
WebsocketAddr: s.wsAddr,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
|
|
@ -32,6 +32,10 @@ const manifest manifestHandler = `
|
|||
|
||||
"whoami":"async",
|
||||
|
||||
"gossip": {
|
||||
"ping": "duplex"
|
||||
},
|
||||
|
||||
"room": {
|
||||
"registerAlias": "async",
|
||||
"revokeAlias": "async",
|
||||
|
|
|
@ -2,6 +2,7 @@ package roomstate
|
|||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -73,6 +74,7 @@ func (rsm roomStateMap) AsList() []string {
|
|||
for m := range rsm {
|
||||
memberList = append(memberList, m)
|
||||
}
|
||||
sort.Strings(memberList)
|
||||
return memberList
|
||||
}
|
||||
|
||||
|
@ -108,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()
|
||||
|
||||
|
|
Loading…
Reference in New Issue