diff --git a/Makefile b/Makefile index 0f960bc2a..567088d2e 100644 --- a/Makefile +++ b/Makefile @@ -6,15 +6,18 @@ else GOBIN=$(shell go env GOBIN) endif -server: +server: generate go build -i -o livekit-server +generate: wire + go generate + GO_TARGET=proto -proto: protoc goproto +proto: protoc protoc-gen-go twirp-gen @{ \ - protoc --go_out=$(GO_TARGET) --go-grpc_out=$(GO_TARGET) \ + protoc --go_out=$(GO_TARGET) --twirp_out=$(GO_TARGET) \ --go_opt=paths=source_relative \ - --go-grpc_opt=paths=source_relative \ + --twirp_opt=paths=source_relative \ --plugin=$(PROTOC_GEN_GO) \ -I=proto \ proto/*.proto ;\ @@ -25,13 +28,29 @@ ifeq (, $(shell which protoc)) echo "protoc is required, and is not installed" endif -goproto: +protoc-gen-go: ifeq (, $(shell which protoc-gen-go-grpc)) @{ \ echo "installing go protobuf plugin" ;\ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc ;\ } -PROTOC_GEN_GO=$(GOBIN)/protoc-gen-go-grpc -else -PROTOC_GEN_GO=$(shell which protoc-gen-go-grpc) +endif + +twirp-gen: +ifeq (, $(shell which protoc-gen-twirp)) + @{ \ + echo "installing twirp protobuf plugin" ;\ + go install github.com/twitchtv/twirp/protoc-gen-twirp ;\ + } +endif + +wire: +ifeq (, $(shell which wire)) + @{ \ + echo "installing wire" ;\ + go install github.com/google/wire/cmd/wire ;\ + } +WIRE=$(GOBIN)/wire +else +WIRE=$(shell which wire) endif diff --git a/clients.go b/clients.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/clients.go @@ -0,0 +1 @@ +package main diff --git a/go.mod b/go.mod index df42dcb85..c06b4f4fa 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,21 @@ go 1.15 require ( github.com/golang/protobuf v1.4.2 github.com/google/uuid v1.1.2 + github.com/google/wire v0.4.0 github.com/pion/stun v0.3.5 - github.com/pion/webrtc/v3 v3.0.0-beta.6 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/stretchr/testify v1.6.1 // indirect + github.com/twitchtv/twirp v7.1.0+incompatible + github.com/urfave/cli/v2 v2.2.0 + github.com/urfave/negroni v1.0.0 + go.uber.org/zap v1.16.0 golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect golang.org/x/sys v0.0.0-20201004230629-f6757f270073 // indirect golang.org/x/text v0.3.3 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect google.golang.org/grpc v1.32.0 - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0 // indirect google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index 93f69fd0c..7bbc0c9e2 100644 --- a/go.sum +++ b/go.sum @@ -1,48 +1,21 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -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/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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= @@ -53,251 +26,115 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/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.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -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= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE= +github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucas-clemente/quic-go v0.18.0 h1:JhQDdqxdwdmGdKsKgXi1+coHRoGhvU6z0rNzOJqZ/4o= -github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.0 h1:i/YPXVxz8q9umso/5y474CNcHmTpA+5DH+mFPjx6PZg= -github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -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.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -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/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0= -github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg= -github.com/pion/dtls/v2 v2.0.3 h1:3qQ0s4+TXD00rsllL8g8KQcxAs+Y/Z6oz618RXX6p14= -github.com/pion/dtls/v2 v2.0.3/go.mod h1:TUjyL8bf8LH95h81Xj7kATmzMRt29F/4lxpIPj2Xe4Y= -github.com/pion/ice/v2 v2.0.7 h1:3Iv+EfthCdPbJCsPbhnL4xRzolR1oh9mJTuUsxyxPJI= -github.com/pion/ice/v2 v2.0.7/go.mod h1:0C4VBZxkO3A7Qj5ZEgZut8OptbJFSbuGNyPyJMftuHA= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= -github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY= -github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0= -github.com/pion/quic v0.1.4 h1:bNz9sCJjlM3GqMdq7Fne57FiWfdyiJ++yHVbuqeoD3Y= -github.com/pion/quic v0.1.4/go.mod h1:dBhNvkLoQqRwfi6h3Vqj3IcPLgiW7rkZxBbRdp7Vzvk= -github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= -github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/rtcp v1.2.4 h1:NT3H5LkUGgaEapvp0HGik+a+CpflRF7KTD7H+o7OWIM= -github.com/pion/rtcp v1.2.4/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= -github.com/pion/rtp v1.6.1 h1:2Y2elcVBrahYnHKN2X7rMHX/r1R4TEBMP1LaVu/wNhk= -github.com/pion/rtp v1.6.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= -github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= -github.com/pion/sctp v1.7.11 h1:UCnj7MsobLKLuP/Hh+JMiI/6W5Bs/VF45lWKgHFjSIE= -github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= -github.com/pion/sdp/v3 v3.0.2 h1:UNnSPVaMM+Pdu/mR9UvAyyo6zkdYbKeuOooCwZvTl/g= -github.com/pion/sdp/v3 v3.0.2/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk= -github.com/pion/srtp v1.5.2 h1:25DmvH+fqKZDqvX64vTwnycVwL9ooJxHF/gkX16bDBY= -github.com/pion/srtp v1.5.2/go.mod h1:NiBff/MSxUwMUwx/fRNyD/xGE+dVvf8BOCeXhjCXZ9U= github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg= github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= -github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8= -github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= -github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM= -github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A= -github.com/pion/turn/v2 v2.0.4 h1:oDguhEv2L/4rxwbL9clGLgtzQPjtuZwCdoM7Te8vQVk= -github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog= -github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI= -github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths= -github.com/pion/webrtc v1.2.0 h1:3LGGPQEMacwG2hcDfhdvwQPz315gvjZXOfY4vaF4+I4= -github.com/pion/webrtc/v3 v3.0.0-beta.6 h1:clI3mrXyk0ZC5WmdDLEQZOJbvqdk2za5l3mAm7WAVeQ= -github.com/pion/webrtc/v3 v3.0.0-beta.6/go.mod h1:g2FHuaaKKuoZ9RlKIDAYvnO/rhocYG/d+2Ly1hRa85o= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/twitchtv/twirp v7.1.0+incompatible h1:3fNSDoSPyq+fTrifIvGue9XM/tptzuhiGY83rxPVNUg= +github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= +github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= +github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 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-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw= golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201004230629-f6757f270073 h1:h6POl/ao7bJKsyalYGo1qALE/khxX70Enx+E/Skq+XE= golang.org/x/sys v0.0.0-20201004230629-f6757f270073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0 h1:lQ+dE99pFsb8osbJB3oRfE5eW4Hx6a/lZQr8Jh+eoT4= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -312,21 +149,15 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/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= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/main.go b/main.go index 1a38e886d..104d3fec5 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,168 @@ package main import ( + "context" + "errors" "fmt" + "net" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" - "github.com/livekit/livekit-server/pkg/node" + "github.com/urfave/cli/v2" + "github.com/urfave/negroni" + + "github.com/livekit/livekit-server/pkg/config" + "github.com/livekit/livekit-server/pkg/logger" + "github.com/livekit/livekit-server/pkg/service" + "github.com/livekit/livekit-server/proto" ) func main() { - n, err := node.NewLocalNode() - if err != nil { - panic(err) + app := &cli.App{ + Name: "livekit-server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + EnvVars: []string{"LIVEKIT_CONFIG"}, + }, + &cli.BoolFlag{ + Name: "dev", + }, + }, + Action: startServer, } - if err := n.DiscoverNetworkInfo(); err != nil { - panic(err) + if err := app.Run(os.Args); err != nil { + logger.GetLogger().Fatal(err) } - - fmt.Println("my IP", n.Ip) +} + +func startServer(c *cli.Context) error { + conf, err := config.NewConfig(c.String("config")) + if err != nil { + return err + } + + conf.UpdateFromCLI(c) + + if conf.Development { + logger.InitDevelopment() + } else { + logger.InitProduction() + } + + server, err := InitializeServer(conf) + if err != nil { + return err + } + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + + go func() { + sig := <-sigChan + logger.GetLogger().Infow("exit requested, shutting down", "signal", sig) + server.Stop() + }() + + return server.Start() +} + +type LivekitServer struct { + config *config.Config + roomServer proto.TwirpServer + rtcServer proto.TwirpServer + roomHttp *http.Server + rtcHttp *http.Server + running bool + doneChan chan bool +} + +func NewLivekitServer(conf *config.Config, + roomService proto.RoomService, + rtcService *service.RTCService) (s *LivekitServer, err error) { + s = &LivekitServer{ + config: conf, + roomServer: proto.NewRoomServiceServer(roomService), + rtcServer: proto.NewRTCServiceServer(rtcService), + } + + roomHandler := configureMiddlewares(conf, s.roomServer) + s.roomHttp = &http.Server{ + Addr: fmt.Sprintf(":%d", conf.APIPort), + Handler: roomHandler, + } + + rtcMux := http.NewServeMux() + rtcMux.Handle(proto.RTCServicePathPrefix, s.rtcServer) + rtcMux.HandleFunc("/rtc/Signal", rtcService.Signal) + rtcHandler := configureMiddlewares(conf, rtcMux) + s.rtcHttp = &http.Server{ + Addr: fmt.Sprintf(":%d", conf.RTCPort), + Handler: rtcHandler, + } + + return +} + +func (s *LivekitServer) Start() error { + if s.running { + return errors.New("already running") + } + s.running = true + s.doneChan = make(chan bool, 1) + + // ensure we could listen + roomLn, err := net.Listen("tcp", s.roomHttp.Addr) + if err != nil { + return err + } + rtcLn, err := net.Listen("tcp", s.rtcHttp.Addr) + if err != nil { + return err + } + + go func() { + logger.GetLogger().Infow("starting Room service", "address", s.roomHttp.Addr) + s.roomHttp.Serve(roomLn) + }() + go func() { + logger.GetLogger().Infow("starting RTC service", "address", s.rtcHttp.Addr) + s.rtcHttp.Serve(rtcLn) + }() + + <-s.doneChan + + // wait for shutdown + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + wg := sync.WaitGroup{} + wg.Add(2) + go func() { + defer wg.Done() + s.rtcHttp.Shutdown(ctx) + }() + go func() { + defer wg.Done() + s.roomHttp.Shutdown(ctx) + }() + wg.Wait() + + return nil +} + +func (s *LivekitServer) Stop() { + s.running = false + + s.doneChan <- true +} + +func configureMiddlewares(conf *config.Config, handler http.Handler) *negroni.Negroni { + n := negroni.New() + n.Use(negroni.NewRecovery()) + n.UseHandler(handler) + return n } diff --git a/pkg/config/config.go b/pkg/config/config.go index 71f40c0b4..b8e1ef7d5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,5 +1,37 @@ package config -type Config struct { +import ( + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v2" +) +type Config struct { + APIPort uint32 `yaml:"api_port"` + RTCPort uint32 `yaml:"rtc_port"` + UDPRangeStart uint32 `yaml:"udp_range_start"` + UDPRangeEnd uint32 `yaml:"udp_range_end"` + + // multi-node configuration, + MultiNode bool `yaml:"multi_node"` + Development bool `yaml:"development"` +} + +func NewConfig(confString string) (*Config, error) { + // start with defaults + conf := &Config{ + APIPort: 7880, + RTCPort: 7881, + UDPRangeStart: 10000, + UDPRangeEnd: 11000, + } + if confString != "" { + yaml.Unmarshal([]byte(confString), conf) + } + return conf, nil +} + +func (conf *Config) UpdateFromCLI(c *cli.Context) { + if c.IsSet("dev") { + conf.Development = c.Bool("dev") + } } diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 000000000..b4a0d74d1 --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,24 @@ +package logger + +import ( + "go.uber.org/zap" +) + +var logger *zap.SugaredLogger + +func GetLogger() *zap.SugaredLogger { + if logger == nil { + InitDevelopment() + } + return logger +} + +func InitProduction() { + l, _ := zap.NewProduction() + logger = l.Sugar() +} + +func InitDevelopment() { + l, _ := zap.NewDevelopment() + logger = l.Sugar() +} diff --git a/pkg/node/chooser.go b/pkg/node/chooser.go deleted file mode 100644 index 0667c5dcb..000000000 --- a/pkg/node/chooser.go +++ /dev/null @@ -1,43 +0,0 @@ -package node - -import ( - "fmt" - - "github.com/livekit/livekit-server/proto" -) - -// Router selects the best node for room interactions -type Chooser interface { - ChooseNodeForRoom(roomId string) (*proto.Node, error) - GetNodeForRoom(roomId string) (*proto.Node, error) - ClearRoom(roomId string) error -} - -func NewSingleNodeChooser(n *Node) *SingleNodeChooser { - return &SingleNodeChooser{ - localNode: n, - rooms: make(map[string]bool), - } -} - -type SingleNodeChooser struct { - localNode *Node - rooms map[string]bool -} - -func (c *SingleNodeChooser) ChooseNodeForRoom(roomId string) (n *proto.Node, err error) { - if c.rooms[roomId] { - err = fmt.Errorf("Room already exists") - return - } - c.rooms[roomId] = true - n = &c.localNode.Node - return -} - -func (c *SingleNodeChooser) GetNodeForRoom(roomId string) (*proto.Node, error) { - if !c.rooms[roomId] { - return nil, fmt.Errorf("room %d had not been created", roomId) - } - return &c.localNode.Node, nil -} diff --git a/pkg/node/node.go b/pkg/node/node.go index 786017695..3b0ff42a0 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -2,6 +2,7 @@ package node import ( "github.com/google/uuid" + "github.com/google/wire" "github.com/pion/stun" "github.com/livekit/livekit-server/proto" @@ -11,16 +12,18 @@ const ( googleStunServer = "stun.l.google.com:19302" ) +var NodeSet = wire.NewSet(NewLocalNode) + type Node struct { proto.Node } type NodeStats struct { - NumRooms int32 - NumClients int32 + NumRooms int32 + NumClients int32 NumVideoChannels int32 NumAudioChannels int32 - BytesPerMin int64 + BytesPerMin int64 } func NewLocalNode() (*Node, error) { @@ -28,11 +31,15 @@ func NewLocalNode() (*Node, error) { if err != nil { return nil, err } - return &Node{ + n := &Node{ proto.Node{ Id: id.String(), }, - }, nil + } + if err = n.DiscoverNetworkInfo(); err != nil { + return nil, err + } + return n, nil } func (n *Node) DiscoverNetworkInfo() error { @@ -65,4 +72,4 @@ func (n *Node) DiscoverNetworkInfo() error { err = stunErr } return err -} \ No newline at end of file +} diff --git a/pkg/service/rtc.go b/pkg/service/rtc.go new file mode 100644 index 000000000..9450ee269 --- /dev/null +++ b/pkg/service/rtc.go @@ -0,0 +1,27 @@ +package service + +import ( + "context" + "net/http" + + "github.com/livekit/livekit-server/proto" +) + +type RTCService struct { +} + +func (s *RTCService) Offer(ctx context.Context, offer *proto.SessionDescription) (answer *proto.SessionDescription, err error) { + return +} + +func (s *RTCService) Trickle(ctx context.Context, req *proto.TrickleRequest) (res *proto.TrickleResponse, err error) { + return +} + +func (s *RTCService) Signal(w http.ResponseWriter, r *http.Request) { + +} + +func NewRTCService() *RTCService { + return &RTCService{} +} diff --git a/pkg/service/service.go b/pkg/service/service.go new file mode 100644 index 000000000..081a3185b --- /dev/null +++ b/pkg/service/service.go @@ -0,0 +1,24 @@ +package service + +import ( + "fmt" + + "github.com/google/wire" + + "github.com/livekit/livekit-server/pkg/config" + "github.com/livekit/livekit-server/pkg/node" + "github.com/livekit/livekit-server/proto" +) + +var ServiceSet = wire.NewSet( + NewRoomService, + NewRTCService, +) + +func NewRoomService(conf *config.Config, localNode *node.Node) (proto.RoomService, error) { + if conf.MultiNode { + return nil, fmt.Errorf("multinode is not supported") + } else { + return NewSimpleRoomService(localNode) + } +} diff --git a/pkg/service/simpleroom.go b/pkg/service/simpleroom.go index e076756c7..fd7646fa7 100644 --- a/pkg/service/simpleroom.go +++ b/pkg/service/simpleroom.go @@ -2,6 +2,10 @@ package service import ( "context" + "sync" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/livekit/livekit-server/pkg/node" "github.com/livekit/livekit-server/proto" @@ -11,26 +15,53 @@ import ( type SimpleRoomService struct { localNode *node.Node rooms map[string]bool + roomLock sync.Mutex } -func NewRoomService(localNode *node.Node) (svc *SimpleRoomService, err error) { +func NewSimpleRoomService(localNode *node.Node) (svc *SimpleRoomService, err error) { svc = &SimpleRoomService{ localNode: localNode, rooms: make(map[string]bool), + roomLock: sync.Mutex{}, } return } func (s *SimpleRoomService) CreateRoom(ctx context.Context, req *proto.CreateRoomRequest) (res *proto.CreateRoomResponse, err error) { + s.roomLock.Lock() + defer s.roomLock.Unlock() + if s.rooms[req.Room] { + err = status.Errorf(codes.AlreadyExists, "room %s already exists", req.Room) + return + } + + s.rooms[req.Room] = true + + res = &proto.CreateRoomResponse{ + Room: req.Room, + NodeIp: s.localNode.Ip, + NodeRtcPort: s.localNode.RtcPort, + } return } func (s *SimpleRoomService) JoinRoom(ctx context.Context, req *proto.JoinRoomRequest) (res *proto.JoinRoomResponse, err error) { + if !s.rooms[req.Room] { + err = status.Errorf(codes.NotFound, "room %s does not exist", req.Room) + return + } + + res = &proto.JoinRoomResponse{ + NodeIp: s.localNode.Ip, + NodeRtcPort: s.localNode.RtcPort, + } return } func (s *SimpleRoomService) DeleteRoom(ctx context.Context, req *proto.DeleteRoomRequest) (res *proto.DeleteRoomResponse, err error) { + delete(s.rooms, req.Room) + res = &proto.DeleteRoomResponse{} return } diff --git a/proto/model.pb.go b/proto/model.pb.go index 1d4ee8a03..407ef19fd 100644 --- a/proto/model.pb.go +++ b/proto/model.pb.go @@ -23,7 +23,7 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type Node struct { Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Ip string `protobuf:"bytes,2,opt,name=ip,proto3" json:"ip,omitempty"` - Port int32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` + RtcPort uint32 `protobuf:"varint,3,opt,name=rtc_port,json=rtcPort,proto3" json:"rtc_port,omitempty"` Stats *NodeStats `protobuf:"bytes,4,opt,name=stats,proto3" json:"stats,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -69,9 +69,9 @@ func (m *Node) GetIp() string { return "" } -func (m *Node) GetPort() int32 { +func (m *Node) GetRtcPort() uint32 { if m != nil { - return m.Port + return m.RtcPort } return 0 } @@ -138,18 +138,19 @@ func init() { func init() { proto.RegisterFile("model.proto", fileDescriptor_4c16552f9fdb66d8) } var fileDescriptor_4c16552f9fdb66d8 = []byte{ - // 206 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x4f, 0xbb, 0x4e, 0xc4, 0x30, - 0x10, 0x54, 0x42, 0x0c, 0x64, 0x23, 0x51, 0x6c, 0x65, 0x89, 0x82, 0xe8, 0x1a, 0x4c, 0x81, 0x4f, - 0x82, 0x3f, 0x80, 0x8a, 0x86, 0xc2, 0x74, 0x34, 0x88, 0x3b, 0x5b, 0x60, 0x11, 0x7b, 0x2d, 0x3f, - 0xee, 0xfb, 0x91, 0x9d, 0x40, 0x35, 0x3b, 0x33, 0xab, 0x99, 0x5d, 0x98, 0x1c, 0x69, 0xb3, 0xc8, - 0x10, 0x29, 0x13, 0x5e, 0x2c, 0xf6, 0x64, 0x7e, 0x6c, 0xde, 0x69, 0x18, 0x5e, 0x49, 0x1b, 0xbc, - 0x82, 0xde, 0x6a, 0xde, 0xcd, 0x9d, 0x18, 0x55, 0x6f, 0x75, 0xe3, 0x81, 0xf7, 0x1b, 0x0f, 0x88, - 0x30, 0x04, 0x8a, 0x99, 0x9f, 0xcd, 0x9d, 0x60, 0xaa, 0xcd, 0x28, 0x80, 0xa5, 0xfc, 0x99, 0x13, - 0x1f, 0xe6, 0x4e, 0x4c, 0x0f, 0x28, 0xb7, 0x50, 0x59, 0x13, 0xdf, 0xaa, 0xa3, 0xd6, 0x85, 0xdd, - 0x0b, 0x8c, 0xff, 0x1a, 0x5e, 0xc3, 0xe8, 0x8b, 0xfb, 0x88, 0x44, 0x2e, 0xb5, 0x46, 0xa6, 0x2e, - 0x7d, 0x71, 0xaa, 0x72, 0xbc, 0x81, 0xa9, 0x9a, 0xc7, 0xc5, 0x1a, 0x9f, 0x53, 0x3b, 0x80, 0x29, - 0xf0, 0xc5, 0x3d, 0xaf, 0xca, 0xd3, 0xdd, 0xfb, 0xed, 0x97, 0xcd, 0xdf, 0xe5, 0x20, 0x8f, 0xe4, - 0xf6, 0x5b, 0xe3, 0x1f, 0xde, 0x27, 0x13, 0x4f, 0x26, 0xee, 0xdb, 0x93, 0x87, 0xf3, 0x06, 0x8f, - 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3b, 0x8d, 0x0e, 0xc7, 0xfa, 0x00, 0x00, 0x00, + // 216 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x8f, 0x31, 0x4f, 0xc3, 0x30, + 0x10, 0x85, 0x95, 0xd0, 0xd0, 0xe6, 0x22, 0x18, 0x3c, 0x05, 0x31, 0x10, 0x75, 0x21, 0x0c, 0xa4, + 0x12, 0xfc, 0x03, 0x98, 0x58, 0x10, 0x32, 0x1b, 0x4b, 0x45, 0x63, 0x0b, 0xac, 0xc6, 0x3e, 0xeb, + 0x7c, 0xee, 0xef, 0x47, 0x76, 0x42, 0xa7, 0xa7, 0xef, 0x3d, 0xeb, 0x3d, 0x1f, 0x34, 0x16, 0x95, + 0x9e, 0x06, 0x4f, 0xc8, 0x28, 0xd6, 0x93, 0x39, 0xe9, 0xa3, 0xe1, 0xed, 0x11, 0x56, 0xef, 0xa8, + 0xb4, 0xb8, 0x86, 0xd2, 0xa8, 0xb6, 0xe8, 0x8a, 0xbe, 0x96, 0xa5, 0x51, 0x99, 0x7d, 0x5b, 0x2e, + 0xec, 0xc5, 0x0d, 0x6c, 0x88, 0xc7, 0xbd, 0x47, 0xe2, 0xf6, 0xa2, 0x2b, 0xfa, 0x2b, 0xb9, 0x26, + 0x1e, 0x3f, 0x90, 0x58, 0xf4, 0x50, 0x05, 0xfe, 0xe6, 0xd0, 0xae, 0xba, 0xa2, 0x6f, 0x9e, 0xc4, + 0xb0, 0x74, 0x0f, 0xa9, 0xf8, 0x33, 0x25, 0x72, 0x7e, 0xb0, 0x7d, 0x83, 0xfa, 0xec, 0x89, 0x5b, + 0xa8, 0x5d, 0xb4, 0x7b, 0x42, 0xb4, 0x21, 0x0f, 0x57, 0x72, 0xe3, 0xa2, 0x95, 0x89, 0xc5, 0x1d, + 0x34, 0x29, 0x1c, 0x27, 0xa3, 0x1d, 0x87, 0xfc, 0x8f, 0x4a, 0x82, 0x8b, 0xf6, 0x75, 0x76, 0x5e, + 0x1e, 0xbe, 0xee, 0x7f, 0x0c, 0xff, 0xc6, 0xc3, 0x30, 0xa2, 0xdd, 0x2d, 0x8b, 0xff, 0xfa, 0x18, + 0x34, 0x9d, 0x34, 0xed, 0xf2, 0xad, 0x87, 0xcb, 0x2c, 0xcf, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, + 0x52, 0x03, 0x38, 0xe7, 0x01, 0x01, 0x00, 0x00, } diff --git a/proto/model.proto b/proto/model.proto index c45711bf2..924fab0be 100644 --- a/proto/model.proto +++ b/proto/model.proto @@ -1,14 +1,14 @@ syntax = "proto3"; package livekit; -option go_package = "github.com/livekit/livekit-server/proto"; +option go_package = "livekit"; // internal types, declaring in proto to get serialization for free message Node { string id = 1; string ip = 2; - int32 port = 3; + uint32 rtc_port = 3; NodeStats stats = 4; } diff --git a/proto/service.pb.go b/proto/service.pb.go index 382efe427..d9157eb3d 100644 --- a/proto/service.pb.go +++ b/proto/service.pb.go @@ -62,7 +62,7 @@ func (m *CreateRoomRequest) GetRoom() string { type CreateRoomResponse struct { Room string `protobuf:"bytes,1,opt,name=room,proto3" json:"room,omitempty"` NodeIp string `protobuf:"bytes,2,opt,name=node_ip,json=nodeIp,proto3" json:"node_ip,omitempty"` - NodePort string `protobuf:"bytes,3,opt,name=node_port,json=nodePort,proto3" json:"node_port,omitempty"` + NodeRtcPort uint32 `protobuf:"varint,3,opt,name=node_rtc_port,json=nodeRtcPort,proto3" json:"node_rtc_port,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -107,11 +107,11 @@ func (m *CreateRoomResponse) GetNodeIp() string { return "" } -func (m *CreateRoomResponse) GetNodePort() string { +func (m *CreateRoomResponse) GetNodeRtcPort() uint32 { if m != nil { - return m.NodePort + return m.NodeRtcPort } - return "" + return 0 } type JoinRoomRequest struct { @@ -163,7 +163,7 @@ func (m *JoinRoomRequest) GetClientId() string { type JoinRoomResponse struct { NodeIp string `protobuf:"bytes,1,opt,name=node_ip,json=nodeIp,proto3" json:"node_ip,omitempty"` - NodePort string `protobuf:"bytes,2,opt,name=node_port,json=nodePort,proto3" json:"node_port,omitempty"` + NodeRtcPort uint32 `protobuf:"varint,2,opt,name=node_rtc_port,json=nodeRtcPort,proto3" json:"node_rtc_port,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -201,11 +201,11 @@ func (m *JoinRoomResponse) GetNodeIp() string { return "" } -func (m *JoinRoomResponse) GetNodePort() string { +func (m *JoinRoomResponse) GetNodeRtcPort() uint32 { if m != nil { - return m.NodePort + return m.NodeRtcPort } - return "" + return 0 } type DeleteRoomRequest struct { @@ -523,36 +523,35 @@ func init() { func init() { proto.RegisterFile("service.proto", fileDescriptor_a0b84a42fa06f626) } var fileDescriptor_a0b84a42fa06f626 = []byte{ - // 487 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0x4d, 0x6f, 0xd3, 0x40, - 0x10, 0xad, 0xdb, 0x12, 0x27, 0x13, 0xda, 0x34, 0x23, 0x44, 0x8c, 0xc3, 0x01, 0xf9, 0x12, 0x38, - 0xe0, 0x42, 0x7a, 0x03, 0x24, 0x44, 0xda, 0x4a, 0x29, 0x17, 0x90, 0xd3, 0x13, 0x07, 0x2a, 0xd7, - 0x9e, 0x96, 0x55, 0x1d, 0xaf, 0xd9, 0xdd, 0x56, 0xea, 0x99, 0x7f, 0xc7, 0x6f, 0xe0, 0xc7, 0x20, - 0xaf, 0xd7, 0x1f, 0xad, 0xd3, 0xe6, 0xe4, 0xf5, 0x9b, 0x37, 0xb3, 0x6f, 0x66, 0x9e, 0x16, 0x76, - 0x24, 0x89, 0x1b, 0x16, 0x91, 0x9f, 0x09, 0xae, 0x38, 0xda, 0x09, 0xbb, 0xa1, 0x2b, 0xa6, 0xbc, - 0x09, 0x0c, 0x0f, 0x05, 0x85, 0x8a, 0x02, 0xce, 0x97, 0x01, 0xfd, 0xbe, 0x26, 0xa9, 0x10, 0x61, - 0x5b, 0x70, 0xbe, 0x74, 0xac, 0x57, 0xd6, 0xeb, 0x5e, 0xa0, 0xcf, 0xde, 0x4f, 0xc0, 0x26, 0x51, - 0x66, 0x3c, 0x95, 0xb4, 0x8a, 0x89, 0x23, 0xb0, 0x53, 0x1e, 0xd3, 0x19, 0xcb, 0x9c, 0x4d, 0x0d, - 0x77, 0xf2, 0xdf, 0x93, 0x0c, 0xc7, 0xd0, 0xd3, 0x81, 0x8c, 0x0b, 0xe5, 0x6c, 0xe9, 0x50, 0x37, - 0x07, 0xbe, 0x73, 0xa1, 0xbc, 0x19, 0x0c, 0xbe, 0x72, 0x96, 0xae, 0x91, 0x91, 0xd7, 0x88, 0x12, - 0x46, 0xa9, 0x3a, 0x63, 0xb1, 0x29, 0xdf, 0x2d, 0x80, 0x93, 0xd8, 0x9b, 0xc3, 0x5e, 0x5d, 0xc3, - 0x28, 0x6c, 0xa8, 0xb1, 0x1e, 0x56, 0xb3, 0x79, 0x4f, 0xcd, 0x04, 0x86, 0x47, 0x94, 0xd0, 0xfa, - 0xb1, 0x3c, 0x03, 0x6c, 0x12, 0x8b, 0x4b, 0xbd, 0x01, 0xec, 0x2c, 0xd8, 0x65, 0x1a, 0x26, 0x26, - 0xd5, 0xfb, 0x63, 0xc1, 0x6e, 0x89, 0x18, 0x61, 0xef, 0x61, 0x3b, 0x26, 0x19, 0xe9, 0x6a, 0xfd, - 0xe9, 0xd8, 0x37, 0x1b, 0xf1, 0x17, 0x24, 0x25, 0xe3, 0xe9, 0x11, 0xc9, 0x48, 0xb0, 0x4c, 0x31, - 0x9e, 0xce, 0x37, 0x02, 0x4d, 0xc5, 0x03, 0xb0, 0x95, 0x60, 0xd1, 0x55, 0x42, 0x5a, 0x70, 0x7f, - 0x3a, 0xaa, 0xb2, 0x4e, 0x0b, 0xdc, 0xdc, 0x37, 0xdf, 0x08, 0x4a, 0xe6, 0xac, 0x07, 0x76, 0x16, - 0xde, 0x26, 0x3c, 0x8c, 0x3d, 0x1f, 0x76, 0xef, 0xf2, 0xf0, 0x25, 0xf4, 0xa2, 0x30, 0x8d, 0x59, - 0x1c, 0x2a, 0x32, 0x7d, 0xd5, 0x80, 0x37, 0x84, 0x41, 0xc5, 0x37, 0x9d, 0x7d, 0x00, 0x6c, 0x0b, - 0xcc, 0x27, 0xa3, 0x6e, 0xb3, 0xb2, 0x82, 0x3e, 0xe3, 0x1e, 0x6c, 0xc9, 0xb8, 0xb0, 0xc0, 0xd3, - 0x20, 0x3f, 0x4e, 0xff, 0x59, 0xd0, 0xcf, 0xc7, 0xb4, 0x28, 0xac, 0x88, 0xc7, 0x00, 0xb5, 0xa5, - 0xd0, 0xad, 0x7a, 0x69, 0x19, 0xd2, 0x1d, 0xaf, 0x8c, 0x99, 0x41, 0x7e, 0x86, 0x6e, 0xb9, 0x75, - 0x74, 0x2a, 0xe2, 0x3d, 0x33, 0xb9, 0x2f, 0x56, 0x44, 0x4c, 0x81, 0x63, 0x80, 0x7a, 0x87, 0x0d, - 0x1d, 0x2d, 0x07, 0x34, 0x74, 0xb4, 0x97, 0x3e, 0xfd, 0x6b, 0x01, 0x04, 0xa7, 0x87, 0x65, 0x77, - 0x5f, 0xe0, 0xc9, 0xb7, 0x8b, 0x0b, 0x12, 0xf8, 0xd8, 0x6a, 0xdd, 0xc7, 0x82, 0xf8, 0x11, 0x3a, - 0x85, 0x69, 0xf0, 0x79, 0x4d, 0x6b, 0xfa, 0xca, 0x1d, 0xb5, 0xf0, 0x42, 0xcc, 0x3b, 0x0b, 0x3f, - 0x81, 0x6d, 0x96, 0x87, 0x0f, 0xd9, 0xc4, 0x75, 0xda, 0x81, 0x22, 0x7f, 0xf6, 0xe6, 0xc7, 0xe4, - 0x92, 0xa9, 0x5f, 0xd7, 0xe7, 0x7e, 0xc4, 0x97, 0xfb, 0x86, 0x55, 0x7e, 0xdf, 0xe6, 0x8f, 0x09, - 0x89, 0x7d, 0xfd, 0x96, 0x9c, 0x77, 0xf4, 0xe7, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xed, - 0x7b, 0x97, 0xdb, 0x63, 0x04, 0x00, 0x00, + // 475 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xc1, 0x4e, 0xdb, 0x40, + 0x10, 0xc5, 0x40, 0x31, 0x99, 0x34, 0x84, 0x8c, 0x2a, 0xe1, 0x3a, 0x3d, 0x20, 0x5f, 0x42, 0x0f, + 0x35, 0x6a, 0xb8, 0x55, 0x95, 0xaa, 0x06, 0x90, 0xa0, 0x17, 0xaa, 0x0d, 0xa7, 0x5e, 0x22, 0xb3, + 0x1e, 0xe8, 0x0a, 0xc7, 0xeb, 0xee, 0x2e, 0x48, 0x9c, 0x7b, 0xee, 0x1f, 0xf6, 0x63, 0x2a, 0xaf, + 0xd7, 0x89, 0x5b, 0xa7, 0xe1, 0xe4, 0xf5, 0xcc, 0xdb, 0x99, 0x37, 0xf3, 0x9e, 0x16, 0x7a, 0x9a, + 0xd4, 0xa3, 0xe0, 0x14, 0x17, 0x4a, 0x1a, 0x89, 0x7e, 0x26, 0x1e, 0xe9, 0x5e, 0x98, 0x68, 0x04, + 0x83, 0x53, 0x45, 0x89, 0x21, 0x26, 0xe5, 0x9c, 0xd1, 0x8f, 0x07, 0xd2, 0x06, 0x11, 0xb6, 0x95, + 0x94, 0xf3, 0xc0, 0x3b, 0xf4, 0x8e, 0x3a, 0xcc, 0x9e, 0x23, 0x02, 0x6c, 0x02, 0x75, 0x21, 0x73, + 0x4d, 0xab, 0x90, 0x78, 0x00, 0x7e, 0x2e, 0x53, 0x9a, 0x89, 0x22, 0xd8, 0xb4, 0xe1, 0x9d, 0xf2, + 0xf7, 0xb2, 0xc0, 0x08, 0x7a, 0x36, 0xa1, 0x0c, 0x9f, 0x15, 0x52, 0x99, 0x60, 0xeb, 0xd0, 0x3b, + 0xea, 0xb1, 0x6e, 0x19, 0x64, 0x86, 0x7f, 0x95, 0xca, 0x44, 0x13, 0xe8, 0x7f, 0x91, 0x22, 0x7f, + 0x86, 0x0d, 0x0e, 0xa1, 0xc3, 0x33, 0x41, 0xb9, 0x99, 0x89, 0xd4, 0x75, 0xd9, 0xad, 0x02, 0x97, + 0x69, 0x74, 0x05, 0xfb, 0xcb, 0x1a, 0x8e, 0x68, 0x83, 0x94, 0xb7, 0x9e, 0xd4, 0x66, 0x9b, 0xd4, + 0x08, 0x06, 0x67, 0x94, 0xd1, 0xf3, 0x4b, 0x7a, 0x05, 0xd8, 0x04, 0x56, 0xbd, 0xa3, 0x3e, 0xf4, + 0xa6, 0xe2, 0x2e, 0x4f, 0x32, 0x77, 0x35, 0xfa, 0xe9, 0xc1, 0x5e, 0x1d, 0x71, 0xfc, 0xde, 0xc3, + 0x76, 0x4a, 0x9a, 0xdb, 0x6a, 0xdd, 0xf1, 0x30, 0x76, 0xfa, 0xc4, 0x53, 0xd2, 0x5a, 0xc8, 0xfc, + 0x8c, 0x34, 0x57, 0xa2, 0x30, 0x42, 0xe6, 0x17, 0x1b, 0xcc, 0x42, 0xf1, 0x04, 0x7c, 0xa3, 0x04, + 0xbf, 0xcf, 0xc8, 0x72, 0xee, 0x8e, 0x0f, 0x16, 0xb7, 0xae, 0xab, 0xb8, 0xeb, 0x77, 0xb1, 0xc1, + 0x6a, 0xe4, 0xa4, 0x03, 0x7e, 0x91, 0x3c, 0x65, 0x32, 0x49, 0xa3, 0x18, 0xf6, 0xfe, 0xc6, 0xe1, + 0x1b, 0xe8, 0xf0, 0x24, 0x4f, 0x45, 0x9a, 0x18, 0x72, 0x73, 0x2d, 0x03, 0xd1, 0x00, 0xfa, 0x0b, + 0xbc, 0x9b, 0xec, 0x03, 0x60, 0x9b, 0x60, 0xb9, 0x19, 0xf3, 0x54, 0xd4, 0x15, 0xec, 0x19, 0xf7, + 0x61, 0x4b, 0xa7, 0x95, 0x21, 0x5e, 0xb2, 0xf2, 0x38, 0xfe, 0xed, 0x41, 0xb7, 0x5c, 0xd3, 0xb4, + 0x32, 0x26, 0x9e, 0x03, 0x2c, 0x0d, 0x86, 0xe1, 0x62, 0x96, 0x96, 0x3d, 0xc3, 0xe1, 0xca, 0x9c, + 0x5b, 0xe4, 0x27, 0xd8, 0xad, 0xc5, 0xc7, 0x60, 0x01, 0xfc, 0xc7, 0x53, 0xe1, 0xeb, 0x15, 0x19, + 0x57, 0xe0, 0x1c, 0x60, 0xa9, 0x61, 0x83, 0x47, 0xcb, 0x01, 0x0d, 0x1e, 0x6d, 0xd1, 0xc7, 0xbf, + 0x3c, 0x00, 0x76, 0x7d, 0x5a, 0x4f, 0xf7, 0x19, 0x5e, 0x5c, 0xdd, 0xde, 0x92, 0xc2, 0x75, 0xd2, + 0x86, 0xeb, 0x92, 0xf8, 0x11, 0x7c, 0xb7, 0x7f, 0xfc, 0x9f, 0xd2, 0x61, 0xd0, 0x4e, 0x54, 0x7c, + 0x26, 0x6f, 0xbf, 0x8d, 0xee, 0x84, 0xf9, 0xfe, 0x70, 0x13, 0x73, 0x39, 0x3f, 0x76, 0xa8, 0xfa, + 0xfb, 0xae, 0x7c, 0x1d, 0x48, 0x1d, 0xdb, 0xc7, 0xe1, 0x66, 0xc7, 0x7e, 0x4e, 0xfe, 0x04, 0x00, + 0x00, 0xff, 0xff, 0x3b, 0x56, 0x68, 0x37, 0x34, 0x04, 0x00, 0x00, } diff --git a/proto/service.proto b/proto/service.proto index f413e3696..bf6bcbe82 100644 --- a/proto/service.proto +++ b/proto/service.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package livekit; -option go_package = "github.com/livekit/livekit-server/proto"; +option go_package = "livekit"; // Room service that can be performed on any node @@ -20,7 +20,7 @@ message CreateRoomRequest { message CreateRoomResponse { string room = 1; string node_ip = 2; - string node_port = 3; + uint32 node_rtc_port = 3; } message JoinRoomRequest { @@ -30,7 +30,7 @@ message JoinRoomRequest { message JoinRoomResponse { string node_ip = 1; - string node_port = 2; + uint32 node_rtc_port = 2; } message DeleteRoomRequest { @@ -42,10 +42,13 @@ message DeleteRoomResponse { // RTC methods performed on target node service RTCService { + // offer allows client to initiate a RTC session rpc Offer(SessionDescription) returns (SessionDescription); // push channel to allow server to push commands to client - rpc Signal(SignalRequest) returns (stream SignalResponse); +// rpc Signal(SignalRequest) returns (stream SignalResponse); + + // trickle sends more ICE candidates to server rpc Trickle(TrickleRequest) returns (TrickleResponse); } diff --git a/proto/service.twirp.go b/proto/service.twirp.go new file mode 100644 index 000000000..309c02309 --- /dev/null +++ b/proto/service.twirp.go @@ -0,0 +1,2365 @@ +// Code generated by protoc-gen-twirp v7.1.0, DO NOT EDIT. +// source: service.proto + +/* +Package proto is a generated twirp stub package. +This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v7.1.0. + +It is generated from these files: + model.proto + service.proto +*/ +package proto + +import bytes "bytes" +import strings "strings" +import context "context" +import fmt "fmt" +import ioutil "io/ioutil" +import http "net/http" +import strconv "strconv" + +import jsonpb "github.com/golang/protobuf/jsonpb" +import proto "github.com/golang/protobuf/proto" +import twirp "github.com/twitchtv/twirp" +import ctxsetters "github.com/twitchtv/twirp/ctxsetters" + +// Imports only used by utility functions: +import io "io" +import json "encoding/json" +import path "path" +import url "net/url" + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the twirp package used in your project. +// A compilation error at this line likely means your copy of the +// twirp package needs to be updated. +const _ = twirp.TwirpPackageIsVersion7 + +// ===================== +// RoomService Interface +// ===================== + +// Room service that can be performed on any node +type RoomService interface { + // TODO: how do we secure room service? + // should be accessible to only internal servers, not external + CreateRoom(context.Context, *CreateRoomRequest) (*CreateRoomResponse, error) + + JoinRoom(context.Context, *JoinRoomRequest) (*JoinRoomResponse, error) + + DeleteRoom(context.Context, *DeleteRoomRequest) (*DeleteRoomResponse, error) +} + +// =========================== +// RoomService Protobuf Client +// =========================== + +type roomServiceProtobufClient struct { + client HTTPClient + urls [3]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewRoomServiceProtobufClient creates a Protobuf client that implements the RoomService interface. +// It communicates using Protobuf and can be configured with a custom HTTPClient. +func NewRoomServiceProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) RoomService { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Build method URLs: []/./ + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(clientOpts.PathPrefix(), "livekit", "RoomService") + urls := [3]string{ + serviceURL + "CreateRoom", + serviceURL + "JoinRoom", + serviceURL + "DeleteRoom", + } + + return &roomServiceProtobufClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *roomServiceProtobufClient) CreateRoom(ctx context.Context, in *CreateRoomRequest) (*CreateRoomResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RoomService") + ctx = ctxsetters.WithMethodName(ctx, "CreateRoom") + caller := c.callCreateRoom + if c.interceptor != nil { + caller = func(ctx context.Context, req *CreateRoomRequest) (*CreateRoomResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*CreateRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*CreateRoomRequest) when calling interceptor") + } + return c.callCreateRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*CreateRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*CreateRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *roomServiceProtobufClient) callCreateRoom(ctx context.Context, in *CreateRoomRequest) (*CreateRoomResponse, error) { + out := new(CreateRoomResponse) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +func (c *roomServiceProtobufClient) JoinRoom(ctx context.Context, in *JoinRoomRequest) (*JoinRoomResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RoomService") + ctx = ctxsetters.WithMethodName(ctx, "JoinRoom") + caller := c.callJoinRoom + if c.interceptor != nil { + caller = func(ctx context.Context, req *JoinRoomRequest) (*JoinRoomResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*JoinRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*JoinRoomRequest) when calling interceptor") + } + return c.callJoinRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*JoinRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*JoinRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *roomServiceProtobufClient) callJoinRoom(ctx context.Context, in *JoinRoomRequest) (*JoinRoomResponse, error) { + out := new(JoinRoomResponse) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +func (c *roomServiceProtobufClient) DeleteRoom(ctx context.Context, in *DeleteRoomRequest) (*DeleteRoomResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RoomService") + ctx = ctxsetters.WithMethodName(ctx, "DeleteRoom") + caller := c.callDeleteRoom + if c.interceptor != nil { + caller = func(ctx context.Context, req *DeleteRoomRequest) (*DeleteRoomResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*DeleteRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*DeleteRoomRequest) when calling interceptor") + } + return c.callDeleteRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*DeleteRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*DeleteRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *roomServiceProtobufClient) callDeleteRoom(ctx context.Context, in *DeleteRoomRequest) (*DeleteRoomResponse, error) { + out := new(DeleteRoomResponse) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ======================= +// RoomService JSON Client +// ======================= + +type roomServiceJSONClient struct { + client HTTPClient + urls [3]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewRoomServiceJSONClient creates a JSON client that implements the RoomService interface. +// It communicates using JSON and can be configured with a custom HTTPClient. +func NewRoomServiceJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) RoomService { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Build method URLs: []/./ + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(clientOpts.PathPrefix(), "livekit", "RoomService") + urls := [3]string{ + serviceURL + "CreateRoom", + serviceURL + "JoinRoom", + serviceURL + "DeleteRoom", + } + + return &roomServiceJSONClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *roomServiceJSONClient) CreateRoom(ctx context.Context, in *CreateRoomRequest) (*CreateRoomResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RoomService") + ctx = ctxsetters.WithMethodName(ctx, "CreateRoom") + caller := c.callCreateRoom + if c.interceptor != nil { + caller = func(ctx context.Context, req *CreateRoomRequest) (*CreateRoomResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*CreateRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*CreateRoomRequest) when calling interceptor") + } + return c.callCreateRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*CreateRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*CreateRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *roomServiceJSONClient) callCreateRoom(ctx context.Context, in *CreateRoomRequest) (*CreateRoomResponse, error) { + out := new(CreateRoomResponse) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +func (c *roomServiceJSONClient) JoinRoom(ctx context.Context, in *JoinRoomRequest) (*JoinRoomResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RoomService") + ctx = ctxsetters.WithMethodName(ctx, "JoinRoom") + caller := c.callJoinRoom + if c.interceptor != nil { + caller = func(ctx context.Context, req *JoinRoomRequest) (*JoinRoomResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*JoinRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*JoinRoomRequest) when calling interceptor") + } + return c.callJoinRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*JoinRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*JoinRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *roomServiceJSONClient) callJoinRoom(ctx context.Context, in *JoinRoomRequest) (*JoinRoomResponse, error) { + out := new(JoinRoomResponse) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +func (c *roomServiceJSONClient) DeleteRoom(ctx context.Context, in *DeleteRoomRequest) (*DeleteRoomResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RoomService") + ctx = ctxsetters.WithMethodName(ctx, "DeleteRoom") + caller := c.callDeleteRoom + if c.interceptor != nil { + caller = func(ctx context.Context, req *DeleteRoomRequest) (*DeleteRoomResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*DeleteRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*DeleteRoomRequest) when calling interceptor") + } + return c.callDeleteRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*DeleteRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*DeleteRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *roomServiceJSONClient) callDeleteRoom(ctx context.Context, in *DeleteRoomRequest) (*DeleteRoomResponse, error) { + out := new(DeleteRoomResponse) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ========================== +// RoomService Server Handler +// ========================== + +type roomServiceServer struct { + RoomService + interceptor twirp.Interceptor + hooks *twirp.ServerHooks + pathPrefix string // prefix for routing + jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response +} + +// NewRoomServiceServer builds a TwirpServer that can be used as an http.Handler to handle +// HTTP requests that are routed to the right method in the provided svc implementation. +// The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). +func NewRoomServiceServer(svc RoomService, opts ...interface{}) TwirpServer { + serverOpts := twirp.ServerOptions{} + for _, opt := range opts { + switch o := opt.(type) { + case twirp.ServerOption: + o(&serverOpts) + case *twirp.ServerHooks: // backwards compatibility, allow to specify hooks as an argument + twirp.WithServerHooks(o)(&serverOpts) + case nil: // backwards compatibility, allow nil value for the argument + continue + default: + panic(fmt.Sprintf("Invalid option type %T on NewRoomServiceServer", o)) + } + } + + return &roomServiceServer{ + RoomService: svc, + pathPrefix: serverOpts.PathPrefix(), + interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), + hooks: serverOpts.Hooks, + jsonSkipDefaults: serverOpts.JSONSkipDefaults, + } +} + +// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func (s *roomServiceServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// RoomServicePathPrefix is a convenience constant that could used to identify URL paths. +// Should be used with caution, it only matches routes generated by Twirp Go clients, +// that add a "/twirp" prefix by default, and use CamelCase service and method names. +// More info: https://twitchtv.github.io/twirp/docs/routing.html +const RoomServicePathPrefix = "/twirp/livekit.RoomService/" + +func (s *roomServiceServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RoomService") + ctx = ctxsetters.WithResponseWriter(ctx, resp) + + var err error + ctx, err = callRequestReceived(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + if req.Method != "POST" { + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + // Verify path format: []/./ + prefix, pkgService, method := parseTwirpPath(req.URL.Path) + if pkgService != "livekit.RoomService" { + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + if prefix != s.pathPrefix { + msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + switch method { + case "CreateRoom": + s.serveCreateRoom(ctx, resp, req) + return + case "JoinRoom": + s.serveJoinRoom(ctx, resp, req) + return + case "DeleteRoom": + s.serveDeleteRoom(ctx, resp, req) + return + default: + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } +} + +func (s *roomServiceServer) serveCreateRoom(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveCreateRoomJSON(ctx, resp, req) + case "application/protobuf": + s.serveCreateRoomProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *roomServiceServer) serveCreateRoomJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "CreateRoom") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(CreateRoomRequest) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded")) + return + } + + handler := s.RoomService.CreateRoom + if s.interceptor != nil { + handler = func(ctx context.Context, req *CreateRoomRequest) (*CreateRoomResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*CreateRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*CreateRoomRequest) when calling interceptor") + } + return s.RoomService.CreateRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*CreateRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*CreateRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *CreateRoomResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *CreateRoomResponse and nil error while calling CreateRoom. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true, EmitDefaults: !s.jsonSkipDefaults} + if err = marshaler.Marshal(&buf, respContent); err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + respBytes := buf.Bytes() + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *roomServiceServer) serveCreateRoomProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "CreateRoom") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to read request body")) + return + } + reqContent := new(CreateRoomRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.RoomService.CreateRoom + if s.interceptor != nil { + handler = func(ctx context.Context, req *CreateRoomRequest) (*CreateRoomResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*CreateRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*CreateRoomRequest) when calling interceptor") + } + return s.RoomService.CreateRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*CreateRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*CreateRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *CreateRoomResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *CreateRoomResponse and nil error while calling CreateRoom. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *roomServiceServer) serveJoinRoom(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveJoinRoomJSON(ctx, resp, req) + case "application/protobuf": + s.serveJoinRoomProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *roomServiceServer) serveJoinRoomJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "JoinRoom") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(JoinRoomRequest) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded")) + return + } + + handler := s.RoomService.JoinRoom + if s.interceptor != nil { + handler = func(ctx context.Context, req *JoinRoomRequest) (*JoinRoomResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*JoinRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*JoinRoomRequest) when calling interceptor") + } + return s.RoomService.JoinRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*JoinRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*JoinRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *JoinRoomResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *JoinRoomResponse and nil error while calling JoinRoom. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true, EmitDefaults: !s.jsonSkipDefaults} + if err = marshaler.Marshal(&buf, respContent); err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + respBytes := buf.Bytes() + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *roomServiceServer) serveJoinRoomProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "JoinRoom") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to read request body")) + return + } + reqContent := new(JoinRoomRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.RoomService.JoinRoom + if s.interceptor != nil { + handler = func(ctx context.Context, req *JoinRoomRequest) (*JoinRoomResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*JoinRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*JoinRoomRequest) when calling interceptor") + } + return s.RoomService.JoinRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*JoinRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*JoinRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *JoinRoomResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *JoinRoomResponse and nil error while calling JoinRoom. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *roomServiceServer) serveDeleteRoom(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveDeleteRoomJSON(ctx, resp, req) + case "application/protobuf": + s.serveDeleteRoomProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *roomServiceServer) serveDeleteRoomJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "DeleteRoom") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(DeleteRoomRequest) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded")) + return + } + + handler := s.RoomService.DeleteRoom + if s.interceptor != nil { + handler = func(ctx context.Context, req *DeleteRoomRequest) (*DeleteRoomResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*DeleteRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*DeleteRoomRequest) when calling interceptor") + } + return s.RoomService.DeleteRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*DeleteRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*DeleteRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *DeleteRoomResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *DeleteRoomResponse and nil error while calling DeleteRoom. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true, EmitDefaults: !s.jsonSkipDefaults} + if err = marshaler.Marshal(&buf, respContent); err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + respBytes := buf.Bytes() + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *roomServiceServer) serveDeleteRoomProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "DeleteRoom") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to read request body")) + return + } + reqContent := new(DeleteRoomRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.RoomService.DeleteRoom + if s.interceptor != nil { + handler = func(ctx context.Context, req *DeleteRoomRequest) (*DeleteRoomResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*DeleteRoomRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*DeleteRoomRequest) when calling interceptor") + } + return s.RoomService.DeleteRoom(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*DeleteRoomResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*DeleteRoomResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *DeleteRoomResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *DeleteRoomResponse and nil error while calling DeleteRoom. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *roomServiceServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 0 +} + +func (s *roomServiceServer) ProtocGenTwirpVersion() string { + return "v7.1.0" +} + +// PathPrefix returns the base service path, in the form: "//./" +// that is everything in a Twirp route except for the . This can be used for routing, +// for example to identify the requests that are targeted to this service in a mux. +func (s *roomServiceServer) PathPrefix() string { + return baseServicePath(s.pathPrefix, "livekit", "RoomService") +} + +// ==================== +// RTCService Interface +// ==================== + +// RTC methods performed on target node +type RTCService interface { + // offer allows client to initiate a RTC session + Offer(context.Context, *SessionDescription) (*SessionDescription, error) + + // trickle sends more ICE candidates to server + Trickle(context.Context, *TrickleRequest) (*TrickleResponse, error) +} + +// ========================== +// RTCService Protobuf Client +// ========================== + +type rTCServiceProtobufClient struct { + client HTTPClient + urls [2]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewRTCServiceProtobufClient creates a Protobuf client that implements the RTCService interface. +// It communicates using Protobuf and can be configured with a custom HTTPClient. +func NewRTCServiceProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) RTCService { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Build method URLs: []/./ + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(clientOpts.PathPrefix(), "livekit", "RTCService") + urls := [2]string{ + serviceURL + "Offer", + serviceURL + "Trickle", + } + + return &rTCServiceProtobufClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *rTCServiceProtobufClient) Offer(ctx context.Context, in *SessionDescription) (*SessionDescription, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RTCService") + ctx = ctxsetters.WithMethodName(ctx, "Offer") + caller := c.callOffer + if c.interceptor != nil { + caller = func(ctx context.Context, req *SessionDescription) (*SessionDescription, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*SessionDescription) when calling interceptor") + } + return c.callOffer(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*SessionDescription) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *rTCServiceProtobufClient) callOffer(ctx context.Context, in *SessionDescription) (*SessionDescription, error) { + out := new(SessionDescription) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +func (c *rTCServiceProtobufClient) Trickle(ctx context.Context, in *TrickleRequest) (*TrickleResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RTCService") + ctx = ctxsetters.WithMethodName(ctx, "Trickle") + caller := c.callTrickle + if c.interceptor != nil { + caller = func(ctx context.Context, req *TrickleRequest) (*TrickleResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*TrickleRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*TrickleRequest) when calling interceptor") + } + return c.callTrickle(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*TrickleResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*TrickleResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *rTCServiceProtobufClient) callTrickle(ctx context.Context, in *TrickleRequest) (*TrickleResponse, error) { + out := new(TrickleResponse) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ====================== +// RTCService JSON Client +// ====================== + +type rTCServiceJSONClient struct { + client HTTPClient + urls [2]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewRTCServiceJSONClient creates a JSON client that implements the RTCService interface. +// It communicates using JSON and can be configured with a custom HTTPClient. +func NewRTCServiceJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) RTCService { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Build method URLs: []/./ + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(clientOpts.PathPrefix(), "livekit", "RTCService") + urls := [2]string{ + serviceURL + "Offer", + serviceURL + "Trickle", + } + + return &rTCServiceJSONClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *rTCServiceJSONClient) Offer(ctx context.Context, in *SessionDescription) (*SessionDescription, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RTCService") + ctx = ctxsetters.WithMethodName(ctx, "Offer") + caller := c.callOffer + if c.interceptor != nil { + caller = func(ctx context.Context, req *SessionDescription) (*SessionDescription, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*SessionDescription) when calling interceptor") + } + return c.callOffer(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*SessionDescription) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *rTCServiceJSONClient) callOffer(ctx context.Context, in *SessionDescription) (*SessionDescription, error) { + out := new(SessionDescription) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +func (c *rTCServiceJSONClient) Trickle(ctx context.Context, in *TrickleRequest) (*TrickleResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RTCService") + ctx = ctxsetters.WithMethodName(ctx, "Trickle") + caller := c.callTrickle + if c.interceptor != nil { + caller = func(ctx context.Context, req *TrickleRequest) (*TrickleResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*TrickleRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*TrickleRequest) when calling interceptor") + } + return c.callTrickle(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*TrickleResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*TrickleResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *rTCServiceJSONClient) callTrickle(ctx context.Context, in *TrickleRequest) (*TrickleResponse, error) { + out := new(TrickleResponse) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ========================= +// RTCService Server Handler +// ========================= + +type rTCServiceServer struct { + RTCService + interceptor twirp.Interceptor + hooks *twirp.ServerHooks + pathPrefix string // prefix for routing + jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response +} + +// NewRTCServiceServer builds a TwirpServer that can be used as an http.Handler to handle +// HTTP requests that are routed to the right method in the provided svc implementation. +// The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). +func NewRTCServiceServer(svc RTCService, opts ...interface{}) TwirpServer { + serverOpts := twirp.ServerOptions{} + for _, opt := range opts { + switch o := opt.(type) { + case twirp.ServerOption: + o(&serverOpts) + case *twirp.ServerHooks: // backwards compatibility, allow to specify hooks as an argument + twirp.WithServerHooks(o)(&serverOpts) + case nil: // backwards compatibility, allow nil value for the argument + continue + default: + panic(fmt.Sprintf("Invalid option type %T on NewRTCServiceServer", o)) + } + } + + return &rTCServiceServer{ + RTCService: svc, + pathPrefix: serverOpts.PathPrefix(), + interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), + hooks: serverOpts.Hooks, + jsonSkipDefaults: serverOpts.JSONSkipDefaults, + } +} + +// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func (s *rTCServiceServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// RTCServicePathPrefix is a convenience constant that could used to identify URL paths. +// Should be used with caution, it only matches routes generated by Twirp Go clients, +// that add a "/twirp" prefix by default, and use CamelCase service and method names. +// More info: https://twitchtv.github.io/twirp/docs/routing.html +const RTCServicePathPrefix = "/twirp/livekit.RTCService/" + +func (s *rTCServiceServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "RTCService") + ctx = ctxsetters.WithResponseWriter(ctx, resp) + + var err error + ctx, err = callRequestReceived(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + if req.Method != "POST" { + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + // Verify path format: []/./ + prefix, pkgService, method := parseTwirpPath(req.URL.Path) + if pkgService != "livekit.RTCService" { + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + if prefix != s.pathPrefix { + msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + switch method { + case "Offer": + s.serveOffer(ctx, resp, req) + return + case "Trickle": + s.serveTrickle(ctx, resp, req) + return + default: + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } +} + +func (s *rTCServiceServer) serveOffer(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveOfferJSON(ctx, resp, req) + case "application/protobuf": + s.serveOfferProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *rTCServiceServer) serveOfferJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Offer") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(SessionDescription) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded")) + return + } + + handler := s.RTCService.Offer + if s.interceptor != nil { + handler = func(ctx context.Context, req *SessionDescription) (*SessionDescription, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*SessionDescription) when calling interceptor") + } + return s.RTCService.Offer(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*SessionDescription) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *SessionDescription + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *SessionDescription and nil error while calling Offer. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true, EmitDefaults: !s.jsonSkipDefaults} + if err = marshaler.Marshal(&buf, respContent); err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + respBytes := buf.Bytes() + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *rTCServiceServer) serveOfferProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Offer") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to read request body")) + return + } + reqContent := new(SessionDescription) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.RTCService.Offer + if s.interceptor != nil { + handler = func(ctx context.Context, req *SessionDescription) (*SessionDescription, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*SessionDescription) when calling interceptor") + } + return s.RTCService.Offer(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*SessionDescription) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*SessionDescription) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *SessionDescription + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *SessionDescription and nil error while calling Offer. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *rTCServiceServer) serveTrickle(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveTrickleJSON(ctx, resp, req) + case "application/protobuf": + s.serveTrickleProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *rTCServiceServer) serveTrickleJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Trickle") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + reqContent := new(TrickleRequest) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded")) + return + } + + handler := s.RTCService.Trickle + if s.interceptor != nil { + handler = func(ctx context.Context, req *TrickleRequest) (*TrickleResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*TrickleRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*TrickleRequest) when calling interceptor") + } + return s.RTCService.Trickle(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*TrickleResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*TrickleResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *TrickleResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *TrickleResponse and nil error while calling Trickle. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true, EmitDefaults: !s.jsonSkipDefaults} + if err = marshaler.Marshal(&buf, respContent); err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + respBytes := buf.Bytes() + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *rTCServiceServer) serveTrickleProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Trickle") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to read request body")) + return + } + reqContent := new(TrickleRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.RTCService.Trickle + if s.interceptor != nil { + handler = func(ctx context.Context, req *TrickleRequest) (*TrickleResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*TrickleRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*TrickleRequest) when calling interceptor") + } + return s.RTCService.Trickle(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*TrickleResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*TrickleResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *TrickleResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *TrickleResponse and nil error while calling Trickle. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *rTCServiceServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 1 +} + +func (s *rTCServiceServer) ProtocGenTwirpVersion() string { + return "v7.1.0" +} + +// PathPrefix returns the base service path, in the form: "//./" +// that is everything in a Twirp route except for the . This can be used for routing, +// for example to identify the requests that are targeted to this service in a mux. +func (s *rTCServiceServer) PathPrefix() string { + return baseServicePath(s.pathPrefix, "livekit", "RTCService") +} + +// ===== +// Utils +// ===== + +// HTTPClient is the interface used by generated clients to send HTTP requests. +// It is fulfilled by *(net/http).Client, which is sufficient for most users. +// Users can provide their own implementation for special retry policies. +// +// HTTPClient implementations should not follow redirects. Redirects are +// automatically disabled if *(net/http).Client is passed to client +// constructors. See the withoutRedirects function in this file for more +// details. +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// TwirpServer is the interface generated server structs will support: they're +// HTTP handlers with additional methods for accessing metadata about the +// service. Those accessors are a low-level API for building reflection tools. +// Most people can think of TwirpServers as just http.Handlers. +type TwirpServer interface { + http.Handler + + // ServiceDescriptor returns gzipped bytes describing the .proto file that + // this service was generated from. Once unzipped, the bytes can be + // unmarshalled as a + // github.com/golang/protobuf/protoc-gen-go/descriptor.FileDescriptorProto. + // + // The returned integer is the index of this particular service within that + // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a + // low-level field, expected to be used for reflection. + ServiceDescriptor() ([]byte, int) + + // ProtocGenTwirpVersion is the semantic version string of the version of + // twirp used to generate this file. + ProtocGenTwirpVersion() string + + // PathPrefix returns the HTTP URL path prefix for all methods handled by this + // service. This can be used with an HTTP mux to route Twirp requests. + // The path prefix is in the form: "//./" + // that is, everything in a Twirp route except for the at the end. + PathPrefix() string +} + +// WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta). +// Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func WriteError(resp http.ResponseWriter, err error) { + writeError(context.Background(), resp, err, nil) +} + +// writeError writes Twirp errors in the response and triggers hooks. +func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) { + // Non-twirp errors are wrapped as Internal (default) + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + + statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code()) + ctx = ctxsetters.WithStatusCode(ctx, statusCode) + ctx = callError(ctx, hooks, twerr) + + respBody := marshalErrorToJSON(twerr) + + resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON + resp.Header().Set("Content-Length", strconv.Itoa(len(respBody))) + resp.WriteHeader(statusCode) // set HTTP status code and send response + + _, writeErr := resp.Write(respBody) + if writeErr != nil { + // We have three options here. We could log the error, call the Error + // hook, or just silently ignore the error. + // + // Logging is unacceptable because we don't have a user-controlled + // logger; writing out to stderr without permission is too rude. + // + // Calling the Error hook would confuse users: it would mean the Error + // hook got called twice for one request, which is likely to lead to + // duplicated log messages and metrics, no matter how well we document + // the behavior. + // + // Silently ignoring the error is our least-bad option. It's highly + // likely that the connection is broken and the original 'err' says + // so anyway. + _ = writeErr + } + + callResponseSent(ctx, hooks) +} + +// sanitizeBaseURL parses the the baseURL, and adds the "http" scheme if needed. +// If the URL is unparsable, the baseURL is returned unchaged. +func sanitizeBaseURL(baseURL string) string { + u, err := url.Parse(baseURL) + if err != nil { + return baseURL // invalid URL will fail later when making requests + } + if u.Scheme == "" { + u.Scheme = "http" + } + return u.String() +} + +// baseServicePath composes the path prefix for the service (without ). +// e.g.: baseServicePath("/twirp", "my.pkg", "MyService") +// returns => "/twirp/my.pkg.MyService/" +// e.g.: baseServicePath("", "", "MyService") +// returns => "/MyService/" +func baseServicePath(prefix, pkg, service string) string { + fullServiceName := service + if pkg != "" { + fullServiceName = pkg + "." + service + } + return path.Join("/", prefix, fullServiceName) + "/" +} + +// parseTwirpPath extracts path components form a valid Twirp route. +// Expected format: "[]/./" +// e.g.: prefix, pkgService, method := parseTwirpPath("/twirp/pkg.Svc/MakeHat") +func parseTwirpPath(path string) (string, string, string) { + parts := strings.Split(path, "/") + if len(parts) < 2 { + return "", "", "" + } + method := parts[len(parts)-1] + pkgService := parts[len(parts)-2] + prefix := strings.Join(parts[0:len(parts)-2], "/") + return prefix, pkgService, method +} + +// getCustomHTTPReqHeaders retrieves a copy of any headers that are set in +// a context through the twirp.WithHTTPRequestHeaders function. +// If there are no headers set, or if they have the wrong type, nil is returned. +func getCustomHTTPReqHeaders(ctx context.Context) http.Header { + header, ok := twirp.HTTPRequestHeaders(ctx) + if !ok || header == nil { + return nil + } + copied := make(http.Header) + for k, vv := range header { + if vv == nil { + copied[k] = nil + continue + } + copied[k] = make([]string, len(vv)) + copy(copied[k], vv) + } + return copied +} + +// newRequest makes an http.Request from a client, adding common headers. +func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { + req, err := http.NewRequest("POST", url, reqBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil { + req.Header = customHeader + } + req.Header.Set("Accept", contentType) + req.Header.Set("Content-Type", contentType) + req.Header.Set("Twirp-Version", "v7.1.0") + return req, nil +} + +// JSON serialization for errors +type twerrJSON struct { + Code string `json:"code"` + Msg string `json:"msg"` + Meta map[string]string `json:"meta,omitempty"` +} + +// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. +// If serialization fails, it will use a descriptive Internal error instead. +func marshalErrorToJSON(twerr twirp.Error) []byte { + // make sure that msg is not too large + msg := twerr.Msg() + if len(msg) > 1e6 { + msg = msg[:1e6] + } + + tj := twerrJSON{ + Code: string(twerr.Code()), + Msg: msg, + Meta: twerr.MetaMap(), + } + + buf, err := json.Marshal(&tj) + if err != nil { + buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback + } + + return buf +} + +// errorFromResponse builds a twirp.Error from a non-200 HTTP response. +// If the response has a valid serialized Twirp error, then it's returned. +// If not, the response status code is used to generate a similar twirp +// error. See twirpErrorFromIntermediary for more info on intermediary errors. +func errorFromResponse(resp *http.Response) twirp.Error { + statusCode := resp.StatusCode + statusText := http.StatusText(statusCode) + + if isHTTPRedirect(statusCode) { + // Unexpected redirect: it must be an error from an intermediary. + // Twirp clients don't follow redirects automatically, Twirp only handles + // POST requests, redirects should only happen on GET and HEAD requests. + location := resp.Header.Get("Location") + msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location) + return twirpErrorFromIntermediary(statusCode, msg, location) + } + + respBodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return wrapInternal(err, "failed to read server error response body") + } + + var tj twerrJSON + dec := json.NewDecoder(bytes.NewReader(respBodyBytes)) + dec.DisallowUnknownFields() + if err := dec.Decode(&tj); err != nil || tj.Code == "" { + // Invalid JSON response; it must be an error from an intermediary. + msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText) + return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes)) + } + + errorCode := twirp.ErrorCode(tj.Code) + if !twirp.IsValidErrorCode(errorCode) { + msg := "invalid type returned from server error response: " + tj.Code + return twirp.InternalError(msg).WithMeta("body", string(respBodyBytes)) + } + + twerr := twirp.NewError(errorCode, tj.Msg) + for k, v := range tj.Meta { + twerr = twerr.WithMeta(k, v) + } + return twerr +} + +// twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors. +// The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. +// Returned twirp Errors have some additional metadata for inspection. +func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error { + var code twirp.ErrorCode + if isHTTPRedirect(status) { // 3xx + code = twirp.Internal + } else { + switch status { + case 400: // Bad Request + code = twirp.Internal + case 401: // Unauthorized + code = twirp.Unauthenticated + case 403: // Forbidden + code = twirp.PermissionDenied + case 404: // Not Found + code = twirp.BadRoute + case 429: // Too Many Requests + code = twirp.ResourceExhausted + case 502, 503, 504: // Bad Gateway, Service Unavailable, Gateway Timeout + code = twirp.Unavailable + default: // All other codes + code = twirp.Unknown + } + } + + twerr := twirp.NewError(code, msg) + twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary + twerr = twerr.WithMeta("status_code", strconv.Itoa(status)) + if isHTTPRedirect(status) { + twerr = twerr.WithMeta("location", bodyOrLocation) + } else { + twerr = twerr.WithMeta("body", bodyOrLocation) + } + return twerr +} + +func isHTTPRedirect(status int) bool { + return status >= 300 && status <= 399 +} + +// wrapInternal wraps an error with a prefix as an Internal error. +// The original error cause is accessible by github.com/pkg/errors.Cause. +func wrapInternal(err error, prefix string) twirp.Error { + return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err}) +} + +type wrappedError struct { + prefix string + cause error +} + +func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() } +func (e *wrappedError) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As +func (e *wrappedError) Cause() error { return e.cause } // for github.com/pkg/errors + +// ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal +// error response (status 500), and error hooks are properly called with the panic wrapped as an error. +// The panic is re-raised so it can be handled normally with middleware. +func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) { + if r := recover(); r != nil { + // Wrap the panic as an error so it can be passed to error hooks. + // The original error is accessible from error hooks, but not visible in the response. + err := errFromPanic(r) + twerr := &internalWithCause{msg: "Internal service panic", cause: err} + // Actually write the error + writeError(ctx, resp, twerr, hooks) + // If possible, flush the error to the wire. + f, ok := resp.(http.Flusher) + if ok { + f.Flush() + } + + panic(r) + } +} + +// errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error. +func errFromPanic(p interface{}) error { + if err, ok := p.(error); ok { + return err + } + return fmt.Errorf("panic: %v", p) +} + +// internalWithCause is a Twirp Internal error wrapping an original error cause, +// but the original error message is not exposed on Msg(). The original error +// can be checked with go1.13+ errors.Is/As, and also by (github.com/pkg/errors).Unwrap +type internalWithCause struct { + msg string + cause error +} + +func (e *internalWithCause) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As +func (e *internalWithCause) Cause() error { return e.cause } // for github.com/pkg/errors +func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() } +func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal } +func (e *internalWithCause) Msg() string { return e.msg } +func (e *internalWithCause) Meta(key string) string { return "" } +func (e *internalWithCause) MetaMap() map[string]string { return nil } +func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e } + +// malformedRequestError is used when the twirp server cannot unmarshal a request +func malformedRequestError(msg string) twirp.Error { + return twirp.NewError(twirp.Malformed, msg) +} + +// badRouteError is used when the twirp server cannot route a request +func badRouteError(msg string, method, url string) twirp.Error { + err := twirp.NewError(twirp.BadRoute, msg) + err = err.WithMeta("twirp_invalid_route", method+" "+url) + return err +} + +// withoutRedirects makes sure that the POST request can not be redirected. +// The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or +// 303 response, and also 301s in go1.8. It redirects by making a second request, changing the +// method to GET and removing the body. This produces very confusing error messages, so instead we +// set a redirect policy that always errors. This stops Go from executing the redirect. +// +// We have to be a little careful in case the user-provided http.Client has its own CheckRedirect +// policy - if so, we'll run through that policy first. +// +// Because this requires modifying the http.Client, we make a new copy of the client and return it. +func withoutRedirects(in *http.Client) *http.Client { + copy := *in + copy.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if in.CheckRedirect != nil { + // Run the input's redirect if it exists, in case it has side effects, but ignore any error it + // returns, since we want to use ErrUseLastResponse. + err := in.CheckRedirect(req, via) + _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use. + } + return http.ErrUseLastResponse + } + return © +} + +// doProtobufRequest makes a Protobuf request to the remote Twirp service. +func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { + reqBodyBytes, err := proto.Marshal(in) + if err != nil { + return ctx, wrapInternal(err, "failed to marshal proto request") + } + reqBody := bytes.NewBuffer(reqBodyBytes) + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + req, err := newRequest(ctx, url, reqBody, "application/protobuf") + if err != nil { + return ctx, wrapInternal(err, "could not build request") + } + ctx, err = callClientRequestPrepared(ctx, hooks, req) + if err != nil { + return ctx, err + } + + req = req.WithContext(ctx) + resp, err := client.Do(req) + if err != nil { + return ctx, wrapInternal(err, "failed to do request") + } + + defer func() { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = wrapInternal(cerr, "failed to close response body") + } + }() + + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + if resp.StatusCode != 200 { + return ctx, errorFromResponse(resp) + } + + respBodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ctx, wrapInternal(err, "failed to read response body") + } + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + if err = proto.Unmarshal(respBodyBytes, out); err != nil { + return ctx, wrapInternal(err, "failed to unmarshal proto response") + } + return ctx, nil +} + +// doJSONRequest makes a JSON request to the remote Twirp service. +func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { + reqBody := bytes.NewBuffer(nil) + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(reqBody, in); err != nil { + return ctx, wrapInternal(err, "failed to marshal json request") + } + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + req, err := newRequest(ctx, url, reqBody, "application/json") + if err != nil { + return ctx, wrapInternal(err, "could not build request") + } + ctx, err = callClientRequestPrepared(ctx, hooks, req) + if err != nil { + return ctx, err + } + + req = req.WithContext(ctx) + resp, err := client.Do(req) + if err != nil { + return ctx, wrapInternal(err, "failed to do request") + } + + defer func() { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = wrapInternal(cerr, "failed to close response body") + } + }() + + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + + if resp.StatusCode != 200 { + return ctx, errorFromResponse(resp) + } + + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(resp.Body, out); err != nil { + return ctx, wrapInternal(err, "failed to unmarshal json response") + } + if err = ctx.Err(); err != nil { + return ctx, wrapInternal(err, "aborted because context was done") + } + return ctx, nil +} + +// Call twirp.ServerHooks.RequestReceived if the hook is available +func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { + if h == nil || h.RequestReceived == nil { + return ctx, nil + } + return h.RequestReceived(ctx) +} + +// Call twirp.ServerHooks.RequestRouted if the hook is available +func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { + if h == nil || h.RequestRouted == nil { + return ctx, nil + } + return h.RequestRouted(ctx) +} + +// Call twirp.ServerHooks.ResponsePrepared if the hook is available +func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context { + if h == nil || h.ResponsePrepared == nil { + return ctx + } + return h.ResponsePrepared(ctx) +} + +// Call twirp.ServerHooks.ResponseSent if the hook is available +func callResponseSent(ctx context.Context, h *twirp.ServerHooks) { + if h == nil || h.ResponseSent == nil { + return + } + h.ResponseSent(ctx) +} + +// Call twirp.ServerHooks.Error if the hook is available +func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context { + if h == nil || h.Error == nil { + return ctx + } + return h.Error(ctx, err) +} + +func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) { + if h == nil || h.ResponseReceived == nil { + return + } + h.ResponseReceived(ctx) +} + +func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) { + if h == nil || h.RequestPrepared == nil { + return ctx, nil + } + return h.RequestPrepared(ctx, req) +} + +func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) { + if h == nil || h.Error == nil { + return + } + h.Error(ctx, err) +} + +var twirpFileDescriptor0 = []byte{ + // 475 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xc1, 0x4e, 0xdb, 0x40, + 0x10, 0xc5, 0x40, 0x31, 0x99, 0x34, 0x84, 0x8c, 0x2a, 0xe1, 0x3a, 0x3d, 0x20, 0x5f, 0x42, 0x0f, + 0x35, 0x6a, 0xb8, 0x55, 0x95, 0xaa, 0x06, 0x90, 0xa0, 0x17, 0xaa, 0x0d, 0xa7, 0x5e, 0x22, 0xb3, + 0x1e, 0xe8, 0x0a, 0xc7, 0xeb, 0xee, 0x2e, 0x48, 0x9c, 0x7b, 0xee, 0x1f, 0xf6, 0x63, 0x2a, 0xaf, + 0xd7, 0x89, 0x5b, 0xa7, 0xe1, 0xe4, 0xf5, 0xcc, 0xdb, 0x99, 0x37, 0xf3, 0x9e, 0x16, 0x7a, 0x9a, + 0xd4, 0xa3, 0xe0, 0x14, 0x17, 0x4a, 0x1a, 0x89, 0x7e, 0x26, 0x1e, 0xe9, 0x5e, 0x98, 0x68, 0x04, + 0x83, 0x53, 0x45, 0x89, 0x21, 0x26, 0xe5, 0x9c, 0xd1, 0x8f, 0x07, 0xd2, 0x06, 0x11, 0xb6, 0x95, + 0x94, 0xf3, 0xc0, 0x3b, 0xf4, 0x8e, 0x3a, 0xcc, 0x9e, 0x23, 0x02, 0x6c, 0x02, 0x75, 0x21, 0x73, + 0x4d, 0xab, 0x90, 0x78, 0x00, 0x7e, 0x2e, 0x53, 0x9a, 0x89, 0x22, 0xd8, 0xb4, 0xe1, 0x9d, 0xf2, + 0xf7, 0xb2, 0xc0, 0x08, 0x7a, 0x36, 0xa1, 0x0c, 0x9f, 0x15, 0x52, 0x99, 0x60, 0xeb, 0xd0, 0x3b, + 0xea, 0xb1, 0x6e, 0x19, 0x64, 0x86, 0x7f, 0x95, 0xca, 0x44, 0x13, 0xe8, 0x7f, 0x91, 0x22, 0x7f, + 0x86, 0x0d, 0x0e, 0xa1, 0xc3, 0x33, 0x41, 0xb9, 0x99, 0x89, 0xd4, 0x75, 0xd9, 0xad, 0x02, 0x97, + 0x69, 0x74, 0x05, 0xfb, 0xcb, 0x1a, 0x8e, 0x68, 0x83, 0x94, 0xb7, 0x9e, 0xd4, 0x66, 0x9b, 0xd4, + 0x08, 0x06, 0x67, 0x94, 0xd1, 0xf3, 0x4b, 0x7a, 0x05, 0xd8, 0x04, 0x56, 0xbd, 0xa3, 0x3e, 0xf4, + 0xa6, 0xe2, 0x2e, 0x4f, 0x32, 0x77, 0x35, 0xfa, 0xe9, 0xc1, 0x5e, 0x1d, 0x71, 0xfc, 0xde, 0xc3, + 0x76, 0x4a, 0x9a, 0xdb, 0x6a, 0xdd, 0xf1, 0x30, 0x76, 0xfa, 0xc4, 0x53, 0xd2, 0x5a, 0xc8, 0xfc, + 0x8c, 0x34, 0x57, 0xa2, 0x30, 0x42, 0xe6, 0x17, 0x1b, 0xcc, 0x42, 0xf1, 0x04, 0x7c, 0xa3, 0x04, + 0xbf, 0xcf, 0xc8, 0x72, 0xee, 0x8e, 0x0f, 0x16, 0xb7, 0xae, 0xab, 0xb8, 0xeb, 0x77, 0xb1, 0xc1, + 0x6a, 0xe4, 0xa4, 0x03, 0x7e, 0x91, 0x3c, 0x65, 0x32, 0x49, 0xa3, 0x18, 0xf6, 0xfe, 0xc6, 0xe1, + 0x1b, 0xe8, 0xf0, 0x24, 0x4f, 0x45, 0x9a, 0x18, 0x72, 0x73, 0x2d, 0x03, 0xd1, 0x00, 0xfa, 0x0b, + 0xbc, 0x9b, 0xec, 0x03, 0x60, 0x9b, 0x60, 0xb9, 0x19, 0xf3, 0x54, 0xd4, 0x15, 0xec, 0x19, 0xf7, + 0x61, 0x4b, 0xa7, 0x95, 0x21, 0x5e, 0xb2, 0xf2, 0x38, 0xfe, 0xed, 0x41, 0xb7, 0x5c, 0xd3, 0xb4, + 0x32, 0x26, 0x9e, 0x03, 0x2c, 0x0d, 0x86, 0xe1, 0x62, 0x96, 0x96, 0x3d, 0xc3, 0xe1, 0xca, 0x9c, + 0x5b, 0xe4, 0x27, 0xd8, 0xad, 0xc5, 0xc7, 0x60, 0x01, 0xfc, 0xc7, 0x53, 0xe1, 0xeb, 0x15, 0x19, + 0x57, 0xe0, 0x1c, 0x60, 0xa9, 0x61, 0x83, 0x47, 0xcb, 0x01, 0x0d, 0x1e, 0x6d, 0xd1, 0xc7, 0xbf, + 0x3c, 0x00, 0x76, 0x7d, 0x5a, 0x4f, 0xf7, 0x19, 0x5e, 0x5c, 0xdd, 0xde, 0x92, 0xc2, 0x75, 0xd2, + 0x86, 0xeb, 0x92, 0xf8, 0x11, 0x7c, 0xb7, 0x7f, 0xfc, 0x9f, 0xd2, 0x61, 0xd0, 0x4e, 0x54, 0x7c, + 0x26, 0x6f, 0xbf, 0x8d, 0xee, 0x84, 0xf9, 0xfe, 0x70, 0x13, 0x73, 0x39, 0x3f, 0x76, 0xa8, 0xfa, + 0xfb, 0xae, 0x7c, 0x1d, 0x48, 0x1d, 0xdb, 0xc7, 0xe1, 0x66, 0xc7, 0x7e, 0x4e, 0xfe, 0x04, 0x00, + 0x00, 0xff, 0xff, 0x3b, 0x56, 0x68, 0x37, 0x34, 0x04, 0x00, 0x00, +} diff --git a/proto/service_grpc.pb.go b/proto/service_grpc.pb.go deleted file mode 100644 index 1d0066cd3..000000000 --- a/proto/service_grpc.pb.go +++ /dev/null @@ -1,358 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package proto - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion7 - -// RoomServiceClient is the client API for RoomService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type RoomServiceClient interface { - // TODO: how do we secure room service? - // should be accessible to only internal servers, not external - CreateRoom(ctx context.Context, in *CreateRoomRequest, opts ...grpc.CallOption) (*CreateRoomResponse, error) - JoinRoom(ctx context.Context, in *JoinRoomRequest, opts ...grpc.CallOption) (*JoinRoomResponse, error) - DeleteRoom(ctx context.Context, in *DeleteRoomRequest, opts ...grpc.CallOption) (*DeleteRoomResponse, error) -} - -type roomServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewRoomServiceClient(cc grpc.ClientConnInterface) RoomServiceClient { - return &roomServiceClient{cc} -} - -func (c *roomServiceClient) CreateRoom(ctx context.Context, in *CreateRoomRequest, opts ...grpc.CallOption) (*CreateRoomResponse, error) { - out := new(CreateRoomResponse) - err := c.cc.Invoke(ctx, "/livekit.RoomService/CreateRoom", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *roomServiceClient) JoinRoom(ctx context.Context, in *JoinRoomRequest, opts ...grpc.CallOption) (*JoinRoomResponse, error) { - out := new(JoinRoomResponse) - err := c.cc.Invoke(ctx, "/livekit.RoomService/JoinRoom", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *roomServiceClient) DeleteRoom(ctx context.Context, in *DeleteRoomRequest, opts ...grpc.CallOption) (*DeleteRoomResponse, error) { - out := new(DeleteRoomResponse) - err := c.cc.Invoke(ctx, "/livekit.RoomService/DeleteRoom", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// RoomServiceServer is the server API for RoomService service. -// All implementations must embed UnimplementedRoomServiceServer -// for forward compatibility -type RoomServiceServer interface { - // TODO: how do we secure room service? - // should be accessible to only internal servers, not external - CreateRoom(context.Context, *CreateRoomRequest) (*CreateRoomResponse, error) - JoinRoom(context.Context, *JoinRoomRequest) (*JoinRoomResponse, error) - DeleteRoom(context.Context, *DeleteRoomRequest) (*DeleteRoomResponse, error) - mustEmbedUnimplementedRoomServiceServer() -} - -// UnimplementedRoomServiceServer must be embedded to have forward compatible implementations. -type UnimplementedRoomServiceServer struct { -} - -func (UnimplementedRoomServiceServer) CreateRoom(context.Context, *CreateRoomRequest) (*CreateRoomResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateRoom not implemented") -} -func (UnimplementedRoomServiceServer) JoinRoom(context.Context, *JoinRoomRequest) (*JoinRoomResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method JoinRoom not implemented") -} -func (UnimplementedRoomServiceServer) DeleteRoom(context.Context, *DeleteRoomRequest) (*DeleteRoomResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteRoom not implemented") -} -func (UnimplementedRoomServiceServer) mustEmbedUnimplementedRoomServiceServer() {} - -// UnsafeRoomServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to RoomServiceServer will -// result in compilation errors. -type UnsafeRoomServiceServer interface { - mustEmbedUnimplementedRoomServiceServer() -} - -func RegisterRoomServiceServer(s *grpc.Server, srv RoomServiceServer) { - s.RegisterService(&_RoomService_serviceDesc, srv) -} - -func _RoomService_CreateRoom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateRoomRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoomServiceServer).CreateRoom(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/livekit.RoomService/CreateRoom", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoomServiceServer).CreateRoom(ctx, req.(*CreateRoomRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _RoomService_JoinRoom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(JoinRoomRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoomServiceServer).JoinRoom(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/livekit.RoomService/JoinRoom", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoomServiceServer).JoinRoom(ctx, req.(*JoinRoomRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _RoomService_DeleteRoom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteRoomRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoomServiceServer).DeleteRoom(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/livekit.RoomService/DeleteRoom", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoomServiceServer).DeleteRoom(ctx, req.(*DeleteRoomRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _RoomService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "livekit.RoomService", - HandlerType: (*RoomServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "CreateRoom", - Handler: _RoomService_CreateRoom_Handler, - }, - { - MethodName: "JoinRoom", - Handler: _RoomService_JoinRoom_Handler, - }, - { - MethodName: "DeleteRoom", - Handler: _RoomService_DeleteRoom_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "service.proto", -} - -// RTCServiceClient is the client API for RTCService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type RTCServiceClient interface { - Offer(ctx context.Context, in *SessionDescription, opts ...grpc.CallOption) (*SessionDescription, error) - // push channel to allow server to push commands to client - Signal(ctx context.Context, in *SignalRequest, opts ...grpc.CallOption) (RTCService_SignalClient, error) - Trickle(ctx context.Context, in *TrickleRequest, opts ...grpc.CallOption) (*TrickleResponse, error) -} - -type rTCServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewRTCServiceClient(cc grpc.ClientConnInterface) RTCServiceClient { - return &rTCServiceClient{cc} -} - -func (c *rTCServiceClient) Offer(ctx context.Context, in *SessionDescription, opts ...grpc.CallOption) (*SessionDescription, error) { - out := new(SessionDescription) - err := c.cc.Invoke(ctx, "/livekit.RTCService/Offer", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *rTCServiceClient) Signal(ctx context.Context, in *SignalRequest, opts ...grpc.CallOption) (RTCService_SignalClient, error) { - stream, err := c.cc.NewStream(ctx, &_RTCService_serviceDesc.Streams[0], "/livekit.RTCService/Signal", opts...) - if err != nil { - return nil, err - } - x := &rTCServiceSignalClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type RTCService_SignalClient interface { - Recv() (*SignalResponse, error) - grpc.ClientStream -} - -type rTCServiceSignalClient struct { - grpc.ClientStream -} - -func (x *rTCServiceSignalClient) Recv() (*SignalResponse, error) { - m := new(SignalResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *rTCServiceClient) Trickle(ctx context.Context, in *TrickleRequest, opts ...grpc.CallOption) (*TrickleResponse, error) { - out := new(TrickleResponse) - err := c.cc.Invoke(ctx, "/livekit.RTCService/Trickle", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// RTCServiceServer is the server API for RTCService service. -// All implementations must embed UnimplementedRTCServiceServer -// for forward compatibility -type RTCServiceServer interface { - Offer(context.Context, *SessionDescription) (*SessionDescription, error) - // push channel to allow server to push commands to client - Signal(*SignalRequest, RTCService_SignalServer) error - Trickle(context.Context, *TrickleRequest) (*TrickleResponse, error) - mustEmbedUnimplementedRTCServiceServer() -} - -// UnimplementedRTCServiceServer must be embedded to have forward compatible implementations. -type UnimplementedRTCServiceServer struct { -} - -func (UnimplementedRTCServiceServer) Offer(context.Context, *SessionDescription) (*SessionDescription, error) { - return nil, status.Errorf(codes.Unimplemented, "method Offer not implemented") -} -func (UnimplementedRTCServiceServer) Signal(*SignalRequest, RTCService_SignalServer) error { - return status.Errorf(codes.Unimplemented, "method Signal not implemented") -} -func (UnimplementedRTCServiceServer) Trickle(context.Context, *TrickleRequest) (*TrickleResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Trickle not implemented") -} -func (UnimplementedRTCServiceServer) mustEmbedUnimplementedRTCServiceServer() {} - -// UnsafeRTCServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to RTCServiceServer will -// result in compilation errors. -type UnsafeRTCServiceServer interface { - mustEmbedUnimplementedRTCServiceServer() -} - -func RegisterRTCServiceServer(s *grpc.Server, srv RTCServiceServer) { - s.RegisterService(&_RTCService_serviceDesc, srv) -} - -func _RTCService_Offer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SessionDescription) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RTCServiceServer).Offer(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/livekit.RTCService/Offer", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RTCServiceServer).Offer(ctx, req.(*SessionDescription)) - } - return interceptor(ctx, in, info, handler) -} - -func _RTCService_Signal_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(SignalRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(RTCServiceServer).Signal(m, &rTCServiceSignalServer{stream}) -} - -type RTCService_SignalServer interface { - Send(*SignalResponse) error - grpc.ServerStream -} - -type rTCServiceSignalServer struct { - grpc.ServerStream -} - -func (x *rTCServiceSignalServer) Send(m *SignalResponse) error { - return x.ServerStream.SendMsg(m) -} - -func _RTCService_Trickle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TrickleRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RTCServiceServer).Trickle(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/livekit.RTCService/Trickle", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RTCServiceServer).Trickle(ctx, req.(*TrickleRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _RTCService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "livekit.RTCService", - HandlerType: (*RTCServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Offer", - Handler: _RTCService_Offer_Handler, - }, - { - MethodName: "Trickle", - Handler: _RTCService_Trickle_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "Signal", - Handler: _RTCService_Signal_Handler, - ServerStreams: true, - }, - }, - Metadata: "service.proto", -} diff --git a/wire.go b/wire.go new file mode 100644 index 000000000..bfff7a962 --- /dev/null +++ b/wire.go @@ -0,0 +1,19 @@ +//+build wireinject + +package main + +import ( + "github.com/google/wire" + "github.com/livekit/livekit-server/pkg/config" + "github.com/livekit/livekit-server/pkg/node" + "github.com/livekit/livekit-server/pkg/service" +) + +func InitializeServer(conf *config.Config) (*LivekitServer, error) { + wire.Build( + NewLivekitServer, + node.NodeSet, + service.ServiceSet, + ) + return &LivekitServer{}, nil +} diff --git a/wire_gen.go b/wire_gen.go new file mode 100644 index 000000000..d18c4382e --- /dev/null +++ b/wire_gen.go @@ -0,0 +1,31 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate wire +//+build !wireinject + +package main + +import ( + "github.com/livekit/livekit-server/pkg/config" + "github.com/livekit/livekit-server/pkg/node" + "github.com/livekit/livekit-server/pkg/service" +) + +// Injectors from wire.go: + +func InitializeServer(conf *config.Config) (*LivekitServer, error) { + nodeNode, err := node.NewLocalNode() + if err != nil { + return nil, err + } + roomService, err := service.NewRoomService(conf, nodeNode) + if err != nil { + return nil, err + } + rtcService := service.NewRTCService() + livekitServer, err := NewLivekitServer(conf, roomService, rtcService) + if err != nil { + return nil, err + } + return livekitServer, nil +}