Compare commits

...

922 commits

Author SHA1 Message Date
Joachim Bauch fd77de0e02
Merge pull request #752 from strukturag/improve-reload
Support reloading more settings
2024-05-28 14:32:53 +02:00
Joachim Bauch c17882307f
proxy: Support reloading target bandwidth. 2024-05-28 13:19:58 +02:00
Joachim Bauch c7cccc9287
Support reloading maximum stream / screen bandwidths. 2024-05-28 12:33:12 +02:00
Joachim Bauch 15edeca814
Support reloading GeoIP overrides. 2024-05-28 12:26:05 +02:00
Joachim Bauch 2a1fd2e018
Support reloading allowed stats IPs. 2024-05-28 12:18:08 +02:00
Joachim Bauch be66d9425b
Support reloading trusted proxies. 2024-05-28 12:11:39 +02:00
Joachim Bauch b033e07e06
Merge pull request #751 from strukturag/grpc-listen-env
grpc: Replace environment variables in listening address.
2024-05-28 12:06:46 +02:00
Joachim Bauch b47a112e7e
grpc: Replace environment variables in listening address. 2024-05-28 11:54:14 +02:00
Joachim Bauch 6c62f9caae
Merge pull request #750 from strukturag/throttle-ipv6
Throttle /64 subnets for IPv6.
2024-05-28 09:37:56 +02:00
Joachim Bauch 8b39217551
Throttle /64 subnets for IPv6. 2024-05-27 09:15:55 +02:00
Joachim Bauch de3507690c
Add IPv6 tests for "GetRealUserIP". 2024-05-27 09:08:42 +02:00
Joachim Bauch 8123be9551
Update changelog for 1.3.1 2024-05-23 21:18:48 +02:00
Joachim Bauch cad442c486
Merge pull request #747 from strukturag/improve-real-ip
Improve detection of actual client IP.
2024-05-23 21:16:16 +02:00
Joachim Bauch e8ebfed711
Merge pull request #749 from strukturag/docker-janus
docker: Update Janus in example image to 1.2.2
2024-05-23 10:18:35 +02:00
Joachim Bauch 8d8ec677f1
CI: Disable Janus "--version" check temporarily in example image.
Needs https://github.com/meetecho/janus-gateway/issues/3383 to be resolved.
2024-05-23 10:16:07 +02:00
Joachim Bauch 80d96916b9
docker: Compile example image on all cores. 2024-05-23 10:07:58 +02:00
Joachim Bauch 8a0ce7c9b6
docker: Update libsrtp in example image to 2.6.0 2024-05-23 10:05:56 +02:00
Joachim Bauch 1952bfc2be
docker: Update Janus in example image to 1.2.2 2024-05-23 10:03:43 +02:00
Joachim Bauch b3d2f7b02c
Merge pull request #748 from strukturag/ci-lint-deprecated-options
CI: Remove deprecated options from lint workflow.
2024-05-23 09:40:10 +02:00
Joachim Bauch 7583fb6486
CI: Remove deprecated options from lint workflow. 2024-05-23 09:37:44 +02:00
Joachim Bauch 040e663b37
Add examples on how to set "X-Real-IP" for Apache and Caddy. 2024-05-23 09:32:10 +02:00
Joachim Bauch 15b1214413
Add note that "X-Real-Ip" will take precedence. 2024-05-23 09:20:08 +02:00
Joachim Bauch 05810e10ce
Improve detection of actual client IP.
Based on recommendations from MDN.
2024-05-23 09:16:25 +02:00
Joachim Bauch 7e7a04ad6c
Merge pull request #746 from strukturag/dependabot/docker/docker/janus/alpine-3.20
Bump alpine from 3.19 to 3.20 in /docker/janus
2024-05-23 07:50:48 +02:00
dependabot[bot] d25169d0ff
Bump alpine from 3.19 to 3.20 in /docker/janus
Bumps alpine from 3.19 to 3.20.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-22 20:08:31 +00:00
Joachim Bauch 79b76b1ca4
Merge pull request #745 from strukturag/shellcheck
docker: Fix proxy entrypoint.
2024-05-22 14:12:50 +02:00
Joachim Bauch f8e37a1bca
docker: Add missing "fi" in proxy entrypoint. 2024-05-22 14:09:52 +02:00
Joachim Bauch b5cbb917c5
Fix shellcheck errors and make executable. 2024-05-22 14:09:52 +02:00
Joachim Bauch e2ac08ae67
CI: Run shellcheck on scripts. 2024-05-22 14:09:51 +02:00
Joachim Bauch 00d17bae97
Update changelog for 1.3.0 2024-05-22 11:05:10 +02:00
Joachim Bauch ff69a294a9
Add note on remote streams. 2024-05-22 10:59:34 +02:00
Joachim Bauch 5790e7a369
Merge pull request #744 from strukturag/backend-throttle
Add throttler for backend requests
2024-05-22 10:39:50 +02:00
Joachim Bauch 4c807c86e8
Throttle resume / internal hello. 2024-05-22 10:35:11 +02:00
Joachim Bauch e862392872
Add throttled requests to metrics. 2024-05-22 10:35:09 +02:00
Joachim Bauch 39f4b2eb11
server: Increase default write timeout so delayed responses can be sent out. 2024-05-22 10:34:29 +02:00
Joachim Bauch 7f8e44b3b5
Add bruteforce detection to backend server room handler. 2024-05-22 10:34:29 +02:00
Joachim Bauch 31b8c74d1c
Add throttler class. 2024-05-22 10:34:25 +02:00
Joachim Bauch 5f18913646
Merge pull request #743 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.16
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.15 to 2.10.16
2024-05-22 07:42:47 +02:00
dependabot[bot] 716a93538b
---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 20:19:36 +00:00
Joachim Bauch 2cd3418f09
Merge pull request #708 from strukturag/proxy-features
Add support for remote streams
2024-05-21 09:49:47 +02:00
Joachim Bauch c6cbe88d0e
Pass contexts when creating / starting MCUs. 2024-05-21 09:29:23 +02:00
Joachim Bauch f73ad7b508
Add tests for publishers on different hubs. 2024-05-21 09:29:23 +02:00
Joachim Bauch efb722a55e
Use interface instead of concrete "Hub" class for GRPC server. 2024-05-21 09:29:22 +02:00
Joachim Bauch d63b1cf14a
proxy: Add more token tests. 2024-05-21 09:29:22 +02:00
Joachim Bauch 75060b25aa
Add testcase for subscriber in different country but same continent. 2024-05-21 09:29:21 +02:00
Joachim Bauch 7e7a6d5c09
Support bandwidth limits when selecting proxy to use. 2024-05-21 09:29:20 +02:00
Joachim Bauch a4b8a81734
Automatically reconnect proxy connections if interrupted. 2024-05-21 09:29:19 +02:00
Joachim Bauch 3ce963ee91
Re-create publisher with new endpoint if it already exists. 2024-05-21 09:29:19 +02:00
Joachim Bauch 24c1a09662
Add methods to unpublish remotely. 2024-05-21 09:29:18 +02:00
Joachim Bauch 56f5a72f61
Get list of remote streams from offer/answer SDP. 2024-05-21 09:29:17 +02:00
Joachim Bauch a66c1d82bf
Move Janus classes to separate files, no functional changes. 2024-05-21 09:29:17 +02:00
Joachim Bauch d9deddfda7
Move remote classes to separate files and add event handlers. 2024-05-21 09:29:16 +02:00
Joachim Bauch 9c99129242
Make "skipverify" configurable for remote proxy requests. 2024-05-21 09:29:15 +02:00
Joachim Bauch 63c42dd84c
First draft of remote subscriber streams. 2024-05-21 09:29:15 +02:00
Joachim Bauch 92cbc28065
Add basic tests for mcu proxy client. 2024-05-21 09:29:14 +02:00
Joachim Bauch 132cf0d474
Add "String()" method to messages to help with debugging. 2024-05-21 09:29:11 +02:00
Joachim Bauch 4fd929c15a
Merge pull request #733 from strukturag/relax-message-validation
Relax "MessageClientMessageData" validation.
2024-05-21 09:28:50 +02:00
Joachim Bauch 879469df19
Merge pull request #741 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.15
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.14 to 2.10.15
2024-05-21 09:27:59 +02:00
Joachim Bauch fe0a002adf
Merge pull request #739 from strukturag/rawmessage-pointer
Don't use unnecessary pointer to "json.RawMessage".
2024-05-21 09:27:31 +02:00
dependabot[bot] 7b555e91ec
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.14 to 2.10.15.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/commits)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 07:21:47 +00:00
Joachim Bauch b2afa88bcc
Merge pull request #740 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.35.0
build(deps): Bump github.com/nats-io/nats.go from 1.34.1 to 1.35.0
2024-05-21 09:20:59 +02:00
Joachim Bauch 1bbc49351a
Merge pull request #742 from strukturag/leave-room-lock-order-inversion
Fix lock order inversion when leaving room / publishing room sessions.
2024-05-21 09:20:34 +02:00
Joachim Bauch dff78d0101
Fix lock order inversion when leaving room / publishing room sessions.
Deadlock could happen between

1 @ 0x44038e 0x451898 0x45186f 0x46f325 0x489f3d 0xbb7b76 0xbb7b45 0xc1fe52 0xc190f7 0x473461
0x46f324	sync.runtime_SemacquireMutex+0x24							/usr/lib/go-1.21/src/runtime/sema.go:77
0x489f3c	sync.(*Mutex).lockSlow+0x15c								/usr/lib/go-1.21/src/sync/mutex.go:171
0xbb7b75	sync.(*Mutex).Lock+0x55									/usr/lib/go-1.21/src/sync/mutex.go:90
0xbb7b44	github.com/strukturag/nextcloud-spreed-signaling.(*ClientSession).RoomSessionId+0x24	/build/nextcloud-spreed-signaling-1.2.3/clientsession.go:157
0xc1fe51	github.com/strukturag/nextcloud-spreed-signaling.(*Room).publishActiveSessions+0x231	/build/nextcloud-spreed-signaling-1.2.3/room.go:925
0xc190f6	github.com/strukturag/nextcloud-spreed-signaling.(*Room).run+0x36			/build/nextcloud-spreed-signaling-1.2.3/room.go:179

(which locks "mu" in the room and then "mu" in the client session) and

1 @ 0x44038e 0x451898 0x45186f 0x46f3e5 0x48b44a 0xc1ba76 0xbba37e 0xbe2aab 0xbdf8e5 0xbee0f8 0xbb6134 0x473461
0x46f3e4	sync.runtime_SemacquireRWMutex+0x24							/usr/lib/go-1.21/src/runtime/sema.go:87
0x48b449	sync.(*RWMutex).Lock+0x69								/usr/lib/go-1.21/src/sync/rwmutex.go:152
0xc1ba75	github.com/strukturag/nextcloud-spreed-signaling.(*Room).RemoveSession+0x35		/build/nextcloud-spreed-signaling-1.2.3/room.go:440
0xbba37d	github.com/strukturag/nextcloud-spreed-signaling.(*ClientSession).LeaveRoom+0xdd	/build/nextcloud-spreed-signaling-1.2.3/clientsession.go:489
0xbe2aaa	github.com/strukturag/nextcloud-spreed-signaling.(*Hub).processRoom+0x6a		/build/nextcloud-spreed-signaling-1.2.3/hub.go:1268
0xbdf8e4	github.com/strukturag/nextcloud-spreed-signaling.(*Hub).processMessage+0x984		/build/nextcloud-spreed-signaling-1.2.3/hub.go:909
0xbee0f7	github.com/strukturag/nextcloud-spreed-signaling.(*Hub).OnMessageReceived+0x17		/build/nextcloud-spreed-signaling-1.2.3/hub.go:2427
0xbb6133	github.com/strukturag/nextcloud-spreed-signaling.(*Client).processMessages+0x53		/build/nextcloud-spreed-signaling-1.2.3/client.go:347

(which locks "mu" in the client session and then "mu" in the room).
2024-05-21 09:09:10 +02:00
dependabot[bot] 2ad2327090
build(deps): Bump github.com/nats-io/nats.go from 1.34.1 to 1.35.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.34.1 to 1.35.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.34.1...v1.35.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-17 20:42:38 +00:00
Joachim Bauch 4b76a49355
Don't use unnecessary pointer to "json.RawMessage".
The slice is a pointer already.
2024-05-16 20:58:42 +02:00
Joachim Bauch f6125dac3f
docker: Make trusted proxies configurable.
Follow-up to #738
2024-05-16 16:31:08 +02:00
Joachim Bauch c2e93cd92a
Merge pull request #738 from strukturag/trusted-proxies
Make trusted proxies configurable and default to loopback / private IPs.
2024-05-16 16:24:44 +02:00
Joachim Bauch 4f8349d4c1
Update tests. 2024-05-16 14:51:28 +02:00
Joachim Bauch aac4874e72
Make trusted proxies configurable and default to loopback / private IPs. 2024-05-16 14:44:00 +02:00
Joachim Bauch 936f83feb9
Merge pull request #693 from strukturag/dependabot/go_modules/etcd-a88448dd84
build(deps): Bump the etcd group with 4 updates
2024-05-16 13:32:20 +02:00
dependabot[bot] c1e9e02087
build(deps): Bump the etcd group with 4 updates
Bumps the etcd group with 4 updates: [go.etcd.io/etcd/api/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/pkg/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) and [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd).


Updates `go.etcd.io/etcd/api/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

Updates `go.etcd.io/etcd/client/pkg/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

Updates `go.etcd.io/etcd/client/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

Updates `go.etcd.io/etcd/server/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/pkg/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-16 11:23:55 +00:00
Joachim Bauch beee423a7c
Merge pull request #694 from strukturag/ci-govuln-check
CI: Run "govulncheck".
2024-05-16 13:22:39 +02:00
Joachim Bauch 5a85fecb10
CI: Run "govulncheck". 2024-05-16 13:21:05 +02:00
Joachim Bauch 88575abea2
Merge pull request #737 from strukturag/remove-golang-1.20
Drop support for Golang 1.20
2024-05-16 13:19:46 +02:00
Joachim Bauch fdc43d12cd
Use new builtin "clear" to remove map entries. 2024-05-16 13:14:56 +02:00
Joachim Bauch d03ea86991
Function "min" is builtin with Go 1.21 2024-05-16 11:23:57 +02:00
Joachim Bauch 18300ce89e
Drop support for Golang 1.20 2024-05-16 11:17:06 +02:00
Joachim Bauch d8f2f265ab
Merge pull request #736 from strukturag/log-mcu-proxy-client-closed
Log something if mcu publisher / subscriber was closed.
2024-05-16 10:37:08 +02:00
Joachim Bauch ddbf1065f6
Merge pull request #707 from strukturag/validate-received-sdp
Validate received SDP earlier.
2024-05-16 10:19:15 +02:00
Joachim Bauch bad52af35a
Validate received SDP earlier. 2024-05-16 10:04:57 +02:00
Joachim Bauch c58564c0e8
Log something if mcu publisher / subscriber was closed. 2024-05-16 09:44:47 +02:00
Joachim Bauch 0b259a8171
Merge pull request #732 from strukturag/close-context
Add Context to clients / sessions.
2024-05-16 09:36:34 +02:00
Joachim Bauch 3fc5f5253d
Merge pull request #735 from strukturag/read-error-after-close
Don't log read error after we closed the connection.
2024-05-16 09:36:07 +02:00
Joachim Bauch 3e92664edc
Don't log read error after we closed the connection. 2024-05-16 09:23:32 +02:00
Joachim Bauch 0ee976d377
Add Context to clients / sessions.
The Context will be closed when the client disconnects / the session is removed,
so any pending requests can be cancelled.
2024-05-16 09:07:59 +02:00
Joachim Bauch 552474f6f0
Merge pull request #734 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.64.0
build(deps): Bump google.golang.org/grpc from 1.63.2 to 1.64.0
2024-05-16 08:51:38 +02:00
dependabot[bot] 09e010ee14
build(deps): Bump google.golang.org/grpc from 1.63.2 to 1.64.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.63.2 to 1.64.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.63.2...v1.64.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-15 20:58:48 +00:00
Joachim Bauch 70a5318973
Relax "MessageClientMessageData" validation.
Allow empty `roomType` values.
2024-05-15 13:12:25 +02:00
Joachim Bauch 94a8f0f02b
test: Reset logging to global defaults on cleanup. 2024-05-14 16:52:46 +02:00
Joachim Bauch 4603b2b290
test: Make sure tests that change global state are not executed concurrently. 2024-05-14 16:51:20 +02:00
Joachim Bauch a50d637107
etcd: Wait for server to be stopped in tests. 2024-05-14 16:13:13 +02:00
Joachim Bauch 307ffdc29a
Merge pull request #721 from strukturag/config-envvars
Support environment variables in some configuration.
2024-05-14 14:25:27 +02:00
Joachim Bauch ec3ac62474
Merge pull request #729 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-6.0.1
build(deps): Bump golangci/golangci-lint-action from 5.1.0 to 6.0.1
2024-05-14 13:33:30 +02:00
Joachim Bauch e3a163fbe5
Support environment variables in URL / listener configuration. 2024-05-13 13:26:38 +02:00
Joachim Bauch cf36530b30
Add function to resolve environment variables in config values. 2024-05-13 13:26:35 +02:00
Joachim Bauch adc72aa578
Merge pull request #731 from strukturag/capabilities-race
Fix potential race in capabilities test.
2024-05-13 13:25:34 +02:00
Joachim Bauch ea0d31b0dc
Fix potential race in capabilities test. 2024-05-13 13:16:49 +02:00
Joachim Bauch 5b305f6f99
Merge pull request #730 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.19.1
build(deps): Bump github.com/prometheus/client_golang from 1.19.0 to 1.19.1
2024-05-13 08:56:20 +02:00
Joachim Bauch 3c923a9ef9
Merge pull request #725 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.34.1
build(deps): Bump google.golang.org/protobuf from 1.33.0 to 1.34.1
2024-05-13 08:55:55 +02:00
Joachim Bauch 1a692bc4bb
Merge pull request #726 from strukturag/dependabot/pip/docs/jinja2-3.1.4
build(deps): Bump jinja2 from 3.1.3 to 3.1.4 in /docs
2024-05-13 08:54:38 +02:00
Joachim Bauch 6a495bfc5c
Merge pull request #728 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.3.0
build(deps): Bump coverallsapp/github-action from 2.2.3 to 2.3.0
2024-05-13 08:53:48 +02:00
dependabot[bot] 9a91e885cf
build(deps): Bump github.com/prometheus/client_golang
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.19.0 to 1.19.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.19.0...v1.19.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-09 20:55:05 +00:00
dependabot[bot] b4830b1fd3
build(deps): Bump golangci/golangci-lint-action from 5.1.0 to 6.0.1
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 5.1.0 to 6.0.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v5.1.0...v6.0.1)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-08 20:37:17 +00:00
dependabot[bot] 16da87106a
build(deps): Bump coverallsapp/github-action from 2.2.3 to 2.3.0
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.2.3 to 2.3.0.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v2.2.3...v2.3.0)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-08 20:37:13 +00:00
dependabot[bot] e763f4519c
build(deps): Bump jinja2 from 3.1.3 to 3.1.4 in /docs
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 20:40:53 +00:00
dependabot[bot] bfb185f382
build(deps): Bump google.golang.org/protobuf from 1.33.0 to 1.34.1
Bumps google.golang.org/protobuf from 1.33.0 to 1.34.1.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 20:36:29 +00:00
Joachim Bauch 46e8ea9148
Merge pull request #722 from strukturag/docker-graceful-stop
docker: Add helper scripts to gracefully stop / wait for server.
2024-04-30 12:01:38 +02:00
Joachim Bauch 4eb1b6609d
Merge pull request #720 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-5.1.0
build(deps): Bump golangci/golangci-lint-action from 5.0.0 to 5.1.0
2024-04-30 11:58:27 +02:00
Joachim Bauch 815088f269
docker: Add helper scripts to gracefully stop / wait for server. 2024-04-30 11:57:58 +02:00
dependabot[bot] 527061bbe2
build(deps): Bump golangci/golangci-lint-action from 5.0.0 to 5.1.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 5.0.0 to 5.1.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v5.0.0...v5.1.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 20:31:34 +00:00
Joachim Bauch a2f0bec564
Merge pull request #719 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-5.0.0
build(deps): Bump golangci/golangci-lint-action from 4.0.0 to 5.0.0
2024-04-29 08:22:06 +02:00
dependabot[bot] 70f0519ca2
build(deps): Bump golangci/golangci-lint-action from 4.0.0 to 5.0.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 4.0.0 to 5.0.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v4.0.0...v5.0.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-25 20:47:19 +00:00
Joachim Bauch 9e2a896326
Catch log of embedded etcd in tests (follow-up to #718). 2024-04-25 16:07:51 +02:00
Joachim Bauch 2d48018b58
Catch log in GeoIP tests (follow-up to #718). 2024-04-25 15:47:28 +02:00
Joachim Bauch cf19b3b1b4
Merge pull request #718 from strukturag/speedup-tests
Speedup tests by running in parallel
2024-04-25 15:27:10 +02:00
Joachim Bauch ebb215c592
make: Don't run tests verbose by default. 2024-04-25 15:22:24 +02:00
Joachim Bauch 0eb234b24d
Run tests in parallel and catch log output from tests. 2024-04-25 15:21:54 +02:00
Joachim Bauch cad397e59e
Merge pull request #706 from strukturag/graceful-shutdown
Gracefully shut down signaling server on SIGUSR1.
2024-04-23 12:43:06 +02:00
Joachim Bauch f8899ef189
Add mutex for "handler" in client.
Fix flaky race as follow-up to #715
2024-04-23 12:42:31 +02:00
Joachim Bauch 54c4f1847a
Gracefully shut down signaling server on SIGUSR1.
This will wait for all non-internal sessions to be removed / expired
but stop accepting new connections.
2024-04-23 12:25:33 +02:00
Joachim Bauch d368a060fa
Merge pull request #715 from strukturag/resume-remote
Support resuming remote sessions
2024-04-23 11:58:07 +02:00
Joachim Bauch 602452fa25
Support resuming sessions that exist on a different Hub in the cluster. 2024-04-23 11:52:43 +02:00
Joachim Bauch 0c2cefa63a
Don't return "false" if message sending closed the connection. 2024-04-23 11:09:04 +02:00
Joachim Bauch 2468443572
Add "HandlerClient" interface to support custom implementations. 2024-04-23 11:03:30 +02:00
Joachim Bauch 3721fb131f
Don't include empty "auth" field in hello client messages. 2024-04-23 11:03:28 +02:00
Joachim Bauch 6960912681
Merge pull request #716 from strukturag/leak-grpc-goroutines
Prevent goroutine leaks in GRPC tests.
2024-04-23 10:59:23 +02:00
Joachim Bauch b77525603c
Enable goroutine leak checks for more tests. 2024-04-23 10:53:55 +02:00
Joachim Bauch 9adb762ccf
Close file watcher on shutdown to prevent goroutine leaks. 2024-04-23 10:53:28 +02:00
Joachim Bauch bf68a15943
Make sure "clientsMap" is updated so all clients are closed on shutdown. 2024-04-23 10:37:15 +02:00
Joachim Bauch bc7aea68f3
Merge pull request #714 from strukturag/dependabot/pip/docs/mkdocs-1.6.0
build(deps): Bump mkdocs from 1.5.3 to 1.6.0 in /docs
2024-04-23 08:20:27 +02:00
dependabot[bot] 69beea84cb
build(deps): Bump mkdocs from 1.5.3 to 1.6.0 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.5.3 to 1.6.0.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.5.3...1.6.0)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 20:25:03 +00:00
Joachim Bauch 952b8ae460
Merge pull request #713 from strukturag/session-expiration
Don't keep expiration timestamp in each session.
2024-04-22 15:19:16 +02:00
Joachim Bauch 2e6cf7f86b
Don't keep expiration timestamp in each session.
Reduces memory size per session and make hub lock usage consistent.
2024-04-22 15:07:48 +02:00
Joachim Bauch dcec32be7e
Merge pull request #711 from strukturag/dependabot/go_modules/golang.org/x/net-0.23.0
build(deps): Bump golang.org/x/net from 0.21.0 to 0.23.0
2024-04-22 10:38:14 +02:00
Joachim Bauch b0d052c6ec
Merge pull request #712 from strukturag/dependabot/pip/docs/sphinx-7.3.7
build(deps): Bump sphinx from 7.3.5 to 7.3.7 in /docs
2024-04-22 10:38:00 +02:00
dependabot[bot] 318ed3700f
build(deps): Bump sphinx from 7.3.5 to 7.3.7 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.3.5 to 7.3.7.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.3.5...v7.3.7)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-19 20:12:57 +00:00
dependabot[bot] ee16a8d8be
build(deps): Bump golang.org/x/net from 0.21.0 to 0.23.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.21.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.21.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-19 13:16:11 +00:00
Joachim Bauch 91033bf8c2
Merge pull request #709 from strukturag/dependabot/pip/docs/sphinx-7.3.5
build(deps): Bump sphinx from 7.2.6 to 7.3.5 in /docs
2024-04-18 11:22:56 +02:00
dependabot[bot] b541ebc4c6
build(deps): Bump sphinx from 7.2.6 to 7.3.5 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.2.6 to 7.3.5.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.2.6...v7.3.5)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-17 20:24:44 +00:00
Joachim Bauch 0aed690463
Merge pull request #669 from strukturag/janus-multistream
Improve support for Janus 1.x
2024-04-16 16:23:58 +02:00
Joachim Bauch 71a4248568
Merge pull request #705 from strukturag/dependabot/go_modules/go.uber.org/zap-1.27.0
build(deps): Bump go.uber.org/zap from 1.17.0 to 1.27.0
2024-04-16 14:29:15 +02:00
dependabot[bot] df210a6a85
build(deps): Bump go.uber.org/zap from 1.17.0 to 1.27.0
Bumps [go.uber.org/zap](https://github.com/uber-go/zap) from 1.17.0 to 1.27.0.
- [Release notes](https://github.com/uber-go/zap/releases)
- [Changelog](https://github.com/uber-go/zap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber-go/zap/compare/v1.17.0...v1.27.0)

---
updated-dependencies:
- dependency-name: go.uber.org/zap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-15 20:22:57 +00:00
Joachim Bauch 5bc9ada233
Merge pull request #704 from strukturag/etcd-prev-value
Include previous value with etcd watch events.
2024-04-15 12:07:37 +02:00
Joachim Bauch d0d68f0d21
Include previous value with etcd watch events. 2024-04-15 11:57:52 +02:00
Joachim Bauch 9a892a194e
Merge pull request #701 from strukturag/etcd-watch-updates
Update etcd watch handling.
2024-04-15 08:58:46 +02:00
Joachim Bauch 26102e7acb
Backoff when retrying watch. 2024-04-15 08:37:52 +02:00
Joachim Bauch 88a575c36c
Cancel GRPC self-check if client is closed. 2024-04-15 08:37:52 +02:00
Joachim Bauch fdab3db819
Update etcd watch handling.
- Properly cancel watch if object is closed.
- Retry watch if interupted.
- Pass revision to watch to not miss changes.
2024-04-15 08:37:52 +02:00
Joachim Bauch c8aa4c71e0
Merge pull request #702 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.14
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.12 to 2.10.14
2024-04-15 08:29:24 +02:00
Joachim Bauch ec9e44f5d6
Merge pull request #700 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.63.2
build(deps): Bump google.golang.org/grpc from 1.63.0 to 1.63.2
2024-04-15 08:29:13 +02:00
dependabot[bot] 543a85f8aa
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.12 to 2.10.14.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.12...v2.10.14)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-12 20:31:46 +00:00
dependabot[bot] 9f104cb281
build(deps): Bump google.golang.org/grpc from 1.63.0 to 1.63.2
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.63.0 to 1.63.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.63.0...v1.63.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-09 20:22:09 +00:00
Joachim Bauch 4e623a8e08
Merge pull request #699 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.63.0
build(deps): Bump google.golang.org/grpc from 1.62.1 to 1.63.0
2024-04-08 11:57:28 +02:00
Joachim Bauch 9ba5b4330a
Switch to "grpc.NewClient" from deprecated "grpc.Dial". 2024-04-08 11:50:15 +02:00
dependabot[bot] 4b6a4dbfe1
build(deps): Bump google.golang.org/grpc from 1.62.1 to 1.63.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.62.1 to 1.63.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.62.1...v1.63.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 20:04:08 +00:00
Joachim Bauch e1f40a024e
Merge pull request #698 from strukturag/filewatcher-rename
Improve detecting renames in file watcher.
2024-04-04 09:55:55 +02:00
Joachim Bauch 47fc6694ca
Merge pull request #697 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.34.1
build(deps): Bump github.com/nats-io/nats.go from 1.34.0 to 1.34.1
2024-04-04 09:48:07 +02:00
Joachim Bauch d0c711b500
Improve detecting renames in file watcher. 2024-04-04 09:47:59 +02:00
dependabot[bot] 7dc450350b
build(deps): Bump github.com/nats-io/nats.go from 1.34.0 to 1.34.1
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.34.0 to 1.34.1.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.34.0...v1.34.1)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 20:20:26 +00:00
Joachim Bauch b2c6bd320b
Update changelog for 1.2.4 2024-04-03 11:06:12 +02:00
Joachim Bauch 4f26d6e2a5
Merge pull request #696 from strukturag/update-readme
Remove deprecated section on multiple signaling servers from README.
2024-04-03 10:53:05 +02:00
Joachim Bauch ddfd976627
Remove deprecated section on multiple signaling servers from README.
This is covered by the "Clustering" section above.
2024-04-03 10:46:40 +02:00
Joachim Bauch 879e1ca5b0
Merge pull request #695 from strukturag/ci-docker-paths
CI: Limit when to run Docker build jobs.
2024-04-03 10:43:43 +02:00
Joachim Bauch 0b698556d6
CI: Only run deploy jobs on relevant PRs. 2024-04-03 10:40:59 +02:00
Joachim Bauch ec96256f29
CI: Limit when to run Docker build jobs. 2024-04-03 10:38:29 +02:00
Joachim Bauch 8d60f81969
Merge pull request #692 from strukturag/ci-check-dependencies
CI: Check dependencies for minimum supported version.
2024-04-03 10:14:54 +02:00
Joachim Bauch 280c2681be
CI: Check dependencies for minimum supported version. 2024-04-03 10:12:19 +02:00
Joachim Bauch 9c7b38d4ff
Merge pull request #691 from strukturag/revert-686-dependabot/go_modules/etcd-a88448dd84
Revert "build(deps): Bump the etcd group with 4 updates"
2024-04-03 10:11:33 +02:00
Joachim Bauch 283da1436a
Revert "build(deps): Bump the etcd group with 4 updates" 2024-04-03 10:01:38 +02:00
Joachim Bauch fdfeeefa39
Update dependencies in go.mod
Package github.com/fsnotify/fsnotify is now directly required.
Follow-up to #680
2024-04-03 09:53:10 +02:00
Joachim Bauch f2bcc000ae
Merge pull request #680 from strukturag/file-watcher
Use fsnotify to detect file changes
2024-04-03 09:51:16 +02:00
Joachim Bauch 2ef9b39959
Update tests to wait for certificate / pool reload to happen before continuing. 2024-04-03 09:41:38 +02:00
Joachim Bauch 1358285c4a
Merge pull request #690 from strukturag/flaky-dnsmonitor-test
Fix flaky DnsMonitor test.
2024-04-03 09:41:33 +02:00
Joachim Bauch 68528d4674
Fix flaky DnsMonitor test. 2024-04-03 09:05:40 +02:00
Joachim Bauch cc7625c544
Use new file watcher to detect changed files. 2024-04-02 17:18:54 +02:00
Joachim Bauch c325fbeae6
Add file watcher class. 2024-04-02 17:18:52 +02:00
Joachim Bauch c859064a45
Merge pull request #685 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.34.0
build(deps): Bump github.com/nats-io/nats.go from 1.33.1 to 1.34.0
2024-04-02 17:18:14 +02:00
Joachim Bauch 2f31532ee2
Merge pull request #686 from strukturag/dependabot/go_modules/etcd-a88448dd84
build(deps): Bump the etcd group with 4 updates
2024-04-02 17:18:04 +02:00
dependabot[bot] d97b071ccf
build(deps): Bump github.com/nats-io/nats.go from 1.33.1 to 1.34.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.33.1 to 1.34.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.33.1...v1.34.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 15:09:12 +00:00
dependabot[bot] 95e2bc10d4
build(deps): Bump the etcd group with 4 updates
Bumps the etcd group with 4 updates: [go.etcd.io/etcd/api/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/pkg/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) and [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd).


Updates `go.etcd.io/etcd/api/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

Updates `go.etcd.io/etcd/client/pkg/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

Updates `go.etcd.io/etcd/client/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

Updates `go.etcd.io/etcd/server/v3` from 3.5.12 to 3.5.13
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.12...v3.5.13)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/pkg/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 15:08:30 +00:00
Joachim Bauch 66dc55a3a5
Merge pull request #687 from strukturag/dependabot/go_modules/github.com/pion/sdp/v3-3.0.9
build(deps): Bump github.com/pion/sdp/v3 from 3.0.8 to 3.0.9
2024-04-02 17:07:22 +02:00
dependabot[bot] 74944ee547
build(deps): Bump github.com/pion/sdp/v3 from 3.0.8 to 3.0.9
Bumps [github.com/pion/sdp/v3](https://github.com/pion/sdp) from 3.0.8 to 3.0.9.
- [Release notes](https://github.com/pion/sdp/releases)
- [Changelog](https://github.com/pion/sdp/blob/master/.goreleaser.yml)
- [Commits](https://github.com/pion/sdp/compare/v3.0.8...v3.0.9)

---
updated-dependencies:
- dependency-name: github.com/pion/sdp/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 15:00:45 +00:00
Joachim Bauch 61b8a91749
Merge pull request #689 from strukturag/db-ip-geoip
Support getting GeoIP DB from db-ip.com for tests.
2024-04-02 16:59:20 +02:00
Joachim Bauch 886ad912da
CI: Use db-ip.com for tests for now.
Ideally should test both with MaxMind and db-ip and make MaxMind optional
(which can happen due to their download quota).
2024-04-02 16:53:35 +02:00
Joachim Bauch 3b4699c11e
CI: Use db-ip.com for tarball tests. 2024-04-02 16:52:06 +02:00
Joachim Bauch 7844a9c21a
Support getting GeoIP DB from db-ip.com for tests. 2024-04-02 16:50:15 +02:00
Joachim Bauch f8eae0b71f
Merge pull request #683 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.12
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.11 to 2.10.12
2024-03-18 09:07:35 +01:00
Joachim Bauch 0b7c17e083
Merge pull request #684 from strukturag/dependabot/pip/docs/markdown-3.6
build(deps): Bump markdown from 3.5.2 to 3.6 in /docs
2024-03-18 09:07:15 +01:00
dependabot[bot] c2eb3a8a27
build(deps): Bump markdown from 3.5.2 to 3.6 in /docs
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.5.2 to 3.6.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.5.2...3.6)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-14 20:40:09 +00:00
dependabot[bot] 010914eed9
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.11 to 2.10.12.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.11...v2.10.12)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 20:22:46 +00:00
Joachim Bauch 1a93c42c38
Merge pull request #682 from strukturag/continentmap-source
Update source of continentmap to original CSV file.
2024-03-11 10:49:19 +01:00
Joachim Bauch 3ba1853e5a
Merge pull request #681 from strukturag/dependabot/go_modules/github.com/pion/sdp/v3-3.0.8
build(deps): Bump github.com/pion/sdp/v3 from 3.0.7 to 3.0.8
2024-03-11 10:43:36 +01:00
Joachim Bauch bbdd991f05
CI: Check continentmap if related files are changed. 2024-03-11 10:42:57 +01:00
Joachim Bauch b0f2e6ea33
Update source of continentmap to original CSV file.
Now fetching from https://github.com/datasets/country-codes repository.
2024-03-11 10:42:56 +01:00
dependabot[bot] f65bdf04ff
build(deps): Bump github.com/pion/sdp/v3 from 3.0.7 to 3.0.8
Bumps [github.com/pion/sdp/v3](https://github.com/pion/sdp) from 3.0.7 to 3.0.8.
- [Release notes](https://github.com/pion/sdp/releases)
- [Changelog](https://github.com/pion/sdp/blob/master/.goreleaser.yml)
- [Commits](https://github.com/pion/sdp/compare/v3.0.7...v3.0.8)

---
updated-dependencies:
- dependency-name: github.com/pion/sdp/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 20:40:45 +00:00
Joachim Bauch 1fa731f20e
Merge pull request #676 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.33.0
build(deps): Bump google.golang.org/protobuf from 1.32.0 to 1.33.0
2024-03-06 09:19:05 +01:00
Joachim Bauch 5dbee53a1b
Update indirect dependency github.com/golang/protobuf from v1.5.3 to v1.5.4 2024-03-06 09:08:46 +01:00
dependabot[bot] e6b3c8d24f
build(deps): Bump google.golang.org/protobuf from 1.32.0 to 1.33.0
Bumps google.golang.org/protobuf from 1.32.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-06 07:51:33 +00:00
Joachim Bauch 687f4101c0
Merge pull request #677 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.62.1
build(deps): Bump google.golang.org/grpc from 1.62.0 to 1.62.1
2024-03-06 08:50:10 +01:00
dependabot[bot] 4fb7142a4e
build(deps): Bump google.golang.org/grpc from 1.62.0 to 1.62.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.62.0 to 1.62.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.62.0...v1.62.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-06 07:42:25 +00:00
Joachim Bauch ec8cb8e1b8
Merge pull request #678 from strukturag/dependabot/go_modules/github.com/pion/sdp/v3-3.0.7
build(deps): Bump github.com/pion/sdp/v3 from 3.0.6 to 3.0.7
2024-03-06 08:40:55 +01:00
dependabot[bot] 2ee3fa509c
build(deps): Bump github.com/pion/sdp/v3 from 3.0.6 to 3.0.7
Bumps [github.com/pion/sdp/v3](https://github.com/pion/sdp) from 3.0.6 to 3.0.7.
- [Release notes](https://github.com/pion/sdp/releases)
- [Changelog](https://github.com/pion/sdp/blob/master/.goreleaser.yml)
- [Commits](https://github.com/pion/sdp/compare/v3.0.6...v3.0.7)

---
updated-dependencies:
- dependency-name: github.com/pion/sdp/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-06 07:34:37 +00:00
Joachim Bauch 204fec1583
Merge pull request #679 from strukturag/make-use-pinned-dependencies
make: Don't update dependencies but use pinned versions.
2024-03-06 08:33:35 +01:00
Joachim Bauch 42005d97c4
make: Don't update dependencies but use pinned versions. 2024-03-06 08:27:46 +01:00
Joachim Bauch e2266a6765
Merge pull request #675 from strukturag/docker-improvements
Docker improvements
2024-02-28 22:57:37 +01:00
Joachim Bauch 9603ed3d6e
Merge pull request #674 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.19.0
build(deps): Bump github.com/prometheus/client_golang from 1.18.0 to 1.19.0
2024-02-28 22:53:49 +01:00
Joachim Bauch 9d313608cf
docker: Add helper scripts to gracefully stop / wait for proxy. 2024-02-28 22:52:54 +01:00
Joachim Bauch 84374590a4
Fix issues found by shellcheck. 2024-02-28 22:31:08 +01:00
Joachim Bauch a082874377
docker: Make sure main process is running with PID 1. 2024-02-28 22:17:34 +01:00
dependabot[bot] b67264e600
build(deps): Bump github.com/prometheus/client_golang
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.18.0 to 1.19.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.19.0/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.18.0...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-27 20:52:59 +00:00
Joachim Bauch bd445bd99b
Subscribe through "streams" list instead of "feed" for multistream Janus. 2024-02-27 17:00:43 +01:00
Joachim Bauch df477a7856
Merge pull request #673 from strukturag/reuse-backoff
Reuse backoff waiting code where possible
2024-02-27 16:59:32 +01:00
Joachim Bauch 1a8444ca71
Reuse backoff waiting code for initial proxy MCU connection. 2024-02-27 16:42:06 +01:00
Joachim Bauch bde0b08eb1
Reuse backoff waiting code in etcd clients. 2024-02-27 16:27:17 +01:00
Joachim Bauch a68454ceec
Reuse backoff waiting code for errors during GeoIP update. 2024-02-27 16:15:42 +01:00
Joachim Bauch f6fe960534
Reuse backoff waiting code in NATS client. 2024-02-27 16:12:04 +01:00
Joachim Bauch fe53c32714
Merge pull request #613 from strukturag/dependabot/docker/docker/janus/alpine-3.19
build(deps): Bump alpine from 3.18 to 3.19 in /docker/janus
2024-02-27 15:46:59 +01:00
dependabot[bot] c3403b1e9a
build(deps): Bump alpine from 3.18 to 3.19 in /docker/janus
Bumps alpine from 3.18 to 3.19.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-27 14:44:11 +00:00
Joachim Bauch ba73d1a7df
Merge pull request #672 from strukturag/docker-janus
docker: Update Janus from 0.11.8 to 0.14.1.
2024-02-27 15:43:37 +01:00
Joachim Bauch 36e704e320
Merge pull request #656 from strukturag/dependabot/docker/docker/proxy/golang-1.22-alpine
build(deps): Bump golang from 1.21-alpine to 1.22-alpine in /docker/proxy
2024-02-27 15:39:02 +01:00
Joachim Bauch 6394539876
Merge pull request #655 from strukturag/dependabot/docker/docker/server/golang-1.22-alpine
build(deps): Bump golang from 1.21-alpine to 1.22-alpine in /docker/server
2024-02-27 15:38:50 +01:00
Joachim Bauch 2012a7a6df
Merge pull request #661 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.33.1
build(deps): Bump github.com/nats-io/nats.go from 1.32.0 to 1.33.1
2024-02-27 15:38:22 +01:00
Joachim Bauch 1bcf07afd3
docker: Update Janus from 0.11.8 to 0.14.1. 2024-02-27 15:37:30 +01:00
dependabot[bot] 3442cad9c3
build(deps): Bump github.com/nats-io/nats.go from 1.32.0 to 1.33.1
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.32.0 to 1.33.1.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.32.0...v1.33.1)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-27 14:27:36 +00:00
Joachim Bauch edd042b00e
Merge pull request #670 from strukturag/proxy-load
Calculate proxy load based on maximum bandwidth.
2024-02-27 15:25:46 +01:00
Joachim Bauch 8f4fc2db6d
Calculate proxy load based on maximum bandwidth.
Take maximum bandwidth of connected clients into account when calculating
load as screensharing requires more than regular audio/video.
2024-02-27 15:20:17 +01:00
Joachim Bauch 7d09c71ab9
Strongly type "StreamType". 2024-02-27 15:20:14 +01:00
Joachim Bauch 26a65cedd1
Merge pull request #671 from strukturag/fix-flaky-static-proxy-dns
Fix flaky "TestProxyConfigStaticDNS".
2024-02-27 15:19:57 +01:00
Joachim Bauch 9010e91ff4
Fix flaky "TestProxyConfigStaticDNS".
Problem was caused by the initial wakeup check sometimes running,
clearing the expected events too soon.
2024-02-27 15:10:42 +01:00
Joachim Bauch da00080303
Merge pull request #668 from strukturag/http-client-connections-metrics
Add metrics for current number of HTTP client connections.
2024-02-27 11:48:39 +01:00
Joachim Bauch 62b54a85ed
Add metrics for current number of HTTP client connections. 2024-02-27 09:14:49 +01:00
Joachim Bauch 3ea60cfe31
Merge pull request #667 from strukturag/dnsmonitor-port
Support ports in full URLs for DNS monitor.
2024-02-27 09:00:49 +01:00
Joachim Bauch 1f8b536c8a
Split port from hostname in full urls. 2024-02-27 08:53:03 +01:00
Joachim Bauch 8385211fa2
Need to start DNS monitor. 2024-02-27 08:50:37 +01:00
Joachim Bauch f5007df0ad
Merge pull request #664 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.62.0
build(deps): Bump google.golang.org/grpc from 1.61.1 to 1.62.0
2024-02-25 20:42:55 +01:00
dependabot[bot] 8b49cf8581
build(deps): Bump google.golang.org/grpc from 1.61.1 to 1.62.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.61.1 to 1.62.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.61.1...v1.62.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-22 20:39:45 +00:00
Joachim Bauch 0f980f2894
Merge pull request #662 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.11
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.10 to 2.10.11
2024-02-22 21:37:22 +01:00
dependabot[bot] 6ac065f603
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.10 to 2.10.11.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.10...v2.10.11)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-22 14:03:03 +00:00
Joachim Bauch ae37a56e34
Merge pull request #663 from strukturag/improve-dnsmonitor
Minor improvements to DNS monitor
2024-02-22 15:01:44 +01:00
Joachim Bauch 45be0ad2fd
Remove unnecessary state variable. 2024-02-22 14:13:05 +01:00
Joachim Bauch 29b0b06f6d
Stopping the static proxy config should unregister from DNS monitor. 2024-02-22 14:06:05 +01:00
Joachim Bauch 7e613f831b
"Stop" should wait until stopped in DNS monitor. 2024-02-22 14:05:23 +01:00
Joachim Bauch 2e8b0dfe25
Merge pull request #658 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-4.0.0
build(deps): Bump golangci/golangci-lint-action from 3.7.0 to 4.0.0
2024-02-19 10:30:12 +01:00
Joachim Bauch 2348297f36
Merge pull request #659 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.61.1
build(deps): Bump google.golang.org/grpc from 1.61.0 to 1.61.1
2024-02-19 10:29:51 +01:00
dependabot[bot] e0fe89f0f2
build(deps): Bump google.golang.org/grpc from 1.61.0 to 1.61.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.61.0 to 1.61.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.61.0...v1.61.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-14 20:11:26 +00:00
dependabot[bot] e0b3797ea9
build(deps): Bump golangci/golangci-lint-action from 3.7.0 to 4.0.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.7.0 to 4.0.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.7.0...v4.0.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 20:28:43 +00:00
dependabot[bot] 35f9d313c7
build(deps): Bump golang in /docker/proxy
Bumps golang from 1.21-alpine to 1.22-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 21:04:16 +00:00
dependabot[bot] 68d4e87d31
build(deps): Bump golang in /docker/server
Bumps golang from 1.21-alpine to 1.22-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 20:56:37 +00:00
Joachim Bauch 27ebf9e037
Merge pull request #653 from strukturag/improve-makefile
Improve Makefile
2024-02-07 14:41:10 +01:00
Joachim Bauch f071a64797
make: Mark generated files as ".SECONDARY". 2024-02-07 13:08:42 +01:00
Joachim Bauch 6488ba1cf5
make: Fix "BINDIR" getting always created. 2024-02-07 13:08:42 +01:00
Joachim Bauch b710d1704e
make: Mark "common" as ".PHONY". 2024-02-07 13:08:41 +01:00
Joachim Bauch 4a762a3264
make: Enable ".DELETE_ON_ERROR". 2024-02-07 13:08:41 +01:00
Joachim Bauch 55aee6e5dc
make: Keep generated easyjson files in variable. 2024-02-07 13:08:40 +01:00
Joachim Bauch 2b62c9e3c1
make: Automatically detect names of generated proto files. 2024-02-07 13:08:40 +01:00
Joachim Bauch 1a0e51499f
make: Split GRPC / regular PB file generation so it can be parallelized. 2024-02-07 13:08:39 +01:00
Joachim Bauch 2430421006
make: Update easyjson / prococ generators if dependencies changed. 2024-02-07 13:08:39 +01:00
Joachim Bauch 5f71a9a0ab
Merge pull request #654 from strukturag/dnsmonitor-deadlock
Fix deadlock when entry is removed while receiver holds lock in lookup.
2024-02-07 13:08:28 +01:00
Joachim Bauch cf5ee8e4a1
Fix deadlock when entry is removed while receiver holds lock in lookup. 2024-02-07 13:03:41 +01:00
Joachim Bauch c85b31bd24
Merge pull request #649 from strukturag/dependabot/go_modules/etcd-1dbe10e201
build(deps): Bump the etcd group with 4 updates
2024-02-07 10:16:06 +01:00
Joachim Bauch 5ec7fcb594
Merge pull request #651 from strukturag/golang-1.22
CI: Also test with Golang 1.22
2024-02-07 10:15:32 +01:00
Joachim Bauch 978024e799
CI: Also test with Golang 1.22 2024-02-07 10:08:34 +01:00
Joachim Bauch d71ca35e97
Merge pull request #652 from strukturag/fix-proxy-config-test-race
Fix race condition when accessing "expected" in proxy_config tests.
2024-02-07 10:08:04 +01:00
Joachim Bauch 48424bf290
Fix race condition when accessing "expected" in proxy_config tests. 2024-02-07 10:01:12 +01:00
Joachim Bauch a3ba73d764
Merge pull request #650 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.10
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.9 to 2.10.10
2024-02-07 08:45:31 +01:00
Joachim Bauch 9da78a1a8b
docker: Fix typo in readme. 2024-02-07 08:41:05 +01:00
dependabot[bot] fa0cb51c8e
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.9 to 2.10.10.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.9...v2.10.10)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-02 20:48:03 +00:00
dependabot[bot] d0a3ce0616
build(deps): Bump the etcd group with 4 updates
Bumps the etcd group with 4 updates: [go.etcd.io/etcd/api/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/pkg/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) and [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd).


Updates `go.etcd.io/etcd/api/v3` from 3.5.11 to 3.5.12
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.11...v3.5.12)

Updates `go.etcd.io/etcd/client/pkg/v3` from 3.5.11 to 3.5.12
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.11...v3.5.12)

Updates `go.etcd.io/etcd/client/v3` from 3.5.11 to 3.5.12
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.11...v3.5.12)

Updates `go.etcd.io/etcd/server/v3` from 3.5.11 to 3.5.12
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.11...v3.5.12)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/pkg/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-31 20:22:25 +00:00
Joachim Bauch 2595420db1
Update changelog for 1.2.3 2024-01-31 09:35:31 +01:00
Joachim Bauch 32ccc2e50e
Merge pull request #628 from strukturag/dnsmonitor
Refactor DNS monitoring
2024-01-30 17:08:57 +01:00
Joachim Bauch 0cd4099f7b
Add locking to "wakeupChanForTesting" of GRPC clients. 2024-01-30 17:03:46 +01:00
Joachim Bauch c4fce20678
Add lock to entries to prevent concurrent modifications. 2024-01-30 17:03:46 +01:00
Joachim Bauch 2c4cdedcae
Don't run monitor if empty. 2024-01-30 17:03:45 +01:00
Joachim Bauch b1c78f6e9d
Use DNS monitor from static GRPC clients configuration. 2024-01-30 17:03:45 +01:00
Joachim Bauch 8db4068989
make: Quote regular expression of tests to run. 2024-01-30 17:03:44 +01:00
Joachim Bauch 528a09e5da
Use DNS monitor from static proxy configuration. 2024-01-30 17:03:43 +01:00
Joachim Bauch 8417f37cba
Move common DNS monitor code to own class. 2024-01-30 17:03:43 +01:00
Joachim Bauch 7a6cffdc10
Merge pull request #647 from strukturag/ci-lint-cache
CI: Disable cache for linter to bring back annotations.
2024-01-30 17:03:22 +01:00
Joachim Bauch f5bef51917
CI: Disable cache for linter to bring back annotations.
Previously, extracting the cache caused lots of errors that filled up
the log and prevented the annotations from being shown.
2024-01-30 17:01:14 +01:00
Joachim Bauch 390f288c1a
Merge pull request #648 from strukturag/ci-no-manual-cache
CI: No longer need to manually cache Go modules.
2024-01-30 17:00:57 +01:00
Joachim Bauch 11a89e0ca9
CI: No longer need to manually cache Go modules. 2024-01-30 16:54:07 +01:00
Joachim Bauch da4cf896c5
Merge pull request #646 from strukturag/dependabot/github_actions/peter-evans/create-or-update-comment-4
build(deps): Bump peter-evans/create-or-update-comment from 3 to 4
2024-01-28 20:52:27 +01:00
dependabot[bot] c89b7bbe8f
build(deps): Bump peter-evans/create-or-update-comment from 3 to 4
Bumps [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) from 3 to 4.
- [Release notes](https://github.com/peter-evans/create-or-update-comment/releases)
- [Commits](https://github.com/peter-evans/create-or-update-comment/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peter-evans/create-or-update-comment
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-25 20:16:37 +00:00
Joachim Bauch 20a34526c5
Merge pull request #645 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.61.0
build(deps): Bump google.golang.org/grpc from 1.60.1 to 1.61.0
2024-01-25 08:28:43 +01:00
dependabot[bot] 682134fe56
build(deps): Bump google.golang.org/grpc from 1.60.1 to 1.61.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.60.1 to 1.61.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.60.1...v1.61.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-24 20:08:56 +00:00
Joachim Bauch beaad80eba
Merge pull request #644 from strukturag/proxy-welcome
Add "welcome" endpoint to proxy.
2024-01-24 10:52:53 +01:00
Joachim Bauch 59cf86d786
Add "welcome" endpoint to proxy. 2024-01-24 10:45:49 +01:00
Joachim Bauch a362682143
Merge pull request #643 from strukturag/dependabot/go_modules/github.com/google/uuid-1.6.0
build(deps): Bump github.com/google/uuid from 1.5.0 to 1.6.0
2024-01-23 22:23:03 +01:00
dependabot[bot] d3f41eb572
build(deps): Bump github.com/google/uuid from 1.5.0 to 1.6.0
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-23 20:36:55 +00:00
Joachim Bauch 88e67cf95e
Merge pull request #641 from strukturag/docker-proxy-client
docker: Always need to set proxy token id / key for server.
2024-01-22 11:29:18 +01:00
Joachim Bauch cc25760dd6
Merge pull request #638 from strukturag/dependabot/github_actions/actions/cache-4
build(deps): Bump actions/cache from 3 to 4
2024-01-22 11:10:33 +01:00
Joachim Bauch 530700e5af
docker: Always need to set proxy token id / key for server. 2024-01-22 11:10:11 +01:00
dependabot[bot] dbba13865d
build(deps): Bump actions/cache from 3 to 4
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-17 20:35:20 +00:00
Joachim Bauch 0a10339d17
Merge pull request #637 from tcitworld/patch-1
Fix link to NATS install docs
2024-01-16 14:24:28 +01:00
Thomas Citharel 02184ace70
Fix link to NATS install docs 2024-01-16 14:23:11 +01:00
Joachim Bauch 07e2e25a07
Merge pull request #635 from strukturag/dependabot/pip/docs/readthedocs-sphinx-search-0.3.2
build(deps): Bump readthedocs-sphinx-search from 0.3.1 to 0.3.2 in /docs
2024-01-16 09:16:25 +01:00
dependabot[bot] 2334f4815e
build(deps): Bump readthedocs-sphinx-search from 0.3.1 to 0.3.2 in /docs
Bumps [readthedocs-sphinx-search](https://github.com/readthedocs/readthedocs-sphinx-search) from 0.3.1 to 0.3.2.
- [Changelog](https://github.com/readthedocs/readthedocs-sphinx-search/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/readthedocs/readthedocs-sphinx-search/commits/0.3.2)

---
updated-dependencies:
- dependency-name: readthedocs-sphinx-search
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-15 20:10:08 +00:00
Joachim Bauch 47ad7619e0
Merge pull request #634 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.32.0
build(deps): Bump github.com/nats-io/nats.go from 1.31.0 to 1.32.0
2024-01-12 23:01:49 +01:00
dependabot[bot] 6a384619b8
build(deps): Bump github.com/nats-io/nats.go from 1.31.0 to 1.32.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.31.0 to 1.32.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.31.0...v1.32.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-12 20:46:29 +00:00
Joachim Bauch 1d12b40867
Merge pull request #631 from strukturag/dependabot/pip/docs/markdown-3.5.2
build(deps): Bump markdown from 3.5.1 to 3.5.2 in /docs
2024-01-11 21:51:54 +01:00
dependabot[bot] a8c2a35221
build(deps): Bump markdown from 3.5.1 to 3.5.2 in /docs
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.5.1 to 3.5.2.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.5.1...3.5.2)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 20:48:32 +00:00
Joachim Bauch dc3bcf2ce7
Merge pull request #633 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.9
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.7 to 2.10.9
2024-01-11 21:47:52 +01:00
Joachim Bauch 8c7882e4a6
Merge pull request #632 from strukturag/dependabot/pip/docs/jinja2-3.1.3
build(deps): Bump jinja2 from 3.1.2 to 3.1.3 in /docs
2024-01-11 21:47:19 +01:00
dependabot[bot] 67f20bd9b2
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.7 to 2.10.9.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.7...v2.10.9)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 20:23:07 +00:00
dependabot[bot] fea65b31dc
build(deps): Bump jinja2 from 3.1.2 to 3.1.3 in /docs
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 19:59:22 +00:00
Joachim Bauch d0085811f8
Merge pull request #630 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.18.0
build(deps): Bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0
2024-01-02 10:12:35 +01:00
dependabot[bot] 719bb9615d
build(deps): Bump github.com/prometheus/client_golang
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-28 20:07:18 +00:00
Joachim Bauch e3e302e453
Merge pull request #629 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.32.0
build(deps): Bump google.golang.org/protobuf from 1.31.0 to 1.32.0
2023-12-22 22:42:43 +01:00
dependabot[bot] 3b509a5f43
build(deps): Bump google.golang.org/protobuf from 1.31.0 to 1.32.0
Bumps google.golang.org/protobuf from 1.31.0 to 1.32.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-22 20:55:00 +00:00
Joachim Bauch 0b61b8bb9f
Merge pull request #627 from strukturag/ci-license-check
CI: Check license headers.
2023-12-21 16:18:17 +01:00
Joachim Bauch 9b09ff083b
Add missing copyright headers. 2023-12-21 16:07:20 +01:00
Joachim Bauch 682d3aa52a
CI: Check license headers of Go files. 2023-12-21 16:07:16 +01:00
Joachim Bauch 110ece7626
Add missing copyright headers. 2023-12-21 13:43:57 +01:00
Joachim Bauch 5fd0efa4bc
Merge pull request #606 from strukturag/refactor-proxy-config
Refactor proxy config
2023-12-21 13:39:28 +01:00
Joachim Bauch bd9e2aa29d
Don't log error if reading failed because a close message was sent before. 2023-12-21 13:23:14 +01:00
Joachim Bauch 8f2933071e
Move proxy configuration code to different files. 2023-12-21 11:50:12 +01:00
Joachim Bauch fb62c53976
Merge pull request #624 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.60.1
build(deps): Bump google.golang.org/grpc from 1.60.0 to 1.60.1
2023-12-19 21:39:56 +01:00
dependabot[bot] 2e0561b90b
build(deps): Bump google.golang.org/grpc from 1.60.0 to 1.60.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.60.0 to 1.60.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.60.0...v1.60.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-19 20:28:42 +00:00
Joachim Bauch eeab0a226b
Merge pull request #623 from strukturag/dependabot/go_modules/golang.org/x/crypto-0.17.0
build(deps): Bump golang.org/x/crypto from 0.16.0 to 0.17.0
2023-12-19 08:16:07 +01:00
dependabot[bot] 32bbbeee32
build(deps): Bump golang.org/x/crypto from 0.16.0 to 0.17.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-19 00:06:51 +00:00
Joachim Bauch 7a8879051d
Merge pull request #622 from strukturag/dependabot/github_actions/artifacts-8204c6933e
build(deps): Bump the artifacts group with 2 updates
2023-12-18 14:45:04 +01:00
dependabot[bot] 075e50560e
build(deps): Bump the artifacts group with 2 updates
Bumps the artifacts group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/upload-artifact` from 3 to 4
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

Updates `actions/download-artifact` from 3 to 4
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: artifacts
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: artifacts
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 07:34:25 +00:00
Joachim Bauch 6c377ee173
dependabot: Group artifact actions. 2023-12-18 08:33:37 +01:00
Joachim Bauch eae19fd61a
Merge pull request #617 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.60.0
build(deps): Bump google.golang.org/grpc from 1.59.0 to 1.60.0
2023-12-14 08:21:56 +01:00
Joachim Bauch a751ede2b2
Merge pull request #618 from strukturag/dependabot/go_modules/github.com/google/uuid-1.5.0
build(deps): Bump github.com/google/uuid from 1.4.0 to 1.5.0
2023-12-14 08:21:34 +01:00
Joachim Bauch 7d11ff4a41
Merge pull request #619 from strukturag/dependabot/github_actions/github/codeql-action-3
build(deps): Bump github/codeql-action from 2 to 3
2023-12-14 08:21:20 +01:00
dependabot[bot] 833f39e608
build(deps): Bump github/codeql-action from 2 to 3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-13 20:44:49 +00:00
dependabot[bot] 4f5bdb2a3f
build(deps): Bump github.com/google/uuid from 1.4.0 to 1.5.0
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-12 20:19:22 +00:00
dependabot[bot] 116e74fab4
build(deps): Bump google.golang.org/grpc from 1.59.0 to 1.60.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.59.0 to 1.60.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.59.0...v1.60.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-12 20:19:13 +00:00
Joachim Bauch 9c0e0ba85d
Update changelog for 1.2.2 2023-12-11 08:29:25 +01:00
Joachim Bauch 23e6b11383
Merge pull request #611 from strukturag/dependabot/go_modules/etcd-65d0f67083
build(deps): Bump the etcd group with 4 updates
2023-12-08 12:12:31 +01:00
dependabot[bot] 7e33d2cf3a
build(deps): Bump the etcd group with 4 updates
Bumps the etcd group with 4 updates: [go.etcd.io/etcd/api/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/pkg/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) and [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd).


Updates `go.etcd.io/etcd/api/v3` from 3.5.10 to 3.5.11
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.10...v3.5.11)

Updates `go.etcd.io/etcd/client/pkg/v3` from 3.5.10 to 3.5.11
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.10...v3.5.11)

Updates `go.etcd.io/etcd/client/v3` from 3.5.10 to 3.5.11
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.10...v3.5.11)

Updates `go.etcd.io/etcd/server/v3` from 3.5.10 to 3.5.11
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.10...v3.5.11)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/pkg/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 11:07:11 +00:00
Joachim Bauch 7fe5995e1d
Merge pull request #612 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.7
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.6 to 2.10.7
2023-12-08 12:04:09 +01:00
Joachim Bauch 2eb84a3301
Merge pull request #610 from strukturag/hangup-on-disinvite
Hangup virtual session if it gets disinvited.
2023-12-08 12:03:52 +01:00
Joachim Bauch 2a16bf0650
Merge pull request #609 from strukturag/geoip-overrides-default
Skip options from default section when parsing "geoip-overrides".
2023-12-08 12:03:34 +01:00
dependabot[bot] d63856e263
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.6 to 2.10.7.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.6...v2.10.7)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-07 20:55:53 +00:00
Joachim Bauch 734eaea85c
Hangup virtual session if it gets disinvited. 2023-12-07 14:38:01 +01:00
Joachim Bauch e61845b086
Move common option list parsing code to own function. 2023-12-07 13:33:54 +01:00
Joachim Bauch 0f83392e2d
Skip options from default section when parsing "geoip-overrides". 2023-12-07 12:14:52 +01:00
Joachim Bauch 362098531b
Merge pull request #608 from strukturag/dependabot/github_actions/actions/setup-go-5
build(deps): Bump actions/setup-go from 4 to 5
2023-12-07 12:05:08 +01:00
Joachim Bauch 0d15971506
Dump previous goroutines if leakcheck fails. 2023-12-07 12:04:29 +01:00
dependabot[bot] fd8d11806b
build(deps): Bump actions/setup-go from 4 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-06 20:59:16 +00:00
Joachim Bauch a8180194ef
Merge pull request #605 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.6
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.5 to 2.10.6
2023-12-02 19:51:52 +01:00
Joachim Bauch dddf194b48
Use dedicated port for peer listener when testing. 2023-12-01 23:33:33 +01:00
dependabot[bot] 2f421e3bdf
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.5 to 2.10.6.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.5...v2.10.6)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-01 20:58:07 +00:00
Joachim Bauch 716be91feb
Merge pull request #604 from strukturag/dependabot/pip/docs/sphinx-rtd-theme-2.0.0
build(deps): Bump sphinx-rtd-theme from 1.3.0 to 2.0.0 in /docs
2023-11-29 08:08:54 +01:00
dependabot[bot] b3dc84b7b8
build(deps): Bump sphinx-rtd-theme from 1.3.0 to 2.0.0 in /docs
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.3.0 to 2.0.0.
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.3.0...2.0.0)

---
updated-dependencies:
- dependency-name: sphinx-rtd-theme
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 20:55:43 +00:00
Joachim Bauch 55d143d6bc
Merge pull request #602 from strukturag/docker-version
Include "~docker" in version if built on Docker.
2023-11-15 12:10:09 +01:00
Joachim Bauch 838e601183
docker: Create "/.dockerenv" if it doesn't exist.
When building through the CI builders, detection of the Docker environment
doesn't work. So we always create the file manually to make sure the version
contains "~docker" in this case.
2023-11-15 12:03:43 +01:00
Joachim Bauch 4b019a991f
Include "~docker" in version if built on Docker. 2023-11-15 12:02:07 +01:00
Joachim Bauch a2faf3dc95
Merge pull request #603 from strukturag/docker-testing
CI: No need to build docker images for testing, done internally.
2023-11-15 11:49:19 +01:00
Joachim Bauch c20ff558f3
CI: No need to build docker images for testing, done internally. 2023-11-15 11:45:48 +01:00
Joachim Bauch 42a3a5a8a7
Update changelog for 1.2.1 2023-11-15 09:28:39 +01:00
Joachim Bauch 5d4cb58fc8
Merge pull request #480 from nickvergessen/feat/noid/log-simplifier
feat(scripts): Add a script to simplify the logs to make it more easi…
2023-11-15 09:18:07 +01:00
Joachim Bauch 026dafe271
Run "go mod tidy".
This is a follow-up to #600.
2023-11-15 09:17:06 +01:00
Joachim Bauch 4aedfb0051
Merge pull request #600 from strukturag/dependabot/go_modules/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc-0.46.0
build(deps): Bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc from 0.25.0 to 0.46.0
2023-11-15 09:16:25 +01:00
Joachim Bauch 5ed1c94796
Update go.opentelemetry.io/otel/sdk to v1.20.0 to fix dependencies. 2023-11-15 09:09:35 +01:00
dependabot[bot] 5fadace4f3
build(deps): Bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
Bumps [go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc](https://github.com/open-telemetry/opentelemetry-go-contrib) from 0.25.0 to 0.46.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.25.0...zpages/v0.46.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-15 08:06:13 +00:00
Joachim Bauch bbde039710
Merge pull request #592 from strukturag/dialout-backends
Improve support for multiple backends with dialouts
2023-11-15 08:56:15 +01:00
Joachim Bauch 400e4883e4
Merge pull request #599 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.5
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.4 to 2.10.5
2023-11-12 23:21:22 +01:00
dependabot[bot] d7c659295e
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.4 to 2.10.5.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.4...v2.10.5)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-10 20:05:05 +00:00
Joachim Bauch a46cf6a504
Merge pull request #596 from strukturag/dependabot/go_modules/github.com/gorilla/mux-1.8.1
build(deps): Bump github.com/gorilla/mux from 1.8.0 to 1.8.1
2023-11-07 09:16:56 +01:00
dependabot[bot] 7bedd963b0
build(deps): Bump github.com/gorilla/mux from 1.8.0 to 1.8.1
Bumps [github.com/gorilla/mux](https://github.com/gorilla/mux) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/gorilla/mux/releases)
- [Commits](https://github.com/gorilla/mux/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/gorilla/mux
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-07 07:59:31 +00:00
Joachim Bauch 57232b7e1f
Merge pull request #597 from strukturag/dependabot/go_modules/github.com/gorilla/securecookie-1.1.2
build(deps): Bump github.com/gorilla/securecookie from 1.1.1 to 1.1.2
2023-11-07 08:57:57 +01:00
dependabot[bot] 2bf877f561
build(deps): Bump github.com/gorilla/securecookie from 1.1.1 to 1.1.2
Bumps [github.com/gorilla/securecookie](https://github.com/gorilla/securecookie) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/gorilla/securecookie/releases)
- [Commits](https://github.com/gorilla/securecookie/compare/v1.1.1...v1.1.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/securecookie
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-07 07:34:05 +00:00
Joachim Bauch 7dfc4064e4
Merge pull request #595 from strukturag/dependabot/go_modules/github.com/gorilla/websocket-1.5.1
build(deps): Bump github.com/gorilla/websocket from 1.5.0 to 1.5.1
2023-11-07 08:33:00 +01:00
dependabot[bot] b1e0d231f7
build(deps): Bump github.com/gorilla/websocket from 1.5.0 to 1.5.1
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.5.0...v1.5.1)

---
updated-dependencies:
- dependency-name: github.com/gorilla/websocket
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 20:56:48 +00:00
Joachim Bauch 6caae105c7
Merge pull request #594 from strukturag/dependabot/pip/docs/markdown-3.5.1
build(deps): Bump markdown from 3.5 to 3.5.1 in /docs
2023-11-02 08:10:36 +01:00
dependabot[bot] 0a982dead3
build(deps): Bump markdown from 3.5 to 3.5.1 in /docs
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.5 to 3.5.1.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.5...3.5.1)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-31 20:05:26 +00:00
Joachim Bauch 97ae079cd6
Support old-style compat backend configurations. 2023-10-31 08:53:06 +01:00
Joachim Bauch 2a40c89585
Include backend URL to when starting dialout request. 2023-10-31 08:42:02 +01:00
Joachim Bauch c72c821687
Check backend when searching for session to use for dialout. 2023-10-31 08:33:46 +01:00
Joachim Bauch 29d10f3723
Update changelog for 1.2.0 2023-10-30 11:00:16 +01:00
Joachim Bauch 00c2e4ea9f
Merge pull request #591 from strukturag/common-flags
Move common flags code to own struct.
2023-10-30 10:23:11 +01:00
Joachim Bauch 43b5243463
Move common flags code to own struct. 2023-10-30 10:13:17 +01:00
Joachim Bauch a43686ad92
Merge pull request #500 from strukturag/atomic-19
Switch to atomic types from Go 1.19
2023-10-30 09:42:19 +01:00
Joachim Bauch c134883138
Switch to atomic types from Go 1.19 2023-10-30 09:32:46 +01:00
Joachim Bauch 2c5ad32391
Merge pull request #590 from strukturag/dependabot/go_modules/etcd-d794cda768
build(deps): Bump the etcd group with 3 updates
2023-10-30 09:17:41 +01:00
dependabot[bot] cc6b888830
build(deps): Bump the etcd group with 3 updates
Bumps the etcd group with 3 updates: [go.etcd.io/etcd/api/v3](https://github.com/etcd-io/etcd), [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) and [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd).


Updates `go.etcd.io/etcd/api/v3` from 3.5.9 to 3.5.10
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.9...v3.5.10)

Updates `go.etcd.io/etcd/client/v3` from 3.5.9 to 3.5.10
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.9...v3.5.10)

Updates `go.etcd.io/etcd/server/v3` from 3.5.9 to 3.5.10
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.9...v3.5.10)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/client/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: etcd
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 08:04:41 +00:00
Joachim Bauch ec7b178c4f
dependabot: Group etcd updates. 2023-10-30 09:03:37 +01:00
Joachim Bauch 20dbd95a44
Merge pull request #585 from strukturag/dependabot/go_modules/github.com/google/uuid-1.4.0
build(deps): Bump github.com/google/uuid from 1.3.1 to 1.4.0
2023-10-30 08:57:49 +01:00
Joachim Bauch 29753a66b9
Merge pull request #586 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.4
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.3 to 2.10.4
2023-10-30 08:57:32 +01:00
Joachim Bauch f4d84114e0
Merge pull request #588 from strukturag/dependabot/go_modules/go.etcd.io/etcd/client/pkg/v3-3.5.10
build(deps): Bump go.etcd.io/etcd/client/pkg/v3 from 3.5.9 to 3.5.10
2023-10-30 08:57:12 +01:00
dependabot[bot] ceefe8dbfe
build(deps): Bump go.etcd.io/etcd/client/pkg/v3 from 3.5.9 to 3.5.10
Bumps [go.etcd.io/etcd/client/pkg/v3](https://github.com/etcd-io/etcd) from 3.5.9 to 3.5.10.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.9...v3.5.10)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/client/pkg/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-27 21:00:32 +00:00
dependabot[bot] c3bb9b45b9
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.10.3 to 2.10.4.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.10.3...v2.10.4)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-27 20:59:44 +00:00
dependabot[bot] e048da5f60
build(deps): Bump github.com/google/uuid from 1.3.1 to 1.4.0
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.3.1...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 20:21:10 +00:00
Joachim Bauch 825d95c8d7
Merge pull request #584 from strukturag/improve-version
Improve get-version.sh
2023-10-26 14:20:06 +02:00
Joachim Bauch 1fb2d93ce0
Merge pull request #583 from strukturag/dependabot/docker/docker/server/golang-1.21-alpine
build(deps): Bump golang from 1.20-alpine to 1.21-alpine in /docker/server
2023-10-26 14:13:22 +02:00
Joachim Bauch 29998777a4
Merge pull request #582 from strukturag/dependabot/docker/docker/janus/alpine-3.18
build(deps): Bump alpine from 3.14 to 3.18 in /docker/janus
2023-10-26 14:13:06 +02:00
Joachim Bauch b1bd1a1f79
Remove useless "cat" invocation. 2023-10-26 14:11:34 +02:00
Joachim Bauch e972f911b0
Use double quotes to prevent globbing and word splitting. 2023-10-26 14:10:33 +02:00
Joachim Bauch 4e140dd334
Get version from tag if building from tag. 2023-10-26 14:09:26 +02:00
dependabot[bot] 835836419e
build(deps): Bump golang in /docker/server
Bumps golang from 1.20-alpine to 1.21-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 12:06:36 +00:00
dependabot[bot] f448a09794
build(deps): Bump alpine from 3.14 to 3.18 in /docker/janus
Bumps alpine from 3.14 to 3.18.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 12:06:05 +00:00
Joachim Bauch 24193f47ac
Merge pull request #576 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.10.3
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.22 to 2.10.3
2023-10-26 14:06:01 +02:00
Joachim Bauch 4868349ac8
Merge pull request #581 from strukturag/dependabot/docker/docker/proxy/golang-1.21-alpine
build(deps): Bump golang from 1.20-alpine to 1.21-alpine in /docker/proxy
2023-10-26 14:04:50 +02:00
dependabot[bot] 213c836b9c
build(deps): Bump golang in /docker/proxy
Bumps golang from 1.20-alpine to 1.21-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 11:59:47 +00:00
Joachim Bauch a2a3771906
dependabot: Check for updates in docker files. 2023-10-26 13:59:19 +02:00
dependabot[bot] cd94e7886e
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.22 to 2.10.3.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.22...v2.10.3)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-26 11:53:12 +00:00
Joachim Bauch 9ef8cbe83e
Merge pull request #580 from strukturag/deprecate-golang-1.19
No longer support Golang 1.19.
2023-10-26 13:51:28 +02:00
Joachim Bauch 3f30271e72
No longer support Golang 1.19.
This follows the Go release policy of only supporting the last two versions.
2023-10-26 13:43:51 +02:00
Joachim Bauch 0936d40f8b
Merge pull request #563 from strukturag/dialout-support
Implement message handler for dialout support.
2023-10-26 13:30:45 +02:00
Joachim Bauch 225f5bbd97
Add lock for TestClient connections to support writing from multiple goroutines. 2023-10-26 10:58:59 +02:00
Joachim Bauch 8e98ad3438
Add tests for dialout messages. 2023-10-26 10:43:52 +02:00
Joachim Bauch e333ddfd53
Make sure room ids for dial-out are numeric. 2023-10-26 10:43:51 +02:00
Joachim Bauch fdb4d74dd6
Add note on control messages and phone sessions. 2023-10-26 10:43:51 +02:00
Joachim Bauch 7c9632575b
Merge pull request #577 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.31.0
build(deps): Bump github.com/nats-io/nats.go from 1.30.2 to 1.31.0
2023-10-26 10:19:54 +02:00
Joachim Bauch 3a7b4c48dc
Pass along dialout status through transient data. 2023-10-26 09:53:58 +02:00
Joachim Bauch e1273a3c52
Keep list of possible dialout sessions. 2023-10-26 09:53:58 +02:00
Joachim Bauch d1544dcb2c
Implement message handler for dialout support. 2023-10-26 09:53:57 +02:00
Joachim Bauch 04192e96f1
Document APi to start dialout. 2023-10-26 09:53:56 +02:00
Joachim Bauch 1333d821d9
CI: Run lint if ".golangci.yml" was changed. 2023-10-26 09:53:40 +02:00
Joachim Bauch 861c4e628d
lint: Disable rule "indent-error-flow". 2023-10-26 09:53:39 +02:00
Joachim Bauch 0c552d7e16
Merge pull request #578 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.59.0
build(deps): Bump google.golang.org/grpc from 1.58.3 to 1.59.0
2023-10-24 11:28:09 +02:00
dependabot[bot] 39cd9f50fb
build(deps): Bump google.golang.org/grpc from 1.58.3 to 1.59.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.3 to 1.59.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.58.3...v1.59.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-18 20:59:35 +00:00
dependabot[bot] 8ebfa81c13
build(deps): Bump github.com/nats-io/nats.go from 1.30.2 to 1.31.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.30.2 to 1.31.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.30.2...v1.31.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 20:46:13 +00:00
Joachim Bauch 152bbe1287
Merge pull request #568 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.30.2
build(deps): Bump github.com/nats-io/nats.go from 1.29.0 to 1.30.2
2023-10-12 11:53:55 +02:00
Joachim Bauch 4a9b25981d
Merge pull request #574 from strukturag/dependabot/go_modules/golang.org/x/net-0.17.0
build(deps): Bump golang.org/x/net from 0.12.0 to 0.17.0
2023-10-12 11:53:45 +02:00
Joachim Bauch 43f88c35a5
Merge pull request #569 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.17.0
build(deps): Bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0
2023-10-12 11:48:07 +02:00
Joachim Bauch 4bd656e8fd
Merge pull request #573 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.58.3
build(deps): Bump google.golang.org/grpc from 1.58.1 to 1.58.3
2023-10-12 11:47:18 +02:00
dependabot[bot] 050475dfc8
build(deps): Bump golang.org/x/net from 0.12.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.12.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.12.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-12 09:33:45 +00:00
dependabot[bot] d877f5193d
build(deps): Bump github.com/nats-io/nats.go from 1.29.0 to 1.30.2
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.29.0 to 1.30.2.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.29.0...v1.30.2)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-12 09:33:39 +00:00
dependabot[bot] 4c0043521a
build(deps): Bump github.com/prometheus/client_golang
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.17.0/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.16.0...v1.17.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-12 09:33:08 +00:00
dependabot[bot] da03095d1b
build(deps): Bump google.golang.org/grpc from 1.58.1 to 1.58.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.1 to 1.58.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.58.1...v1.58.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-12 09:32:52 +00:00
Joachim Bauch c17c5fd444
Merge pull request #575 from strukturag/transient-ttl
Support TTL for transient data.
2023-10-12 11:31:38 +02:00
Joachim Bauch e895fe3aeb
Attempt to fix flaky TestBackendServer_RoomDisinviteDifferentRooms. 2023-10-12 11:19:07 +02:00
Joachim Bauch b006903a56
Support passing TTL when setting transient data from clients. 2023-10-12 11:07:11 +02:00
Joachim Bauch 1ace748432
Support TTL for transient data. 2023-10-12 11:07:10 +02:00
Joachim Bauch 4190f2709d
Merge pull request #570 from strukturag/dependabot/pip/docs/markdown-3.5
build(deps): Bump markdown from 3.4.4 to 3.5 in /docs
2023-10-11 09:05:46 +02:00
dependabot[bot] a96a1c5ebc
build(deps): Bump markdown from 3.4.4 to 3.5 in /docs
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.4.4 to 3.5.
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.4.4...3.5)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-06 20:25:53 +00:00
Joachim Bauch 4d21091172
Merge pull request #561 from strukturag/dependabot/pip/docs/mkdocs-1.5.3
build(deps): Bump mkdocs from 1.5.2 to 1.5.3 in /docs
2023-09-20 12:42:34 +02:00
dependabot[bot] 94ce9a6067
build(deps): Bump mkdocs from 1.5.2 to 1.5.3 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.5.2...1.5.3)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-19 20:09:12 +00:00
Joachim Bauch 15f6204990
Merge pull request #560 from strukturag/dependabot/pip/docs/sphinx-7.2.6
build(deps): Bump sphinx from 7.2.5 to 7.2.6 in /docs
2023-09-18 14:19:35 +02:00
Joachim Bauch a232fb7fc3
Merge pull request #559 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.58.1
build(deps): Bump google.golang.org/grpc from 1.58.0 to 1.58.1
2023-09-18 14:19:18 +02:00
Joachim Bauch b7be0bf910
Merge pull request #547 from strukturag/response-join-twice
Return response if session tries to join room again.
2023-09-18 14:18:54 +02:00
dependabot[bot] 858b176e37
build(deps): Bump sphinx from 7.2.5 to 7.2.6 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.2.5 to 7.2.6.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.2.5...v7.2.6)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-14 20:51:56 +00:00
dependabot[bot] 5e82df1848
build(deps): Bump google.golang.org/grpc from 1.58.0 to 1.58.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.0 to 1.58.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.58.0...v1.58.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-14 20:17:38 +00:00
Joachim Bauch 96fdf92d68
Merge pull request #558 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.29.0
build(deps): Bump github.com/nats-io/nats.go from 1.28.0 to 1.29.0
2023-09-14 15:35:58 +02:00
Joachim Bauch 1905b81fb6
Merge pull request #557 from strukturag/dependabot/github_actions/docker/build-push-action-5
build(deps): Bump docker/build-push-action from 4 to 5
2023-09-14 15:35:23 +02:00
dependabot[bot] c7f3dc5853
build(deps): Bump github.com/nats-io/nats.go from 1.28.0 to 1.29.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.28.0 to 1.29.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.28.0...v1.29.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-13 20:30:40 +00:00
dependabot[bot] 3b4a8bc521
build(deps): Bump docker/build-push-action from 4 to 5
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-13 20:30:07 +00:00
Joachim Bauch 28ac875b90
Merge pull request #550 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.22
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.21 to 2.9.22
2023-09-13 13:35:03 +02:00
Joachim Bauch 366de99f30
Merge pull request #551 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.2.3
build(deps): Bump coverallsapp/github-action from 2.2.2 to 2.2.3
2023-09-13 13:32:05 +02:00
Joachim Bauch 6f60b23a33
Merge pull request #555 from strukturag/dependabot/github_actions/docker/setup-buildx-action-3
build(deps): Bump docker/setup-buildx-action from 2 to 3
2023-09-13 13:31:49 +02:00
dependabot[bot] 342f54044c
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.21 to 2.9.22.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.21...v2.9.22)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-13 11:21:16 +00:00
dependabot[bot] e3a8a6b1ac
build(deps): Bump docker/setup-buildx-action from 2 to 3
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-13 11:20:22 +00:00
Joachim Bauch 48b4e3beb8
Merge pull request #554 from strukturag/dependabot/github_actions/docker/login-action-3
build(deps): Bump docker/login-action from 2 to 3
2023-09-13 13:19:51 +02:00
Joachim Bauch 4d7d5094bf
Merge pull request #553 from strukturag/dependabot/github_actions/docker/setup-qemu-action-3
build(deps): Bump docker/setup-qemu-action from 2 to 3
2023-09-13 13:19:35 +02:00
Joachim Bauch 4c87ecb4b3
Merge pull request #552 from strukturag/dependabot/github_actions/docker/metadata-action-5
build(deps): Bump docker/metadata-action from 4 to 5
2023-09-13 13:19:17 +02:00
Joachim Bauch 9af73fa513
Merge pull request #549 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.58.0
build(deps): Bump google.golang.org/grpc from 1.57.0 to 1.58.0
2023-09-13 13:18:56 +02:00
dependabot[bot] f62ada7f81
build(deps): Bump docker/login-action from 2 to 3
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 20:16:08 +00:00
dependabot[bot] ac720bc2be
build(deps): Bump docker/setup-qemu-action from 2 to 3
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 20:16:05 +00:00
dependabot[bot] 7d12d74f38
build(deps): Bump docker/metadata-action from 4 to 5
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 20:16:03 +00:00
dependabot[bot] 0ab7c8efeb
build(deps): Bump coverallsapp/github-action from 2.2.2 to 2.2.3
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v2.2.2...v2.2.3)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 20:10:38 +00:00
dependabot[bot] f355b25ffb
build(deps): Bump google.golang.org/grpc from 1.57.0 to 1.58.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.57.0 to 1.58.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.57.0...v1.58.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-07 20:10:07 +00:00
Joachim Bauch 35135433c2
Return response if session tries to join room again. 2023-09-05 15:43:46 +02:00
Joachim Bauch 92690f4613
Update entry if room session id changes. 2023-09-05 15:23:19 +02:00
Joachim Bauch bc7dba17c1
Merge pull request #545 from strukturag/dependabot/github_actions/actions/checkout-4
build(deps): Bump actions/checkout from 3 to 4
2023-09-05 09:01:02 +02:00
Joachim Bauch 470ae9b703
Merge pull request #546 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.2.2
build(deps): Bump coverallsapp/github-action from 2.2.1 to 2.2.2
2023-09-05 08:29:19 +02:00
dependabot[bot] ceea757a45
build(deps): Bump coverallsapp/github-action from 2.2.1 to 2.2.2
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v2.2.1...v2.2.2)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 20:25:21 +00:00
dependabot[bot] 0f043a4ba9
build(deps): Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 20:25:17 +00:00
Joachim Bauch 46cec93d9f
Merge pull request #544 from strukturag/dependabot/pip/docs/sphinx-7.2.5
build(deps): Bump sphinx from 7.2.4 to 7.2.5 in /docs
2023-09-04 20:00:19 +02:00
dependabot[bot] aeef825cd4
build(deps): Bump sphinx from 7.2.4 to 7.2.5 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.2.4 to 7.2.5.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.2.4...v7.2.5)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-31 20:40:04 +00:00
Joachim Bauch 3e134f1278
Merge pull request #539 from strukturag/dependabot/go_modules/github.com/google/uuid-1.3.1
build(deps): Bump github.com/google/uuid from 1.3.0 to 1.3.1
2023-08-28 08:55:25 +02:00
Joachim Bauch ecdc5d75f0
Merge pull request #542 from strukturag/dependabot/pip/docs/sphinx-7.2.4
build(deps): Bump sphinx from 6.2.1 to 7.2.4 in /docs
2023-08-28 08:55:11 +02:00
dependabot[bot] 26652fba1a
build(deps): Bump sphinx from 6.2.1 to 7.2.4 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 6.2.1 to 7.2.4.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v6.2.1...v7.2.4)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 06:40:35 +00:00
Joachim Bauch a6b1998bba
Merge pull request #537 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-3.7.0
build(deps): Bump golangci/golangci-lint-action from 3.6.0 to 3.7.0
2023-08-28 08:39:26 +02:00
Joachim Bauch b0e9126848
Merge pull request #540 from strukturag/dependabot/pip/docs/sphinx-rtd-theme-1.3.0
build(deps): Bump sphinx-rtd-theme from 1.2.2 to 1.3.0 in /docs
2023-08-28 08:38:55 +02:00
dependabot[bot] 9163bf435f
build(deps): Bump sphinx-rtd-theme from 1.2.2 to 1.3.0 in /docs
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.2.2 to 1.3.0.
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.2.2...1.3.0)

---
updated-dependencies:
- dependency-name: sphinx-rtd-theme
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 21:02:57 +00:00
dependabot[bot] 543df327a8
build(deps): Bump github.com/google/uuid from 1.3.0 to 1.3.1
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 20:55:00 +00:00
dependabot[bot] 84bde0591c
build(deps): Bump golangci/golangci-lint-action from 3.6.0 to 3.7.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.6.0...v3.7.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-15 20:59:23 +00:00
Joachim Bauch 2e6de510bb
Merge pull request #536 from strukturag/golang-1.21
CI: Test with Golang 1.21
2023-08-09 14:51:06 +02:00
Joachim Bauch 7914d4bc49
CI: Test with Golang 1.21 2023-08-09 09:00:46 +02:00
Joachim Bauch e427d0daa6
Merge pull request #534 from strukturag/common-shared-secret
Fallback to common shared secret if none is set for backends.
2023-08-08 11:03:18 +02:00
Joachim Bauch 042a78f99d
Fallback to common shared secret if none is set for backends.
Only applies to static backend configuration.
2023-08-08 10:54:47 +02:00
Joachim Bauch 0591be1bad
Merge pull request #533 from strukturag/warn-missing-backends
Log warning if no (static) backends have been configured.
2023-08-08 10:41:25 +02:00
Joachim Bauch 17e25bbe6e
Log warning if no (static) backends have been configured. 2023-08-08 10:31:27 +02:00
Joachim Bauch f514afad99
Merge pull request #532 from strukturag/geoip-overrides
Use GeoIP overrides if no GeoIP database is configured.
2023-08-07 12:05:20 +02:00
Joachim Bauch e9140178f9
Use GeoIP overrides if no GeoIP database is configured. 2023-08-07 11:41:51 +02:00
Joachim Bauch e703982890
Merge pull request #530 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.21
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.20 to 2.9.21
2023-08-07 10:02:57 +02:00
dependabot[bot] 940365ea52
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.20 to 2.9.21.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.20...v2.9.21)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-04 20:11:39 +00:00
Joachim Bauch 65209deb7d
Merge pull request #524 from strukturag/dependabot/go_modules/github.com/oschwald/maxminddb-golang-1.12.0
build(deps): Bump github.com/oschwald/maxminddb-golang from 1.11.0 to 1.12.0
2023-08-03 08:36:08 +02:00
Joachim Bauch 8a5dcb4ac9
Merge pull request #525 from strukturag/dependabot/pip/docs/mkdocs-1.5.2
build(deps): Bump mkdocs from 1.5.1 to 1.5.2 in /docs
2023-08-03 08:26:03 +02:00
dependabot[bot] 0e55b5aa80
build(deps): Bump mkdocs from 1.5.1 to 1.5.2 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.5.1...1.5.2)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-02 20:24:55 +00:00
dependabot[bot] ebf9f3efc0
build(deps): Bump github.com/oschwald/maxminddb-golang
Bumps [github.com/oschwald/maxminddb-golang](https://github.com/oschwald/maxminddb-golang) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/oschwald/maxminddb-golang/releases)
- [Commits](https://github.com/oschwald/maxminddb-golang/compare/v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/oschwald/maxminddb-golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-01 20:15:27 +00:00
Joachim Bauch 8bf0b53d0b
Merge pull request #519 from strukturag/dependabot/pip/docs/markdown-3.4.4
build(deps): Bump markdown from 3.3.7 to 3.4.4 in /docs
2023-07-31 08:55:53 +02:00
dependabot[bot] 5dc0dbe4d6
build(deps): Bump markdown from 3.3.7 to 3.4.4 in /docs
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.3.7 to 3.4.4.
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/change_log/release-2.6.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.3.7...3.4.4)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 06:41:01 +00:00
Joachim Bauch a275c62e9e
Merge pull request #523 from strukturag/dependabot/pip/docs/mkdocs-1.5.1
build(deps): Bump mkdocs from 1.4.3 to 1.5.1 in /docs
2023-07-31 08:40:21 +02:00
dependabot[bot] 8e17ea2ffb
build(deps): Bump mkdocs from 1.4.3 to 1.5.1 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.4.3 to 1.5.1.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.4.3...1.5.1)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-28 20:07:02 +00:00
Joachim Bauch 2b3f63ddcb
Merge pull request #513 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.20
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.19 to 2.9.20
2023-07-27 08:58:18 +02:00
dependabot[bot] e3600469c5
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.19 to 2.9.20.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.19...v2.9.20)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-27 06:26:10 +00:00
Joachim Bauch b758250aae
Merge pull request #515 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.28.0
build(deps): Bump github.com/nats-io/nats.go from 1.27.1 to 1.28.0
2023-07-27 08:24:46 +02:00
Joachim Bauch d1771af3ff
Merge pull request #514 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.2.1
build(deps): Bump coverallsapp/github-action from 2.2.0 to 2.2.1
2023-07-27 08:24:03 +02:00
Joachim Bauch ae94c9f194
Merge pull request #520 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.57.0
build(deps): Bump google.golang.org/grpc from 1.56.1 to 1.57.0
2023-07-27 08:23:46 +02:00
dependabot[bot] 99b3bedb8b
build(deps): Bump google.golang.org/grpc from 1.56.1 to 1.57.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.56.1 to 1.57.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.56.1...v1.57.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-26 20:20:28 +00:00
dependabot[bot] 25d5933f1e
build(deps): Bump github.com/nats-io/nats.go from 1.27.1 to 1.28.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.27.1 to 1.28.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.27.1...v1.28.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-20 20:10:50 +00:00
dependabot[bot] 3e7b57e005
build(deps): Bump coverallsapp/github-action from 2.2.0 to 2.2.1
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v2.2.0...v2.2.1)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-13 21:00:00 +00:00
Joachim Bauch 12de5a9b71
Update changelog for 1.1.3 2023-07-05 11:17:27 +02:00
Joachim Bauch 6e81becd4c
Merge pull request #507 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.31.0
build(deps): Bump google.golang.org/protobuf from 1.30.0 to 1.31.0
2023-07-05 08:33:21 +02:00
dependabot[bot] 05fe7e8f7c
build(deps): Bump google.golang.org/protobuf from 1.30.0 to 1.31.0
Bumps google.golang.org/protobuf from 1.30.0 to 1.31.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-26 20:07:23 +00:00
Joachim Bauch 5cba69686b
Merge pull request #506 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.27.1
build(deps): Bump github.com/nats-io/nats.go from 1.27.0 to 1.27.1
2023-06-26 09:14:56 +02:00
dependabot[bot] 31a57b65d5
build(deps): Bump github.com/nats-io/nats.go from 1.27.0 to 1.27.1
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.27.0 to 1.27.1.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.27.0...v1.27.1)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-22 20:57:25 +00:00
Joachim Bauch 03266eb323
Merge pull request #505 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.56.1
build(deps): Bump google.golang.org/grpc from 1.56.0 to 1.56.1
2023-06-22 08:07:43 +02:00
dependabot[bot] 694ef9a834
build(deps): Bump google.golang.org/grpc from 1.56.0 to 1.56.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.56.0 to 1.56.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.56.0...v1.56.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-21 20:57:44 +00:00
Joachim Bauch 3473c48a67
Merge pull request #504 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.19
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.18 to 2.9.19
2023-06-21 08:18:18 +02:00
dependabot[bot] cbd09f2bdf
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.18 to 2.9.19.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.18...v2.9.19)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-20 20:58:07 +00:00
Joachim Bauch 97abe3d016
Merge pull request #503 from strukturag/dependabot/go_modules/github.com/oschwald/maxminddb-golang-1.11.0
build(deps): Bump github.com/oschwald/maxminddb-golang from 1.10.0 to 1.11.0
2023-06-20 09:04:24 +02:00
dependabot[bot] 332bb01b80
build(deps): Bump github.com/oschwald/maxminddb-golang
Bumps [github.com/oschwald/maxminddb-golang](https://github.com/oschwald/maxminddb-golang) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/oschwald/maxminddb-golang/releases)
- [Commits](https://github.com/oschwald/maxminddb-golang/compare/v1.10.0...v1.11.0)

---
updated-dependencies:
- dependency-name: github.com/oschwald/maxminddb-golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 20:58:07 +00:00
Joachim Bauch 844def50df
Merge pull request #501 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.16.0
build(deps): Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0
2023-06-19 09:15:32 +02:00
Joachim Bauch 4a8f352304
Merge pull request #502 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.56.0
build(deps): Bump google.golang.org/grpc from 1.55.0 to 1.56.0
2023-06-19 09:14:23 +02:00
dependabot[bot] ed0041898f
build(deps): Bump google.golang.org/grpc from 1.55.0 to 1.56.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.55.0 to 1.56.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.55.0...v1.56.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-15 20:58:06 +00:00
dependabot[bot] b95c03babe
build(deps): Bump github.com/prometheus/client_golang
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.1 to 1.16.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-15 20:57:48 +00:00
Joachim Bauch 0c3c245c37
Merge pull request #499 from strukturag/remove-golang-1.18
Follow the Go release policy by supporting only the last two versions.
2023-06-15 11:53:00 +02:00
Joachim Bauch 4fa17018c8
Follow the Go release policy by supporting only the last two versions.
This removes support for Go 1.18
2023-06-15 11:41:30 +02:00
Joachim Bauch 18335071e9
Merge pull request #491 from strukturag/struct-chan
Use "struct{}" channel if only used as signaling mechanism.
2023-06-15 11:41:11 +02:00
Joachim Bauch 1fafd16d36
Merge pull request #498 from strukturag/docker-build-concurrent
docker: Don't build concurrently.
2023-06-15 11:40:57 +02:00
Joachim Bauch 6d492e3bfd
docker: Don't build concurrently.
This fixes flaky builds on GitHub CI.
2023-06-15 11:34:10 +02:00
Joachim Bauch fd29f83454
Use "struct{}" channel if only used as signaling mechanism. 2023-06-15 11:30:28 +02:00
Joachim Bauch e83cdd67ad
Merge pull request #497 from strukturag/roomsessionid-lock
Add missing lock for "roomSessionId" to avoid potential races.
2023-06-15 11:29:53 +02:00
Joachim Bauch 6da48e31b5
Add missing lock for "roomSessionId" to avoid potential races. 2023-06-15 11:22:26 +02:00
Joachim Bauch d22b4ef2ad
Merge pull request #493 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.15.1
build(deps): Bump github.com/prometheus/client_golang from 1.14.0 to 1.15.1
2023-06-14 08:30:05 +02:00
dependabot[bot] 4f5b5e3c83
build(deps): Bump github.com/prometheus/client_golang
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.14.0 to 1.15.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.14.0...v1.15.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-14 06:16:27 +00:00
Joachim Bauch 4d92d14e0a
Merge pull request #496 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.18
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.17 to 2.9.18
2023-06-14 08:15:42 +02:00
dependabot[bot] 7eb7f56f17
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.17 to 2.9.18.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.17...v2.9.18)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-13 20:57:49 +00:00
Joachim Bauch c0e60204ba
Merge pull request #495 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.17
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.15 to 2.9.17
2023-06-13 11:23:23 +02:00
Joachim Bauch 58e5d4a18b
Merge pull request #492 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-3.6.0
build(deps): Bump golangci/golangci-lint-action from 3.5.0 to 3.6.0
2023-06-13 11:12:04 +02:00
dependabot[bot] e92d4ebe65
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.15 to 2.9.17.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.15...v2.9.17)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 20:59:46 +00:00
dependabot[bot] ab4cd5d838
build(deps): Bump golangci/golangci-lint-action from 3.5.0 to 3.6.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.5.0...v3.6.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 20:57:39 +00:00
Joachim Bauch c11902b2f3
etcd: Use dedicated port for HTTP listener when testing to silent warning.
Follow-up to #475
2023-06-12 12:06:43 +02:00
Joachim Bauch b0f677dbc4
Merge pull request #475 from strukturag/dependabot/go_modules/go.etcd.io/etcd/server/v3-3.5.9
build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.7 to 3.5.9
2023-06-12 11:57:12 +02:00
Joachim Bauch 73808f5a68
Update for changed etcd config API. 2023-06-12 11:42:13 +02:00
Joachim Bauch a0c4919481
Merge pull request #478 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.26.0
build(deps): Bump github.com/nats-io/nats.go from 1.24.0 to 1.26.0
2023-06-12 11:29:57 +02:00
dependabot[bot] 186c7bf052
build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.7 to 3.5.9
Bumps [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd) from 3.5.7 to 3.5.9.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.7...v3.5.9)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 09:28:56 +00:00
Joachim Bauch ac74ced46c
Merge pull request #473 from strukturag/dependabot/go_modules/go.etcd.io/etcd/client/v3-3.5.9
build(deps): Bump go.etcd.io/etcd/client/v3 from 3.5.7 to 3.5.9
2023-06-12 11:27:11 +02:00
Joachim Bauch cd1a1aab0b
Merge pull request #472 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.55.0
build(deps): Bump google.golang.org/grpc from 1.53.0 to 1.55.0
2023-06-12 11:25:30 +02:00
Joachim Bauch 29be44cb74
Merge pull request #484 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.2.0
build(deps): Bump coverallsapp/github-action from 2.1.2 to 2.2.0
2023-06-12 11:23:12 +02:00
dependabot[bot] 26b5ebb948
build(deps): Bump go.etcd.io/etcd/client/v3 from 3.5.7 to 3.5.9
Bumps [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) from 3.5.7 to 3.5.9.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.7...v3.5.9)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/client/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 09:10:10 +00:00
dependabot[bot] 2175b372e2
build(deps): Bump google.golang.org/grpc from 1.53.0 to 1.55.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.53.0 to 1.55.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.53.0...v1.55.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 09:09:45 +00:00
dependabot[bot] bf488c4516
build(deps): Bump github.com/nats-io/nats.go from 1.24.0 to 1.26.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.24.0 to 1.26.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.24.0...v1.26.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 09:08:12 +00:00
dependabot[bot] dc1c777f41
build(deps): Bump coverallsapp/github-action from 2.1.2 to 2.2.0
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.1.2 to 2.2.0.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v2.1.2...v2.2.0)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-12 09:08:07 +00:00
Joachim Bauch db0b591366
Merge pull request #485 from strukturag/dependabot/pip/docs/sphinx-rtd-theme-1.2.2
build(deps): Bump sphinx-rtd-theme from 1.2.1 to 1.2.2 in /docs
2023-06-12 10:58:53 +02:00
Joachim Bauch 49ac751010
Merge pull request #490 from strukturag/fix-duplicate-join
Fix duplicate join events
2023-06-12 10:58:25 +02:00
Joachim Bauch bd4f6524cb
Include remaining hellos in message if error occurred. 2023-06-12 10:48:16 +02:00
Joachim Bauch 2fdd346766
Include times in log when message is ignored. 2023-06-12 10:47:52 +02:00
Joachim Bauch e6c35a3354
Merge pull request #487 from SystemKeeper/fix/486/multiple-backends-docker
Write backends comma-separated to config
2023-06-12 10:25:14 +02:00
Joachim Bauch 2e2d2f64e9
Merge pull request #488 from SystemKeeper/feat/noid/add-allowall-to-docker-image
Add allowall to docker image
2023-06-12 10:21:07 +02:00
Joachim Bauch 3ed2a0e4cd
Filter out duplicate "join" events.
Due to the asynchronous events, a session might received a "Joined" event
for the same (other) session twice, so filter these out on a per-session
level.
2023-06-12 10:19:00 +02:00
Joachim Bauch c4ae9cdc6c
Simplify "WaitForUsersJoined" in tests. 2023-06-12 10:03:28 +02:00
Joachim Bauch b45f4a2bfc
Merge pull request #482 from strukturag/simplify-vendor
Simplify vendoring.
2023-06-12 09:37:49 +02:00
Marcel Müller 0b212819c7 Add allowall to docker image
Signed-off-by: Marcel Müller <marcel-mueller@gmx.de>
2023-06-11 17:15:37 +02:00
Marcel Müller a083cf3001 Write backends comma-separated to config
Signed-off-by: Marcel Müller <marcel-mueller@gmx.de>
2023-06-11 17:01:22 +02:00
dependabot[bot] de9283969f
build(deps): Bump sphinx-rtd-theme from 1.2.1 to 1.2.2 in /docs
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.2.1 to 1.2.2.
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.2.1...1.2.2)

---
updated-dependencies:
- dependency-name: sphinx-rtd-theme
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-07 21:00:18 +00:00
Joachim Bauch 9dad3f8aad
CI: Print tarball contents. 2023-06-07 17:26:32 +02:00
Joachim Bauch 84151b295a
Simplify vendoring.
Instead of manually copying files to vendor folder, try to get "go" to
detect dependencies instead.
2023-06-07 17:26:32 +02:00
Joachim Bauch 5a84dd7d65
Merge pull request #481 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-3.5.0
build(deps): Bump golangci/golangci-lint-action from 3.4.0 to 3.5.0
2023-06-07 16:47:21 +02:00
dependabot[bot] 40eea01644
build(deps): Bump golangci/golangci-lint-action from 3.4.0 to 3.5.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.4.0...v3.5.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-02 20:57:44 +00:00
Joachim Bauch 0a343acacf
Merge pull request #466 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.1.2
build(deps): Bump coverallsapp/github-action from 2.1.0 to 2.1.2
2023-05-31 11:35:35 +02:00
Joachim Bauch 514fd8e0fd
Merge pull request #479 from strukturag/dependabot/pip/docs/sphinx-rtd-theme-1.2.1
build(deps): Bump sphinx-rtd-theme from 1.2.0 to 1.2.1 in /docs
2023-05-31 11:34:19 +02:00
Joas Schilling 3145cd3598
feat(scripts): Add a script to simplify the logs to make it more easily to trace a user/session
Signed-off-by: Joas Schilling <coding@schilljs.com>
2023-05-31 09:12:24 +02:00
dependabot[bot] e0dd2617e7
build(deps): Bump sphinx-rtd-theme from 1.2.0 to 1.2.1 in /docs
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.2.0 to 1.2.1.
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.2.0...1.2.1)

---
updated-dependencies:
- dependency-name: sphinx-rtd-theme
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-23 21:00:14 +00:00
Joachim Bauch df65394c12
Merge pull request #471 from strukturag/dependabot/pip/docs/mkdocs-1.4.3
build(deps): Bump mkdocs from 1.4.2 to 1.4.3 in /docs
2023-05-04 09:56:21 +02:00
dependabot[bot] b49797fc6c
build(deps): Bump mkdocs from 1.4.2 to 1.4.3 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.4.2...1.4.3)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-03 20:59:58 +00:00
Joachim Bauch fc169a8f59
Merge pull request #468 from strukturag/dependabot/pip/docs/sphinx-6.2.1
build(deps): Bump sphinx from 6.1.3 to 6.2.1 in /docs
2023-04-27 08:48:16 +02:00
dependabot[bot] 2c6b22640e
build(deps): Bump sphinx from 6.1.3 to 6.2.1 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 6.1.3 to 6.2.1.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v6.1.3...v6.2.1)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-25 20:59:52 +00:00
dependabot[bot] e2c981c000
build(deps): Bump coverallsapp/github-action from 2.1.0 to 2.1.2
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.1.0 to 2.1.2.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v2.1.0...v2.1.2)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-19 20:58:14 +00:00
Joachim Bauch dd37df185e
Merge pull request #459 from strukturag/dependabot/github_actions/peter-evans/create-or-update-comment-3
build(deps): Bump peter-evans/create-or-update-comment from 2 to 3
2023-04-11 22:11:27 +02:00
Joachim Bauch 7d547f41d3
Merge pull request #460 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.1.0
build(deps): Bump coverallsapp/github-action from 2.0.0 to 2.1.0
2023-04-11 22:10:37 +02:00
dependabot[bot] b6e83c7ffb
build(deps): Bump coverallsapp/github-action from 2.0.0 to 2.1.0
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 20:58:16 +00:00
dependabot[bot] 433748e5e9
build(deps): Bump peter-evans/create-or-update-comment from 2 to 3
Bumps [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) from 2 to 3.
- [Release notes](https://github.com/peter-evans/create-or-update-comment/releases)
- [Commits](https://github.com/peter-evans/create-or-update-comment/compare/v2...v3)

---
updated-dependencies:
- dependency-name: peter-evans/create-or-update-comment
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-05 20:57:24 +00:00
Joachim Bauch d4eafc851a
Merge pull request #456 from strukturag/dependabot/pip/docs/readthedocs-sphinx-search-0.3.1
build(deps): Bump readthedocs-sphinx-search from 0.2.0 to 0.3.1 in /docs
2023-04-03 10:57:23 +02:00
dependabot[bot] fc61335b2e
build(deps): Bump readthedocs-sphinx-search from 0.2.0 to 0.3.1 in /docs
Bumps [readthedocs-sphinx-search](https://github.com/readthedocs/readthedocs-sphinx-search) from 0.2.0 to 0.3.1.
- [Release notes](https://github.com/readthedocs/readthedocs-sphinx-search/releases)
- [Changelog](https://github.com/readthedocs/readthedocs-sphinx-search/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/readthedocs/readthedocs-sphinx-search/commits)

---
updated-dependencies:
- dependency-name: readthedocs-sphinx-search
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 21:03:42 +00:00
Joachim Bauch e4f7fc3020
Merge pull request #451 from strukturag/dependabot/github_actions/coverallsapp/github-action-2.0.0
build(deps): Bump coverallsapp/github-action from 1.2.4 to 2.0.0
2023-03-20 09:14:58 +01:00
Joachim Bauch 5e0c7623be
Merge pull request #450 from strukturag/refactor-allowed-ips
Add common code to handle allowed IPs.
2023-03-20 09:14:18 +01:00
dependabot[bot] 776a8080a7
build(deps): Bump coverallsapp/github-action from 1.2.4 to 2.0.0
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 1.2.4 to 2.0.0.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v1.2.4...v2.0.0)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-17 20:09:06 +00:00
Joachim Bauch be949f90b1
Add common code to handle allowed IPs. 2023-03-16 17:11:57 +01:00
Joachim Bauch 407fee2685
Merge pull request #448 from strukturag/stats-allow-nets
stats: Support configuring subnets for allowed IPs.
2023-03-16 16:36:18 +01:00
Joachim Bauch 1e8dd56f7e
stats: Support configuring subnets for allowed IPs. 2023-03-16 16:22:48 +01:00
Joachim Bauch 91ab020c3f
Merge pull request #449 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.30.0
build(deps): Bump google.golang.org/protobuf from 1.29.1 to 1.30.0
2023-03-16 16:21:49 +01:00
dependabot[bot] 171262b068
build(deps): Bump google.golang.org/protobuf from 1.29.1 to 1.30.0
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.1 to 1.30.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.1...v1.30.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-16 15:10:12 +00:00
Joachim Bauch 8a83976d38
Merge pull request #447 from strukturag/dependabot/github_actions/actions/setup-go-4
build(deps): Bump actions/setup-go from 3 to 4
2023-03-16 15:44:16 +01:00
dependabot[bot] 75347c553f
build(deps): Bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-15 20:57:44 +00:00
Joachim Bauch d49147f376
Merge pull request #446 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.29.1
build(deps): Bump google.golang.org/protobuf from 1.29.0 to 1.29.1
2023-03-15 08:17:54 +01:00
dependabot[bot] 2497e6585b
build(deps): Bump google.golang.org/protobuf from 1.29.0 to 1.29.1
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.0 to 1.29.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.0...v1.29.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-14 20:58:25 +00:00
Joachim Bauch 31aa0e2a11
Merge pull request #445 from strukturag/docker-latest-tag
CI: Make sure proxy Docker image is never tagged as "latest".
2023-03-13 16:11:09 +01:00
Joachim Bauch 799e01df86
CI: Make sure proxy Docker image is never tagged as "latest". 2023-03-13 16:07:00 +01:00
Joachim Bauch de701dbcae
Update changelog for 1.1.2 2023-03-13 10:27:33 +01:00
Joachim Bauch 49a832f41a
Merge pull request #443 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.29.0
build(deps): Bump google.golang.org/protobuf from 1.28.1 to 1.29.0
2023-03-13 10:20:37 +01:00
Joachim Bauch 30a2fb134e
tarball: Copy more "google.golang.org/protobuf" modules to vendor.
This is required for the "protoc-gen-go" to be able to compile.
Auto-detection only works for modules that are imported by the main
module.
2023-03-13 10:08:25 +01:00
Joachim Bauch 1b4f1b09dd
Merge pull request #439 from strukturag/docker-turn-settings
docker: Don't rely on default values when updating TURN settings.
2023-03-13 09:27:27 +01:00
dependabot[bot] 2174980c8a
build(deps): Bump google.golang.org/protobuf from 1.28.1 to 1.29.0
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.1 to 1.29.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.1...v1.29.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-08 20:58:02 +00:00
Joachim Bauch 48bdf22f27
Merge pull request #438 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.15
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.14 to 2.9.15
2023-03-06 11:05:51 +01:00
Joachim Bauch 91307acee3
Merge pull request #441 from strukturag/ci-deprecate-set-output
CI: Stop using deprecated "set-output".
2023-03-06 10:41:27 +01:00
dependabot[bot] 281bf73f62
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.14 to 2.9.15.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.14...v2.9.15)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 09:38:55 +00:00
Joachim Bauch 74fe96bb50
CI: Stop using deprecated "set-output". 2023-03-06 10:34:39 +01:00
Joachim Bauch 9b27cf8bd1
Merge pull request #442 from strukturag/update-protoc-gen-grpc
Update protoc-gen-go-grpc to v1.3.0
2023-03-06 10:33:49 +01:00
Joachim Bauch c2182f0440
make: Ensure folder for protoc-gen-go-grpc command exists for tarball. 2023-03-06 10:24:13 +01:00
Joachim Bauch 43ad9b794b
Update protoc-gen-go-grpc to v1.3.0 2023-03-06 10:18:33 +01:00
Joachim Bauch 93b9c08e90
CI: Increase timeout for golangci-lint to 2 minutes. 2023-03-06 09:56:59 +01:00
Joachim Bauch 149ea220a1
CI: Run golangci-lint with Go 1.20 2023-03-06 09:52:24 +01:00
Joachim Bauch 4f94d35eb1
Mark as "go 1.18". 2023-03-06 09:41:45 +01:00
Joachim Bauch 107426f96d
Merge pull request #440 from strukturag/go-mod-tidy
Run "go mod tidy -compat=1.18".
2023-03-06 09:37:49 +01:00
Joachim Bauch 5730d806ec
Run "go mod tidy -compat=1.18". 2023-03-06 09:29:39 +01:00
Joachim Bauch 28c1f39062
Merge pull request #434 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.24.0
build(deps): Bump github.com/nats-io/nats.go from 1.23.0 to 1.24.0
2023-03-06 09:19:24 +01:00
Joachim Bauch db6fb9fc6b
docker: Don't rely on default values when updating TURN settings. 2023-03-06 09:16:53 +01:00
Joachim Bauch 54fd71dae3
Merge pull request #437 from strukturag/dependabot/github_actions/coverallsapp/github-action-1.2.4
build(deps): Bump coverallsapp/github-action from 1.2.3 to 1.2.4
2023-03-06 09:04:53 +01:00
dependabot[bot] 947782367d
build(deps): Bump coverallsapp/github-action from 1.2.3 to 1.2.4
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v1.2.3...v1.2.4)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-01 21:00:38 +00:00
Joachim Bauch b9a6fad3fe
Merge pull request #436 from strukturag/dependabot/github_actions/coverallsapp/github-action-1.2.3
build(deps): Bump coverallsapp/github-action from 1.2.2 to 1.2.3
2023-03-01 08:10:15 +01:00
dependabot[bot] 066f03e5d6
build(deps): Bump coverallsapp/github-action from 1.2.2 to 1.2.3
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 1.2.2 to 1.2.3.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v1.2.2...v1.2.3)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 20:57:48 +00:00
Joachim Bauch af10d38c03
Merge pull request #435 from strukturag/dependabot/github_actions/coverallsapp/github-action-1.2.2
build(deps): Bump coverallsapp/github-action from 1.2.0 to 1.2.2
2023-02-28 13:47:13 +01:00
Joachim Bauch fa5c0bc637
Merge pull request #430 from SystemKeeper/add-skip-verify
Allow SKIP_VERIFY in docker image
2023-02-28 13:46:34 +01:00
dependabot[bot] 31b69e97c2
build(deps): Bump coverallsapp/github-action from 1.2.0 to 1.2.2
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 1.2.0 to 1.2.2.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/v1.2.0...v1.2.2)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 20:36:11 +00:00
Joachim Bauch ed5246b82d
Merge pull request #433 from strukturag/dependabot/github_actions/coverallsapp/github-action-1.2.0
build(deps): Bump coverallsapp/github-action from 1.1.3 to 1.2.0
2023-02-27 08:58:57 +01:00
dependabot[bot] a52dde3594
build(deps): Bump github.com/nats-io/nats.go from 1.23.0 to 1.24.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.23.0 to 1.24.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.23.0...v1.24.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-24 20:58:00 +00:00
dependabot[bot] 75a9391b74
build(deps): Bump coverallsapp/github-action from 1.1.3 to 1.2.0
Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 1.1.3 to 1.2.0.
- [Release notes](https://github.com/coverallsapp/github-action/releases)
- [Commits](https://github.com/coverallsapp/github-action/compare/1.1.3...v1.2.0)

---
updated-dependencies:
- dependency-name: coverallsapp/github-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-24 20:57:42 +00:00
Marcel Müller e32ede8717 Allow SKIP_VERIFY in docker image
Signed-off-by: Marcel Müller <marcel-mueller@gmx.de>
2023-02-23 10:35:30 +01:00
Zoey 6c6ebb647a
keep Docker images alpine based (#427)
keep Docker images alpine based

Signed-off-by: Zoey <zoey@z0ey.de>
2023-02-23 09:13:41 +01:00
Joachim Bauch 4a7bf38bde
Merge pull request #428 from 3x3cut0r/patch-1
TURN_API_KEY and TURN_SECRET fix
2023-02-23 08:43:52 +01:00
Joachim Bauch 84c8378fe8
Merge pull request #429 from SystemKeeper/patch-2
Fix example in docker README
2023-02-23 08:14:41 +01:00
Marcel Müller 42799f231b
Fix example in docker README 2023-02-22 21:31:25 +01:00
3x3cut0r 5921d5dcd3
TURN_API_KEY and TURN_SECRET fix
the docker environment variables TURN_API_KEY and TURN_SECRET didn't work
this is because the entrypoint.sh is only replacing the values but do not uncomment the line like the other replacements do
2023-02-22 19:59:10 +01:00
Joachim Bauch e93291b100
Update changelog for 1.1.1 2023-02-22 11:56:24 +01:00
Joachim Bauch 63c51309ce
Merge pull request #425 from strukturag/docker-fixes
Fix docker images.
2023-02-22 11:55:45 +01:00
Joachim Bauch 4a43fe1df9
docker: Switch to Debian base image.
With Go 1.20, a dependency to "libresolv.so.2" is added which is not available
in Alpine.
2023-02-22 11:51:07 +01:00
Joachim Bauch 737d637987
CI: Test docker images. 2023-02-22 11:51:07 +01:00
Joachim Bauch 3c6d2d1517
Update changelog for 1.1.0 2023-02-22 09:11:03 +01:00
Joachim Bauch 748f03cadc
Merge pull request #400 from strukturag/refactor-simplify
Various refactorings to simplify code
2023-02-22 08:42:06 +01:00
Joachim Bauch 3d3297f006
Merge pull request #390 from strukturag/dependabot/pip/docs/sphinx-6.1.3
build(deps): Bump sphinx from 5.3.0 to 6.1.3 in /docs
2023-02-22 08:40:46 +01:00
Joachim Bauch d49d3704fa
Use interface for client callbacks. 2023-02-22 08:34:17 +01:00
Joachim Bauch e9f80c6b4d
Start message processing earlier. 2023-02-22 08:34:17 +01:00
Joachim Bauch 20228b176f
Migrate to channel waiter helper class. 2023-02-22 08:34:16 +01:00
Joachim Bauch 5e7dec014a
Add helper class for channel waiters. 2023-02-22 08:34:16 +01:00
Joachim Bauch 8353cbbb0f
Migrate to closer helper class. 2023-02-22 08:34:15 +01:00
Joachim Bauch e6b2d1e0aa
Add helper class to handle close channels. 2023-02-22 08:34:15 +01:00
Joachim Bauch 2a47829164
Use empty struct for close channel. 2023-02-22 08:34:14 +01:00
Joachim Bauch 758899b745
Simplify close code of client to make clear when it gets closed internally. 2023-02-22 08:34:14 +01:00
Joachim Bauch b17eb584b4
Use simpler implementation without Context for notifier. 2023-02-22 08:34:14 +01:00
Joachim Bauch 0b4e48af3b
Remove unnecessary variable to flag closed loopback client. 2023-02-22 08:34:13 +01:00
Joachim Bauch dc55e7d5c8
Simplify stopping of deferred executor. 2023-02-22 08:34:11 +01:00
Joachim Bauch 15490b802a
Merge pull request #413 from strukturag/golang-1.20
Add support for Golang 1.20
2023-02-22 08:33:43 +01:00
Joachim Bauch 5757b9ca30
Merge pull request #421 from strukturag/internal-incall
Allow internal clients to set / change the "inCall" flags.
2023-02-22 08:31:08 +01:00
Joachim Bauch a34f3b6093
No longer support Golang 1.17.
While it might still compile with 1.17, it's no longer tested through CI
and at some point, features that require 1.18 will be used.
2023-02-22 08:19:30 +01:00
Joachim Bauch cb68e074bb
docker: Build with Golang 1.20 2023-02-22 08:17:07 +01:00
Joachim Bauch c8fa90d6ab
CI: Also test with Golang 1.20 2023-02-22 08:17:05 +01:00
Joachim Bauch f3ba485e9d
Merge pull request #423 from strukturag/dependabot/go_modules/github.com/golang-jwt/jwt/v4-4.5.0
build(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.3 to 4.5.0
2023-02-21 07:56:48 +01:00
dependabot[bot] f37b9b2fdd
build(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.3 to 4.5.0
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.3 to 4.5.0.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.3...v4.5.0)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 21:07:20 +00:00
Joachim Bauch 14876a92a8
Merge pull request #422 from strukturag/dependabot/go_modules/golang.org/x/net-0.7.0
build(deps): Bump golang.org/x/net from 0.5.0 to 0.7.0
2023-02-20 15:52:25 +01:00
Joachim Bauch 2f6e2ba87c
Allow internal clients to set / change the "inCall" flags. 2023-02-20 13:25:37 +01:00
Joachim Bauch 1997a8eecb
Add docs on internal clients. 2023-02-20 13:25:37 +01:00
dependabot[bot] 02892abcf4
build(deps): Bump golang.org/x/net from 0.5.0 to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-18 04:47:45 +00:00
dependabot[bot] c281195575
build(deps): Bump sphinx from 5.3.0 to 6.1.3 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.3.0 to 6.1.3.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.3.0...v6.1.3)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-15 13:06:24 +00:00
Joachim Bauch 6f5f069b80
Merge pull request #417 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.53.0
build(deps): Bump google.golang.org/grpc from 1.52.3 to 1.53.0
2023-02-15 14:04:52 +01:00
Joachim Bauch 9b4e369393
Merge pull request #418 from strukturag/dependabot/pip/docs/sphinx-rtd-theme-1.2.0
build(deps): Bump sphinx-rtd-theme from 1.1.1 to 1.2.0 in /docs
2023-02-15 14:04:01 +01:00
dependabot[bot] 323b59f477
build(deps): Bump sphinx-rtd-theme from 1.1.1 to 1.2.0 in /docs
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/readthedocs/sphinx_rtd_theme/releases)
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.1.1...1.2.0)

---
updated-dependencies:
- dependency-name: sphinx-rtd-theme
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-08 20:05:07 +00:00
dependabot[bot] 804d3d0b07
build(deps): Bump google.golang.org/grpc from 1.52.3 to 1.53.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.3 to 1.53.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.52.3...v1.53.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-08 20:02:16 +00:00
Joachim Bauch d56e67387b
Merge pull request #415 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.14
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.11 to 2.9.14
2023-02-07 12:24:38 +01:00
dependabot[bot] a0bb6a04a0
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.11 to 2.9.14.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.11...v2.9.14)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-07 09:09:35 +00:00
Joachim Bauch 955a623419
Merge pull request #416 from strukturag/golangci-fix
Explicitly use type "sysConn".
2023-02-07 10:06:37 +01:00
Joachim Bauch e1761da4a8
Explicitly use type "sysConn".
Fixes error with newer versions of golangci-lint.
2023-02-07 09:04:35 +01:00
Joachim Bauch 2df1dc467a
Merge pull request #410 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.52.3
build(deps): Bump google.golang.org/grpc from 1.52.1 to 1.52.3
2023-01-31 09:21:20 +01:00
Joachim Bauch 1bf860f9f1
Merge pull request #412 from strukturag/dependabot/github_actions/docker/build-push-action-4
build(deps): Bump docker/build-push-action from 3 to 4
2023-01-31 09:04:09 +01:00
Joachim Bauch 570baa78f4
Merge pull request #409 from strukturag/switchto-support
Implement "switchto" support
2023-01-31 08:53:28 +01:00
dependabot[bot] 44fe19f9e3
build(deps): Bump docker/build-push-action from 3 to 4
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-30 20:02:43 +00:00
Joachim Bauch bb24bf5f0d
Implement switchto messages. 2023-01-30 15:41:55 +01:00
dependabot[bot] 5266e58663
build(deps): Bump google.golang.org/grpc from 1.52.1 to 1.52.3
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.1 to 1.52.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.52.1...v1.52.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-26 20:03:14 +00:00
Joachim Bauch 69dfb0686f
API: Document switchto messages. 2023-01-26 16:57:37 +01:00
Joachim Bauch 1e1da6f8dd
Merge pull request #408 from strukturag/ci-docker-compose
CI: Update building with docker-compose
2023-01-26 08:32:39 +01:00
Joachim Bauch 42b18d5547
Add note on requiring docker-compose v2 to the readme. 2023-01-26 08:26:13 +01:00
Joachim Bauch 7d4ba11207
CI: Build with docker-compose 2.15.1 2023-01-26 08:23:53 +01:00
Joachim Bauch 7cf0bd8b88
Merge pull request #406 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.52.1
build(deps): Bump google.golang.org/grpc from 1.52.0 to 1.52.1
2023-01-26 08:03:30 +01:00
Joachim Bauch 335e280e62
Merge pull request #407 from strukturag/dependabot/pip/docs/readthedocs-sphinx-search-0.2.0
build(deps): Bump readthedocs-sphinx-search from 0.1.2 to 0.2.0 in /docs
2023-01-26 08:03:01 +01:00
Joachim Bauch 313dfa2c61
CI: Also run docker-compose jobs if Dockerfile is changed. 2023-01-26 08:02:17 +01:00
Joachim Bauch 50390ba1be
CI: Update condition when to run docker-compose jobs. 2023-01-26 07:59:20 +01:00
dependabot[bot] 1899628c1b
build(deps): Bump readthedocs-sphinx-search from 0.1.2 to 0.2.0 in /docs
Bumps [readthedocs-sphinx-search](https://github.com/readthedocs/readthedocs-sphinx-search) from 0.1.2 to 0.2.0.
- [Release notes](https://github.com/readthedocs/readthedocs-sphinx-search/releases)
- [Changelog](https://github.com/readthedocs/readthedocs-sphinx-search/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/readthedocs/readthedocs-sphinx-search/commits)

---
updated-dependencies:
- dependency-name: readthedocs-sphinx-search
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-25 20:05:32 +00:00
dependabot[bot] 067a69bc12
build(deps): Bump google.golang.org/grpc from 1.52.0 to 1.52.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.0 to 1.52.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.52.0...v1.52.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-25 20:02:29 +00:00
Joachim Bauch efe1ef368f
Merge pull request #405 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-3.4.0
build(deps): Bump golangci/golangci-lint-action from 3.3.1 to 3.4.0
2023-01-24 07:51:28 +01:00
dependabot[bot] 982ad47e95
build(deps): Bump golangci/golangci-lint-action from 3.3.1 to 3.4.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.1...v3.4.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 20:03:58 +00:00
Joachim Bauch 10fa5f03b1
Merge pull request #404 from strukturag/dependabot/go_modules/go.etcd.io/etcd/server/v3-3.5.7
build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.6 to 3.5.7
2023-01-23 09:43:03 +01:00
dependabot[bot] b22d88df71
build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.6 to 3.5.7
Bumps [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd) from 3.5.6 to 3.5.7.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Changelog](https://github.com/etcd-io/etcd/blob/main/Dockerfile-release.amd64)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.6...v3.5.7)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 08:35:31 +00:00
Joachim Bauch 4943403cd7
Merge pull request #403 from strukturag/dependabot/go_modules/go.etcd.io/etcd/client/v3-3.5.7
build(deps): Bump go.etcd.io/etcd/client/v3 from 3.5.6 to 3.5.7
2023-01-23 09:34:22 +01:00
dependabot[bot] 5c6ac999a6
build(deps): Bump go.etcd.io/etcd/client/v3 from 3.5.6 to 3.5.7
Bumps [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) from 3.5.6 to 3.5.7.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Changelog](https://github.com/etcd-io/etcd/blob/main/Dockerfile-release.amd64)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.6...v3.5.7)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/client/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 08:26:02 +00:00
Joachim Bauch fa645ad2d3
Merge pull request #402 from strukturag/dependabot/go_modules/go.etcd.io/etcd/api/v3-3.5.7
build(deps): Bump go.etcd.io/etcd/api/v3 from 3.5.6 to 3.5.7
2023-01-23 09:10:21 +01:00
dependabot[bot] 1b13494c04
build(deps): Bump go.etcd.io/etcd/api/v3 from 3.5.6 to 3.5.7
Bumps [go.etcd.io/etcd/api/v3](https://github.com/etcd-io/etcd) from 3.5.6 to 3.5.7.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Changelog](https://github.com/etcd-io/etcd/blob/main/Dockerfile-release.amd64)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.6...v3.5.7)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-20 20:03:10 +00:00
Joachim Bauch fdfd627b43
Merge pull request #399 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.23.0
build(deps): Bump github.com/nats-io/nats.go from 1.22.1 to 1.23.0
2023-01-19 12:00:07 +01:00
Joachim Bauch be39cee1ea
Merge pull request #397 from strukturag/etcd-update-timeout
Test: add timeout while waiting for etcd event.
2023-01-19 11:59:37 +01:00
dependabot[bot] a9b32ea833
build(deps): Bump github.com/nats-io/nats.go from 1.22.1 to 1.23.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.22.1 to 1.23.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.22.1...v1.23.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-18 20:03:08 +00:00
Joachim Bauch 86ee075a3b
Prevent endless loop in case of context timeout / cancellation. 2023-01-18 16:46:25 +01:00
Joachim Bauch 05b9f4d6c9
Always process initial joined / virtual session flags asynchronously.
Otherwise the ordering might be different as the initial messages could be
received before previous asynchronous messages.
2023-01-18 16:44:31 +01:00
Joachim Bauch 567183747a
Mark functions as test helpers. 2023-01-18 15:43:32 +01:00
Joachim Bauch f5261135d2
Fix error message to log what is actually expected. 2023-01-18 15:43:16 +01:00
Joachim Bauch b7f221705a
Close message channel before closing client.
Otherwise the call to "Close" might wait forever for the message processing
goroutine to finish.
2023-01-18 14:26:40 +01:00
Joachim Bauch 6395b87577
Test: log number of active read/write pumps in case of error.
Also dump goroutines to help tracking down what is blocking cleanup.
2023-01-18 14:26:40 +01:00
Joachim Bauch 89637c0a51
Test: add timeout while waiting for etcd event. 2023-01-17 15:41:51 +01:00
Joachim Bauch ef58f9087a
CI: Increase timeout for tests. 2023-01-17 15:15:00 +01:00
Joachim Bauch 8201e433d3
Merge pull request #396 from strukturag/test-count-goroutines
Fix goroutines leak check.
2023-01-17 14:58:25 +01:00
Joachim Bauch 0020076f2b
Run function to test for goroutine leaks as subtest.
This makes sure the test has been completely teared down before the
goroutines are counted and thus should prevent flaky failed tests.
2023-01-17 14:51:02 +01:00
Joachim Bauch 5a12959821
Test: Fix counting / comparing of goroutines. 2023-01-17 14:34:01 +01:00
Joachim Bauch 58bbe76b06
Merge pull request #395 from strukturag/handled-throttled-response
Improve handling of throttled responses from Nextcloud.
2023-01-17 13:44:00 +01:00
Joachim Bauch 5af8636573
Improve handling of throttled responses from Nextcloud. 2023-01-17 13:33:47 +01:00
Joachim Bauch 7bd6fdd93f
Merge pull request #394 from strukturag/messages-done-wg
Stop using WaitGroup to detect finished message processing.
2023-01-17 12:24:08 +01:00
Joachim Bauch 8de8b39a5c
Stop using WaitGroup to detect finished message processing.
This causes flaky races if "Wait" and "Add" are being used from different
goroutines.
2023-01-17 12:09:06 +01:00
Joachim Bauch 2582e4ffb4
Set "platforms" when building with docker-compose.
Follow-up to #384.
2023-01-17 11:59:38 +01:00
Joachim Bauch 0f9090bced
CI: Retry - Update permissions to deploy Docker images. 2023-01-17 11:56:33 +01:00
Joachim Bauch 374492a3a8
CI: Update permissions to deploy Docker images. 2023-01-17 11:48:31 +01:00
Joachim Bauch f1f16f6a22
Merge pull request #393 from strukturag/ci-permissions
CI: Setup permissions for workflows.
2023-01-17 11:42:34 +01:00
Joachim Bauch be4348fc3c
Merge pull request #384 from Zoey2936/master
add aarch64/arm64 docker build
2023-01-17 11:33:27 +01:00
Joachim Bauch e366def1ab
Merge pull request #387 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.11
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.10 to 2.9.11
2023-01-17 11:30:30 +01:00
Joachim Bauch a8ffcfa156
CI: Setup permissions for workflows. 2023-01-17 11:29:54 +01:00
dependabot[bot] f6519a70c9
build(deps): Bump github.com/nats-io/nats-server/v2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.10 to 2.9.11.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.10...v2.9.11)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-12 07:13:04 +00:00
Joachim Bauch d8927601be
Merge pull request #391 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.52.0
build(deps): Bump google.golang.org/grpc from 1.51.0 to 1.52.0
2023-01-12 08:11:55 +01:00
dependabot[bot] 8efb5c7f26
build(deps): Bump google.golang.org/grpc from 1.51.0 to 1.52.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.51.0 to 1.52.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.51.0...v1.52.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-11 20:03:28 +00:00
Joachim Bauch d7bbf2b0bd
Merge pull request #383 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.22.1
build(deps): Bump github.com/nats-io/nats.go from 1.21.0 to 1.22.1
2023-01-11 08:20:21 +01:00
Zoey 44a1a68db7
add aarch64/arm64 docker build
Signed-off-by: Zoey <zoey@z0ey.de>
2023-01-10 16:49:23 +01:00
dependabot[bot] 3226b32f28
build(deps): Bump github.com/nats-io/nats.go from 1.21.0 to 1.22.1
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.21.0 to 1.22.1.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.21.0...v1.22.1)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 07:12:24 +00:00
Joachim Bauch 5c9fdf8d4e
Merge pull request #382 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.10
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.9 to 2.9.10
2023-01-02 08:11:32 +01:00
dependabot[bot] 7db6619a07
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.9 to 2.9.10
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.9 to 2.9.10.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.9...v2.9.10)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-20 20:02:33 +00:00
Joachim Bauch c1a6a6f586
Merge pull request #377 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.9
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.8 to 2.9.9
2022-12-19 10:49:09 +01:00
Joachim Bauch bcd5180acb
Merge pull request #379 from strukturag/dependabot/github_actions/cirrus-actions/rebase-1.8
build(deps): Bump cirrus-actions/rebase from 1.7 to 1.8
2022-12-19 09:59:35 +01:00
dependabot[bot] 382898df53
build(deps): Bump cirrus-actions/rebase from 1.7 to 1.8
Bumps [cirrus-actions/rebase](https://github.com/cirrus-actions/rebase) from 1.7 to 1.8.
- [Release notes](https://github.com/cirrus-actions/rebase/releases)
- [Commits](https://github.com/cirrus-actions/rebase/compare/1.7...1.8)

---
updated-dependencies:
- dependency-name: cirrus-actions/rebase
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-13 20:02:04 +00:00
dependabot[bot] cf9128c677
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.8 to 2.9.9
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.8 to 2.9.9.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.8...v2.9.9)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-08 20:02:53 +00:00
Joachim Bauch b4d957fbc5
Merge pull request #374 from strukturag/dependabot/go_modules/github.com/golang-jwt/jwt/v4-4.4.3
build(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.2 to 4.4.3
2022-12-05 21:21:23 +01:00
Joachim Bauch b7e7170170
Merge pull request #375 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.21.0
build(deps): Bump github.com/nats-io/nats.go from 1.20.0 to 1.21.0
2022-12-05 21:20:56 +01:00
dependabot[bot] dd71201ed0
build(deps): Bump github.com/nats-io/nats.go from 1.20.0 to 1.21.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.20.0 to 1.21.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.20.0...v1.21.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 20:03:26 +00:00
dependabot[bot] d6cf0ec534
build(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.2 to 4.4.3
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.2 to 4.4.3.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.2...v4.4.3)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-29 20:02:29 +00:00
Joachim Bauch 3714de9822
Merge pull request #371 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.8
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.7 to 2.9.8
2022-11-24 08:47:48 +01:00
Joachim Bauch 00447dbc0d
Merge pull request #372 from strukturag/dependabot/go_modules/go.etcd.io/etcd/server/v3-3.5.6
build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.5 to 3.5.6
2022-11-24 08:44:34 +01:00
dependabot[bot] f425460fd9
build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.5 to 3.5.6
Bumps [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd) from 3.5.5 to 3.5.6.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Changelog](https://github.com/etcd-io/etcd/blob/main/Dockerfile-release.amd64)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.5...v3.5.6)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-23 20:06:11 +00:00
dependabot[bot] dcfc73e81b
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.7 to 2.9.8
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.7 to 2.9.8.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.7...v2.9.8)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-23 20:05:57 +00:00
Joachim Bauch b5fc698005
Merge pull request #367 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.7
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.6 to 2.9.7
2022-11-23 13:56:17 +01:00
Joachim Bauch fb2f1519f0
Merge pull request #364 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.14.0
build(deps): Bump github.com/prometheus/client_golang from 1.13.1 to 1.14.0
2022-11-23 13:56:00 +01:00
Joachim Bauch 6141847cec
Merge pull request #368 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.51.0
build(deps): Bump google.golang.org/grpc from 1.50.1 to 1.51.0
2022-11-23 13:55:39 +01:00
dependabot[bot] 24d3a365b7
build(deps): Bump google.golang.org/grpc from 1.50.1 to 1.51.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.50.1 to 1.51.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.50.1...v1.51.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-18 20:33:15 +00:00
dependabot[bot] ea0d36290e
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.6 to 2.9.7
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.6 to 2.9.7.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.6...v2.9.7)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-17 20:07:21 +00:00
Joachim Bauch 0ef230a83f
Merge pull request #366 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.20.0
build(deps): Bump github.com/nats-io/nats.go from 1.19.0 to 1.20.0
2022-11-14 10:31:00 +01:00
dependabot[bot] 800a823b3d
build(deps): Bump github.com/nats-io/nats.go from 1.19.0 to 1.20.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.19.0 to 1.20.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.19.0...v1.20.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 09:23:16 +00:00
Joachim Bauch 01c70b1182
Merge pull request #361 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.6
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.5 to 2.9.6
2022-11-14 10:22:36 +01:00
Joachim Bauch e51d8127f5
Merge pull request #363 from strukturag/dependabot/pip/docs/sphinx-rtd-theme-1.1.1
build(deps): Bump sphinx-rtd-theme from 1.1.0 to 1.1.1 in /docs
2022-11-14 08:48:40 +01:00
Joachim Bauch fdb71f8858
Merge pull request #365 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-3.3.1
build(deps): Bump golangci/golangci-lint-action from 3.3.0 to 3.3.1
2022-11-14 08:48:24 +01:00
dependabot[bot] 34baa0113a
build(deps): Bump golangci/golangci-lint-action from 3.3.0 to 3.3.1
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.0...v3.3.1)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-11 20:02:58 +00:00
dependabot[bot] e231ced83a
build(deps): Bump github.com/prometheus/client_golang
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.13.1 to 1.14.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.13.1...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 20:01:53 +00:00
dependabot[bot] 70fcd848b5
build(deps): Bump sphinx-rtd-theme from 1.1.0 to 1.1.1 in /docs
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/readthedocs/sphinx_rtd_theme/releases)
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.1.0...1.1.1)

---
updated-dependencies:
- dependency-name: sphinx-rtd-theme
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-07 20:47:46 +00:00
dependabot[bot] 324fe501b8
build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.5 to 2.9.6
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.5 to 2.9.6.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.5...v2.9.6)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-07 20:38:30 +00:00
Joachim Bauch b68ced9483
Merge pull request #359 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.5
Bump github.com/nats-io/nats-server/v2 from 2.9.3 to 2.9.5
2022-11-02 22:05:50 +01:00
Joachim Bauch a6becbd29f
Merge pull request #360 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.13.1
Bump github.com/prometheus/client_golang from 1.13.0 to 1.13.1
2022-11-02 22:05:34 +01:00
dependabot[bot] db03863346
Bump github.com/prometheus/client_golang from 1.13.0 to 1.13.1
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.13.0 to 1.13.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/v1.13.1/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.13.0...v1.13.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-02 20:21:18 +00:00
dependabot[bot] f24340ede6
Bump github.com/nats-io/nats-server/v2 from 2.9.3 to 2.9.5
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.3 to 2.9.5.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.3...v2.9.5)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-02 12:43:14 +00:00
Joachim Bauch 7ae4b79d02
Merge pull request #354 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.19.0
Bump github.com/nats-io/nats.go from 1.18.0 to 1.19.0
2022-11-02 13:42:11 +01:00
Joachim Bauch 7bce37f172
Merge pull request #357 from strukturag/dependabot/pip/docs/sphinx-rtd-theme-1.1.0
Bump sphinx-rtd-theme from 1.0.0 to 1.1.0 in /docs
2022-11-02 13:41:24 +01:00
Joachim Bauch 76a7a151d3
Merge pull request #358 from strukturag/dependabot/pip/docs/mkdocs-1.4.2
Bump mkdocs from 1.4.1 to 1.4.2 in /docs
2022-11-02 13:40:46 +01:00
dependabot[bot] d72083068a
Bump mkdocs from 1.4.1 to 1.4.2 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.4.1...1.4.2)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 20:44:06 +00:00
dependabot[bot] fb8e1f0bd6
Bump sphinx-rtd-theme from 1.0.0 to 1.1.0 in /docs
Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 1.0.0 to 1.1.0.
- [Release notes](https://github.com/readthedocs/sphinx_rtd_theme/releases)
- [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst)
- [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/1.0.0...1.1.0)

---
updated-dependencies:
- dependency-name: sphinx-rtd-theme
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 20:44:02 +00:00
dependabot[bot] f2c74307d9
Bump github.com/nats-io/nats.go from 1.18.0 to 1.19.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.18.0 to 1.19.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.18.0...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-27 20:23:53 +00:00
Joachim Bauch 48a857bf6b
Merge pull request #353 from strukturag/dependabot/github_actions/golangci/golangci-lint-action-3.3.0
Bump golangci/golangci-lint-action from 3.2.0 to 3.3.0
2022-10-24 08:41:41 +02:00
dependabot[bot] bc8d2ee3ca
Bump golangci/golangci-lint-action from 3.2.0 to 3.3.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.2.0...v3.3.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-21 20:23:59 +00:00
Joachim Bauch 374b8f935e
Merge pull request #350 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.50.1
Bump google.golang.org/grpc from 1.50.0 to 1.50.1
2022-10-18 08:45:44 +02:00
Joachim Bauch a5e7dfe896
Merge pull request #352 from strukturag/dependabot/pip/docs/mkdocs-1.4.1
Bump mkdocs from 1.4.0 to 1.4.1 in /docs
2022-10-18 08:45:07 +02:00
Joachim Bauch d8b051f6ff
Merge pull request #351 from strukturag/dependabot/pip/docs/sphinx-5.3.0
Bump sphinx from 5.2.3 to 5.3.0 in /docs
2022-10-18 08:44:33 +02:00
dependabot[bot] b65fe16002
Bump mkdocs from 1.4.0 to 1.4.1 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.4.0...1.4.1)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-17 20:36:23 +00:00
dependabot[bot] e965f03f39
Bump sphinx from 5.2.3 to 5.3.0 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.2.3 to 5.3.0.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.2.3...v5.3.0)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-17 20:36:20 +00:00
dependabot[bot] 275fecbb5a
Bump google.golang.org/grpc from 1.50.0 to 1.50.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.50.0 to 1.50.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.50.0...v1.50.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-17 20:11:10 +00:00
Joachim Bauch fd49e627a9
Merge pull request #349 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.18.0
Bump github.com/nats-io/nats.go from 1.17.0 to 1.18.0
2022-10-13 08:44:21 +02:00
dependabot[bot] 9798c77673
Bump github.com/nats-io/nats.go from 1.17.0 to 1.18.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats.go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-12 20:10:17 +00:00
Joachim Bauch ae26102447
Merge pull request #348 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.3
Bump github.com/nats-io/nats-server/v2 from 2.9.2 to 2.9.3
2022-10-12 08:44:29 +02:00
Joachim Bauch 64bdb36ce2
Merge pull request #346 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.50.0
Bump google.golang.org/grpc from 1.49.0 to 1.50.0
2022-10-12 08:20:21 +02:00
dependabot[bot] 8cff75accf
Bump github.com/nats-io/nats-server/v2 from 2.9.2 to 2.9.3
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.2 to 2.9.3.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.2...v2.9.3)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-11 20:11:20 +00:00
dependabot[bot] 7f133e5998
Bump google.golang.org/grpc from 1.49.0 to 1.50.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.49.0 to 1.50.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.49.0...v1.50.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-06 20:13:40 +00:00
Joachim Bauch c8ff009ecd
Merge pull request #334 from strukturag/dependabot/go_modules/go.etcd.io/etcd/server/v3-3.5.5
Bump go.etcd.io/etcd/server/v3 from 3.5.4 to 3.5.5
2022-10-04 10:27:33 +02:00
dependabot[bot] ad5e9244ef
Bump go.etcd.io/etcd/server/v3 from 3.5.4 to 3.5.5
Bumps [go.etcd.io/etcd/server/v3](https://github.com/etcd-io/etcd) from 3.5.4 to 3.5.5.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Changelog](https://github.com/etcd-io/etcd/blob/main/Dockerfile-release.amd64)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.4...v3.5.5)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/server/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-04 06:58:24 +00:00
Joachim Bauch a58f7ae118
Merge pull request #333 from strukturag/dependabot/go_modules/go.etcd.io/etcd/api/v3-3.5.5
Bump go.etcd.io/etcd/api/v3 from 3.5.4 to 3.5.5
2022-10-04 08:57:18 +02:00
Joachim Bauch 10488a6352
Merge pull request #344 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.2
Bump github.com/nats-io/nats-server/v2 from 2.9.0 to 2.9.2
2022-09-30 23:17:28 +02:00
Joachim Bauch b0b6f88a73
Merge pull request #345 from strukturag/dependabot/pip/docs/sphinx-5.2.3
Bump sphinx from 5.2.2 to 5.2.3 in /docs
2022-09-30 23:17:13 +02:00
dependabot[bot] ea112f79eb
Bump sphinx from 5.2.2 to 5.2.3 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.2.2 to 5.2.3.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.2.2...v5.2.3)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-30 21:11:19 +00:00
dependabot[bot] 9b20762fae
Bump github.com/nats-io/nats-server/v2 from 2.9.0 to 2.9.2
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.9.0 to 2.9.2.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.9.0...v2.9.2)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-30 20:25:52 +00:00
Joachim Bauch 5f716c3b64
Merge pull request #340 from strukturag/dependabot/pip/docs/mkdocs-1.4.0
Bump mkdocs from 1.3.1 to 1.4.0 in /docs
2022-09-28 08:42:15 +02:00
Joachim Bauch 19feb0c61f
Merge pull request #339 from strukturag/dependabot/pip/docs/sphinx-5.2.2
Bump sphinx from 5.1.1 to 5.2.2 in /docs
2022-09-28 08:41:44 +02:00
dependabot[bot] 1bbc177196
Bump mkdocs from 1.3.1 to 1.4.0 in /docs
Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.3.1...1.4.0)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-27 20:55:19 +00:00
dependabot[bot] 9a2e9a2ce6
Bump sphinx from 5.1.1 to 5.2.2 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.1.1 to 5.2.2.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.1.1...v5.2.2)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-27 20:55:16 +00:00
dependabot[bot] d8c58d87e0
Bump go.etcd.io/etcd/api/v3 from 3.5.4 to 3.5.5
Bumps [go.etcd.io/etcd/api/v3](https://github.com/etcd-io/etcd) from 3.5.4 to 3.5.5.
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Changelog](https://github.com/etcd-io/etcd/blob/main/Dockerfile-release.amd64)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.4...v3.5.5)

---
updated-dependencies:
- dependency-name: go.etcd.io/etcd/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-15 20:12:10 +00:00
Joachim Bauch af803cfbc7
Merge pull request #330 from strukturag/dependabot/go_modules/github.com/nats-io/nats-server/v2-2.9.0
Bump github.com/nats-io/nats-server/v2 from 2.8.4 to 2.9.0
2022-09-15 15:01:37 +02:00
dependabot[bot] a83c799eaf
Bump github.com/nats-io/nats-server/v2 from 2.8.4 to 2.9.0
Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.8.4 to 2.9.0.
- [Release notes](https://github.com/nats-io/nats-server/releases)
- [Changelog](https://github.com/nats-io/nats-server/blob/main/.goreleaser.yml)
- [Commits](https://github.com/nats-io/nats-server/compare/v2.8.4...v2.9.0)

---
updated-dependencies:
- dependency-name: github.com/nats-io/nats-server/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 20:10:31 +00:00
Joachim Bauch 42a8aab307
Log error responses when deleting proxy publishers / subscribers. 2022-08-30 16:02:07 +02:00
Joachim Bauch 1774d56d7d
Merge pull request #327 from strukturag/fix-proxy-deadlock
Fix deadlock for proxy connection issues
2022-08-30 15:45:31 +02:00
Joachim Bauch 9a868c6d91
Handle error responses when creating proxy publisher / subscriber. 2022-08-30 11:49:08 +02:00
Joachim Bauch 960cb0ea3c
Use "defer" to re-acquire released lock. 2022-08-30 11:47:38 +02:00
Joachim Bauch cdbc177179
Schedule reconnect asynchronously if ping could not be sent. 2022-08-30 11:46:45 +02:00
Joachim Bauch 4dfffca285
Merge pull request #326 from strukturag/proxy-backendclient
Use proxy from environment for backend client requests.
2022-08-30 10:34:57 +02:00
Joachim Bauch f1f018e338
Use proxy from environment for backend client requests. 2022-08-30 10:06:06 +02:00
Joachim Bauch 06a1d072f1
Merge pull request #324 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.49.0
Bump google.golang.org/grpc from 1.48.0 to 1.49.0
2022-08-25 08:03:03 +02:00
dependabot[bot] 708a513970
Bump google.golang.org/grpc from 1.48.0 to 1.49.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.48.0 to 1.49.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.48.0...v1.49.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-24 20:08:52 +00:00
Joachim Bauch e01881a040
Merge pull request #322 from morph027/remove-@resources-from-SystemCallFilter
remove @resources from SystemCallFilter
2022-08-23 09:37:31 +02:00
morph027 8fffe72c07
remove @resources from SystemCallFilter
Signed-off-by: morph027 <stefan.heitmueller@gmx.com>
2022-08-19 12:34:51 +02:00
Joachim Bauch ba8b9f12a4
Merge pull request #320 from strukturag/dependabot/go_modules/github.com/pion/sdp/v3-3.0.6
Bump github.com/pion/sdp/v3 from 3.0.5 to 3.0.6
2022-08-12 10:50:17 +02:00
dependabot[bot] 1ecf795f33
Bump github.com/pion/sdp/v3 from 3.0.5 to 3.0.6
Bumps [github.com/pion/sdp/v3](https://github.com/pion/sdp) from 3.0.5 to 3.0.6.
- [Release notes](https://github.com/pion/sdp/releases)
- [Commits](https://github.com/pion/sdp/compare/v3.0.5...v3.0.6)

---
updated-dependencies:
- dependency-name: github.com/pion/sdp/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-10 20:08:31 +00:00
Joachim Bauch d2f4662232
Merge pull request #317 from strukturag/dependabot/go_modules/github.com/oschwald/maxminddb-golang-1.10.0
Bump github.com/oschwald/maxminddb-golang from 1.9.0 to 1.10.0
2022-08-09 09:26:46 +02:00
dependabot[bot] ce1862bffc
Bump github.com/oschwald/maxminddb-golang from 1.9.0 to 1.10.0
Bumps [github.com/oschwald/maxminddb-golang](https://github.com/oschwald/maxminddb-golang) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/oschwald/maxminddb-golang/releases)
- [Commits](https://github.com/oschwald/maxminddb-golang/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/oschwald/maxminddb-golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-09 07:11:44 +00:00
Joachim Bauch 2cc0a3b2e5
Merge pull request #316 from strukturag/dependabot/go_modules/github.com/prometheus/client_golang-1.13.0
Bump github.com/prometheus/client_golang from 1.12.2 to 1.13.0
2022-08-09 09:10:57 +02:00
dependabot[bot] bdd7d57ac6
Bump github.com/prometheus/client_golang from 1.12.2 to 1.13.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.12.2 to 1.13.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.12.2...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-08 20:11:03 +00:00
Joachim Bauch f40fd33d9c
Merge pull request #314 from strukturag/improve-docker
Official docker images.
2022-08-05 15:59:54 +02:00
Joachim Bauch c302f4f5a1
CI: Build and upload images. 2022-08-05 15:32:16 +02:00
Joachim Bauch b2e8217c1f
Add official docker images. 2022-08-05 15:32:10 +02:00
Joachim Bauch 2394c09013
Merge pull request #313 from strukturag/vendor-protobuf
vendor: Automatically vendor protobuf modules.
2022-08-05 10:43:24 +02:00
Joachim Bauch 5843555f6f
vendor: Automatically vendor protobuf modules. 2022-08-05 10:20:56 +02:00
Joachim Bauch ff032ddec5
Update comment to match new gofmt output. 2022-08-05 10:20:43 +02:00
Joachim Bauch 5f25c1c453
Fix file mode, should not be executable. 2022-08-05 09:45:58 +02:00
Joachim Bauch 5b34d8cc3f
Merge pull request #312 from solracsf/patch-1
Switch to apt-get on CLI
2022-08-05 09:04:11 +02:00
Git'Fellow 635c5ce9bd
Switch to apt-get on CLI
`apt` does not have a stable CLI interface
2022-08-04 19:53:15 +02:00
Joachim Bauch 33dc5a554b
Update changelog for 1.0.0 2022-08-04 11:52:36 +02:00
Joachim Bauch b28a8b163b
Fix typo. 2022-08-04 09:12:27 +02:00
Joachim Bauch aaa9b2dde2
Merge pull request #251 from strukturag/jwt-auth
Support hello auth version "2.0" with JWT
2022-08-04 09:09:18 +02:00
Joachim Bauch cbb6d9ca53
Update capabilities if no hello v2 token key is found in cache.
This is necessary to detect updated Talk setups where the signaling server
might have cached capabilities without the v2 token key but the clients
are trying to connect with a hello v2 token. Fetch updated capabilities in
such cases (but throttle to about one invalidation per minute).
2022-08-04 08:55:21 +02:00
Joachim Bauch 184c941f8a
Support plain Ed25519 key as returned by Nextcloud / PHP. 2022-08-03 16:33:48 +02:00
Joachim Bauch 0338e9db42
Document hello version 2.0 authentication. 2022-08-03 16:33:48 +02:00
Joachim Bauch 9c159dc4f8
Add feature flag for hello version 2.0 2022-08-03 16:33:47 +02:00
Joachim Bauch 156bc360ff
Add hello version "2.0" that authenticates connections using a JWT. 2022-08-03 16:33:45 +02:00
Joachim Bauch 88e4c5f2c3
Merge pull request #310 from strukturag/golang-1.19
CI: Also test with Golang 1.19
2022-08-03 16:32:35 +02:00
Joachim Bauch b14392b367
CI: Fix condition to run tarball tests. 2022-08-03 16:26:06 +02:00
Joachim Bauch aa96695cc1
CI: Also test with Golang 1.19 2022-08-03 16:24:23 +02:00
Joachim Bauch 82e3b62c34
Merge pull request #306 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.28.1
Bump google.golang.org/protobuf from 1.28.0 to 1.28.1
2022-07-29 09:13:16 +02:00
Joachim Bauch 6e25490401
Merge pull request #308 from strukturag/dependabot/github_actions/actions/download-artifact-3
Bump actions/download-artifact from 2 to 3
2022-07-28 22:32:44 +02:00
Joachim Bauch 58d5a7c3d8
Merge pull request #307 from strukturag/dependabot/github_actions/actions/upload-artifact-3
Bump actions/upload-artifact from 2 to 3
2022-07-28 22:32:13 +02:00
dependabot[bot] 2d4ffcaeda
Bump actions/download-artifact from 2 to 3
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-28 20:09:27 +00:00
dependabot[bot] 04983e4b2c
Bump actions/upload-artifact from 2 to 3
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-28 20:09:25 +00:00
dependabot[bot] 004252d767
Bump google.golang.org/protobuf from 1.28.0 to 1.28.1
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.0 to 1.28.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.0...v1.28.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-28 20:09:11 +00:00
Joachim Bauch fee5d22afb
docs: Pin markdown to 3.3.7 2022-07-28 09:38:58 +02:00
Joachim Bauch 417a77aeae
Merge pull request #305 from strukturag/docs-pin-dependencies
docs: update and pin dependencies
2022-07-28 09:34:08 +02:00
Joachim Bauch 556ff5230d
docs: Pin jinja2 to 3.1.2 2022-07-28 09:24:37 +02:00
Joachim Bauch 485991033d
docs: Update and pin mkdocs to 1.3.1. 2022-07-28 09:24:28 +02:00
Joachim Bauch 474987662a
docs: Sort requirements. 2022-07-28 09:09:06 +02:00
Joachim Bauch cded46e5cf
Merge pull request #300 from strukturag/tarball-vendored-dependencies
make: Include vendored dependencies in tarball.
2022-07-28 08:20:19 +02:00
Joachim Bauch 75ae615d80
CI: Test if vendored tarball works. 2022-07-27 17:28:18 +02:00
Joachim Bauch 3ae23f0336
make: Include vendored dependencies in tarball. 2022-07-27 17:28:18 +02:00
Joachim Bauch 57b74e57a8
Merge pull request #303 from strukturag/dependabot/pip/docs/sphinx-5.1.1
Bump sphinx from 5.0.2 to 5.1.1 in /docs
2022-07-27 17:16:49 +02:00
dependabot[bot] b478e875fe
Bump sphinx from 5.0.2 to 5.1.1 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.0.2 to 5.1.1.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.0.2...v5.1.1)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-27 14:55:24 +00:00
Joachim Bauch def519b9cf
Merge pull request #302 from strukturag/fix-markdown
Fix RTD builds
2022-07-27 16:54:34 +02:00
Joachim Bauch e40940c25b
docs: Pin version of markdown to < 3.4.0 2022-07-27 16:52:11 +02:00
Joachim Bauch a9280f1b43
Merge pull request #301 from strukturag/sdp-v3
Update to github.com/pion/sdp v3.0.5
2022-07-27 16:25:49 +02:00
Joachim Bauch e54fcf9559
Update to github.com/pion/sdp v3.0.5 2022-07-27 16:18:45 +02:00
Joachim Bauch dcc583736a
Merge pull request #297 from strukturag/dependabot/go_modules/google.golang.org/grpc-1.48.0
Bump google.golang.org/grpc from 1.47.0 to 1.48.0
2022-07-25 15:01:12 +02:00
dependabot[bot] 45c290a9fb
Bump google.golang.org/grpc from 1.47.0 to 1.48.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.47.0 to 1.48.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.47.0...v1.48.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-13 20:12:16 +00:00
Joachim Bauch 51fb410c28
Merge pull request #296 from strukturag/clustered-session-limit
Implement per-backend session limit for clusters.
2022-07-13 12:04:49 +02:00
Joachim Bauch deaa17acc5
Implement per-backend session limit for clusters. 2022-07-13 11:52:20 +02:00
Joachim Bauch 12a8fa98d0
Merge pull request #295 from strukturag/virtualsessions-tests
Add tests for virtual sessions.
2022-07-12 15:21:29 +02:00
Joachim Bauch 706a876fc3
Send initial user flags to internal sessions asynchronously. 2022-07-12 14:16:33 +02:00
Joachim Bauch 532587eb9f
Don't log message as string twice in case of errors. 2022-07-12 13:31:40 +02:00
Joachim Bauch f0eb80b6fd
Add tests for virtual sessions. 2022-07-12 12:10:01 +02:00
Joachim Bauch faf7544f2d
Add methods to get recipient of control/message messages. 2022-07-12 12:10:00 +02:00
Joachim Bauch 4f84d3ad0d
Fix accessing sender when checking received control messages. 2022-07-12 11:55:51 +02:00
Joachim Bauch 7d9970713d
Add methods to send internal messages. 2022-07-12 11:55:26 +02:00
Joachim Bauch e101e74672
Fix sending messages to virtual sessions on different servers. 2022-07-12 11:54:41 +02:00
Joachim Bauch 6173a350a1
Add method to get virtual sessions of a client session. 2022-07-12 11:11:52 +02:00
Joachim Bauch 8ea6072de5
Set userid / userdata when removing virtual session in backend. 2022-07-12 11:11:19 +02:00
Joachim Bauch d2036fcbd6
Add unexpected messages to ignore list instead of returning error. 2022-07-12 10:10:15 +02:00
Joachim Bauch d1c5d785c8
Fix deadlock if virtual session leaves room. 2022-07-12 10:09:22 +02:00
Joachim Bauch 5fc61b15b6
Add more "String()" methods to help with debugging. 2022-07-12 10:07:23 +02:00
Joachim Bauch 13d2795b00
Merge pull request #294 from strukturag/synchronous-roomsession-disconnect
Disconnect sessions with the same room session id synchronously.
2022-07-11 10:53:05 +02:00
Joachim Bauch 95d5e50705
Disconnect sessions with the same room session id synchronously.
Previously there was a difference in the events being received by other
sessions for the clustered case compared to the single-server case.
Now the events / ordering are the same for both cases.
2022-07-11 10:39:06 +02:00
Joachim Bauch 3de149c7ae
Merge pull request #293 from strukturag/fix-unsharescreen
Fix handling of "unshareScreen" messages and add test.
2022-07-08 17:18:15 +02:00
Joachim Bauch 66e502dc9b
Fix handling of "unshareScreen" messages and add test.
Also update tests for "requestoffer" / "sendoffer".
2022-07-08 17:09:22 +02:00
Joachim Bauch 4770fc8ad6
Merge pull request #292 from strukturag/more-tests
Add more tests
2022-07-08 16:00:12 +02:00
Joachim Bauch 608415c3ff
Track anonymous sessions not in rooms instead of clients.
Otherwise it would be possible for clients to reconnect to reset their
timer to join a room.
2022-07-08 15:52:32 +02:00
Joachim Bauch ab26dfe90d
Add testcase for initial permissions in a room. 2022-07-08 15:11:50 +02:00
Joachim Bauch 545bce0082
Fix and add test for clustered disconnect of duplicate room ids. 2022-07-08 14:57:29 +02:00
Joachim Bauch e99d843c65
Add testcase for anonymous clients needing to join new room. 2022-07-08 14:21:50 +02:00
Joachim Bauch a1f62ffd18
Add missing lock call for timeouts in waiter methods. 2022-07-08 14:19:13 +02:00
Joachim Bauch 6e9a36a434
Merge pull request #291 from strukturag/etcd-watch-race
etcd: Fix race in initialized event.
2022-07-08 11:02:55 +02:00
Joachim Bauch 40e1b208c0
etcd: Fix race in initialized event.
It could happen that the initialized event was triggered even though the
watch was not fully created yet.
2022-07-08 10:56:36 +02:00
Joachim Bauch 0165788fe3
Merge pull request #290 from strukturag/ci-fix-slow-cpu
Fix issues on slow CPUs
2022-07-08 10:12:18 +02:00
Joachim Bauch 8704bc3b5b
Make sure replaced files have different modification times.
Depending on filesystem time resolution, the modified certificates could
have the same timestamp in tests, causing the reload to fail.
2022-07-08 10:06:15 +02:00
Joachim Bauch 75e5013dd8
Create copy of filtered messages.
Otherwise it can happen that the modified message is reused and sent to
another session.
2022-07-08 09:49:32 +02:00
Joachim Bauch 5296e09a2e
grpc: Always use reloadable credentials.
Settings the callaback functions on tls.Config seems to causes issues on
slow CPUs (e.g. GitHub actions) where old certificates might be reused.
2022-07-08 09:34:17 +02:00
Joachim Bauch c463791e21
CI: Don't retry tests in case of failures.
Flaky tests should be fixed instead.
2022-07-08 08:33:57 +02:00
Joachim Bauch a9517feebb
Merge pull request #289 from strukturag/jwt-v4
Update to Go module version of github.com/golang-jwt/jwt
2022-07-07 17:16:53 +02:00
Joachim Bauch 924fce6713
Stop using deprecated "jwt.StandardClaims". 2022-07-07 17:12:21 +02:00
Joachim Bauch 8a97fa7f5e
Update to Go module version of github.com/golang-jwt/jwt 2022-07-07 17:04:34 +02:00
Joachim Bauch ce5d74bbec
Run "go mod tidy". 2022-07-07 17:03:41 +02:00
Joachim Bauch 5b3b147794
Merge pull request #276 from Tachi107/systemd-hardening
dist: harden systemd service unit
2022-07-07 16:34:27 +02:00
Joachim Bauch d3f8876d25
Merge pull request #281 from strukturag/refactor-async-events
Clustering support
2022-07-07 16:24:15 +02:00
Joachim Bauch 042d447ab4
Merge pull request #288 from strukturag/initial-welcome
Send initial "welcome" message when clients connect.
2022-07-07 10:10:43 +02:00
Joachim Bauch 243411671d
Add documentation for welcome message. 2022-07-07 10:04:13 +02:00
Joachim Bauch f7db8a38e1
Send initial "welcome" message when clients connect.
This can be used to detect server features before performing the
actual "hello" handshake.
2022-07-07 09:57:10 +02:00
Joachim Bauch ad1dea2780
Only send single "incall" message with "all: true" in clustered setup.
Previously each instance would send one message to all users in the cluster.
2022-07-04 15:26:12 +02:00
Joachim Bauch 32a2f822e0
Merge pull request #287 from strukturag/arbitrary-capabilities
Support arbitrary capabilities values.
2022-07-04 15:00:41 +02:00
Joachim Bauch ec62503bd3
Support arbitrary capabilities values. 2022-07-04 13:53:02 +02:00
Joachim Bauch b2da4002a4
grpc: Reload certificate if file has changed and support mutual authentication. 2022-07-04 11:05:21 +02:00
Joachim Bauch 06e9ae0644
Add certificate reloader class. 2022-07-04 10:50:44 +02:00
Joachim Bauch 44bf8b74c2
grpc: Make sure DNS discovery of clients continues if initial lookup failed. 2022-07-01 11:42:49 +02:00
Joachim Bauch 15dabeee1e
grpc: Check clients for own server id asychronously.
The external address of the (own) GRPC server might only be reachable after
some time, so performing the check only initially could fail but will
succeed later.
2022-07-01 10:22:16 +02:00
Joachim Bauch 715b2317df
Add helper to wait with exponential backoff. 2022-07-01 10:21:49 +02:00
Joachim Bauch 24eab34da7
Allow configuring backends through etcd. 2022-06-30 11:35:36 +02:00
Joachim Bauch 01858a89f4
grpc: Enable DNS discovery for GRPC clients. 2022-06-30 11:35:35 +02:00
Joachim Bauch 20cc51c2fe
grpc: Automatically detect if a target is the current server itself.
This allows configuring the same list of targets for all instances without
having to setup the "own" address differently for each server.
2022-06-30 11:35:35 +02:00
Joachim Bauch 5a242b2570
readme: Add note on clustering. 2022-06-30 11:35:34 +02:00
Joachim Bauch 0e144906a4
Added tests for clustered behaviour. 2022-06-30 11:35:33 +02:00
Joachim Bauch dcb5be956c
Implement "sendoffer" for remote sessions. 2022-06-30 11:35:33 +02:00
Joachim Bauch 36710c8aa9
Improve detection of decodable sessions that were created on a different server. 2022-06-30 11:35:32 +02:00
Joachim Bauch 25dabf910d
Allow configuring GRPC targets through etcd. 2022-06-30 11:35:32 +02:00
Joachim Bauch b6e419f18a
Add metrics for GRPC calls. 2022-06-30 11:35:31 +02:00
Joachim Bauch b315c09a3b
Allow configuring GRPC transport credentials. 2022-06-30 11:35:30 +02:00
Joachim Bauch 6f64ff901d
Create temporary connection to proxy used by remote publisher. 2022-06-30 11:35:30 +02:00
Joachim Bauch 2ca9fb21c4
Add SingleNotifier class. 2022-06-30 11:35:29 +02:00
Joachim Bauch a0d3af14e0
Add initial clustering support. 2022-06-30 11:35:28 +02:00
Joachim Bauch 7b24dc1d1d
Add grpc 1.47.0 / protobuf 1.28.0 2022-06-24 13:37:38 +02:00
Joachim Bauch ece2903413
Trigger "joined" events through async messages. 2022-06-24 13:37:37 +02:00
Joachim Bauch 0115c97946
Refactor asynchronous events to central location. 2022-06-24 13:37:35 +02:00
Joachim Bauch ddb7ece622
Merge pull request #283 from strukturag/etcd-tests-running
Fix testing etcd server not starting up if etcd is running on host.
2022-06-24 13:37:11 +02:00
Joachim Bauch a761f135a8
Fix testing etcd server not starting up if etcd is running on host. 2022-06-24 13:30:32 +02:00
Joachim Bauch a06bc333d2
make: remove leftover easyjson bootstrap files in "clean" target 2022-06-24 13:01:03 +02:00
Joachim Bauch af4bd51ec0
Merge pull request #282 from strukturag/refactor-etcd
Move common etcd code to own class.
2022-06-24 12:20:15 +02:00
Joachim Bauch b0624be0a9
Move etcd configuration to common section. 2022-06-24 11:15:29 +02:00
Joachim Bauch 134d22bfe7
Move common etcd code to own class. 2022-06-24 11:15:29 +02:00
Joachim Bauch 28b94191b1
Merge pull request #277 from strukturag/dependabot/pip/docs/sphinx-5.0.2
Bump sphinx from 5.0.1 to 5.0.2 in /docs
2022-06-20 08:47:39 +02:00
dependabot[bot] 83ce95f39f
Bump sphinx from 5.0.1 to 5.0.2 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-16 20:31:46 +00:00
Joachim Bauch 79532954da
readme: Update link to documentation. 2022-06-15 09:13:14 +02:00
Joachim Bauch 3393ffde8a
Merge pull request #275 from Tachi107/systemd-sysusers
dist: add systemd sysusers file
2022-06-15 08:39:27 +02:00
Andrea Pappacoda 15a9bea122
dist: harden systemd service unit
With this patch the systemd service will now run in a hardened sandbox
that limits the kinds of subsystems available to the unit. This improves
the overall security of the system, as nextcloud-spreed-signaling
becomes almost pointless to exploit.

The most notable changes include:

- The entire fie system is mounted read-only with ProtectSystem=strict
- No binaries are executable, apart from /usr/bin/signaling, with
  NoExecPaths=/ and ExecPaths=/usr/bin/signaling
- The service cannot see any user on the system apart from the one that
  is running the process, with PrivateUsers=yes
- Most of the /proc subsystem is inaccessible, and things like system
  stats may be unavailabe, with ProcSubset=pid
- All home directories are inaccessible, with ProtectHome=yes
- The kinds of permitted system calls are limited, via SystemCallFilter=

I highly recommend you to read the systemd.exec(5) manual page to fully
understand what these options do and how they can protect the system.
https://www.freedesktop.org/software/systemd/man/systemd.exec.html
2022-06-15 00:00:20 +02:00
Andrea Pappacoda f09c343592
dist: add systemd sysusers file
The systemd unit makes use of the user "signaling", but it is not
created in any way, so the directive is ignored.

By creating a sysusers file it is possible to tell the system to create
a "signaling" user so that the directive is honoured.

For more information, see the sysusers.d manpage, at
https://www.freedesktop.org/software/systemd/man/sysusers.d.html

This is mainly useful on systems running systemd, but the sysusers
concept is implemented also by other projects that don't use systemd,
like opensysusers, originated from Artix Linux.
2022-06-14 22:30:31 +02:00
Joachim Bauch da1efac59d
make: No need to run easyjson against room.go.
Doesn't define any JSON structs, so easyjson is no longer needed.
2022-06-14 16:50:34 +02:00
Joachim Bauch 4bedfdf780
Merge pull request #274 from strukturag/ignore-room-message-not-joined
Fix check for async room messages received while not joined to a room.
2022-06-14 16:44:14 +02:00
Joachim Bauch 078768f9c8
Fix check for async room messages received while not joined to a room. 2022-06-14 16:38:29 +02:00
Joachim Bauch 26f9edd476
Merge pull request #270 from strukturag/dependabot/pip/docs/sphinx-5.0.1
Bump sphinx from 5.0.0 to 5.0.1 in /docs
2022-06-03 08:25:44 +02:00
dependabot[bot] 31e7923ec1
Bump sphinx from 5.0.0 to 5.0.1 in /docs
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.0.0...v5.0.1)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-02 20:57:29 +00:00
Joachim Bauch 6a1a00551d
Merge pull request #269 from strukturag/dont_run_mod_tidy
Don't run "go mod tidy" while building.
2022-06-02 15:58:11 +02:00
Joachim Bauch b83bf7cb5d
Don't run "go mod tidy" while building. 2022-06-02 15:52:29 +02:00
178 changed files with 28302 additions and 5385 deletions

View file

@ -1,5 +1,3 @@
/bin
/docker/janus
/Dockerfile
/docker/*/Dockerfile
/docker-compose.yml
/vendor

View file

@ -1,14 +1,37 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/docker/janus"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/docker/proxy"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/docker/server"
schedule:
interval: "daily"
- package-ecosystem: gomod
directory: /
schedule:
interval: daily
groups:
etcd:
patterns:
- "go.etcd.io*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
groups:
artifacts:
patterns:
- "actions/*-artifact"
- package-ecosystem: "pip"
directory: "/docs"

View file

@ -1,15 +1,30 @@
name: check-continentmap
on:
push:
branches: [ master ]
paths:
- '.github/workflows/check-continentmap.yml'
- 'scripts/get_continent_map.py'
- 'Makefile'
pull_request:
branches: [ master ]
paths:
- '.github/workflows/check-continentmap.yml'
- 'scripts/get_continent_map.py'
- 'Makefile'
schedule:
- cron: "0 2 * * SUN"
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Check continentmap
run: make check-continentmap

View file

@ -16,6 +16,9 @@ on:
schedule:
- cron: '28 2 * * 5'
permissions:
contents: read
jobs:
analyze:
name: Analyze
@ -33,15 +36,15 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View file

@ -9,16 +9,21 @@ on:
issue_comment:
types: created
permissions:
contents: read
jobs:
rebase:
runs-on: ubuntu-latest
permissions:
contents: none
# On pull requests and if the comment starts with `/rebase`
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase')
steps:
- name: Add reaction on start
uses: peter-evans/create-or-update-comment@v2
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.COMMAND_BOT_PAT }}
repository: ${{ github.event.repository.full_name }}
@ -26,18 +31,18 @@ jobs:
reaction-type: "+1"
- name: Checkout the latest code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.COMMAND_BOT_PAT }}
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.7
uses: cirrus-actions/rebase@1.8
env:
GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
- name: Add reaction on failure
uses: peter-evans/create-or-update-comment@v2
uses: peter-evans/create-or-update-comment@v4
if: failure()
with:
token: ${{ secrets.COMMAND_BOT_PAT }}

158
.github/workflows/deploydocker.yml vendored Normal file
View file

@ -0,0 +1,158 @@
name: Deploy to Docker Hub / GHCR
on:
pull_request:
branches: [ master ]
paths:
- '.github/workflows/deploydocker.yml'
- '**.go'
- 'go.*'
- 'Makefile'
- '*.conf.in'
- 'docker/server/*'
- 'docker/proxy/*'
push:
branches:
- master
tags:
- "v*.*.*"
permissions:
contents: read
packages: write
jobs:
server:
runs-on: ubuntu-latest
steps:
- name: Check Out Repo
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Generate Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
strukturag/nextcloud-spreed-signaling
ghcr.io/strukturag/nextcloud-spreed-signaling
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/server/Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
proxy:
runs-on: ubuntu-latest
steps:
- name: Check Out Repo
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Generate Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
strukturag/nextcloud-spreed-signaling
ghcr.io/strukturag/nextcloud-spreed-signaling
labels: |
org.opencontainers.image.title=nextcloud-spreed-signaling-proxy
org.opencontainers.image.description=Signaling proxy for the standalone signaling server for Nextcloud Talk.
flavor: |
suffix=-proxy,onlatest=true
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/proxy/Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

View file

@ -5,28 +5,43 @@ on:
branches: [ master ]
paths:
- '.github/workflows/docker-compose.yml'
- 'docker-compose.yml'
- '**/docker-compose.yml'
- 'docker/server/Dockerfile'
push:
branches: [ master ]
paths:
- '.github/workflows/docker-compose.yml'
- 'docker-compose.yml'
- '**/docker-compose.yml'
- 'docker/server/Dockerfile'
permissions:
contents: read
jobs:
pull:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Update docker-compose
run: |
curl -SL https://github.com/docker/compose/releases/download/v2.15.1/docker-compose-linux-x86_64 -o docker-compose
chmod a+x docker-compose
- name: Pull Docker images
run: docker-compose pull
run: ./docker-compose -f docker/docker-compose.yml pull
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Update docker-compose
run: |
curl -SL https://github.com/docker/compose/releases/download/v2.15.1/docker-compose-linux-x86_64 -o docker-compose
chmod a+x docker-compose
- name: Build Docker images
run: docker-compose build
run: ./docker-compose -f docker/docker-compose.yml build

View file

@ -12,17 +12,25 @@ on:
- '.github/workflows/docker-janus.yml'
- 'docker/janus/Dockerfile'
permissions:
contents: read
env:
TEST_TAG: strukturag/nextcloud-spreed-signaling:janus-test
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
context: docker/janus
load: true
tags: ${{ env.TEST_TAG }}

View file

@ -3,20 +3,66 @@ name: Docker image
on:
pull_request:
branches: [ master ]
paths:
- '.github/workflows/docker.yml'
- '**.go'
- 'go.*'
- 'Makefile'
- '*.conf.in'
- 'docker/server/*'
- 'docker/proxy/*'
push:
branches: [ master ]
paths:
- '.github/workflows/docker.yml'
- '**.go'
- 'go.*'
- 'Makefile'
- '*.conf.in'
- 'docker/server/*'
- 'docker/proxy/*'
permissions:
contents: read
env:
TEST_TAG: strukturag/nextcloud-spreed-signaling:test
jobs:
build:
server:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
context: .
file: docker/server/Dockerfile
platforms: linux/amd64,linux/arm64
proxy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
file: docker/proxy/Dockerfile
platforms: linux/amd64,linux/arm64

46
.github/workflows/govuln.yml vendored Normal file
View file

@ -0,0 +1,46 @@
name: Go Vulnerability Checker
on:
push:
branches: [ master ]
paths:
- '.github/workflows/govuln.yml'
- '**.go'
- 'go.*'
pull_request:
branches: [ master ]
paths:
- '.github/workflows/govuln.yml'
- '**.go'
- 'go.*'
schedule:
- cron: "0 2 * * SUN"
permissions:
contents: read
jobs:
run:
runs-on: ubuntu-latest
strategy:
matrix:
go-version:
- "1.21"
- "1.22"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- run: date
- name: Install dependencies
run: |
sudo apt -y update && sudo apt -y install protobuf-compiler
make common
- name: Install and run govulncheck
run: |
set -euo pipefail
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

46
.github/workflows/licensecheck.yml vendored Normal file
View file

@ -0,0 +1,46 @@
name: licensecheck
on:
push:
branches: [ master ]
paths:
- '.github/workflows/licensecheck.yml'
- '**.go'
pull_request:
branches: [ master ]
paths:
- '.github/workflows/licensecheck.yml'
- '**.go'
permissions:
contents: read
jobs:
golang:
name: golang
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install licensecheck
run: |
sudo apt-get -y update
sudo apt-get -y install licensecheck
- id: licensecheck
name: Check licenses
run: |
{
echo 'CHECK_RESULT<<EOF'
licensecheck *.go */*.go
echo EOF
} >> "$GITHUB_ENV"
- name: Check for missing licenses
run: |
MISSING=$(echo "$CHECK_RESULT" | grep UNKNOWN || true)
if [ -n "$MISSING" ]; then \
echo "$MISSING"; \
exit 1; \
fi

View file

@ -5,49 +5,58 @@ on:
branches: [ master ]
paths:
- '.github/workflows/lint.yml'
- '.golangci.yml'
- '**.go'
- 'go.*'
pull_request:
branches: [ master ]
paths:
- '.github/workflows/lint.yml'
- '.golangci.yml'
- '**.go'
- 'go.*'
permissions:
contents: read
jobs:
lint:
name: golang
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.17"
- id: go-cache-paths
run: |
echo "::set-output name=go-build::$(go env GOCACHE)"
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
echo "::set-output name=go-version::$(go version | cut -d ' ' -f 3)"
- name: Go build cache
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-${{ steps.go-cache-paths.outputs.go-version }}-build-${{ hashFiles('**/go.mod', '**/go.sum') }}
- name: Go mod cache
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-${{ steps.go-cache-paths.outputs.go-version }}-mod-${{ hashFiles('**/go.mod', '**/go.sum') }}
go-version: "1.21"
- name: Install dependencies
run: |
sudo apt -y update && sudo apt -y install protobuf-compiler
make common
- name: lint
uses: golangci/golangci-lint-action@v3.2.0
uses: golangci/golangci-lint-action@v6.0.1
with:
version: latest
args: --timeout=2m0s
skip-cache: true
dependencies:
name: dependencies
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "stable"
- name: Check minimum supported version of Go
run: |
go mod tidy -go=1.21 -compat=1.21
- name: Check go.mod / go.sum
run: |
git add go.*
git diff --cached --exit-code go.*

27
.github/workflows/shellcheck.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: shellcheck
on:
push:
branches: [ master ]
paths:
- '.github/workflows/shellcheck.yml'
- '**.sh'
pull_request:
branches: [ master ]
paths:
- '.github/workflows/shellcheck.yml'
- '**.sh'
permissions:
contents: read
jobs:
lint:
name: shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: shellcheck
run: |
find -name "*.sh" | xargs shellcheck

91
.github/workflows/tarball.yml vendored Normal file
View file

@ -0,0 +1,91 @@
name: tarball
on:
push:
branches: [ master ]
paths:
- '.github/workflows/tarball.yml'
- '**.go'
- 'go.*'
- 'Makefile'
pull_request:
branches: [ master ]
paths:
- '.github/workflows/tarball.yml'
- '**.go'
- 'go.*'
- 'Makefile'
permissions:
contents: read
jobs:
create:
strategy:
matrix:
go-version:
- "1.21"
- "1.22"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Install dependencies
run: |
sudo apt -y update && sudo apt -y install protobuf-compiler
- name: Create tarball
run: |
echo "Building with $(nproc) threads"
make tarball
- name: Upload tarball
uses: actions/upload-artifact@v4
with:
name: tarball-${{ matrix.go-version }}
path: nextcloud-spreed-signaling*.tar.gz
test:
strategy:
matrix:
go-version:
- "1.21"
- "1.22"
runs-on: ubuntu-latest
needs: [create]
steps:
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Install dependencies
run: |
sudo apt -y update && sudo apt -y install protobuf-compiler
- name: Download tarball
uses: actions/download-artifact@v4
with:
name: tarball-${{ matrix.go-version }}
- name: Extract tarball
run: |
mkdir -p tmp
tar xvf nextcloud-spreed-signaling*.tar.gz --strip-components=1 -C tmp
[ -d "tmp/vendor" ] || exit 1
- name: Build
env:
GOPROXY: off
run: |
echo "Building with $(nproc) threads"
make -C tmp build -j$(nproc)
- name: Run tests
env:
GOPROXY: off
USE_DB_IP_GEOIP_DATABASE: "1"
run: |
make -C tmp test TIMEOUT=120s

View file

@ -16,39 +16,29 @@ on:
- 'go.*'
- 'Makefile'
permissions:
contents: read
jobs:
go:
env:
MAXMIND_GEOLITE2_LICENSE: ${{ secrets.MAXMIND_GEOLITE2_LICENSE }}
USE_DB_IP_GEOIP_DATABASE: "1"
strategy:
matrix:
go-version:
- "1.17"
- "1.18"
- "1.21"
- "1.22"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- id: go-cache-paths
- name: Install dependencies
run: |
echo "::set-output name=go-build::$(go env GOCACHE)"
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
echo "::set-output name=go-version::$(go version | cut -d ' ' -f 3)"
- name: Go build cache
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-${{ steps.go-cache-paths.outputs.go-version }}-build-${{ hashFiles('**/go.mod', '**/go.sum') }}
- name: Go mod cache
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-${{ steps.go-cache-paths.outputs.go-version }}-mod-${{ hashFiles('**/go.mod', '**/go.sum') }}
sudo apt -y update && sudo apt -y install protobuf-compiler
- name: Build applications
run: |
@ -59,11 +49,11 @@ jobs:
- name: Run tests
run: |
make test || make test
make test TIMEOUT=120s
- name: Generate coverage report
run: |
make cover || make cover
make cover TIMEOUT=120s
echo "GOROOT=$(go env GOROOT)" >> $GITHUB_ENV
- name: Convert coverage to lcov
@ -73,7 +63,7 @@ jobs:
outfile: cover.lcov
- name: Coveralls Parallel
uses: coverallsapp/github-action@1.1.3
uses: coverallsapp/github-action@v2.3.0
env:
COVERALLS_FLAG_NAME: run-${{ matrix.go-version }}
with:
@ -82,11 +72,13 @@ jobs:
parallel: true
finish:
permissions:
contents: none
needs: go
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@1.1.3
uses: coverallsapp/github-action@v2.3.0
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true

1
.gitignore vendored
View file

@ -3,6 +3,7 @@ vendor/
*_easyjson.go
*.pem
*.pb.go
*.prof
*.socket
*.tar.gz

View file

@ -25,7 +25,7 @@ linters-settings:
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
#- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else

View file

@ -2,6 +2,788 @@
All notable changes to this project will be documented in this file.
## 1.3.1 - 2024-05-23
### Changed
- Bump alpine from 3.19 to 3.20 in /docker/janus
[#746](https://github.com/strukturag/nextcloud-spreed-signaling/pull/746)
- CI: Remove deprecated options from lint workflow.
[#748](https://github.com/strukturag/nextcloud-spreed-signaling/pull/748)
- docker: Update Janus in example image to 1.2.2
[#749](https://github.com/strukturag/nextcloud-spreed-signaling/pull/749)
- Improve detection of actual client IP.
[#747](https://github.com/strukturag/nextcloud-spreed-signaling/pull/747)
### Fixed
- docker: Fix proxy entrypoint.
[#745](https://github.com/strukturag/nextcloud-spreed-signaling/pull/745)
## 1.3.0 - 2024-05-22
### Added
- Support resuming remote sessions
[#715](https://github.com/strukturag/nextcloud-spreed-signaling/pull/715)
- Gracefully shut down signaling server on SIGUSR1.
[#706](https://github.com/strukturag/nextcloud-spreed-signaling/pull/706)
- docker: Add helper scripts to gracefully stop / wait for server.
[#722](https://github.com/strukturag/nextcloud-spreed-signaling/pull/722)
- Support environment variables in some configuration.
[#721](https://github.com/strukturag/nextcloud-spreed-signaling/pull/721)
- Add Context to clients / sessions.
[#732](https://github.com/strukturag/nextcloud-spreed-signaling/pull/732)
- Drop support for Golang 1.20
[#737](https://github.com/strukturag/nextcloud-spreed-signaling/pull/737)
- CI: Run "govulncheck".
[#694](https://github.com/strukturag/nextcloud-spreed-signaling/pull/694)
- Make trusted proxies configurable and default to loopback / private IPs.
[#738](https://github.com/strukturag/nextcloud-spreed-signaling/pull/738)
- Add support for remote streams (preview)
[#708](https://github.com/strukturag/nextcloud-spreed-signaling/pull/708)
- Add throttler for backend requests
[#744](https://github.com/strukturag/nextcloud-spreed-signaling/pull/744)
### Changed
- build(deps): Bump github.com/nats-io/nats.go from 1.34.0 to 1.34.1
[#697](https://github.com/strukturag/nextcloud-spreed-signaling/pull/697)
- build(deps): Bump google.golang.org/grpc from 1.62.1 to 1.63.0
[#699](https://github.com/strukturag/nextcloud-spreed-signaling/pull/699)
- build(deps): Bump google.golang.org/grpc from 1.63.0 to 1.63.2
[#700](https://github.com/strukturag/nextcloud-spreed-signaling/pull/700)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.12 to 2.10.14
[#702](https://github.com/strukturag/nextcloud-spreed-signaling/pull/702)
- Include previous value with etcd watch events.
[#704](https://github.com/strukturag/nextcloud-spreed-signaling/pull/704)
- build(deps): Bump go.uber.org/zap from 1.17.0 to 1.27.0
[#705](https://github.com/strukturag/nextcloud-spreed-signaling/pull/705)
- Improve support for Janus 1.x
[#669](https://github.com/strukturag/nextcloud-spreed-signaling/pull/669)
- build(deps): Bump sphinx from 7.2.6 to 7.3.5 in /docs
[#709](https://github.com/strukturag/nextcloud-spreed-signaling/pull/709)
- build(deps): Bump sphinx from 7.3.5 to 7.3.7 in /docs
[#712](https://github.com/strukturag/nextcloud-spreed-signaling/pull/712)
- build(deps): Bump golang.org/x/net from 0.21.0 to 0.23.0
[#711](https://github.com/strukturag/nextcloud-spreed-signaling/pull/711)
- Don't keep expiration timestamp in each session.
[#713](https://github.com/strukturag/nextcloud-spreed-signaling/pull/713)
- build(deps): Bump mkdocs from 1.5.3 to 1.6.0 in /docs
[#714](https://github.com/strukturag/nextcloud-spreed-signaling/pull/714)
- Speedup tests by running in parallel
[#718](https://github.com/strukturag/nextcloud-spreed-signaling/pull/718)
- build(deps): Bump golangci/golangci-lint-action from 4.0.0 to 5.0.0
[#719](https://github.com/strukturag/nextcloud-spreed-signaling/pull/719)
- build(deps): Bump golangci/golangci-lint-action from 5.0.0 to 5.1.0
[#720](https://github.com/strukturag/nextcloud-spreed-signaling/pull/720)
- build(deps): Bump coverallsapp/github-action from 2.2.3 to 2.3.0
[#728](https://github.com/strukturag/nextcloud-spreed-signaling/pull/728)
- build(deps): Bump jinja2 from 3.1.3 to 3.1.4 in /docs
[#726](https://github.com/strukturag/nextcloud-spreed-signaling/pull/726)
- build(deps): Bump google.golang.org/protobuf from 1.33.0 to 1.34.1
[#725](https://github.com/strukturag/nextcloud-spreed-signaling/pull/725)
- build(deps): Bump github.com/prometheus/client_golang from 1.19.0 to 1.19.1
[#730](https://github.com/strukturag/nextcloud-spreed-signaling/pull/730)
- build(deps): Bump golangci/golangci-lint-action from 5.1.0 to 6.0.1
[#729](https://github.com/strukturag/nextcloud-spreed-signaling/pull/729)
- build(deps): Bump google.golang.org/grpc from 1.63.2 to 1.64.0
[#734](https://github.com/strukturag/nextcloud-spreed-signaling/pull/734)
- Validate received SDP earlier.
[#707](https://github.com/strukturag/nextcloud-spreed-signaling/pull/707)
- Log something if mcu publisher / subscriber was closed.
[#736](https://github.com/strukturag/nextcloud-spreed-signaling/pull/736)
- build(deps): Bump the etcd group with 4 updates
[#693](https://github.com/strukturag/nextcloud-spreed-signaling/pull/693)
- build(deps): Bump github.com/nats-io/nats.go from 1.34.1 to 1.35.0
[#740](https://github.com/strukturag/nextcloud-spreed-signaling/pull/740)
- Don't use unnecessary pointer to "json.RawMessage".
[#739](https://github.com/strukturag/nextcloud-spreed-signaling/pull/739)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.14 to 2.10.15
[#741](https://github.com/strukturag/nextcloud-spreed-signaling/pull/741)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.15 to 2.10.16
[#743](https://github.com/strukturag/nextcloud-spreed-signaling/pull/743)
### Fixed
- Improve detecting renames in file watcher.
[#698](https://github.com/strukturag/nextcloud-spreed-signaling/pull/698)
- Update etcd watch handling.
[#701](https://github.com/strukturag/nextcloud-spreed-signaling/pull/701)
- Prevent goroutine leaks in GRPC tests.
[#716](https://github.com/strukturag/nextcloud-spreed-signaling/pull/716)
- Fix potential race in capabilities test.
[#731](https://github.com/strukturag/nextcloud-spreed-signaling/pull/731)
- Don't log read error after we closed the connection.
[#735](https://github.com/strukturag/nextcloud-spreed-signaling/pull/735)
- Fix lock order inversion when leaving room / publishing room sessions.
[#742](https://github.com/strukturag/nextcloud-spreed-signaling/pull/742)
- Relax "MessageClientMessageData" validation.
[#733](https://github.com/strukturag/nextcloud-spreed-signaling/pull/733)
## 1.2.4 - 2024-04-03
### Added
- Add metrics for current number of HTTP client connections.
[#668](https://github.com/strukturag/nextcloud-spreed-signaling/pull/668)
- Support getting GeoIP DB from db-ip.com for tests.
[#689](https://github.com/strukturag/nextcloud-spreed-signaling/pull/689)
- Use fsnotify to detect file changes
[#680](https://github.com/strukturag/nextcloud-spreed-signaling/pull/680)
- CI: Check dependencies for minimum supported version.
[#692](https://github.com/strukturag/nextcloud-spreed-signaling/pull/692)
### Changed
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.9 to 2.10.10
[#650](https://github.com/strukturag/nextcloud-spreed-signaling/pull/650)
- CI: Also test with Golang 1.22
[#651](https://github.com/strukturag/nextcloud-spreed-signaling/pull/651)
- build(deps): Bump the etcd group with 4 updates
[#649](https://github.com/strukturag/nextcloud-spreed-signaling/pull/649)
- Improve Makefile
[#653](https://github.com/strukturag/nextcloud-spreed-signaling/pull/653)
- build(deps): Bump google.golang.org/grpc from 1.61.0 to 1.61.1
[#659](https://github.com/strukturag/nextcloud-spreed-signaling/pull/659)
- build(deps): Bump golangci/golangci-lint-action from 3.7.0 to 4.0.0
[#658](https://github.com/strukturag/nextcloud-spreed-signaling/pull/658)
- Minor improvements to DNS monitor
[#663](https://github.com/strukturag/nextcloud-spreed-signaling/pull/663)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.10 to 2.10.11
[#662](https://github.com/strukturag/nextcloud-spreed-signaling/pull/662)
- build(deps): Bump google.golang.org/grpc from 1.61.1 to 1.62.0
[#664](https://github.com/strukturag/nextcloud-spreed-signaling/pull/664)
- Support ports in full URLs for DNS monitor.
[#667](https://github.com/strukturag/nextcloud-spreed-signaling/pull/667)
- Calculate proxy load based on maximum bandwidth.
[#670](https://github.com/strukturag/nextcloud-spreed-signaling/pull/670)
- build(deps): Bump github.com/nats-io/nats.go from 1.32.0 to 1.33.1
[#661](https://github.com/strukturag/nextcloud-spreed-signaling/pull/661)
- build(deps): Bump golang from 1.21-alpine to 1.22-alpine in /docker/server
[#655](https://github.com/strukturag/nextcloud-spreed-signaling/pull/655)
- build(deps): Bump golang from 1.21-alpine to 1.22-alpine in /docker/proxy
[#656](https://github.com/strukturag/nextcloud-spreed-signaling/pull/656)
- docker: Update Janus from 0.11.8 to 0.14.1.
[#672](https://github.com/strukturag/nextcloud-spreed-signaling/pull/672)
- build(deps): Bump alpine from 3.18 to 3.19 in /docker/janus
[#613](https://github.com/strukturag/nextcloud-spreed-signaling/pull/613)
- Reuse backoff waiting code where possible
[#673](https://github.com/strukturag/nextcloud-spreed-signaling/pull/673)
- build(deps): Bump github.com/prometheus/client_golang from 1.18.0 to 1.19.0
[#674](https://github.com/strukturag/nextcloud-spreed-signaling/pull/674)
- Docker improvements
[#675](https://github.com/strukturag/nextcloud-spreed-signaling/pull/675)
- make: Don't update dependencies but use pinned versions.
[#679](https://github.com/strukturag/nextcloud-spreed-signaling/pull/679)
- build(deps): Bump github.com/pion/sdp/v3 from 3.0.6 to 3.0.7
[#678](https://github.com/strukturag/nextcloud-spreed-signaling/pull/678)
- build(deps): Bump google.golang.org/grpc from 1.62.0 to 1.62.1
[#677](https://github.com/strukturag/nextcloud-spreed-signaling/pull/677)
- build(deps): Bump google.golang.org/protobuf from 1.32.0 to 1.33.0
[#676](https://github.com/strukturag/nextcloud-spreed-signaling/pull/676)
- build(deps): Bump github.com/pion/sdp/v3 from 3.0.7 to 3.0.8
[#681](https://github.com/strukturag/nextcloud-spreed-signaling/pull/681)
- Update source of continentmap to original CSV file.
[#682](https://github.com/strukturag/nextcloud-spreed-signaling/pull/682)
- build(deps): Bump markdown from 3.5.2 to 3.6 in /docs
[#684](https://github.com/strukturag/nextcloud-spreed-signaling/pull/684)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.11 to 2.10.12
[#683](https://github.com/strukturag/nextcloud-spreed-signaling/pull/683)
- build(deps): Bump github.com/pion/sdp/v3 from 3.0.8 to 3.0.9
[#687](https://github.com/strukturag/nextcloud-spreed-signaling/pull/687)
- build(deps): Bump the etcd group with 4 updates
[#686](https://github.com/strukturag/nextcloud-spreed-signaling/pull/686)
- build(deps): Bump github.com/nats-io/nats.go from 1.33.1 to 1.34.0
[#685](https://github.com/strukturag/nextcloud-spreed-signaling/pull/685)
- Revert "build(deps): Bump the etcd group with 4 updates"
[#691](https://github.com/strukturag/nextcloud-spreed-signaling/pull/691)
- CI: Limit when to run Docker build jobs.
[#695](https://github.com/strukturag/nextcloud-spreed-signaling/pull/695)
- Remove deprecated section on multiple signaling servers from README.
[#696](https://github.com/strukturag/nextcloud-spreed-signaling/pull/696)
### Fixed
- Fix race condition when accessing "expected" in proxy_config tests.
[#652](https://github.com/strukturag/nextcloud-spreed-signaling/pull/652)
- Fix deadlock when entry is removed while receiver holds lock in lookup.
[#654](https://github.com/strukturag/nextcloud-spreed-signaling/pull/654)
- Fix flaky "TestProxyConfigStaticDNS".
[#671](https://github.com/strukturag/nextcloud-spreed-signaling/pull/671)
- Fix flaky DnsMonitor test.
[#690](https://github.com/strukturag/nextcloud-spreed-signaling/pull/690)
## 1.2.3 - 2024-01-31
### Added
- CI: Check license headers.
[#627](https://github.com/strukturag/nextcloud-spreed-signaling/pull/627)
- Add "welcome" endpoint to proxy.
[#644](https://github.com/strukturag/nextcloud-spreed-signaling/pull/644)
### Changed
- build(deps): Bump github/codeql-action from 2 to 3
[#619](https://github.com/strukturag/nextcloud-spreed-signaling/pull/619)
- build(deps): Bump github.com/google/uuid from 1.4.0 to 1.5.0
[#618](https://github.com/strukturag/nextcloud-spreed-signaling/pull/618)
- build(deps): Bump google.golang.org/grpc from 1.59.0 to 1.60.0
[#617](https://github.com/strukturag/nextcloud-spreed-signaling/pull/617)
- build(deps): Bump the artifacts group with 2 updates
[#622](https://github.com/strukturag/nextcloud-spreed-signaling/pull/622)
- build(deps): Bump golang.org/x/crypto from 0.16.0 to 0.17.0
[#623](https://github.com/strukturag/nextcloud-spreed-signaling/pull/623)
- build(deps): Bump google.golang.org/grpc from 1.60.0 to 1.60.1
[#624](https://github.com/strukturag/nextcloud-spreed-signaling/pull/624)
- Refactor proxy config
[#606](https://github.com/strukturag/nextcloud-spreed-signaling/pull/606)
- build(deps): Bump google.golang.org/protobuf from 1.31.0 to 1.32.0
[#629](https://github.com/strukturag/nextcloud-spreed-signaling/pull/629)
- build(deps): Bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0
[#630](https://github.com/strukturag/nextcloud-spreed-signaling/pull/630)
- build(deps): Bump jinja2 from 3.1.2 to 3.1.3 in /docs
[#632](https://github.com/strukturag/nextcloud-spreed-signaling/pull/632)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.7 to 2.10.9
[#633](https://github.com/strukturag/nextcloud-spreed-signaling/pull/633)
- build(deps): Bump markdown from 3.5.1 to 3.5.2 in /docs
[#631](https://github.com/strukturag/nextcloud-spreed-signaling/pull/631)
- build(deps): Bump github.com/nats-io/nats.go from 1.31.0 to 1.32.0
[#634](https://github.com/strukturag/nextcloud-spreed-signaling/pull/634)
- build(deps): Bump readthedocs-sphinx-search from 0.3.1 to 0.3.2 in /docs
[#635](https://github.com/strukturag/nextcloud-spreed-signaling/pull/635)
- build(deps): Bump actions/cache from 3 to 4
[#638](https://github.com/strukturag/nextcloud-spreed-signaling/pull/638)
- build(deps): Bump github.com/google/uuid from 1.5.0 to 1.6.0
[#643](https://github.com/strukturag/nextcloud-spreed-signaling/pull/643)
- build(deps): Bump google.golang.org/grpc from 1.60.1 to 1.61.0
[#645](https://github.com/strukturag/nextcloud-spreed-signaling/pull/645)
- build(deps): Bump peter-evans/create-or-update-comment from 3 to 4
[#646](https://github.com/strukturag/nextcloud-spreed-signaling/pull/646)
- CI: No longer need to manually cache Go modules.
[#648](https://github.com/strukturag/nextcloud-spreed-signaling/pull/648)
- CI: Disable cache for linter to bring back annotations.
[#647](https://github.com/strukturag/nextcloud-spreed-signaling/pull/647)
- Refactor DNS monitoring
[#648](https://github.com/strukturag/nextcloud-spreed-signaling/pull/648)
### Fixed
- Fix link to NATS install docs
[#637](https://github.com/strukturag/nextcloud-spreed-signaling/pull/637)
- docker: Always need to set proxy token id / key for server.
[#641](https://github.com/strukturag/nextcloud-spreed-signaling/pull/641)
## 1.2.2 - 2023-12-11
### Added
- Include "~docker" in version if built on Docker.
[#602](https://github.com/strukturag/nextcloud-spreed-signaling/pull/602)
### Changed
- CI: No need to build docker images for testing, done internally.
[#603](https://github.com/strukturag/nextcloud-spreed-signaling/pull/603)
- build(deps): Bump sphinx-rtd-theme from 1.3.0 to 2.0.0 in /docs
[#604](https://github.com/strukturag/nextcloud-spreed-signaling/pull/604)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.5 to 2.10.6
[#605](https://github.com/strukturag/nextcloud-spreed-signaling/pull/605)
- build(deps): Bump actions/setup-go from 4 to 5
[#608](https://github.com/strukturag/nextcloud-spreed-signaling/pull/608)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.6 to 2.10.7
[#612](https://github.com/strukturag/nextcloud-spreed-signaling/pull/612)
- build(deps): Bump the etcd group with 4 updates
[#611](https://github.com/strukturag/nextcloud-spreed-signaling/pull/611)
### Fixed
- Skip options from default section when parsing "geoip-overrides".
[#609](https://github.com/strukturag/nextcloud-spreed-signaling/pull/609)
- Hangup virtual session if it gets disinvited.
[#610](https://github.com/strukturag/nextcloud-spreed-signaling/pull/610)
## 1.2.1 - 2023-11-15
### Added
- feat(scripts): Add a script to simplify the logs to make it more easily to trace a user/session
[#480](https://github.com/strukturag/nextcloud-spreed-signaling/pull/480)
### Changed
- build(deps): Bump markdown from 3.5 to 3.5.1 in /docs
[#594](https://github.com/strukturag/nextcloud-spreed-signaling/pull/594)
- build(deps): Bump github.com/gorilla/websocket from 1.5.0 to 1.5.1
[#595](https://github.com/strukturag/nextcloud-spreed-signaling/pull/595)
- build(deps): Bump github.com/gorilla/securecookie from 1.1.1 to 1.1.2
[#597](https://github.com/strukturag/nextcloud-spreed-signaling/pull/597)
- build(deps): Bump github.com/gorilla/mux from 1.8.0 to 1.8.1
[#596](https://github.com/strukturag/nextcloud-spreed-signaling/pull/596)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.4 to 2.10.5
[#599](https://github.com/strukturag/nextcloud-spreed-signaling/pull/599)
- Improve support for multiple backends with dialouts
[#592](https://github.com/strukturag/nextcloud-spreed-signaling/pull/592)
- build(deps): Bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc from 0.25.0 to 0.46.0
[#600](https://github.com/strukturag/nextcloud-spreed-signaling/pull/600)
## 1.2.0 - 2023-10-30
### Added
- Use GeoIP overrides if no GeoIP database is configured.
[#532](https://github.com/strukturag/nextcloud-spreed-signaling/pull/532)
- Log warning if no (static) backends have been configured.
[#533](https://github.com/strukturag/nextcloud-spreed-signaling/pull/533)
- Fallback to common shared secret if none is set for backends.
[#534](https://github.com/strukturag/nextcloud-spreed-signaling/pull/534)
- CI: Test with Golang 1.21
[#536](https://github.com/strukturag/nextcloud-spreed-signaling/pull/536)
- Return response if session tries to join room again.
[#547](https://github.com/strukturag/nextcloud-spreed-signaling/pull/547)
- Support TTL for transient data.
[#575](https://github.com/strukturag/nextcloud-spreed-signaling/pull/575)
- Implement message handler for dialout support.
[#563](https://github.com/strukturag/nextcloud-spreed-signaling/pull/563)
- No longer support Golang 1.19.
[#580](https://github.com/strukturag/nextcloud-spreed-signaling/pull/580)
### Changed
- build(deps): Bump google.golang.org/grpc from 1.56.1 to 1.57.0
[#520](https://github.com/strukturag/nextcloud-spreed-signaling/pull/520)
- build(deps): Bump coverallsapp/github-action from 2.2.0 to 2.2.1
[#514](https://github.com/strukturag/nextcloud-spreed-signaling/pull/514)
- build(deps): Bump github.com/nats-io/nats.go from 1.27.1 to 1.28.0
[#515](https://github.com/strukturag/nextcloud-spreed-signaling/pull/515)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.19 to 2.9.20
[#513](https://github.com/strukturag/nextcloud-spreed-signaling/pull/513)
- build(deps): Bump mkdocs from 1.4.3 to 1.5.1 in /docs
[#523](https://github.com/strukturag/nextcloud-spreed-signaling/pull/523)
- build(deps): Bump markdown from 3.3.7 to 3.4.4 in /docs
[#519](https://github.com/strukturag/nextcloud-spreed-signaling/pull/519)
- build(deps): Bump mkdocs from 1.5.1 to 1.5.2 in /docs
[#525](https://github.com/strukturag/nextcloud-spreed-signaling/pull/525)
- build(deps): Bump github.com/oschwald/maxminddb-golang from 1.11.0 to 1.12.0
[#524](https://github.com/strukturag/nextcloud-spreed-signaling/pull/524)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.20 to 2.9.21
[#530](https://github.com/strukturag/nextcloud-spreed-signaling/pull/530)
- build(deps): Bump sphinx from 6.2.1 to 7.2.4 in /docs
[#542](https://github.com/strukturag/nextcloud-spreed-signaling/pull/542)
- build(deps): Bump github.com/google/uuid from 1.3.0 to 1.3.1
[#539](https://github.com/strukturag/nextcloud-spreed-signaling/pull/539)
- build(deps): Bump sphinx from 7.2.4 to 7.2.5 in /docs
[#544](https://github.com/strukturag/nextcloud-spreed-signaling/pull/544)
- build(deps): Bump coverallsapp/github-action from 2.2.1 to 2.2.2
[#546](https://github.com/strukturag/nextcloud-spreed-signaling/pull/546)
- build(deps): Bump actions/checkout from 3 to 4
[#545](https://github.com/strukturag/nextcloud-spreed-signaling/pull/545)
- build(deps): Bump google.golang.org/grpc from 1.57.0 to 1.58.0
[#549](https://github.com/strukturag/nextcloud-spreed-signaling/pull/549)
- build(deps): Bump docker/metadata-action from 4 to 5
[#552](https://github.com/strukturag/nextcloud-spreed-signaling/pull/552)
- build(deps): Bump docker/setup-qemu-action from 2 to 3
[#553](https://github.com/strukturag/nextcloud-spreed-signaling/pull/553)
- build(deps): Bump docker/login-action from 2 to 3
[#554](https://github.com/strukturag/nextcloud-spreed-signaling/pull/554)
- build(deps): Bump docker/setup-buildx-action from 2 to 3
[#555](https://github.com/strukturag/nextcloud-spreed-signaling/pull/555)
- build(deps): Bump coverallsapp/github-action from 2.2.2 to 2.2.3
[#551](https://github.com/strukturag/nextcloud-spreed-signaling/pull/551)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.21 to 2.9.22
[#550](https://github.com/strukturag/nextcloud-spreed-signaling/pull/550)
- build(deps): Bump docker/build-push-action from 4 to 5
[#557](https://github.com/strukturag/nextcloud-spreed-signaling/pull/557)
- build(deps): Bump github.com/nats-io/nats.go from 1.28.0 to 1.29.0
[#558](https://github.com/strukturag/nextcloud-spreed-signaling/pull/558)
- build(deps): Bump google.golang.org/grpc from 1.58.0 to 1.58.1
[#559](https://github.com/strukturag/nextcloud-spreed-signaling/pull/559)
- build(deps): Bump sphinx from 7.2.5 to 7.2.6 in /docs
[#560](https://github.com/strukturag/nextcloud-spreed-signaling/pull/560)
- build(deps): Bump mkdocs from 1.5.2 to 1.5.3 in /docs
[#561](https://github.com/strukturag/nextcloud-spreed-signaling/pull/561)
- build(deps): Bump markdown from 3.4.4 to 3.5 in /docs
[#570](https://github.com/strukturag/nextcloud-spreed-signaling/pull/570)
- build(deps): Bump google.golang.org/grpc from 1.58.1 to 1.58.3
[#573](https://github.com/strukturag/nextcloud-spreed-signaling/pull/573)
- build(deps): Bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0
[#569](https://github.com/strukturag/nextcloud-spreed-signaling/pull/569)
- build(deps): Bump golang.org/x/net from 0.12.0 to 0.17.0
[#574](https://github.com/strukturag/nextcloud-spreed-signaling/pull/574)
- build(deps): Bump github.com/nats-io/nats.go from 1.29.0 to 1.30.2
[#568](https://github.com/strukturag/nextcloud-spreed-signaling/pull/568)
- build(deps): Bump google.golang.org/grpc from 1.58.3 to 1.59.0
[#578](https://github.com/strukturag/nextcloud-spreed-signaling/pull/578)
- build(deps): Bump github.com/nats-io/nats.go from 1.30.2 to 1.31.0
[#577](https://github.com/strukturag/nextcloud-spreed-signaling/pull/577)
- dependabot: Check for updates in docker files.
- build(deps): Bump golang from 1.20-alpine to 1.21-alpine in /docker/proxy
[#581](https://github.com/strukturag/nextcloud-spreed-signaling/pull/581)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.22 to 2.10.3
[#576](https://github.com/strukturag/nextcloud-spreed-signaling/pull/576)
- build(deps): Bump alpine from 3.14 to 3.18 in /docker/janus
[#582](https://github.com/strukturag/nextcloud-spreed-signaling/pull/582)
- build(deps): Bump golang from 1.20-alpine to 1.21-alpine in /docker/server
[#583](https://github.com/strukturag/nextcloud-spreed-signaling/pull/583)
- Improve get-version.sh
[#584](https://github.com/strukturag/nextcloud-spreed-signaling/pull/584)
-build(deps): Bump go.etcd.io/etcd/client/pkg/v3 from 3.5.9 to 3.5.10
[#588](https://github.com/strukturag/nextcloud-spreed-signaling/pull/588)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.10.3 to 2.10.4
[#586](https://github.com/strukturag/nextcloud-spreed-signaling/pull/586)
- build(deps): Bump github.com/google/uuid from 1.3.1 to 1.4.0
[#585](https://github.com/strukturag/nextcloud-spreed-signaling/pull/585)
- dependabot: Group etcd updates.
- build(deps): Bump the etcd group with 3 updates
[#590](https://github.com/strukturag/nextcloud-spreed-signaling/pull/590)
- Switch to atomic types from Go 1.19
[#500](https://github.com/strukturag/nextcloud-spreed-signaling/pull/500)
- Move common flags code to own struct.
[#591](https://github.com/strukturag/nextcloud-spreed-signaling/pull/591)
## 1.1.3 - 2023-07-05
### Added
- stats: Support configuring subnets for allowed IPs.
[#448](https://github.com/strukturag/nextcloud-spreed-signaling/pull/448)
- Add common code to handle allowed IPs.
[#450](https://github.com/strukturag/nextcloud-spreed-signaling/pull/450)
- Add allowall to docker image
[#488](https://github.com/strukturag/nextcloud-spreed-signaling/pull/488)
- Follow the Go release policy by supporting only the last two versions.
This drops support for Golang 1.18.
[#499](https://github.com/strukturag/nextcloud-spreed-signaling/pull/499)
### Changed
- build(deps): Bump google.golang.org/protobuf from 1.29.0 to 1.29.1
[#446](https://github.com/strukturag/nextcloud-spreed-signaling/pull/446)
- build(deps): Bump actions/setup-go from 3 to 4
[#447](https://github.com/strukturag/nextcloud-spreed-signaling/pull/447)
- build(deps): Bump google.golang.org/protobuf from 1.29.1 to 1.30.0
[#449](https://github.com/strukturag/nextcloud-spreed-signaling/pull/449)
- build(deps): Bump coverallsapp/github-action from 1.2.4 to 2.0.0
[#451](https://github.com/strukturag/nextcloud-spreed-signaling/pull/451)
- build(deps): Bump readthedocs-sphinx-search from 0.2.0 to 0.3.1 in /docs
[#456](https://github.com/strukturag/nextcloud-spreed-signaling/pull/456)
- build(deps): Bump coverallsapp/github-action from 2.0.0 to 2.1.0
[#460](https://github.com/strukturag/nextcloud-spreed-signaling/pull/460)
- build(deps): Bump peter-evans/create-or-update-comment from 2 to 3
[#459](https://github.com/strukturag/nextcloud-spreed-signaling/pull/459)
- build(deps): Bump sphinx from 6.1.3 to 6.2.1 in /docs
[#468](https://github.com/strukturag/nextcloud-spreed-signaling/pull/468)
- build(deps): Bump mkdocs from 1.4.2 to 1.4.3 in /docs
[#471](https://github.com/strukturag/nextcloud-spreed-signaling/pull/471)
- build(deps): Bump sphinx-rtd-theme from 1.2.0 to 1.2.1 in /docs
[#479](https://github.com/strukturag/nextcloud-spreed-signaling/pull/479)
- build(deps): Bump coverallsapp/github-action from 2.1.0 to 2.1.2
[#466](https://github.com/strukturag/nextcloud-spreed-signaling/pull/466)
- build(deps): Bump golangci/golangci-lint-action from 3.4.0 to 3.5.0
[#481](https://github.com/strukturag/nextcloud-spreed-signaling/pull/481)
- Simplify vendoring.
[#482](https://github.com/strukturag/nextcloud-spreed-signaling/pull/482)
- build(deps): Bump sphinx-rtd-theme from 1.2.1 to 1.2.2 in /docs
[#485](https://github.com/strukturag/nextcloud-spreed-signaling/pull/485)
- build(deps): Bump coverallsapp/github-action from 2.1.2 to 2.2.0
[#484](https://github.com/strukturag/nextcloud-spreed-signaling/pull/484)
- build(deps): Bump google.golang.org/grpc from 1.53.0 to 1.55.0
[#472](https://github.com/strukturag/nextcloud-spreed-signaling/pull/472)
- build(deps): Bump go.etcd.io/etcd/client/v3 from 3.5.7 to 3.5.9
[#473](https://github.com/strukturag/nextcloud-spreed-signaling/pull/473)
- build(deps): Bump github.com/nats-io/nats.go from 1.24.0 to 1.26.0
[#478](https://github.com/strukturag/nextcloud-spreed-signaling/pull/478)
- build(deps): Bump golangci/golangci-lint-action from 3.5.0 to 3.6.0
[#492](https://github.com/strukturag/nextcloud-spreed-signaling/pull/492)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.15 to 2.9.17
[#495](https://github.com/strukturag/nextcloud-spreed-signaling/pull/495)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.17 to 2.9.18
[#496](https://github.com/strukturag/nextcloud-spreed-signaling/pull/496)
- build(deps): Bump github.com/prometheus/client_golang from 1.14.0 to 1.15.1
[#493](https://github.com/strukturag/nextcloud-spreed-signaling/pull/493)
- docker: Don't build concurrently.
[#498](https://github.com/strukturag/nextcloud-spreed-signaling/pull/498)
- Use "struct{}" channel if only used as signaling mechanism.
[#491](https://github.com/strukturag/nextcloud-spreed-signaling/pull/491)
- build(deps): Bump google.golang.org/grpc from 1.55.0 to 1.56.0
[#502](https://github.com/strukturag/nextcloud-spreed-signaling/pull/502)
- build(deps): Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0
[#501](https://github.com/strukturag/nextcloud-spreed-signaling/pull/501)
- build(deps): Bump github.com/oschwald/maxminddb-golang from 1.10.0 to 1.11.0
[#503](https://github.com/strukturag/nextcloud-spreed-signaling/pull/503)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.18 to 2.9.19
[#504](https://github.com/strukturag/nextcloud-spreed-signaling/pull/504)
- build(deps): Bump google.golang.org/grpc from 1.56.0 to 1.56.1
[#505](https://github.com/strukturag/nextcloud-spreed-signaling/pull/505)
- build(deps): Bump github.com/nats-io/nats.go from 1.27.0 to 1.27.1
[#506](https://github.com/strukturag/nextcloud-spreed-signaling/pull/506)
- build(deps): Bump google.golang.org/protobuf from 1.30.0 to 1.31.0
[#507](https://github.com/strukturag/nextcloud-spreed-signaling/pull/507)
### Fixed
- CI: Make sure proxy Docker image is never tagged as "latest".
[#445](https://github.com/strukturag/nextcloud-spreed-signaling/pull/445)
- Write backends comma-separated to config
[#487](https://github.com/strukturag/nextcloud-spreed-signaling/pull/487)
- Fix duplicate join events
[#490](https://github.com/strukturag/nextcloud-spreed-signaling/pull/490)
- Add missing lock for "roomSessionId" to avoid potential races.
[#497](https://github.com/strukturag/nextcloud-spreed-signaling/pull/497)
## 1.1.2 - 2023-03-13
### Added
- Allow SKIP_VERIFY in docker image.
[#430](https://github.com/strukturag/nextcloud-spreed-signaling/pull/430)
### Changed
- Keep Docker images alpine based.
[#427](https://github.com/strukturag/nextcloud-spreed-signaling/pull/427)
- build(deps): Bump coverallsapp/github-action from 1.1.3 to 1.2.0
[#433](https://github.com/strukturag/nextcloud-spreed-signaling/pull/433)
- build(deps): Bump coverallsapp/github-action from 1.2.0 to 1.2.2
[#435](https://github.com/strukturag/nextcloud-spreed-signaling/pull/435)
- build(deps): Bump coverallsapp/github-action from 1.2.2 to 1.2.3
[#436](https://github.com/strukturag/nextcloud-spreed-signaling/pull/436)
- build(deps): Bump coverallsapp/github-action from 1.2.3 to 1.2.4
[#437](https://github.com/strukturag/nextcloud-spreed-signaling/pull/437)
- build(deps): Bump github.com/nats-io/nats.go from 1.23.0 to 1.24.0
[#434](https://github.com/strukturag/nextcloud-spreed-signaling/pull/434)
- Run "go mod tidy -compat=1.18".
[#440](https://github.com/strukturag/nextcloud-spreed-signaling/pull/440)
- CI: Run golangci-lint with Go 1.20
- Update protoc-gen-go-grpc to v1.3.0
[#442](https://github.com/strukturag/nextcloud-spreed-signaling/pull/442)
- CI: Stop using deprecated "set-output".
[#441](https://github.com/strukturag/nextcloud-spreed-signaling/pull/441)
- docker: Don't rely on default values when updating TURN settings.
[#439](https://github.com/strukturag/nextcloud-spreed-signaling/pull/439)
- build(deps): Bump google.golang.org/protobuf from 1.28.1 to 1.29.0
[#443](https://github.com/strukturag/nextcloud-spreed-signaling/pull/443)
### Fixed
- Fix example in docker README.
[#429](https://github.com/strukturag/nextcloud-spreed-signaling/pull/429)
- TURN_API_KEY and TURN_SECRET fix.
[#428](https://github.com/strukturag/nextcloud-spreed-signaling/pull/428)
## 1.1.1 - 2023-02-22
### Fixed
- Fix Docker images.
[#425](https://github.com/strukturag/nextcloud-spreed-signaling/pull/425)
## 1.1.0 - 2023-02-22
### Added
- Official docker images.
[#314](https://github.com/strukturag/nextcloud-spreed-signaling/pull/314)
- Use proxy from environment for backend client requests.
[#326](https://github.com/strukturag/nextcloud-spreed-signaling/pull/326)
- Add aarch64/arm64 docker build
[#384](https://github.com/strukturag/nextcloud-spreed-signaling/pull/384)
- CI: Setup permissions for workflows.
[#393](https://github.com/strukturag/nextcloud-spreed-signaling/pull/393)
- Implement "switchto" support
[#409](https://github.com/strukturag/nextcloud-spreed-signaling/pull/409)
- Allow internal clients to set / change the "inCall" flags.
[#421](https://github.com/strukturag/nextcloud-spreed-signaling/pull/421)
- Add support for Golang 1.20
[#413](https://github.com/strukturag/nextcloud-spreed-signaling/pull/413)
### Changed
- Switch to apt-get on CLI.
[#312](https://github.com/strukturag/nextcloud-spreed-signaling/pull/312)
- vendor: Automatically vendor protobuf modules.
[#313](https://github.com/strukturag/nextcloud-spreed-signaling/pull/313)
- Bump github.com/prometheus/client_golang from 1.12.2 to 1.13.0
[#316](https://github.com/strukturag/nextcloud-spreed-signaling/pull/316)
- Bump github.com/oschwald/maxminddb-golang from 1.9.0 to 1.10.0
[#317](https://github.com/strukturag/nextcloud-spreed-signaling/pull/317)
- Bump github.com/pion/sdp/v3 from 3.0.5 to 3.0.6
[#320](https://github.com/strukturag/nextcloud-spreed-signaling/pull/320)
- Bump google.golang.org/grpc from 1.48.0 to 1.49.0
[#324](https://github.com/strukturag/nextcloud-spreed-signaling/pull/324)
- Bump github.com/nats-io/nats-server/v2 from 2.8.4 to 2.9.0
[#330](https://github.com/strukturag/nextcloud-spreed-signaling/pull/330)
- Bump sphinx from 5.1.1 to 5.2.2 in /docs
[#339](https://github.com/strukturag/nextcloud-spreed-signaling/pull/339)
- Bump mkdocs from 1.3.1 to 1.4.0 in /docs
[#340](https://github.com/strukturag/nextcloud-spreed-signaling/pull/340)
- Bump sphinx from 5.2.2 to 5.2.3 in /docs
[#345](https://github.com/strukturag/nextcloud-spreed-signaling/pull/345)
- Bump github.com/nats-io/nats-server/v2 from 2.9.0 to 2.9.2
[#344](https://github.com/strukturag/nextcloud-spreed-signaling/pull/344)
- Bump go.etcd.io/etcd/api/v3 from 3.5.4 to 3.5.5
[#333](https://github.com/strukturag/nextcloud-spreed-signaling/pull/333)
- Bump go.etcd.io/etcd/server/v3 from 3.5.4 to 3.5.5
[#334](https://github.com/strukturag/nextcloud-spreed-signaling/pull/334)
- Bump google.golang.org/grpc from 1.49.0 to 1.50.0
[#346](https://github.com/strukturag/nextcloud-spreed-signaling/pull/346)
- Bump github.com/nats-io/nats-server/v2 from 2.9.2 to 2.9.3
[#348](https://github.com/strukturag/nextcloud-spreed-signaling/pull/348)
- Bump github.com/nats-io/nats.go from 1.17.0 to 1.18.0
[#349](https://github.com/strukturag/nextcloud-spreed-signaling/pull/349)
- Bump sphinx from 5.2.3 to 5.3.0 in /docs
[#351](https://github.com/strukturag/nextcloud-spreed-signaling/pull/351)
- Bump mkdocs from 1.4.0 to 1.4.1 in /docs
[#352](https://github.com/strukturag/nextcloud-spreed-signaling/pull/352)
- Bump google.golang.org/grpc from 1.50.0 to 1.50.1
[#350](https://github.com/strukturag/nextcloud-spreed-signaling/pull/350)
- Bump golangci/golangci-lint-action from 3.2.0 to 3.3.0
[#353](https://github.com/strukturag/nextcloud-spreed-signaling/pull/353)
- Bump mkdocs from 1.4.1 to 1.4.2 in /docs
[#358](https://github.com/strukturag/nextcloud-spreed-signaling/pull/358)
- Bump sphinx-rtd-theme from 1.0.0 to 1.1.0 in /docs
[#357](https://github.com/strukturag/nextcloud-spreed-signaling/pull/357)
- Bump github.com/nats-io/nats.go from 1.18.0 to 1.19.0
[#354](https://github.com/strukturag/nextcloud-spreed-signaling/pull/354)
- Bump github.com/prometheus/client_golang from 1.13.0 to 1.13.1
[#360](https://github.com/strukturag/nextcloud-spreed-signaling/pull/360)
- Bump github.com/nats-io/nats-server/v2 from 2.9.3 to 2.9.5
[#359](https://github.com/strukturag/nextcloud-spreed-signaling/pull/359)
- build(deps): Bump golangci/golangci-lint-action from 3.3.0 to 3.3.1
[#365](https://github.com/strukturag/nextcloud-spreed-signaling/pull/365)
- build(deps): Bump sphinx-rtd-theme from 1.1.0 to 1.1.1 in /docs
[#363](https://github.com/strukturag/nextcloud-spreed-signaling/pull/363)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.5 to 2.9.6
[#361](https://github.com/strukturag/nextcloud-spreed-signaling/pull/361)
- build(deps): Bump github.com/nats-io/nats.go from 1.19.0 to 1.20.0
[#366](https://github.com/strukturag/nextcloud-spreed-signaling/pull/366)
- build(deps): Bump google.golang.org/grpc from 1.50.1 to 1.51.0
[#368](https://github.com/strukturag/nextcloud-spreed-signaling/pull/368)
- build(deps): Bump github.com/prometheus/client_golang from 1.13.1 to 1.14.0
[#364](https://github.com/strukturag/nextcloud-spreed-signaling/pull/364)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.6 to 2.9.7
[#367](https://github.com/strukturag/nextcloud-spreed-signaling/pull/367)
- build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.5 to 3.5.6
[#372](https://github.com/strukturag/nextcloud-spreed-signaling/pull/372)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.7 to 2.9.8
[#371](https://github.com/strukturag/nextcloud-spreed-signaling/pull/371)
- build(deps): Bump github.com/nats-io/nats.go from 1.20.0 to 1.21.0
[#375](https://github.com/strukturag/nextcloud-spreed-signaling/pull/375)
- build(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.2 to 4.4.3
[#374](https://github.com/strukturag/nextcloud-spreed-signaling/pull/374)
- build(deps): Bump cirrus-actions/rebase from 1.7 to 1.8
[#379](https://github.com/strukturag/nextcloud-spreed-signaling/pull/379)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.8 to 2.9.9
[#377](https://github.com/strukturag/nextcloud-spreed-signaling/pull/377)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.9 to 2.9.10
[#382](https://github.com/strukturag/nextcloud-spreed-signaling/pull/382)
- build(deps): Bump github.com/nats-io/nats.go from 1.21.0 to 1.22.1
[#383](https://github.com/strukturag/nextcloud-spreed-signaling/pull/383)
- build(deps): Bump google.golang.org/grpc from 1.51.0 to 1.52.0
[#391](https://github.com/strukturag/nextcloud-spreed-signaling/pull/391)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.10 to 2.9.11
[#387](https://github.com/strukturag/nextcloud-spreed-signaling/pull/387)
- Stop using WaitGroup to detect finished message processing.
[#394](https://github.com/strukturag/nextcloud-spreed-signaling/pull/394)
- Improve handling of throttled responses from Nextcloud.
[#395](https://github.com/strukturag/nextcloud-spreed-signaling/pull/395)
- Test: add timeout while waiting for etcd event.
[#397](https://github.com/strukturag/nextcloud-spreed-signaling/pull/397)
- build(deps): Bump github.com/nats-io/nats.go from 1.22.1 to 1.23.0
[#399](https://github.com/strukturag/nextcloud-spreed-signaling/pull/399)
- build(deps): Bump go.etcd.io/etcd/api/v3 from 3.5.6 to 3.5.7
[#402](https://github.com/strukturag/nextcloud-spreed-signaling/pull/402)
- build(deps): Bump go.etcd.io/etcd/client/v3 from 3.5.6 to 3.5.7
[#403](https://github.com/strukturag/nextcloud-spreed-signaling/pull/403)
- build(deps): Bump go.etcd.io/etcd/server/v3 from 3.5.6 to 3.5.7
[#404](https://github.com/strukturag/nextcloud-spreed-signaling/pull/404)
- build(deps): Bump golangci/golangci-lint-action from 3.3.1 to 3.4.0
[#405](https://github.com/strukturag/nextcloud-spreed-signaling/pull/405)
- build(deps): Bump readthedocs-sphinx-search from 0.1.2 to 0.2.0 in /docs
[#407](https://github.com/strukturag/nextcloud-spreed-signaling/pull/407)
- build(deps): Bump google.golang.org/grpc from 1.52.0 to 1.52.1
[#406](https://github.com/strukturag/nextcloud-spreed-signaling/pull/406)
- build(deps): Bump docker/build-push-action from 3 to 4
[#412](https://github.com/strukturag/nextcloud-spreed-signaling/pull/412)
- build(deps): Bump google.golang.org/grpc from 1.52.1 to 1.52.3
[#410](https://github.com/strukturag/nextcloud-spreed-signaling/pull/410)
- Explicitly use type "sysConn".
[#416](https://github.com/strukturag/nextcloud-spreed-signaling/pull/416)
- build(deps): Bump github.com/nats-io/nats-server/v2 from 2.9.11 to 2.9.14
[#415](https://github.com/strukturag/nextcloud-spreed-signaling/pull/415)
- build(deps): Bump sphinx-rtd-theme from 1.1.1 to 1.2.0 in /docs
[#418](https://github.com/strukturag/nextcloud-spreed-signaling/pull/418)
- build(deps): Bump google.golang.org/grpc from 1.52.3 to 1.53.0
[#417](https://github.com/strukturag/nextcloud-spreed-signaling/pull/417)
- build(deps): Bump golang.org/x/net from 0.5.0 to 0.7.0
[#422](https://github.com/strukturag/nextcloud-spreed-signaling/pull/422)
- build(deps): Bump github.com/golang-jwt/jwt/v4 from 4.4.3 to 4.5.0
[#423](https://github.com/strukturag/nextcloud-spreed-signaling/pull/423)
- build(deps): Bump sphinx from 5.3.0 to 6.1.3 in /docs
[#390](https://github.com/strukturag/nextcloud-spreed-signaling/pull/390)
- Various refactorings to simplify code
[#400](https://github.com/strukturag/nextcloud-spreed-signaling/pull/400)
### Fixed
- Remove @resources from SystemCallFilter
[#322](https://github.com/strukturag/nextcloud-spreed-signaling/pull/322)
- Fix deadlock for proxy connection issues
[#327](https://github.com/strukturag/nextcloud-spreed-signaling/pull/327)
- Fix goroutines leak check.
[#396](https://github.com/strukturag/nextcloud-spreed-signaling/pull/396)
## 1.0.0 - 2022-08-04
### Added
- Clustering support.
[#281](https://github.com/strukturag/nextcloud-spreed-signaling/pull/281)
- Send initial "welcome" message when clients connect.
[#288](https://github.com/strukturag/nextcloud-spreed-signaling/pull/288)
- Support hello auth version "2.0" with JWT.
[#251](https://github.com/strukturag/nextcloud-spreed-signaling/pull/251)
- dist: add systemd sysusers file.
[#275](https://github.com/strukturag/nextcloud-spreed-signaling/pull/275)
- Add more tests.
[#292](https://github.com/strukturag/nextcloud-spreed-signaling/pull/292)
- Add tests for virtual sessions.
[#295](https://github.com/strukturag/nextcloud-spreed-signaling/pull/295)
- Implement per-backend session limit for clusters.
[#296](https://github.com/strukturag/nextcloud-spreed-signaling/pull/296)
### Changed
- Don't run "go mod tidy" when building.
[#269](https://github.com/strukturag/nextcloud-spreed-signaling/pull/269)
- Bump sphinx from 5.0.0 to 5.0.1 in /docs
[#270](https://github.com/strukturag/nextcloud-spreed-signaling/pull/270)
- Bump sphinx from 5.0.1 to 5.0.2 in /docs
[#277](https://github.com/strukturag/nextcloud-spreed-signaling/pull/277)
- Move common etcd code to own class.
[#282](https://github.com/strukturag/nextcloud-spreed-signaling/pull/282)
- Support arbitrary capabilities values.
[#287](https://github.com/strukturag/nextcloud-spreed-signaling/pull/287)
- dist: harden systemd service unit.
[#276](https://github.com/strukturag/nextcloud-spreed-signaling/pull/276)
- Update to Go module version of github.com/golang-jwt/jwt
[#289](https://github.com/strukturag/nextcloud-spreed-signaling/pull/289)
- Disconnect sessions with the same room session id synchronously.
[#294](https://github.com/strukturag/nextcloud-spreed-signaling/pull/294)
- Bump google.golang.org/grpc from 1.47.0 to 1.48.0
[#297](https://github.com/strukturag/nextcloud-spreed-signaling/pull/297)
- Update to github.com/pion/sdp v3.0.5
[#301](https://github.com/strukturag/nextcloud-spreed-signaling/pull/301)
- Bump sphinx from 5.0.2 to 5.1.1 in /docs
[#303](https://github.com/strukturag/nextcloud-spreed-signaling/pull/303)
- make: Include vendored dependencies in tarball.
[#300](https://github.com/strukturag/nextcloud-spreed-signaling/pull/300)
- docs: update and pin dependencies.
[#305](https://github.com/strukturag/nextcloud-spreed-signaling/pull/305)
- Bump actions/upload-artifact from 2 to 3
[#307](https://github.com/strukturag/nextcloud-spreed-signaling/pull/307)
- Bump actions/download-artifact from 2 to 3
[#308](https://github.com/strukturag/nextcloud-spreed-signaling/pull/308)
- Bump google.golang.org/protobuf from 1.28.0 to 1.28.1
[#306](https://github.com/strukturag/nextcloud-spreed-signaling/pull/306)
- CI: Also test with Golang 1.19
[#310](https://github.com/strukturag/nextcloud-spreed-signaling/pull/310)
### Fixed
- Fix check for async room messages received while not joined to a room.
[#274](https://github.com/strukturag/nextcloud-spreed-signaling/pull/274)
- Fix testing etcd server not starting up if etcd is running on host.
[#283](https://github.com/strukturag/nextcloud-spreed-signaling/pull/283)
- Fix CI issues on slow CPUs.
[#290](https://github.com/strukturag/nextcloud-spreed-signaling/pull/290)
- Fix handling of "unshareScreen" messages and add test.
[#293](https://github.com/strukturag/nextcloud-spreed-signaling/pull/293)
- Fix Read The Ddocs builds.
[#302](https://github.com/strukturag/nextcloud-spreed-signaling/pull/302)
## 0.5.0 - 2022-06-02
### Added

View file

@ -1,17 +0,0 @@
FROM golang:1.18 AS builder
WORKDIR /workdir
COPY . .
RUN make build
FROM alpine:3.15
ENV CONFIG=/config/server.conf
RUN adduser -D spreedbackend && \
apk add --no-cache ca-certificates libc6-compat libstdc++
USER spreedbackend
COPY --from=builder /workdir/bin/signaling /usr/local/signaling
COPY ./server.conf.in /config/server.conf
CMD ["/bin/sh", "-c", "/usr/local/signaling --config=$CONFIG"]

View file

@ -7,11 +7,20 @@ GOFMT := "$(GODIR)/gofmt"
GOOS ?= linux
GOARCH ?= amd64
GOVERSION := $(shell "$(GO)" env GOVERSION | sed "s|go||" )
BINDIR := "$(CURDIR)/bin"
BINDIR := $(CURDIR)/bin
VENDORDIR := "$(CURDIR)/vendor"
VERSION := $(shell "$(CURDIR)/scripts/get-version.sh")
TARVERSION := $(shell "$(CURDIR)/scripts/get-version.sh" --tar)
PACKAGENAME := github.com/strukturag/nextcloud-spreed-signaling
ALL_PACKAGES := $(PACKAGENAME) $(PACKAGENAME)/client $(PACKAGENAME)/proxy $(PACKAGENAME)/server
PROTO_FILES := $(basename $(wildcard *.proto))
PROTO_GO_FILES := $(addsuffix .pb.go,$(PROTO_FILES)) $(addsuffix _grpc.pb.go,$(PROTO_FILES))
EASYJSON_GO_FILES := \
api_async_easyjson.go \
api_backend_easyjson.go \
api_grpc_easyjson.go \
api_proxy_easyjson.go \
api_signaling_easyjson.go
ifneq ($(VERSION),)
INTERNALLDFLAGS := -X main.version=$(VERSION)
@ -36,13 +45,21 @@ TIMEOUT := 60s
endif
ifneq ($(TEST),)
TESTARGS := $(TESTARGS) -run $(TEST)
TESTARGS := $(TESTARGS) -run "$(TEST)"
endif
ifneq ($(COUNT),)
TESTARGS := $(TESTARGS) -count $(COUNT)
endif
ifneq ($(PARALLEL),)
TESTARGS := $(TESTARGS) -parallel $(PARALLEL)
endif
ifneq ($(VERBOSE),)
TESTARGS := $(TESTARGS) -v
endif
ifeq ($(GOARCH), amd64)
GOPATHBIN := $(GOPATH)/bin
else
@ -52,10 +69,17 @@ endif
hook:
[ ! -d "$(CURDIR)/.git/hooks" ] || ln -sf "$(CURDIR)/scripts/pre-commit.hook" "$(CURDIR)/.git/hooks/pre-commit"
$(GOPATHBIN)/easyjson:
$(GO) get -u -d github.com/mailru/easyjson/...
$(GOPATHBIN)/easyjson: go.mod go.sum
[ "$(GOPROXY)" = "off" ] || $(GO) get -d github.com/mailru/easyjson/...
$(GO) install github.com/mailru/easyjson/...
$(GOPATHBIN)/protoc-gen-go: go.mod go.sum
$(GO) install google.golang.org/protobuf/cmd/protoc-gen-go
$(GOPATHBIN)/protoc-gen-go-grpc: go.mod go.sum
[ "$(GOPROXY)" = "off" ] || $(GO) get -d google.golang.org/grpc/cmd/protoc-gen-go-grpc
$(GO) install google.golang.org/grpc/cmd/protoc-gen-go-grpc
continentmap.go:
$(CURDIR)/scripts/get_continent_map.py $@
@ -70,62 +94,85 @@ check-continentmap:
get:
$(GO) get $(PACKAGE)
fmt: hook
fmt: hook | $(PROTO_GO_FILES)
$(GOFMT) -s -w *.go client proxy server
vet: common
$(GO) vet $(ALL_PACKAGES)
test: vet common
$(GO) test -v -timeout $(TIMEOUT) $(TESTARGS) $(ALL_PACKAGES)
$(GO) test -timeout $(TIMEOUT) $(TESTARGS) $(ALL_PACKAGES)
cover: vet common
rm -f cover.out && \
$(GO) test -v -timeout $(TIMEOUT) -coverprofile cover.out $(ALL_PACKAGES) && \
$(GO) test -timeout $(TIMEOUT) -coverprofile cover.out $(ALL_PACKAGES) && \
sed -i "/_easyjson/d" cover.out && \
sed -i "/\.pb\.go/d" cover.out && \
$(GO) tool cover -func=cover.out
coverhtml: vet common
rm -f cover.out && \
$(GO) test -v -timeout $(TIMEOUT) -coverprofile cover.out $(ALL_PACKAGES) && \
$(GO) test -timeout $(TIMEOUT) -coverprofile cover.out $(ALL_PACKAGES) && \
sed -i "/_easyjson/d" cover.out && \
sed -i "/\.pb\.go/d" cover.out && \
$(GO) tool cover -html=cover.out -o coverage.html
%_easyjson.go: %.go $(GOPATHBIN)/easyjson
%_easyjson.go: %.go $(GOPATHBIN)/easyjson | $(PROTO_GO_FILES)
rm -f easyjson-bootstrap*.go
PATH="$(GODIR)":$(PATH) "$(GOPATHBIN)/easyjson" -all $*.go
common: \
api_signaling_easyjson.go \
api_backend_easyjson.go \
api_proxy_easyjson.go \
natsclient_easyjson.go \
room_easyjson.go
$(GO) mod tidy
%.pb.go: %.proto $(GOPATHBIN)/protoc-gen-go $(GOPATHBIN)/protoc-gen-go-grpc
PATH="$(GODIR)":"$(GOPATHBIN)":$(PATH) protoc \
--go_out=. --go_opt=paths=source_relative \
$*.proto
%_grpc.pb.go: %.proto $(GOPATHBIN)/protoc-gen-go $(GOPATHBIN)/protoc-gen-go-grpc
PATH="$(GODIR)":"$(GOPATHBIN)":$(PATH) protoc \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
$*.proto
common: $(EASYJSON_GO_FILES) $(PROTO_GO_FILES)
$(BINDIR):
mkdir -p $(BINDIR)
mkdir -p "$(BINDIR)"
client: common $(BINDIR)
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o $(BINDIR)/client ./client/...
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o "$(BINDIR)/client" ./client/...
server: common $(BINDIR)
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o $(BINDIR)/signaling ./server/...
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o "$(BINDIR)/signaling" ./server/...
proxy: common $(BINDIR)
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o $(BINDIR)/proxy ./proxy/...
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o "$(BINDIR)/proxy" ./proxy/...
clean:
rm -f *_easyjson.go
rm -f $(EASYJSON_GO_FILES)
rm -f easyjson-bootstrap*.go
rm -f $(PROTO_GO_FILES)
build: server proxy
tarball:
vendor: go.mod go.sum common
set -e ;\
rm -rf $(VENDORDIR)
$(GO) mod tidy; \
$(GO) mod vendor
tarball: vendor
git archive \
--prefix=nextcloud-spreed-signaling-$(TARVERSION)/ \
-o nextcloud-spreed-signaling-$(TARVERSION).tar.gz \
-o nextcloud-spreed-signaling-$(TARVERSION).tar \
HEAD
tar rf nextcloud-spreed-signaling-$(TARVERSION).tar \
-C $(CURDIR) \
--mtime="$(shell git log -1 --date=iso8601-strict --format=%cd HEAD)" \
--transform "s//nextcloud-spreed-signaling-$(TARVERSION)\//" \
vendor
gzip --force nextcloud-spreed-signaling-$(TARVERSION).tar
dist: tarball
.NOTPARALLEL: %_easyjson.go
.PHONY: continentmap.go
.NOTPARALLEL: $(EASYJSON_GO_FILES)
.PHONY: continentmap.go common vendor
.SECONDARY: $(EASYJSON_GO_FILES) $(PROTO_GO_FILES)
.DELETE_ON_ERROR:

View file

@ -8,7 +8,7 @@
This repository contains the standalone signaling server which can be used for
Nextcloud Talk (https://apps.nextcloud.com/apps/spreed).
See https://nextcloud-talk.readthedocs.io/en/latest/standalone-signaling-api-v1/ for further
See https://nextcloud-spreed-signaling.readthedocs.io/en/latest/ for further
information on the API of the signaling server.
@ -17,8 +17,12 @@ information on the API of the signaling server.
The following tools are required for building the signaling server.
- git
- go >= 1.17
- go >= 1.21
- make
- protobuf-compiler >= 3
Usually the last two versions of Go are supported. This follows the release
policy of Go: https://go.dev/doc/devel/release#policy
All other dependencies are fetched automatically while building.
@ -87,23 +91,34 @@ systemctl start signaling.service
### Running with Docker
Official docker containers for the signaling server and -proxy are available on
Docker Hub at https://hub.docker.com/r/strukturag/nextcloud-spreed-signaling
See the `README.md` in the `docker` subfolder for details.
#### Docker Compose
You will likely have to adjust the Janus command line options depending on the exact network configuration on your server. Refer to [Setup of Janus](#setup-of-janus) and the Janus documentation for how to configure your Janus server.
Copy `server.conf.in` to `server.conf` and adjust it to your liking.
If you're using the [docker-compose.yml](docker-compose.yml) configuration as is, the MCU Url must be set to `ws://localhost:8188`, the NATS Url must be set to `nats://localhost:4222`, and TURN Servers must be set to `turn:localhost:3478?transport=udp,turn:localhost:3478?transport=tcp`.
If you're using the [docker-compose.yml](docker/docker-compose.yml) configuration as is, the MCU Url must be set to `ws://localhost:8188`, the NATS Url must be set to `nats://localhost:4222`, and TURN Servers must be set to `turn:localhost:3478?transport=udp,turn:localhost:3478?transport=tcp`.
```bash
docker-compose build
docker-compose up -d
```
Please note that docker-compose v2 is required for building while most
distributions will ship older versions. You can download a recent version from
https://docs.docker.com/compose/install/
## Setup of NATS server
There is a detailed description on how to install and run the NATS server
available at http://nats.io/documentation/tutorials/gnatsd-install/
available at https://docs.nats.io/running-a-nats-service/introduction
You can use the `gnatsd.conf` file as base for the configuration of the NATS
server.
@ -156,6 +171,30 @@ proxy process gracefully after all clients have been disconnected. No new
publishers will be accepted in this case.
### Remote streams (preview)
With Janus 1.1.0 or newer, remote streams are supported, i.e. a subscriber can
receive a published stream from any server. For this, you need to configure
`hostname`, `token_id` and `token_key` in the proxy configuration. Each proxy
server also supports configuring maximum `incoming` and `outgoing` bandwidth
settings, which will also be used to select remote streams.
See `proxy.conf.in` in section `app` for details.
## Clustering
The signaling server supports a clustering mode where multiple running servers
can be interconnected to form a single "virtual" server. This can be used to
increase the capacity of the signaling server or provide a failover setup.
For that a central NATS server / cluster must be used by all instances. Each
instance must run a GRPC server (enable `listening` in section `grpc` and
optionally setup certificate, private key and CA). The list of other GRPC
targets must be configured as `targets` in section `grpc` or can be retrieved
from an etcd cluster. See `server.conf.in` in section `grpc` for configuration
details.
## Setup of frontend webserver
Usually the standalone signaling server is running behind a webserver that does
@ -270,6 +309,8 @@ interface on port `8080` below):
# Enable proxying Websocket requests to the standalone signaling server.
ProxyPass "/standalone-signaling/" "ws://127.0.0.1:8080/"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RewriteEngine On
# Websocket connections from the clients.
RewriteRule ^/standalone-signaling/spreed/$ - [L]
@ -305,6 +346,7 @@ myserver.domain.invalid {
route /standalone-signaling/* {
uri strip_prefix /standalone-signaling
reverse_proxy http://127.0.0.1:8080
header_up X-Real-IP {remote_host}
}
}
```
@ -351,23 +393,3 @@ Usage:
config file to use (default "server.conf")
-maxClients int
number of client connections (default 100)
## Running multiple signaling servers
IMPORTANT: This is considered experimental and might not work with all
functionality of the signaling server, especially when using the Janus
integration.
The signaling server uses the NATS server to send messages to peers that are
not connected locally. Therefore multiple signaling servers running on different
hosts can use the same NATS server to build a simple cluster, allowing more
simultaneous connections and distribute the load.
To set this up, make sure all signaling servers are using the same settings for
their `session` keys and the `secret` in the `backend` section. Also the URL to
the NATS server (option `url` in section `nats`) must point to the same NATS
server.
If all this is setup correctly, clients can connect to either of the signaling
servers and exchange messages between them.

134
allowed_ips.go Normal file
View file

@ -0,0 +1,134 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"bytes"
"fmt"
"net"
"strings"
)
type AllowedIps struct {
allowed []*net.IPNet
}
func (a *AllowedIps) String() string {
var b bytes.Buffer
b.WriteString("[")
for idx, n := range a.allowed {
if idx > 0 {
b.WriteString(", ")
}
b.WriteString(n.String())
}
b.WriteString("]")
return b.String()
}
func (a *AllowedIps) Empty() bool {
return len(a.allowed) == 0
}
func (a *AllowedIps) Allowed(ip net.IP) bool {
for _, i := range a.allowed {
if i.Contains(ip) {
return true
}
}
return false
}
func parseIPNet(s string) (*net.IPNet, error) {
var ipnet *net.IPNet
if strings.ContainsRune(s, '/') {
var err error
if _, ipnet, err = net.ParseCIDR(s); err != nil {
return nil, fmt.Errorf("invalid IP address/subnet %s: %w", s, err)
}
} else {
ip := net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("invalid IP address %s", s)
}
ipnet = &net.IPNet{
IP: ip,
Mask: net.CIDRMask(len(ip)*8, len(ip)*8),
}
}
return ipnet, nil
}
func ParseAllowedIps(allowed string) (*AllowedIps, error) {
var allowedIps []*net.IPNet
for _, ip := range strings.Split(allowed, ",") {
ip = strings.TrimSpace(ip)
if ip != "" {
i, err := parseIPNet(ip)
if err != nil {
return nil, err
}
allowedIps = append(allowedIps, i)
}
}
result := &AllowedIps{
allowed: allowedIps,
}
return result, nil
}
func DefaultAllowedIps() *AllowedIps {
allowedIps := []*net.IPNet{
{
IP: net.ParseIP("127.0.0.1"),
Mask: net.CIDRMask(32, 32),
},
}
result := &AllowedIps{
allowed: allowedIps,
}
return result
}
var (
privateIpNets = []string{
// Loopback addresses.
"127.0.0.0/8",
// Private addresses.
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
}
)
func DefaultPrivateIps() *AllowedIps {
allowed, err := ParseAllowedIps(strings.Join(privateIpNets, ","))
if err != nil {
panic(fmt.Errorf("could not parse private ips %+v: %w", privateIpNets, err))
}
return allowed
}

73
allowed_ips_test.go Normal file
View file

@ -0,0 +1,73 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"net"
"testing"
)
func TestAllowedIps(t *testing.T) {
a, err := ParseAllowedIps("127.0.0.1, 192.168.0.1, 192.168.1.1/24")
if err != nil {
t.Fatal(err)
}
if a.Empty() {
t.Fatal("should not be empty")
}
if expected := `[127.0.0.1/32, 192.168.0.1/32, 192.168.1.0/24]`; a.String() != expected {
t.Errorf("expected %s, got %s", expected, a.String())
}
allowed := []string{
"127.0.0.1",
"192.168.0.1",
"192.168.1.1",
"192.168.1.100",
}
notAllowed := []string{
"192.168.0.2",
"10.1.2.3",
}
for _, addr := range allowed {
t.Run(addr, func(t *testing.T) {
ip := net.ParseIP(addr)
if ip == nil {
t.Errorf("error parsing %s", addr)
} else if !a.Allowed(ip) {
t.Errorf("should allow %s", addr)
}
})
}
for _, addr := range notAllowed {
t.Run(addr, func(t *testing.T) {
ip := net.ParseIP(addr)
if ip == nil {
t.Errorf("error parsing %s", addr)
} else if a.Allowed(ip) {
t.Errorf("should not allow %s", addr)
}
})
}
}

67
api_async.go Normal file
View file

@ -0,0 +1,67 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"encoding/json"
"fmt"
"time"
)
type AsyncMessage struct {
SendTime time.Time `json:"sendtime"`
Type string `json:"type"`
Message *ServerMessage `json:"message,omitempty"`
Room *BackendServerRoomRequest `json:"room,omitempty"`
Permissions []Permission `json:"permissions,omitempty"`
AsyncRoom *AsyncRoomMessage `json:"asyncroom,omitempty"`
SendOffer *SendOfferMessage `json:"sendoffer,omitempty"`
Id string `json:"id"`
}
func (m *AsyncMessage) String() string {
data, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("Could not serialize %#v: %s", m, err)
}
return string(data)
}
type AsyncRoomMessage struct {
Type string `json:"type"`
SessionId string `json:"sessionid,omitempty"`
ClientType string `json:"clienttype,omitempty"`
}
type SendOfferMessage struct {
MessageId string `json:"messageid,omitempty"`
SessionId string `json:"sessionid"`
Data *MessageClientMessageData `json:"data"`
}

View file

@ -28,7 +28,12 @@ import (
"crypto/subtle"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
"time"
)
const (
@ -37,6 +42,11 @@ const (
HeaderBackendSignalingRandom = "Spreed-Signaling-Random"
HeaderBackendSignalingChecksum = "Spreed-Signaling-Checksum"
HeaderBackendServer = "Spreed-Signaling-Backend"
ConfigGroupSignaling = "signaling"
ConfigKeyHelloV2TokenKey = "hello-v2-token-key"
ConfigKeySessionPingLimit = "session-ping-limit"
)
func newRandomString(length int) string {
@ -94,6 +104,12 @@ type BackendServerRoomRequest struct {
Message *BackendRoomMessageRequest `json:"message,omitempty"`
SwitchTo *BackendRoomSwitchToMessageRequest `json:"switchto,omitempty"`
Dialout *BackendRoomDialoutRequest `json:"dialout,omitempty"`
Transient *BackendRoomTransientRequest `json:"transient,omitempty"`
// Internal properties
ReceivedTime int64 `json:"received,omitempty"`
}
@ -102,8 +118,8 @@ type BackendRoomInviteRequest struct {
UserIds []string `json:"userids,omitempty"`
// TODO(jojo): We should get rid of "AllUserIds" and find a better way to
// notify existing users the room has changed and they need to update it.
AllUserIds []string `json:"alluserids,omitempty"`
Properties *json.RawMessage `json:"properties,omitempty"`
AllUserIds []string `json:"alluserids,omitempty"`
Properties json.RawMessage `json:"properties,omitempty"`
}
type BackendRoomDisinviteRequest struct {
@ -111,13 +127,13 @@ type BackendRoomDisinviteRequest struct {
SessionIds []string `json:"sessionids,omitempty"`
// TODO(jojo): We should get rid of "AllUserIds" and find a better way to
// notify existing users the room has changed and they need to update it.
AllUserIds []string `json:"alluserids,omitempty"`
Properties *json.RawMessage `json:"properties,omitempty"`
AllUserIds []string `json:"alluserids,omitempty"`
Properties json.RawMessage `json:"properties,omitempty"`
}
type BackendRoomUpdateRequest struct {
UserIds []string `json:"userids,omitempty"`
Properties *json.RawMessage `json:"properties,omitempty"`
UserIds []string `json:"userids,omitempty"`
Properties json.RawMessage `json:"properties,omitempty"`
}
type BackendRoomDeleteRequest struct {
@ -138,14 +154,91 @@ type BackendRoomParticipantsRequest struct {
}
type BackendRoomMessageRequest struct {
Data *json.RawMessage `json:"data,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
}
type BackendRoomSwitchToSessionsList []string
type BackendRoomSwitchToSessionsMap map[string]json.RawMessage
type BackendRoomSwitchToMessageRequest struct {
// Target room id
RoomId string `json:"roomid"`
// Sessions is either a BackendRoomSwitchToSessionsList or a
// BackendRoomSwitchToSessionsMap.
// In the map, the key is the session id, the value additional details
// (or null) for the session. The details will be included in the request
// to the connected client.
Sessions json.RawMessage `json:"sessions,omitempty"`
// Internal properties
SessionsList BackendRoomSwitchToSessionsList `json:"sessionslist,omitempty"`
SessionsMap BackendRoomSwitchToSessionsMap `json:"sessionsmap,omitempty"`
}
type BackendRoomDialoutRequest struct {
// E.164 number to dial (e.g. "+1234567890")
Number string `json:"number"`
Options json.RawMessage `json:"options,omitempty"`
}
var (
checkE164Number = regexp.MustCompile(`^\+\d{2,}$`)
)
func isValidNumber(s string) bool {
return checkE164Number.MatchString(s)
}
func (r *BackendRoomDialoutRequest) ValidateNumber() *Error {
if r.Number == "" {
return NewError("number_missing", "No number provided")
}
if !isValidNumber(r.Number) {
return NewError("invalid_number", "Expected E.164 number.")
}
return nil
}
type TransientAction string
const (
TransientActionSet TransientAction = "set"
TransientActionDelete TransientAction = "delete"
)
type BackendRoomTransientRequest struct {
Action TransientAction `json:"action"`
Key string `json:"key"`
Value interface{} `json:"value,omitempty"`
TTL time.Duration `json:"ttl,omitempty"`
}
type BackendServerRoomResponse struct {
Type string `json:"type"`
Dialout *BackendRoomDialoutResponse `json:"dialout,omitempty"`
}
type BackendRoomDialoutError struct {
Code string `json:"code"`
Message string `json:"message,omitempty"`
}
type BackendRoomDialoutResponse struct {
CallId string `json:"callid,omitempty"`
Error *Error `json:"error,omitempty"`
}
// Requests from the signaling server to the Nextcloud backend.
type BackendClientAuthRequest struct {
Version string `json:"version"`
Params *json.RawMessage `json:"params"`
Version string `json:"version"`
Params json.RawMessage `json:"params"`
}
type BackendClientRequest struct {
@ -163,7 +256,7 @@ type BackendClientRequest struct {
Session *BackendClientSessionRequest `json:"session,omitempty"`
}
func NewBackendClientAuthRequest(params *json.RawMessage) *BackendClientRequest {
func NewBackendClientAuthRequest(params json.RawMessage) *BackendClientRequest {
return &BackendClientRequest{
Type: "auth",
Auth: &BackendClientAuthRequest{
@ -191,9 +284,9 @@ type BackendClientResponse struct {
}
type BackendClientAuthResponse struct {
Version string `json:"version"`
UserId string `json:"userid"`
User *json.RawMessage `json:"user"`
Version string `json:"version"`
UserId string `json:"userid"`
User json.RawMessage `json:"user"`
}
type BackendClientRoomRequest struct {
@ -222,14 +315,14 @@ func NewBackendClientRoomRequest(roomid string, userid string, sessionid string)
}
type BackendClientRoomResponse struct {
Version string `json:"version"`
RoomId string `json:"roomid"`
Properties *json.RawMessage `json:"properties"`
Version string `json:"version"`
RoomId string `json:"roomid"`
Properties json.RawMessage `json:"properties"`
// Optional information about the Nextcloud Talk session. Can be used for
// example to define a "userid" for otherwise anonymous users.
// See "RoomSessionData" for a possible content.
Session *json.RawMessage `json:"session,omitempty"`
Session json.RawMessage `json:"session,omitempty"`
Permissions *[]Permission `json:"permissions,omitempty"`
}
@ -266,12 +359,12 @@ type BackendClientRingResponse struct {
}
type BackendClientSessionRequest struct {
Version string `json:"version"`
RoomId string `json:"roomid"`
Action string `json:"action"`
SessionId string `json:"sessionid"`
UserId string `json:"userid,omitempty"`
User *json.RawMessage `json:"user,omitempty"`
Version string `json:"version"`
RoomId string `json:"roomid"`
Action string `json:"action"`
SessionId string `json:"sessionid"`
UserId string `json:"userid,omitempty"`
User json.RawMessage `json:"user,omitempty"`
}
type BackendClientSessionResponse struct {
@ -303,8 +396,8 @@ type OcsMeta struct {
}
type OcsBody struct {
Meta OcsMeta `json:"meta"`
Data *json.RawMessage `json:"data"`
Meta OcsMeta `json:"meta"`
Data json.RawMessage `json:"data"`
}
type OcsResponse struct {
@ -321,3 +414,39 @@ type TurnCredentials struct {
TTL int64 `json:"ttl"`
URIs []string `json:"uris"`
}
// Information on a backend in the etcd cluster.
type BackendInformationEtcd struct {
parsedUrl *url.URL
Url string `json:"url"`
Secret string `json:"secret"`
MaxStreamBitrate int `json:"maxstreambitrate,omitempty"`
MaxScreenBitrate int `json:"maxscreenbitrate,omitempty"`
SessionLimit uint64 `json:"sessionlimit,omitempty"`
}
func (p *BackendInformationEtcd) CheckValid() error {
if p.Url == "" {
return fmt.Errorf("url missing")
}
if p.Secret == "" {
return fmt.Errorf("secret missing")
}
parsedUrl, err := url.Parse(p.Url)
if err != nil {
return fmt.Errorf("invalid url: %w", err)
}
if strings.Contains(parsedUrl.Host, ":") && hasStandardPort(parsedUrl) {
parsedUrl.Host = parsedUrl.Hostname()
p.Url = parsedUrl.String()
}
p.parsedUrl = parsedUrl
return nil
}

View file

@ -27,6 +27,7 @@ import (
)
func TestBackendChecksum(t *testing.T) {
t.Parallel()
rnd := newRandomString(32)
body := []byte{1, 2, 3, 4, 5}
secret := []byte("shared-secret")
@ -56,3 +57,28 @@ func TestBackendChecksum(t *testing.T) {
t.Errorf("Checksum %s could not be validated from request", check1)
}
}
func TestValidNumbers(t *testing.T) {
t.Parallel()
valid := []string{
"+12",
"+12345",
}
invalid := []string{
"+1",
"12345",
" +12345",
" +12345 ",
"+123-45",
}
for _, number := range valid {
if !isValidNumber(number) {
t.Errorf("number %s should be valid", number)
}
}
for _, number := range invalid {
if isValidNumber(number) {
t.Errorf("number %s should not be valid", number)
}
}
}

41
api_grpc.go Normal file
View file

@ -0,0 +1,41 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"fmt"
)
// Information on a GRPC target in the etcd cluster.
type GrpcTargetInformationEtcd struct {
Address string `json:"address"`
}
func (p *GrpcTargetInformationEtcd) CheckValid() error {
if l := len(p.Address); l == 0 {
return fmt.Errorf("address missing")
} else if p.Address[l-1] == '/' {
p.Address = p.Address[:l-1]
}
return nil
}

View file

@ -24,8 +24,9 @@ package signaling
import (
"encoding/json"
"fmt"
"net/url"
"github.com/golang-jwt/jwt"
"github.com/golang-jwt/jwt/v4"
)
type ProxyClientMessage struct {
@ -48,6 +49,14 @@ type ProxyClientMessage struct {
Payload *PayloadProxyClientMessage `json:"payload,omitempty"`
}
func (m *ProxyClientMessage) String() string {
data, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("Could not serialize %#v: %s", m, err)
}
return string(data)
}
func (m *ProxyClientMessage) CheckValid() error {
switch m.Type {
case "":
@ -115,6 +124,14 @@ type ProxyServerMessage struct {
Event *EventProxyServerMessage `json:"event,omitempty"`
}
func (r *ProxyServerMessage) String() string {
data, err := json.Marshal(r)
if err != nil {
return fmt.Sprintf("Could not serialize %#v: %s", r, err)
}
return string(data)
}
func (r *ProxyServerMessage) CloseAfterSend(session Session) bool {
switch r.Type {
case "bye":
@ -127,7 +144,7 @@ func (r *ProxyServerMessage) CloseAfterSend(session Session) bool {
// Type "hello"
type TokenClaims struct {
jwt.StandardClaims
jwt.RegisteredClaims
}
type HelloProxyClientMessage struct {
@ -142,7 +159,7 @@ type HelloProxyClientMessage struct {
}
func (m *HelloProxyClientMessage) CheckValid() error {
if m.Version != HelloVersion {
if m.Version != HelloVersionV1 {
return fmt.Errorf("unsupported hello version: %s", m.Version)
}
if m.ResumeId == "" {
@ -156,8 +173,8 @@ func (m *HelloProxyClientMessage) CheckValid() error {
type HelloProxyServerMessage struct {
Version string `json:"version"`
SessionId string `json:"sessionid"`
Server *HelloServerMessageServer `json:"server,omitempty"`
SessionId string `json:"sessionid"`
Server *WelcomeServerMessage `json:"server,omitempty"`
}
// Type "bye"
@ -179,12 +196,20 @@ type ByeProxyServerMessage struct {
type CommandProxyClientMessage struct {
Type string `json:"type"`
Sid string `json:"sid,omitempty"`
StreamType string `json:"streamType,omitempty"`
PublisherId string `json:"publisherId,omitempty"`
ClientId string `json:"clientId,omitempty"`
Bitrate int `json:"bitrate,omitempty"`
MediaTypes MediaType `json:"mediatypes,omitempty"`
Sid string `json:"sid,omitempty"`
StreamType StreamType `json:"streamType,omitempty"`
PublisherId string `json:"publisherId,omitempty"`
ClientId string `json:"clientId,omitempty"`
Bitrate int `json:"bitrate,omitempty"`
MediaTypes MediaType `json:"mediatypes,omitempty"`
RemoteUrl string `json:"remoteUrl,omitempty"`
remoteUrl *url.URL
RemoteToken string `json:"remoteToken,omitempty"`
Hostname string `json:"hostname,omitempty"`
Port int `json:"port,omitempty"`
RtcpPort int `json:"rtcpPort,omitempty"`
}
func (m *CommandProxyClientMessage) CheckValid() error {
@ -202,6 +227,17 @@ func (m *CommandProxyClientMessage) CheckValid() error {
if m.StreamType == "" {
return fmt.Errorf("stream type missing")
}
if m.RemoteUrl != "" {
if m.RemoteToken == "" {
return fmt.Errorf("remote token missing")
}
remoteUrl, err := url.Parse(m.RemoteUrl)
if err != nil {
return fmt.Errorf("invalid remote url: %w", err)
}
m.remoteUrl = remoteUrl
}
case "delete-publisher":
fallthrough
case "delete-subscriber":
@ -215,6 +251,10 @@ func (m *CommandProxyClientMessage) CheckValid() error {
type CommandProxyServerMessage struct {
Id string `json:"id,omitempty"`
Sid string `json:"sid,omitempty"`
Bitrate int `json:"bitrate,omitempty"`
Streams []PublisherStream `json:"streams,omitempty"`
}
// Type "payload"
@ -259,12 +299,41 @@ type PayloadProxyServerMessage struct {
// Type "event"
type EventProxyServerBandwidth struct {
// Incoming is the bandwidth utilization for publishers in percent.
Incoming *float64 `json:"incoming,omitempty"`
// Outgoing is the bandwidth utilization for subscribers in percent.
Outgoing *float64 `json:"outgoing,omitempty"`
}
func (b *EventProxyServerBandwidth) String() string {
if b.Incoming != nil && b.Outgoing != nil {
return fmt.Sprintf("bandwidth: incoming=%.3f%%, outgoing=%.3f%%", *b.Incoming, *b.Outgoing)
} else if b.Incoming != nil {
return fmt.Sprintf("bandwidth: incoming=%.3f%%, outgoing=unlimited", *b.Incoming)
} else if b.Outgoing != nil {
return fmt.Sprintf("bandwidth: incoming=unlimited, outgoing=%.3f%%", *b.Outgoing)
} else {
return "bandwidth: incoming=unlimited, outgoing=unlimited"
}
}
func (b EventProxyServerBandwidth) AllowIncoming() bool {
return b.Incoming == nil || *b.Incoming < 100
}
func (b EventProxyServerBandwidth) AllowOutgoing() bool {
return b.Outgoing == nil || *b.Outgoing < 100
}
type EventProxyServerMessage struct {
Type string `json:"type"`
ClientId string `json:"clientId,omitempty"`
Load int64 `json:"load,omitempty"`
Sid string `json:"sid,omitempty"`
Bandwidth *EventProxyServerBandwidth `json:"bandwidth,omitempty"`
}
// Information on a proxy in the etcd cluster.

View file

@ -23,14 +23,29 @@ package signaling
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/url"
"sort"
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/pion/sdp/v3"
)
const (
// Version that must be sent in a "hello" message.
HelloVersion = "1.0"
// Version 1.0 validates auth params against the Nextcloud instance.
HelloVersionV1 = "1.0"
// Version 2.0 validates auth params encoded as JWT.
HelloVersionV2 = "2.0"
)
var (
ErrNoSdp = NewError("no_sdp", "Payload does not contain a SDP.")
ErrInvalidSdp = NewError("invalid_sdp", "Payload does not contain a valid SDP.")
)
// ClientMessage is a message that is sent from a client to the server.
@ -141,6 +156,8 @@ type ServerMessage struct {
Error *Error `json:"error,omitempty"`
Welcome *WelcomeServerMessage `json:"welcome,omitempty"`
Hello *HelloServerMessage `json:"hello,omitempty"`
Bye *ByeServerMessage `json:"bye,omitempty"`
@ -154,6 +171,10 @@ type ServerMessage struct {
Event *EventServerMessage `json:"event,omitempty"`
TransientData *TransientDataServerMessage `json:"transient,omitempty"`
Internal *InternalServerMessage `json:"internal,omitempty"`
Dialout *DialoutInternalClientMessage `json:"dialout,omitempty"`
}
func (r *ServerMessage) CloseAfterSend(session Session) bool {
@ -177,12 +198,12 @@ func (r *ServerMessage) CloseAfterSend(session Session) bool {
}
func (r *ServerMessage) IsChatRefresh() bool {
if r.Type != "message" || r.Message == nil || r.Message.Data == nil || len(*r.Message.Data) == 0 {
if r.Type != "message" || r.Message == nil || len(r.Message.Data) == 0 {
return false
}
var data MessageServerMessageData
if err := json.Unmarshal(*r.Message.Data, &data); err != nil {
if err := json.Unmarshal(r.Message.Data, &data); err != nil {
return false
}
@ -212,9 +233,9 @@ func (r *ServerMessage) String() string {
}
type Error struct {
Code string `json:"code"`
Message string `json:"message"`
Details interface{} `json:"details,omitempty"`
Code string `json:"code"`
Message string `json:"message"`
Details json.RawMessage `json:"details,omitempty"`
}
func NewError(code string, message string) *Error {
@ -222,10 +243,19 @@ func NewError(code string, message string) *Error {
}
func NewErrorDetail(code string, message string, details interface{}) *Error {
var rawDetails json.RawMessage
if details != nil {
var err error
if rawDetails, err = json.Marshal(details); err != nil {
log.Printf("Could not marshal details %+v for error %s with %s: %s", details, code, message, err)
return NewError("internal_error", "Could not marshal error details")
}
}
return &Error{
Code: code,
Message: message,
Details: details,
Details: rawDetails,
}
}
@ -233,6 +263,54 @@ func (e *Error) Error() string {
return e.Message
}
type WelcomeServerMessage struct {
Version string `json:"version"`
Features []string `json:"features,omitempty"`
Country string `json:"country,omitempty"`
}
func NewWelcomeServerMessage(version string, feature ...string) *WelcomeServerMessage {
message := &WelcomeServerMessage{
Version: version,
Features: feature,
}
if len(feature) > 0 {
sort.Strings(message.Features)
}
return message
}
func (m *WelcomeServerMessage) AddFeature(feature ...string) {
newFeatures := make([]string, len(m.Features))
copy(newFeatures, m.Features)
for _, feat := range feature {
found := false
for _, f := range newFeatures {
if f == feat {
found = true
break
}
}
if !found {
newFeatures = append(newFeatures, feat)
}
}
sort.Strings(newFeatures)
m.Features = newFeatures
}
func (m *WelcomeServerMessage) RemoveFeature(feature ...string) {
newFeatures := make([]string, len(m.Features))
copy(newFeatures, m.Features)
for _, feat := range feature {
idx := sort.SearchStrings(newFeatures, feat)
if idx < len(newFeatures) && newFeatures[idx] == feat {
newFeatures = append(newFeatures[:idx], newFeatures[idx+1:]...)
}
}
m.Features = newFeatures
}
const (
HelloClientTypeClient = "client"
HelloClientTypeInternal = "internal"
@ -274,17 +352,35 @@ func (p *ClientTypeInternalAuthParams) CheckValid() error {
return nil
}
type HelloV2AuthParams struct {
Token string `json:"token"`
}
func (p *HelloV2AuthParams) CheckValid() error {
if p.Token == "" {
return fmt.Errorf("token missing")
}
return nil
}
type HelloV2TokenClaims struct {
jwt.RegisteredClaims
UserData json.RawMessage `json:"userdata,omitempty"`
}
type HelloClientMessageAuth struct {
// The client type that is connecting. Leave empty to use the default
// "HelloClientTypeClient"
Type string `json:"type,omitempty"`
Params *json.RawMessage `json:"params"`
Params json.RawMessage `json:"params"`
Url string `json:"url"`
parsedUrl *url.URL
internalParams ClientTypeInternalAuthParams
helloV2Params HelloV2AuthParams
}
// Type "hello"
@ -297,15 +393,15 @@ type HelloClientMessage struct {
Features []string `json:"features,omitempty"`
// The authentication credentials.
Auth HelloClientMessageAuth `json:"auth"`
Auth *HelloClientMessageAuth `json:"auth,omitempty"`
}
func (m *HelloClientMessage) CheckValid() error {
if m.Version != HelloVersion {
return fmt.Errorf("unsupported hello version: %s", m.Version)
if m.Version != HelloVersionV1 && m.Version != HelloVersionV2 {
return InvalidHelloVersion
}
if m.ResumeId == "" {
if m.Auth.Params == nil || len(*m.Auth.Params) == 0 {
if m.Auth == nil || len(m.Auth.Params) == 0 {
return fmt.Errorf("params missing")
}
if m.Auth.Type == "" {
@ -324,8 +420,19 @@ func (m *HelloClientMessage) CheckValid() error {
m.Auth.parsedUrl = u
}
switch m.Version {
case HelloVersionV1:
// No additional validation necessary.
case HelloVersionV2:
if err := json.Unmarshal(m.Auth.Params, &m.Auth.helloV2Params); err != nil {
return err
} else if err := m.Auth.helloV2Params.CheckValid(); err != nil {
return err
}
}
case HelloClientTypeInternal:
if err := json.Unmarshal(*m.Auth.Params, &m.Auth.internalParams); err != nil {
if err := json.Unmarshal(m.Auth.Params, &m.Auth.internalParams); err != nil {
return err
} else if err := m.Auth.internalParams.CheckValid(); err != nil {
return err
@ -338,16 +445,24 @@ func (m *HelloClientMessage) CheckValid() error {
}
const (
// Features for all clients.
// Features to send to all clients.
ServerFeatureMcu = "mcu"
ServerFeatureSimulcast = "simulcast"
ServerFeatureUpdateSdp = "update-sdp"
ServerFeatureAudioVideoPermissions = "audio-video-permissions"
ServerFeatureTransientData = "transient-data"
ServerFeatureInCallAll = "incall-all"
ServerFeatureWelcome = "welcome"
ServerFeatureHelloV2 = "hello-v2"
ServerFeatureSwitchTo = "switchto"
ServerFeatureDialout = "dialout"
// Features for internal clients only.
// Features to send to internal clients only.
ServerFeatureInternalVirtualSessions = "virtual-sessions"
// Possible client features from the "hello" request.
ClientFeatureInternalInCall = "internal-incall"
ClientFeatureStartDialout = "start-dialout"
)
var (
@ -355,27 +470,41 @@ var (
ServerFeatureAudioVideoPermissions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
}
DefaultFeaturesInternal = []string{
ServerFeatureInternalVirtualSessions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
}
DefaultWelcomeFeatures = []string{
ServerFeatureAudioVideoPermissions,
ServerFeatureInternalVirtualSessions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
}
)
type HelloServerMessageServer struct {
Version string `json:"version"`
Features []string `json:"features,omitempty"`
Country string `json:"country,omitempty"`
}
type HelloServerMessage struct {
Version string `json:"version"`
SessionId string `json:"sessionid"`
ResumeId string `json:"resumeid"`
UserId string `json:"userid"`
Server *HelloServerMessageServer `json:"server,omitempty"`
SessionId string `json:"sessionid"`
ResumeId string `json:"resumeid"`
UserId string `json:"userid"`
// TODO: Remove once all clients have switched to the "welcome" message.
Server *WelcomeServerMessage `json:"server,omitempty"`
}
// Type "bye"
@ -405,8 +534,12 @@ func (m *RoomClientMessage) CheckValid() error {
}
type RoomServerMessage struct {
RoomId string `json:"roomid"`
Properties *json.RawMessage `json:"properties,omitempty"`
RoomId string `json:"roomid"`
Properties json.RawMessage `json:"properties,omitempty"`
}
type RoomErrorDetails struct {
Room *RoomServerMessage `json:"room"`
}
// Type "message"
@ -427,7 +560,7 @@ type MessageClientMessageRecipient struct {
type MessageClientMessage struct {
Recipient MessageClientMessageRecipient `json:"recipient"`
Data *json.RawMessage `json:"data"`
Data json.RawMessage `json:"data"`
}
type MessageClientMessageData struct {
@ -436,10 +569,44 @@ type MessageClientMessageData struct {
RoomType string `json:"roomType"`
Bitrate int `json:"bitrate,omitempty"`
Payload map[string]interface{} `json:"payload"`
offerSdp *sdp.SessionDescription // Only set if Type == "offer"
answerSdp *sdp.SessionDescription // Only set if Type == "answer"
}
func (m *MessageClientMessageData) CheckValid() error {
if m.RoomType != "" && !IsValidStreamType(m.RoomType) {
return fmt.Errorf("invalid room type: %s", m.RoomType)
}
if m.Type == "offer" || m.Type == "answer" {
sdpValue, found := m.Payload["sdp"]
if !found {
return ErrNoSdp
}
sdpText, ok := sdpValue.(string)
if !ok {
return ErrInvalidSdp
}
var sdp sdp.SessionDescription
if err := sdp.Unmarshal([]byte(sdpText)); err != nil {
return NewErrorDetail("invalid_sdp", "Error parsing SDP from payload.", map[string]interface{}{
"error": err.Error(),
})
}
switch m.Type {
case "offer":
m.offerSdp = &sdp
case "answer":
m.answerSdp = &sdp
}
}
return nil
}
func (m *MessageClientMessage) CheckValid() error {
if m.Data == nil || len(*m.Data) == 0 {
if len(m.Data) == 0 {
return fmt.Errorf("message empty")
}
switch m.Recipient.Type {
@ -480,7 +647,7 @@ type MessageServerMessage struct {
Sender *MessageServerMessageSender `json:"sender"`
Recipient *MessageClientMessageRecipient `json:"recipient,omitempty"`
Data *json.RawMessage `json:"data"`
Data json.RawMessage `json:"data"`
}
// Type "control"
@ -497,7 +664,7 @@ type ControlServerMessage struct {
Sender *MessageServerMessageSender `json:"sender"`
Recipient *MessageClientMessageRecipient `json:"recipient,omitempty"`
Data *json.RawMessage `json:"data"`
Data json.RawMessage `json:"data"`
}
// Type "internal"
@ -526,9 +693,10 @@ type AddSessionOptions struct {
type AddSessionInternalClientMessage struct {
CommonSessionInternalClientMessage
UserId string `json:"userid,omitempty"`
User *json.RawMessage `json:"user,omitempty"`
Flags uint32 `json:"flags,omitempty"`
UserId string `json:"userid,omitempty"`
User json.RawMessage `json:"user,omitempty"`
Flags uint32 `json:"flags,omitempty"`
InCall *int `json:"incall,omitempty"`
Options *AddSessionOptions `json:"options,omitempty"`
}
@ -540,7 +708,8 @@ func (m *AddSessionInternalClientMessage) CheckValid() error {
type UpdateSessionInternalClientMessage struct {
CommonSessionInternalClientMessage
Flags *uint32 `json:"flags,omitempty"`
Flags *uint32 `json:"flags,omitempty"`
InCall *int `json:"incall,omitempty"`
}
func (m *UpdateSessionInternalClientMessage) CheckValid() error {
@ -557,6 +726,60 @@ func (m *RemoveSessionInternalClientMessage) CheckValid() error {
return m.CommonSessionInternalClientMessage.CheckValid()
}
type InCallInternalClientMessage struct {
InCall int `json:"incall"`
}
func (m *InCallInternalClientMessage) CheckValid() error {
return nil
}
type DialoutStatus string
var (
DialoutStatusAccepted DialoutStatus = "accepted"
DialoutStatusRinging DialoutStatus = "ringing"
DialoutStatusConnected DialoutStatus = "connected"
DialoutStatusRejected DialoutStatus = "rejected"
DialoutStatusCleared DialoutStatus = "cleared"
)
type DialoutStatusInternalClientMessage struct {
CallId string `json:"callid"`
Status DialoutStatus `json:"status"`
// Cause is set if Status is "cleared" or "rejected".
Cause string `json:"cause,omitempty"`
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
type DialoutInternalClientMessage struct {
Type string `json:"type"`
RoomId string `json:"roomid,omitempty"`
Error *Error `json:"error,omitempty"`
Status *DialoutStatusInternalClientMessage `json:"status,omitempty"`
}
func (m *DialoutInternalClientMessage) CheckValid() error {
switch m.Type {
case "":
return errors.New("type missing")
case "error":
if m.Error == nil {
return errors.New("error missing")
}
case "status":
if m.Status == nil {
return errors.New("status missing")
}
}
return nil
}
type InternalClientMessage struct {
Type string `json:"type"`
@ -565,10 +788,16 @@ type InternalClientMessage struct {
UpdateSession *UpdateSessionInternalClientMessage `json:"updatesession,omitempty"`
RemoveSession *RemoveSessionInternalClientMessage `json:"removesession,omitempty"`
InCall *InCallInternalClientMessage `json:"incall,omitempty"`
Dialout *DialoutInternalClientMessage `json:"dialout,omitempty"`
}
func (m *InternalClientMessage) CheckValid() error {
switch m.Type {
case "":
return errors.New("type missing")
case "addsession":
if m.AddSession == nil {
return fmt.Errorf("addsession missing")
@ -587,23 +816,56 @@ func (m *InternalClientMessage) CheckValid() error {
} else if err := m.RemoveSession.CheckValid(); err != nil {
return err
}
case "incall":
if m.InCall == nil {
return fmt.Errorf("incall missing")
} else if err := m.InCall.CheckValid(); err != nil {
return err
}
case "dialout":
if m.Dialout == nil {
return fmt.Errorf("dialout missing")
} else if err := m.Dialout.CheckValid(); err != nil {
return err
}
}
return nil
}
type InternalServerDialoutRequest struct {
RoomId string `json:"roomid"`
Backend string `json:"backend"`
Request *BackendRoomDialoutRequest `json:"request"`
}
type InternalServerMessage struct {
Type string `json:"type"`
Dialout *InternalServerDialoutRequest `json:"dialout,omitempty"`
}
// Type "event"
type RoomEventServerMessage struct {
RoomId string `json:"roomid"`
Properties *json.RawMessage `json:"properties,omitempty"`
RoomId string `json:"roomid"`
Properties json.RawMessage `json:"properties,omitempty"`
// TODO(jojo): Change "InCall" to "int" when #914 has landed in NC Talk.
InCall *json.RawMessage `json:"incall,omitempty"`
InCall json.RawMessage `json:"incall,omitempty"`
Changed []map[string]interface{} `json:"changed,omitempty"`
Users []map[string]interface{} `json:"users,omitempty"`
All bool `json:"all,omitempty"`
}
func (m *RoomEventServerMessage) String() string {
data, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("Could not serialize %#v: %s", m, err)
}
return string(data)
}
const (
DisinviteReasonDisinvited = "disinvited"
DisinviteReasonDeleted = "deleted"
@ -616,8 +878,8 @@ type RoomDisinviteEventServerMessage struct {
}
type RoomEventMessage struct {
RoomId string `json:"roomid"`
Data *json.RawMessage `json:"data,omitempty"`
RoomId string `json:"roomid"`
Data json.RawMessage `json:"data,omitempty"`
}
type RoomFlagsServerMessage struct {
@ -643,9 +905,10 @@ type EventServerMessage struct {
Type string `json:"type"`
// Used for target "room"
Join []*EventServerMessageSessionEntry `json:"join,omitempty"`
Leave []string `json:"leave,omitempty"`
Change []*EventServerMessageSessionEntry `json:"change,omitempty"`
Join []*EventServerMessageSessionEntry `json:"join,omitempty"`
Leave []string `json:"leave,omitempty"`
Change []*EventServerMessageSessionEntry `json:"change,omitempty"`
SwitchTo *EventServerMessageSwitchTo `json:"switchto,omitempty"`
// Used for target "roomlist" / "participants"
Invite *RoomEventServerMessage `json:"invite,omitempty"`
@ -657,11 +920,19 @@ type EventServerMessage struct {
Message *RoomEventMessage `json:"message,omitempty"`
}
func (m *EventServerMessage) String() string {
data, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("Could not serialize %#v: %s", m, err)
}
return string(data)
}
type EventServerMessageSessionEntry struct {
SessionId string `json:"sessionid"`
UserId string `json:"userid"`
User *json.RawMessage `json:"user,omitempty"`
RoomSessionId string `json:"roomsessionid,omitempty"`
SessionId string `json:"sessionid"`
UserId string `json:"userid"`
User json.RawMessage `json:"user,omitempty"`
RoomSessionId string `json:"roomsessionid,omitempty"`
}
func (e *EventServerMessageSessionEntry) Clone() *EventServerMessageSessionEntry {
@ -673,6 +944,11 @@ func (e *EventServerMessageSessionEntry) Clone() *EventServerMessageSessionEntry
}
}
type EventServerMessageSwitchTo struct {
RoomId string `json:"roomid"`
Details json.RawMessage `json:"details,omitempty"`
}
// MCU-related types
type AnswerOfferMessage struct {
@ -689,8 +965,9 @@ type AnswerOfferMessage struct {
type TransientDataClientMessage struct {
Type string `json:"type"`
Key string `json:"key,omitempty"`
Value *json.RawMessage `json:"value,omitempty"`
Key string `json:"key,omitempty"`
Value json.RawMessage `json:"value,omitempty"`
TTL time.Duration `json:"ttl,omitempty"`
}
func (m *TransientDataClientMessage) CheckValid() error {

View file

@ -24,6 +24,8 @@ package signaling
import (
"encoding/json"
"fmt"
"reflect"
"sort"
"testing"
)
@ -79,6 +81,7 @@ func testMessages(t *testing.T, messageType string, valid_messages []testCheckVa
}
func TestClientMessage(t *testing.T) {
t.Parallel()
// The message needs a type.
msg := ClientMessage{}
if err := msg.CheckValid(); err == nil {
@ -87,77 +90,135 @@ func TestClientMessage(t *testing.T) {
}
func TestHelloClientMessage(t *testing.T) {
t.Parallel()
internalAuthParams := []byte("{\"backend\":\"https://domain.invalid\"}")
tokenAuthParams := []byte("{\"token\":\"invalid-token\"}")
valid_messages := []testCheckValid{
// Hello version 1
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Params: json.RawMessage("{}"),
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Type: "client",
Params: &json.RawMessage{'{', '}'},
Params: json.RawMessage("{}"),
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Type: "internal",
Params: (*json.RawMessage)(&internalAuthParams),
Params: internalAuthParams,
},
},
&HelloClientMessage{
Version: HelloVersion,
Version: HelloVersionV1,
ResumeId: "the-resume-id",
},
// Hello version 2
&HelloClientMessage{
Version: HelloVersionV2,
Auth: &HelloClientMessageAuth{
Params: tokenAuthParams,
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: &HelloClientMessageAuth{
Type: "client",
Params: tokenAuthParams,
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
ResumeId: "the-resume-id",
},
}
invalid_messages := []testCheckValid{
// Hello version 1
&HelloClientMessage{},
&HelloClientMessage{Version: "0.0"},
&HelloClientMessage{Version: HelloVersion},
&HelloClientMessage{Version: HelloVersionV1},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Params: json.RawMessage("{}"),
Type: "invalid-type",
},
},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Params: json.RawMessage("{}"),
},
},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'},
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Params: json.RawMessage("{}"),
Url: "invalid-url",
},
},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Type: "internal",
Params: &json.RawMessage{'{', '}'},
Params: json.RawMessage("{}"),
},
},
&HelloClientMessage{
Version: HelloVersion,
Auth: HelloClientMessageAuth{
Version: HelloVersionV1,
Auth: &HelloClientMessageAuth{
Type: "internal",
Params: &json.RawMessage{'x', 'y', 'z'}, // Invalid JSON.
Params: json.RawMessage("xyz"), // Invalid JSON.
},
},
// Hello version 2
&HelloClientMessage{
Version: HelloVersionV2,
Auth: &HelloClientMessageAuth{
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: &HelloClientMessageAuth{
Params: tokenAuthParams,
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: &HelloClientMessageAuth{
Params: tokenAuthParams,
Url: "invalid-url",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: &HelloClientMessageAuth{
Params: internalAuthParams,
Url: "https://domain.invalid",
},
},
&HelloClientMessage{
Version: HelloVersionV2,
Auth: &HelloClientMessageAuth{
Params: json.RawMessage("xyz"), // Invalid JSON.
Url: "https://domain.invalid",
},
},
}
@ -174,26 +235,27 @@ func TestHelloClientMessage(t *testing.T) {
}
func TestMessageClientMessage(t *testing.T) {
t.Parallel()
valid_messages := []testCheckValid{
&MessageClientMessage{
Recipient: MessageClientMessageRecipient{
Type: "session",
SessionId: "the-session-id",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
&MessageClientMessage{
Recipient: MessageClientMessageRecipient{
Type: "user",
UserId: "the-user-id",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
&MessageClientMessage{
Recipient: MessageClientMessageRecipient{
Type: "room",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
}
invalid_messages := []testCheckValid{
@ -208,20 +270,20 @@ func TestMessageClientMessage(t *testing.T) {
Recipient: MessageClientMessageRecipient{
Type: "session",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
&MessageClientMessage{
Recipient: MessageClientMessageRecipient{
Type: "session",
UserId: "the-user-id",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
&MessageClientMessage{
Recipient: MessageClientMessageRecipient{
Type: "user",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
&MessageClientMessage{
Recipient: MessageClientMessageRecipient{
@ -234,13 +296,13 @@ func TestMessageClientMessage(t *testing.T) {
Type: "user",
SessionId: "the-user-id",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
&MessageClientMessage{
Recipient: MessageClientMessageRecipient{
Type: "unknown-type",
},
Data: &json.RawMessage{'{', '}'},
Data: json.RawMessage("{}"),
},
}
testMessages(t, "message", valid_messages, invalid_messages)
@ -255,6 +317,7 @@ func TestMessageClientMessage(t *testing.T) {
}
func TestByeClientMessage(t *testing.T) {
t.Parallel()
// Any "bye" message is valid.
valid_messages := []testCheckValid{
&ByeClientMessage{},
@ -273,6 +336,7 @@ func TestByeClientMessage(t *testing.T) {
}
func TestRoomClientMessage(t *testing.T) {
t.Parallel()
// Any "room" message is valid.
valid_messages := []testCheckValid{
&RoomClientMessage{},
@ -291,6 +355,7 @@ func TestRoomClientMessage(t *testing.T) {
}
func TestErrorMessages(t *testing.T) {
t.Parallel()
id := "request-id"
msg := ClientMessage{
Id: id,
@ -323,12 +388,13 @@ func TestErrorMessages(t *testing.T) {
}
func TestIsChatRefresh(t *testing.T) {
t.Parallel()
var msg ServerMessage
data_true := []byte("{\"type\":\"chat\",\"chat\":{\"refresh\":true}}")
msg = ServerMessage{
Type: "message",
Message: &MessageServerMessage{
Data: (*json.RawMessage)(&data_true),
Data: data_true,
},
}
if !msg.IsChatRefresh() {
@ -339,10 +405,50 @@ func TestIsChatRefresh(t *testing.T) {
msg = ServerMessage{
Type: "message",
Message: &MessageServerMessage{
Data: (*json.RawMessage)(&data_false),
Data: data_false,
},
}
if msg.IsChatRefresh() {
t.Error("message should not be detected as chat refresh")
}
}
func assertEqualStrings(t *testing.T, expected, result []string) {
t.Helper()
if expected == nil {
expected = make([]string, 0)
} else {
sort.Strings(expected)
}
if result == nil {
result = make([]string, 0)
} else {
sort.Strings(result)
}
if !reflect.DeepEqual(expected, result) {
t.Errorf("Expected %+v, got %+v", expected, result)
}
}
func Test_Welcome_AddRemoveFeature(t *testing.T) {
t.Parallel()
var msg WelcomeServerMessage
assertEqualStrings(t, []string{}, msg.Features)
msg.AddFeature("one", "two", "one")
assertEqualStrings(t, []string{"one", "two"}, msg.Features)
if !sort.StringsAreSorted(msg.Features) {
t.Errorf("features should be sorted, got %+v", msg.Features)
}
msg.AddFeature("three")
assertEqualStrings(t, []string{"one", "two", "three"}, msg.Features)
if !sort.StringsAreSorted(msg.Features) {
t.Errorf("features should be sorted, got %+v", msg.Features)
}
msg.RemoveFeature("three", "one")
assertEqualStrings(t, []string{"two"}, msg.Features)
}

210
async_events.go Normal file
View file

@ -0,0 +1,210 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import "sync"
type AsyncBackendRoomEventListener interface {
ProcessBackendRoomRequest(message *AsyncMessage)
}
type AsyncRoomEventListener interface {
ProcessAsyncRoomMessage(message *AsyncMessage)
}
type AsyncUserEventListener interface {
ProcessAsyncUserMessage(message *AsyncMessage)
}
type AsyncSessionEventListener interface {
ProcessAsyncSessionMessage(message *AsyncMessage)
}
type AsyncEvents interface {
Close()
RegisterBackendRoomListener(roomId string, backend *Backend, listener AsyncBackendRoomEventListener) error
UnregisterBackendRoomListener(roomId string, backend *Backend, listener AsyncBackendRoomEventListener)
RegisterRoomListener(roomId string, backend *Backend, listener AsyncRoomEventListener) error
UnregisterRoomListener(roomId string, backend *Backend, listener AsyncRoomEventListener)
RegisterUserListener(userId string, backend *Backend, listener AsyncUserEventListener) error
UnregisterUserListener(userId string, backend *Backend, listener AsyncUserEventListener)
RegisterSessionListener(sessionId string, backend *Backend, listener AsyncSessionEventListener) error
UnregisterSessionListener(sessionId string, backend *Backend, listener AsyncSessionEventListener)
PublishBackendRoomMessage(roomId string, backend *Backend, message *AsyncMessage) error
PublishRoomMessage(roomId string, backend *Backend, message *AsyncMessage) error
PublishUserMessage(userId string, backend *Backend, message *AsyncMessage) error
PublishSessionMessage(sessionId string, backend *Backend, message *AsyncMessage) error
}
func NewAsyncEvents(url string) (AsyncEvents, error) {
client, err := NewNatsClient(url)
if err != nil {
return nil, err
}
return NewAsyncEventsNats(client)
}
type asyncBackendRoomSubscriber struct {
mu sync.Mutex
listeners map[AsyncBackendRoomEventListener]bool
}
func (s *asyncBackendRoomSubscriber) processBackendRoomRequest(message *AsyncMessage) {
s.mu.Lock()
defer s.mu.Unlock()
for listener := range s.listeners {
s.mu.Unlock()
listener.ProcessBackendRoomRequest(message)
s.mu.Lock()
}
}
func (s *asyncBackendRoomSubscriber) addListener(listener AsyncBackendRoomEventListener) {
s.mu.Lock()
defer s.mu.Unlock()
if s.listeners == nil {
s.listeners = make(map[AsyncBackendRoomEventListener]bool)
}
s.listeners[listener] = true
}
func (s *asyncBackendRoomSubscriber) removeListener(listener AsyncBackendRoomEventListener) bool {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.listeners, listener)
return len(s.listeners) > 0
}
type asyncRoomSubscriber struct {
mu sync.Mutex
listeners map[AsyncRoomEventListener]bool
}
func (s *asyncRoomSubscriber) processAsyncRoomMessage(message *AsyncMessage) {
s.mu.Lock()
defer s.mu.Unlock()
for listener := range s.listeners {
s.mu.Unlock()
listener.ProcessAsyncRoomMessage(message)
s.mu.Lock()
}
}
func (s *asyncRoomSubscriber) addListener(listener AsyncRoomEventListener) {
s.mu.Lock()
defer s.mu.Unlock()
if s.listeners == nil {
s.listeners = make(map[AsyncRoomEventListener]bool)
}
s.listeners[listener] = true
}
func (s *asyncRoomSubscriber) removeListener(listener AsyncRoomEventListener) bool {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.listeners, listener)
return len(s.listeners) > 0
}
type asyncUserSubscriber struct {
mu sync.Mutex
listeners map[AsyncUserEventListener]bool
}
func (s *asyncUserSubscriber) processAsyncUserMessage(message *AsyncMessage) {
s.mu.Lock()
defer s.mu.Unlock()
for listener := range s.listeners {
s.mu.Unlock()
listener.ProcessAsyncUserMessage(message)
s.mu.Lock()
}
}
func (s *asyncUserSubscriber) addListener(listener AsyncUserEventListener) {
s.mu.Lock()
defer s.mu.Unlock()
if s.listeners == nil {
s.listeners = make(map[AsyncUserEventListener]bool)
}
s.listeners[listener] = true
}
func (s *asyncUserSubscriber) removeListener(listener AsyncUserEventListener) bool {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.listeners, listener)
return len(s.listeners) > 0
}
type asyncSessionSubscriber struct {
mu sync.Mutex
listeners map[AsyncSessionEventListener]bool
}
func (s *asyncSessionSubscriber) processAsyncSessionMessage(message *AsyncMessage) {
s.mu.Lock()
defer s.mu.Unlock()
for listener := range s.listeners {
s.mu.Unlock()
listener.ProcessAsyncSessionMessage(message)
s.mu.Lock()
}
}
func (s *asyncSessionSubscriber) addListener(listener AsyncSessionEventListener) {
s.mu.Lock()
defer s.mu.Unlock()
if s.listeners == nil {
s.listeners = make(map[AsyncSessionEventListener]bool)
}
s.listeners[listener] = true
}
func (s *asyncSessionSubscriber) removeListener(listener AsyncSessionEventListener) bool {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.listeners, listener)
return len(s.listeners) > 0
}

452
async_events_nats.go Normal file
View file

@ -0,0 +1,452 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"log"
"sync"
"time"
"github.com/nats-io/nats.go"
)
func GetSubjectForBackendRoomId(roomId string, backend *Backend) string {
if backend == nil || backend.IsCompat() {
return GetEncodedSubject("backend.room", roomId)
}
return GetEncodedSubject("backend.room", roomId+"|"+backend.Id())
}
func GetSubjectForRoomId(roomId string, backend *Backend) string {
if backend == nil || backend.IsCompat() {
return GetEncodedSubject("room", roomId)
}
return GetEncodedSubject("room", roomId+"|"+backend.Id())
}
func GetSubjectForUserId(userId string, backend *Backend) string {
if backend == nil || backend.IsCompat() {
return GetEncodedSubject("user", userId)
}
return GetEncodedSubject("user", userId+"|"+backend.Id())
}
func GetSubjectForSessionId(sessionId string, backend *Backend) string {
return "session." + sessionId
}
type asyncSubscriberNats struct {
key string
client NatsClient
receiver chan *nats.Msg
closeChan chan struct{}
subscription NatsSubscription
processMessage func(*nats.Msg)
}
func newAsyncSubscriberNats(key string, client NatsClient) (*asyncSubscriberNats, error) {
receiver := make(chan *nats.Msg, 64)
sub, err := client.Subscribe(key, receiver)
if err != nil {
return nil, err
}
result := &asyncSubscriberNats{
key: key,
client: client,
receiver: receiver,
closeChan: make(chan struct{}),
subscription: sub,
}
return result, nil
}
func (s *asyncSubscriberNats) run() {
defer func() {
if err := s.subscription.Unsubscribe(); err != nil {
log.Printf("Error unsubscribing %s: %s", s.key, err)
}
}()
for {
select {
case msg := <-s.receiver:
s.processMessage(msg)
for count := len(s.receiver); count > 0; count-- {
s.processMessage(<-s.receiver)
}
case <-s.closeChan:
return
}
}
}
func (s *asyncSubscriberNats) close() {
close(s.closeChan)
}
type asyncBackendRoomSubscriberNats struct {
*asyncSubscriberNats
asyncBackendRoomSubscriber
}
func newAsyncBackendRoomSubscriberNats(key string, client NatsClient) (*asyncBackendRoomSubscriberNats, error) {
sub, err := newAsyncSubscriberNats(key, client)
if err != nil {
return nil, err
}
result := &asyncBackendRoomSubscriberNats{
asyncSubscriberNats: sub,
}
result.processMessage = result.doProcessMessage
go result.run()
return result, nil
}
func (s *asyncBackendRoomSubscriberNats) doProcessMessage(msg *nats.Msg) {
var message AsyncMessage
if err := s.client.Decode(msg, &message); err != nil {
log.Printf("Could not decode NATS message %+v, %s", msg, err)
return
}
s.processBackendRoomRequest(&message)
}
type asyncRoomSubscriberNats struct {
asyncRoomSubscriber
*asyncSubscriberNats
}
func newAsyncRoomSubscriberNats(key string, client NatsClient) (*asyncRoomSubscriberNats, error) {
sub, err := newAsyncSubscriberNats(key, client)
if err != nil {
return nil, err
}
result := &asyncRoomSubscriberNats{
asyncSubscriberNats: sub,
}
result.processMessage = result.doProcessMessage
go result.run()
return result, nil
}
func (s *asyncRoomSubscriberNats) doProcessMessage(msg *nats.Msg) {
var message AsyncMessage
if err := s.client.Decode(msg, &message); err != nil {
log.Printf("Could not decode nats message %+v, %s", msg, err)
return
}
s.processAsyncRoomMessage(&message)
}
type asyncUserSubscriberNats struct {
*asyncSubscriberNats
asyncUserSubscriber
}
func newAsyncUserSubscriberNats(key string, client NatsClient) (*asyncUserSubscriberNats, error) {
sub, err := newAsyncSubscriberNats(key, client)
if err != nil {
return nil, err
}
result := &asyncUserSubscriberNats{
asyncSubscriberNats: sub,
}
result.processMessage = result.doProcessMessage
go result.run()
return result, nil
}
func (s *asyncUserSubscriberNats) doProcessMessage(msg *nats.Msg) {
var message AsyncMessage
if err := s.client.Decode(msg, &message); err != nil {
log.Printf("Could not decode nats message %+v, %s", msg, err)
return
}
s.processAsyncUserMessage(&message)
}
type asyncSessionSubscriberNats struct {
*asyncSubscriberNats
asyncSessionSubscriber
}
func newAsyncSessionSubscriberNats(key string, client NatsClient) (*asyncSessionSubscriberNats, error) {
sub, err := newAsyncSubscriberNats(key, client)
if err != nil {
return nil, err
}
result := &asyncSessionSubscriberNats{
asyncSubscriberNats: sub,
}
result.processMessage = result.doProcessMessage
go result.run()
return result, nil
}
func (s *asyncSessionSubscriberNats) doProcessMessage(msg *nats.Msg) {
var message AsyncMessage
if err := s.client.Decode(msg, &message); err != nil {
log.Printf("Could not decode nats message %+v, %s", msg, err)
return
}
s.processAsyncSessionMessage(&message)
}
type asyncEventsNats struct {
mu sync.Mutex
client NatsClient
backendRoomSubscriptions map[string]*asyncBackendRoomSubscriberNats
roomSubscriptions map[string]*asyncRoomSubscriberNats
userSubscriptions map[string]*asyncUserSubscriberNats
sessionSubscriptions map[string]*asyncSessionSubscriberNats
}
func NewAsyncEventsNats(client NatsClient) (AsyncEvents, error) {
events := &asyncEventsNats{
client: client,
backendRoomSubscriptions: make(map[string]*asyncBackendRoomSubscriberNats),
roomSubscriptions: make(map[string]*asyncRoomSubscriberNats),
userSubscriptions: make(map[string]*asyncUserSubscriberNats),
sessionSubscriptions: make(map[string]*asyncSessionSubscriberNats),
}
return events, nil
}
func (e *asyncEventsNats) Close() {
e.mu.Lock()
defer e.mu.Unlock()
var wg sync.WaitGroup
wg.Add(1)
go func(subscriptions map[string]*asyncBackendRoomSubscriberNats) {
defer wg.Done()
for _, sub := range subscriptions {
sub.close()
}
}(e.backendRoomSubscriptions)
wg.Add(1)
go func(subscriptions map[string]*asyncRoomSubscriberNats) {
defer wg.Done()
for _, sub := range subscriptions {
sub.close()
}
}(e.roomSubscriptions)
wg.Add(1)
go func(subscriptions map[string]*asyncUserSubscriberNats) {
defer wg.Done()
for _, sub := range subscriptions {
sub.close()
}
}(e.userSubscriptions)
wg.Add(1)
go func(subscriptions map[string]*asyncSessionSubscriberNats) {
defer wg.Done()
for _, sub := range subscriptions {
sub.close()
}
}(e.sessionSubscriptions)
// Can't use clear(...) here as the maps are processed asynchronously by the
// goroutines above.
e.backendRoomSubscriptions = make(map[string]*asyncBackendRoomSubscriberNats)
e.roomSubscriptions = make(map[string]*asyncRoomSubscriberNats)
e.userSubscriptions = make(map[string]*asyncUserSubscriberNats)
e.sessionSubscriptions = make(map[string]*asyncSessionSubscriberNats)
wg.Wait()
e.client.Close()
}
func (e *asyncEventsNats) RegisterBackendRoomListener(roomId string, backend *Backend, listener AsyncBackendRoomEventListener) error {
key := GetSubjectForBackendRoomId(roomId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.backendRoomSubscriptions[key]
if !found {
var err error
if sub, err = newAsyncBackendRoomSubscriberNats(key, e.client); err != nil {
return err
}
e.backendRoomSubscriptions[key] = sub
}
sub.addListener(listener)
return nil
}
func (e *asyncEventsNats) UnregisterBackendRoomListener(roomId string, backend *Backend, listener AsyncBackendRoomEventListener) {
key := GetSubjectForBackendRoomId(roomId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.backendRoomSubscriptions[key]
if !found {
return
}
if !sub.removeListener(listener) {
delete(e.backendRoomSubscriptions, key)
sub.close()
}
}
func (e *asyncEventsNats) RegisterRoomListener(roomId string, backend *Backend, listener AsyncRoomEventListener) error {
key := GetSubjectForRoomId(roomId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.roomSubscriptions[key]
if !found {
var err error
if sub, err = newAsyncRoomSubscriberNats(key, e.client); err != nil {
return err
}
e.roomSubscriptions[key] = sub
}
sub.addListener(listener)
return nil
}
func (e *asyncEventsNats) UnregisterRoomListener(roomId string, backend *Backend, listener AsyncRoomEventListener) {
key := GetSubjectForRoomId(roomId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.roomSubscriptions[key]
if !found {
return
}
if !sub.removeListener(listener) {
delete(e.roomSubscriptions, key)
sub.close()
}
}
func (e *asyncEventsNats) RegisterUserListener(roomId string, backend *Backend, listener AsyncUserEventListener) error {
key := GetSubjectForUserId(roomId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.userSubscriptions[key]
if !found {
var err error
if sub, err = newAsyncUserSubscriberNats(key, e.client); err != nil {
return err
}
e.userSubscriptions[key] = sub
}
sub.addListener(listener)
return nil
}
func (e *asyncEventsNats) UnregisterUserListener(roomId string, backend *Backend, listener AsyncUserEventListener) {
key := GetSubjectForUserId(roomId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.userSubscriptions[key]
if !found {
return
}
if !sub.removeListener(listener) {
delete(e.userSubscriptions, key)
sub.close()
}
}
func (e *asyncEventsNats) RegisterSessionListener(sessionId string, backend *Backend, listener AsyncSessionEventListener) error {
key := GetSubjectForSessionId(sessionId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.sessionSubscriptions[key]
if !found {
var err error
if sub, err = newAsyncSessionSubscriberNats(key, e.client); err != nil {
return err
}
e.sessionSubscriptions[key] = sub
}
sub.addListener(listener)
return nil
}
func (e *asyncEventsNats) UnregisterSessionListener(sessionId string, backend *Backend, listener AsyncSessionEventListener) {
key := GetSubjectForSessionId(sessionId, backend)
e.mu.Lock()
defer e.mu.Unlock()
sub, found := e.sessionSubscriptions[key]
if !found {
return
}
if !sub.removeListener(listener) {
delete(e.sessionSubscriptions, key)
sub.close()
}
}
func (e *asyncEventsNats) publish(subject string, message *AsyncMessage) error {
message.SendTime = time.Now()
return e.client.Publish(subject, message)
}
func (e *asyncEventsNats) PublishBackendRoomMessage(roomId string, backend *Backend, message *AsyncMessage) error {
subject := GetSubjectForBackendRoomId(roomId, backend)
return e.publish(subject, message)
}
func (e *asyncEventsNats) PublishRoomMessage(roomId string, backend *Backend, message *AsyncMessage) error {
subject := GetSubjectForRoomId(roomId, backend)
return e.publish(subject, message)
}
func (e *asyncEventsNats) PublishUserMessage(userId string, backend *Backend, message *AsyncMessage) error {
subject := GetSubjectForUserId(userId, backend)
return e.publish(subject, message)
}
func (e *asyncEventsNats) PublishSessionMessage(sessionId string, backend *Backend, message *AsyncMessage) error {
subject := GetSubjectForSessionId(sessionId, backend)
return e.publish(subject, message)
}

73
async_events_test.go Normal file
View file

@ -0,0 +1,73 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"strings"
"testing"
)
var (
eventBackendsForTest = []string{
"loopback",
"nats",
}
)
func getAsyncEventsForTest(t *testing.T) AsyncEvents {
var events AsyncEvents
if strings.HasSuffix(t.Name(), "/nats") {
events = getRealAsyncEventsForTest(t)
} else {
events = getLoopbackAsyncEventsForTest(t)
}
t.Cleanup(func() {
events.Close()
})
return events
}
func getRealAsyncEventsForTest(t *testing.T) AsyncEvents {
url := startLocalNatsServer(t)
events, err := NewAsyncEvents(url)
if err != nil {
t.Fatal(err)
}
return events
}
func getLoopbackAsyncEventsForTest(t *testing.T) AsyncEvents {
events, err := NewAsyncEvents(NatsLoopbackUrl)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
nats := (events.(*asyncEventsNats)).client
(nats).(*LoopbackNatsClient).waitForSubscriptionsEmpty(ctx, t)
})
return events
}

View file

@ -39,6 +39,9 @@ import (
var (
ErrNotRedirecting = errors.New("not redirecting to different host")
ErrUnsupportedContentType = errors.New("unsupported_content_type")
ErrIncompleteResponse = errors.New("incomplete OCS response")
ErrThrottledResponse = errors.New("throttled OCS response")
)
type BackendClient struct {
@ -50,8 +53,8 @@ type BackendClient struct {
capabilities *Capabilities
}
func NewBackendClient(config *goconf.ConfigFile, maxConcurrentRequestsPerHost int, version string) (*BackendClient, error) {
backends, err := NewBackendConfiguration(config)
func NewBackendClient(config *goconf.ConfigFile, maxConcurrentRequestsPerHost int, version string, etcdClient *EtcdClient) (*BackendClient, error) {
backends, err := NewBackendConfiguration(config, etcdClient)
if err != nil {
return nil, err
}
@ -80,6 +83,10 @@ func NewBackendClient(config *goconf.ConfigFile, maxConcurrentRequestsPerHost in
}, nil
}
func (b *BackendClient) Close() {
b.backends.Close()
}
func (b *BackendClient) Reload(config *goconf.ConfigFile) {
b.backends.Reload(config)
}
@ -187,11 +194,19 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ
if err := json.Unmarshal(body, &ocs); err != nil {
log.Printf("Could not decode OCS response %s from %s: %s", string(body), req.URL, err)
return err
} else if ocs.Ocs == nil || ocs.Ocs.Data == nil {
} else if ocs.Ocs == nil || len(ocs.Ocs.Data) == 0 {
log.Printf("Incomplete OCS response %s from %s", string(body), req.URL)
return fmt.Errorf("incomplete OCS response")
} else if err := json.Unmarshal(*ocs.Ocs.Data, response); err != nil {
log.Printf("Could not decode OCS response body %s from %s: %s", string(*ocs.Ocs.Data), req.URL, err)
return ErrIncompleteResponse
}
switch ocs.Ocs.Meta.StatusCode {
case http.StatusTooManyRequests:
log.Printf("Throttled OCS response %s from %s", string(body), req.URL)
return ErrThrottledResponse
}
if err := json.Unmarshal(ocs.Ocs.Data, response); err != nil {
log.Printf("Could not decode OCS response body %s from %s: %s", string(ocs.Ocs.Data), req.URL, err)
return err
}
} else if err := json.Unmarshal(body, response); err != nil {

View file

@ -30,6 +30,7 @@ import (
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
"github.com/dlintw/goconf"
@ -44,9 +45,17 @@ func returnOCS(t *testing.T, w http.ResponseWriter, body []byte) {
StatusCode: http.StatusOK,
Message: "OK",
},
Data: (*json.RawMessage)(&body),
Data: body,
},
}
if strings.Contains(t.Name(), "Throttled") {
response.Ocs.Meta = OcsMeta{
Status: "failure",
StatusCode: 429,
Message: "Reached maximum delay",
}
}
data, err := json.Marshal(response)
if err != nil {
t.Fatal(err)
@ -61,6 +70,8 @@ func returnOCS(t *testing.T, w http.ResponseWriter, body []byte) {
}
func TestPostOnRedirect(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
r := mux.NewRouter()
r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/ocs/v2.php/two", http.StatusTemporaryRedirect)
@ -95,7 +106,7 @@ func TestPostOnRedirect(t *testing.T) {
if u.Scheme == "http" {
config.AddOption("backend", "allowhttp", "true")
}
client, err := NewBackendClient(config, 1, "0.0")
client, err := NewBackendClient(config, 1, "0.0", nil)
if err != nil {
t.Fatal(err)
}
@ -116,6 +127,8 @@ func TestPostOnRedirect(t *testing.T) {
}
func TestPostOnRedirectDifferentHost(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
r := mux.NewRouter()
r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "http://domain.invalid/ocs/v2.php/two", http.StatusTemporaryRedirect)
@ -134,7 +147,7 @@ func TestPostOnRedirectDifferentHost(t *testing.T) {
if u.Scheme == "http" {
config.AddOption("backend", "allowhttp", "true")
}
client, err := NewBackendClient(config, 1, "0.0")
client, err := NewBackendClient(config, 1, "0.0", nil)
if err != nil {
t.Fatal(err)
}
@ -156,6 +169,8 @@ func TestPostOnRedirectDifferentHost(t *testing.T) {
}
func TestPostOnRedirectStatusFound(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
r := mux.NewRouter()
r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/ocs/v2.php/two", http.StatusFound)
@ -187,7 +202,7 @@ func TestPostOnRedirectStatusFound(t *testing.T) {
if u.Scheme == "http" {
config.AddOption("backend", "allowhttp", "true")
}
client, err := NewBackendClient(config, 1, "0.0")
client, err := NewBackendClient(config, 1, "0.0", nil)
if err != nil {
t.Fatal(err)
}
@ -206,3 +221,42 @@ func TestPostOnRedirectStatusFound(t *testing.T) {
t.Errorf("Expected empty response, got %+v", response)
}
}
func TestHandleThrottled(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
r := mux.NewRouter()
r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) {
returnOCS(t, w, []byte("[]"))
})
server := httptest.NewServer(r)
defer server.Close()
u, err := url.Parse(server.URL + "/ocs/v2.php/one")
if err != nil {
t.Fatal(err)
}
config := goconf.NewConfigFile()
config.AddOption("backend", "allowed", u.Host)
config.AddOption("backend", "secret", string(testBackendSecret))
if u.Scheme == "http" {
config.AddOption("backend", "allowhttp", "true")
}
client, err := NewBackendClient(config, 1, "0.0", nil)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
request := map[string]string{
"foo": "bar",
}
var response map[string]string
err = client.PerformJSONRequest(ctx, u, request, &response)
if err == nil {
t.Error("should have triggered an error")
} else if !errors.Is(err, ErrThrottledResponse) {
t.Error(err)
}
}

View file

@ -22,24 +22,31 @@
package signaling
import (
"log"
"fmt"
"net/url"
"reflect"
"strings"
"sync"
"github.com/dlintw/goconf"
)
const (
BackendTypeStatic = "static"
BackendTypeEtcd = "etcd"
DefaultBackendType = BackendTypeStatic
)
var (
SessionLimitExceeded = NewError("session_limit_exceeded", "Too many sessions connected for this backend.")
)
type Backend struct {
id string
url string
secret []byte
compat bool
id string
url string
parsedUrl *url.URL
secret []byte
compat bool
allowHttp bool
@ -74,6 +81,24 @@ func (b *Backend) IsUrlAllowed(u *url.URL) bool {
}
}
func (b *Backend) Url() string {
return b.url
}
func (b *Backend) ParsedUrl() *url.URL {
return b.parsedUrl
}
func (b *Backend) Limit() int {
return int(b.sessionLimit)
}
func (b *Backend) Len() int {
b.sessionsLock.Lock()
defer b.sessionsLock.Unlock()
return len(b.sessions)
}
func (b *Backend) AddSession(session Session) error {
if session.ClientType() == HelloClientTypeInternal || session.ClientType() == HelloClientTypeVirtual {
// Internal and virtual sessions are not counting to the limit.
@ -105,271 +130,43 @@ func (b *Backend) RemoveSession(session Session) {
delete(b.sessions, session.PublicId())
}
type BackendConfiguration struct {
type BackendStorage interface {
Close()
Reload(config *goconf.ConfigFile)
GetCompatBackend() *Backend
GetBackend(u *url.URL) *Backend
GetBackends() []*Backend
}
type backendStorageCommon struct {
mu sync.RWMutex
backends map[string][]*Backend
// Deprecated
allowAll bool
commonSecret []byte
compatBackend *Backend
}
func NewBackendConfiguration(config *goconf.ConfigFile) (*BackendConfiguration, error) {
allowAll, _ := config.GetBool("backend", "allowall")
allowHttp, _ := config.GetBool("backend", "allowhttp")
commonSecret, _ := config.GetString("backend", "secret")
sessionLimit, err := config.GetInt("backend", "sessionlimit")
if err != nil || sessionLimit < 0 {
sessionLimit = 0
func (s *backendStorageCommon) GetBackends() []*Backend {
s.mu.RLock()
defer s.mu.RUnlock()
var result []*Backend
for _, entries := range s.backends {
result = append(result, entries...)
}
backends := make(map[string][]*Backend)
var compatBackend *Backend
numBackends := 0
if allowAll {
log.Println("WARNING: All backend hostnames are allowed, only use for development!")
compatBackend = &Backend{
id: "compat",
secret: []byte(commonSecret),
compat: true,
allowHttp: allowHttp,
sessionLimit: uint64(sessionLimit),
}
if sessionLimit > 0 {
log.Printf("Allow a maximum of %d sessions", sessionLimit)
}
numBackends++
} else if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" {
for host, configuredBackends := range getConfiguredHosts(backendIds, config) {
backends[host] = append(backends[host], configuredBackends...)
for _, be := range configuredBackends {
log.Printf("Backend %s added for %s", be.id, be.url)
}
numBackends += len(configuredBackends)
}
} else if allowedUrls, _ := config.GetString("backend", "allowed"); allowedUrls != "" {
// Old-style configuration, only hosts are configured and are using a common secret.
allowMap := make(map[string]bool)
for _, u := range strings.Split(allowedUrls, ",") {
u = strings.TrimSpace(u)
if idx := strings.IndexByte(u, '/'); idx != -1 {
log.Printf("WARNING: Removing path from allowed hostname \"%s\", check your configuration!", u)
u = u[:idx]
}
if u != "" {
allowMap[strings.ToLower(u)] = true
}
}
if len(allowMap) == 0 {
log.Println("WARNING: No backend hostnames are allowed, check your configuration!")
} else {
compatBackend = &Backend{
id: "compat",
secret: []byte(commonSecret),
compat: true,
allowHttp: allowHttp,
sessionLimit: uint64(sessionLimit),
}
hosts := make([]string, 0, len(allowMap))
for host := range allowMap {
hosts = append(hosts, host)
backends[host] = []*Backend{compatBackend}
}
if len(hosts) > 1 {
log.Println("WARNING: Using deprecated backend configuration. Please migrate the \"allowed\" setting to the new \"backends\" configuration.")
}
log.Printf("Allowed backend hostnames: %s", hosts)
if sessionLimit > 0 {
log.Printf("Allow a maximum of %d sessions", sessionLimit)
}
numBackends++
}
}
RegisterBackendConfigurationStats()
statsBackendsCurrent.Add(float64(numBackends))
return &BackendConfiguration{
backends: backends,
allowAll: allowAll,
commonSecret: []byte(commonSecret),
compatBackend: compatBackend,
}, nil
return result
}
func (b *BackendConfiguration) RemoveBackendsForHost(host string) {
if oldBackends := b.backends[host]; len(oldBackends) > 0 {
for _, backend := range oldBackends {
log.Printf("Backend %s removed for %s", backend.id, backend.url)
}
statsBackendsCurrent.Sub(float64(len(oldBackends)))
}
delete(b.backends, host)
}
func (s *backendStorageCommon) getBackendLocked(u *url.URL) *Backend {
s.mu.RLock()
defer s.mu.RUnlock()
func (b *BackendConfiguration) UpsertHost(host string, backends []*Backend) {
for existingIndex, existingBackend := range b.backends[host] {
found := false
index := 0
for _, newBackend := range backends {
if reflect.DeepEqual(existingBackend, newBackend) { // otherwise we could manually compare the struct members here
found = true
backends = append(backends[:index], backends[index+1:]...)
break
} else if newBackend.id == existingBackend.id {
found = true
b.backends[host][existingIndex] = newBackend
backends = append(backends[:index], backends[index+1:]...)
log.Printf("Backend %s updated for %s", newBackend.id, newBackend.url)
break
}
index++
}
if !found {
removed := b.backends[host][existingIndex]
log.Printf("Backend %s removed for %s", removed.id, removed.url)
b.backends[host] = append(b.backends[host][:existingIndex], b.backends[host][existingIndex+1:]...)
statsBackendsCurrent.Dec()
}
}
b.backends[host] = append(b.backends[host], backends...)
for _, added := range backends {
log.Printf("Backend %s added for %s", added.id, added.url)
}
statsBackendsCurrent.Add(float64(len(backends)))
}
func getConfiguredBackendIDs(backendIds string) (ids []string) {
seen := make(map[string]bool)
for _, id := range strings.Split(backendIds, ",") {
id = strings.TrimSpace(id)
if id == "" {
continue
}
if seen[id] {
continue
}
ids = append(ids, id)
seen[id] = true
}
return ids
}
func getConfiguredHosts(backendIds string, config *goconf.ConfigFile) (hosts map[string][]*Backend) {
hosts = make(map[string][]*Backend)
for _, id := range getConfiguredBackendIDs(backendIds) {
u, _ := config.GetString(id, "url")
if u == "" {
log.Printf("Backend %s is missing or incomplete, skipping", id)
continue
}
if u[len(u)-1] != '/' {
u += "/"
}
parsed, err := url.Parse(u)
if err != nil {
log.Printf("Backend %s has an invalid url %s configured (%s), skipping", id, u, err)
continue
}
if strings.Contains(parsed.Host, ":") && hasStandardPort(parsed) {
parsed.Host = parsed.Hostname()
u = parsed.String()
}
secret, _ := config.GetString(id, "secret")
if u == "" || secret == "" {
log.Printf("Backend %s is missing or incomplete, skipping", id)
continue
}
sessionLimit, err := config.GetInt(id, "sessionlimit")
if err != nil || sessionLimit < 0 {
sessionLimit = 0
}
if sessionLimit > 0 {
log.Printf("Backend %s allows a maximum of %d sessions", id, sessionLimit)
}
maxStreamBitrate, err := config.GetInt(id, "maxstreambitrate")
if err != nil || maxStreamBitrate < 0 {
maxStreamBitrate = 0
}
maxScreenBitrate, err := config.GetInt(id, "maxscreenbitrate")
if err != nil || maxScreenBitrate < 0 {
maxScreenBitrate = 0
}
hosts[parsed.Host] = append(hosts[parsed.Host], &Backend{
id: id,
url: u,
secret: []byte(secret),
allowHttp: parsed.Scheme == "http",
maxStreamBitrate: maxStreamBitrate,
maxScreenBitrate: maxScreenBitrate,
sessionLimit: uint64(sessionLimit),
})
}
return hosts
}
func (b *BackendConfiguration) Reload(config *goconf.ConfigFile) {
if b.compatBackend != nil {
log.Println("Old-style configuration active, reload is not supported")
return
}
if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" {
configuredHosts := getConfiguredHosts(backendIds, config)
// remove backends that are no longer configured
for hostname := range b.backends {
if _, ok := configuredHosts[hostname]; !ok {
b.RemoveBackendsForHost(hostname)
}
}
// rewrite backends adding newly configured ones and rewriting existing ones
for hostname, configuredBackends := range configuredHosts {
b.UpsertHost(hostname, configuredBackends)
}
}
}
func (b *BackendConfiguration) GetCompatBackend() *Backend {
return b.compatBackend
}
func (b *BackendConfiguration) GetBackend(u *url.URL) *Backend {
if strings.Contains(u.Host, ":") && hasStandardPort(u) {
u.Host = u.Hostname()
}
entries, found := b.backends[u.Host]
entries, found := s.backends[u.Host]
if !found {
if b.allowAll {
return b.compatBackend
}
return nil
}
s := u.String()
if s[len(s)-1] != '/' {
s += "/"
url := u.String()
if url[len(url)-1] != '/' {
url += "/"
}
for _, entry := range entries {
if !entry.IsUrlAllowed(u) {
@ -379,7 +176,7 @@ func (b *BackendConfiguration) GetBackend(u *url.URL) *Backend {
if entry.url == "" {
// Old-style configuration, only hosts are configured.
return entry
} else if strings.HasPrefix(s, entry.url) {
} else if strings.HasPrefix(url, entry.url) {
return entry
}
}
@ -387,12 +184,59 @@ func (b *BackendConfiguration) GetBackend(u *url.URL) *Backend {
return nil
}
func (b *BackendConfiguration) GetBackends() []*Backend {
var result []*Backend
for _, entries := range b.backends {
result = append(result, entries...)
type BackendConfiguration struct {
storage BackendStorage
}
func NewBackendConfiguration(config *goconf.ConfigFile, etcdClient *EtcdClient) (*BackendConfiguration, error) {
backendType, _ := config.GetString("backend", "backendtype")
if backendType == "" {
backendType = DefaultBackendType
}
return result
RegisterBackendConfigurationStats()
var storage BackendStorage
var err error
switch backendType {
case BackendTypeStatic:
storage, err = NewBackendStorageStatic(config)
case BackendTypeEtcd:
storage, err = NewBackendStorageEtcd(config, etcdClient)
default:
err = fmt.Errorf("unknown backend type: %s", backendType)
}
if err != nil {
return nil, err
}
return &BackendConfiguration{
storage: storage,
}, nil
}
func (b *BackendConfiguration) Close() {
b.storage.Close()
}
func (b *BackendConfiguration) Reload(config *goconf.ConfigFile) {
b.storage.Reload(config)
}
func (b *BackendConfiguration) GetCompatBackend() *Backend {
return b.storage.GetCompatBackend()
}
func (b *BackendConfiguration) GetBackend(u *url.URL) *Backend {
if strings.Contains(u.Host, ":") && hasStandardPort(u) {
u.Host = u.Hostname()
}
return b.storage.GetBackend(u)
}
func (b *BackendConfiguration) GetBackends() []*Backend {
return b.storage.GetBackends()
}
func (b *BackendConfiguration) IsUrlAllowed(u *url.URL) bool {
@ -416,5 +260,5 @@ func (b *BackendConfiguration) GetSecret(u *url.URL) []byte {
return nil
}
return entry.secret
return entry.Secret()
}

View file

@ -23,8 +23,10 @@ package signaling
import (
"bytes"
"context"
"net/url"
"reflect"
"sort"
"testing"
"github.com/dlintw/goconf"
@ -90,6 +92,7 @@ func testBackends(t *testing.T, config *BackendConfiguration, valid_urls [][]str
}
func TestIsUrlAllowed_Compat(t *testing.T) {
CatchLogForTest(t)
// Old-style configuration
valid_urls := []string{
"http://domain.invalid",
@ -104,7 +107,7 @@ func TestIsUrlAllowed_Compat(t *testing.T) {
config.AddOption("backend", "allowed", "domain.invalid")
config.AddOption("backend", "allowhttp", "true")
config.AddOption("backend", "secret", string(testBackendSecret))
cfg, err := NewBackendConfiguration(config)
cfg, err := NewBackendConfiguration(config, nil)
if err != nil {
t.Fatal(err)
}
@ -112,6 +115,7 @@ func TestIsUrlAllowed_Compat(t *testing.T) {
}
func TestIsUrlAllowed_CompatForceHttps(t *testing.T) {
CatchLogForTest(t)
// Old-style configuration, force HTTPS
valid_urls := []string{
"https://domain.invalid",
@ -125,7 +129,7 @@ func TestIsUrlAllowed_CompatForceHttps(t *testing.T) {
config := goconf.NewConfigFile()
config.AddOption("backend", "allowed", "domain.invalid")
config.AddOption("backend", "secret", string(testBackendSecret))
cfg, err := NewBackendConfiguration(config)
cfg, err := NewBackendConfiguration(config, nil)
if err != nil {
t.Fatal(err)
}
@ -133,6 +137,7 @@ func TestIsUrlAllowed_CompatForceHttps(t *testing.T) {
}
func TestIsUrlAllowed(t *testing.T) {
CatchLogForTest(t)
valid_urls := [][]string{
{"https://domain.invalid/foo", string(testBackendSecret) + "-foo"},
{"https://domain.invalid/foo/", string(testBackendSecret) + "-foo"},
@ -170,7 +175,7 @@ func TestIsUrlAllowed(t *testing.T) {
config.AddOption("baz", "secret", string(testBackendSecret)+"-baz")
config.AddOption("lala", "url", "https://otherdomain.invalid/")
config.AddOption("lala", "secret", string(testBackendSecret)+"-lala")
cfg, err := NewBackendConfiguration(config)
cfg, err := NewBackendConfiguration(config, nil)
if err != nil {
t.Fatal(err)
}
@ -178,6 +183,7 @@ func TestIsUrlAllowed(t *testing.T) {
}
func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) {
CatchLogForTest(t)
valid_urls := []string{}
invalid_urls := []string{
"http://domain.invalid",
@ -187,7 +193,7 @@ func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) {
config := goconf.NewConfigFile()
config.AddOption("backend", "allowed", "")
config.AddOption("backend", "secret", string(testBackendSecret))
cfg, err := NewBackendConfiguration(config)
cfg, err := NewBackendConfiguration(config, nil)
if err != nil {
t.Fatal(err)
}
@ -195,6 +201,7 @@ func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) {
}
func TestIsUrlAllowed_AllowAll(t *testing.T) {
CatchLogForTest(t)
valid_urls := []string{
"http://domain.invalid",
"https://domain.invalid",
@ -207,7 +214,7 @@ func TestIsUrlAllowed_AllowAll(t *testing.T) {
config.AddOption("backend", "allowall", "true")
config.AddOption("backend", "allowed", "")
config.AddOption("backend", "secret", string(testBackendSecret))
cfg, err := NewBackendConfiguration(config)
cfg, err := NewBackendConfiguration(config, nil)
if err != nil {
t.Fatal(err)
}
@ -220,6 +227,7 @@ type ParseBackendIdsTestcase struct {
}
func TestParseBackendIds(t *testing.T) {
CatchLogForTest(t)
testcases := []ParseBackendIdsTestcase{
{"", nil},
{"backend1", []string{"backend1"}},
@ -239,6 +247,7 @@ func TestParseBackendIds(t *testing.T) {
}
func TestBackendReloadNoChange(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2")
@ -247,7 +256,7 @@ func TestBackendReloadNoChange(t *testing.T) {
original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
original_config.AddOption("backend2", "url", "http://domain2.invalid")
original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
o_cfg, err := NewBackendConfiguration(original_config)
o_cfg, err := NewBackendConfiguration(original_config, nil)
if err != nil {
t.Fatal(err)
}
@ -260,7 +269,7 @@ func TestBackendReloadNoChange(t *testing.T) {
new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
new_config.AddOption("backend2", "url", "http://domain2.invalid")
new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
n_cfg, err := NewBackendConfiguration(new_config)
n_cfg, err := NewBackendConfiguration(new_config, nil)
if err != nil {
t.Fatal(err)
}
@ -274,6 +283,7 @@ func TestBackendReloadNoChange(t *testing.T) {
}
func TestBackendReloadChangeExistingURL(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2")
@ -282,7 +292,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) {
original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
original_config.AddOption("backend2", "url", "http://domain2.invalid")
original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
o_cfg, err := NewBackendConfiguration(original_config)
o_cfg, err := NewBackendConfiguration(original_config, nil)
if err != nil {
t.Fatal(err)
}
@ -296,7 +306,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) {
new_config.AddOption("backend1", "sessionlimit", "10")
new_config.AddOption("backend2", "url", "http://domain2.invalid")
new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
n_cfg, err := NewBackendConfiguration(new_config)
n_cfg, err := NewBackendConfiguration(new_config, nil)
if err != nil {
t.Fatal(err)
}
@ -314,6 +324,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) {
}
func TestBackendReloadChangeSecret(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2")
@ -322,7 +333,7 @@ func TestBackendReloadChangeSecret(t *testing.T) {
original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
original_config.AddOption("backend2", "url", "http://domain2.invalid")
original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
o_cfg, err := NewBackendConfiguration(original_config)
o_cfg, err := NewBackendConfiguration(original_config, nil)
if err != nil {
t.Fatal(err)
}
@ -335,7 +346,7 @@ func TestBackendReloadChangeSecret(t *testing.T) {
new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend3")
new_config.AddOption("backend2", "url", "http://domain2.invalid")
new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
n_cfg, err := NewBackendConfiguration(new_config)
n_cfg, err := NewBackendConfiguration(new_config, nil)
if err != nil {
t.Fatal(err)
}
@ -352,13 +363,14 @@ func TestBackendReloadChangeSecret(t *testing.T) {
}
func TestBackendReloadAddBackend(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1")
original_config.AddOption("backend", "allowall", "false")
original_config.AddOption("backend1", "url", "http://domain1.invalid")
original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
o_cfg, err := NewBackendConfiguration(original_config)
o_cfg, err := NewBackendConfiguration(original_config, nil)
if err != nil {
t.Fatal(err)
}
@ -372,7 +384,7 @@ func TestBackendReloadAddBackend(t *testing.T) {
new_config.AddOption("backend2", "url", "http://domain2.invalid")
new_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
new_config.AddOption("backend2", "sessionlimit", "10")
n_cfg, err := NewBackendConfiguration(new_config)
n_cfg, err := NewBackendConfiguration(new_config, nil)
if err != nil {
t.Fatal(err)
}
@ -392,6 +404,7 @@ func TestBackendReloadAddBackend(t *testing.T) {
}
func TestBackendReloadRemoveHost(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2")
@ -400,7 +413,7 @@ func TestBackendReloadRemoveHost(t *testing.T) {
original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
original_config.AddOption("backend2", "url", "http://domain2.invalid")
original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
o_cfg, err := NewBackendConfiguration(original_config)
o_cfg, err := NewBackendConfiguration(original_config, nil)
if err != nil {
t.Fatal(err)
}
@ -411,7 +424,7 @@ func TestBackendReloadRemoveHost(t *testing.T) {
new_config.AddOption("backend", "allowall", "false")
new_config.AddOption("backend1", "url", "http://domain1.invalid")
new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
n_cfg, err := NewBackendConfiguration(new_config)
n_cfg, err := NewBackendConfiguration(new_config, nil)
if err != nil {
t.Fatal(err)
}
@ -429,6 +442,7 @@ func TestBackendReloadRemoveHost(t *testing.T) {
}
func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2")
@ -437,7 +451,7 @@ func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
original_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
original_config.AddOption("backend2", "url", "http://domain1.invalid/bar/")
original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
o_cfg, err := NewBackendConfiguration(original_config)
o_cfg, err := NewBackendConfiguration(original_config, nil)
if err != nil {
t.Fatal(err)
}
@ -448,7 +462,7 @@ func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
new_config.AddOption("backend", "allowall", "false")
new_config.AddOption("backend1", "url", "http://domain1.invalid/foo/")
new_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
n_cfg, err := NewBackendConfiguration(new_config)
n_cfg, err := NewBackendConfiguration(new_config, nil)
if err != nil {
t.Fatal(err)
}
@ -464,3 +478,209 @@ func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
t.Error("BackendConfiguration should be equal after Reload")
}
}
func sortBackends(backends []*Backend) []*Backend {
result := make([]*Backend, len(backends))
copy(result, backends)
sort.Slice(result, func(i, j int) bool {
return result[i].Id() < result[j].Id()
})
return result
}
func mustParse(s string) *url.URL {
p, err := url.Parse(s)
if err != nil {
panic(err)
}
return p
}
func TestBackendConfiguration_Etcd(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t)
url1 := "https://domain1.invalid/foo"
initialSecret1 := string(testBackendSecret) + "-backend1-initial"
secret1 := string(testBackendSecret) + "-backend1"
SetEtcdValue(etcd, "/backends/1_one", []byte("{\"url\":\""+url1+"\",\"secret\":\""+initialSecret1+"\"}"))
config := goconf.NewConfigFile()
config.AddOption("backend", "backendtype", "etcd")
config.AddOption("backend", "backendprefix", "/backends")
cfg, err := NewBackendConfiguration(config, client)
if err != nil {
t.Fatal(err)
}
defer cfg.Close()
storage := cfg.storage.(*backendStorageEtcd)
ch := storage.getWakeupChannelForTesting()
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
if err := storage.WaitForInitialized(ctx); err != nil {
t.Fatal(err)
}
if backends := sortBackends(cfg.GetBackends()); len(backends) != 1 {
t.Errorf("Expected one backend, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if string(backends[0].secret) != initialSecret1 {
t.Errorf("Expected backend secret %s, got %s", initialSecret1, string(backends[0].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
t.Errorf("Expected backend %+v, got %+v", backends[0], backend)
}
drainWakeupChannel(ch)
SetEtcdValue(etcd, "/backends/1_one", []byte("{\"url\":\""+url1+"\",\"secret\":\""+secret1+"\"}"))
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 1 {
t.Errorf("Expected one backend, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if string(backends[0].secret) != secret1 {
t.Errorf("Expected backend secret %s, got %s", secret1, string(backends[0].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
t.Errorf("Expected backend %+v, got %+v", backends[0], backend)
}
url2 := "https://domain1.invalid/bar"
secret2 := string(testBackendSecret) + "-backend2"
drainWakeupChannel(ch)
SetEtcdValue(etcd, "/backends/2_two", []byte("{\"url\":\""+url2+"\",\"secret\":\""+secret2+"\"}"))
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 2 {
t.Errorf("Expected two backends, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if string(backends[0].secret) != secret1 {
t.Errorf("Expected backend secret %s, got %s", secret1, string(backends[0].secret))
} else if backends[1].url != url2 {
t.Errorf("Expected backend url %s, got %s", url2, backends[1].url)
} else if string(backends[1].secret) != secret2 {
t.Errorf("Expected backend secret %s, got %s", secret2, string(backends[1].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
t.Errorf("Expected backend %+v, got %+v", backends[0], backend)
} else if backend := cfg.GetBackend(mustParse(url2)); backend != backends[1] {
t.Errorf("Expected backend %+v, got %+v", backends[1], backend)
}
url3 := "https://domain2.invalid/foo"
secret3 := string(testBackendSecret) + "-backend3"
drainWakeupChannel(ch)
SetEtcdValue(etcd, "/backends/3_three", []byte("{\"url\":\""+url3+"\",\"secret\":\""+secret3+"\"}"))
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 3 {
t.Errorf("Expected three backends, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if string(backends[0].secret) != secret1 {
t.Errorf("Expected backend secret %s, got %s", secret1, string(backends[0].secret))
} else if backends[1].url != url2 {
t.Errorf("Expected backend url %s, got %s", url2, backends[1].url)
} else if string(backends[1].secret) != secret2 {
t.Errorf("Expected backend secret %s, got %s", secret2, string(backends[1].secret))
} else if backends[2].url != url3 {
t.Errorf("Expected backend url %s, got %s", url3, backends[2].url)
} else if string(backends[2].secret) != secret3 {
t.Errorf("Expected backend secret %s, got %s", secret3, string(backends[2].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
t.Errorf("Expected backend %+v, got %+v", backends[0], backend)
} else if backend := cfg.GetBackend(mustParse(url2)); backend != backends[1] {
t.Errorf("Expected backend %+v, got %+v", backends[1], backend)
} else if backend := cfg.GetBackend(mustParse(url3)); backend != backends[2] {
t.Errorf("Expected backend %+v, got %+v", backends[2], backend)
}
drainWakeupChannel(ch)
DeleteEtcdValue(etcd, "/backends/1_one")
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 2 {
t.Errorf("Expected two backends, got %+v", backends)
} else if backends[0].url != url2 {
t.Errorf("Expected backend url %s, got %s", url2, backends[0].url)
} else if string(backends[0].secret) != secret2 {
t.Errorf("Expected backend secret %s, got %s", secret2, string(backends[0].secret))
} else if backends[1].url != url3 {
t.Errorf("Expected backend url %s, got %s", url3, backends[1].url)
} else if string(backends[1].secret) != secret3 {
t.Errorf("Expected backend secret %s, got %s", secret3, string(backends[1].secret))
}
drainWakeupChannel(ch)
DeleteEtcdValue(etcd, "/backends/2_two")
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 1 {
t.Errorf("Expected one backend, got %+v", backends)
} else if backends[0].url != url3 {
t.Errorf("Expected backend url %s, got %s", url3, backends[0].url)
} else if string(backends[0].secret) != secret3 {
t.Errorf("Expected backend secret %s, got %s", secret3, string(backends[0].secret))
}
if _, found := storage.backends["domain1.invalid"]; found {
t.Errorf("Should have removed host information for %s", "domain1.invalid")
}
}
func TestBackendCommonSecret(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
u1, err := url.Parse("http://domain1.invalid")
if err != nil {
t.Fatal(err)
}
u2, err := url.Parse("http://domain2.invalid")
if err != nil {
t.Fatal(err)
}
original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2")
original_config.AddOption("backend", "secret", string(testBackendSecret))
original_config.AddOption("backend1", "url", u1.String())
original_config.AddOption("backend2", "url", u2.String())
original_config.AddOption("backend2", "secret", string(testBackendSecret)+"-backend2")
cfg, err := NewBackendConfiguration(original_config, nil)
if err != nil {
t.Fatal(err)
}
if b1 := cfg.GetBackend(u1); b1 == nil {
t.Error("didn't get backend")
} else if !bytes.Equal(b1.Secret(), testBackendSecret) {
t.Errorf("expected secret %s, got %s", string(testBackendSecret), string(b1.Secret()))
}
if b2 := cfg.GetBackend(u2); b2 == nil {
t.Error("didn't get backend")
} else if !bytes.Equal(b2.Secret(), []byte(string(testBackendSecret)+"-backend2")) {
t.Errorf("expected secret %s, got %s", string(testBackendSecret)+"-backend2", string(b2.Secret()))
}
updated_config := goconf.NewConfigFile()
updated_config.AddOption("backend", "backends", "backend1, backend2")
updated_config.AddOption("backend", "secret", string(testBackendSecret))
updated_config.AddOption("backend1", "url", u1.String())
updated_config.AddOption("backend1", "secret", string(testBackendSecret)+"-backend1")
updated_config.AddOption("backend2", "url", u2.String())
cfg.Reload(updated_config)
if b1 := cfg.GetBackend(u1); b1 == nil {
t.Error("didn't get backend")
} else if !bytes.Equal(b1.Secret(), []byte(string(testBackendSecret)+"-backend1")) {
t.Errorf("expected secret %s, got %s", string(testBackendSecret)+"-backend1", string(b1.Secret()))
}
if b2 := cfg.GetBackend(u2); b2 == nil {
t.Error("didn't get backend")
} else if !bytes.Equal(b2.Secret(), testBackendSecret) {
t.Errorf("expected secret %s, got %s", string(testBackendSecret), string(b2.Secret()))
}
}

View file

@ -22,11 +22,13 @@
package signaling
import (
"context"
"crypto/hmac"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"log"
@ -34,8 +36,10 @@ import (
"net/http"
"net/url"
"reflect"
"regexp"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/dlintw/goconf"
@ -53,7 +57,7 @@ const (
type BackendServer struct {
hub *Hub
nats NatsClient
events AsyncEvents
roomSessions RoomSessions
version string
@ -64,7 +68,7 @@ type BackendServer struct {
turnvalid time.Duration
turnservers []string
statsAllowedIps map[string]bool
statsAllowedIps atomic.Pointer[AllowedIps]
invalidSecret []byte
}
@ -99,21 +103,16 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac
}
statsAllowed, _ := config.GetString("stats", "allowed_ips")
var statsAllowedIps map[string]bool
if statsAllowed == "" {
log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1")
statsAllowedIps = map[string]bool{
"127.0.0.1": true,
}
statsAllowedIps, err := ParseAllowedIps(statsAllowed)
if err != nil {
return nil, err
}
if !statsAllowedIps.Empty() {
log.Printf("Only allowing access to the stats endpoint from %s", statsAllowed)
} else {
log.Printf("Only allowing access to the stats endpoing from %s", statsAllowed)
statsAllowedIps = make(map[string]bool)
for _, ip := range strings.Split(statsAllowed, ",") {
ip = strings.TrimSpace(ip)
if ip != "" {
statsAllowedIps[ip] = true
}
}
log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1")
statsAllowedIps = DefaultAllowedIps()
}
invalidSecret := make([]byte, 32)
@ -121,9 +120,9 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac
return nil, err
}
return &BackendServer{
result := &BackendServer{
hub: hub,
nats: hub.nats,
events: hub.events,
roomSessions: hub.roomSessions,
version: version,
@ -132,9 +131,27 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac
turnvalid: turnvalid,
turnservers: turnserverslist,
statsAllowedIps: statsAllowedIps,
invalidSecret: invalidSecret,
}, nil
invalidSecret: invalidSecret,
}
result.statsAllowedIps.Store(statsAllowedIps)
return result, nil
}
func (b *BackendServer) Reload(config *goconf.ConfigFile) {
statsAllowed, _ := config.GetString("stats", "allowed_ips")
if statsAllowedIps, err := ParseAllowedIps(statsAllowed); err == nil {
if !statsAllowedIps.Empty() {
log.Printf("Only allowing access to the stats endpoint from %s", statsAllowed)
} else {
log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1")
statsAllowedIps = DefaultAllowedIps()
}
b.statsAllowedIps.Store(statsAllowedIps)
} else {
log.Printf("Error parsing allowed stats ips from \"%s\": %s", statsAllowedIps, err)
}
}
func (b *BackendServer) Start(r *mux.Router) error {
@ -278,46 +295,54 @@ func (b *BackendServer) parseRequestBody(f func(http.ResponseWriter, *http.Reque
}
}
func (b *BackendServer) sendRoomInvite(roomid string, backend *Backend, userids []string, properties *json.RawMessage) {
msg := &ServerMessage{
Type: "event",
Event: &EventServerMessage{
Target: "roomlist",
Type: "invite",
Invite: &RoomEventServerMessage{
RoomId: roomid,
Properties: properties,
func (b *BackendServer) sendRoomInvite(roomid string, backend *Backend, userids []string, properties json.RawMessage) {
msg := &AsyncMessage{
Type: "message",
Message: &ServerMessage{
Type: "event",
Event: &EventServerMessage{
Target: "roomlist",
Type: "invite",
Invite: &RoomEventServerMessage{
RoomId: roomid,
Properties: properties,
},
},
},
}
for _, userid := range userids {
if err := b.nats.PublishMessage(GetSubjectForUserId(userid, backend), msg); err != nil {
if err := b.events.PublishUserMessage(userid, backend, msg); err != nil {
log.Printf("Could not publish room invite for user %s in backend %s: %s", userid, backend.Id(), err)
}
}
}
func (b *BackendServer) sendRoomDisinvite(roomid string, backend *Backend, reason string, userids []string, sessionids []string) {
msg := &ServerMessage{
Type: "event",
Event: &EventServerMessage{
Target: "roomlist",
Type: "disinvite",
Disinvite: &RoomDisinviteEventServerMessage{
RoomEventServerMessage: RoomEventServerMessage{
RoomId: roomid,
msg := &AsyncMessage{
Type: "message",
Message: &ServerMessage{
Type: "event",
Event: &EventServerMessage{
Target: "roomlist",
Type: "disinvite",
Disinvite: &RoomDisinviteEventServerMessage{
RoomEventServerMessage: RoomEventServerMessage{
RoomId: roomid,
},
Reason: reason,
},
Reason: reason,
},
},
}
for _, userid := range userids {
if err := b.nats.PublishMessage(GetSubjectForUserId(userid, backend), msg); err != nil {
if err := b.events.PublishUserMessage(userid, backend, msg); err != nil {
log.Printf("Could not publish room disinvite for user %s in backend %s: %s", userid, backend.Id(), err)
}
}
timeout := time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
var wg sync.WaitGroup
for _, sessionid := range sessionids {
if sessionid == sessionIdNotInMeeting {
@ -328,10 +353,10 @@ func (b *BackendServer) sendRoomDisinvite(roomid string, backend *Backend, reaso
wg.Add(1)
go func(sessionid string) {
defer wg.Done()
if sid, err := b.lookupByRoomSessionId(sessionid, nil, timeout); err != nil {
if sid, err := b.lookupByRoomSessionId(ctx, sessionid, nil); err != nil {
log.Printf("Could not lookup by room session %s: %s", sessionid, err)
} else if sid != "" {
if err := b.nats.PublishMessage("session."+sid, msg); err != nil {
if err := b.events.PublishSessionMessage(sid, backend, msg); err != nil {
log.Printf("Could not publish room disinvite for session %s: %s", sid, err)
}
}
@ -340,15 +365,18 @@ func (b *BackendServer) sendRoomDisinvite(roomid string, backend *Backend, reaso
wg.Wait()
}
func (b *BackendServer) sendRoomUpdate(roomid string, backend *Backend, notified_userids []string, all_userids []string, properties *json.RawMessage) {
msg := &ServerMessage{
Type: "event",
Event: &EventServerMessage{
Target: "roomlist",
Type: "update",
Update: &RoomEventServerMessage{
RoomId: roomid,
Properties: properties,
func (b *BackendServer) sendRoomUpdate(roomid string, backend *Backend, notified_userids []string, all_userids []string, properties json.RawMessage) {
msg := &AsyncMessage{
Type: "message",
Message: &ServerMessage{
Type: "event",
Event: &EventServerMessage{
Target: "roomlist",
Type: "update",
Update: &RoomEventServerMessage{
RoomId: roomid,
Properties: properties,
},
},
},
}
@ -362,13 +390,13 @@ func (b *BackendServer) sendRoomUpdate(roomid string, backend *Backend, notified
continue
}
if err := b.nats.PublishMessage(GetSubjectForUserId(userid, backend), msg); err != nil {
if err := b.events.PublishUserMessage(userid, backend, msg); err != nil {
log.Printf("Could not publish room update for user %s in backend %s: %s", userid, backend.Id(), err)
}
}
}
func (b *BackendServer) lookupByRoomSessionId(roomSessionId string, cache *ConcurrentStringStringMap, timeout time.Duration) (string, error) {
func (b *BackendServer) lookupByRoomSessionId(ctx context.Context, roomSessionId string, cache *ConcurrentStringStringMap) (string, error) {
if roomSessionId == sessionIdNotInMeeting {
log.Printf("Trying to lookup empty room session id: %s", roomSessionId)
return "", nil
@ -380,7 +408,7 @@ func (b *BackendServer) lookupByRoomSessionId(roomSessionId string, cache *Concu
}
}
sid, err := b.roomSessions.GetSessionId(roomSessionId)
sid, err := b.roomSessions.LookupSessionId(ctx, roomSessionId, "")
if err == ErrNoSuchRoomSession {
return "", nil
} else if err != nil {
@ -393,7 +421,7 @@ func (b *BackendServer) lookupByRoomSessionId(roomSessionId string, cache *Concu
return sid, nil
}
func (b *BackendServer) fixupUserSessions(cache *ConcurrentStringStringMap, users []map[string]interface{}, timeout time.Duration) []map[string]interface{} {
func (b *BackendServer) fixupUserSessions(ctx context.Context, cache *ConcurrentStringStringMap, users []map[string]interface{}) []map[string]interface{} {
if len(users) == 0 {
return users
}
@ -421,7 +449,7 @@ func (b *BackendServer) fixupUserSessions(cache *ConcurrentStringStringMap, user
wg.Add(1)
go func(roomSessionId string, u map[string]interface{}) {
defer wg.Done()
if sessionId, err := b.lookupByRoomSessionId(roomSessionId, cache, timeout); err != nil {
if sessionId, err := b.lookupByRoomSessionId(ctx, roomSessionId, cache); err != nil {
log.Printf("Could not lookup by room session %s: %s", roomSessionId, err)
delete(u, "sessionId")
} else if sessionId != "" {
@ -447,27 +475,35 @@ func (b *BackendServer) sendRoomIncall(roomid string, backend *Backend, request
if !request.InCall.All {
timeout := time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
var cache ConcurrentStringStringMap
// Convert (Nextcloud) session ids to signaling session ids.
request.InCall.Users = b.fixupUserSessions(&cache, request.InCall.Users, timeout)
request.InCall.Users = b.fixupUserSessions(ctx, &cache, request.InCall.Users)
// Entries in "Changed" are most likely already fetched through the "Users" list.
request.InCall.Changed = b.fixupUserSessions(&cache, request.InCall.Changed, timeout)
request.InCall.Changed = b.fixupUserSessions(ctx, &cache, request.InCall.Changed)
if len(request.InCall.Users) == 0 && len(request.InCall.Changed) == 0 {
return nil
}
}
return b.nats.PublishBackendServerRoomRequest(GetSubjectForBackendRoomId(roomid, backend), request)
message := &AsyncMessage{
Type: "room",
Room: request,
}
return b.events.PublishBackendRoomMessage(roomid, backend, message)
}
func (b *BackendServer) sendRoomParticipantsUpdate(roomid string, backend *Backend, request *BackendServerRoomRequest) error {
timeout := time.Second
// Convert (Nextcloud) session ids to signaling session ids.
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
var cache ConcurrentStringStringMap
request.Participants.Users = b.fixupUserSessions(&cache, request.Participants.Users, timeout)
request.Participants.Changed = b.fixupUserSessions(&cache, request.Participants.Changed, timeout)
request.Participants.Users = b.fixupUserSessions(ctx, &cache, request.Participants.Users)
request.Participants.Changed = b.fixupUserSessions(ctx, &cache, request.Participants.Changed)
if len(request.Participants.Users) == 0 && len(request.Participants.Changed) == 0 {
return nil
@ -500,25 +536,259 @@ loop:
go func(sessionId string, permissions []Permission) {
defer wg.Done()
message := &NatsMessage{
message := &AsyncMessage{
Type: "permissions",
Permissions: permissions,
}
if err := b.nats.Publish("session."+sessionId, message); err != nil {
if err := b.events.PublishSessionMessage(sessionId, backend, message); err != nil {
log.Printf("Could not send permissions update (%+v) to session %s: %s", permissions, sessionId, err)
}
}(sessionId, permissions)
}
wg.Wait()
return b.nats.PublishBackendServerRoomRequest(GetSubjectForBackendRoomId(roomid, backend), request)
message := &AsyncMessage{
Type: "room",
Room: request,
}
return b.events.PublishBackendRoomMessage(roomid, backend, message)
}
func (b *BackendServer) sendRoomMessage(roomid string, backend *Backend, request *BackendServerRoomRequest) error {
return b.nats.PublishBackendServerRoomRequest(GetSubjectForBackendRoomId(roomid, backend), request)
message := &AsyncMessage{
Type: "room",
Room: request,
}
return b.events.PublishBackendRoomMessage(roomid, backend, message)
}
func (b *BackendServer) sendRoomSwitchTo(roomid string, backend *Backend, request *BackendServerRoomRequest) error {
timeout := time.Second
// Convert (Nextcloud) session ids to signaling session ids.
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
var wg sync.WaitGroup
var mu sync.Mutex
if len(request.SwitchTo.Sessions) > 0 {
// We support both a list of sessions or a map with additional details per session.
if request.SwitchTo.Sessions[0] == '[' {
var sessionsList BackendRoomSwitchToSessionsList
if err := json.Unmarshal(request.SwitchTo.Sessions, &sessionsList); err != nil {
return err
}
if len(sessionsList) == 0 {
return nil
}
var internalSessionsList BackendRoomSwitchToSessionsList
for _, roomSessionId := range sessionsList {
if roomSessionId == sessionIdNotInMeeting {
continue
}
wg.Add(1)
go func(roomSessionId string) {
defer wg.Done()
if sessionId, err := b.lookupByRoomSessionId(ctx, roomSessionId, nil); err != nil {
log.Printf("Could not lookup by room session %s: %s", roomSessionId, err)
} else if sessionId != "" {
mu.Lock()
defer mu.Unlock()
internalSessionsList = append(internalSessionsList, sessionId)
}
}(roomSessionId)
}
wg.Wait()
mu.Lock()
defer mu.Unlock()
if len(internalSessionsList) == 0 {
return nil
}
request.SwitchTo.SessionsList = internalSessionsList
request.SwitchTo.SessionsMap = nil
} else {
var sessionsMap BackendRoomSwitchToSessionsMap
if err := json.Unmarshal(request.SwitchTo.Sessions, &sessionsMap); err != nil {
return err
}
if len(sessionsMap) == 0 {
return nil
}
internalSessionsMap := make(BackendRoomSwitchToSessionsMap)
for roomSessionId, details := range sessionsMap {
if roomSessionId == sessionIdNotInMeeting {
continue
}
wg.Add(1)
go func(roomSessionId string, details json.RawMessage) {
defer wg.Done()
if sessionId, err := b.lookupByRoomSessionId(ctx, roomSessionId, nil); err != nil {
log.Printf("Could not lookup by room session %s: %s", roomSessionId, err)
} else if sessionId != "" {
mu.Lock()
defer mu.Unlock()
internalSessionsMap[sessionId] = details
}
}(roomSessionId, details)
}
wg.Wait()
mu.Lock()
defer mu.Unlock()
if len(internalSessionsMap) == 0 {
return nil
}
request.SwitchTo.SessionsList = nil
request.SwitchTo.SessionsMap = internalSessionsMap
}
}
request.SwitchTo.Sessions = nil
message := &AsyncMessage{
Type: "room",
Room: request,
}
return b.events.PublishBackendRoomMessage(roomid, backend, message)
}
type BackendResponseWithStatus interface {
Status() int
}
type DialoutErrorResponse struct {
BackendServerRoomResponse
status int
}
func (r *DialoutErrorResponse) Status() int {
return r.status
}
func returnDialoutError(status int, err *Error) (any, error) {
response := &DialoutErrorResponse{
BackendServerRoomResponse: BackendServerRoomResponse{
Type: "dialout",
Dialout: &BackendRoomDialoutResponse{
Error: err,
},
},
status: status,
}
return response, nil
}
var checkNumeric = regexp.MustCompile(`^[0-9]+$`)
func isNumeric(s string) bool {
return checkNumeric.MatchString(s)
}
func (b *BackendServer) startDialout(roomid string, backend *Backend, backendUrl string, request *BackendServerRoomRequest) (any, error) {
if err := request.Dialout.ValidateNumber(); err != nil {
return returnDialoutError(http.StatusBadRequest, err)
}
if !isNumeric(roomid) {
return returnDialoutError(http.StatusBadRequest, NewError("invalid_roomid", "The room id must be numeric."))
}
session := b.hub.GetDialoutSession(roomid, backend)
if session == nil {
return returnDialoutError(http.StatusNotFound, NewError("no_client_available", "No available client found to trigger dialout."))
}
url := backend.Url()
if url == "" {
// Old-style compat backend, use client-provided URL.
url = backendUrl
if url != "" && url[len(url)-1] != '/' {
url += "/"
}
}
id := newRandomString(32)
msg := &ServerMessage{
Id: id,
Type: "internal",
Internal: &InternalServerMessage{
Type: "dialout",
Dialout: &InternalServerDialoutRequest{
RoomId: roomid,
Backend: url,
Request: request.Dialout,
},
},
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var response atomic.Pointer[DialoutInternalClientMessage]
session.HandleResponse(id, func(message *ClientMessage) bool {
response.Store(message.Internal.Dialout)
cancel()
// Don't send error to other sessions in the room.
return message.Internal.Dialout.Error != nil
})
defer session.ClearResponseHandler(id)
if !session.SendMessage(msg) {
return returnDialoutError(http.StatusBadGateway, NewError("error_notify", "Could not notify about new dialout."))
}
<-ctx.Done()
if err := ctx.Err(); err != nil && !errors.Is(err, context.Canceled) {
return returnDialoutError(http.StatusGatewayTimeout, NewError("timeout", "Timeout while waiting for dialout to start."))
}
dialout := response.Load()
if dialout == nil {
return returnDialoutError(http.StatusBadGateway, NewError("error_notify", "No dialout response received."))
}
switch dialout.Type {
case "error":
return returnDialoutError(http.StatusBadGateway, dialout.Error)
case "status":
if dialout.Status.Status != DialoutStatusAccepted {
log.Printf("Received unsupported dialout status when triggering dialout: %+v", dialout)
return returnDialoutError(http.StatusBadGateway, NewError("unsupported_status", "Unsupported dialout status received."))
}
return &BackendServerRoomResponse{
Type: "dialout",
Dialout: &BackendRoomDialoutResponse{
CallId: dialout.Status.CallId,
},
}, nil
}
log.Printf("Received unsupported dialout type when triggering dialout: %+v", dialout)
return returnDialoutError(http.StatusBadGateway, NewError("unsupported_type", "Unsupported dialout type received."))
}
func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body []byte) {
throttle, err := b.hub.throttler.CheckBruteforce(r.Context(), b.hub.getRealUserIP(r), "BackendRoomAuth")
if err == ErrBruteforceDetected {
http.Error(w, "Too many requests", http.StatusTooManyRequests)
return
} else if err != nil {
log.Printf("Error checking for bruteforce: %s", err)
http.Error(w, "Could not check for bruteforce", http.StatusInternalServerError)
return
}
v := mux.Vars(r)
roomid := v["roomid"]
@ -531,6 +801,7 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
if backend == nil {
// Unknown backend URL passed, return immediately.
throttle(r.Context())
http.Error(w, "Authentication check failed", http.StatusForbidden)
return
}
@ -552,12 +823,14 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
}
if backend == nil {
throttle(r.Context())
http.Error(w, "Authentication check failed", http.StatusForbidden)
return
}
}
if !ValidateBackendChecksum(r, body, backend.Secret()) {
throttle(r.Context())
http.Error(w, "Authentication check failed", http.StatusForbidden)
return
}
@ -571,7 +844,7 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
request.ReceivedTime = time.Now().UnixNano()
var err error
var response any
switch request.Type {
case "invite":
b.sendRoomInvite(roomid, backend, request.Invite.UserIds, request.Invite.Properties)
@ -580,10 +853,18 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
b.sendRoomDisinvite(roomid, backend, DisinviteReasonDisinvited, request.Disinvite.UserIds, request.Disinvite.SessionIds)
b.sendRoomUpdate(roomid, backend, request.Disinvite.UserIds, request.Disinvite.AllUserIds, request.Disinvite.Properties)
case "update":
err = b.nats.PublishBackendServerRoomRequest(GetSubjectForBackendRoomId(roomid, backend), &request)
message := &AsyncMessage{
Type: "room",
Room: &request,
}
err = b.events.PublishBackendRoomMessage(roomid, backend, message)
b.sendRoomUpdate(roomid, backend, nil, request.Update.UserIds, request.Update.Properties)
case "delete":
err = b.nats.PublishBackendServerRoomRequest(GetSubjectForBackendRoomId(roomid, backend), &request)
message := &AsyncMessage{
Type: "room",
Room: &request,
}
err = b.events.PublishBackendRoomMessage(roomid, backend, message)
b.sendRoomDisinvite(roomid, backend, DisinviteReasonDeleted, request.Delete.UserIds, nil)
case "incall":
err = b.sendRoomIncall(roomid, backend, &request)
@ -591,6 +872,10 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
err = b.sendRoomParticipantsUpdate(roomid, backend, &request)
case "message":
err = b.sendRoomMessage(roomid, backend, &request)
case "switchto":
err = b.sendRoomSwitchTo(roomid, backend, &request)
case "dialout":
response, err = b.startDialout(roomid, backend, backendUrl, &request)
default:
http.Error(w, "Unsupported request type: "+request.Type, http.StatusBadRequest)
return
@ -602,22 +887,43 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
return
}
var responseData []byte
responseStatus := http.StatusOK
if response == nil {
// TODO(jojo): Return better response struct.
responseData = []byte("{}")
} else {
if s, ok := response.(BackendResponseWithStatus); ok {
responseStatus = s.Status()
}
responseData, err = json.Marshal(response)
if err != nil {
log.Printf("Could not serialize backend response %+v: %s", response, err)
responseStatus = http.StatusInternalServerError
responseData = []byte("{\"error\":\"could_not_serialize\"}")
}
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK)
// TODO(jojo): Return better response struct.
w.Write([]byte("{}")) // nolint
w.WriteHeader(responseStatus)
w.Write(responseData) // nolint
}
func (b *BackendServer) allowStatsAccess(r *http.Request) bool {
addr := b.hub.getRealUserIP(r)
ip := net.ParseIP(addr)
if len(ip) == 0 {
return false
}
allowed := b.statsAllowedIps.Load()
return allowed != nil && allowed.Allowed(ip)
}
func (b *BackendServer) validateStatsRequest(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
addr := getRealUserIP(r)
if strings.Contains(addr, ":") {
if host, _, err := net.SplitHostPort(addr); err == nil {
addr = host
}
}
if !b.statsAllowedIps[addr] {
if !b.allowStatsAccess(r) {
http.Error(w, "Authentication check failed", http.StatusForbidden)
return
}

File diff suppressed because it is too large Load diff

282
backend_storage_etcd.go Normal file
View file

@ -0,0 +1,282 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"net/url"
"time"
"github.com/dlintw/goconf"
clientv3 "go.etcd.io/etcd/client/v3"
)
type backendStorageEtcd struct {
backendStorageCommon
etcdClient *EtcdClient
keyPrefix string
keyInfos map[string]*BackendInformationEtcd
initializedCtx context.Context
initializedFunc context.CancelFunc
wakeupChanForTesting chan struct{}
closeCtx context.Context
closeFunc context.CancelFunc
}
func NewBackendStorageEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient) (BackendStorage, error) {
if etcdClient == nil || !etcdClient.IsConfigured() {
return nil, fmt.Errorf("no etcd endpoints configured")
}
keyPrefix, _ := config.GetString("backend", "backendprefix")
if keyPrefix == "" {
return nil, fmt.Errorf("no backend prefix configured")
}
initializedCtx, initializedFunc := context.WithCancel(context.Background())
closeCtx, closeFunc := context.WithCancel(context.Background())
result := &backendStorageEtcd{
backendStorageCommon: backendStorageCommon{
backends: make(map[string][]*Backend),
},
etcdClient: etcdClient,
keyPrefix: keyPrefix,
keyInfos: make(map[string]*BackendInformationEtcd),
initializedCtx: initializedCtx,
initializedFunc: initializedFunc,
closeCtx: closeCtx,
closeFunc: closeFunc,
}
etcdClient.AddListener(result)
return result, nil
}
func (s *backendStorageEtcd) WaitForInitialized(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-s.initializedCtx.Done():
return nil
}
}
func (s *backendStorageEtcd) wakeupForTesting() {
if s.wakeupChanForTesting == nil {
return
}
select {
case s.wakeupChanForTesting <- struct{}{}:
default:
}
}
func (s *backendStorageEtcd) EtcdClientCreated(client *EtcdClient) {
go func() {
if err := client.WaitForConnection(s.closeCtx); err != nil {
if errors.Is(err, context.Canceled) {
return
}
panic(err)
}
backoff, err := NewExponentialBackoff(initialWaitDelay, maxWaitDelay)
if err != nil {
panic(err)
}
for s.closeCtx.Err() == nil {
response, err := s.getBackends(s.closeCtx, client, s.keyPrefix)
if err != nil {
if errors.Is(err, context.Canceled) {
return
} else if errors.Is(err, context.DeadlineExceeded) {
log.Printf("Timeout getting initial list of backends, retry in %s", backoff.NextWait())
} else {
log.Printf("Could not get initial list of backends, retry in %s: %s", backoff.NextWait(), err)
}
backoff.Wait(s.closeCtx)
continue
}
for _, ev := range response.Kvs {
s.EtcdKeyUpdated(client, string(ev.Key), ev.Value, nil)
}
s.initializedFunc()
nextRevision := response.Header.Revision + 1
prevRevision := nextRevision
backoff.Reset()
for s.closeCtx.Err() == nil {
var err error
if nextRevision, err = client.Watch(s.closeCtx, s.keyPrefix, nextRevision, s, clientv3.WithPrefix()); err != nil {
log.Printf("Error processing watch for %s (%s), retry in %s", s.keyPrefix, err, backoff.NextWait())
backoff.Wait(s.closeCtx)
continue
}
if nextRevision != prevRevision {
backoff.Reset()
prevRevision = nextRevision
} else {
log.Printf("Processing watch for %s interrupted, retry in %s", s.keyPrefix, backoff.NextWait())
backoff.Wait(s.closeCtx)
}
}
return
}
}()
}
func (s *backendStorageEtcd) EtcdWatchCreated(client *EtcdClient, key string) {
}
func (s *backendStorageEtcd) getBackends(ctx context.Context, client *EtcdClient, keyPrefix string) (*clientv3.GetResponse, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
return client.Get(ctx, keyPrefix, clientv3.WithPrefix())
}
func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data []byte, prevValue []byte) {
var info BackendInformationEtcd
if err := json.Unmarshal(data, &info); err != nil {
log.Printf("Could not decode backend information %s: %s", string(data), err)
return
}
if err := info.CheckValid(); err != nil {
log.Printf("Received invalid backend information %s: %s", string(data), err)
return
}
backend := &Backend{
id: key,
url: info.Url,
parsedUrl: info.parsedUrl,
secret: []byte(info.Secret),
allowHttp: info.parsedUrl.Scheme == "http",
maxStreamBitrate: info.MaxStreamBitrate,
maxScreenBitrate: info.MaxScreenBitrate,
sessionLimit: info.SessionLimit,
}
host := info.parsedUrl.Host
s.mu.Lock()
defer s.mu.Unlock()
s.keyInfos[key] = &info
entries, found := s.backends[host]
if !found {
// Simple case, first backend for this host
log.Printf("Added backend %s (from %s)", info.Url, key)
s.backends[host] = []*Backend{backend}
statsBackendsCurrent.Inc()
s.wakeupForTesting()
return
}
// Was the backend changed?
replaced := false
for idx, entry := range entries {
if entry.id == key {
log.Printf("Updated backend %s (from %s)", info.Url, key)
entries[idx] = backend
replaced = true
break
}
}
if !replaced {
// New backend, add to list.
log.Printf("Added backend %s (from %s)", info.Url, key)
s.backends[host] = append(entries, backend)
statsBackendsCurrent.Inc()
}
s.wakeupForTesting()
}
func (s *backendStorageEtcd) EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte) {
s.mu.Lock()
defer s.mu.Unlock()
info, found := s.keyInfos[key]
if !found {
return
}
delete(s.keyInfos, key)
host := info.parsedUrl.Host
entries, found := s.backends[host]
if !found {
return
}
log.Printf("Removing backend %s (from %s)", info.Url, key)
newEntries := make([]*Backend, 0, len(entries)-1)
for _, entry := range entries {
if entry.id == key {
statsBackendsCurrent.Dec()
continue
}
newEntries = append(newEntries, entry)
}
if len(newEntries) > 0 {
s.backends[host] = newEntries
} else {
delete(s.backends, host)
}
s.wakeupForTesting()
}
func (s *backendStorageEtcd) Close() {
s.etcdClient.RemoveListener(s)
s.closeFunc()
}
func (s *backendStorageEtcd) Reload(config *goconf.ConfigFile) {
// Backend updates are processed through etcd.
}
func (s *backendStorageEtcd) GetCompatBackend() *Backend {
return nil
}
func (s *backendStorageEtcd) GetBackend(u *url.URL) *Backend {
s.mu.RLock()
defer s.mu.RUnlock()
return s.getBackendLocked(u)
}

View file

@ -0,0 +1,77 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"testing"
"github.com/dlintw/goconf"
"go.etcd.io/etcd/server/v3/embed"
)
func (s *backendStorageEtcd) getWakeupChannelForTesting() <-chan struct{} {
s.mu.Lock()
defer s.mu.Unlock()
if s.wakeupChanForTesting != nil {
return s.wakeupChanForTesting
}
ch := make(chan struct{}, 1)
s.wakeupChanForTesting = ch
return ch
}
type testListener struct {
etcd *embed.Etcd
closed chan struct{}
}
func (tl *testListener) EtcdClientCreated(client *EtcdClient) {
tl.etcd.Server.Stop()
close(tl.closed)
}
func Test_BackendStorageEtcdNoLeak(t *testing.T) {
CatchLogForTest(t)
ensureNoGoroutinesLeak(t, func(t *testing.T) {
etcd, client := NewEtcdClientForTest(t)
tl := &testListener{
etcd: etcd,
closed: make(chan struct{}),
}
client.AddListener(tl)
defer client.RemoveListener(tl)
config := goconf.NewConfigFile()
config.AddOption("backend", "backendtype", "etcd")
config.AddOption("backend", "backendprefix", "/backends")
cfg, err := NewBackendConfiguration(config, client)
if err != nil {
t.Fatal(err)
}
<-tl.closed
cfg.Close()
})
}

314
backend_storage_static.go Normal file
View file

@ -0,0 +1,314 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"log"
"net/url"
"reflect"
"strings"
"github.com/dlintw/goconf"
)
type backendStorageStatic struct {
backendStorageCommon
// Deprecated
allowAll bool
commonSecret []byte
compatBackend *Backend
}
func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) {
allowAll, _ := config.GetBool("backend", "allowall")
allowHttp, _ := config.GetBool("backend", "allowhttp")
commonSecret, _ := config.GetString("backend", "secret")
sessionLimit, err := config.GetInt("backend", "sessionlimit")
if err != nil || sessionLimit < 0 {
sessionLimit = 0
}
backends := make(map[string][]*Backend)
var compatBackend *Backend
numBackends := 0
if allowAll {
log.Println("WARNING: All backend hostnames are allowed, only use for development!")
compatBackend = &Backend{
id: "compat",
secret: []byte(commonSecret),
compat: true,
allowHttp: allowHttp,
sessionLimit: uint64(sessionLimit),
}
if sessionLimit > 0 {
log.Printf("Allow a maximum of %d sessions", sessionLimit)
}
numBackends++
} else if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" {
for host, configuredBackends := range getConfiguredHosts(backendIds, config, commonSecret) {
backends[host] = append(backends[host], configuredBackends...)
for _, be := range configuredBackends {
log.Printf("Backend %s added for %s", be.id, be.url)
}
numBackends += len(configuredBackends)
}
} else if allowedUrls, _ := config.GetString("backend", "allowed"); allowedUrls != "" {
// Old-style configuration, only hosts are configured and are using a common secret.
allowMap := make(map[string]bool)
for _, u := range strings.Split(allowedUrls, ",") {
u = strings.TrimSpace(u)
if idx := strings.IndexByte(u, '/'); idx != -1 {
log.Printf("WARNING: Removing path from allowed hostname \"%s\", check your configuration!", u)
u = u[:idx]
}
if u != "" {
allowMap[strings.ToLower(u)] = true
}
}
if len(allowMap) == 0 {
log.Println("WARNING: No backend hostnames are allowed, check your configuration!")
} else {
compatBackend = &Backend{
id: "compat",
secret: []byte(commonSecret),
compat: true,
allowHttp: allowHttp,
sessionLimit: uint64(sessionLimit),
}
hosts := make([]string, 0, len(allowMap))
for host := range allowMap {
hosts = append(hosts, host)
backends[host] = []*Backend{compatBackend}
}
if len(hosts) > 1 {
log.Println("WARNING: Using deprecated backend configuration. Please migrate the \"allowed\" setting to the new \"backends\" configuration.")
}
log.Printf("Allowed backend hostnames: %s", hosts)
if sessionLimit > 0 {
log.Printf("Allow a maximum of %d sessions", sessionLimit)
}
numBackends++
}
}
if numBackends == 0 {
log.Printf("WARNING: No backends configured, client connections will not be possible.")
}
statsBackendsCurrent.Add(float64(numBackends))
return &backendStorageStatic{
backendStorageCommon: backendStorageCommon{
backends: backends,
},
allowAll: allowAll,
commonSecret: []byte(commonSecret),
compatBackend: compatBackend,
}, nil
}
func (s *backendStorageStatic) Close() {
}
func (s *backendStorageStatic) RemoveBackendsForHost(host string) {
if oldBackends := s.backends[host]; len(oldBackends) > 0 {
for _, backend := range oldBackends {
log.Printf("Backend %s removed for %s", backend.id, backend.url)
}
statsBackendsCurrent.Sub(float64(len(oldBackends)))
}
delete(s.backends, host)
}
func (s *backendStorageStatic) UpsertHost(host string, backends []*Backend) {
for existingIndex, existingBackend := range s.backends[host] {
found := false
index := 0
for _, newBackend := range backends {
if reflect.DeepEqual(existingBackend, newBackend) { // otherwise we could manually compare the struct members here
found = true
backends = append(backends[:index], backends[index+1:]...)
break
} else if newBackend.id == existingBackend.id {
found = true
s.backends[host][existingIndex] = newBackend
backends = append(backends[:index], backends[index+1:]...)
log.Printf("Backend %s updated for %s", newBackend.id, newBackend.url)
break
}
index++
}
if !found {
removed := s.backends[host][existingIndex]
log.Printf("Backend %s removed for %s", removed.id, removed.url)
s.backends[host] = append(s.backends[host][:existingIndex], s.backends[host][existingIndex+1:]...)
statsBackendsCurrent.Dec()
}
}
s.backends[host] = append(s.backends[host], backends...)
for _, added := range backends {
log.Printf("Backend %s added for %s", added.id, added.url)
}
statsBackendsCurrent.Add(float64(len(backends)))
}
func getConfiguredBackendIDs(backendIds string) (ids []string) {
seen := make(map[string]bool)
for _, id := range strings.Split(backendIds, ",") {
id = strings.TrimSpace(id)
if id == "" {
continue
}
if seen[id] {
continue
}
ids = append(ids, id)
seen[id] = true
}
return ids
}
func getConfiguredHosts(backendIds string, config *goconf.ConfigFile, commonSecret string) (hosts map[string][]*Backend) {
hosts = make(map[string][]*Backend)
for _, id := range getConfiguredBackendIDs(backendIds) {
u, _ := config.GetString(id, "url")
if u == "" {
log.Printf("Backend %s is missing or incomplete, skipping", id)
continue
}
if u[len(u)-1] != '/' {
u += "/"
}
parsed, err := url.Parse(u)
if err != nil {
log.Printf("Backend %s has an invalid url %s configured (%s), skipping", id, u, err)
continue
}
if strings.Contains(parsed.Host, ":") && hasStandardPort(parsed) {
parsed.Host = parsed.Hostname()
u = parsed.String()
}
secret, _ := config.GetString(id, "secret")
if secret == "" && commonSecret != "" {
log.Printf("Backend %s has no own shared secret set, using common shared secret", id)
secret = commonSecret
}
if u == "" || secret == "" {
log.Printf("Backend %s is missing or incomplete, skipping", id)
continue
}
sessionLimit, err := config.GetInt(id, "sessionlimit")
if err != nil || sessionLimit < 0 {
sessionLimit = 0
}
if sessionLimit > 0 {
log.Printf("Backend %s allows a maximum of %d sessions", id, sessionLimit)
}
maxStreamBitrate, err := config.GetInt(id, "maxstreambitrate")
if err != nil || maxStreamBitrate < 0 {
maxStreamBitrate = 0
}
maxScreenBitrate, err := config.GetInt(id, "maxscreenbitrate")
if err != nil || maxScreenBitrate < 0 {
maxScreenBitrate = 0
}
hosts[parsed.Host] = append(hosts[parsed.Host], &Backend{
id: id,
url: u,
parsedUrl: parsed,
secret: []byte(secret),
allowHttp: parsed.Scheme == "http",
maxStreamBitrate: maxStreamBitrate,
maxScreenBitrate: maxScreenBitrate,
sessionLimit: uint64(sessionLimit),
})
}
return hosts
}
func (s *backendStorageStatic) Reload(config *goconf.ConfigFile) {
s.mu.Lock()
defer s.mu.Unlock()
if s.compatBackend != nil {
log.Println("Old-style configuration active, reload is not supported")
return
}
commonSecret, _ := config.GetString("backend", "secret")
if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" {
configuredHosts := getConfiguredHosts(backendIds, config, commonSecret)
// remove backends that are no longer configured
for hostname := range s.backends {
if _, ok := configuredHosts[hostname]; !ok {
s.RemoveBackendsForHost(hostname)
}
}
// rewrite backends adding newly configured ones and rewriting existing ones
for hostname, configuredBackends := range configuredHosts {
s.UpsertHost(hostname, configuredBackends)
}
}
}
func (s *backendStorageStatic) GetCompatBackend() *Backend {
s.mu.RLock()
defer s.mu.RUnlock()
return s.compatBackend
}
func (s *backendStorageStatic) GetBackend(u *url.URL) *Backend {
s.mu.RLock()
defer s.mu.RUnlock()
if _, found := s.backends[u.Host]; !found {
if s.allowAll {
return s.compatBackend
}
return nil
}
return s.getBackendLocked(u)
}

76
backoff.go Normal file
View file

@ -0,0 +1,76 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"fmt"
"time"
)
type Backoff interface {
Reset()
NextWait() time.Duration
Wait(context.Context)
}
type exponentialBackoff struct {
initial time.Duration
maxWait time.Duration
nextWait time.Duration
}
func NewExponentialBackoff(initial time.Duration, maxWait time.Duration) (Backoff, error) {
if initial <= 0 {
return nil, fmt.Errorf("initial must be larger than 0")
}
if maxWait < initial {
return nil, fmt.Errorf("maxWait must be larger or equal to initial")
}
return &exponentialBackoff{
initial: initial,
maxWait: maxWait,
nextWait: initial,
}, nil
}
func (b *exponentialBackoff) Reset() {
b.nextWait = b.initial
}
func (b *exponentialBackoff) NextWait() time.Duration {
return b.nextWait
}
func (b *exponentialBackoff) Wait(ctx context.Context) {
waiter, cancel := context.WithTimeout(ctx, b.nextWait)
defer cancel()
b.nextWait = b.nextWait * 2
if b.nextWait > b.maxWait {
b.nextWait = b.maxWait
}
<-waiter.Done()
}

65
backoff_test.go Normal file
View file

@ -0,0 +1,65 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"testing"
"time"
)
func TestBackoff_Exponential(t *testing.T) {
t.Parallel()
backoff, err := NewExponentialBackoff(100*time.Millisecond, 500*time.Millisecond)
if err != nil {
t.Fatal(err)
}
waitTimes := []time.Duration{
100 * time.Millisecond,
200 * time.Millisecond,
400 * time.Millisecond,
500 * time.Millisecond,
500 * time.Millisecond,
}
for _, wait := range waitTimes {
if backoff.NextWait() != wait {
t.Errorf("Wait time should be %s, got %s", wait, backoff.NextWait())
}
a := time.Now()
backoff.Wait(context.Background())
b := time.Now()
if b.Sub(a) < wait {
t.Errorf("Should have waited %s, got %s", wait, b.Sub(a))
}
}
backoff.Reset()
a := time.Now()
backoff.Wait(context.Background())
b := time.Now()
if b.Sub(a) < 100*time.Millisecond {
t.Errorf("Should have waited %s, got %s", 100*time.Millisecond, b.Sub(a))
}
}

View file

@ -43,6 +43,9 @@ const (
// Cache received capabilities for one hour.
CapabilitiesCacheDuration = time.Hour
// Don't invalidate more than once per minute.
maxInvalidateInterval = time.Minute
)
type capabilitiesEntry struct {
@ -53,16 +56,23 @@ type capabilitiesEntry struct {
type Capabilities struct {
mu sync.RWMutex
version string
pool *HttpClientPool
entries map[string]*capabilitiesEntry
// Can be overwritten by tests.
getNow func() time.Time
version string
pool *HttpClientPool
entries map[string]*capabilitiesEntry
nextInvalidate map[string]time.Time
}
func NewCapabilities(version string, pool *HttpClientPool) (*Capabilities, error) {
result := &Capabilities{
version: version,
pool: pool,
entries: make(map[string]*capabilitiesEntry),
getNow: time.Now,
version: version,
pool: pool,
entries: make(map[string]*capabilitiesEntry),
nextInvalidate: make(map[string]time.Time),
}
return result, nil
@ -78,15 +88,15 @@ type CapabilitiesVersion struct {
}
type CapabilitiesResponse struct {
Version CapabilitiesVersion `json:"version"`
Capabilities map[string]map[string]interface{} `json:"capabilities"`
Version CapabilitiesVersion `json:"version"`
Capabilities map[string]json.RawMessage `json:"capabilities"`
}
func (c *Capabilities) getCapabilities(key string) (map[string]interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
now := time.Now()
now := c.getNow()
if entry, found := c.entries[key]; found && entry.nextUpdate.After(now) {
return entry.capabilities, true
}
@ -95,22 +105,40 @@ func (c *Capabilities) getCapabilities(key string) (map[string]interface{}, bool
}
func (c *Capabilities) setCapabilities(key string, capabilities map[string]interface{}) {
now := time.Now()
c.mu.Lock()
defer c.mu.Unlock()
now := c.getNow()
entry := &capabilitiesEntry{
nextUpdate: now.Add(CapabilitiesCacheDuration),
capabilities: capabilities,
}
c.mu.Lock()
defer c.mu.Unlock()
c.entries[key] = entry
}
func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[string]interface{}, error) {
key := u.String()
func (c *Capabilities) invalidateCapabilities(key string) {
c.mu.Lock()
defer c.mu.Unlock()
now := c.getNow()
if entry, found := c.nextInvalidate[key]; found && entry.After(now) {
return
}
delete(c.entries, key)
c.nextInvalidate[key] = now.Add(maxInvalidateInterval)
}
func (c *Capabilities) getKeyForUrl(u *url.URL) string {
key := u.String()
return key
}
func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[string]interface{}, bool, error) {
key := c.getKeyForUrl(u)
if caps, found := c.getCapabilities(key); found {
return caps, nil
return caps, true, nil
}
capUrl := *u
@ -128,14 +156,14 @@ func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[st
client, pool, err := c.pool.Get(ctx, &capUrl)
if err != nil {
log.Printf("Could not get client for host %s: %s", capUrl.Host, err)
return nil, err
return nil, false, err
}
defer pool.Put(client)
req, err := http.NewRequestWithContext(ctx, "GET", capUrl.String(), nil)
if err != nil {
log.Printf("Could not create request to %s: %s", &capUrl, err)
return nil, err
return nil, false, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("OCS-APIRequest", "true")
@ -143,50 +171,56 @@ func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[st
resp, err := client.Do(req)
if err != nil {
return nil, err
return nil, false, err
}
defer resp.Body.Close()
ct := resp.Header.Get("Content-Type")
if !strings.HasPrefix(ct, "application/json") {
log.Printf("Received unsupported content-type from %s: %s (%s)", capUrl.String(), ct, resp.Status)
return nil, ErrUnsupportedContentType
return nil, false, ErrUnsupportedContentType
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Could not read response body from %s: %s", capUrl.String(), err)
return nil, err
return nil, false, err
}
var ocs OcsResponse
if err := json.Unmarshal(body, &ocs); err != nil {
log.Printf("Could not decode OCS response %s from %s: %s", string(body), capUrl.String(), err)
return nil, err
} else if ocs.Ocs == nil || ocs.Ocs.Data == nil {
return nil, false, err
} else if ocs.Ocs == nil || len(ocs.Ocs.Data) == 0 {
log.Printf("Incomplete OCS response %s from %s", string(body), u)
return nil, fmt.Errorf("incomplete OCS response")
return nil, false, fmt.Errorf("incomplete OCS response")
}
var response CapabilitiesResponse
if err := json.Unmarshal(*ocs.Ocs.Data, &response); err != nil {
log.Printf("Could not decode OCS response body %s from %s: %s", string(*ocs.Ocs.Data), capUrl.String(), err)
return nil, err
if err := json.Unmarshal(ocs.Ocs.Data, &response); err != nil {
log.Printf("Could not decode OCS response body %s from %s: %s", string(ocs.Ocs.Data), capUrl.String(), err)
return nil, false, err
}
capa, found := response.Capabilities[AppNameSpreed]
if !found {
capaObj, found := response.Capabilities[AppNameSpreed]
if !found || len(capaObj) == 0 {
log.Printf("No capabilities received for app spreed from %s: %+v", capUrl.String(), response)
return nil, nil
return nil, false, nil
}
var capa map[string]interface{}
if err := json.Unmarshal(capaObj, &capa); err != nil {
log.Printf("Unsupported capabilities received for app spreed from %s: %+v", capUrl.String(), response)
return nil, false, nil
}
log.Printf("Received capabilities %+v from %s", capa, capUrl.String())
c.setCapabilities(key, capa)
return capa, nil
return capa, false, nil
}
func (c *Capabilities) HasCapabilityFeature(ctx context.Context, u *url.URL, feature string) bool {
caps, err := c.loadCapabilities(ctx, u)
caps, _, err := c.loadCapabilities(ctx, u)
if err != nil {
log.Printf("Could not get capabilities for %s: %s", u, err)
return false
@ -211,80 +245,86 @@ func (c *Capabilities) HasCapabilityFeature(ctx context.Context, u *url.URL, fea
return false
}
func (c *Capabilities) getConfigGroup(ctx context.Context, u *url.URL, group string) (map[string]interface{}, bool) {
caps, err := c.loadCapabilities(ctx, u)
func (c *Capabilities) getConfigGroup(ctx context.Context, u *url.URL, group string) (map[string]interface{}, bool, bool) {
caps, cached, err := c.loadCapabilities(ctx, u)
if err != nil {
log.Printf("Could not get capabilities for %s: %s", u, err)
return nil, false
return nil, cached, false
}
configInterface := caps["config"]
if configInterface == nil {
return nil, false
return nil, cached, false
}
config, ok := configInterface.(map[string]interface{})
if !ok {
log.Printf("Invalid config mapping received from %s: %+v", u, configInterface)
return nil, false
return nil, cached, false
}
groupInterface := config[group]
if groupInterface == nil {
return nil, false
return nil, cached, false
}
groupConfig, ok := groupInterface.(map[string]interface{})
if !ok {
log.Printf("Invalid group mapping \"%s\" received from %s: %+v", group, u, groupInterface)
return nil, false
return nil, cached, false
}
return groupConfig, true
return groupConfig, cached, true
}
func (c *Capabilities) GetIntegerConfig(ctx context.Context, u *url.URL, group, key string) (int, bool) {
groupConfig, found := c.getConfigGroup(ctx, u, group)
func (c *Capabilities) GetIntegerConfig(ctx context.Context, u *url.URL, group, key string) (int, bool, bool) {
groupConfig, cached, found := c.getConfigGroup(ctx, u, group)
if !found {
return 0, false
return 0, cached, false
}
value, found := groupConfig[key]
if !found {
return 0, false
return 0, cached, false
}
switch value := value.(type) {
case int:
return value, true
return value, cached, true
case float32:
return int(value), true
return int(value), cached, true
case float64:
return int(value), true
return int(value), cached, true
default:
log.Printf("Invalid config value for \"%s\" received from %s: %+v", key, u, value)
}
return 0, false
return 0, cached, false
}
func (c *Capabilities) GetStringConfig(ctx context.Context, u *url.URL, group, key string) (string, bool) {
groupConfig, found := c.getConfigGroup(ctx, u, group)
func (c *Capabilities) GetStringConfig(ctx context.Context, u *url.URL, group, key string) (string, bool, bool) {
groupConfig, cached, found := c.getConfigGroup(ctx, u, group)
if !found {
return "", false
return "", cached, false
}
value, found := groupConfig[key]
if !found {
return "", false
return "", cached, false
}
switch value := value.(type) {
case string:
return value, true
return value, cached, true
default:
log.Printf("Invalid config value for \"%s\" received from %s: %+v", key, u, value)
}
return "", false
return "", cached, false
}
func (c *Capabilities) InvalidateCapabilities(u *url.URL) {
key := c.getKeyForUrl(u)
c.invalidateCapabilities(key)
}

View file

@ -28,12 +28,14 @@ import (
"net/http/httptest"
"net/url"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/gorilla/mux"
)
func NewCapabilitiesForTest(t *testing.T) (*url.URL, *Capabilities) {
func NewCapabilitiesForTestWithCallback(t *testing.T, callback func(*CapabilitiesResponse)) (*url.URL, *Capabilities) {
pool, err := NewHttpClientPool(1, false)
if err != nil {
t.Fatal(err)
@ -69,18 +71,25 @@ func NewCapabilitiesForTest(t *testing.T) (*url.URL, *Capabilities) {
config := map[string]interface{}{
"signaling": signaling,
}
spreedCapa, _ := json.Marshal(map[string]interface{}{
"features": features,
"config": config,
})
emptyArray := []byte("[]")
response := &CapabilitiesResponse{
Version: CapabilitiesVersion{
Major: 20,
},
Capabilities: map[string]map[string]interface{}{
"spreed": {
"features": features,
"config": config,
},
Capabilities: map[string]json.RawMessage{
"anotherApp": emptyArray,
"spreed": spreedCapa,
},
}
if callback != nil {
callback(response)
}
data, err := json.Marshal(response)
if err != nil {
t.Errorf("Could not marshal %+v: %s", response, err)
@ -93,7 +102,7 @@ func NewCapabilitiesForTest(t *testing.T) (*url.URL, *Capabilities) {
StatusCode: http.StatusOK,
Message: http.StatusText(http.StatusOK),
},
Data: (*json.RawMessage)(&data),
Data: data,
}
if data, err = json.Marshal(ocs); err != nil {
t.Fatal(err)
@ -107,7 +116,29 @@ func NewCapabilitiesForTest(t *testing.T) (*url.URL, *Capabilities) {
return u, capabilities
}
func NewCapabilitiesForTest(t *testing.T) (*url.URL, *Capabilities) {
return NewCapabilitiesForTestWithCallback(t, nil)
}
func SetCapabilitiesGetNow(t *testing.T, capabilities *Capabilities, f func() time.Time) {
capabilities.mu.Lock()
defer capabilities.mu.Unlock()
old := capabilities.getNow
t.Cleanup(func() {
capabilities.mu.Lock()
defer capabilities.mu.Unlock()
capabilities.getNow = old
})
capabilities.getNow = f
}
func TestCapabilities(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
url, capabilities := NewCapabilitiesForTest(t)
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
@ -121,34 +152,124 @@ func TestCapabilities(t *testing.T) {
}
expectedString := "bar"
if value, found := capabilities.GetStringConfig(ctx, url, "signaling", "foo"); !found {
if value, cached, found := capabilities.GetStringConfig(ctx, url, "signaling", "foo"); !found {
t.Error("could not find value for \"foo\"")
} else if value != expectedString {
t.Errorf("expected value %s, got %s", expectedString, value)
} else if !cached {
t.Errorf("expected cached response")
}
if value, found := capabilities.GetStringConfig(ctx, url, "signaling", "baz"); found {
if value, cached, found := capabilities.GetStringConfig(ctx, url, "signaling", "baz"); found {
t.Errorf("should not have found value for \"baz\", got %s", value)
} else if !cached {
t.Errorf("expected cached response")
}
if value, found := capabilities.GetStringConfig(ctx, url, "signaling", "invalid"); found {
if value, cached, found := capabilities.GetStringConfig(ctx, url, "signaling", "invalid"); found {
t.Errorf("should not have found value for \"invalid\", got %s", value)
} else if !cached {
t.Errorf("expected cached response")
}
if value, found := capabilities.GetStringConfig(ctx, url, "invalid", "foo"); found {
if value, cached, found := capabilities.GetStringConfig(ctx, url, "invalid", "foo"); found {
t.Errorf("should not have found value for \"baz\", got %s", value)
} else if !cached {
t.Errorf("expected cached response")
}
expectedInt := 42
if value, found := capabilities.GetIntegerConfig(ctx, url, "signaling", "baz"); !found {
if value, cached, found := capabilities.GetIntegerConfig(ctx, url, "signaling", "baz"); !found {
t.Error("could not find value for \"baz\"")
} else if value != expectedInt {
t.Errorf("expected value %d, got %d", expectedInt, value)
} else if !cached {
t.Errorf("expected cached response")
}
if value, found := capabilities.GetIntegerConfig(ctx, url, "signaling", "foo"); found {
if value, cached, found := capabilities.GetIntegerConfig(ctx, url, "signaling", "foo"); found {
t.Errorf("should not have found value for \"foo\", got %d", value)
} else if !cached {
t.Errorf("expected cached response")
}
if value, found := capabilities.GetIntegerConfig(ctx, url, "signaling", "invalid"); found {
if value, cached, found := capabilities.GetIntegerConfig(ctx, url, "signaling", "invalid"); found {
t.Errorf("should not have found value for \"invalid\", got %d", value)
} else if !cached {
t.Errorf("expected cached response")
}
if value, found := capabilities.GetIntegerConfig(ctx, url, "invalid", "baz"); found {
if value, cached, found := capabilities.GetIntegerConfig(ctx, url, "invalid", "baz"); found {
t.Errorf("should not have found value for \"baz\", got %d", value)
} else if !cached {
t.Errorf("expected cached response")
}
}
func TestInvalidateCapabilities(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
var called atomic.Uint32
url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse) {
called.Add(1)
})
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
expectedString := "bar"
if value, cached, found := capabilities.GetStringConfig(ctx, url, "signaling", "foo"); !found {
t.Error("could not find value for \"foo\"")
} else if value != expectedString {
t.Errorf("expected value %s, got %s", expectedString, value)
} else if cached {
t.Errorf("expected direct response")
}
if value := called.Load(); value != 1 {
t.Errorf("expected called %d, got %d", 1, value)
}
// Invalidating will cause the capabilities to be reloaded.
capabilities.InvalidateCapabilities(url)
if value, cached, found := capabilities.GetStringConfig(ctx, url, "signaling", "foo"); !found {
t.Error("could not find value for \"foo\"")
} else if value != expectedString {
t.Errorf("expected value %s, got %s", expectedString, value)
} else if cached {
t.Errorf("expected direct response")
}
if value := called.Load(); value != 2 {
t.Errorf("expected called %d, got %d", 2, value)
}
// Invalidating is throttled to about once per minute.
capabilities.InvalidateCapabilities(url)
if value, cached, found := capabilities.GetStringConfig(ctx, url, "signaling", "foo"); !found {
t.Error("could not find value for \"foo\"")
} else if value != expectedString {
t.Errorf("expected value %s, got %s", expectedString, value)
} else if !cached {
t.Errorf("expected cached response")
}
if value := called.Load(); value != 2 {
t.Errorf("expected called %d, got %d", 2, value)
}
// At a later time, invalidating can be done again.
SetCapabilitiesGetNow(t, capabilities, func() time.Time {
return time.Now().Add(2 * time.Minute)
})
capabilities.InvalidateCapabilities(url)
if value, cached, found := capabilities.GetStringConfig(ctx, url, "signaling", "foo"); !found {
t.Error("could not find value for \"foo\"")
} else if value != expectedString {
t.Errorf("expected value %s, got %s", expectedString, value)
} else if cached {
t.Errorf("expected direct response")
}
if value := called.Load(); value != 3 {
t.Errorf("expected called %d, got %d", 3, value)
}
}

165
certificate_reloader.go Normal file
View file

@ -0,0 +1,165 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"crypto/tls"
"crypto/x509"
"fmt"
"log"
"os"
"sync/atomic"
)
type CertificateReloader struct {
certFile string
certWatcher *FileWatcher
keyFile string
keyWatcher *FileWatcher
certificate atomic.Pointer[tls.Certificate]
reloadCounter atomic.Uint64
}
func NewCertificateReloader(certFile string, keyFile string) (*CertificateReloader, error) {
pair, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, fmt.Errorf("could not load certificate / key: %w", err)
}
reloader := &CertificateReloader{
certFile: certFile,
keyFile: keyFile,
}
reloader.certificate.Store(&pair)
reloader.certWatcher, err = NewFileWatcher(certFile, reloader.reload)
if err != nil {
return nil, err
}
reloader.keyWatcher, err = NewFileWatcher(keyFile, reloader.reload)
if err != nil {
reloader.certWatcher.Close() // nolint
return nil, err
}
return reloader, nil
}
func (r *CertificateReloader) Close() {
r.keyWatcher.Close()
r.certWatcher.Close()
}
func (r *CertificateReloader) reload(filename string) {
log.Printf("reloading certificate from %s with %s", r.certFile, r.keyFile)
pair, err := tls.LoadX509KeyPair(r.certFile, r.keyFile)
if err != nil {
log.Printf("could not load certificate / key: %s", err)
return
}
r.certificate.Store(&pair)
r.reloadCounter.Add(1)
}
func (r *CertificateReloader) getCertificate() (*tls.Certificate, error) {
return r.certificate.Load(), nil
}
func (r *CertificateReloader) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
return r.getCertificate()
}
func (r *CertificateReloader) GetClientCertificate(i *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return r.getCertificate()
}
func (r *CertificateReloader) GetReloadCounter() uint64 {
return r.reloadCounter.Load()
}
type CertPoolReloader struct {
certFile string
certWatcher *FileWatcher
pool atomic.Pointer[x509.CertPool]
reloadCounter atomic.Uint64
}
func loadCertPool(filename string) (*x509.CertPool, error) {
cert, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(cert) {
return nil, fmt.Errorf("invalid CA in %s: %w", filename, err)
}
return pool, nil
}
func NewCertPoolReloader(certFile string) (*CertPoolReloader, error) {
pool, err := loadCertPool(certFile)
if err != nil {
return nil, err
}
reloader := &CertPoolReloader{
certFile: certFile,
}
reloader.pool.Store(pool)
reloader.certWatcher, err = NewFileWatcher(certFile, reloader.reload)
if err != nil {
return nil, err
}
return reloader, nil
}
func (r *CertPoolReloader) Close() {
r.certWatcher.Close()
}
func (r *CertPoolReloader) reload(filename string) {
log.Printf("reloading certificate pool from %s", r.certFile)
pool, err := loadCertPool(r.certFile)
if err != nil {
log.Printf("could not load certificate pool: %s", err)
return
}
r.pool.Store(pool)
r.reloadCounter.Add(1)
}
func (r *CertPoolReloader) GetCertPool() *x509.CertPool {
return r.pool.Load()
}
func (r *CertPoolReloader) GetReloadCounter() uint64 {
return r.reloadCounter.Load()
}

View file

@ -0,0 +1,62 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"testing"
"time"
)
func UpdateCertificateCheckIntervalForTest(t *testing.T, interval time.Duration) {
t.Helper()
// Make sure test is not executed with "t.Parallel()"
t.Setenv("PARALLEL_CHECK", "1")
old := deduplicateWatchEvents.Load()
t.Cleanup(func() {
deduplicateWatchEvents.Store(old)
})
deduplicateWatchEvents.Store(int64(interval))
}
func (r *CertificateReloader) WaitForReload(ctx context.Context) error {
counter := r.GetReloadCounter()
for counter == r.GetReloadCounter() {
if err := ctx.Err(); err != nil {
return err
}
time.Sleep(time.Millisecond)
}
return nil
}
func (r *CertPoolReloader) WaitForReload(ctx context.Context) error {
counter := r.GetReloadCounter()
for counter == r.GetReloadCounter() {
if err := ctx.Err(); err != nil {
return err
}
time.Sleep(time.Millisecond)
}
return nil
}

62
channel_waiter.go Normal file
View file

@ -0,0 +1,62 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"sync"
)
type ChannelWaiters struct {
mu sync.RWMutex
id uint64
waiters map[uint64]chan struct{}
}
func (w *ChannelWaiters) Wakeup() {
w.mu.RLock()
defer w.mu.RUnlock()
for _, ch := range w.waiters {
select {
case ch <- struct{}{}:
default:
// Receiver is still processing previous wakeup.
}
}
}
func (w *ChannelWaiters) Add(ch chan struct{}) uint64 {
w.mu.Lock()
defer w.mu.Unlock()
if w.waiters == nil {
w.waiters = make(map[uint64]chan struct{})
}
id := w.id
w.id++
w.waiters[id] = ch
return id
}
func (w *ChannelWaiters) Remove(id uint64) {
w.mu.Lock()
defer w.mu.Unlock()
delete(w.waiters, id)
}

66
channel_waiter_test.go Normal file
View file

@ -0,0 +1,66 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"testing"
)
func TestChannelWaiters(t *testing.T) {
var waiters ChannelWaiters
ch1 := make(chan struct{}, 1)
id1 := waiters.Add(ch1)
defer waiters.Remove(id1)
ch2 := make(chan struct{}, 1)
id2 := waiters.Add(ch2)
defer waiters.Remove(id2)
waiters.Wakeup()
<-ch1
<-ch2
select {
case <-ch1:
t.Error("should have not received another event")
case <-ch2:
t.Error("should have not received another event")
default:
}
ch3 := make(chan struct{}, 1)
id3 := waiters.Add(ch3)
waiters.Remove(id3)
// Multiple wakeups work even without processing.
waiters.Wakeup()
waiters.Wakeup()
waiters.Wakeup()
<-ch1
<-ch2
select {
case <-ch3:
t.Error("should have not received another event")
default:
}
}

250
client.go
View file

@ -23,14 +23,16 @@ package signaling
import (
"bytes"
"context"
"encoding/json"
"errors"
"log"
"net"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"unsafe"
"github.com/gorilla/websocket"
"github.com/mailru/easyjson"
@ -93,30 +95,59 @@ type WritableClientMessage interface {
CloseAfterSend(session Session) bool
}
type HandlerClient interface {
Context() context.Context
RemoteAddr() string
Country() string
UserAgent() string
IsConnected() bool
IsAuthenticated() bool
GetSession() Session
SetSession(session Session)
SendError(e *Error) bool
SendByeResponse(message *ClientMessage) bool
SendByeResponseWithReason(message *ClientMessage, reason string) bool
SendMessage(message WritableClientMessage) bool
Close()
}
type ClientHandler interface {
OnClosed(HandlerClient)
OnMessageReceived(HandlerClient, []byte)
OnRTTReceived(HandlerClient, time.Duration)
}
type ClientGeoIpHandler interface {
OnLookupCountry(HandlerClient) string
}
type Client struct {
ctx context.Context
conn *websocket.Conn
addr string
agent string
closed uint32
closed atomic.Int32
country *string
logRTT bool
session unsafe.Pointer
handlerMu sync.RWMutex
handler ClientHandler
session atomic.Pointer[Session]
sessionId atomic.Pointer[string]
mu sync.Mutex
closeChan chan bool
messagesDone sync.WaitGroup
messageChan chan *bytes.Buffer
messageProcessing uint32
OnLookupCountry func(*Client) string
OnClosed func(*Client)
OnMessageReceived func(*Client, []byte)
OnRTTReceived func(*Client, time.Duration)
closer *Closer
closeOnce sync.Once
messagesDone chan struct{}
messageChan chan *bytes.Buffer
}
func NewClient(conn *websocket.Conn, remoteAddress string, agent string) (*Client, error) {
func NewClient(ctx context.Context, conn *websocket.Conn, remoteAddress string, agent string, handler ClientHandler) (*Client, error) {
remoteAddress = strings.TrimSpace(remoteAddress)
if remoteAddress == "" {
remoteAddress = "unknown remote address"
@ -125,47 +156,82 @@ func NewClient(conn *websocket.Conn, remoteAddress string, agent string) (*Clien
if agent == "" {
agent = "unknown user agent"
}
client := &Client{
conn: conn,
addr: remoteAddress,
ctx: ctx,
agent: agent,
logRTT: true,
closeChan: make(chan bool, 1),
messageChan: make(chan *bytes.Buffer, 16),
OnLookupCountry: func(client *Client) string { return unknownCountry },
OnClosed: func(client *Client) {},
OnMessageReceived: func(client *Client, data []byte) {},
OnRTTReceived: func(client *Client, rtt time.Duration) {},
}
client.SetConn(conn, remoteAddress, handler)
return client, nil
}
func (c *Client) SetConn(conn *websocket.Conn, remoteAddress string) {
func (c *Client) SetConn(conn *websocket.Conn, remoteAddress string, handler ClientHandler) {
c.conn = conn
c.addr = remoteAddress
c.closeChan = make(chan bool, 1)
c.SetHandler(handler)
c.closer = NewCloser()
c.messageChan = make(chan *bytes.Buffer, 16)
c.OnLookupCountry = func(client *Client) string { return unknownCountry }
c.OnClosed = func(client *Client) {}
c.OnMessageReceived = func(client *Client, data []byte) {}
c.messagesDone = make(chan struct{})
}
func (c *Client) SetHandler(handler ClientHandler) {
c.handlerMu.Lock()
defer c.handlerMu.Unlock()
c.handler = handler
}
func (c *Client) getHandler() ClientHandler {
c.handlerMu.RLock()
defer c.handlerMu.RUnlock()
return c.handler
}
func (c *Client) Context() context.Context {
return c.ctx
}
func (c *Client) IsConnected() bool {
return atomic.LoadUint32(&c.closed) == 0
return c.closed.Load() == 0
}
func (c *Client) IsAuthenticated() bool {
return c.GetSession() != nil
}
func (c *Client) GetSession() *ClientSession {
return (*ClientSession)(atomic.LoadPointer(&c.session))
func (c *Client) GetSession() Session {
session := c.session.Load()
if session == nil {
return nil
}
return *session
}
func (c *Client) SetSession(session *ClientSession) {
atomic.StorePointer(&c.session, unsafe.Pointer(session))
func (c *Client) SetSession(session Session) {
if session == nil {
c.session.Store(nil)
} else {
c.session.Store(&session)
}
}
func (c *Client) SetSessionId(sessionId string) {
c.sessionId.Store(&sessionId)
}
func (c *Client) GetSessionId() string {
sessionId := c.sessionId.Load()
if sessionId == nil {
session := c.GetSession()
if session == nil {
return ""
}
return session.PublicId()
}
return *sessionId
}
func (c *Client) RemoteAddr() string {
@ -178,7 +244,12 @@ func (c *Client) UserAgent() string {
func (c *Client) Country() string {
if c.country == nil {
country := c.OnLookupCountry(c)
var country string
if handler, ok := c.getHandler().(ClientGeoIpHandler); ok {
country = handler.OnLookupCountry(c)
} else {
country = unknownCountry
}
c.country = &country
}
@ -186,38 +257,36 @@ func (c *Client) Country() string {
}
func (c *Client) Close() {
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
if c.closed.Load() >= 2 {
// Prevent reentrant call in case this was the second closing
// step. Would otherwise deadlock in the "Once.Do" call path
// through "Hub.processUnregister" (which calls "Close" again).
return
}
c.mu.Lock()
if c.conn != nil {
c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) // nolint
}
c.mu.Unlock()
if atomic.LoadUint32(&c.messageProcessing) == 1 {
// Defer closing
atomic.StoreUint32(&c.closed, 2)
return
}
c.doClose()
c.closeOnce.Do(func() {
c.doClose()
})
}
func (c *Client) doClose() {
c.closeChan <- true
c.messagesDone.Wait()
closed := c.closed.Add(1)
if closed == 1 {
c.mu.Lock()
defer c.mu.Unlock()
if c.conn != nil {
c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) // nolint
c.conn.Close()
c.conn = nil
}
} else if closed == 2 {
// Both the read pump and message processing must be finished before closing.
c.closer.Close()
<-c.messagesDone
c.OnClosed(c)
c.SetSession(nil)
c.mu.Lock()
if c.conn != nil {
c.conn.Close()
c.conn = nil
c.getHandler().OnClosed(c)
c.SetSession(nil)
}
c.mu.Unlock()
}
func (c *Client) SendError(e *Error) bool {
@ -235,12 +304,14 @@ func (c *Client) SendByeResponse(message *ClientMessage) bool {
func (c *Client) SendByeResponseWithReason(message *ClientMessage, reason string) bool {
response := &ServerMessage{
Type: "bye",
Bye: &ByeServerMessage{},
}
if message != nil {
response.Id = message.Id
}
if reason != "" {
if response.Bye == nil {
response.Bye = &ByeServerMessage{}
}
response.Bye.Reason = reason
}
return c.SendMessage(response)
@ -252,10 +323,12 @@ func (c *Client) SendMessage(message WritableClientMessage) bool {
func (c *Client) ReadPump() {
defer func() {
c.Close()
close(c.messageChan)
c.Close()
}()
go c.processMessages()
addr := c.RemoteAddr()
c.mu.Lock()
conn := c.conn
@ -276,29 +349,30 @@ func (c *Client) ReadPump() {
rtt := now.Sub(time.Unix(0, ts))
if c.logRTT {
rtt_ms := rtt.Nanoseconds() / time.Millisecond.Nanoseconds()
if session := c.GetSession(); session != nil {
log.Printf("Client %s has RTT of %d ms (%s)", session.PublicId(), rtt_ms, rtt)
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Client %s has RTT of %d ms (%s)", sessionId, rtt_ms, rtt)
} else {
log.Printf("Client from %s has RTT of %d ms (%s)", addr, rtt_ms, rtt)
}
}
c.OnRTTReceived(c, rtt)
c.getHandler().OnRTTReceived(c, rtt)
}
return nil
})
go c.processMessages()
for {
conn.SetReadDeadline(time.Now().Add(pongWait)) // nolint
messageType, reader, err := conn.NextReader()
if err != nil {
if _, ok := err.(*websocket.CloseError); !ok || websocket.IsUnexpectedCloseError(err,
// Gorilla websocket hides the original net.Error, so also compare error messages
if errors.Is(err, net.ErrClosed) || strings.Contains(err.Error(), net.ErrClosed.Error()) {
break
} else if _, ok := err.(*websocket.CloseError); !ok || websocket.IsUnexpectedCloseError(err,
websocket.CloseNormalClosure,
websocket.CloseGoingAway,
websocket.CloseNoStatusReceived) {
if session := c.GetSession(); session != nil {
log.Printf("Error reading from client %s: %v", session.PublicId(), err)
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Error reading from client %s: %v", sessionId, err)
} else {
log.Printf("Error reading from %s: %v", addr, err)
}
@ -307,8 +381,8 @@ func (c *Client) ReadPump() {
}
if messageType != websocket.TextMessage {
if session := c.GetSession(); session != nil {
log.Printf("Unsupported message type %v from client %s", messageType, session.PublicId())
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Unsupported message type %v from client %s", messageType, sessionId)
} else {
log.Printf("Unsupported message type %v from %s", messageType, addr)
}
@ -320,8 +394,8 @@ func (c *Client) ReadPump() {
decodeBuffer.Reset()
if _, err := decodeBuffer.ReadFrom(reader); err != nil {
bufferPool.Put(decodeBuffer)
if session := c.GetSession(); session != nil {
log.Printf("Error reading message from client %s: %v", session.PublicId(), err)
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Error reading message from client %s: %v", sessionId, err)
} else {
log.Printf("Error reading message from %s: %v", addr, err)
}
@ -329,12 +403,11 @@ func (c *Client) ReadPump() {
}
// Stop processing if the client was closed.
if atomic.LoadUint32(&c.closed) != 0 {
if !c.IsConnected() {
bufferPool.Put(decodeBuffer)
break
}
c.messagesDone.Add(1)
c.messageChan <- decodeBuffer
}
}
@ -346,16 +419,12 @@ func (c *Client) processMessages() {
break
}
atomic.StoreUint32(&c.messageProcessing, 1)
c.OnMessageReceived(c, buffer.Bytes())
atomic.StoreUint32(&c.messageProcessing, 0)
c.messagesDone.Done()
c.getHandler().OnMessageReceived(c, buffer.Bytes())
bufferPool.Put(buffer)
}
if atomic.LoadUint32(&c.closed) == 2 {
c.doClose()
}
close(c.messagesDone)
c.doClose()
}
func (c *Client) writeInternal(message json.Marshaler) bool {
@ -379,8 +448,8 @@ func (c *Client) writeInternal(message json.Marshaler) bool {
return false
}
if session := c.GetSession(); session != nil {
log.Printf("Could not send message %+v to client %s: %v", message, session.PublicId(), err)
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send message %+v to client %s: %v", message, sessionId, err)
} else {
log.Printf("Could not send message %+v to %s: %v", message, c.RemoteAddr(), err)
}
@ -392,8 +461,8 @@ func (c *Client) writeInternal(message json.Marshaler) bool {
close:
c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint
if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil {
if session := c.GetSession(); session != nil {
log.Printf("Could not send close message to client %s: %v", session.PublicId(), err)
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send close message to client %s: %v", sessionId, err)
} else {
log.Printf("Could not send close message to %s: %v", c.RemoteAddr(), err)
}
@ -419,8 +488,8 @@ func (c *Client) writeError(e error) bool { // nolint
closeData := websocket.FormatCloseMessage(websocket.CloseInternalServerErr, e.Error())
c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint
if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil {
if session := c.GetSession(); session != nil {
log.Printf("Could not send close message to client %s: %v", session.PublicId(), err)
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send close message to client %s: %v", sessionId, err)
} else {
log.Printf("Could not send close message to %s: %v", c.RemoteAddr(), err)
}
@ -451,7 +520,6 @@ func (c *Client) writeMessageLocked(message WritableClientMessage) bool {
go session.Close()
}
go c.Close()
return false
}
return true
@ -468,8 +536,8 @@ func (c *Client) sendPing() bool {
msg := strconv.FormatInt(now, 10)
c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint
if err := c.conn.WriteMessage(websocket.PingMessage, []byte(msg)); err != nil {
if session := c.GetSession(); session != nil {
log.Printf("Could not send ping to client %s: %v", session.PublicId(), err)
if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send ping to client %s: %v", sessionId, err)
} else {
log.Printf("Could not send ping to %s: %v", c.RemoteAddr(), err)
}
@ -493,7 +561,7 @@ func (c *Client) WritePump() {
if !c.sendPing() {
return
}
case <-c.closeChan:
case <-c.closer.C:
return
}
}

View file

@ -81,8 +81,8 @@ const (
)
type Stats struct {
numRecvMessages uint64
numSentMessages uint64
numRecvMessages atomic.Uint64
numSentMessages atomic.Uint64
resetRecvMessages uint64
resetSentMessages uint64
@ -90,8 +90,8 @@ type Stats struct {
}
func (s *Stats) reset(start time.Time) {
s.resetRecvMessages = atomic.AddUint64(&s.numRecvMessages, 0)
s.resetSentMessages = atomic.AddUint64(&s.numSentMessages, 0)
s.resetRecvMessages = s.numRecvMessages.Load()
s.resetSentMessages = s.numSentMessages.Load()
s.start = start
}
@ -103,9 +103,9 @@ func (s *Stats) Log() {
return
}
totalSentMessages := atomic.AddUint64(&s.numSentMessages, 0)
totalSentMessages := s.numSentMessages.Load()
sentMessages := totalSentMessages - s.resetSentMessages
totalRecvMessages := atomic.AddUint64(&s.numRecvMessages, 0)
totalRecvMessages := s.numRecvMessages.Load()
recvMessages := totalRecvMessages - s.resetRecvMessages
log.Printf("Stats: sent=%d (%d/sec), recv=%d (%d/sec), delta=%d",
totalSentMessages, sentMessages/perSec,
@ -125,9 +125,9 @@ type SignalingClient struct {
conn *websocket.Conn
stats *Stats
closed uint32
closed atomic.Bool
stopChan chan bool
stopChan chan struct{}
lock sync.Mutex
privateSessionId string
@ -149,7 +149,7 @@ func NewSignalingClient(cookie *securecookie.SecureCookie, url string, stats *St
stats: stats,
stopChan: make(chan bool),
stopChan: make(chan struct{}),
}
doneWg.Add(2)
go func() {
@ -164,15 +164,12 @@ func NewSignalingClient(cookie *securecookie.SecureCookie, url string, stats *St
}
func (c *SignalingClient) Close() {
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
if !c.closed.CompareAndSwap(false, true) {
return
}
// Signal writepump to terminate
select {
case c.stopChan <- true:
default:
}
close(c.stopChan)
c.lock.Lock()
c.publicSessionId = ""
@ -200,7 +197,7 @@ func (c *SignalingClient) Send(message *signaling.ClientMessage) {
}
func (c *SignalingClient) processMessage(message *signaling.ServerMessage) {
atomic.AddUint64(&c.stats.numRecvMessages, 1)
c.stats.numRecvMessages.Add(1)
switch message.Type {
case "hello":
c.processHelloMessage(message)
@ -251,7 +248,7 @@ func (c *SignalingClient) PublicSessionId() string {
func (c *SignalingClient) processMessageMessage(message *signaling.ServerMessage) {
var msg MessagePayload
if err := json.Unmarshal(*message.Message.Data, &msg); err != nil {
if err := json.Unmarshal(message.Message.Data, &msg); err != nil {
log.Println("Error in unmarshal", err)
return
}
@ -337,7 +334,7 @@ func (c *SignalingClient) writeInternal(message *signaling.ClientMessage) bool {
}
writer.Close()
atomic.AddUint64(&c.stats.numSentMessages, 1)
c.stats.numSentMessages.Add(1)
return true
close:
@ -386,7 +383,7 @@ func (c *SignalingClient) SendMessages(clients []*SignalingClient) {
sessionIds[c] = c.PublicSessionId()
}
for atomic.LoadUint32(&c.closed) == 0 {
for !c.closed.Load() {
now := time.Now()
sender := c
@ -407,7 +404,7 @@ func (c *SignalingClient) SendMessages(clients []*SignalingClient) {
Type: "session",
SessionId: sessionIds[recipient],
},
Data: (*json.RawMessage)(&data),
Data: data,
},
}
sender.Send(msg)
@ -464,7 +461,7 @@ func registerAuthHandler(router *mux.Router) {
StatusCode: http.StatusOK,
Message: http.StatusText(http.StatusOK),
},
Data: &rawdata,
Data: rawdata,
},
}
@ -603,10 +600,10 @@ func main() {
request := &signaling.ClientMessage{
Type: "hello",
Hello: &signaling.HelloClientMessage{
Version: signaling.HelloVersion,
Auth: signaling.HelloClientMessageAuth{
Version: signaling.HelloVersionV1,
Auth: &signaling.HelloClientMessageAuth{
Url: backendUrl + "/auth",
Params: &json.RawMessage{'{', '}'},
Params: json.RawMessage("{}"),
},
},
}

File diff suppressed because it is too large Load diff

View file

@ -117,6 +117,7 @@ func Test_permissionsEqual(t *testing.T) {
for idx, test := range tests {
test := test
t.Run(strconv.Itoa(idx), func(t *testing.T) {
t.Parallel()
equal := permissionsEqual(test.a, test.b)
if equal != test.equal {
t.Errorf("Expected %+v to be %s to %+v but was %s", test.a, equalStrings[test.equal], test.b, equalStrings[equal])
@ -126,12 +127,17 @@ func Test_permissionsEqual(t *testing.T) {
}
func TestBandwidth_Client(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
hub, _, _, server := CreateHubForTest(t)
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
mcu, err := NewTestMCU()
if err != nil {
t.Fatal(err)
} else if err := mcu.Start(); err != nil {
} else if err := mcu.Start(ctx); err != nil {
t.Fatal(err)
}
defer mcu.Stop()
@ -145,9 +151,6 @@ func TestBandwidth_Client(t *testing.T) {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
hello, err := client.RunUntilHello(ctx)
if err != nil {
t.Fatal(err)
@ -198,6 +201,8 @@ func TestBandwidth_Client(t *testing.T) {
}
func TestBandwidth_Backend(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
hub, _, _, server := CreateHubWithMultipleBackendsForTest(t)
u, err := url.Parse(server.URL + "/one")
@ -212,33 +217,33 @@ func TestBandwidth_Backend(t *testing.T) {
backend.maxScreenBitrate = 1000
backend.maxStreamBitrate = 2000
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
mcu, err := NewTestMCU()
if err != nil {
t.Fatal(err)
} else if err := mcu.Start(); err != nil {
} else if err := mcu.Start(ctx); err != nil {
t.Fatal(err)
}
defer mcu.Stop()
hub.SetMcu(mcu)
streamTypes := []string{
streamTypeVideo,
streamTypeScreen,
streamTypes := []StreamType{
StreamTypeVideo,
StreamTypeScreen,
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
for _, streamType := range streamTypes {
t.Run(streamType, func(t *testing.T) {
t.Run(string(streamType), func(t *testing.T) {
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
params := TestBackendClientAuthParams{
UserId: testDefaultUserId,
}
if err := client.SendHelloParams(server.URL+"/one", "client", params); err != nil {
if err := client.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", nil, params); err != nil {
t.Fatal(err)
}
@ -268,7 +273,7 @@ func TestBandwidth_Backend(t *testing.T) {
}, MessageClientMessageData{
Type: "offer",
Sid: "54321",
RoomType: streamType,
RoomType: string(streamType),
Bitrate: bitrate,
Payload: map[string]interface{}{
"sdp": MockSdpOfferAudioAndVideo,
@ -287,7 +292,7 @@ func TestBandwidth_Backend(t *testing.T) {
}
var expectBitrate int
if streamType == streamTypeVideo {
if streamType == StreamTypeVideo {
expectBitrate = backend.maxStreamBitrate
} else {
expectBitrate = backend.maxScreenBitrate

47
closer.go Normal file
View file

@ -0,0 +1,47 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"sync/atomic"
)
type Closer struct {
closed atomic.Bool
C chan struct{}
}
func NewCloser() *Closer {
return &Closer{
C: make(chan struct{}),
}
}
func (c *Closer) IsClosed() bool {
return c.closed.Load()
}
func (c *Closer) Close() {
if c.closed.CompareAndSwap(false, true) {
close(c.C)
}
}

62
closer_test.go Normal file
View file

@ -0,0 +1,62 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"sync"
"testing"
)
func TestCloserMulti(t *testing.T) {
closer := NewCloser()
var wg sync.WaitGroup
count := 10
for i := 0; i < count; i++ {
wg.Add(1)
go func() {
defer wg.Done()
<-closer.C
}()
}
if closer.IsClosed() {
t.Error("should not be closed")
}
closer.Close()
if !closer.IsClosed() {
t.Error("should be closed")
}
wg.Wait()
}
func TestCloserCloseBeforeWait(t *testing.T) {
closer := NewCloser()
closer.Close()
if !closer.IsClosed() {
t.Error("should be closed")
}
<-closer.C
if !closer.IsClosed() {
t.Error("should be closed")
}
}

87
config.go Normal file
View file

@ -0,0 +1,87 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"errors"
"os"
"regexp"
"github.com/dlintw/goconf"
)
var (
searchVarsRegexp = regexp.MustCompile(`\$\([A-Za-z][A-Za-z0-9_]*\)`)
)
func replaceEnvVars(s string) string {
return searchVarsRegexp.ReplaceAllStringFunc(s, func(name string) string {
name = name[2 : len(name)-1]
value, found := os.LookupEnv(name)
if !found {
return name
}
return value
})
}
// GetStringOptionWithEnv will get the string option and resolve any environment
// variable references in the form "$(VAR)".
func GetStringOptionWithEnv(config *goconf.ConfigFile, section string, option string) (string, error) {
value, err := config.GetString(section, option)
if err != nil {
return "", err
}
value = replaceEnvVars(value)
return value, nil
}
func GetStringOptions(config *goconf.ConfigFile, section string, ignoreErrors bool) (map[string]string, error) {
options, _ := config.GetOptions(section)
if len(options) == 0 {
return nil, nil
}
result := make(map[string]string)
for _, option := range options {
value, err := GetStringOptionWithEnv(config, section, option)
if err != nil {
if ignoreErrors {
continue
}
var ge goconf.GetError
if errors.As(err, &ge) && ge.Reason == goconf.OptionNotFound {
// Skip options from "default" section.
continue
}
return nil, err
}
result[option] = value
}
return result, nil
}

92
config_test.go Normal file
View file

@ -0,0 +1,92 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"reflect"
"testing"
"github.com/dlintw/goconf"
)
func TestStringOptions(t *testing.T) {
t.Setenv("FOO", "foo")
expected := map[string]string{
"one": "1",
"two": "2",
"foo": "http://foo/1",
}
config := goconf.NewConfigFile()
for k, v := range expected {
if k == "foo" {
config.AddOption("foo", k, "http://$(FOO)/1")
} else {
config.AddOption("foo", k, v)
}
}
config.AddOption("default", "three", "3")
options, err := GetStringOptions(config, "foo", false)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(expected, options) {
t.Errorf("expected %+v, got %+v", expected, options)
}
}
func TestStringOptionWithEnv(t *testing.T) {
t.Setenv("FOO", "foo")
t.Setenv("BAR", "")
t.Setenv("BA_R", "bar")
config := goconf.NewConfigFile()
config.AddOption("test", "foo", "http://$(FOO)/1")
config.AddOption("test", "bar", "http://$(BAR)/2")
config.AddOption("test", "bar2", "http://$(BA_R)/3")
config.AddOption("test", "baz", "http://$(BAZ)/4")
config.AddOption("test", "inv1", "http://$(FOO")
config.AddOption("test", "inv2", "http://$FOO)")
config.AddOption("test", "inv3", "http://$((FOO)")
config.AddOption("test", "inv4", "http://$(F.OO)")
expected := map[string]string{
"foo": "http://foo/1",
"bar": "http:///2",
"bar2": "http://bar/3",
"baz": "http://BAZ/4",
"inv1": "http://$(FOO",
"inv2": "http://$FOO)",
"inv3": "http://$((FOO)",
"inv4": "http://$(F.OO)",
}
for k, v := range expected {
value, err := GetStringOptionWithEnv(config, "test", k)
if err != nil {
t.Errorf("expected value for %s, got %s", k, err)
} else if value != v {
t.Errorf("expected value %s for %s, got %s", v, k, value)
}
}
}

View file

@ -1,7 +1,7 @@
package signaling
// This file has been automatically generated, do not modify.
// Source: https://datahub.io/core/country-codes/r/country-codes.json
// Source: https://github.com/datasets/country-codes/raw/master/data/country-codes.csv
var (
ContinentMap = map[string][]string{

View file

@ -33,8 +33,7 @@ import (
// their order.
type DeferredExecutor struct {
queue chan func()
closeChan chan bool
closed chan bool
closed chan struct{}
closeOnce sync.Once
}
@ -43,28 +42,24 @@ func NewDeferredExecutor(queueSize int) *DeferredExecutor {
queueSize = 0
}
result := &DeferredExecutor{
queue: make(chan func(), queueSize),
closeChan: make(chan bool, 1),
closed: make(chan bool, 1),
queue: make(chan func(), queueSize),
closed: make(chan struct{}),
}
go result.run()
return result
}
func (e *DeferredExecutor) run() {
loop:
defer close(e.closed)
for {
select {
case f := <-e.queue:
if f == nil {
break loop
}
f()
case <-e.closeChan:
break loop
f := <-e.queue
if f == nil {
break
}
f()
}
e.closed <- true
}
func getFunctionName(i interface{}) string {
@ -83,14 +78,9 @@ func (e *DeferredExecutor) Execute(f func()) {
}
func (e *DeferredExecutor) Close() {
select {
case e.closeChan <- true:
e.closeOnce.Do(func() {
close(e.queue)
})
default:
// Already closed.
}
e.closeOnce.Do(func() {
close(e.queue)
})
}
func (e *DeferredExecutor) waitForStop() {

View file

@ -35,6 +35,7 @@ func TestDeferredExecutor_MultiClose(t *testing.T) {
}
func TestDeferredExecutor_QueueSize(t *testing.T) {
t.Parallel()
e := NewDeferredExecutor(0)
defer e.waitForStop()
defer e.Close()
@ -69,13 +70,13 @@ func TestDeferredExecutor_Order(t *testing.T) {
}
}
done := make(chan bool)
done := make(chan struct{})
for x := 0; x < 10; x++ {
e.Execute(getFunc(x))
}
e.Execute(func() {
done <- true
close(done)
})
<-done
@ -90,16 +91,17 @@ func TestDeferredExecutor_CloseFromFunc(t *testing.T) {
e := NewDeferredExecutor(64)
defer e.waitForStop()
done := make(chan bool)
done := make(chan struct{})
e.Execute(func() {
defer close(done)
e.Close()
done <- true
})
<-done
}
func TestDeferredExecutor_DeferAfterClose(t *testing.T) {
CatchLogForTest(t)
e := NewDeferredExecutor(64)
defer e.waitForStop()
@ -109,3 +111,12 @@ func TestDeferredExecutor_DeferAfterClose(t *testing.T) {
t.Error("method should not have been called")
})
}
func TestDeferredExecutor_WaitForStopTwice(t *testing.T) {
e := NewDeferredExecutor(64)
defer e.waitForStop()
e.Close()
e.waitForStop()
}

View file

@ -7,5 +7,37 @@ User=signaling
Group=signaling
Restart=on-failure
# Makes sure that /etc/signaling is owned by this service
ConfigurationDirectory=signaling
# Hardening - see systemd.exec(5)
CapabilityBoundingSet=
ExecPaths=/usr/bin/signaling /usr/lib
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoExecPaths=/
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
PrivateUsers=yes
ProcSubset=pid
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~ @privileged
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
u signaling - "nextcloud-spreed-signaling user"

343
dns_monitor.go Normal file
View file

@ -0,0 +1,343 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"log"
"net"
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
)
var (
lookupDnsMonitorIP = net.LookupIP
)
const (
defaultDnsMonitorInterval = time.Second
)
type DnsMonitorCallback = func(entry *DnsMonitorEntry, all []net.IP, add []net.IP, keep []net.IP, remove []net.IP)
type DnsMonitorEntry struct {
entry atomic.Pointer[dnsMonitorEntry]
url string
callback DnsMonitorCallback
}
func (e *DnsMonitorEntry) URL() string {
return e.url
}
type dnsMonitorEntry struct {
hostname string
hostIP net.IP
mu sync.Mutex
ips []net.IP
entries map[*DnsMonitorEntry]bool
}
func (e *dnsMonitorEntry) setIPs(ips []net.IP, fromIP bool) {
e.mu.Lock()
defer e.mu.Unlock()
empty := len(e.ips) == 0
if empty {
// Simple case: initial lookup.
if len(ips) > 0 {
e.ips = ips
e.runCallbacks(ips, ips, nil, nil)
}
return
} else if fromIP {
// No more updates possible for IP addresses.
return
} else if len(ips) == 0 {
// Simple case: no records received from lookup.
if !empty {
removed := e.ips
e.ips = nil
e.runCallbacks(nil, nil, nil, removed)
}
return
}
var newIPs []net.IP
var addedIPs []net.IP
var removedIPs []net.IP
var keepIPs []net.IP
for _, oldIP := range e.ips {
found := false
for idx, newIP := range ips {
if oldIP.Equal(newIP) {
ips = append(ips[:idx], ips[idx+1:]...)
found = true
keepIPs = append(keepIPs, oldIP)
newIPs = append(newIPs, oldIP)
break
}
}
if !found {
removedIPs = append(removedIPs, oldIP)
}
}
if len(ips) > 0 {
addedIPs = append(addedIPs, ips...)
newIPs = append(newIPs, ips...)
}
e.ips = newIPs
if len(addedIPs) > 0 || len(removedIPs) > 0 {
e.runCallbacks(newIPs, addedIPs, keepIPs, removedIPs)
}
}
func (e *dnsMonitorEntry) addEntry(entry *DnsMonitorEntry) {
e.mu.Lock()
defer e.mu.Unlock()
e.entries[entry] = true
}
func (e *dnsMonitorEntry) removeEntry(entry *DnsMonitorEntry) bool {
e.mu.Lock()
defer e.mu.Unlock()
delete(e.entries, entry)
return len(e.entries) == 0
}
func (e *dnsMonitorEntry) runCallbacks(all []net.IP, add []net.IP, keep []net.IP, remove []net.IP) {
for entry := range e.entries {
entry.callback(entry, all, add, keep, remove)
}
}
type DnsMonitor struct {
interval time.Duration
stopCtx context.Context
stopFunc func()
stopped chan struct{}
mu sync.RWMutex
cond *sync.Cond
hostnames map[string]*dnsMonitorEntry
hasRemoved atomic.Bool
// Can be overwritten from tests.
checkHostnames func()
}
func NewDnsMonitor(interval time.Duration) (*DnsMonitor, error) {
if interval < 0 {
interval = defaultDnsMonitorInterval
}
stopCtx, stopFunc := context.WithCancel(context.Background())
monitor := &DnsMonitor{
interval: interval,
stopCtx: stopCtx,
stopFunc: stopFunc,
stopped: make(chan struct{}),
hostnames: make(map[string]*dnsMonitorEntry),
}
monitor.cond = sync.NewCond(&monitor.mu)
monitor.checkHostnames = monitor.doCheckHostnames
return monitor, nil
}
func (m *DnsMonitor) Start() error {
go m.run()
return nil
}
func (m *DnsMonitor) Stop() {
m.stopFunc()
m.cond.Signal()
<-m.stopped
}
func (m *DnsMonitor) Add(target string, callback DnsMonitorCallback) (*DnsMonitorEntry, error) {
var hostname string
if strings.Contains(target, "://") {
// Full URL passed.
parsed, err := url.Parse(target)
if err != nil {
return nil, err
}
hostname = parsed.Host
} else {
// Hostname only passed.
hostname = target
}
if h, _, err := net.SplitHostPort(hostname); err == nil {
hostname = h
}
m.mu.Lock()
defer m.mu.Unlock()
e := &DnsMonitorEntry{
url: target,
callback: callback,
}
entry, found := m.hostnames[hostname]
if !found {
entry = &dnsMonitorEntry{
hostname: hostname,
hostIP: net.ParseIP(hostname),
entries: make(map[*DnsMonitorEntry]bool),
}
m.hostnames[hostname] = entry
}
e.entry.Store(entry)
entry.addEntry(e)
m.cond.Signal()
return e, nil
}
func (m *DnsMonitor) Remove(entry *DnsMonitorEntry) {
oldEntry := entry.entry.Swap(nil)
if oldEntry == nil {
// Already removed.
return
}
locked := m.mu.TryLock()
// Spin-lock for simple cases that resolve immediately to avoid deferred removal.
for i := 0; !locked && i < 1000; i++ {
time.Sleep(time.Nanosecond)
locked = m.mu.TryLock()
}
if !locked {
// Currently processing callbacks for this entry, need to defer removal.
m.hasRemoved.Store(true)
return
}
defer m.mu.Unlock()
e, found := m.hostnames[oldEntry.hostname]
if !found {
return
}
if e.removeEntry(entry) {
delete(m.hostnames, e.hostname)
}
}
func (m *DnsMonitor) clearRemoved() {
if !m.hasRemoved.CompareAndSwap(true, false) {
return
}
m.mu.Lock()
defer m.mu.Unlock()
for hostname, entry := range m.hostnames {
deleted := false
for e := range entry.entries {
if e.entry.Load() == nil {
delete(entry.entries, e)
deleted = true
}
}
if deleted && len(entry.entries) == 0 {
delete(m.hostnames, hostname)
}
}
}
func (m *DnsMonitor) waitForEntries() (waited bool) {
m.mu.Lock()
defer m.mu.Unlock()
for len(m.hostnames) == 0 && m.stopCtx.Err() == nil {
m.cond.Wait()
waited = true
}
return
}
func (m *DnsMonitor) run() {
ticker := time.NewTicker(m.interval)
defer ticker.Stop()
defer close(m.stopped)
for {
if m.waitForEntries() {
ticker.Reset(m.interval)
if m.stopCtx.Err() == nil {
// Initial check when a new entry was added. More checks will be
// triggered by the Ticker.
m.checkHostnames()
continue
}
}
select {
case <-m.stopCtx.Done():
return
case <-ticker.C:
m.checkHostnames()
}
}
}
func (m *DnsMonitor) doCheckHostnames() {
m.clearRemoved()
m.mu.RLock()
defer m.mu.RUnlock()
for _, entry := range m.hostnames {
m.checkHostname(entry)
}
}
func (m *DnsMonitor) checkHostname(entry *dnsMonitorEntry) {
if len(entry.hostIP) > 0 {
entry.setIPs([]net.IP{entry.hostIP}, true)
return
}
ips, err := lookupDnsMonitorIP(entry.hostname)
if err != nil {
log.Printf("Could not lookup %s: %s", entry.hostname, err)
return
}
entry.setIPs(ips, false)
}

428
dns_monitor_test.go Normal file
View file

@ -0,0 +1,428 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"fmt"
"net"
"reflect"
"sync"
"sync/atomic"
"testing"
"time"
)
type mockDnsLookup struct {
sync.RWMutex
ips map[string][]net.IP
}
func newMockDnsLookupForTest(t *testing.T) *mockDnsLookup {
mock := &mockDnsLookup{
ips: make(map[string][]net.IP),
}
prev := lookupDnsMonitorIP
t.Cleanup(func() {
lookupDnsMonitorIP = prev
})
lookupDnsMonitorIP = mock.lookup
return mock
}
func (m *mockDnsLookup) Set(host string, ips []net.IP) {
m.Lock()
defer m.Unlock()
m.ips[host] = ips
}
func (m *mockDnsLookup) Get(host string) []net.IP {
m.Lock()
defer m.Unlock()
return m.ips[host]
}
func (m *mockDnsLookup) lookup(host string) ([]net.IP, error) {
m.RLock()
defer m.RUnlock()
ips, found := m.ips[host]
if !found {
return nil, &net.DNSError{
Err: fmt.Sprintf("could not resolve %s", host),
Name: host,
IsNotFound: true,
}
}
return append([]net.IP{}, ips...), nil
}
func newDnsMonitorForTest(t *testing.T, interval time.Duration) *DnsMonitor {
t.Helper()
monitor, err := NewDnsMonitor(interval)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
monitor.Stop()
})
if err := monitor.Start(); err != nil {
t.Fatal(err)
}
return monitor
}
type dnsMonitorReceiverRecord struct {
all []net.IP
add []net.IP
keep []net.IP
remove []net.IP
}
func (r *dnsMonitorReceiverRecord) Equal(other *dnsMonitorReceiverRecord) bool {
return r == other || (reflect.DeepEqual(r.add, other.add) &&
reflect.DeepEqual(r.keep, other.keep) &&
reflect.DeepEqual(r.remove, other.remove))
}
func (r *dnsMonitorReceiverRecord) String() string {
return fmt.Sprintf("all=%v, add=%v, keep=%v, remove=%v", r.all, r.add, r.keep, r.remove)
}
var (
expectNone = &dnsMonitorReceiverRecord{}
)
type dnsMonitorReceiver struct {
sync.Mutex
t *testing.T
expected *dnsMonitorReceiverRecord
received *dnsMonitorReceiverRecord
}
func newDnsMonitorReceiverForTest(t *testing.T) *dnsMonitorReceiver {
return &dnsMonitorReceiver{
t: t,
}
}
func (r *dnsMonitorReceiver) OnLookup(entry *DnsMonitorEntry, all, add, keep, remove []net.IP) {
r.Lock()
defer r.Unlock()
received := &dnsMonitorReceiverRecord{
all: all,
add: add,
keep: keep,
remove: remove,
}
expected := r.expected
r.expected = nil
if expected == expectNone {
r.t.Errorf("expected no event, got %v", received)
return
}
if expected == nil {
if r.received != nil && !r.received.Equal(received) {
r.t.Errorf("already received %v, got %v", r.received, received)
}
return
}
if !expected.Equal(received) {
r.t.Errorf("expected %v, got %v", expected, received)
}
r.received = nil
r.expected = nil
}
func (r *dnsMonitorReceiver) WaitForExpected(ctx context.Context) {
r.t.Helper()
r.Lock()
defer r.Unlock()
ticker := time.NewTicker(time.Microsecond)
abort := false
for r.expected != nil && !abort {
r.Unlock()
select {
case <-ticker.C:
case <-ctx.Done():
r.t.Error(ctx.Err())
abort = true
}
r.Lock()
}
}
func (r *dnsMonitorReceiver) Expect(all, add, keep, remove []net.IP) {
r.t.Helper()
r.Lock()
defer r.Unlock()
if r.expected != nil && r.expected != expectNone {
r.t.Errorf("didn't get previously expected %v", r.expected)
}
expected := &dnsMonitorReceiverRecord{
all: all,
add: add,
keep: keep,
remove: remove,
}
if r.received != nil && r.received.Equal(expected) {
r.received = nil
return
}
r.expected = expected
}
func (r *dnsMonitorReceiver) ExpectNone() {
r.t.Helper()
r.Lock()
defer r.Unlock()
if r.expected != nil && r.expected != expectNone {
r.t.Errorf("didn't get previously expected %v", r.expected)
}
r.expected = expectNone
}
func TestDnsMonitor(t *testing.T) {
lookup := newMockDnsLookupForTest(t)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
interval := time.Millisecond
monitor := newDnsMonitorForTest(t, interval)
ip1 := net.ParseIP("192.168.0.1")
ip2 := net.ParseIP("192.168.1.1")
ip3 := net.ParseIP("10.1.2.3")
ips1 := []net.IP{
ip1,
ip2,
}
lookup.Set("foo", ips1)
rec1 := newDnsMonitorReceiverForTest(t)
rec1.Expect(ips1, ips1, nil, nil)
entry1, err := monitor.Add("https://foo:12345", rec1.OnLookup)
if err != nil {
t.Fatal(err)
}
defer monitor.Remove(entry1)
rec1.WaitForExpected(ctx)
ips2 := []net.IP{
ip1,
ip2,
ip3,
}
add2 := []net.IP{ip3}
keep2 := []net.IP{ip1, ip2}
rec1.Expect(ips2, add2, keep2, nil)
lookup.Set("foo", ips2)
rec1.WaitForExpected(ctx)
ips3 := []net.IP{
ip2,
ip3,
}
keep3 := []net.IP{ip2, ip3}
remove3 := []net.IP{ip1}
rec1.Expect(ips3, nil, keep3, remove3)
lookup.Set("foo", ips3)
rec1.WaitForExpected(ctx)
rec1.ExpectNone()
time.Sleep(5 * interval)
remove4 := []net.IP{ip2, ip3}
rec1.Expect(nil, nil, nil, remove4)
lookup.Set("foo", nil)
rec1.WaitForExpected(ctx)
rec1.ExpectNone()
time.Sleep(5 * interval)
// Removing multiple times is supported.
monitor.Remove(entry1)
monitor.Remove(entry1)
// No more events after removing.
lookup.Set("foo", ips1)
rec1.ExpectNone()
time.Sleep(5 * interval)
}
func TestDnsMonitorIP(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
interval := time.Millisecond
monitor := newDnsMonitorForTest(t, interval)
ip := "192.168.0.1"
ips := []net.IP{
net.ParseIP(ip),
}
rec1 := newDnsMonitorReceiverForTest(t)
rec1.Expect(ips, ips, nil, nil)
entry, err := monitor.Add(ip+":12345", rec1.OnLookup)
if err != nil {
t.Fatal(err)
}
defer monitor.Remove(entry)
rec1.WaitForExpected(ctx)
rec1.ExpectNone()
time.Sleep(5 * interval)
}
func TestDnsMonitorNoLookupIfEmpty(t *testing.T) {
interval := time.Millisecond
monitor := newDnsMonitorForTest(t, interval)
var checked atomic.Bool
monitor.checkHostnames = func() {
checked.Store(true)
monitor.doCheckHostnames()
}
time.Sleep(10 * interval)
if checked.Load() {
t.Error("should not have checked hostnames")
}
}
type deadlockMonitorReceiver struct {
t *testing.T
monitor *DnsMonitor
mu sync.RWMutex
wg sync.WaitGroup
entry *DnsMonitorEntry
started chan struct{}
triggered bool
closed atomic.Bool
}
func newDeadlockMonitorReceiver(t *testing.T, monitor *DnsMonitor) *deadlockMonitorReceiver {
return &deadlockMonitorReceiver{
t: t,
monitor: monitor,
started: make(chan struct{}),
}
}
func (r *deadlockMonitorReceiver) OnLookup(entry *DnsMonitorEntry, all []net.IP, add []net.IP, keep []net.IP, remove []net.IP) {
if r.closed.Load() {
r.t.Error("received lookup after closed")
return
}
r.mu.Lock()
defer r.mu.Unlock()
if r.triggered {
return
}
r.triggered = true
r.wg.Add(1)
go func() {
defer r.wg.Done()
r.mu.RLock()
defer r.mu.RUnlock()
close(r.started)
time.Sleep(50 * time.Millisecond)
}()
}
func (r *deadlockMonitorReceiver) Start() {
r.mu.Lock()
defer r.mu.Unlock()
entry, err := r.monitor.Add("foo", r.OnLookup)
if err != nil {
r.t.Errorf("error adding listener: %s", err)
return
}
r.entry = entry
}
func (r *deadlockMonitorReceiver) Close() {
r.mu.Lock()
defer r.mu.Unlock()
if r.entry != nil {
r.monitor.Remove(r.entry)
r.closed.Store(true)
}
r.wg.Wait()
}
func TestDnsMonitorDeadlock(t *testing.T) {
lookup := newMockDnsLookupForTest(t)
ip1 := net.ParseIP("192.168.0.1")
ip2 := net.ParseIP("192.168.0.2")
lookup.Set("foo", []net.IP{ip1})
interval := time.Millisecond
monitor := newDnsMonitorForTest(t, interval)
r := newDeadlockMonitorReceiver(t, monitor)
r.Start()
<-r.started
lookup.Set("foo", []net.IP{ip2})
r.Close()
lookup.Set("foo", []net.IP{ip1})
time.Sleep(10 * interval)
monitor.mu.Lock()
defer monitor.mu.Unlock()
if len(monitor.hostnames) > 0 {
t.Errorf("should have cleared hostnames, got %+v", monitor.hostnames)
}
}

134
docker/README.md Normal file
View file

@ -0,0 +1,134 @@
# Docker images for nextcloud-spreed-signaling
## Signaling server
The image for the signaling server can be retrieved from
strukturag/nextcloud-spreed-signaling:<version>
Replace `version` with the tag or commit you want to use.
### Configuration
The running container can be configured through different environment variables:
- `CONFIG`: Optional name of configuration file to use.
- `HTTP_LISTEN`: Address of HTTP listener.
- `HTTPS_LISTEN`: Address of HTTPS listener.
- `HTTPS_CERTIFICATE`: Name of certificate file for the HTTPS listener.
- `HTTPS_KEY`: Name of private key file for the HTTPS listener.
- `HASH_KEY`: Secret value used to generate checksums of sessions (32 or 64 bytes).
- `BLOCK_KEY`: Key for encrypting data in the sessions (16, 24 or 32 bytes).
- `INTERNAL_SHARED_SECRET_KEY`: Shared secret for connections from internal clients.
- `BACKENDS_ALLOWALL`: Allow all backends. Extremly insecure - use only for development!
- `BACKENDS_ALLOWALL_SECRET`: Secret when `BACKENDS_ALLOWALL` is enabled.
- `BACKENDS`: Space-separated list of backend ids.
- `BACKEND_<ID>_URL`: Url of backend `ID` (where `ID` is the uppercase backend id).
- `BACKEND_<ID>_SHARED_SECRET`: Shared secret for backend `ID` (where `ID` is the uppercase backend id).
- `BACKEND_<ID>_SESSION_LIMIT`: Optional session limit for backend `ID` (where `ID` is the uppercase backend id).
- `BACKEND_<ID>_MAX_STREAM_BITRATE`: Optional maximum bitrate for audio/video streams in backend `ID` (where `ID` is the uppercase backend id).
- `BACKEND_<ID>_MAX_SCREEN_BITRATE`: Optional maximum bitrate for screensharing streams in backend `ID` (where `ID` is the uppercase backend id).
- `NATS_URL`: Optional URL of NATS server.
- `ETCD_ENDPOINTS`: Static list of etcd endpoints (if etcd should be used).
- `ETCD_DISCOVERY_SRV`: Alternative domain to use for DNS SRV configuration of etcd endpoints (if etcd should be used).
- `ETCD_DISCOVERY_SERVICE`: Optional service name for DNS SRV configuration of etcd..
- `ETCD_CLIENT_CERTIFICATE`: Filename of certificate for etcd client.
- `ETCD_CLIENT_KEY`: Filename of private key for etcd client.
- `ETCD_CLIENT_CA`: Filename of CA for etcd client.
- `USE_JANUS`: Set to `1` if Janus should be used as WebRTC backend.
- `JANUS_URL`: Url to Janus server (if `USE_JANUS` is set to `1`).
- `USE_PROXY`: Set to `1` if proxy servers should be used as WebRTC backends.
- `PROXY_TOKEN_ID`: Id of the token to use when connecting to proxy servers.
- `PROXY_TOKEN_KEY`: Private key for the configured token id.
- `PROXY_URLS`: Space-separated list of proxy URLs to connect to.
- `PROXY_DNS_DISCOVERY`: Enable DNS discovery on hostnames of configured static URLs.
- `PROXY_ETCD`: Set to `1` if etcd should be used to configure proxy connections.
- `PROXY_KEY_PREFIX`: Key prefix of proxy entries.
- `MAX_STREAM_BITRATE`: Optional global maximum bitrate for audio/video streams.
- `MAX_SCREEN_BITRATE`: Optional global maximum bitrate for screensharing streams.
- `TURN_API_KEY`: API key that Janus will need to send when requesting TURN credentials.
- `TURN_SECRET`: The shared secret to use for generating TURN credentials.
- `TURN_SERVERS`: A comma-separated list of TURN servers to use.
- `GEOIP_LICENSE`: License key to use when downloading the MaxMind GeoIP database.
- `GEOIP_URL`: Optional URL to download a MaxMind GeoIP database from.
- `GEOIP_OVERRIDES`: Optional space-separated list of overrides for GeoIP lookups.
- `CONTINENT_OVERRIDES`: Optional space-separated list of overrides for continent mappings.
- `STATS_IPS`: Comma-separated list of IP addresses that are allowed to access the stats endpoint.
- `TRUSTED_PROXIES`: Comma-separated list of IPs / networks that are trusted proxies.
- `GRPC_LISTEN`: IP and port to listen on for GRPC requests.
- `GRPC_SERVER_CERTIFICATE`: Certificate to use for the GRPC server.
- `GRPC_SERVER_KEY`: Private key to use for the GRPC server.
- `GRPC_SERVER_CA`: CA certificate that is allowed to issue certificates of GRPC servers.
- `GRPC_CLIENT_CERTIFICATE`: Certificate to use for the GRPC client.
- `GRPC_CLIENT_KEY`: Private key to use for the GRPC client.
- `GRPC_CLIENT_CA`: CA certificate that is allowed to issue certificates of GRPC clients.
- `GRPC_TARGETS`: Comma-separated list of GRPC targets to connect to for clustering mode.
- `GRPC_DNS_DISCOVERY`: Enable DNS discovery on hostnames of configured GRPC targets.
- `GRPC_ETCD`: Set to `1` if etcd should be used to configure GRPC peers.
- `GRPC_TARGET_PREFIX`: Key prefix of GRPC target entries.
- `SKIP_VERIFY`: Set to `true` to skip certificate validation of backends and proxy servers. This should only be enabled during development, e.g. to work with self-signed certificates.
Example with two backends:
docker run \
... \
-e BACKENDS="foo bar" \
-e BACKEND_FOO_URL=https://cloud.server1.tld \
-e BACKEND_FOO_SHARED_SECRET=verysecret \
-e BACKEND_BAR_URL=https://cloud.server2.tld \
-e BACKEND_BAR_SHARED_SECRET=moresecret \
...
See https://github.com/strukturag/nextcloud-spreed-signaling/blob/master/server.conf.in
for further details on the different options.
## Signaling proxy
The image for the signaling proxy can be retrieved from
strukturag/nextcloud-spreed-signaling:<version>-proxy
Replace `version` with the tag or commit you want to use.
### Configuration
The running container can be configured through different environment variables:
- `CONFIG`: Optional name of configuration file to use.
- `HTTP_LISTEN`: Address of HTTP listener.
- `COUNTRY`: Optional ISO 3166 country this proxy is located at.
- `EXTERNAL_HOSTNAME`: The external hostname for remote streams. Will try to autodetect if omitted.
- `TOKEN_ID`: Id of the token to use when connecting remote streams.
- `TOKEN_KEY`: Private key for the configured token id.
- `BANDWIDTH_INCOMING`: Optional incoming target bandwidth (in megabits per second).
- `BANDWIDTH_OUTGOING`: Optional outgoing target bandwidth (in megabits per second).
- `JANUS_URL`: Url to Janus server.
- `MAX_STREAM_BITRATE`: Optional maximum bitrate for audio/video streams.
- `MAX_SCREEN_BITRATE`: Optional maximum bitrate for screensharing streams.
- `STATS_IPS`: Comma-separated list of IP addresses that are allowed to access the stats endpoint.
- `TRUSTED_PROXIES`: Comma-separated list of IPs / networks that are trusted proxies.
- `ETCD_ENDPOINTS`: Static list of etcd endpoints (if etcd should be used).
- `ETCD_DISCOVERY_SRV`: Alternative domain to use for DNS SRV configuration of etcd endpoints (if etcd should be used).
- `ETCD_DISCOVERY_SERVICE`: Optional service name for DNS SRV configuration of etcd..
- `ETCD_CLIENT_CERTIFICATE`: Filename of certificate for etcd client.
- `ETCD_CLIENT_KEY`: Filename of private key for etcd client.
- `ETCD_CLIENT_CA`: Filename of CA for etcd client.
- `TOKENS_ETCD`: Set to `1` if etcd should be used to configure tokens.
- `TOKEN_KEY_FORMAT`: Format of key name to retrieve the public key from, "%s" will be replaced with the token id.
- `TOKENS`: Space-separated list of token ids.
- `TOKEN_<ID>_KEY`: Filename of public key for token `ID` (where `ID` is the uppercase token id).
Example with two tokens:
docker run \
... \
-e TOKENS="foo signaling.server1.tld" \
-e TOKEN_FOO_KEY=/path/to/foo.key \
-e TOKEN_SIGNALING_SERVER1_TLD_KEY=/path/to/signaling.server1.tld.key \
...
See https://github.com/strukturag/nextcloud-spreed-signaling/blob/master/proxy.conf.in
for further details on the different options.

View file

@ -2,7 +2,11 @@ version: '3'
services:
spreedbackend:
build: .
build:
context: ..
dockerfile: docker/server/Dockerfile
platforms:
- "linux/amd64"
volumes:
- ./server.conf:/config/server.conf
network_mode: host
@ -19,7 +23,7 @@ services:
network_mode: host
restart: unless-stopped
janus:
build: docker/janus
build: janus
command: ["janus", "--full-trickle"]
network_mode: host
restart: unless-stopped

View file

@ -1,5 +1,5 @@
# Modified from https://gitlab.com/powerpaul17/nc_talk_backend/-/blob/dcbb918d8716dad1eb72a889d1e6aa1e3a543641/docker/janus/Dockerfile
FROM alpine:3.14
FROM alpine:3.20
RUN apk add --no-cache curl autoconf automake libtool pkgconf build-base \
glib-dev libconfig-dev libnice-dev jansson-dev openssl-dev zlib libsrtp-dev \
@ -15,30 +15,30 @@ RUN cd /tmp && \
git checkout $USRSCTP_VERSION && \
./bootstrap && \
./configure --prefix=/usr && \
make && make install
make -j$(nproc) && make install
# libsrtp
ARG LIBSRTP_VERSION=2.4.2
ARG LIBSRTP_VERSION=2.6.0
RUN cd /tmp && \
wget https://github.com/cisco/libsrtp/archive/v$LIBSRTP_VERSION.tar.gz && \
tar xfv v$LIBSRTP_VERSION.tar.gz && \
cd libsrtp-$LIBSRTP_VERSION && \
./configure --prefix=/usr --enable-openssl && \
make shared_library && \
make shared_library -j$(nproc) && \
make install && \
rm -fr /libsrtp-$LIBSRTP_VERSION && \
rm -f /v$LIBSRTP_VERSION.tar.gz
# JANUS
ARG JANUS_VERSION=0.11.8
ARG JANUS_VERSION=1.2.2
RUN mkdir -p /usr/src/janus && \
cd /usr/src/janus && \
curl -L https://github.com/meetecho/janus-gateway/archive/v$JANUS_VERSION.tar.gz | tar -xz && \
cd /usr/src/janus/janus-gateway-$JANUS_VERSION && \
./autogen.sh && \
./configure --disable-rabbitmq --disable-mqtt --disable-boringssl && \
make && \
make -j$(nproc) && \
make install && \
make configs

30
docker/proxy/Dockerfile Normal file
View file

@ -0,0 +1,30 @@
FROM --platform=${BUILDPLATFORM} golang:1.22-alpine AS builder
ARG TARGETARCH
ARG TARGETOS
WORKDIR /workdir
COPY . .
RUN touch /.dockerenv && \
apk add --no-cache bash git build-base protobuf && \
if [ -d "vendor" ]; then GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOPROXY=off make proxy; else \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} make proxy; fi
FROM alpine:3
ENV CONFIG=/config/proxy.conf
RUN adduser -D spreedbackend && \
apk add --no-cache bash tzdata ca-certificates
COPY --from=builder /workdir/bin/proxy /usr/bin/nextcloud-spreed-signaling-proxy
COPY ./proxy.conf.in /config/proxy.conf.in
COPY ./docker/proxy/entrypoint.sh /
COPY ./docker/proxy/stop.sh /
COPY ./docker/proxy/wait.sh /
RUN chown spreedbackend /config
RUN /usr/bin/nextcloud-spreed-signaling-proxy -version
USER spreedbackend
STOPSIGNAL SIGUSR1
ENTRYPOINT [ "/entrypoint.sh" ]

135
docker/proxy/entrypoint.sh Executable file
View file

@ -0,0 +1,135 @@
#!/bin/bash
#
# Standalone signaling server for the Nextcloud Spreed app.
# Copyright (C) 2022 struktur AG
#
# @author Joachim Bauch <bauch@struktur.de>
#
# @license GNU AGPL version 3 or any later version
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
set -e
if [ -n "$1" ]; then
# Run custom command.
exec "$@"
fi
if [ -z "$CONFIG" ]; then
echo "No configuration filename given in CONFIG environment variable"
exit 1
fi
if [ ! -f "$CONFIG" ]; then
echo "Preparing signaling proxy configuration in $CONFIG ..."
cp /config/proxy.conf.in "$CONFIG"
if [ -n "$HTTP_LISTEN" ]; then
sed -i "s|#listen = 127.0.0.1:9090|listen = $HTTP_LISTEN|" "$CONFIG"
fi
if [ -n "$COUNTRY" ]; then
sed -i "s|#country =.*|country = $COUNTRY|" "$CONFIG"
fi
if [ -n "$EXTERNAL_HOSTNAME" ]; then
sed -i "s|#hostname =.*|hostname = $EXTERNAL_HOSTNAME|" "$CONFIG"
fi
if [ -n "$TOKEN_ID" ]; then
sed -i "s|#token_id =.*|token_id = $TOKEN_ID|" "$CONFIG"
fi
if [ -n "$TOKEN_KEY" ]; then
sed -i "s|#token_key =.*|token_key = $TOKEN_KEY|" "$CONFIG"
fi
if [ -n "$BANDWIDTH_INCOMING" ]; then
sed -i "s|#incoming =.*|incoming = $BANDWIDTH_INCOMING|" "$CONFIG"
fi
if [ -n "$BANDWIDTH_OUTGOING" ]; then
sed -i "s|#outgoing =.*|outgoing = $BANDWIDTH_OUTGOING|" "$CONFIG"
fi
HAS_ETCD=
if [ -n "$ETCD_ENDPOINTS" ]; then
sed -i "s|#endpoints =.*|endpoints = $ETCD_ENDPOINTS|" "$CONFIG"
HAS_ETCD=1
else
if [ -n "$ETCD_DISCOVERY_SRV" ]; then
sed -i "s|#discoverysrv =.*|discoverysrv = $ETCD_DISCOVERY_SRV|" "$CONFIG"
HAS_ETCD=1
fi
if [ -n "$ETCD_DISCOVERY_SERVICE" ]; then
sed -i "s|#discoveryservice =.*|discoveryservice = $ETCD_DISCOVERY_SERVICE|" "$CONFIG"
fi
fi
if [ -n "$HAS_ETCD" ]; then
if [ -n "$ETCD_CLIENT_KEY" ]; then
sed -i "s|#clientkey = /path/to/etcd-client.key|clientkey = $ETCD_CLIENT_KEY|" "$CONFIG"
fi
if [ -n "$ETCD_CLIENT_CERTIFICATE" ]; then
sed -i "s|#clientcert = /path/to/etcd-client.crt|clientcert = $ETCD_CLIENT_CERTIFICATE|" "$CONFIG"
fi
if [ -n "$ETCD_CLIENT_CA" ]; then
sed -i "s|#cacert = /path/to/etcd-ca.crt|cacert = $ETCD_CLIENT_CA|" "$CONFIG"
fi
fi
if [ -n "$JANUS_URL" ]; then
sed -i "s|url =.*|url = $JANUS_URL|" "$CONFIG"
else
sed -i "s|url =.*|#url =|" "$CONFIG"
fi
if [ -n "$MAX_STREAM_BITRATE" ]; then
sed -i "s|#maxstreambitrate =.*|maxstreambitrate = $MAX_STREAM_BITRATE|" "$CONFIG"
fi
if [ -n "$MAX_SCREEN_BITRATE" ]; then
sed -i "s|#maxscreenbitrate =.*|maxscreenbitrate = $MAX_SCREEN_BITRATE|" "$CONFIG"
fi
if [ -n "$TOKENS_ETCD" ]; then
if [ -z "$HAS_ETCD" ]; then
echo "No etcd endpoint configured, can't use etcd for proxy tokens"
exit 1
fi
sed -i "s|tokentype =.*|tokentype = etcd|" "$CONFIG"
if [ -n "$TOKEN_KEY_FORMAT" ]; then
sed -i "s|#keyformat =.*|keyformat = $TOKEN_KEY_FORMAT|" "$CONFIG"
fi
else
sed -i "s|\[tokens\]|#[tokens]|" "$CONFIG"
echo >> "$CONFIG"
echo "[tokens]" >> "$CONFIG"
for token in $TOKENS; do
declare var="TOKEN_${token^^}_KEY"
var=${var//./_}
if [ -n "${!var}" ]; then
echo "$token = ${!var}" >> "$CONFIG"
fi
done
echo >> "$CONFIG"
fi
if [ -n "$STATS_IPS" ]; then
sed -i "s|#allowed_ips =.*|allowed_ips = $STATS_IPS|" "$CONFIG"
fi
if [ -n "$TRUSTED_PROXIES" ]; then
sed -i "s|#trustedproxies =.*|trustedproxies = $TRUSTED_PROXIES|" "$CONFIG"
fi
fi
echo "Starting signaling proxy with $CONFIG ..."
exec /usr/bin/nextcloud-spreed-signaling-proxy -config "$CONFIG"

26
docker/proxy/stop.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
#
# Standalone signaling server for the Nextcloud Spreed app.
# Copyright (C) 2024 struktur AG
#
# @author Joachim Bauch <bauch@struktur.de>
#
# @license GNU AGPL version 3 or any later version
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
set -e
echo "Schedule signaling proxy to shutdown ..."
exec killall -USR1 nextcloud-spreed-signaling-proxy

33
docker/proxy/wait.sh Executable file
View file

@ -0,0 +1,33 @@
#!/bin/bash
#
# Standalone signaling server for the Nextcloud Spreed app.
# Copyright (C) 2024 struktur AG
#
# @author Joachim Bauch <bauch@struktur.de>
#
# @license GNU AGPL version 3 or any later version
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
set -e
echo "Waiting for signaling proxy to shutdown ..."
while true
do
if ! pgrep nextcloud-spreed-signaling-proxy > /dev/null ; then
echo "Signaling proxy has stopped"
exit 0
fi
sleep 1
done

30
docker/server/Dockerfile Normal file
View file

@ -0,0 +1,30 @@
FROM --platform=${BUILDPLATFORM} golang:1.22-alpine AS builder
ARG TARGETARCH
ARG TARGETOS
WORKDIR /workdir
COPY . .
RUN touch /.dockerenv && \
apk add --no-cache bash git build-base protobuf && \
if [ -d "vendor" ]; then GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOPROXY=off make server; else \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} make server; fi
FROM alpine:3
ENV CONFIG=/config/server.conf
RUN adduser -D spreedbackend && \
apk add --no-cache bash tzdata ca-certificates
COPY --from=builder /workdir/bin/signaling /usr/bin/nextcloud-spreed-signaling
COPY ./server.conf.in /config/server.conf.in
COPY ./docker/server/entrypoint.sh /
COPY ./docker/server/stop.sh /
COPY ./docker/server/wait.sh /
RUN chown spreedbackend /config
RUN /usr/bin/nextcloud-spreed-signaling -version
USER spreedbackend
STOPSIGNAL SIGUSR1
ENTRYPOINT [ "/entrypoint.sh" ]

273
docker/server/entrypoint.sh Executable file
View file

@ -0,0 +1,273 @@
#!/bin/bash
#
# Standalone signaling server for the Nextcloud Spreed app.
# Copyright (C) 2022 struktur AG
#
# @author Joachim Bauch <bauch@struktur.de>
#
# @license GNU AGPL version 3 or any later version
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
set -e
if [ -n "$1" ]; then
# Run custom command.
exec "$@"
fi
if [ -z "$CONFIG" ]; then
echo "No configuration filename given in CONFIG environment variable"
exit 1
fi
if [ ! -f "$CONFIG" ]; then
echo "Preparing signaling server configuration in $CONFIG ..."
cp /config/server.conf.in "$CONFIG"
if [ -n "$HTTP_LISTEN" ]; then
sed -i "s|#listen = 127.0.0.1:8080|listen = $HTTP_LISTEN|" "$CONFIG"
fi
if [ -n "$HTTPS_LISTEN" ]; then
sed -i "s|#listen = 127.0.0.1:8443|listen = $HTTPS_LISTEN|" "$CONFIG"
if [ -n "$HTTPS_CERTIFICATE" ]; then
sed -i "s|certificate = /etc/nginx/ssl/server.crt|certificate = $HTTPS_CERTIFICATE|" "$CONFIG"
fi
if [ -n "$HTTPS_KEY" ]; then
sed -i "s|key = /etc/nginx/ssl/server.key|key = $HTTPS_KEY|" "$CONFIG"
fi
fi
if [ -n "$HASH_KEY" ]; then
sed -i "s|the-secret-for-session-checksums|$HASH_KEY|" "$CONFIG"
fi
if [ -n "$BLOCK_KEY" ]; then
sed -i "s|-encryption-key-|$BLOCK_KEY|" "$CONFIG"
fi
if [ -n "$INTERNAL_SHARED_SECRET_KEY" ]; then
sed -i "s|the-shared-secret-for-internal-clients|$INTERNAL_SHARED_SECRET_KEY|" "$CONFIG"
fi
if [ -n "$NATS_URL" ]; then
sed -i "s|#url = nats://localhost:4222|url = $NATS_URL|" "$CONFIG"
else
sed -i "s|#url = nats://localhost:4222|url = nats://loopback|" "$CONFIG"
fi
HAS_ETCD=
if [ -n "$ETCD_ENDPOINTS" ]; then
sed -i "s|#endpoints =.*|endpoints = $ETCD_ENDPOINTS|" "$CONFIG"
HAS_ETCD=1
else
if [ -n "$ETCD_DISCOVERY_SRV" ]; then
sed -i "s|#discoverysrv =.*|discoverysrv = $ETCD_DISCOVERY_SRV|" "$CONFIG"
HAS_ETCD=1
fi
if [ -n "$ETCD_DISCOVERY_SERVICE" ]; then
sed -i "s|#discoveryservice =.*|discoveryservice = $ETCD_DISCOVERY_SERVICE|" "$CONFIG"
fi
fi
if [ -n "$HAS_ETCD" ]; then
if [ -n "$ETCD_CLIENT_KEY" ]; then
sed -i "s|#clientkey = /path/to/etcd-client.key|clientkey = $ETCD_CLIENT_KEY|" "$CONFIG"
fi
if [ -n "$ETCD_CLIENT_CERTIFICATE" ]; then
sed -i "s|#clientcert = /path/to/etcd-client.crt|clientcert = $ETCD_CLIENT_CERTIFICATE|" "$CONFIG"
fi
if [ -n "$ETCD_CLIENT_CA" ]; then
sed -i "s|#cacert = /path/to/etcd-ca.crt|cacert = $ETCD_CLIENT_CA|" "$CONFIG"
fi
fi
if [ -n "$USE_JANUS" ]; then
sed -i "s|#type =$|type = janus|" "$CONFIG"
if [ -n "$JANUS_URL" ]; then
sed -i "/proxy URLs to connect to/{n;s|#url =$|url = $JANUS_URL|}" "$CONFIG"
fi
elif [ -n "$USE_PROXY" ]; then
sed -i "s|#type =$|type = proxy|" "$CONFIG"
if [ -n "$PROXY_TOKEN_ID" ]; then
sed -i "s|#token_id =.*|token_id = $PROXY_TOKEN_ID|" "$CONFIG"
fi
if [ -n "$PROXY_TOKEN_KEY" ]; then
sed -i "s|#token_key =.*|token_key = $PROXY_TOKEN_KEY|" "$CONFIG"
fi
if [ -n "$PROXY_ETCD" ]; then
if [ -z "$HAS_ETCD" ]; then
echo "No etcd endpoint configured, can't use etcd for proxy connections"
exit 1
fi
sed -i "s|#urltype = static|urltype = etcd|" "$CONFIG"
if [ -n "$PROXY_KEY_PREFIX" ]; then
sed -i "s|#keyprefix =.*|keyprefix = $PROXY_KEY_PREFIX|" "$CONFIG"
fi
else
if [ -n "$PROXY_URLS" ]; then
sed -i "/proxy URLs to connect to/{n;s|#url =$|url = $PROXY_URLS|}" "$CONFIG"
fi
if [ -n "$PROXY_DNS_DISCOVERY" ]; then
sed -i "/or deleted as necessary/{n;s|#dnsdiscovery =.*|dnsdiscovery = true|}" "$CONFIG"
fi
fi
fi
if [ -n "$MAX_STREAM_BITRATE" ]; then
sed -i "s|#maxstreambitrate =.*|maxstreambitrate = $MAX_STREAM_BITRATE|" "$CONFIG"
fi
if [ -n "$MAX_SCREEN_BITRATE" ]; then
sed -i "s|#maxscreenbitrate =.*|maxscreenbitrate = $MAX_SCREEN_BITRATE|" "$CONFIG"
fi
if [ -n "$SKIP_VERIFY" ]; then
sed -i "s|#skipverify =.*|skipverify = $SKIP_VERIFY|" "$CONFIG"
fi
if [ -n "$TURN_API_KEY" ]; then
sed -i "s|#\?apikey =.*|apikey = $TURN_API_KEY|" "$CONFIG"
fi
if [ -n "$TURN_SECRET" ]; then
sed -i "/same as on the TURN server/{n;s|#\?secret =.*|secret = $TURN_SECRET|}" "$CONFIG"
fi
if [ -n "$TURN_SERVERS" ]; then
sed -i "s|#servers =.*|servers = $TURN_SERVERS|" "$CONFIG"
fi
if [ -n "$GEOIP_LICENSE" ]; then
sed -i "s|#license =.*|license = $GEOIP_LICENSE|" "$CONFIG"
fi
if [ -n "$GEOIP_URL" ]; then
sed -i "/looking up IP addresses/{n;s|#url =$|url = $GEOIP_URL|}" "$CONFIG"
fi
if [ -n "$STATS_IPS" ]; then
sed -i "s|#allowed_ips =.*|allowed_ips = $STATS_IPS|" "$CONFIG"
fi
if [ -n "$TRUSTED_PROXIES" ]; then
sed -i "s|#trustedproxies =.*|trustedproxies = $TRUSTED_PROXIES|" "$CONFIG"
fi
if [ -n "$GRPC_LISTEN" ]; then
sed -i "s|#listen = 0.0.0.0:9090|listen = $GRPC_LISTEN|" "$CONFIG"
if [ -n "$GRPC_SERVER_CERTIFICATE" ]; then
sed -i "s|#servercertificate =.*|servercertificate = $GRPC_SERVER_CERTIFICATE|" "$CONFIG"
fi
if [ -n "$GRPC_SERVER_KEY" ]; then
sed -i "s|#serverkey =.*|serverkey = $GRPC_SERVER_KEY|" "$CONFIG"
fi
if [ -n "$GRPC_SERVER_CA" ]; then
sed -i "s|#serverca =.*|serverca = $GRPC_SERVER_CA|" "$CONFIG"
fi
if [ -n "$GRPC_CLIENT_CERTIFICATE" ]; then
sed -i "s|#clientcertificate =.*|clientcertificate = $GRPC_CLIENT_CERTIFICATE|" "$CONFIG"
fi
if [ -n "$GRPC_CLIENT_KEY" ]; then
sed -i "s|#clientkey = /path/to/grpc-client.key|clientkey = $GRPC_CLIENT_KEY|" "$CONFIG"
fi
if [ -n "$GRPC_CLIENT_CA" ]; then
sed -i "s|#clientca =.*|clientca = $GRPC_CLIENT_CA|" "$CONFIG"
fi
if [ -n "$GRPC_ETCD" ]; then
if [ -z "$HAS_ETCD" ]; then
echo "No etcd endpoint configured, can't use etcd for GRPC"
exit 1
fi
sed -i "s|#targettype =$|targettype = etcd|" "$CONFIG"
if [ -n "$GRPC_TARGET_PREFIX" ]; then
sed -i "s|#targetprefix =.*|targetprefix = $GRPC_TARGET_PREFIX|" "$CONFIG"
fi
else
if [ -n "$GRPC_TARGETS" ]; then
sed -i "s|#targets =.*|targets = $GRPC_TARGETS|" "$CONFIG"
if [ -n "$GRPC_DNS_DISCOVERY" ]; then
sed -i "/# deleted as necessary/{n;s|#dnsdiscovery =.*|dnsdiscovery = true|}" "$CONFIG"
fi
fi
fi
fi
if [ -n "$GEOIP_OVERRIDES" ]; then
sed -i "s|\[geoip-overrides\]|#[geoip-overrides]|" "$CONFIG"
echo >> "$CONFIG"
echo "[geoip-overrides]" >> "$CONFIG"
for override in $GEOIP_OVERRIDES; do
echo "$override" >> "$CONFIG"
done
echo >> "$CONFIG"
fi
if [ -n "$CONTINENT_OVERRIDES" ]; then
sed -i "s|\[continent-overrides\]|#[continent-overrides]|" "$CONFIG"
echo >> "$CONFIG"
echo "[continent-overrides]" >> "$CONFIG"
for override in $CONTINENT_OVERRIDES; do
echo "$override" >> "$CONFIG"
done
echo >> "$CONFIG"
fi
if [ -n "$BACKENDS_ALLOWALL" ]; then
sed -i "s|allowall = false|allowall = $BACKENDS_ALLOWALL|" "$CONFIG"
fi
if [ -n "$BACKENDS_ALLOWALL_SECRET" ]; then
sed -i "s|#secret = the-shared-secret-for-allowall|secret = $BACKENDS_ALLOWALL_SECRET|" "$CONFIG"
fi
if [ -n "$BACKENDS" ]; then
BACKENDS_CONFIG=${BACKENDS// /,}
sed -i "s|#backends = .*|backends = $BACKENDS_CONFIG|" "$CONFIG"
echo >> "$CONFIG"
for backend in $BACKENDS; do
echo "[$backend]" >> "$CONFIG"
declare var="BACKEND_${backend^^}_URL"
if [ -n "${!var}" ]; then
echo "url = ${!var}" >> "$CONFIG"
fi
declare var="BACKEND_${backend^^}_SHARED_SECRET"
if [ -n "${!var}" ]; then
echo "secret = ${!var}" >> "$CONFIG"
fi
declare var="BACKEND_${backend^^}_SESSION_LIMIT"
if [ -n "${!var}" ]; then
echo "sessionlimit = ${!var}" >> "$CONFIG"
fi
declare var="BACKEND_${backend^^}_MAX_STREAM_BITRATE"
if [ -n "${!var}" ]; then
echo "maxstreambitrate = ${!var}" >> "$CONFIG"
fi
declare var="BACKEND_${backend^^}_MAX_SCREEN_BITRATE"
if [ -n "${!var}" ]; then
echo "maxscreenbitrate = ${!var}" >> "$CONFIG"
fi
echo >> "$CONFIG"
done
fi
fi
echo "Starting signaling server with $CONFIG ..."
exec /usr/bin/nextcloud-spreed-signaling -config "$CONFIG"

26
docker/server/stop.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
#
# Standalone signaling server for the Nextcloud Spreed app.
# Copyright (C) 2024 struktur AG
#
# @author Joachim Bauch <bauch@struktur.de>
#
# @license GNU AGPL version 3 or any later version
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
set -e
echo "Schedule signaling server to shutdown ..."
exec killall -USR1 nextcloud-spreed-signaling

33
docker/server/wait.sh Executable file
View file

@ -0,0 +1,33 @@
#!/bin/bash
#
# Standalone signaling server for the Nextcloud Spreed app.
# Copyright (C) 2024 struktur AG
#
# @author Joachim Bauch <bauch@struktur.de>
#
# @license GNU AGPL version 3 or any later version
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
set -e
echo "Waiting for signaling server to shutdown ..."
while true
do
if ! pgrep nextcloud-spreed-signaling > /dev/null ; then
echo "Signaling server has stopped"
exit 0
fi
sleep 1
done

View file

@ -45,3 +45,9 @@ The following metrics are available:
| `signaling_mcu_no_backend_available_total` | Counter | 0.4.0 | Total number of publishing requests where no backend was available | `type` |
| `signaling_room_sessions` | Gauge | 0.4.0 | The current number of sessions in a room | `backend`, `room`, `clienttype` |
| `signaling_server_messages_total` | Counter | 0.4.0 | The total number of signaling messages | `type` |
| `signaling_grpc_clients` | Gauge | 1.0.0 | The current number of GRPC clients | |
| `signaling_grpc_client_calls_total` | Counter | 1.0.0 | The total number of GRPC client calls | `method` |
| `signaling_grpc_server_calls_total` | Counter | 1.0.0 | The total number of GRPC server calls | `method` |
| `signaling_http_client_pool_connections` | Gauge | 1.2.4 | The current number of HTTP client connections per host | `host` |
| `signaling_throttle_delayed_total` | Counter | 1.2.5 | The total number of delayed requests | `action`, `delay` |
| `signaling_throttle_bruteforce_total` | Counter | 1.2.5 | The total number of rejected bruteforce requests | `action` |

View file

@ -1,4 +1,6 @@
sphinx==5.0.0
sphinx_rtd_theme==1.0.0
readthedocs-sphinx-search==0.1.2
jinja2<3.1.0
jinja2==3.1.4
markdown==3.6
mkdocs==1.6.0
readthedocs-sphinx-search==0.3.2
sphinx==7.3.7
sphinx_rtd_theme==2.0.0

View file

@ -111,6 +111,23 @@ must contain two additional HTTP headers:
- Calculated checksum: `3c4a69ff328299803ac2879614b707c807b4758cf19450755c60656cac46e3bc`
## Welcome message
When a client connects, the server will immediately send a `welcome` message to
notify the client about supported features. This is available if the server
supports the `welcome` feature id.
Message format (Server -> Client):
{
"type": "welcome",
"welcome": {
"features": ["optional", "list, "of", "feature", "ids"],
...additional information about the server...
}
}
## Establish connection
This must be the first request by a newly connected client and is used to
@ -123,7 +140,8 @@ Message format (Client -> Server):
"id": "unique-request-id",
"type": "hello",
"hello": {
"version": "the-protocol-version-must-be-1.0",
"version": "the-protocol-version",
"features": ["optional", "list, "of", "client", "feature", "ids"],
"auth": {
"url": "the-url-to-the-auth-backend",
"params": {
@ -142,7 +160,7 @@ Message format (Server -> Client):
"sessionid": "the-unique-session-id",
"resumeid": "the-unique-resume-id",
"userid": "the-user-id-for-known-users",
"version": "the-protocol-version-must-be-1.0",
"version": "the-protocol-version",
"server": {
"features": ["optional", "list, "of", "feature", "ids"],
...additional information about the server...
@ -150,13 +168,87 @@ Message format (Server -> Client):
}
}
Please note that the `server` entry is deprecated and will be removed in a
future version. Clients should use the data from the
[`welcome` message](#welcome-message) instead.
### Protocol version "1.0"
For protocol version `1.0` in the `hello` request, the `params` from the `auth`
field are sent to the Nextcloud backend for [validation](#backend-validation).
### Protocol version "2.0"
For protocol version `2.0` in the `hello` request, the `params` from the `auth`
field must contain a `token` entry containing a [JWT](https://jwt.io/).
The JWT must contain the following fields:
- `iss`: URL of the Nextcloud server that issued the token.
- `iat`: Timestamp when the token has been issued.
- `exp`: Timestamp of the token expiration.
- `sub`: User Id (if known).
- `userdata`: Optional JSON containing more user data.
It must be signed with an RSA, ECDSA or Ed25519 key.
Example token:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwczovL25leHRjbG91ZC1tYXN0ZXIubG9jYWwvIiwiaWF0IjoxNjU0ODQyMDgwLCJleHAiOjE2NTQ4NDIzODAsInN1YiI6ImFkbWluIiwidXNlcmRhdGEiOnsiZGlzcGxheW5hbWUiOiJBZG1pbmlzdHJhdG9yIn19.5rV0jh89_0fG2L-BUPtciu1q49PoYkLboj33EOdD0qQeYcvE7_di2r5WXM1WmKUCOGeX3hzn6qldDMrJBNuxvQ
```
Example public key:
```
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIoCsNSCXyxK25zvSKRio0uiBzwub
ONq3tiGTPZo3p2Ogn6wAhhsuSxbFuUQDWMX7Tsu9fDzVdwpRHPT4y3V9cA==
-----END PUBLIC KEY-----
```
Example payload:
```
{
"iss": "https://nextcloud-master.local/",
"iat": 1654842080,
"exp": 1654842380,
"sub": "admin",
"userdata": {
"displayname": "Administrator"
}
}
```
The public key is retrieved from the capabilities of the Nextcloud instance
in `config` key `hello-v2-token-key` inside `signaling`.
```
"spreed": {
"features": [
"audio",
"video",
"chat-v2",
"conversation-v4",
...
],
"config": {
"signaling": {
"hello-v2-token-key": "-----BEGIN RSA PUBLIC KEY----- ..."
}
}
},
```
### Backend validation
The server validates the connection request against the passed auth backend
(needs to make sure the passed url / hostname is in a whitelist). It performs
a POST request and passes the provided `params` as JSON payload in the body
of the request.
For `hello` protocol version `1.0`, the server validates the connection request
against the passed auth backend (needs to make sure the passed url / hostname
is in a whitelist).
It performs a POST request and passes the provided `params` as JSON payload in
the body of the request.
Message format (Server -> Auth backend):
@ -215,7 +307,8 @@ Message format (Client -> Server):
"id": "unique-request-id",
"type": "hello",
"hello": {
"version": "the-protocol-version-must-be-1.0",
"version": "the-protocol-version",
"features": ["optional", "list, "of", "client", "feature", "ids"],
"auth": {
"type": "the-client-type",
...other attributes depending on the client type...
@ -273,7 +366,7 @@ Message format (Client -> Server):
"id": "unique-request-id",
"type": "hello",
"hello": {
"version": "the-protocol-version-must-be-1.0",
"version": "the-protocol-version",
"resumeid": "the-resume-id-from-the-original-hello-response"
}
}
@ -285,7 +378,7 @@ Message format (Server -> Client):
"type": "hello",
"hello": {
"sessionid": "the-unique-session-id",
"version": "the-protocol-version-must-be-1.0"
"version": "the-protocol-version"
}
}
@ -367,6 +460,26 @@ Message format (Server -> Client):
the current room or the properties of a room change.
Message format (Server -> Client if already joined before):
{
"id": "unique-request-id-from-request",
"type": "error",
"error": {
"code": "already_joined",
"message": "Human readable error message",
"details": {
"roomid": "the-room-id",
"properties": {
...additional room properties...
}
}
}
}
- Sent if a client tried to join a room it is already in.
### Backend validation
Rooms are managed by the Nextcloud backend, so the signaling server has to
@ -680,6 +793,74 @@ Message format (Server -> Client, receive message)
- The `userid` is omitted if a message was sent by an anonymous user.
## Control messages
Similar to regular messages between clients which can be sent by any session,
messages with type `control` can only be sent if the permission flag `control`
is available.
These messages can be used to perform actions on clients that should only be
possible by some users (e.g. moderators).
Message format (Client -> Server, mute phone):
{
"id": "unique-request-id",
"type": "control",
"control": {
"recipient": {
"type": "session",
"sessionid": "the-session-id-to-send-to"
},
"data": {
"type": "mute",
"audio": "audio-flags"
}
}
}
The bit-field `audio-flags` supports the following bits:
- `1`: mute speaking (i.e. phone can no longer talk)
- `2`: mute listening (i.e. phone is on hold and can no longer hear)
To unmute, a value of `0` must be sent.
Message format (Client -> Server, hangup phone):
{
"id": "unique-request-id",
"type": "control",
"control": {
"recipient": {
"type": "session",
"sessionid": "the-session-id-to-send-to"
},
"data": {
"type": "hangup"
}
}
}
Message format (Client -> Server, send DTMF):
{
"id": "unique-request-id",
"type": "control",
"control": {
"recipient": {
"type": "session",
"sessionid": "the-session-id-to-send-to"
},
"data": {
"type": "dtmf",
"digit": "the-digit"
}
}
}
Supported digits are `0`-`9`, `*` and `#`.
## Transient data
Transient data can be used to share data in a room that is valid while sessions
@ -704,14 +885,17 @@ Message format (Client -> Server):
"transient": {
"type": "set",
"key": "sample-key",
"value": "any-json-object"
"value": "any-json-object",
"ttl": "optional-ttl"
}
}
- The `key` must be a string.
- The `value` can be of any type (i.e. string, number, array, object, etc.).
- The `ttl` is the time to live in nanoseconds. The value will be removed after
that time (if it is still present).
- Requests to set a value that is already present for the key are silently
ignored.
ignored. Any TTL value will be updated / removed.
Message format (Server -> Client):
@ -778,6 +962,105 @@ Message format (Server -> Client):
}
## Internal clients
Internal clients can be used by third-party applications to perform tasks that
a regular client can not be used. Examples are adding virtual sessions or
sending media without a regular client connected. This is used for example by
the SIP bridge to publish mixed phone audio and show "virtual" sessions for the
individial phone calls.
See above for details on how to connect as internal client. By default, internal
clients have their "inCall" and the "publishing audio" flags set. Virtual
sessions have their "inCall" and the "publishing phone" flags set.
This can be changed by including the client feature flag `internal-incall`
which will require the client to set the flags as necessary.
### Add virtual session
Message format (Client -> Server):
{
"type": "internal",
"internal": {
"type": "addsession",
"addsession": {
"sessionid": "the-virtual-sessionid",
"roomid": "the-room-id-to-add-the-session",
"userid": "optional-user-id",
"user": {
...additional data of the user...
},
"flags": "optional-initial-flags",
"incall": "optional-initial-incall",
"options": {
"actorId": "optional-actor-id",
"actorType": "optional-actor-type",
}
}
}
}
Phone sessions will have `type` set to `phone` in the additional user data
(which will be included in the `joined` [room event](#room-events)),
`callid` will be the id of the phone call and `number` the target of the call.
The call id will match the one returned for accepted outgoing calls and the
associated session id can be used to hangup a call or send DTMF tones to it.
### Update virtual session
Message format (Client -> Server):
{
"type": "internal",
"internal": {
"type": "updatesession",
"updatesession": {
"sessionid": "the-virtual-sessionid",
"roomid": "the-room-id-to-update-the-session",
"flags": "optional-updated-flags",
"incall": "optional-updated-incall"
}
}
}
### Remove virtual session
Message format (Client -> Server):
{
"type": "internal",
"internal": {
"type": "removesession",
"removesession": {
"sessionid": "the-virtual-sessionid",
"roomid": "the-room-id-to-add-the-session",
"userid": "optional-user-id"
}
}
}
### Change inCall flags of internal client
Message format (Client -> Server):
{
"type": "internal",
"internal": {
"type": "incall",
"incall": {
"incall": "the-incall-flags"
}
}
}
# Internal signaling server API
The signaling server provides an internal API that can be called from Nextcloud
@ -939,3 +1222,109 @@ Message format (Backend -> Server)
}
}
}
### Notify sessions to switch to a different room
This can be used to let sessions in a room know that they switch to a different
room (available if the server returns the `switchto` feature). The session ids
sent should be the Talk room session ids.
Message format (Backend -> Server, no additional details)
{
"type": "switchto"
"switchto" {
"roomid": "target-room-id",
"sessions": [
"the-nextcloud-session-id-1",
"the-nextcloud-session-id-2",
]
}
}
Message format (Backend -> Server, with additional details)
{
"type": "switchto"
"switchto" {
"roomid": "target-room-id",
"sessions": {
"the-nextcloud-session-id-1": {
...arbitrary object to sent to clients...
},
"the-nextcloud-session-id-2": null
}
}
}
The signaling server will sent messages to the sessions mentioned in the
received `switchto` event. If a details object was included for a session, it
will be forwarded in the client message, otherwise the `details` will be
omitted.
Message format (Server -> Client):
{
"type": "event"
"event": {
"target": "room",
"type": "switchto",
"switchto": {
"roomid": "target-room-id",
"details": {
...arbitrary object to sent to clients...
}
}
}
}
Clients are expected to follow the `switchto` message. If clients don't switch
to the target room after some time, they might get disconnected.
### Start dialout from a room
Use this to start a phone dialout to a new user in a given room.
Message format (Backend -> Server)
{
"type": "dialout"
"dialout" {
"number": "e164-target-number",
"options": {
...arbitrary options that will be sent back to validate...
}
}
}
Please note that this requires a connected internal client that supports
dialout (e.g. the SIP bridge).
Message format (Server -> Backend, request was accepted)
{
"type": "dialout"
"dialout" {
"callid": "the-unique-call-id"
}
}
Message format (Server -> Backend, request could not be processed)
{
"type": "dialout"
"dialout" {
"error": {
"code": "the-internal-message-id",
"message": "human-readable-error-message",
"details": {
...optional additional details...
}
}
}
}
A HTTP error status code will be set in this case.

295
etcd_client.go Normal file
View file

@ -0,0 +1,295 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"errors"
"fmt"
"log"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/dlintw/goconf"
"go.etcd.io/etcd/client/pkg/v3/srv"
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type EtcdClientListener interface {
EtcdClientCreated(client *EtcdClient)
}
type EtcdClientWatcher interface {
EtcdWatchCreated(client *EtcdClient, key string)
EtcdKeyUpdated(client *EtcdClient, key string, value []byte, prevValue []byte)
EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte)
}
type EtcdClient struct {
compatSection string
mu sync.Mutex
client atomic.Value
listeners map[EtcdClientListener]bool
}
func NewEtcdClient(config *goconf.ConfigFile, compatSection string) (*EtcdClient, error) {
result := &EtcdClient{
compatSection: compatSection,
}
if err := result.load(config, false); err != nil {
return nil, err
}
return result, nil
}
func (c *EtcdClient) getConfigStringWithFallback(config *goconf.ConfigFile, option string) string {
value, _ := config.GetString("etcd", option)
if value == "" && c.compatSection != "" {
value, _ = config.GetString(c.compatSection, option)
if value != "" {
log.Printf("WARNING: Configuring etcd option \"%s\" in section \"%s\" is deprecated, use section \"etcd\" instead", option, c.compatSection)
}
}
return value
}
func (c *EtcdClient) load(config *goconf.ConfigFile, ignoreErrors bool) error {
var endpoints []string
if endpointsString := c.getConfigStringWithFallback(config, "endpoints"); endpointsString != "" {
for _, ep := range strings.Split(endpointsString, ",") {
ep := strings.TrimSpace(ep)
if ep != "" {
endpoints = append(endpoints, ep)
}
}
} else if discoverySrv := c.getConfigStringWithFallback(config, "discoverysrv"); discoverySrv != "" {
discoveryService := c.getConfigStringWithFallback(config, "discoveryservice")
clients, err := srv.GetClient("etcd-client", discoverySrv, discoveryService)
if err != nil {
if !ignoreErrors {
return fmt.Errorf("Could not discover etcd endpoints for %s: %w", discoverySrv, err)
}
} else {
endpoints = clients.Endpoints
}
}
if len(endpoints) == 0 {
if !ignoreErrors {
return nil
}
log.Printf("No etcd endpoints configured, not changing client")
} else {
cfg := clientv3.Config{
Endpoints: endpoints,
// set timeout per request to fail fast when the target endpoint is unavailable
DialTimeout: time.Second,
}
if logLevel, _ := config.GetString("etcd", "loglevel"); logLevel != "" {
var l zapcore.Level
if err := l.Set(logLevel); err != nil {
return fmt.Errorf("Unsupported etcd log level %s: %w", logLevel, err)
}
logConfig := zap.NewProductionConfig()
logConfig.Level = zap.NewAtomicLevelAt(l)
cfg.LogConfig = &logConfig
}
clientKey := c.getConfigStringWithFallback(config, "clientkey")
clientCert := c.getConfigStringWithFallback(config, "clientcert")
caCert := c.getConfigStringWithFallback(config, "cacert")
if clientKey != "" && clientCert != "" && caCert != "" {
tlsInfo := transport.TLSInfo{
CertFile: clientCert,
KeyFile: clientKey,
TrustedCAFile: caCert,
}
tlsConfig, err := tlsInfo.ClientConfig()
if err != nil {
if !ignoreErrors {
return fmt.Errorf("Could not setup etcd TLS configuration: %w", err)
}
log.Printf("Could not setup TLS configuration, will be disabled (%s)", err)
} else {
cfg.TLS = tlsConfig
}
}
client, err := clientv3.New(cfg)
if err != nil {
if !ignoreErrors {
return err
}
log.Printf("Could not create new client from etd endpoints %+v: %s", endpoints, err)
} else {
prev := c.getEtcdClient()
if prev != nil {
prev.Close()
}
c.client.Store(client)
log.Printf("Using etcd endpoints %+v", endpoints)
c.notifyListeners()
}
}
return nil
}
func (c *EtcdClient) Close() error {
client := c.getEtcdClient()
if client != nil {
return client.Close()
}
return nil
}
func (c *EtcdClient) IsConfigured() bool {
return c.getEtcdClient() != nil
}
func (c *EtcdClient) getEtcdClient() *clientv3.Client {
client := c.client.Load()
if client == nil {
return nil
}
return client.(*clientv3.Client)
}
func (c *EtcdClient) syncClient(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
return c.getEtcdClient().Sync(ctx)
}
func (c *EtcdClient) notifyListeners() {
c.mu.Lock()
defer c.mu.Unlock()
for listener := range c.listeners {
listener.EtcdClientCreated(c)
}
}
func (c *EtcdClient) AddListener(listener EtcdClientListener) {
c.mu.Lock()
defer c.mu.Unlock()
if c.listeners == nil {
c.listeners = make(map[EtcdClientListener]bool)
}
c.listeners[listener] = true
if client := c.getEtcdClient(); client != nil {
go listener.EtcdClientCreated(c)
}
}
func (c *EtcdClient) RemoveListener(listener EtcdClientListener) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.listeners, listener)
}
func (c *EtcdClient) WaitForConnection(ctx context.Context) error {
backoff, err := NewExponentialBackoff(initialWaitDelay, maxWaitDelay)
if err != nil {
return err
}
for {
if err := ctx.Err(); err != nil {
return err
}
if err := c.syncClient(ctx); err != nil {
if errors.Is(err, context.Canceled) {
return err
} else if errors.Is(err, context.DeadlineExceeded) {
log.Printf("Timeout waiting for etcd client to connect to the cluster, retry in %s", backoff.NextWait())
} else {
log.Printf("Could not sync etcd client with the cluster, retry in %s: %s", backoff.NextWait(), err)
}
backoff.Wait(ctx)
continue
}
log.Printf("Client synced, using endpoints %+v", c.getEtcdClient().Endpoints())
return nil
}
}
func (c *EtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
return c.getEtcdClient().Get(ctx, key, opts...)
}
func (c *EtcdClient) Watch(ctx context.Context, key string, nextRevision int64, watcher EtcdClientWatcher, opts ...clientv3.OpOption) (int64, error) {
log.Printf("Wait for leader and start watching on %s (rev=%d)", key, nextRevision)
opts = append(opts, clientv3.WithRev(nextRevision), clientv3.WithPrevKV())
ch := c.getEtcdClient().Watch(clientv3.WithRequireLeader(ctx), key, opts...)
log.Printf("Watch created for %s", key)
watcher.EtcdWatchCreated(c, key)
for response := range ch {
if err := response.Err(); err != nil {
return nextRevision, err
}
nextRevision = response.Header.Revision + 1
for _, ev := range response.Events {
switch ev.Type {
case clientv3.EventTypePut:
var prevValue []byte
if ev.PrevKv != nil {
prevValue = ev.PrevKv.Value
}
watcher.EtcdKeyUpdated(c, string(ev.Kv.Key), ev.Kv.Value, prevValue)
case clientv3.EventTypeDelete:
var prevValue []byte
if ev.PrevKv != nil {
prevValue = ev.PrevKv.Value
}
watcher.EtcdKeyDeleted(c, string(ev.Kv.Key), prevValue)
default:
log.Printf("Unsupported watch event %s %q -> %q", ev.Type, ev.Kv.Key, ev.Kv.Value)
}
}
}
return nextRevision, nil
}

340
etcd_client_test.go Normal file
View file

@ -0,0 +1,340 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"errors"
"net"
"net/url"
"os"
"runtime"
"strconv"
"syscall"
"testing"
"time"
"github.com/dlintw/goconf"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/embed"
"go.etcd.io/etcd/server/v3/lease"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
)
var (
etcdListenUrl = "http://localhost:8080"
)
func isErrorAddressAlreadyInUse(err error) bool {
var eOsSyscall *os.SyscallError
if !errors.As(err, &eOsSyscall) {
return false
}
var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr)
if !errors.As(eOsSyscall, &errErrno) {
return false
}
if errErrno == syscall.EADDRINUSE {
return true
}
const WSAEADDRINUSE = 10048
if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
return true
}
return false
}
func NewEtcdForTest(t *testing.T) *embed.Etcd {
cfg := embed.NewConfig()
cfg.Dir = t.TempDir()
os.Chmod(cfg.Dir, 0700) // nolint
cfg.LogLevel = "warn"
u, err := url.Parse(etcdListenUrl)
if err != nil {
t.Fatal(err)
}
// Find a free port to bind the server to.
var etcd *embed.Etcd
for port := 50000; port < 50100; port++ {
u.Host = net.JoinHostPort("localhost", strconv.Itoa(port))
cfg.ListenClientUrls = []url.URL{*u}
cfg.AdvertiseClientUrls = []url.URL{*u}
httpListener := u
httpListener.Host = net.JoinHostPort("localhost", strconv.Itoa(port+1))
cfg.ListenClientHttpUrls = []url.URL{*httpListener}
peerListener := u
peerListener.Host = net.JoinHostPort("localhost", strconv.Itoa(port+2))
cfg.ListenPeerUrls = []url.URL{*peerListener}
cfg.AdvertisePeerUrls = []url.URL{*peerListener}
cfg.InitialCluster = "default=" + peerListener.String()
cfg.ZapLoggerBuilder = embed.NewZapLoggerBuilder(zaptest.NewLogger(t, zaptest.Level(zap.WarnLevel)))
etcd, err = embed.StartEtcd(cfg)
if isErrorAddressAlreadyInUse(err) {
continue
} else if err != nil {
t.Fatal(err)
}
break
}
if etcd == nil {
t.Fatal("could not find free port")
}
t.Cleanup(func() {
etcd.Close()
<-etcd.Server.StopNotify()
})
// Wait for server to be ready.
<-etcd.Server.ReadyNotify()
return etcd
}
func NewEtcdClientForTest(t *testing.T) (*embed.Etcd, *EtcdClient) {
etcd := NewEtcdForTest(t)
config := goconf.NewConfigFile()
config.AddOption("etcd", "endpoints", etcd.Config().ListenClientUrls[0].String())
config.AddOption("etcd", "loglevel", "error")
client, err := NewEtcdClient(config, "")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := client.Close(); err != nil {
t.Error(err)
}
})
return etcd, client
}
func SetEtcdValue(etcd *embed.Etcd, key string, value []byte) {
if kv := etcd.Server.KV(); kv != nil {
kv.Put([]byte(key), value, lease.NoLease)
kv.Commit()
}
}
func DeleteEtcdValue(etcd *embed.Etcd, key string) {
if kv := etcd.Server.KV(); kv != nil {
kv.DeleteRange([]byte(key), nil)
kv.Commit()
}
}
func Test_EtcdClient_Get(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t)
if response, err := client.Get(context.Background(), "foo"); err != nil {
t.Error(err)
} else if response.Count != 0 {
t.Errorf("expected 0 response, got %d", response.Count)
}
SetEtcdValue(etcd, "foo", []byte("bar"))
if response, err := client.Get(context.Background(), "foo"); err != nil {
t.Error(err)
} else if response.Count != 1 {
t.Errorf("expected 1 responses, got %d", response.Count)
} else if string(response.Kvs[0].Key) != "foo" {
t.Errorf("expected key \"foo\", got \"%s\"", string(response.Kvs[0].Key))
} else if string(response.Kvs[0].Value) != "bar" {
t.Errorf("expected value \"bar\", got \"%s\"", string(response.Kvs[0].Value))
}
}
func Test_EtcdClient_GetPrefix(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t)
if response, err := client.Get(context.Background(), "foo"); err != nil {
t.Error(err)
} else if response.Count != 0 {
t.Errorf("expected 0 response, got %d", response.Count)
}
SetEtcdValue(etcd, "foo", []byte("1"))
SetEtcdValue(etcd, "foo/lala", []byte("2"))
SetEtcdValue(etcd, "lala/foo", []byte("3"))
if response, err := client.Get(context.Background(), "foo", clientv3.WithPrefix()); err != nil {
t.Error(err)
} else if response.Count != 2 {
t.Errorf("expected 2 responses, got %d", response.Count)
} else if string(response.Kvs[0].Key) != "foo" {
t.Errorf("expected key \"foo\", got \"%s\"", string(response.Kvs[0].Key))
} else if string(response.Kvs[0].Value) != "1" {
t.Errorf("expected value \"1\", got \"%s\"", string(response.Kvs[0].Value))
} else if string(response.Kvs[1].Key) != "foo/lala" {
t.Errorf("expected key \"foo/lala\", got \"%s\"", string(response.Kvs[1].Key))
} else if string(response.Kvs[1].Value) != "2" {
t.Errorf("expected value \"2\", got \"%s\"", string(response.Kvs[1].Value))
}
}
type etcdEvent struct {
t mvccpb.Event_EventType
key string
value string
prevValue string
}
type EtcdClientTestListener struct {
t *testing.T
ctx context.Context
cancel context.CancelFunc
initial chan struct{}
events chan etcdEvent
}
func NewEtcdClientTestListener(ctx context.Context, t *testing.T) *EtcdClientTestListener {
ctx, cancel := context.WithCancel(ctx)
return &EtcdClientTestListener{
t: t,
ctx: ctx,
cancel: cancel,
initial: make(chan struct{}),
events: make(chan etcdEvent),
}
}
func (l *EtcdClientTestListener) Close() {
l.cancel()
}
func (l *EtcdClientTestListener) EtcdClientCreated(client *EtcdClient) {
go func() {
if err := client.WaitForConnection(l.ctx); err != nil {
l.t.Errorf("error waiting for connection: %s", err)
return
}
ctx, cancel := context.WithTimeout(l.ctx, time.Second)
defer cancel()
response, err := client.Get(ctx, "foo", clientv3.WithPrefix())
if err != nil {
l.t.Error(err)
} else if response.Count != 1 {
l.t.Errorf("expected 1 responses, got %d", response.Count)
} else if string(response.Kvs[0].Key) != "foo/a" {
l.t.Errorf("expected key \"foo/a\", got \"%s\"", string(response.Kvs[0].Key))
} else if string(response.Kvs[0].Value) != "1" {
l.t.Errorf("expected value \"1\", got \"%s\"", string(response.Kvs[0].Value))
}
close(l.initial)
nextRevision := response.Header.Revision + 1
for l.ctx.Err() == nil {
var err error
if nextRevision, err = client.Watch(clientv3.WithRequireLeader(l.ctx), "foo", nextRevision, l, clientv3.WithPrefix()); err != nil {
l.t.Error(err)
}
}
}()
}
func (l *EtcdClientTestListener) EtcdWatchCreated(client *EtcdClient, key string) {
}
func (l *EtcdClientTestListener) EtcdKeyUpdated(client *EtcdClient, key string, value []byte, prevValue []byte) {
evt := etcdEvent{
t: clientv3.EventTypePut,
key: string(key),
value: string(value),
}
if len(prevValue) > 0 {
evt.prevValue = string(prevValue)
}
l.events <- evt
}
func (l *EtcdClientTestListener) EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte) {
evt := etcdEvent{
t: clientv3.EventTypeDelete,
key: string(key),
}
if len(prevValue) > 0 {
evt.prevValue = string(prevValue)
}
l.events <- evt
}
func Test_EtcdClient_Watch(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t)
SetEtcdValue(etcd, "foo/a", []byte("1"))
listener := NewEtcdClientTestListener(context.Background(), t)
defer listener.Close()
client.AddListener(listener)
defer client.RemoveListener(listener)
<-listener.initial
SetEtcdValue(etcd, "foo/b", []byte("2"))
event := <-listener.events
if event.t != clientv3.EventTypePut {
t.Errorf("expected type %d, got %d", clientv3.EventTypePut, event.t)
} else if event.key != "foo/b" {
t.Errorf("expected key %s, got %s", "foo/b", event.key)
} else if event.value != "2" {
t.Errorf("expected value %s, got %s", "2", event.value)
}
SetEtcdValue(etcd, "foo/a", []byte("3"))
event = <-listener.events
if event.t != clientv3.EventTypePut {
t.Errorf("expected type %d, got %d", clientv3.EventTypePut, event.t)
} else if event.key != "foo/a" {
t.Errorf("expected key %s, got %s", "foo/a", event.key)
} else if event.value != "3" {
t.Errorf("expected value %s, got %s", "3", event.value)
}
DeleteEtcdValue(etcd, "foo/a")
event = <-listener.events
if event.t != clientv3.EventTypeDelete {
t.Errorf("expected type %d, got %d", clientv3.EventTypeDelete, event.t)
} else if event.key != "foo/a" {
t.Errorf("expected key %s, got %s", "foo/a", event.key)
} else if event.prevValue != "3" {
t.Errorf("expected previous value %s, got %s", "3", event.prevValue)
}
}

168
file_watcher.go Normal file
View file

@ -0,0 +1,168 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2024 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"errors"
"log"
"os"
"path"
"path/filepath"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/fsnotify/fsnotify"
)
const (
defaultDeduplicateWatchEvents = 100 * time.Millisecond
)
var (
deduplicateWatchEvents atomic.Int64
)
func init() {
deduplicateWatchEvents.Store(int64(defaultDeduplicateWatchEvents))
}
type FileWatcherCallback func(filename string)
type FileWatcher struct {
filename string
target string
callback FileWatcherCallback
watcher *fsnotify.Watcher
closeCtx context.Context
closeFunc context.CancelFunc
}
func NewFileWatcher(filename string, callback FileWatcherCallback) (*FileWatcher, error) {
realFilename, err := filepath.EvalSymlinks(filename)
if err != nil {
return nil, err
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
if err := watcher.Add(realFilename); err != nil {
watcher.Close() // nolint
return nil, err
}
if err := watcher.Add(path.Dir(filename)); err != nil {
watcher.Close() // nolint
return nil, err
}
closeCtx, closeFunc := context.WithCancel(context.Background())
w := &FileWatcher{
filename: filename,
target: realFilename,
callback: callback,
watcher: watcher,
closeCtx: closeCtx,
closeFunc: closeFunc,
}
go w.run()
return w, nil
}
func (f *FileWatcher) Close() error {
f.closeFunc()
return f.watcher.Close()
}
func (f *FileWatcher) run() {
var mu sync.Mutex
timers := make(map[string]*time.Timer)
triggerEvent := func(event fsnotify.Event) {
deduplicate := time.Duration(deduplicateWatchEvents.Load())
if deduplicate <= 0 {
f.callback(f.filename)
return
}
// Use timer to deduplicate multiple events for the same file.
mu.Lock()
t, found := timers[event.Name]
mu.Unlock()
if !found {
t = time.AfterFunc(deduplicate, func() {
f.callback(f.filename)
mu.Lock()
delete(timers, event.Name)
mu.Unlock()
})
mu.Lock()
timers[event.Name] = t
mu.Unlock()
} else {
t.Reset(deduplicate)
}
}
for {
select {
case event := <-f.watcher.Events:
if !event.Has(fsnotify.Write) && !event.Has(fsnotify.Create) && !event.Has(fsnotify.Rename) {
continue
}
if stat, err := os.Lstat(event.Name); err != nil {
if !errors.Is(err, os.ErrNotExist) {
log.Printf("Could not lstat %s: %s", event.Name, err)
}
} else if stat.Mode()&os.ModeSymlink != 0 {
target, err := filepath.EvalSymlinks(event.Name)
if err == nil && target != f.target && strings.HasSuffix(event.Name, f.filename) {
f.target = target
triggerEvent(event)
}
continue
}
if strings.HasSuffix(event.Name, f.filename) || strings.HasSuffix(event.Name, f.target) {
triggerEvent(event)
}
case err := <-f.watcher.Errors:
if err == nil {
return
}
log.Printf("Error watching %s: %s", f.filename, err)
case <-f.closeCtx.Done():
return
}
}
}

310
file_watcher_test.go Normal file
View file

@ -0,0 +1,310 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2024 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"errors"
"os"
"path"
"testing"
)
var (
testWatcherNoEventTimeout = 2 * defaultDeduplicateWatchEvents
)
func TestFileWatcher_NotExist(t *testing.T) {
tmpdir := t.TempDir()
w, err := NewFileWatcher(path.Join(tmpdir, "test.txt"), func(filename string) {})
if err == nil {
t.Error("should not be able to watch non-existing files")
if err := w.Close(); err != nil {
t.Error(err)
}
} else if !errors.Is(err, os.ErrNotExist) {
t.Error(err)
}
}
func TestFileWatcher_File(t *testing.T) {
ensureNoGoroutinesLeak(t, func(t *testing.T) {
tmpdir := t.TempDir()
filename := path.Join(tmpdir, "test.txt")
if err := os.WriteFile(filename, []byte("Hello world!"), 0644); err != nil {
t.Fatal(err)
}
modified := make(chan struct{})
w, err := NewFileWatcher(filename, func(filename string) {
modified <- struct{}{}
})
if err != nil {
t.Fatal(err)
}
defer w.Close()
if err := os.WriteFile(filename, []byte("Updated"), 0644); err != nil {
t.Fatal(err)
}
<-modified
ctxTimeout, cancel := context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
if err := os.WriteFile(filename, []byte("Updated"), 0644); err != nil {
t.Fatal(err)
}
<-modified
ctxTimeout, cancel = context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
})
}
func TestFileWatcher_Rename(t *testing.T) {
tmpdir := t.TempDir()
filename := path.Join(tmpdir, "test.txt")
if err := os.WriteFile(filename, []byte("Hello world!"), 0644); err != nil {
t.Fatal(err)
}
modified := make(chan struct{})
w, err := NewFileWatcher(filename, func(filename string) {
modified <- struct{}{}
})
if err != nil {
t.Fatal(err)
}
defer w.Close()
filename2 := path.Join(tmpdir, "test.txt.tmp")
if err := os.WriteFile(filename2, []byte("Updated"), 0644); err != nil {
t.Fatal(err)
}
ctxTimeout, cancel := context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
if err := os.Rename(filename2, filename); err != nil {
t.Fatal(err)
}
<-modified
ctxTimeout, cancel = context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
}
func TestFileWatcher_Symlink(t *testing.T) {
tmpdir := t.TempDir()
sourceFilename := path.Join(tmpdir, "test1.txt")
if err := os.WriteFile(sourceFilename, []byte("Hello world!"), 0644); err != nil {
t.Fatal(err)
}
filename := path.Join(tmpdir, "symlink.txt")
if err := os.Symlink(sourceFilename, filename); err != nil {
t.Fatal(err)
}
modified := make(chan struct{})
w, err := NewFileWatcher(filename, func(filename string) {
modified <- struct{}{}
})
if err != nil {
t.Fatal(err)
}
defer w.Close()
if err := os.WriteFile(sourceFilename, []byte("Updated"), 0644); err != nil {
t.Fatal(err)
}
<-modified
ctxTimeout, cancel := context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
}
func TestFileWatcher_ChangeSymlinkTarget(t *testing.T) {
tmpdir := t.TempDir()
sourceFilename1 := path.Join(tmpdir, "test1.txt")
if err := os.WriteFile(sourceFilename1, []byte("Hello world!"), 0644); err != nil {
t.Fatal(err)
}
sourceFilename2 := path.Join(tmpdir, "test2.txt")
if err := os.WriteFile(sourceFilename2, []byte("Updated"), 0644); err != nil {
t.Fatal(err)
}
filename := path.Join(tmpdir, "symlink.txt")
if err := os.Symlink(sourceFilename1, filename); err != nil {
t.Fatal(err)
}
modified := make(chan struct{})
w, err := NewFileWatcher(filename, func(filename string) {
modified <- struct{}{}
})
if err != nil {
t.Fatal(err)
}
defer w.Close()
// Replace symlink by creating new one and rename it to the original target.
if err := os.Symlink(sourceFilename2, filename+".tmp"); err != nil {
t.Fatal(err)
}
if err := os.Rename(filename+".tmp", filename); err != nil {
t.Fatal(err)
}
<-modified
ctxTimeout, cancel := context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
}
func TestFileWatcher_OtherSymlink(t *testing.T) {
tmpdir := t.TempDir()
sourceFilename1 := path.Join(tmpdir, "test1.txt")
if err := os.WriteFile(sourceFilename1, []byte("Hello world!"), 0644); err != nil {
t.Fatal(err)
}
sourceFilename2 := path.Join(tmpdir, "test2.txt")
if err := os.WriteFile(sourceFilename2, []byte("Updated"), 0644); err != nil {
t.Fatal(err)
}
filename := path.Join(tmpdir, "symlink.txt")
if err := os.Symlink(sourceFilename1, filename); err != nil {
t.Fatal(err)
}
modified := make(chan struct{})
w, err := NewFileWatcher(filename, func(filename string) {
modified <- struct{}{}
})
if err != nil {
t.Fatal(err)
}
defer w.Close()
if err := os.Symlink(sourceFilename2, filename+".tmp"); err != nil {
t.Fatal(err)
}
ctxTimeout, cancel := context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received event for other symlink")
case <-ctxTimeout.Done():
}
}
func TestFileWatcher_RenameSymlinkTarget(t *testing.T) {
tmpdir := t.TempDir()
sourceFilename1 := path.Join(tmpdir, "test1.txt")
if err := os.WriteFile(sourceFilename1, []byte("Hello world!"), 0644); err != nil {
t.Fatal(err)
}
filename := path.Join(tmpdir, "test.txt")
if err := os.Symlink(sourceFilename1, filename); err != nil {
t.Fatal(err)
}
modified := make(chan struct{})
w, err := NewFileWatcher(filename, func(filename string) {
modified <- struct{}{}
})
if err != nil {
t.Fatal(err)
}
defer w.Close()
sourceFilename2 := path.Join(tmpdir, "test1.txt.tmp")
if err := os.WriteFile(sourceFilename2, []byte("Updated"), 0644); err != nil {
t.Fatal(err)
}
ctxTimeout, cancel := context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
if err := os.Rename(sourceFilename2, sourceFilename1); err != nil {
t.Fatal(err)
}
<-modified
ctxTimeout, cancel = context.WithTimeout(context.Background(), testWatcherNoEventTimeout)
defer cancel()
select {
case <-modified:
t.Error("should not have received another event")
case <-ctxTimeout.Done():
}
}

77
flags.go Normal file
View file

@ -0,0 +1,77 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"sync/atomic"
)
type Flags struct {
flags atomic.Uint32
}
func (f *Flags) Add(flags uint32) bool {
for {
old := f.flags.Load()
if old&flags == flags {
// Flags already set.
return false
}
newFlags := old | flags
if f.flags.CompareAndSwap(old, newFlags) {
return true
}
// Another thread updated the flags while we were checking, retry.
}
}
func (f *Flags) Remove(flags uint32) bool {
for {
old := f.flags.Load()
if old&flags == 0 {
// Flags not set.
return false
}
newFlags := old & ^flags
if f.flags.CompareAndSwap(old, newFlags) {
return true
}
// Another thread updated the flags while we were checking, retry.
}
}
func (f *Flags) Set(flags uint32) bool {
for {
old := f.flags.Load()
if old == flags {
return false
}
if f.flags.CompareAndSwap(old, flags) {
return true
}
}
}
func (f *Flags) Get() uint32 {
return f.flags.Load()
}

143
flags_test.go Normal file
View file

@ -0,0 +1,143 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2023 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"sync"
"sync/atomic"
"testing"
)
func TestFlags(t *testing.T) {
var f Flags
if f.Get() != 0 {
t.Fatalf("Expected flags 0, got %d", f.Get())
}
if !f.Add(1) {
t.Error("expected true")
}
if f.Get() != 1 {
t.Fatalf("Expected flags 1, got %d", f.Get())
}
if f.Add(1) {
t.Error("expected false")
}
if f.Get() != 1 {
t.Fatalf("Expected flags 1, got %d", f.Get())
}
if !f.Add(2) {
t.Error("expected true")
}
if f.Get() != 3 {
t.Fatalf("Expected flags 3, got %d", f.Get())
}
if !f.Remove(1) {
t.Error("expected true")
}
if f.Get() != 2 {
t.Fatalf("Expected flags 2, got %d", f.Get())
}
if f.Remove(1) {
t.Error("expected false")
}
if f.Get() != 2 {
t.Fatalf("Expected flags 2, got %d", f.Get())
}
if !f.Add(3) {
t.Error("expected true")
}
if f.Get() != 3 {
t.Fatalf("Expected flags 3, got %d", f.Get())
}
if !f.Remove(1) {
t.Error("expected true")
}
if f.Get() != 2 {
t.Fatalf("Expected flags 2, got %d", f.Get())
}
}
func runConcurrentFlags(t *testing.T, count int, f func()) {
var start sync.WaitGroup
start.Add(1)
var ready sync.WaitGroup
var done sync.WaitGroup
for i := 0; i < count; i++ {
done.Add(1)
ready.Add(1)
go func() {
defer done.Done()
ready.Done()
start.Wait()
f()
}()
}
ready.Wait()
start.Done()
done.Wait()
}
func TestFlagsConcurrentAdd(t *testing.T) {
t.Parallel()
var flags Flags
var added atomic.Int32
runConcurrentFlags(t, 100, func() {
if flags.Add(1) {
added.Add(1)
}
})
if added.Load() != 1 {
t.Errorf("expected only one successfull attempt, got %d", added.Load())
}
}
func TestFlagsConcurrentRemove(t *testing.T) {
t.Parallel()
var flags Flags
flags.Set(1)
var removed atomic.Int32
runConcurrentFlags(t, 100, func() {
if flags.Remove(1) {
removed.Add(1)
}
})
if removed.Load() != 1 {
t.Errorf("expected only one successfull attempt, got %d", removed.Load())
}
}
func TestFlagsConcurrentSet(t *testing.T) {
t.Parallel()
var flags Flags
var set atomic.Int32
runConcurrentFlags(t, 100, func() {
if flags.Set(1) {
set.Add(1)
}
})
if set.Load() != 1 {
t.Errorf("expected only one successfull attempt, got %d", set.Load())
}
}

100
geoip.go
View file

@ -35,6 +35,7 @@ import (
"sync"
"time"
"github.com/dlintw/goconf"
"github.com/oschwald/maxminddb-golang"
)
@ -156,36 +157,45 @@ func (g *GeoLookup) updateUrl() error {
}
body := response.Body
if strings.HasSuffix(g.url, ".gz") {
url := g.url
if strings.HasSuffix(url, ".gz") {
body, err = gzip.NewReader(body)
if err != nil {
return err
}
url = strings.TrimSuffix(url, ".gz")
}
tarfile := tar.NewReader(body)
var geoipdata []byte
for {
header, err := tarfile.Next()
if err == io.EOF {
if strings.HasSuffix(url, ".tar") || strings.HasSuffix(url, "=tar") {
tarfile := tar.NewReader(body)
for {
header, err := tarfile.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
if !strings.HasSuffix(header.Name, ".mmdb") {
continue
}
geoipdata, err = io.ReadAll(tarfile)
if err != nil {
return err
}
break
} else if err != nil {
return err
}
if !strings.HasSuffix(header.Name, ".mmdb") {
continue
}
geoipdata, err = io.ReadAll(tarfile)
} else {
geoipdata, err = io.ReadAll(body)
if err != nil {
return err
}
break
}
if len(geoipdata) == 0 {
return fmt.Errorf("did not find MaxMind database in tarball from %s", g.url)
return fmt.Errorf("did not find GeoIP database in download from %s", g.url)
}
reader, err := maxminddb.FromBytes(geoipdata)
@ -267,3 +277,63 @@ func IsValidContinent(continent string) bool {
return false
}
}
func LoadGeoIPOverrides(config *goconf.ConfigFile, ignoreErrors bool) (map[*net.IPNet]string, error) {
options, _ := GetStringOptions(config, "geoip-overrides", true)
if len(options) == 0 {
return nil, nil
}
var err error
geoipOverrides := make(map[*net.IPNet]string, len(options))
for option, value := range options {
var ip net.IP
var ipNet *net.IPNet
if strings.Contains(option, "/") {
_, ipNet, err = net.ParseCIDR(option)
if err != nil {
if ignoreErrors {
log.Printf("could not parse CIDR %s (%s), skipping", option, err)
continue
}
return nil, fmt.Errorf("could not parse CIDR %s: %s", option, err)
}
} else {
ip = net.ParseIP(option)
if ip == nil {
if ignoreErrors {
log.Printf("could not parse IP %s, skipping", option)
continue
}
return nil, fmt.Errorf("could not parse IP %s", option)
}
var mask net.IPMask
if ipv4 := ip.To4(); ipv4 != nil {
mask = net.CIDRMask(32, 32)
} else {
mask = net.CIDRMask(128, 128)
}
ipNet = &net.IPNet{
IP: ip,
Mask: mask,
}
}
value = strings.ToUpper(strings.TrimSpace(value))
if value == "" {
log.Printf("IP %s doesn't have a country assigned, skipping", option)
continue
} else if !IsValidCountry(value) {
log.Printf("Country %s for IP %s is invalid, skipping", value, option)
continue
}
log.Printf("Using country %s for %s", value, ipNet)
geoipOverrides[ipNet] = value
}
return geoipOverrides, nil
}

View file

@ -24,12 +24,14 @@ package signaling
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
"testing"
"time"
)
func testGeoLookupReader(t *testing.T, reader *GeoLookup) {
@ -57,13 +59,27 @@ func testGeoLookupReader(t *testing.T, reader *GeoLookup) {
}
}
func TestGeoLookup(t *testing.T) {
license := os.Getenv("MAXMIND_GEOLITE2_LICENSE")
if license == "" {
t.Skip("No MaxMind GeoLite2 license was set in MAXMIND_GEOLITE2_LICENSE environment variable.")
}
func GetGeoIpUrlForTest(t *testing.T) string {
t.Helper()
reader, err := NewGeoLookupFromUrl(GetGeoIpDownloadUrl(license))
var geoIpUrl string
if os.Getenv("USE_DB_IP_GEOIP_DATABASE") != "" {
now := time.Now().UTC()
geoIpUrl = fmt.Sprintf("https://download.db-ip.com/free/dbip-country-lite-%d-%.2d.mmdb.gz", now.Year(), now.Month())
}
if geoIpUrl == "" {
license := os.Getenv("MAXMIND_GEOLITE2_LICENSE")
if license == "" {
t.Skip("No MaxMind GeoLite2 license was set in MAXMIND_GEOLITE2_LICENSE environment variable.")
}
geoIpUrl = GetGeoIpDownloadUrl(license)
}
return geoIpUrl
}
func TestGeoLookup(t *testing.T) {
CatchLogForTest(t)
reader, err := NewGeoLookupFromUrl(GetGeoIpUrlForTest(t))
if err != nil {
t.Fatal(err)
}
@ -77,12 +93,8 @@ func TestGeoLookup(t *testing.T) {
}
func TestGeoLookupCaching(t *testing.T) {
license := os.Getenv("MAXMIND_GEOLITE2_LICENSE")
if license == "" {
t.Skip("No MaxMind GeoLite2 license was set in MAXMIND_GEOLITE2_LICENSE environment variable.")
}
reader, err := NewGeoLookupFromUrl(GetGeoIpDownloadUrl(license))
CatchLogForTest(t)
reader, err := NewGeoLookupFromUrl(GetGeoIpUrlForTest(t))
if err != nil {
t.Fatal(err)
}
@ -128,6 +140,7 @@ func TestGeoLookupContinent(t *testing.T) {
}
func TestGeoLookupCloseEmpty(t *testing.T) {
CatchLogForTest(t)
reader, err := NewGeoLookupFromUrl("ignore-url")
if err != nil {
t.Fatal(err)
@ -136,24 +149,23 @@ func TestGeoLookupCloseEmpty(t *testing.T) {
}
func TestGeoLookupFromFile(t *testing.T) {
license := os.Getenv("MAXMIND_GEOLITE2_LICENSE")
if license == "" {
t.Skip("No MaxMind GeoLite2 license was set in MAXMIND_GEOLITE2_LICENSE environment variable.")
}
CatchLogForTest(t)
geoIpUrl := GetGeoIpUrlForTest(t)
url := GetGeoIpDownloadUrl(license)
resp, err := http.Get(url)
resp, err := http.Get(geoIpUrl)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
body := resp.Body
if strings.HasSuffix(url, ".gz") {
url := geoIpUrl
if strings.HasSuffix(geoIpUrl, ".gz") {
body, err = gzip.NewReader(body)
if err != nil {
t.Fatal(err)
}
url = strings.TrimSuffix(url, ".gz")
}
tmpfile, err := os.CreateTemp("", "geoipdb")
@ -164,21 +176,33 @@ func TestGeoLookupFromFile(t *testing.T) {
os.Remove(tmpfile.Name())
})
tarfile := tar.NewReader(body)
foundDatabase := false
for {
header, err := tarfile.Next()
if err == io.EOF {
if strings.HasSuffix(url, ".tar") || strings.HasSuffix(url, "=tar") {
tarfile := tar.NewReader(body)
for {
header, err := tarfile.Next()
if err == io.EOF {
break
} else if err != nil {
t.Fatal(err)
}
if !strings.HasSuffix(header.Name, ".mmdb") {
continue
}
if _, err := io.Copy(tmpfile, tarfile); err != nil {
tmpfile.Close()
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
foundDatabase = true
break
} else if err != nil {
t.Fatal(err)
}
if !strings.HasSuffix(header.Name, ".mmdb") {
continue
}
if _, err := io.Copy(tmpfile, tarfile); err != nil {
} else {
if _, err := io.Copy(tmpfile, body); err != nil {
tmpfile.Close()
t.Fatal(err)
}
@ -186,11 +210,10 @@ func TestGeoLookupFromFile(t *testing.T) {
t.Fatal(err)
}
foundDatabase = true
break
}
if !foundDatabase {
t.Fatal("Did not find MaxMind database in tarball")
t.Fatalf("Did not find GeoIP database in download from %s", geoIpUrl)
}
reader, err := NewGeoLookupFromFile(tmpfile.Name())

108
go.mod
View file

@ -1,85 +1,89 @@
module github.com/strukturag/nextcloud-spreed-signaling
go 1.17
go 1.21
require (
github.com/dlintw/goconf v0.0.0-20120228082610-dcc070983490
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/securecookie v1.1.1
github.com/gorilla/websocket v1.5.0
github.com/fsnotify/fsnotify v1.7.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/gorilla/securecookie v1.1.2
github.com/gorilla/websocket v1.5.1
github.com/mailru/easyjson v0.7.7
github.com/nats-io/nats-server/v2 v2.8.4
github.com/nats-io/nats.go v1.16.0
github.com/nats-io/nats-server/v2 v2.10.16
github.com/nats-io/nats.go v1.35.0
github.com/notedit/janus-go v0.0.0-20200517101215-10eb8b95d1a0
github.com/oschwald/maxminddb-golang v1.9.0
github.com/pion/sdp v1.3.0
github.com/prometheus/client_golang v1.12.2
go.etcd.io/etcd/client/pkg/v3 v3.5.4
go.etcd.io/etcd/client/v3 v3.5.4
go.etcd.io/etcd/server/v3 v3.5.4
github.com/oschwald/maxminddb-golang v1.12.0
github.com/pion/sdp/v3 v3.0.9
github.com/prometheus/client_golang v1.19.1
go.etcd.io/etcd/api/v3 v3.5.13
go.etcd.io/etcd/client/pkg/v3 v3.5.13
go.etcd.io/etcd/client/v3 v3.5.13
go.etcd.io/etcd/server/v3 v3.5.13
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.64.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
google.golang.org/protobuf v1.34.1
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.14.4 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/minio/highwayhash v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a // indirect
github.com/nats-io/nkeys v0.3.0 // indirect
github.com/nats-io/jwt/v2 v2.5.7 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/etcd/api/v3 v3.5.4 // indirect
go.etcd.io/etcd/client/v2 v2.305.4 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.4 // indirect
go.etcd.io/etcd/raft/v3 v3.5.4 // indirect
go.opentelemetry.io/contrib v0.20.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
go.opentelemetry.io/otel v0.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v0.20.0 // indirect
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20220325203850-36772127a21f // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
google.golang.org/grpc v1.38.0 // indirect
google.golang.org/protobuf v1.26.0 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.etcd.io/etcd/client/v2 v2.305.13 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.13 // indirect
go.etcd.io/etcd/raft/v3 v3.5.13 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect
go.opentelemetry.io/otel v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect
go.opentelemetry.io/otel/metric v1.20.0 // indirect
go.opentelemetry.io/otel/sdk v1.20.0 // indirect
go.opentelemetry.io/otel/trace v1.20.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect

759
go.sum
View file

@ -1,97 +1,33 @@
cloud.google.com/go v0.26.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.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs=
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc=
github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlintw/goconf v0.0.0-20120228082610-dcc070983490 h1:I8/Qu5NTaiXi1TsEYmTeLDUlf7u9pEdbG+azjDvx8Vg=
github.com/dlintw/goconf v0.0.0-20120228082610-dcc070983490/go.mod h1:jWlUIP63OLr0cV2FGN2IEzSFsMAe58if8rk/SAE0JRE=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@ -99,686 +35,291 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
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/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
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.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/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.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I=
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=
github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4=
github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g=
github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
github.com/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c=
github.com/nats-io/jwt/v2 v2.5.7/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
github.com/nats-io/nats-server/v2 v2.10.16 h1:2jXaiydp5oB/nAx/Ytf9fdCi9QN6ItIc9eehX8kwVV0=
github.com/nats-io/nats-server/v2 v2.10.16/go.mod h1:Pksi38H2+6xLe1vQx0/EA4bzetM0NqyIHcIbmgXSkIU=
github.com/nats-io/nats.go v1.35.0 h1:XFNqNM7v5B+MQMKqVGAyHwYhyKb48jrenXNxIU20ULk=
github.com/nats-io/nats.go v1.35.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/notedit/janus-go v0.0.0-20200517101215-10eb8b95d1a0 h1:EFU9iv8BMPyBo8iFMHvQleYlF5M3PY6zpAbxsngImjE=
github.com/notedit/janus-go v0.0.0-20200517101215-10eb8b95d1a0/go.mod h1:BN/Txse3qz8tZOmCm2OfajB2wHVujWmX3o9nVdsI6gE=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y=
github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pion/sdp v1.3.0 h1:21lpgEILHyolpsIrbCBagZaAPj4o057cFjzaFebkVOs=
github.com/pion/sdp v1.3.0/go.mod h1:ceA2lTyftydQTuCIbUNoH77aAt6CiQJaRpssA4Gee8I=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
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/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.4 h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao=
go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
go.etcd.io/etcd/pkg/v3 v3.5.4 h1:V5Dvl7S39ZDwjkKqJG2BfXgxZ3QREqqKifWQgIw5IM0=
go.etcd.io/etcd/pkg/v3 v3.5.4/go.mod h1:OI+TtO+Aa3nhQSppMbwE4ld3uF1/fqqwbpfndbbrEe0=
go.etcd.io/etcd/raft/v3 v3.5.4 h1:YGrnAgRfgXloBNuqa+oBI/aRZMcK/1GS6trJePJ/Gqc=
go.etcd.io/etcd/raft/v3 v3.5.4/go.mod h1:SCuunjYvZFC0fBX0vxMSPjuZmpcSk+XaAcMrD6Do03w=
go.etcd.io/etcd/server/v3 v3.5.4 h1:CMAZd0g8Bn5NRhynW6pKhc4FRg41/0QYy3d7aNm9874=
go.etcd.io/etcd/server/v3 v3.5.4/go.mod h1:S5/YTU15KxymM5l3T6b09sNOHPXqGYIZStpuuGbb65c=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0=
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 h1:sO4WKdPAudZGKPcpZT4MJn6JaDmpyLrMPDGGyA1SttE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw=
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8=
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g=
go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8=
go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd/api/v3 v3.5.13 h1:8WXU2/NBge6AUF1K1gOexB6e07NgsN1hXK0rSTtgSp4=
go.etcd.io/etcd/api/v3 v3.5.13/go.mod h1:gBqlqkcMMZMVTMm4NDZloEVJzxQOQIls8splbqBDa0c=
go.etcd.io/etcd/client/pkg/v3 v3.5.13 h1:RVZSAnWWWiI5IrYAXjQorajncORbS0zI48LQlE2kQWg=
go.etcd.io/etcd/client/pkg/v3 v3.5.13/go.mod h1:XxHT4u1qU12E2+po+UVPrEeL94Um6zL58ppuJWXSAB8=
go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8=
go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg=
go.etcd.io/etcd/client/v3 v3.5.13 h1:o0fHTNJLeO0MyVbc7I3fsCf6nrOqn5d+diSarKnB2js=
go.etcd.io/etcd/client/v3 v3.5.13/go.mod h1:cqiAeY8b5DEEcpxvgWKsbLIWNM/8Wy2xJSDMtioMcoI=
go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M=
go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0=
go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA=
go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw=
go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok=
go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0=
go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM=
go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0=
go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/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/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
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/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=
golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/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-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

38
grpc_backend.proto Normal file
View file

@ -0,0 +1,38 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
option go_package = "github.com/strukturag/nextcloud-spreed-signaling;signaling";
package signaling;
service RpcBackend {
rpc GetSessionCount(GetSessionCountRequest) returns (GetSessionCountReply) {}
}
message GetSessionCountRequest {
string url = 1;
}
message GetSessionCountReply {
uint32 count = 1;
}

936
grpc_client.go Normal file
View file

@ -0,0 +1,936 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net"
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/dlintw/goconf"
clientv3 "go.etcd.io/etcd/client/v3"
"google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver"
status "google.golang.org/grpc/status"
)
const (
GrpcTargetTypeStatic = "static"
GrpcTargetTypeEtcd = "etcd"
DefaultGrpcTargetType = GrpcTargetTypeStatic
)
var (
ErrNoSuchResumeId = fmt.Errorf("unknown resume id")
customResolverPrefix atomic.Uint64
)
func init() {
RegisterGrpcClientStats()
}
type grpcClientImpl struct {
RpcBackendClient
RpcInternalClient
RpcMcuClient
RpcSessionsClient
}
func newGrpcClientImpl(conn grpc.ClientConnInterface) *grpcClientImpl {
return &grpcClientImpl{
RpcBackendClient: NewRpcBackendClient(conn),
RpcInternalClient: NewRpcInternalClient(conn),
RpcMcuClient: NewRpcMcuClient(conn),
RpcSessionsClient: NewRpcSessionsClient(conn),
}
}
type GrpcClient struct {
ip net.IP
target string
conn *grpc.ClientConn
impl *grpcClientImpl
isSelf atomic.Bool
}
type customIpResolver struct {
resolver.Builder
resolver.Resolver
scheme string
addr string
hostname string
}
func (r *customIpResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
state := resolver.State{
Addresses: []resolver.Address{
{
Addr: r.addr,
ServerName: r.hostname,
},
},
}
if err := cc.UpdateState(state); err != nil {
return nil, err
}
return r, nil
}
func (r *customIpResolver) Scheme() string {
return r.scheme
}
func (r *customIpResolver) ResolveNow(opts resolver.ResolveNowOptions) {
// Noop, we use a static configuration.
}
func (r *customIpResolver) Close() {
// Noop
}
func NewGrpcClient(target string, ip net.IP, opts ...grpc.DialOption) (*GrpcClient, error) {
var conn *grpc.ClientConn
var err error
if ip != nil {
prefix := customResolverPrefix.Add(1)
addr := ip.String()
hostname := target
if host, port, err := net.SplitHostPort(target); err == nil {
addr = net.JoinHostPort(addr, port)
hostname = host
}
resolver := &customIpResolver{
scheme: fmt.Sprintf("custom%d", prefix),
addr: addr,
hostname: hostname,
}
opts = append(opts, grpc.WithResolvers(resolver))
conn, err = grpc.NewClient(fmt.Sprintf("%s://%s", resolver.Scheme(), target), opts...)
} else {
conn, err = grpc.NewClient(target, opts...)
}
if err != nil {
return nil, err
}
result := &GrpcClient{
ip: ip,
target: target,
conn: conn,
impl: newGrpcClientImpl(conn),
}
if ip != nil {
result.target += " (" + ip.String() + ")"
}
return result, nil
}
func (c *GrpcClient) Target() string {
return c.target
}
func (c *GrpcClient) Close() error {
return c.conn.Close()
}
func (c *GrpcClient) IsSelf() bool {
return c.isSelf.Load()
}
func (c *GrpcClient) SetSelf(self bool) {
c.isSelf.Store(self)
}
func (c *GrpcClient) GetServerId(ctx context.Context) (string, error) {
statsGrpcClientCalls.WithLabelValues("GetServerId").Inc()
response, err := c.impl.GetServerId(ctx, &GetServerIdRequest{}, grpc.WaitForReady(true))
if err != nil {
return "", err
}
return response.GetServerId(), nil
}
func (c *GrpcClient) LookupResumeId(ctx context.Context, resumeId string) (*LookupResumeIdReply, error) {
statsGrpcClientCalls.WithLabelValues("LookupResumeId").Inc()
// TODO: Remove debug logging
log.Printf("Lookup resume id %s on %s", resumeId, c.Target())
response, err := c.impl.LookupResumeId(ctx, &LookupResumeIdRequest{
ResumeId: resumeId,
}, grpc.WaitForReady(true))
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return nil, ErrNoSuchResumeId
} else if err != nil {
return nil, err
}
if sessionId := response.GetSessionId(); sessionId == "" {
return nil, ErrNoSuchResumeId
}
return response, nil
}
func (c *GrpcClient) LookupSessionId(ctx context.Context, roomSessionId string, disconnectReason string) (string, error) {
statsGrpcClientCalls.WithLabelValues("LookupSessionId").Inc()
// TODO: Remove debug logging
log.Printf("Lookup room session %s on %s", roomSessionId, c.Target())
response, err := c.impl.LookupSessionId(ctx, &LookupSessionIdRequest{
RoomSessionId: roomSessionId,
DisconnectReason: disconnectReason,
}, grpc.WaitForReady(true))
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return "", ErrNoSuchRoomSession
} else if err != nil {
return "", err
}
sessionId := response.GetSessionId()
if sessionId == "" {
return "", ErrNoSuchRoomSession
}
return sessionId, nil
}
func (c *GrpcClient) IsSessionInCall(ctx context.Context, sessionId string, room *Room) (bool, error) {
statsGrpcClientCalls.WithLabelValues("IsSessionInCall").Inc()
// TODO: Remove debug logging
log.Printf("Check if session %s is in call %s on %s", sessionId, room.Id(), c.Target())
response, err := c.impl.IsSessionInCall(ctx, &IsSessionInCallRequest{
SessionId: sessionId,
RoomId: room.Id(),
BackendUrl: room.Backend().url,
}, grpc.WaitForReady(true))
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return false, nil
} else if err != nil {
return false, err
}
return response.GetInCall(), nil
}
func (c *GrpcClient) GetPublisherId(ctx context.Context, sessionId string, streamType StreamType) (string, string, net.IP, error) {
statsGrpcClientCalls.WithLabelValues("GetPublisherId").Inc()
// TODO: Remove debug logging
log.Printf("Get %s publisher id %s on %s", streamType, sessionId, c.Target())
response, err := c.impl.GetPublisherId(ctx, &GetPublisherIdRequest{
SessionId: sessionId,
StreamType: string(streamType),
}, grpc.WaitForReady(true))
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return "", "", nil, nil
} else if err != nil {
return "", "", nil, err
}
return response.GetPublisherId(), response.GetProxyUrl(), net.ParseIP(response.GetIp()), nil
}
func (c *GrpcClient) GetSessionCount(ctx context.Context, u *url.URL) (uint32, error) {
statsGrpcClientCalls.WithLabelValues("GetSessionCount").Inc()
// TODO: Remove debug logging
log.Printf("Get session count for %s on %s", u, c.Target())
response, err := c.impl.GetSessionCount(ctx, &GetSessionCountRequest{
Url: u.String(),
}, grpc.WaitForReady(true))
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return 0, nil
} else if err != nil {
return 0, err
}
return response.GetCount(), nil
}
type ProxySessionReceiver interface {
RemoteAddr() string
Country() string
UserAgent() string
OnProxyMessage(message *ServerSessionMessage) error
OnProxyClose(err error)
}
type SessionProxy struct {
sessionId string
receiver ProxySessionReceiver
sendMu sync.Mutex
client RpcSessions_ProxySessionClient
}
func (p *SessionProxy) recvPump() {
var closeError error
defer func() {
p.receiver.OnProxyClose(closeError)
if err := p.Close(); err != nil {
log.Printf("Error closing proxy for session %s: %s", p.sessionId, err)
}
}()
for {
msg, err := p.client.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
log.Printf("Error receiving message from proxy for session %s: %s", p.sessionId, err)
closeError = err
break
}
if err := p.receiver.OnProxyMessage(msg); err != nil {
log.Printf("Error processing message %+v from proxy for session %s: %s", msg, p.sessionId, err)
}
}
}
func (p *SessionProxy) Send(message *ClientSessionMessage) error {
p.sendMu.Lock()
defer p.sendMu.Unlock()
return p.client.Send(message)
}
func (p *SessionProxy) Close() error {
p.sendMu.Lock()
defer p.sendMu.Unlock()
return p.client.CloseSend()
}
func (c *GrpcClient) ProxySession(ctx context.Context, sessionId string, receiver ProxySessionReceiver) (*SessionProxy, error) {
statsGrpcClientCalls.WithLabelValues("ProxySession").Inc()
md := metadata.Pairs(
"sessionId", sessionId,
"remoteAddr", receiver.RemoteAddr(),
"country", receiver.Country(),
"userAgent", receiver.UserAgent(),
)
client, err := c.impl.ProxySession(metadata.NewOutgoingContext(ctx, md), grpc.WaitForReady(true))
if err != nil {
return nil, err
}
proxy := &SessionProxy{
sessionId: sessionId,
receiver: receiver,
client: client,
}
go proxy.recvPump()
return proxy, nil
}
type grpcClientsList struct {
clients []*GrpcClient
entry *DnsMonitorEntry
}
type GrpcClients struct {
mu sync.RWMutex
clientsMap map[string]*grpcClientsList
clients []*GrpcClient
dnsMonitor *DnsMonitor
dnsDiscovery bool
etcdClient *EtcdClient
targetPrefix string
targetInformation map[string]*GrpcTargetInformationEtcd
dialOptions atomic.Value // []grpc.DialOption
creds credentials.TransportCredentials
initializedCtx context.Context
initializedFunc context.CancelFunc
wakeupChanForTesting chan struct{}
selfCheckWaitGroup sync.WaitGroup
closeCtx context.Context
closeFunc context.CancelFunc
}
func NewGrpcClients(config *goconf.ConfigFile, etcdClient *EtcdClient, dnsMonitor *DnsMonitor) (*GrpcClients, error) {
initializedCtx, initializedFunc := context.WithCancel(context.Background())
closeCtx, closeFunc := context.WithCancel(context.Background())
result := &GrpcClients{
dnsMonitor: dnsMonitor,
etcdClient: etcdClient,
initializedCtx: initializedCtx,
initializedFunc: initializedFunc,
closeCtx: closeCtx,
closeFunc: closeFunc,
}
if err := result.load(config, false); err != nil {
return nil, err
}
return result, nil
}
func (c *GrpcClients) load(config *goconf.ConfigFile, fromReload bool) error {
creds, err := NewReloadableCredentials(config, false)
if err != nil {
return err
}
if c.creds != nil {
if cr, ok := c.creds.(*reloadableCredentials); ok {
cr.Close()
}
}
c.creds = creds
opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
c.dialOptions.Store(opts)
targetType, _ := config.GetString("grpc", "targettype")
if targetType == "" {
targetType = DefaultGrpcTargetType
}
switch targetType {
case GrpcTargetTypeStatic:
err = c.loadTargetsStatic(config, fromReload, opts...)
case GrpcTargetTypeEtcd:
err = c.loadTargetsEtcd(config, fromReload, opts...)
default:
err = fmt.Errorf("unknown GRPC target type: %s", targetType)
}
return err
}
func (c *GrpcClients) closeClient(client *GrpcClient) {
if client.IsSelf() {
// Already closed.
return
}
if err := client.Close(); err != nil {
log.Printf("Error closing client to %s: %s", client.Target(), err)
}
}
func (c *GrpcClients) isClientAvailable(target string, client *GrpcClient) bool {
c.mu.RLock()
defer c.mu.RUnlock()
entries, found := c.clientsMap[target]
if !found {
return false
}
for _, entry := range entries.clients {
if entry == client {
return true
}
}
return false
}
func (c *GrpcClients) getServerIdWithTimeout(ctx context.Context, client *GrpcClient) (string, error) {
ctx2, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
id, err := client.GetServerId(ctx2)
return id, err
}
func (c *GrpcClients) checkIsSelf(ctx context.Context, target string, client *GrpcClient) {
backoff, _ := NewExponentialBackoff(initialWaitDelay, maxWaitDelay)
defer c.selfCheckWaitGroup.Done()
loop:
for {
select {
case <-ctx.Done():
// Cancelled
return
default:
if !c.isClientAvailable(target, client) {
return
}
id, err := c.getServerIdWithTimeout(ctx, client)
if err != nil {
if errors.Is(err, context.Canceled) {
return
}
if status.Code(err) != codes.Canceled {
log.Printf("Error checking GRPC server id of %s, retrying in %s: %s", client.Target(), backoff.NextWait(), err)
}
backoff.Wait(ctx)
continue
}
if id == GrpcServerId {
log.Printf("GRPC target %s is this server, removing", client.Target())
c.closeClient(client)
client.SetSelf(true)
} else {
log.Printf("Checked GRPC server id of %s", client.Target())
}
break loop
}
}
}
func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bool, opts ...grpc.DialOption) error {
c.mu.Lock()
defer c.mu.Unlock()
dnsDiscovery, _ := config.GetBool("grpc", "dnsdiscovery")
if dnsDiscovery != c.dnsDiscovery {
if !dnsDiscovery {
for _, entry := range c.clientsMap {
if entry.entry != nil {
c.dnsMonitor.Remove(entry.entry)
entry.entry = nil
}
}
}
c.dnsDiscovery = dnsDiscovery
}
clientsMap := make(map[string]*grpcClientsList)
var clients []*GrpcClient
removeTargets := make(map[string]bool, len(c.clientsMap))
for target, entries := range c.clientsMap {
removeTargets[target] = true
clientsMap[target] = entries
}
targets, _ := config.GetString("grpc", "targets")
for _, target := range strings.Split(targets, ",") {
target = strings.TrimSpace(target)
if target == "" {
continue
}
if entries, found := clientsMap[target]; found {
clients = append(clients, entries.clients...)
if dnsDiscovery && entries.entry == nil {
entry, err := c.dnsMonitor.Add(target, c.onLookup)
if err != nil {
return err
}
entries.entry = entry
}
delete(removeTargets, target)
continue
}
host := target
if h, _, err := net.SplitHostPort(target); err == nil {
host = h
}
if dnsDiscovery && net.ParseIP(host) == nil {
// Use dedicated client for each IP address.
entry, err := c.dnsMonitor.Add(target, c.onLookup)
if err != nil {
return err
}
clientsMap[target] = &grpcClientsList{
entry: entry,
}
continue
}
client, err := NewGrpcClient(target, nil, opts...)
if err != nil {
for _, entry := range clientsMap {
for _, client := range entry.clients {
c.closeClient(client)
}
if entry.entry != nil {
c.dnsMonitor.Remove(entry.entry)
entry.entry = nil
}
}
return err
}
c.selfCheckWaitGroup.Add(1)
go c.checkIsSelf(c.closeCtx, target, client)
log.Printf("Adding %s as GRPC target", client.Target())
entry, found := clientsMap[target]
if !found {
entry = &grpcClientsList{}
clientsMap[target] = entry
}
entry.clients = append(entry.clients, client)
clients = append(clients, client)
}
for target := range removeTargets {
if entry, found := clientsMap[target]; found {
for _, client := range entry.clients {
log.Printf("Deleting GRPC target %s", client.Target())
c.closeClient(client)
}
if entry.entry != nil {
c.dnsMonitor.Remove(entry.entry)
entry.entry = nil
}
delete(clientsMap, target)
}
}
c.clients = clients
c.clientsMap = clientsMap
c.initializedFunc()
statsGrpcClients.Set(float64(len(clients)))
return nil
}
func (c *GrpcClients) onLookup(entry *DnsMonitorEntry, all []net.IP, added []net.IP, keep []net.IP, removed []net.IP) {
c.mu.Lock()
defer c.mu.Unlock()
target := entry.URL()
e, found := c.clientsMap[target]
if !found {
return
}
opts := c.dialOptions.Load().([]grpc.DialOption)
mapModified := false
var newClients []*GrpcClient
for _, ip := range removed {
for _, client := range e.clients {
if ip.Equal(client.ip) {
mapModified = true
log.Printf("Removing connection to %s", client.Target())
c.closeClient(client)
c.wakeupForTesting()
}
}
}
for _, ip := range keep {
for _, client := range e.clients {
if ip.Equal(client.ip) {
newClients = append(newClients, client)
}
}
}
for _, ip := range added {
client, err := NewGrpcClient(target, ip, opts...)
if err != nil {
log.Printf("Error creating client to %s with IP %s: %s", target, ip.String(), err)
continue
}
c.selfCheckWaitGroup.Add(1)
go c.checkIsSelf(c.closeCtx, target, client)
log.Printf("Adding %s as GRPC target", client.Target())
newClients = append(newClients, client)
mapModified = true
c.wakeupForTesting()
}
if mapModified {
c.clientsMap[target].clients = newClients
c.clients = make([]*GrpcClient, 0, len(c.clientsMap))
for _, entry := range c.clientsMap {
c.clients = append(c.clients, entry.clients...)
}
statsGrpcClients.Set(float64(len(c.clients)))
}
}
func (c *GrpcClients) loadTargetsEtcd(config *goconf.ConfigFile, fromReload bool, opts ...grpc.DialOption) error {
if !c.etcdClient.IsConfigured() {
return fmt.Errorf("No etcd endpoints configured")
}
targetPrefix, _ := config.GetString("grpc", "targetprefix")
if targetPrefix == "" {
return fmt.Errorf("No GRPC target prefix configured")
}
c.targetPrefix = targetPrefix
if c.targetInformation == nil {
c.targetInformation = make(map[string]*GrpcTargetInformationEtcd)
}
c.etcdClient.AddListener(c)
return nil
}
func (c *GrpcClients) EtcdClientCreated(client *EtcdClient) {
go func() {
if err := client.WaitForConnection(c.closeCtx); err != nil {
if errors.Is(err, context.Canceled) {
return
}
panic(err)
}
backoff, _ := NewExponentialBackoff(initialWaitDelay, maxWaitDelay)
var nextRevision int64
for c.closeCtx.Err() == nil {
response, err := c.getGrpcTargets(c.closeCtx, client, c.targetPrefix)
if err != nil {
if errors.Is(err, context.Canceled) {
return
} else if errors.Is(err, context.DeadlineExceeded) {
log.Printf("Timeout getting initial list of GRPC targets, retry in %s", backoff.NextWait())
} else {
log.Printf("Could not get initial list of GRPC targets, retry in %s: %s", backoff.NextWait(), err)
}
backoff.Wait(c.closeCtx)
continue
}
for _, ev := range response.Kvs {
c.EtcdKeyUpdated(client, string(ev.Key), ev.Value, nil)
}
c.initializedFunc()
nextRevision = response.Header.Revision + 1
break
}
prevRevision := nextRevision
backoff.Reset()
for c.closeCtx.Err() == nil {
var err error
if nextRevision, err = client.Watch(c.closeCtx, c.targetPrefix, nextRevision, c, clientv3.WithPrefix()); err != nil {
log.Printf("Error processing watch for %s (%s), retry in %s", c.targetPrefix, err, backoff.NextWait())
backoff.Wait(c.closeCtx)
continue
}
if nextRevision != prevRevision {
backoff.Reset()
prevRevision = nextRevision
} else {
log.Printf("Processing watch for %s interrupted, retry in %s", c.targetPrefix, backoff.NextWait())
backoff.Wait(c.closeCtx)
}
}
}()
}
func (c *GrpcClients) EtcdWatchCreated(client *EtcdClient, key string) {
}
func (c *GrpcClients) getGrpcTargets(ctx context.Context, client *EtcdClient, targetPrefix string) (*clientv3.GetResponse, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
return client.Get(ctx, targetPrefix, clientv3.WithPrefix())
}
func (c *GrpcClients) EtcdKeyUpdated(client *EtcdClient, key string, data []byte, prevValue []byte) {
var info GrpcTargetInformationEtcd
if err := json.Unmarshal(data, &info); err != nil {
log.Printf("Could not decode GRPC target %s=%s: %s", key, string(data), err)
return
}
if err := info.CheckValid(); err != nil {
log.Printf("Received invalid GRPC target %s=%s: %s", key, string(data), err)
return
}
c.mu.Lock()
defer c.mu.Unlock()
prev, found := c.targetInformation[key]
if found && prev.Address != info.Address {
// Address of endpoint has changed, remove old one.
c.removeEtcdClientLocked(key)
}
if _, found := c.clientsMap[info.Address]; found {
log.Printf("GRPC target %s already exists, ignoring %s", info.Address, key)
return
}
opts := c.dialOptions.Load().([]grpc.DialOption)
cl, err := NewGrpcClient(info.Address, nil, opts...)
if err != nil {
log.Printf("Could not create GRPC client for target %s: %s", info.Address, err)
return
}
c.selfCheckWaitGroup.Add(1)
go c.checkIsSelf(c.closeCtx, info.Address, cl)
log.Printf("Adding %s as GRPC target", cl.Target())
if c.clientsMap == nil {
c.clientsMap = make(map[string]*grpcClientsList)
}
c.clientsMap[info.Address] = &grpcClientsList{
clients: []*GrpcClient{cl},
}
c.clients = append(c.clients, cl)
c.targetInformation[key] = &info
statsGrpcClients.Inc()
c.wakeupForTesting()
}
func (c *GrpcClients) EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte) {
c.mu.Lock()
defer c.mu.Unlock()
c.removeEtcdClientLocked(key)
}
func (c *GrpcClients) removeEtcdClientLocked(key string) {
info, found := c.targetInformation[key]
if !found {
log.Printf("No connection found for %s, ignoring", key)
c.wakeupForTesting()
return
}
delete(c.targetInformation, key)
entry, found := c.clientsMap[info.Address]
if !found {
return
}
for _, client := range entry.clients {
log.Printf("Removing connection to %s (from %s)", client.Target(), key)
c.closeClient(client)
}
delete(c.clientsMap, info.Address)
c.clients = make([]*GrpcClient, 0, len(c.clientsMap))
for _, entry := range c.clientsMap {
c.clients = append(c.clients, entry.clients...)
}
statsGrpcClients.Dec()
c.wakeupForTesting()
}
func (c *GrpcClients) WaitForInitialized(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-c.initializedCtx.Done():
return nil
}
}
func (c *GrpcClients) wakeupForTesting() {
if c.wakeupChanForTesting == nil {
return
}
select {
case c.wakeupChanForTesting <- struct{}{}:
default:
}
}
func (c *GrpcClients) Reload(config *goconf.ConfigFile) {
if err := c.load(config, true); err != nil {
log.Printf("Could not reload RPC clients: %s", err)
}
}
func (c *GrpcClients) Close() {
c.mu.Lock()
defer c.mu.Unlock()
for _, entry := range c.clientsMap {
for _, client := range entry.clients {
if err := client.Close(); err != nil {
log.Printf("Error closing client to %s: %s", client.Target(), err)
}
}
if entry.entry != nil {
c.dnsMonitor.Remove(entry.entry)
entry.entry = nil
}
}
c.clients = nil
c.clientsMap = nil
c.dnsDiscovery = false
if c.etcdClient != nil {
c.etcdClient.RemoveListener(c)
}
if c.creds != nil {
if cr, ok := c.creds.(*reloadableCredentials); ok {
cr.Close()
}
}
c.closeFunc()
}
func (c *GrpcClients) GetClients() []*GrpcClient {
c.mu.RLock()
defer c.mu.RUnlock()
if len(c.clients) == 0 {
return c.clients
}
result := make([]*GrpcClient, 0, len(c.clients)-1)
for _, client := range c.clients {
if client.IsSelf() {
continue
}
result = append(result, client)
}
return result
}

389
grpc_client_test.go Normal file
View file

@ -0,0 +1,389 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"crypto/rand"
"crypto/rsa"
"fmt"
"net"
"os"
"path"
"testing"
"time"
"github.com/dlintw/goconf"
"go.etcd.io/etcd/server/v3/embed"
)
func (c *GrpcClients) getWakeupChannelForTesting() <-chan struct{} {
c.mu.Lock()
defer c.mu.Unlock()
if c.wakeupChanForTesting != nil {
return c.wakeupChanForTesting
}
ch := make(chan struct{}, 1)
c.wakeupChanForTesting = ch
return ch
}
func NewGrpcClientsForTestWithConfig(t *testing.T, config *goconf.ConfigFile, etcdClient *EtcdClient) (*GrpcClients, *DnsMonitor) {
dnsMonitor := newDnsMonitorForTest(t, time.Hour) // will be updated manually
client, err := NewGrpcClients(config, etcdClient, dnsMonitor)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
client.Close()
})
return client, dnsMonitor
}
func NewGrpcClientsForTest(t *testing.T, addr string) (*GrpcClients, *DnsMonitor) {
config := goconf.NewConfigFile()
config.AddOption("grpc", "targets", addr)
config.AddOption("grpc", "dnsdiscovery", "true")
return NewGrpcClientsForTestWithConfig(t, config, nil)
}
func NewGrpcClientsWithEtcdForTest(t *testing.T, etcd *embed.Etcd) (*GrpcClients, *DnsMonitor) {
config := goconf.NewConfigFile()
config.AddOption("etcd", "endpoints", etcd.Config().ListenClientUrls[0].String())
config.AddOption("grpc", "targettype", "etcd")
config.AddOption("grpc", "targetprefix", "/grpctargets")
etcdClient, err := NewEtcdClient(config, "")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := etcdClient.Close(); err != nil {
t.Error(err)
}
})
return NewGrpcClientsForTestWithConfig(t, config, etcdClient)
}
func drainWakeupChannel(ch <-chan struct{}) {
for {
select {
case <-ch:
default:
return
}
}
}
func waitForEvent(ctx context.Context, t *testing.T, ch <-chan struct{}) {
t.Helper()
select {
case <-ch:
return
case <-ctx.Done():
t.Error("timeout waiting for event")
}
}
func Test_GrpcClients_EtcdInitial(t *testing.T) {
CatchLogForTest(t)
ensureNoGoroutinesLeak(t, func(t *testing.T) {
_, addr1 := NewGrpcServerForTest(t)
_, addr2 := NewGrpcServerForTest(t)
etcd := NewEtcdForTest(t)
SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}"))
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}"))
client, _ := NewGrpcClientsWithEtcdForTest(t, etcd)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := client.WaitForInitialized(ctx); err != nil {
t.Fatal(err)
}
if clients := client.GetClients(); len(clients) != 2 {
t.Errorf("Expected two clients, got %+v", clients)
}
})
}
func Test_GrpcClients_EtcdUpdate(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd := NewEtcdForTest(t)
client, _ := NewGrpcClientsWithEtcdForTest(t, etcd)
ch := client.getWakeupChannelForTesting()
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
if clients := client.GetClients(); len(clients) != 0 {
t.Errorf("Expected no clients, got %+v", clients)
}
drainWakeupChannel(ch)
_, addr1 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}"))
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr1 {
t.Errorf("Expected target %s, got %s", addr1, clients[0].Target())
}
drainWakeupChannel(ch)
_, addr2 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}"))
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 2 {
t.Errorf("Expected two clients, got %+v", clients)
} else if clients[0].Target() != addr1 {
t.Errorf("Expected target %s, got %s", addr1, clients[0].Target())
} else if clients[1].Target() != addr2 {
t.Errorf("Expected target %s, got %s", addr2, clients[1].Target())
}
drainWakeupChannel(ch)
DeleteEtcdValue(etcd, "/grpctargets/one")
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr2 {
t.Errorf("Expected target %s, got %s", addr2, clients[0].Target())
}
drainWakeupChannel(ch)
_, addr3 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr3+"\"}"))
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr3 {
t.Errorf("Expected target %s, got %s", addr3, clients[0].Target())
}
}
func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd := NewEtcdForTest(t)
client, _ := NewGrpcClientsWithEtcdForTest(t, etcd)
ch := client.getWakeupChannelForTesting()
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
if clients := client.GetClients(); len(clients) != 0 {
t.Errorf("Expected no clients, got %+v", clients)
}
drainWakeupChannel(ch)
_, addr1 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}"))
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr1 {
t.Errorf("Expected target %s, got %s", addr1, clients[0].Target())
}
drainWakeupChannel(ch)
server2, addr2 := NewGrpcServerForTest(t)
server2.serverId = GrpcServerId
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}"))
waitForEvent(ctx, t, ch)
client.selfCheckWaitGroup.Wait()
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr1 {
t.Errorf("Expected target %s, got %s", addr1, clients[0].Target())
}
drainWakeupChannel(ch)
DeleteEtcdValue(etcd, "/grpctargets/two")
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr1 {
t.Errorf("Expected target %s, got %s", addr1, clients[0].Target())
}
}
func Test_GrpcClients_DnsDiscovery(t *testing.T) {
CatchLogForTest(t)
ensureNoGoroutinesLeak(t, func(t *testing.T) {
lookup := newMockDnsLookupForTest(t)
target := "testgrpc:12345"
ip1 := net.ParseIP("192.168.0.1")
ip2 := net.ParseIP("192.168.0.2")
targetWithIp1 := fmt.Sprintf("%s (%s)", target, ip1)
targetWithIp2 := fmt.Sprintf("%s (%s)", target, ip2)
lookup.Set("testgrpc", []net.IP{ip1})
client, dnsMonitor := NewGrpcClientsForTest(t, target)
ch := client.getWakeupChannelForTesting()
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
dnsMonitor.checkHostnames()
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != targetWithIp1 {
t.Errorf("Expected target %s, got %s", targetWithIp1, clients[0].Target())
} else if !clients[0].ip.Equal(ip1) {
t.Errorf("Expected IP %s, got %s", ip1, clients[0].ip)
}
lookup.Set("testgrpc", []net.IP{ip1, ip2})
drainWakeupChannel(ch)
dnsMonitor.checkHostnames()
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 2 {
t.Errorf("Expected two client, got %+v", clients)
} else if clients[0].Target() != targetWithIp1 {
t.Errorf("Expected target %s, got %s", targetWithIp1, clients[0].Target())
} else if !clients[0].ip.Equal(ip1) {
t.Errorf("Expected IP %s, got %s", ip1, clients[0].ip)
} else if clients[1].Target() != targetWithIp2 {
t.Errorf("Expected target %s, got %s", targetWithIp2, clients[1].Target())
} else if !clients[1].ip.Equal(ip2) {
t.Errorf("Expected IP %s, got %s", ip2, clients[1].ip)
}
lookup.Set("testgrpc", []net.IP{ip2})
drainWakeupChannel(ch)
dnsMonitor.checkHostnames()
waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != targetWithIp2 {
t.Errorf("Expected target %s, got %s", targetWithIp2, clients[0].Target())
} else if !clients[0].ip.Equal(ip2) {
t.Errorf("Expected IP %s, got %s", ip2, clients[0].ip)
}
})
}
func Test_GrpcClients_DnsDiscoveryInitialFailed(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
lookup := newMockDnsLookupForTest(t)
target := "testgrpc:12345"
ip1 := net.ParseIP("192.168.0.1")
targetWithIp1 := fmt.Sprintf("%s (%s)", target, ip1)
client, dnsMonitor := NewGrpcClientsForTest(t, target)
ch := client.getWakeupChannelForTesting()
testCtx, testCtxCancel := context.WithTimeout(context.Background(), testTimeout)
defer testCtxCancel()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := client.WaitForInitialized(ctx); err != nil {
t.Fatal(err)
}
if clients := client.GetClients(); len(clients) != 0 {
t.Errorf("Expected no client, got %+v", clients)
}
lookup.Set("testgrpc", []net.IP{ip1})
drainWakeupChannel(ch)
dnsMonitor.checkHostnames()
waitForEvent(testCtx, t, ch)
if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != targetWithIp1 {
t.Errorf("Expected target %s, got %s", targetWithIp1, clients[0].Target())
} else if !clients[0].ip.Equal(ip1) {
t.Errorf("Expected IP %s, got %s", ip1, clients[0].ip)
}
}
func Test_GrpcClients_Encryption(t *testing.T) {
CatchLogForTest(t)
ensureNoGoroutinesLeak(t, func(t *testing.T) {
serverKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
t.Fatal(err)
}
clientKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
t.Fatal(err)
}
serverCert := GenerateSelfSignedCertificateForTesting(t, 1024, "Server cert", serverKey)
clientCert := GenerateSelfSignedCertificateForTesting(t, 1024, "Testing client", clientKey)
dir := t.TempDir()
serverPrivkeyFile := path.Join(dir, "server-privkey.pem")
serverPubkeyFile := path.Join(dir, "server-pubkey.pem")
serverCertFile := path.Join(dir, "server-cert.pem")
WritePrivateKey(serverKey, serverPrivkeyFile) // nolint
WritePublicKey(&serverKey.PublicKey, serverPubkeyFile) // nolint
os.WriteFile(serverCertFile, serverCert, 0755) // nolint
clientPrivkeyFile := path.Join(dir, "client-privkey.pem")
clientPubkeyFile := path.Join(dir, "client-pubkey.pem")
clientCertFile := path.Join(dir, "client-cert.pem")
WritePrivateKey(clientKey, clientPrivkeyFile) // nolint
WritePublicKey(&clientKey.PublicKey, clientPubkeyFile) // nolint
os.WriteFile(clientCertFile, clientCert, 0755) // nolint
serverConfig := goconf.NewConfigFile()
serverConfig.AddOption("grpc", "servercertificate", serverCertFile)
serverConfig.AddOption("grpc", "serverkey", serverPrivkeyFile)
serverConfig.AddOption("grpc", "clientca", clientCertFile)
_, addr := NewGrpcServerForTestWithConfig(t, serverConfig)
clientConfig := goconf.NewConfigFile()
clientConfig.AddOption("grpc", "targets", addr)
clientConfig.AddOption("grpc", "clientcertificate", clientCertFile)
clientConfig.AddOption("grpc", "clientkey", clientPrivkeyFile)
clientConfig.AddOption("grpc", "serverca", serverCertFile)
clients, _ := NewGrpcClientsForTestWithConfig(t, clientConfig, nil)
ctx, cancel1 := context.WithTimeout(context.Background(), time.Second)
defer cancel1()
if err := clients.WaitForInitialized(ctx); err != nil {
t.Fatal(err)
}
for _, client := range clients.GetClients() {
if _, err := client.GetServerId(ctx); err != nil {
t.Fatal(err)
}
}
})
}

189
grpc_common.go Normal file
View file

@ -0,0 +1,189 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"crypto/tls"
"fmt"
"log"
"net"
"github.com/dlintw/goconf"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
type reloadableCredentials struct {
config *tls.Config
loader *CertificateReloader
pool *CertPoolReloader
}
func (c *reloadableCredentials) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
// use local cfg to avoid clobbering ServerName if using multiple endpoints
cfg := c.config.Clone()
if c.loader != nil {
cfg.GetClientCertificate = c.loader.GetClientCertificate
}
if c.pool != nil {
cfg.RootCAs = c.pool.GetCertPool()
}
if cfg.ServerName == "" {
serverName, _, err := net.SplitHostPort(authority)
if err != nil {
// If the authority had no host port or if the authority cannot be parsed, use it as-is.
serverName = authority
}
cfg.ServerName = serverName
}
conn := tls.Client(rawConn, cfg)
errChannel := make(chan error, 1)
go func() {
errChannel <- conn.Handshake()
close(errChannel)
}()
select {
case err := <-errChannel:
if err != nil {
conn.Close()
return nil, nil, err
}
case <-ctx.Done():
conn.Close()
return nil, nil, ctx.Err()
}
tlsInfo := credentials.TLSInfo{
State: conn.ConnectionState(),
CommonAuthInfo: credentials.CommonAuthInfo{
SecurityLevel: credentials.PrivacyAndIntegrity,
},
}
return WrapSyscallConn(rawConn, conn), tlsInfo, nil
}
func (c *reloadableCredentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
cfg := c.config.Clone()
if c.loader != nil {
cfg.GetCertificate = c.loader.GetCertificate
}
if c.pool != nil {
cfg.ClientCAs = c.pool.GetCertPool()
}
conn := tls.Server(rawConn, cfg)
if err := conn.Handshake(); err != nil {
conn.Close()
return nil, nil, err
}
tlsInfo := credentials.TLSInfo{
State: conn.ConnectionState(),
CommonAuthInfo: credentials.CommonAuthInfo{
SecurityLevel: credentials.PrivacyAndIntegrity,
},
}
return WrapSyscallConn(rawConn, conn), tlsInfo, nil
}
func (c *reloadableCredentials) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{
SecurityProtocol: "tls",
SecurityVersion: "1.2",
ServerName: c.config.ServerName,
}
}
func (c *reloadableCredentials) Clone() credentials.TransportCredentials {
return &reloadableCredentials{
config: c.config.Clone(),
pool: c.pool,
}
}
func (c *reloadableCredentials) OverrideServerName(serverName string) error {
c.config.ServerName = serverName
return nil
}
func (c *reloadableCredentials) Close() {
if c.loader != nil {
c.loader.Close()
}
if c.pool != nil {
c.pool.Close()
}
}
func NewReloadableCredentials(config *goconf.ConfigFile, server bool) (credentials.TransportCredentials, error) {
var prefix string
var caPrefix string
if server {
prefix = "server"
caPrefix = "client"
} else {
prefix = "client"
caPrefix = "server"
}
certificateFile, _ := config.GetString("grpc", prefix+"certificate")
keyFile, _ := config.GetString("grpc", prefix+"key")
caFile, _ := config.GetString("grpc", caPrefix+"ca")
cfg := &tls.Config{
NextProtos: []string{"h2"},
}
var loader *CertificateReloader
var err error
if certificateFile != "" && keyFile != "" {
loader, err = NewCertificateReloader(certificateFile, keyFile)
if err != nil {
return nil, fmt.Errorf("invalid GRPC %s certificate / key in %s / %s: %w", prefix, certificateFile, keyFile, err)
}
}
var pool *CertPoolReloader
if caFile != "" {
pool, err = NewCertPoolReloader(caFile)
if err != nil {
return nil, err
}
if server {
cfg.ClientAuth = tls.RequireAndVerifyClientCert
}
}
if loader == nil && pool == nil {
if server {
log.Printf("WARNING: No GRPC server certificate and/or key configured, running unencrypted")
} else {
log.Printf("WARNING: No GRPC CA configured, expecting unencrypted connections")
}
return insecure.NewCredentials(), nil
}
creds := &reloadableCredentials{
config: cfg,
loader: loader,
pool: pool,
}
return creds, nil
}

136
grpc_common_test.go Normal file
View file

@ -0,0 +1,136 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"io/fs"
"math/big"
"net"
"os"
"testing"
"time"
)
func (c *reloadableCredentials) WaitForCertificateReload(ctx context.Context) error {
if c.loader == nil {
return errors.New("no certificate loaded")
}
return c.loader.WaitForReload(ctx)
}
func (c *reloadableCredentials) WaitForCertPoolReload(ctx context.Context) error {
if c.pool == nil {
return errors.New("no certificate pool loaded")
}
return c.pool.WaitForReload(ctx)
}
func GenerateSelfSignedCertificateForTesting(t *testing.T, bits int, organization string, key *rsa.PrivateKey) []byte {
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{organization},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 180),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageServerAuth,
},
BasicConstraintsValid: true,
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
}
data, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
if err != nil {
t.Fatal(err)
}
data = pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: data,
})
return data
}
func WritePrivateKey(key *rsa.PrivateKey, filename string) error {
data := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
})
return os.WriteFile(filename, data, 0600)
}
func WritePublicKey(key *rsa.PublicKey, filename string) error {
data, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return err
}
data = pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: data,
})
return os.WriteFile(filename, data, 0755)
}
func replaceFile(t *testing.T, filename string, data []byte, perm fs.FileMode) {
t.Helper()
oldStat, err := os.Stat(filename)
if err != nil {
t.Fatalf("can't stat old file %s: %s", filename, err)
return
}
for {
if err := os.WriteFile(filename, data, perm); err != nil {
t.Fatalf("can't write file %s: %s", filename, err)
return
}
newStat, err := os.Stat(filename)
if err != nil {
t.Fatalf("can't stat new file %s: %s", filename, err)
return
}
// We need different modification times.
if !newStat.ModTime().Equal(oldStat.ModTime()) {
break
}
time.Sleep(time.Millisecond)
}
}

37
grpc_internal.proto Normal file
View file

@ -0,0 +1,37 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
option go_package = "github.com/strukturag/nextcloud-spreed-signaling;signaling";
package signaling;
service RpcInternal {
rpc GetServerId(GetServerIdRequest) returns (GetServerIdReply) {}
}
message GetServerIdRequest {
}
message GetServerIdReply {
string serverId = 1;
}

41
grpc_mcu.proto Normal file
View file

@ -0,0 +1,41 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
option go_package = "github.com/strukturag/nextcloud-spreed-signaling;signaling";
package signaling;
service RpcMcu {
rpc GetPublisherId(GetPublisherIdRequest) returns (GetPublisherIdReply) {}
}
message GetPublisherIdRequest {
string sessionId = 1;
string streamType = 2;
}
message GetPublisherIdReply {
string publisherId = 1;
string proxyUrl = 2;
string ip = 3;
}

229
grpc_remote_client.go Normal file
View file

@ -0,0 +1,229 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2024 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"sync/atomic"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
const (
grpcRemoteClientMessageQueue = 16
)
func getMD(md metadata.MD, key string) string {
if values := md.Get(key); len(values) > 0 {
return values[0]
}
return ""
}
// remoteGrpcClient is a remote client connecting from a GRPC proxy to a Hub.
type remoteGrpcClient struct {
hub *Hub
client RpcSessions_ProxySessionServer
sessionId string
remoteAddr string
country string
userAgent string
closeCtx context.Context
closeFunc context.CancelCauseFunc
session atomic.Pointer[Session]
messages chan WritableClientMessage
}
func newRemoteGrpcClient(hub *Hub, request RpcSessions_ProxySessionServer) (*remoteGrpcClient, error) {
md, found := metadata.FromIncomingContext(request.Context())
if !found {
return nil, errors.New("no metadata provided")
}
closeCtx, closeFunc := context.WithCancelCause(context.Background())
result := &remoteGrpcClient{
hub: hub,
client: request,
sessionId: getMD(md, "sessionId"),
remoteAddr: getMD(md, "remoteAddr"),
country: getMD(md, "country"),
userAgent: getMD(md, "userAgent"),
closeCtx: closeCtx,
closeFunc: closeFunc,
messages: make(chan WritableClientMessage, grpcRemoteClientMessageQueue),
}
return result, nil
}
func (c *remoteGrpcClient) readPump() {
var closeError error
defer func() {
c.closeFunc(closeError)
c.hub.OnClosed(c)
}()
for {
msg, err := c.client.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
// Connection was closed locally.
break
}
if status.Code(err) != codes.Canceled {
log.Printf("Error reading from remote client for session %s: %s", c.sessionId, err)
closeError = err
}
break
}
c.hub.OnMessageReceived(c, msg.Message)
}
}
func (c *remoteGrpcClient) Context() context.Context {
return c.client.Context()
}
func (c *remoteGrpcClient) RemoteAddr() string {
return c.remoteAddr
}
func (c *remoteGrpcClient) UserAgent() string {
return c.userAgent
}
func (c *remoteGrpcClient) Country() string {
return c.country
}
func (c *remoteGrpcClient) IsConnected() bool {
return true
}
func (c *remoteGrpcClient) IsAuthenticated() bool {
return c.GetSession() != nil
}
func (c *remoteGrpcClient) GetSession() Session {
session := c.session.Load()
if session == nil {
return nil
}
return *session
}
func (c *remoteGrpcClient) SetSession(session Session) {
if session == nil {
c.session.Store(nil)
} else {
c.session.Store(&session)
}
}
func (c *remoteGrpcClient) SendError(e *Error) bool {
message := &ServerMessage{
Type: "error",
Error: e,
}
return c.SendMessage(message)
}
func (c *remoteGrpcClient) SendByeResponse(message *ClientMessage) bool {
return c.SendByeResponseWithReason(message, "")
}
func (c *remoteGrpcClient) SendByeResponseWithReason(message *ClientMessage, reason string) bool {
response := &ServerMessage{
Type: "bye",
}
if message != nil {
response.Id = message.Id
}
if reason != "" {
if response.Bye == nil {
response.Bye = &ByeServerMessage{}
}
response.Bye.Reason = reason
}
return c.SendMessage(response)
}
func (c *remoteGrpcClient) SendMessage(message WritableClientMessage) bool {
if c.closeCtx.Err() != nil {
return false
}
select {
case c.messages <- message:
return true
default:
log.Printf("Message queue for remote client of session %s is full, not sending %+v", c.sessionId, message)
return false
}
}
func (c *remoteGrpcClient) Close() {
c.closeFunc(nil)
}
func (c *remoteGrpcClient) run() error {
go c.readPump()
for {
select {
case <-c.closeCtx.Done():
if err := context.Cause(c.closeCtx); err != context.Canceled {
return err
}
return nil
case msg := <-c.messages:
data, err := json.Marshal(msg)
if err != nil {
log.Printf("Error marshalling %+v for remote client for session %s: %s", msg, c.sessionId, err)
continue
}
if err := c.client.Send(&ServerSessionMessage{
Message: data,
}); err != nil {
return fmt.Errorf("error sending %+v to remote client for session %s: %w", msg, c.sessionId, err)
}
}
}
}

258
grpc_server.go Normal file
View file

@ -0,0 +1,258 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"log"
"net"
"net/url"
"os"
"github.com/dlintw/goconf"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
status "google.golang.org/grpc/status"
)
var (
GrpcServerId string
)
func init() {
RegisterGrpcServerStats()
hostname, err := os.Hostname()
if err != nil {
hostname = newRandomString(8)
}
md := sha256.New()
md.Write([]byte(fmt.Sprintf("%s-%s-%d", newRandomString(32), hostname, os.Getpid())))
GrpcServerId = hex.EncodeToString(md.Sum(nil))
}
type GrpcServerHub interface {
GetSessionByResumeId(resumeId string) Session
GetSessionByPublicId(sessionId string) Session
GetSessionIdByRoomSessionId(roomSessionId string) (string, error)
GetBackend(u *url.URL) *Backend
}
type GrpcServer struct {
UnimplementedRpcBackendServer
UnimplementedRpcInternalServer
UnimplementedRpcMcuServer
UnimplementedRpcSessionsServer
creds credentials.TransportCredentials
conn *grpc.Server
listener net.Listener
serverId string // can be overwritten from tests
hub GrpcServerHub
}
func NewGrpcServer(config *goconf.ConfigFile) (*GrpcServer, error) {
var listener net.Listener
if addr, _ := GetStringOptionWithEnv(config, "grpc", "listen"); addr != "" {
var err error
listener, err = net.Listen("tcp", addr)
if err != nil {
return nil, fmt.Errorf("could not create GRPC listener %s: %w", addr, err)
}
}
creds, err := NewReloadableCredentials(config, true)
if err != nil {
return nil, err
}
conn := grpc.NewServer(grpc.Creds(creds))
result := &GrpcServer{
creds: creds,
conn: conn,
listener: listener,
serverId: GrpcServerId,
}
RegisterRpcBackendServer(conn, result)
RegisterRpcInternalServer(conn, result)
RegisterRpcSessionsServer(conn, result)
RegisterRpcMcuServer(conn, result)
return result, nil
}
func (s *GrpcServer) Run() error {
if s.listener == nil {
return nil
}
return s.conn.Serve(s.listener)
}
func (s *GrpcServer) Close() {
s.conn.GracefulStop()
if cr, ok := s.creds.(*reloadableCredentials); ok {
cr.Close()
}
}
func (s *GrpcServer) LookupResumeId(ctx context.Context, request *LookupResumeIdRequest) (*LookupResumeIdReply, error) {
statsGrpcServerCalls.WithLabelValues("LookupResumeId").Inc()
// TODO: Remove debug logging
log.Printf("Lookup session for resume id %s", request.ResumeId)
session := s.hub.GetSessionByResumeId(request.ResumeId)
if session == nil {
return nil, status.Error(codes.NotFound, "no such room session id")
}
return &LookupResumeIdReply{
SessionId: session.PublicId(),
}, nil
}
func (s *GrpcServer) LookupSessionId(ctx context.Context, request *LookupSessionIdRequest) (*LookupSessionIdReply, error) {
statsGrpcServerCalls.WithLabelValues("LookupSessionId").Inc()
// TODO: Remove debug logging
log.Printf("Lookup session id for room session id %s", request.RoomSessionId)
sid, err := s.hub.GetSessionIdByRoomSessionId(request.RoomSessionId)
if errors.Is(err, ErrNoSuchRoomSession) {
return nil, status.Error(codes.NotFound, "no such room session id")
} else if err != nil {
return nil, err
}
if sid != "" && request.DisconnectReason != "" {
if session := s.hub.GetSessionByPublicId(sid); session != nil {
log.Printf("Closing session %s because same room session %s connected", session.PublicId(), request.RoomSessionId)
session.LeaveRoom(false)
switch sess := session.(type) {
case *ClientSession:
if client := sess.GetClient(); client != nil {
client.SendByeResponseWithReason(nil, "room_session_reconnected")
}
}
session.Close()
}
}
return &LookupSessionIdReply{
SessionId: sid,
}, nil
}
func (s *GrpcServer) IsSessionInCall(ctx context.Context, request *IsSessionInCallRequest) (*IsSessionInCallReply, error) {
statsGrpcServerCalls.WithLabelValues("IsSessionInCall").Inc()
// TODO: Remove debug logging
log.Printf("Check if session %s is in call %s on %s", request.SessionId, request.RoomId, request.BackendUrl)
session := s.hub.GetSessionByPublicId(request.SessionId)
if session == nil {
return nil, status.Error(codes.NotFound, "no such session id")
}
result := &IsSessionInCallReply{}
room := session.GetRoom()
if room == nil || room.Id() != request.GetRoomId() || room.Backend().url != request.GetBackendUrl() ||
(session.ClientType() != HelloClientTypeInternal && !room.IsSessionInCall(session)) {
// Recipient is not in a room, a different room or not in the call.
result.InCall = false
} else {
result.InCall = true
}
return result, nil
}
func (s *GrpcServer) GetPublisherId(ctx context.Context, request *GetPublisherIdRequest) (*GetPublisherIdReply, error) {
statsGrpcServerCalls.WithLabelValues("GetPublisherId").Inc()
// TODO: Remove debug logging
log.Printf("Get %s publisher id for session %s", request.StreamType, request.SessionId)
session := s.hub.GetSessionByPublicId(request.SessionId)
if session == nil {
return nil, status.Error(codes.NotFound, "no such session")
}
clientSession, ok := session.(*ClientSession)
if !ok {
return nil, status.Error(codes.NotFound, "no such session")
}
publisher := clientSession.GetOrWaitForPublisher(ctx, StreamType(request.StreamType))
if publisher, ok := publisher.(*mcuProxyPublisher); ok {
reply := &GetPublisherIdReply{
PublisherId: publisher.Id(),
ProxyUrl: publisher.conn.rawUrl,
}
if ip := publisher.conn.ip; ip != nil {
reply.Ip = ip.String()
}
return reply, nil
}
return nil, status.Error(codes.NotFound, "no such publisher")
}
func (s *GrpcServer) GetServerId(ctx context.Context, request *GetServerIdRequest) (*GetServerIdReply, error) {
statsGrpcServerCalls.WithLabelValues("GetServerId").Inc()
return &GetServerIdReply{
ServerId: s.serverId,
}, nil
}
func (s *GrpcServer) GetSessionCount(ctx context.Context, request *GetSessionCountRequest) (*GetSessionCountReply, error) {
statsGrpcServerCalls.WithLabelValues("SessionCount").Inc()
u, err := url.Parse(request.Url)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid url")
}
backend := s.hub.GetBackend(u)
if backend == nil {
return nil, status.Error(codes.NotFound, "no such backend")
}
return &GetSessionCountReply{
Count: uint32(backend.Len()),
}, nil
}
func (s *GrpcServer) ProxySession(request RpcSessions_ProxySessionServer) error {
statsGrpcServerCalls.WithLabelValues("ProxySession").Inc()
hub, ok := s.hub.(*Hub)
if !ok {
return status.Error(codes.Internal, "invalid hub type")
}
client, err := newRemoteGrpcClient(hub, request)
if err != nil {
return err
}
sid := hub.registerClient(client)
defer hub.unregisterClient(sid)
return client.run()
}

277
grpc_server_test.go Normal file
View file

@ -0,0 +1,277 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package signaling
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"net"
"os"
"path"
"strconv"
"testing"
"time"
"github.com/dlintw/goconf"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
func (s *GrpcServer) WaitForCertificateReload(ctx context.Context) error {
c, ok := s.creds.(*reloadableCredentials)
if !ok {
return errors.New("no reloadable credentials found")
}
return c.WaitForCertificateReload(ctx)
}
func (s *GrpcServer) WaitForCertPoolReload(ctx context.Context) error {
c, ok := s.creds.(*reloadableCredentials)
if !ok {
return errors.New("no reloadable credentials found")
}
return c.WaitForCertPoolReload(ctx)
}
func NewGrpcServerForTestWithConfig(t *testing.T, config *goconf.ConfigFile) (server *GrpcServer, addr string) {
for port := 50000; port < 50100; port++ {
addr = net.JoinHostPort("127.0.0.1", strconv.Itoa(port))
config.AddOption("grpc", "listen", addr)
var err error
server, err = NewGrpcServer(config)
if isErrorAddressAlreadyInUse(err) {
continue
} else if err != nil {
t.Fatal(err)
}
break
}
if server == nil {
t.Fatal("could not find free port")
}
// Don't match with own server id by default.
server.serverId = "dont-match"
go func() {
if err := server.Run(); err != nil {
t.Errorf("could not start GRPC server: %s", err)
}
}()
t.Cleanup(func() {
server.Close()
})
return server, addr
}
func NewGrpcServerForTest(t *testing.T) (server *GrpcServer, addr string) {
config := goconf.NewConfigFile()
return NewGrpcServerForTestWithConfig(t, config)
}
func Test_GrpcServer_ReloadCerts(t *testing.T) {
CatchLogForTest(t)
key, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
t.Fatal(err)
}
org1 := "Testing certificate"
cert1 := GenerateSelfSignedCertificateForTesting(t, 1024, org1, key)
dir := t.TempDir()
privkeyFile := path.Join(dir, "privkey.pem")
pubkeyFile := path.Join(dir, "pubkey.pem")
certFile := path.Join(dir, "cert.pem")
WritePrivateKey(key, privkeyFile) // nolint
WritePublicKey(&key.PublicKey, pubkeyFile) // nolint
os.WriteFile(certFile, cert1, 0755) // nolint
config := goconf.NewConfigFile()
config.AddOption("grpc", "servercertificate", certFile)
config.AddOption("grpc", "serverkey", privkeyFile)
UpdateCertificateCheckIntervalForTest(t, 0)
server, addr := NewGrpcServerForTestWithConfig(t, config)
cp1 := x509.NewCertPool()
if !cp1.AppendCertsFromPEM(cert1) {
t.Fatalf("could not add certificate")
}
cfg1 := &tls.Config{
RootCAs: cp1,
}
conn1, err := tls.Dial("tcp", addr, cfg1)
if err != nil {
t.Fatal(err)
}
defer conn1.Close() // nolint
state1 := conn1.ConnectionState()
if certs := state1.PeerCertificates; len(certs) == 0 {
t.Errorf("expected certificates, got %+v", state1)
} else if len(certs[0].Subject.Organization) == 0 {
t.Errorf("expected organization, got %s", certs[0].Subject)
} else if certs[0].Subject.Organization[0] != org1 {
t.Errorf("expected organization %s, got %s", org1, certs[0].Subject)
}
org2 := "Updated certificate"
cert2 := GenerateSelfSignedCertificateForTesting(t, 1024, org2, key)
replaceFile(t, certFile, cert2, 0755)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := server.WaitForCertificateReload(ctx); err != nil {
t.Fatal(err)
}
cp2 := x509.NewCertPool()
if !cp2.AppendCertsFromPEM(cert2) {
t.Fatalf("could not add certificate")
}
cfg2 := &tls.Config{
RootCAs: cp2,
}
conn2, err := tls.Dial("tcp", addr, cfg2)
if err != nil {
t.Fatal(err)
}
defer conn2.Close() // nolint
state2 := conn2.ConnectionState()
if certs := state2.PeerCertificates; len(certs) == 0 {
t.Errorf("expected certificates, got %+v", state2)
} else if len(certs[0].Subject.Organization) == 0 {
t.Errorf("expected organization, got %s", certs[0].Subject)
} else if certs[0].Subject.Organization[0] != org2 {
t.Errorf("expected organization %s, got %s", org2, certs[0].Subject)
}
}
func Test_GrpcServer_ReloadCA(t *testing.T) {
CatchLogForTest(t)
serverKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
t.Fatal(err)
}
clientKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
t.Fatal(err)
}
serverCert := GenerateSelfSignedCertificateForTesting(t, 1024, "Server cert", serverKey)
org1 := "Testing client"
clientCert1 := GenerateSelfSignedCertificateForTesting(t, 1024, org1, clientKey)
dir := t.TempDir()
privkeyFile := path.Join(dir, "privkey.pem")
pubkeyFile := path.Join(dir, "pubkey.pem")
certFile := path.Join(dir, "cert.pem")
caFile := path.Join(dir, "ca.pem")
WritePrivateKey(serverKey, privkeyFile) // nolint
WritePublicKey(&serverKey.PublicKey, pubkeyFile) // nolint
os.WriteFile(certFile, serverCert, 0755) // nolint
os.WriteFile(caFile, clientCert1, 0755) // nolint
config := goconf.NewConfigFile()
config.AddOption("grpc", "servercertificate", certFile)
config.AddOption("grpc", "serverkey", privkeyFile)
config.AddOption("grpc", "clientca", caFile)
UpdateCertificateCheckIntervalForTest(t, 0)
server, addr := NewGrpcServerForTestWithConfig(t, config)
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(serverCert) {
t.Fatalf("could not add certificate")
}
pair1, err := tls.X509KeyPair(clientCert1, pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(clientKey),
}))
if err != nil {
t.Fatal(err)
}
cfg1 := &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{pair1},
}
client1, err := NewGrpcClient(addr, nil, grpc.WithTransportCredentials(credentials.NewTLS(cfg1)))
if err != nil {
t.Fatal(err)
}
defer client1.Close() // nolint
ctx1, cancel1 := context.WithTimeout(context.Background(), time.Second)
defer cancel1()
if _, err := client1.GetServerId(ctx1); err != nil {
t.Fatal(err)
}
org2 := "Updated client"
clientCert2 := GenerateSelfSignedCertificateForTesting(t, 1024, org2, clientKey)
replaceFile(t, caFile, clientCert2, 0755)
if err := server.WaitForCertPoolReload(ctx1); err != nil {
t.Fatal(err)
}
pair2, err := tls.X509KeyPair(clientCert2, pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(clientKey),
}))
if err != nil {
t.Fatal(err)
}
cfg2 := &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{pair2},
}
client2, err := NewGrpcClient(addr, nil, grpc.WithTransportCredentials(credentials.NewTLS(cfg2)))
if err != nil {
t.Fatal(err)
}
defer client2.Close() // nolint
ctx2, cancel2 := context.WithTimeout(context.Background(), time.Second)
defer cancel2()
// This will fail if the CA certificate has not been reloaded by the server.
if _, err := client2.GetServerId(ctx2); err != nil {
t.Fatal(err)
}
}

69
grpc_sessions.proto Normal file
View file

@ -0,0 +1,69 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
option go_package = "github.com/strukturag/nextcloud-spreed-signaling;signaling";
package signaling;
service RpcSessions {
rpc LookupResumeId(LookupResumeIdRequest) returns (LookupResumeIdReply) {}
rpc LookupSessionId(LookupSessionIdRequest) returns (LookupSessionIdReply) {}
rpc IsSessionInCall(IsSessionInCallRequest) returns (IsSessionInCallReply) {}
rpc ProxySession(stream ClientSessionMessage) returns (stream ServerSessionMessage) {}
}
message LookupResumeIdRequest {
string resumeId = 1;
}
message LookupResumeIdReply {
string sessionId = 1;
}
message LookupSessionIdRequest {
string roomSessionId = 1;
// Optional: set if the session should be disconnected with a given reason.
string disconnectReason = 2;
}
message LookupSessionIdReply {
string sessionId = 1;
}
message IsSessionInCallRequest {
string sessionId = 1;
string roomId = 2;
string backendUrl = 3;
}
message IsSessionInCallReply {
bool inCall = 1;
}
message ClientSessionMessage {
bytes message = 1;
}
message ServerSessionMessage {
bytes message = 1;
}

Some files were not shown because too many files have changed in this diff Show more