Compare commits

...

831 commits

Author SHA1 Message Date
Joachim Bauch e28854324c
Merge pull request #766 from strukturag/build-badge
Update badge for build status to new URL.
2024-06-20 08:44:07 +02:00
Joachim Bauch 5e74115117
Update badge for build status to new URL. 2024-06-20 08:40:59 +02:00
Joachim Bauch 88529d4159
Merge pull request #765 from strukturag/throttle-resume
Don't throttle valid but expired resume requests.
2024-06-20 08:25:01 +02:00
Joachim Bauch 918b10a7a7
Don't throttle valid but expired resume requests. 2024-06-19 13:28:49 +02:00
Joachim Bauch cf95b97f5d
Merge pull request #764 from strukturag/throttle-delay-overflow
Prevent overflows when calculating throttle delay.
2024-06-19 13:17:50 +02:00
Joachim Bauch 14254278ec
Prevent overflows when calculating throttle delay. 2024-06-19 12:26:35 +02:00
Joachim Bauch 4fae357860
Merge pull request #762 from strukturag/dependabot/github_actions/docker/build-push-action-6
Bump docker/build-push-action from 5 to 6
2024-06-18 08:31:32 +02:00
dependabot[bot] 772d648764
Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
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>
2024-06-17 20:39:24 +00:00
Joachim Bauch f1827b9223
Merge pull request #757 from strukturag/dependabot/go_modules/github.com/oschwald/maxminddb-golang-1.13.0
Bump github.com/oschwald/maxminddb-golang from 1.12.0 to 1.13.0
2024-06-17 15:43:21 +02:00
Joachim Bauch 890e9bca06
Merge pull request #759 from strukturag/dependabot/go_modules/google.golang.org/protobuf-1.34.2
Bump google.golang.org/protobuf from 1.34.1 to 1.34.2
2024-06-17 12:22:50 +02:00
dependabot[bot] 7af7f1ff09
Bump google.golang.org/protobuf from 1.34.1 to 1.34.2
Bumps google.golang.org/protobuf from 1.34.1 to 1.34.2.

---
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>
2024-06-17 10:10:36 +00:00
Joachim Bauch 8173a3dba5
Merge pull request #760 from strukturag/dependabot/go_modules/github.com/gorilla/websocket-1.5.3
Bump github.com/gorilla/websocket from 1.5.1 to 1.5.3
2024-06-17 12:10:05 +02:00
dependabot[bot] c4aad46c06
Bump github.com/oschwald/maxminddb-golang from 1.12.0 to 1.13.0
Bumps [github.com/oschwald/maxminddb-golang](https://github.com/oschwald/maxminddb-golang) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/oschwald/maxminddb-golang/releases)
- [Commits](https://github.com/oschwald/maxminddb-golang/compare/v1.12.0...v1.13.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>
2024-06-17 10:10:04 +00:00
Joachim Bauch 4248da5cab
Merge pull request #756 from strukturag/dependabot/go_modules/google.golang.org/grpc/cmd/protoc-gen-go-grpc-1.4.0
Bump google.golang.org/grpc/cmd/protoc-gen-go-grpc from 1.3.0 to 1.4.0
2024-06-17 12:09:31 +02:00
Joachim Bauch 3ec1c3d4de
Merge pull request #761 from strukturag/dependabot/go_modules/github.com/nats-io/nats.go-1.36.0
Bump github.com/nats-io/nats.go from 1.35.0 to 1.36.0
2024-06-17 12:09:14 +02:00
dependabot[bot] ea7530256a
Bump github.com/nats-io/nats.go from 1.35.0 to 1.36.0
Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.35.0 to 1.36.0.
- [Release notes](https://github.com/nats-io/nats.go/releases)
- [Commits](https://github.com/nats-io/nats.go/compare/v1.35.0...v1.36.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-06-14 20:44:33 +00:00
dependabot[bot] 531b16462c
Bump github.com/gorilla/websocket from 1.5.1 to 1.5.3
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.5.1...v1.5.3)

---
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>
2024-06-14 20:44:28 +00:00
dependabot[bot] d127a7efa6
Bump google.golang.org/grpc/cmd/protoc-gen-go-grpc from 1.3.0 to 1.4.0
Bumps [google.golang.org/grpc/cmd/protoc-gen-go-grpc](https://github.com/grpc/grpc-go) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.3.0...v1.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 20:28:58 +00:00
Joachim Bauch ef01f2c4d9
Merge pull request #755 from strukturag/websocket-features
Include list of supported features in websocket response.
2024-06-03 11:21:28 +02:00
Joachim Bauch 6e1a2f6d7e
Include list of supported features in websocket response. 2024-06-03 11:14:19 +02:00
Joachim Bauch c46f7b7f9d
Merge pull request #754 from strukturag/make-dont-update-dependencies
make: Don't update CLI tools before installing.
2024-06-03 10:01:47 +02:00
Joachim Bauch 82919ce97b
No more need for "GOPROXY" environment variable. 2024-06-03 09:57:04 +02:00
Joachim Bauch 26e3a39f12
make: No need to run "go mod tidy" before vendoring. 2024-06-03 09:51:22 +02:00
Joachim Bauch 224daa5efd
make: Don't update CLI tools before installing.
This makes sure the go.mod / go.sum files are not updated when generating
the "vendor" folder (which will generate common files and thus might update
the CLI tools). Mismatching "vendor" and "go.*" files will break building.
2024-06-03 09:49:46 +02:00
Joachim Bauch 2f9bdbd919
Merge pull request #753 from strukturag/dependabot/go_modules/etcd-ad7f4f318d
Bump the etcd group with 4 updates
2024-06-03 09:07:36 +02:00
dependabot[bot] 5b04cb41e6
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.13 to 3.5.14
- [Release notes](https://github.com/etcd-io/etcd/releases)
- [Commits](https://github.com/etcd-io/etcd/compare/v3.5.13...v3.5.14)

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

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

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

---
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-29 20:58:13 +00:00
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
164 changed files with 19041 additions and 4470 deletions

View file

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

View file

@ -1,14 +1,37 @@
version: 2 version: 2
updates: 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 - package-ecosystem: gomod
directory: / directory: /
schedule: schedule:
interval: daily interval: daily
groups:
etcd:
patterns:
- "go.etcd.io*"
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
schedule: schedule:
interval: "daily" interval: "daily"
groups:
artifacts:
patterns:
- "actions/*-artifact"
- package-ecosystem: "pip" - package-ecosystem: "pip"
directory: "/docs" directory: "/docs"

View file

@ -1,15 +1,30 @@
name: check-continentmap name: check-continentmap
on: 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: schedule:
- cron: "0 2 * * SUN" - cron: "0 2 * * SUN"
permissions:
contents: read
jobs: jobs:
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Check continentmap - name: Check continentmap
run: make check-continentmap run: make check-continentmap

View file

@ -16,6 +16,9 @@ on:
schedule: schedule:
- cron: '28 2 * * 5' - cron: '28 2 * * 5'
permissions:
contents: read
jobs: jobs:
analyze: analyze:
name: Analyze name: Analyze
@ -33,15 +36,15 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v2 uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis - 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: issue_comment:
types: created types: created
permissions:
contents: read
jobs: jobs:
rebase: rebase:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: none
# On pull requests and if the comment starts with `/rebase` # On pull requests and if the comment starts with `/rebase`
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase') if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase')
steps: steps:
- name: Add reaction on start - name: Add reaction on start
uses: peter-evans/create-or-update-comment@v2 uses: peter-evans/create-or-update-comment@v4
with: with:
token: ${{ secrets.COMMAND_BOT_PAT }} token: ${{ secrets.COMMAND_BOT_PAT }}
repository: ${{ github.event.repository.full_name }} repository: ${{ github.event.repository.full_name }}
@ -26,18 +31,18 @@ jobs:
reaction-type: "+1" reaction-type: "+1"
- name: Checkout the latest code - name: Checkout the latest code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.COMMAND_BOT_PAT }} token: ${{ secrets.COMMAND_BOT_PAT }}
- name: Automatic Rebase - name: Automatic Rebase
uses: cirrus-actions/rebase@1.7 uses: cirrus-actions/rebase@1.8
env: env:
GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
- name: Add reaction on failure - name: Add reaction on failure
uses: peter-evans/create-or-update-comment@v2 uses: peter-evans/create-or-update-comment@v4
if: failure() if: failure()
with: with:
token: ${{ secrets.COMMAND_BOT_PAT }} 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@v6
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@v6
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 ] branches: [ master ]
paths: paths:
- '.github/workflows/docker-compose.yml' - '.github/workflows/docker-compose.yml'
- 'docker-compose.yml' - '**/docker-compose.yml'
- 'docker/server/Dockerfile'
push: push:
branches: [ master ] branches: [ master ]
paths: paths:
- '.github/workflows/docker-compose.yml' - '.github/workflows/docker-compose.yml'
- 'docker-compose.yml' - '**/docker-compose.yml'
- 'docker/server/Dockerfile'
permissions:
contents: read
jobs: jobs:
pull: pull:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: 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 - name: Pull Docker images
run: docker-compose pull run: ./docker-compose -f docker/docker-compose.yml pull
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: 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 - 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' - '.github/workflows/docker-janus.yml'
- 'docker/janus/Dockerfile' - 'docker/janus/Dockerfile'
permissions:
contents: read
env:
TEST_TAG: strukturag/nextcloud-spreed-signaling:janus-test
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Build Docker image - name: Build Docker image
uses: docker/build-push-action@v3 uses: docker/build-push-action@v6
with: with:
context: docker/janus context: docker/janus
load: true
tags: ${{ env.TEST_TAG }}

View file

@ -3,20 +3,66 @@ name: Docker image
on: on:
pull_request: pull_request:
branches: [ master ] branches: [ master ]
paths:
- '.github/workflows/docker.yml'
- '**.go'
- 'go.*'
- 'Makefile'
- '*.conf.in'
- 'docker/server/*'
- 'docker/proxy/*'
push: push:
branches: [ master ] 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: jobs:
build: server:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Build Docker image - name: Build Docker image
uses: docker/build-push-action@v3 uses: docker/build-push-action@v6
with: with:
context: . 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@v6
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,43 +5,30 @@ on:
branches: [ master ] branches: [ master ]
paths: paths:
- '.github/workflows/lint.yml' - '.github/workflows/lint.yml'
- '.golangci.yml'
- '**.go' - '**.go'
- 'go.*' - 'go.*'
pull_request: pull_request:
branches: [ master ] branches: [ master ]
paths: paths:
- '.github/workflows/lint.yml' - '.github/workflows/lint.yml'
- '.golangci.yml'
- '**.go' - '**.go'
- 'go.*' - 'go.*'
permissions:
contents: read
jobs: jobs:
lint: lint:
name: golang name: golang
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-go@v3 - uses: actions/setup-go@v5
with: with:
go-version: "1.17" go-version: "1.21"
- 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') }}
- name: Install dependencies - name: Install dependencies
run: | run: |
@ -49,6 +36,27 @@ jobs:
make common make common
- name: lint - name: lint
uses: golangci/golangci-lint-action@v3.2.0 uses: golangci/golangci-lint-action@v6.0.1
with: with:
version: latest 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

View file

@ -16,39 +16,23 @@ on:
- 'go.*' - 'go.*'
- 'Makefile' - 'Makefile'
permissions:
contents: read
jobs: jobs:
create: create:
strategy: strategy:
matrix: matrix:
go-version: go-version:
- "1.17" - "1.21"
- "1.18" - "1.22"
- "1.19"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-go@v3 - uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- 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') }}
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt -y update && sudo apt -y install protobuf-compiler sudo apt -y update && sudo apt -y install protobuf-compiler
@ -59,7 +43,7 @@ jobs:
make tarball make tarball
- name: Upload tarball - name: Upload tarball
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: tarball-${{ matrix.go-version }} name: tarball-${{ matrix.go-version }}
path: nextcloud-spreed-signaling*.tar.gz path: nextcloud-spreed-signaling*.tar.gz
@ -68,13 +52,12 @@ jobs:
strategy: strategy:
matrix: matrix:
go-version: go-version:
- "1.17" - "1.21"
- "1.18" - "1.22"
- "1.19"
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [create] needs: [create]
steps: steps:
- uses: actions/setup-go@v3 - uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
@ -83,25 +66,23 @@ jobs:
sudo apt -y update && sudo apt -y install protobuf-compiler sudo apt -y update && sudo apt -y install protobuf-compiler
- name: Download tarball - name: Download tarball
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: tarball-${{ matrix.go-version }} name: tarball-${{ matrix.go-version }}
- name: Extract tarball - name: Extract tarball
run: | run: |
mkdir -p tmp mkdir -p tmp
tar xf nextcloud-spreed-signaling*.tar.gz --strip-components=1 -C tmp tar xvf nextcloud-spreed-signaling*.tar.gz --strip-components=1 -C tmp
[ -d "tmp/vendor" ] || exit 1 [ -d "tmp/vendor" ] || exit 1
- name: Build - name: Build
env:
GOPROXY: off
run: | run: |
echo "Building with $(nproc) threads" echo "Building with $(nproc) threads"
make -C tmp build -j$(nproc) make -C tmp build -j$(nproc)
- name: Run tests - name: Run tests
env: env:
GOPROXY: off USE_DB_IP_GEOIP_DATABASE: "1"
run: | run: |
make -C tmp test make -C tmp test TIMEOUT=120s

View file

@ -16,41 +16,26 @@ on:
- 'go.*' - 'go.*'
- 'Makefile' - 'Makefile'
permissions:
contents: read
jobs: jobs:
go: go:
env: env:
MAXMIND_GEOLITE2_LICENSE: ${{ secrets.MAXMIND_GEOLITE2_LICENSE }} MAXMIND_GEOLITE2_LICENSE: ${{ secrets.MAXMIND_GEOLITE2_LICENSE }}
USE_DB_IP_GEOIP_DATABASE: "1"
strategy: strategy:
matrix: matrix:
go-version: go-version:
- "1.17" - "1.21"
- "1.18" - "1.22"
- "1.19"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-go@v3 - uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- 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') }}
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt -y update && sudo apt -y install protobuf-compiler sudo apt -y update && sudo apt -y install protobuf-compiler
@ -64,11 +49,11 @@ jobs:
- name: Run tests - name: Run tests
run: | run: |
make test make test TIMEOUT=120s
- name: Generate coverage report - name: Generate coverage report
run: | run: |
make cover make cover TIMEOUT=120s
echo "GOROOT=$(go env GOROOT)" >> $GITHUB_ENV echo "GOROOT=$(go env GOROOT)" >> $GITHUB_ENV
- name: Convert coverage to lcov - name: Convert coverage to lcov
@ -78,7 +63,7 @@ jobs:
outfile: cover.lcov outfile: cover.lcov
- name: Coveralls Parallel - name: Coveralls Parallel
uses: coverallsapp/github-action@1.1.3 uses: coverallsapp/github-action@v2.3.0
env: env:
COVERALLS_FLAG_NAME: run-${{ matrix.go-version }} COVERALLS_FLAG_NAME: run-${{ matrix.go-version }}
with: with:
@ -87,11 +72,13 @@ jobs:
parallel: true parallel: true
finish: finish:
permissions:
contents: none
needs: go needs: go
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Coveralls Finished - name: Coveralls Finished
uses: coverallsapp/github-action@1.1.3 uses: coverallsapp/github-action@v2.3.0
with: with:
github-token: ${{ secrets.github_token }} github-token: ${{ secrets.github_token }}
parallel-finished: true parallel-finished: true

View file

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

View file

@ -2,6 +2,721 @@
All notable changes to this project will be documented in this file. 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 ## 1.0.0 - 2022-08-04
### Added ### Added

View file

@ -1,18 +0,0 @@
FROM golang:1.18 AS builder
WORKDIR /workdir
COPY . .
RUN apt -y update && apt -y install protobuf-compiler
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,14 +7,20 @@ GOFMT := "$(GODIR)/gofmt"
GOOS ?= linux GOOS ?= linux
GOARCH ?= amd64 GOARCH ?= amd64
GOVERSION := $(shell "$(GO)" env GOVERSION | sed "s|go||" ) GOVERSION := $(shell "$(GO)" env GOVERSION | sed "s|go||" )
BINDIR := "$(CURDIR)/bin" BINDIR := $(CURDIR)/bin
VENDORDIR := "$(CURDIR)/vendor" VENDORDIR := "$(CURDIR)/vendor"
VERSION := $(shell "$(CURDIR)/scripts/get-version.sh") VERSION := $(shell "$(CURDIR)/scripts/get-version.sh")
TARVERSION := $(shell "$(CURDIR)/scripts/get-version.sh" --tar) TARVERSION := $(shell "$(CURDIR)/scripts/get-version.sh" --tar)
PACKAGENAME := github.com/strukturag/nextcloud-spreed-signaling PACKAGENAME := github.com/strukturag/nextcloud-spreed-signaling
ALL_PACKAGES := $(PACKAGENAME) $(PACKAGENAME)/client $(PACKAGENAME)/proxy $(PACKAGENAME)/server ALL_PACKAGES := $(PACKAGENAME) $(PACKAGENAME)/client $(PACKAGENAME)/proxy $(PACKAGENAME)/server
PROTO_FILES := $(basename $(wildcard *.proto))
PROTOC_GEN_GRPC_VERSION := v1.2.0 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),) ifneq ($(VERSION),)
INTERNALLDFLAGS := -X main.version=$(VERSION) INTERNALLDFLAGS := -X main.version=$(VERSION)
@ -39,13 +45,21 @@ TIMEOUT := 60s
endif endif
ifneq ($(TEST),) ifneq ($(TEST),)
TESTARGS := $(TESTARGS) -run $(TEST) TESTARGS := $(TESTARGS) -run "$(TEST)"
endif endif
ifneq ($(COUNT),) ifneq ($(COUNT),)
TESTARGS := $(TESTARGS) -count $(COUNT) TESTARGS := $(TESTARGS) -count $(COUNT)
endif endif
ifneq ($(PARALLEL),)
TESTARGS := $(TESTARGS) -parallel $(PARALLEL)
endif
ifneq ($(VERBOSE),)
TESTARGS := $(TESTARGS) -v
endif
ifeq ($(GOARCH), amd64) ifeq ($(GOARCH), amd64)
GOPATHBIN := $(GOPATH)/bin GOPATHBIN := $(GOPATH)/bin
else else
@ -55,15 +69,13 @@ endif
hook: hook:
[ ! -d "$(CURDIR)/.git/hooks" ] || ln -sf "$(CURDIR)/scripts/pre-commit.hook" "$(CURDIR)/.git/hooks/pre-commit" [ ! -d "$(CURDIR)/.git/hooks" ] || ln -sf "$(CURDIR)/scripts/pre-commit.hook" "$(CURDIR)/.git/hooks/pre-commit"
$(GOPATHBIN)/easyjson: $(GOPATHBIN)/easyjson: go.mod go.sum
[ "$(GOPROXY)" = "off" ] || $(GO) get -u -d github.com/mailru/easyjson/...
$(GO) install github.com/mailru/easyjson/... $(GO) install github.com/mailru/easyjson/...
$(GOPATHBIN)/protoc-gen-go: $(GOPATHBIN)/protoc-gen-go: go.mod go.sum
$(GO) install google.golang.org/protobuf/cmd/protoc-gen-go $(GO) install google.golang.org/protobuf/cmd/protoc-gen-go
$(GOPATHBIN)/protoc-gen-go-grpc: $(GOPATHBIN)/protoc-gen-go-grpc: go.mod go.sum
[ "$(GOPROXY)" = "off" ] || $(GO) get -u -d google.golang.org/grpc/cmd/protoc-gen-go-grpc
$(GO) install google.golang.org/grpc/cmd/protoc-gen-go-grpc $(GO) install google.golang.org/grpc/cmd/protoc-gen-go-grpc
continentmap.go: continentmap.go:
@ -80,85 +92,68 @@ check-continentmap:
get: get:
$(GO) get $(PACKAGE) $(GO) get $(PACKAGE)
fmt: hook | common_proto fmt: hook | $(PROTO_GO_FILES)
$(GOFMT) -s -w *.go client proxy server $(GOFMT) -s -w *.go client proxy server
vet: common vet: common
$(GO) vet $(ALL_PACKAGES) $(GO) vet $(ALL_PACKAGES)
test: vet common test: vet common
$(GO) test -v -timeout $(TIMEOUT) $(TESTARGS) $(ALL_PACKAGES) $(GO) test -timeout $(TIMEOUT) $(TESTARGS) $(ALL_PACKAGES)
cover: vet common cover: vet common
rm -f cover.out && \ 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 "/_easyjson/d" cover.out && \
sed -i "/\.pb\.go/d" cover.out && \ sed -i "/\.pb\.go/d" cover.out && \
$(GO) tool cover -func=cover.out $(GO) tool cover -func=cover.out
coverhtml: vet common coverhtml: vet common
rm -f cover.out && \ 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 "/_easyjson/d" cover.out && \
sed -i "/\.pb\.go/d" cover.out && \ sed -i "/\.pb\.go/d" cover.out && \
$(GO) tool cover -html=cover.out -o coverage.html $(GO) tool cover -html=cover.out -o coverage.html
%_easyjson.go: %.go $(GOPATHBIN)/easyjson | common_proto %_easyjson.go: %.go $(GOPATHBIN)/easyjson | $(PROTO_GO_FILES)
rm -f easyjson-bootstrap*.go
PATH="$(GODIR)":$(PATH) "$(GOPATHBIN)/easyjson" -all $*.go PATH="$(GODIR)":$(PATH) "$(GOPATHBIN)/easyjson" -all $*.go
%.pb.go: %.proto $(GOPATHBIN)/protoc-gen-go $(GOPATHBIN)/protoc-gen-go-grpc %.pb.go: %.proto $(GOPATHBIN)/protoc-gen-go $(GOPATHBIN)/protoc-gen-go-grpc
PATH="$(GODIR)":"$(GOPATHBIN)":$(PATH) protoc --go_out=. --go_opt=paths=source_relative \ 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 \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \
$*.proto $*.proto
common: common_easyjson common_proto common: $(EASYJSON_GO_FILES) $(PROTO_GO_FILES)
common_easyjson: \
api_async_easyjson.go \
api_backend_easyjson.go \
api_grpc_easyjson.go \
api_proxy_easyjson.go \
api_signaling_easyjson.go
common_proto: \
grpc_backend.pb.go \
grpc_internal.pb.go \
grpc_mcu.pb.go \
grpc_sessions.pb.go
$(BINDIR): $(BINDIR):
mkdir -p $(BINDIR) mkdir -p "$(BINDIR)"
client: common $(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) server: common $(BINDIR)
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o $(BINDIR)/signaling ./server/... $(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o "$(BINDIR)/signaling" ./server/...
proxy: common $(BINDIR) proxy: common $(BINDIR)
$(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o $(BINDIR)/proxy ./proxy/... $(GO) build $(BUILDARGS) -ldflags '$(INTERNALLDFLAGS)' -o "$(BINDIR)/proxy" ./proxy/...
clean: clean:
rm -f *_easyjson.go rm -f $(EASYJSON_GO_FILES)
rm -f easyjson-bootstrap*.go rm -f easyjson-bootstrap*.go
rm -f *.pb.go rm -f $(PROTO_GO_FILES)
build: server proxy build: server proxy
vendor: go.mod go.sum common vendor: go.mod go.sum common
set -e ;\ set -e ;\
rm -rf $(VENDORDIR) rm -rf $(VENDORDIR)
EASYJSON_DIR=$$($(GO) list -m -f '{{.Dir}}' github.com/mailru/easyjson); \ $(GO) mod vendor
PROTOBUF_DIR=$$($(GO) list -m -f '{{.Dir}}' google.golang.org/protobuf); \
$(GO) mod tidy; \
$(GO) mod vendor; \
mkdir -p $(VENDORDIR)/github.com/mailru/easyjson/; \
cp -rf --no-preserve=mode $$EASYJSON_DIR/easyjson/ $(VENDORDIR)/github.com/mailru/easyjson/; \
mkdir -p $(VENDORDIR)/google.golang.org/grpc/cmd/protoc-gen-go-grpc/; \
cp -rf --no-preserve=mode $(GOPATH)/pkg/mod/google.golang.org/grpc/cmd/protoc-gen-go-grpc@$(PROTOC_GEN_GRPC_VERSION)/* $(VENDORDIR)/google.golang.org/grpc/cmd/protoc-gen-go-grpc/; \
cp -rf --no-preserve=mode $$PROTOBUF_DIR/cmd/ $(VENDORDIR)/google.golang.org/protobuf/; \
cp -rf --no-preserve=mode $$PROTOBUF_DIR/compiler/ $(VENDORDIR)/google.golang.org/protobuf/; \
cp -rf --no-preserve=mode $$PROTOBUF_DIR/types/ $(VENDORDIR)/google.golang.org/protobuf/; \
tarball: vendor tarball: vendor
git archive \ git archive \
@ -174,5 +169,7 @@ tarball: vendor
dist: tarball dist: tarball
.NOTPARALLEL: %_easyjson.go .NOTPARALLEL: $(EASYJSON_GO_FILES)
.PHONY: continentmap.go vendor .PHONY: continentmap.go common vendor
.SECONDARY: $(EASYJSON_GO_FILES) $(PROTO_GO_FILES)
.DELETE_ON_ERROR:

View file

@ -1,6 +1,6 @@
# Spreed standalone signaling server # Spreed standalone signaling server
![Build Status](https://github.com/strukturag/nextcloud-spreed-signaling/workflows/test/badge.svg) ![Build Status](https://github.com/strukturag/nextcloud-spreed-signaling/actions/workflows/test.yml/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/strukturag/nextcloud-spreed-signaling/badge.svg?branch=master)](https://coveralls.io/github/strukturag/nextcloud-spreed-signaling?branch=master) [![Coverage Status](https://coveralls.io/repos/github/strukturag/nextcloud-spreed-signaling/badge.svg?branch=master)](https://coveralls.io/github/strukturag/nextcloud-spreed-signaling?branch=master)
[![Documentation Status](https://readthedocs.org/projects/nextcloud-spreed-signaling/badge/?version=latest)](https://nextcloud-spreed-signaling.readthedocs.io/en/latest/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/nextcloud-spreed-signaling/badge/?version=latest)](https://nextcloud-spreed-signaling.readthedocs.io/en/latest/?badge=latest)
[![Go Report](https://goreportcard.com/badge/github.com/strukturag/nextcloud-spreed-signaling)](https://goreportcard.com/report/github.com/strukturag/nextcloud-spreed-signaling) [![Go Report](https://goreportcard.com/badge/github.com/strukturag/nextcloud-spreed-signaling)](https://goreportcard.com/report/github.com/strukturag/nextcloud-spreed-signaling)
@ -17,10 +17,13 @@ information on the API of the signaling server.
The following tools are required for building the signaling server. The following tools are required for building the signaling server.
- git - git
- go >= 1.17 - go >= 1.21
- make - make
- protobuf-compiler >= 3 - 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. All other dependencies are fetched automatically while building.
$ make build $ make build
@ -88,23 +91,34 @@ systemctl start signaling.service
### Running with Docker ### 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 #### 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. 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. 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 ```bash
docker-compose build docker-compose build
docker-compose up -d 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 ## Setup of NATS server
There is a detailed description on how to install and run the 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 You can use the `gnatsd.conf` file as base for the configuration of the NATS
server. server.
@ -157,7 +171,17 @@ proxy process gracefully after all clients have been disconnected. No new
publishers will be accepted in this case. publishers will be accepted in this case.
### Clustering ### 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 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 can be interconnected to form a single "virtual" server. This can be used to
@ -285,6 +309,8 @@ interface on port `8080` below):
# Enable proxying Websocket requests to the standalone signaling server. # Enable proxying Websocket requests to the standalone signaling server.
ProxyPass "/standalone-signaling/" "ws://127.0.0.1:8080/" ProxyPass "/standalone-signaling/" "ws://127.0.0.1:8080/"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RewriteEngine On RewriteEngine On
# Websocket connections from the clients. # Websocket connections from the clients.
RewriteRule ^/standalone-signaling/spreed/$ - [L] RewriteRule ^/standalone-signaling/spreed/$ - [L]
@ -320,6 +346,7 @@ myserver.domain.invalid {
route /standalone-signaling/* { route /standalone-signaling/* {
uri strip_prefix /standalone-signaling uri strip_prefix /standalone-signaling
reverse_proxy http://127.0.0.1:8080 reverse_proxy http://127.0.0.1:8080
header_up X-Real-IP {remote_host}
} }
} }
``` ```
@ -366,23 +393,3 @@ Usage:
config file to use (default "server.conf") config file to use (default "server.conf")
-maxClients int -maxClients int
number of client connections (default 100) 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)
}
})
}
}

View file

@ -21,7 +21,11 @@
*/ */
package signaling package signaling
import "time" import (
"encoding/json"
"fmt"
"time"
)
type AsyncMessage struct { type AsyncMessage struct {
SendTime time.Time `json:"sendtime"` SendTime time.Time `json:"sendtime"`
@ -41,6 +45,14 @@ type AsyncMessage struct {
Id string `json:"id"` 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 AsyncRoomMessage struct {
Type string `json:"type"` Type string `json:"type"`

View file

@ -31,7 +31,9 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"regexp"
"strings" "strings"
"time"
) )
const ( const (
@ -102,6 +104,12 @@ type BackendServerRoomRequest struct {
Message *BackendRoomMessageRequest `json:"message,omitempty"` Message *BackendRoomMessageRequest `json:"message,omitempty"`
SwitchTo *BackendRoomSwitchToMessageRequest `json:"switchto,omitempty"`
Dialout *BackendRoomDialoutRequest `json:"dialout,omitempty"`
Transient *BackendRoomTransientRequest `json:"transient,omitempty"`
// Internal properties // Internal properties
ReceivedTime int64 `json:"received,omitempty"` ReceivedTime int64 `json:"received,omitempty"`
} }
@ -110,8 +118,8 @@ type BackendRoomInviteRequest struct {
UserIds []string `json:"userids,omitempty"` UserIds []string `json:"userids,omitempty"`
// TODO(jojo): We should get rid of "AllUserIds" and find a better way to // 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. // notify existing users the room has changed and they need to update it.
AllUserIds []string `json:"alluserids,omitempty"` AllUserIds []string `json:"alluserids,omitempty"`
Properties *json.RawMessage `json:"properties,omitempty"` Properties json.RawMessage `json:"properties,omitempty"`
} }
type BackendRoomDisinviteRequest struct { type BackendRoomDisinviteRequest struct {
@ -119,13 +127,13 @@ type BackendRoomDisinviteRequest struct {
SessionIds []string `json:"sessionids,omitempty"` SessionIds []string `json:"sessionids,omitempty"`
// TODO(jojo): We should get rid of "AllUserIds" and find a better way to // 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. // notify existing users the room has changed and they need to update it.
AllUserIds []string `json:"alluserids,omitempty"` AllUserIds []string `json:"alluserids,omitempty"`
Properties *json.RawMessage `json:"properties,omitempty"` Properties json.RawMessage `json:"properties,omitempty"`
} }
type BackendRoomUpdateRequest struct { type BackendRoomUpdateRequest struct {
UserIds []string `json:"userids,omitempty"` UserIds []string `json:"userids,omitempty"`
Properties *json.RawMessage `json:"properties,omitempty"` Properties json.RawMessage `json:"properties,omitempty"`
} }
type BackendRoomDeleteRequest struct { type BackendRoomDeleteRequest struct {
@ -146,14 +154,91 @@ type BackendRoomParticipantsRequest struct {
} }
type BackendRoomMessageRequest 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. // Requests from the signaling server to the Nextcloud backend.
type BackendClientAuthRequest struct { type BackendClientAuthRequest struct {
Version string `json:"version"` Version string `json:"version"`
Params *json.RawMessage `json:"params"` Params json.RawMessage `json:"params"`
} }
type BackendClientRequest struct { type BackendClientRequest struct {
@ -171,7 +256,7 @@ type BackendClientRequest struct {
Session *BackendClientSessionRequest `json:"session,omitempty"` Session *BackendClientSessionRequest `json:"session,omitempty"`
} }
func NewBackendClientAuthRequest(params *json.RawMessage) *BackendClientRequest { func NewBackendClientAuthRequest(params json.RawMessage) *BackendClientRequest {
return &BackendClientRequest{ return &BackendClientRequest{
Type: "auth", Type: "auth",
Auth: &BackendClientAuthRequest{ Auth: &BackendClientAuthRequest{
@ -199,9 +284,9 @@ type BackendClientResponse struct {
} }
type BackendClientAuthResponse struct { type BackendClientAuthResponse struct {
Version string `json:"version"` Version string `json:"version"`
UserId string `json:"userid"` UserId string `json:"userid"`
User *json.RawMessage `json:"user"` User json.RawMessage `json:"user"`
} }
type BackendClientRoomRequest struct { type BackendClientRoomRequest struct {
@ -230,14 +315,14 @@ func NewBackendClientRoomRequest(roomid string, userid string, sessionid string)
} }
type BackendClientRoomResponse struct { type BackendClientRoomResponse struct {
Version string `json:"version"` Version string `json:"version"`
RoomId string `json:"roomid"` RoomId string `json:"roomid"`
Properties *json.RawMessage `json:"properties"` Properties json.RawMessage `json:"properties"`
// Optional information about the Nextcloud Talk session. Can be used for // Optional information about the Nextcloud Talk session. Can be used for
// example to define a "userid" for otherwise anonymous users. // example to define a "userid" for otherwise anonymous users.
// See "RoomSessionData" for a possible content. // See "RoomSessionData" for a possible content.
Session *json.RawMessage `json:"session,omitempty"` Session json.RawMessage `json:"session,omitempty"`
Permissions *[]Permission `json:"permissions,omitempty"` Permissions *[]Permission `json:"permissions,omitempty"`
} }
@ -274,12 +359,12 @@ type BackendClientRingResponse struct {
} }
type BackendClientSessionRequest struct { type BackendClientSessionRequest struct {
Version string `json:"version"` Version string `json:"version"`
RoomId string `json:"roomid"` RoomId string `json:"roomid"`
Action string `json:"action"` Action string `json:"action"`
SessionId string `json:"sessionid"` SessionId string `json:"sessionid"`
UserId string `json:"userid,omitempty"` UserId string `json:"userid,omitempty"`
User *json.RawMessage `json:"user,omitempty"` User json.RawMessage `json:"user,omitempty"`
} }
type BackendClientSessionResponse struct { type BackendClientSessionResponse struct {
@ -311,8 +396,8 @@ type OcsMeta struct {
} }
type OcsBody struct { type OcsBody struct {
Meta OcsMeta `json:"meta"` Meta OcsMeta `json:"meta"`
Data *json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
type OcsResponse struct { type OcsResponse struct {

View file

@ -27,6 +27,7 @@ import (
) )
func TestBackendChecksum(t *testing.T) { func TestBackendChecksum(t *testing.T) {
t.Parallel()
rnd := newRandomString(32) rnd := newRandomString(32)
body := []byte{1, 2, 3, 4, 5} body := []byte{1, 2, 3, 4, 5}
secret := []byte("shared-secret") secret := []byte("shared-secret")
@ -56,3 +57,28 @@ func TestBackendChecksum(t *testing.T) {
t.Errorf("Checksum %s could not be validated from request", check1) 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)
}
}
}

View file

@ -24,6 +24,7 @@ package signaling
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
@ -48,6 +49,14 @@ type ProxyClientMessage struct {
Payload *PayloadProxyClientMessage `json:"payload,omitempty"` 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 { func (m *ProxyClientMessage) CheckValid() error {
switch m.Type { switch m.Type {
case "": case "":
@ -115,6 +124,14 @@ type ProxyServerMessage struct {
Event *EventProxyServerMessage `json:"event,omitempty"` 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 { func (r *ProxyServerMessage) CloseAfterSend(session Session) bool {
switch r.Type { switch r.Type {
case "bye": case "bye":
@ -179,12 +196,20 @@ type ByeProxyServerMessage struct {
type CommandProxyClientMessage struct { type CommandProxyClientMessage struct {
Type string `json:"type"` Type string `json:"type"`
Sid string `json:"sid,omitempty"` Sid string `json:"sid,omitempty"`
StreamType string `json:"streamType,omitempty"` StreamType StreamType `json:"streamType,omitempty"`
PublisherId string `json:"publisherId,omitempty"` PublisherId string `json:"publisherId,omitempty"`
ClientId string `json:"clientId,omitempty"` ClientId string `json:"clientId,omitempty"`
Bitrate int `json:"bitrate,omitempty"` Bitrate int `json:"bitrate,omitempty"`
MediaTypes MediaType `json:"mediatypes,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 { func (m *CommandProxyClientMessage) CheckValid() error {
@ -202,6 +227,17 @@ func (m *CommandProxyClientMessage) CheckValid() error {
if m.StreamType == "" { if m.StreamType == "" {
return fmt.Errorf("stream type missing") 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": case "delete-publisher":
fallthrough fallthrough
case "delete-subscriber": case "delete-subscriber":
@ -215,6 +251,10 @@ func (m *CommandProxyClientMessage) CheckValid() error {
type CommandProxyServerMessage struct { type CommandProxyServerMessage struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Sid string `json:"sid,omitempty"` Sid string `json:"sid,omitempty"`
Bitrate int `json:"bitrate,omitempty"`
Streams []PublisherStream `json:"streams,omitempty"`
} }
// Type "payload" // Type "payload"
@ -259,12 +299,41 @@ type PayloadProxyServerMessage struct {
// Type "event" // 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 EventProxyServerMessage struct {
Type string `json:"type"` Type string `json:"type"`
ClientId string `json:"clientId,omitempty"` ClientId string `json:"clientId,omitempty"`
Load int64 `json:"load,omitempty"` Load int64 `json:"load,omitempty"`
Sid string `json:"sid,omitempty"` Sid string `json:"sid,omitempty"`
Bandwidth *EventProxyServerBandwidth `json:"bandwidth,omitempty"`
} }
// Information on a proxy in the etcd cluster. // Information on a proxy in the etcd cluster.

View file

@ -23,12 +23,16 @@ package signaling
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"log"
"net/url" "net/url"
"sort" "sort"
"strings" "strings"
"time"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
"github.com/pion/sdp/v3"
) )
const ( const (
@ -39,6 +43,11 @@ const (
HelloVersionV2 = "2.0" 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. // ClientMessage is a message that is sent from a client to the server.
type ClientMessage struct { type ClientMessage struct {
json.Marshaler json.Marshaler
@ -162,6 +171,10 @@ type ServerMessage struct {
Event *EventServerMessage `json:"event,omitempty"` Event *EventServerMessage `json:"event,omitempty"`
TransientData *TransientDataServerMessage `json:"transient,omitempty"` TransientData *TransientDataServerMessage `json:"transient,omitempty"`
Internal *InternalServerMessage `json:"internal,omitempty"`
Dialout *DialoutInternalClientMessage `json:"dialout,omitempty"`
} }
func (r *ServerMessage) CloseAfterSend(session Session) bool { func (r *ServerMessage) CloseAfterSend(session Session) bool {
@ -185,12 +198,12 @@ func (r *ServerMessage) CloseAfterSend(session Session) bool {
} }
func (r *ServerMessage) IsChatRefresh() 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 return false
} }
var data MessageServerMessageData 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 return false
} }
@ -220,9 +233,9 @@ func (r *ServerMessage) String() string {
} }
type Error struct { type Error struct {
Code string `json:"code"` Code string `json:"code"`
Message string `json:"message"` Message string `json:"message"`
Details interface{} `json:"details,omitempty"` Details json.RawMessage `json:"details,omitempty"`
} }
func NewError(code string, message string) *Error { func NewError(code string, message string) *Error {
@ -230,10 +243,19 @@ func NewError(code string, message string) *Error {
} }
func NewErrorDetail(code string, message string, details interface{}) *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{ return &Error{
Code: code, Code: code,
Message: message, Message: message,
Details: details, Details: rawDetails,
} }
} }
@ -344,7 +366,7 @@ func (p *HelloV2AuthParams) CheckValid() error {
type HelloV2TokenClaims struct { type HelloV2TokenClaims struct {
jwt.RegisteredClaims jwt.RegisteredClaims
UserData *json.RawMessage `json:"userdata,omitempty"` UserData json.RawMessage `json:"userdata,omitempty"`
} }
type HelloClientMessageAuth struct { type HelloClientMessageAuth struct {
@ -352,7 +374,7 @@ type HelloClientMessageAuth struct {
// "HelloClientTypeClient" // "HelloClientTypeClient"
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Params *json.RawMessage `json:"params"` Params json.RawMessage `json:"params"`
Url string `json:"url"` Url string `json:"url"`
parsedUrl *url.URL parsedUrl *url.URL
@ -371,7 +393,7 @@ type HelloClientMessage struct {
Features []string `json:"features,omitempty"` Features []string `json:"features,omitempty"`
// The authentication credentials. // The authentication credentials.
Auth HelloClientMessageAuth `json:"auth"` Auth *HelloClientMessageAuth `json:"auth,omitempty"`
} }
func (m *HelloClientMessage) CheckValid() error { func (m *HelloClientMessage) CheckValid() error {
@ -379,7 +401,7 @@ func (m *HelloClientMessage) CheckValid() error {
return InvalidHelloVersion return InvalidHelloVersion
} }
if m.ResumeId == "" { 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") return fmt.Errorf("params missing")
} }
if m.Auth.Type == "" { if m.Auth.Type == "" {
@ -403,14 +425,14 @@ func (m *HelloClientMessage) CheckValid() error {
case HelloVersionV1: case HelloVersionV1:
// No additional validation necessary. // No additional validation necessary.
case HelloVersionV2: case HelloVersionV2:
if err := json.Unmarshal(*m.Auth.Params, &m.Auth.helloV2Params); err != nil { if err := json.Unmarshal(m.Auth.Params, &m.Auth.helloV2Params); err != nil {
return err return err
} else if err := m.Auth.helloV2Params.CheckValid(); err != nil { } else if err := m.Auth.helloV2Params.CheckValid(); err != nil {
return err return err
} }
} }
case HelloClientTypeInternal: 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 return err
} else if err := m.Auth.internalParams.CheckValid(); err != nil { } else if err := m.Auth.internalParams.CheckValid(); err != nil {
return err return err
@ -423,7 +445,7 @@ func (m *HelloClientMessage) CheckValid() error {
} }
const ( const (
// Features for all clients. // Features to send to all clients.
ServerFeatureMcu = "mcu" ServerFeatureMcu = "mcu"
ServerFeatureSimulcast = "simulcast" ServerFeatureSimulcast = "simulcast"
ServerFeatureUpdateSdp = "update-sdp" ServerFeatureUpdateSdp = "update-sdp"
@ -432,9 +454,15 @@ const (
ServerFeatureInCallAll = "incall-all" ServerFeatureInCallAll = "incall-all"
ServerFeatureWelcome = "welcome" ServerFeatureWelcome = "welcome"
ServerFeatureHelloV2 = "hello-v2" ServerFeatureHelloV2 = "hello-v2"
ServerFeatureSwitchTo = "switchto"
ServerFeatureDialout = "dialout"
// Features for internal clients only. // Features to send to internal clients only.
ServerFeatureInternalVirtualSessions = "virtual-sessions" ServerFeatureInternalVirtualSessions = "virtual-sessions"
// Possible client features from the "hello" request.
ClientFeatureInternalInCall = "internal-incall"
ClientFeatureStartDialout = "start-dialout"
) )
var ( var (
@ -444,6 +472,8 @@ var (
ServerFeatureInCallAll, ServerFeatureInCallAll,
ServerFeatureWelcome, ServerFeatureWelcome,
ServerFeatureHelloV2, ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
} }
DefaultFeaturesInternal = []string{ DefaultFeaturesInternal = []string{
ServerFeatureInternalVirtualSessions, ServerFeatureInternalVirtualSessions,
@ -451,6 +481,8 @@ var (
ServerFeatureInCallAll, ServerFeatureInCallAll,
ServerFeatureWelcome, ServerFeatureWelcome,
ServerFeatureHelloV2, ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
} }
DefaultWelcomeFeatures = []string{ DefaultWelcomeFeatures = []string{
ServerFeatureAudioVideoPermissions, ServerFeatureAudioVideoPermissions,
@ -459,6 +491,8 @@ var (
ServerFeatureInCallAll, ServerFeatureInCallAll,
ServerFeatureWelcome, ServerFeatureWelcome,
ServerFeatureHelloV2, ServerFeatureHelloV2,
ServerFeatureSwitchTo,
ServerFeatureDialout,
} }
) )
@ -500,8 +534,12 @@ func (m *RoomClientMessage) CheckValid() error {
} }
type RoomServerMessage struct { type RoomServerMessage struct {
RoomId string `json:"roomid"` RoomId string `json:"roomid"`
Properties *json.RawMessage `json:"properties,omitempty"` Properties json.RawMessage `json:"properties,omitempty"`
}
type RoomErrorDetails struct {
Room *RoomServerMessage `json:"room"`
} }
// Type "message" // Type "message"
@ -522,7 +560,7 @@ type MessageClientMessageRecipient struct {
type MessageClientMessage struct { type MessageClientMessage struct {
Recipient MessageClientMessageRecipient `json:"recipient"` Recipient MessageClientMessageRecipient `json:"recipient"`
Data *json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
type MessageClientMessageData struct { type MessageClientMessageData struct {
@ -531,10 +569,44 @@ type MessageClientMessageData struct {
RoomType string `json:"roomType"` RoomType string `json:"roomType"`
Bitrate int `json:"bitrate,omitempty"` Bitrate int `json:"bitrate,omitempty"`
Payload map[string]interface{} `json:"payload"` 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 { func (m *MessageClientMessage) CheckValid() error {
if m.Data == nil || len(*m.Data) == 0 { if len(m.Data) == 0 {
return fmt.Errorf("message empty") return fmt.Errorf("message empty")
} }
switch m.Recipient.Type { switch m.Recipient.Type {
@ -575,7 +647,7 @@ type MessageServerMessage struct {
Sender *MessageServerMessageSender `json:"sender"` Sender *MessageServerMessageSender `json:"sender"`
Recipient *MessageClientMessageRecipient `json:"recipient,omitempty"` Recipient *MessageClientMessageRecipient `json:"recipient,omitempty"`
Data *json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
// Type "control" // Type "control"
@ -592,7 +664,7 @@ type ControlServerMessage struct {
Sender *MessageServerMessageSender `json:"sender"` Sender *MessageServerMessageSender `json:"sender"`
Recipient *MessageClientMessageRecipient `json:"recipient,omitempty"` Recipient *MessageClientMessageRecipient `json:"recipient,omitempty"`
Data *json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
// Type "internal" // Type "internal"
@ -621,9 +693,10 @@ type AddSessionOptions struct {
type AddSessionInternalClientMessage struct { type AddSessionInternalClientMessage struct {
CommonSessionInternalClientMessage CommonSessionInternalClientMessage
UserId string `json:"userid,omitempty"` UserId string `json:"userid,omitempty"`
User *json.RawMessage `json:"user,omitempty"` User json.RawMessage `json:"user,omitempty"`
Flags uint32 `json:"flags,omitempty"` Flags uint32 `json:"flags,omitempty"`
InCall *int `json:"incall,omitempty"`
Options *AddSessionOptions `json:"options,omitempty"` Options *AddSessionOptions `json:"options,omitempty"`
} }
@ -635,7 +708,8 @@ func (m *AddSessionInternalClientMessage) CheckValid() error {
type UpdateSessionInternalClientMessage struct { type UpdateSessionInternalClientMessage struct {
CommonSessionInternalClientMessage CommonSessionInternalClientMessage
Flags *uint32 `json:"flags,omitempty"` Flags *uint32 `json:"flags,omitempty"`
InCall *int `json:"incall,omitempty"`
} }
func (m *UpdateSessionInternalClientMessage) CheckValid() error { func (m *UpdateSessionInternalClientMessage) CheckValid() error {
@ -652,6 +726,60 @@ func (m *RemoveSessionInternalClientMessage) CheckValid() error {
return m.CommonSessionInternalClientMessage.CheckValid() 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 InternalClientMessage struct {
Type string `json:"type"` Type string `json:"type"`
@ -660,10 +788,16 @@ type InternalClientMessage struct {
UpdateSession *UpdateSessionInternalClientMessage `json:"updatesession,omitempty"` UpdateSession *UpdateSessionInternalClientMessage `json:"updatesession,omitempty"`
RemoveSession *RemoveSessionInternalClientMessage `json:"removesession,omitempty"` RemoveSession *RemoveSessionInternalClientMessage `json:"removesession,omitempty"`
InCall *InCallInternalClientMessage `json:"incall,omitempty"`
Dialout *DialoutInternalClientMessage `json:"dialout,omitempty"`
} }
func (m *InternalClientMessage) CheckValid() error { func (m *InternalClientMessage) CheckValid() error {
switch m.Type { switch m.Type {
case "":
return errors.New("type missing")
case "addsession": case "addsession":
if m.AddSession == nil { if m.AddSession == nil {
return fmt.Errorf("addsession missing") return fmt.Errorf("addsession missing")
@ -682,17 +816,42 @@ func (m *InternalClientMessage) CheckValid() error {
} else if err := m.RemoveSession.CheckValid(); err != nil { } else if err := m.RemoveSession.CheckValid(); err != nil {
return err 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 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 "event"
type RoomEventServerMessage struct { type RoomEventServerMessage struct {
RoomId string `json:"roomid"` RoomId string `json:"roomid"`
Properties *json.RawMessage `json:"properties,omitempty"` Properties json.RawMessage `json:"properties,omitempty"`
// TODO(jojo): Change "InCall" to "int" when #914 has landed in NC Talk. // 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"` Changed []map[string]interface{} `json:"changed,omitempty"`
Users []map[string]interface{} `json:"users,omitempty"` Users []map[string]interface{} `json:"users,omitempty"`
@ -719,8 +878,8 @@ type RoomDisinviteEventServerMessage struct {
} }
type RoomEventMessage struct { type RoomEventMessage struct {
RoomId string `json:"roomid"` RoomId string `json:"roomid"`
Data *json.RawMessage `json:"data,omitempty"` Data json.RawMessage `json:"data,omitempty"`
} }
type RoomFlagsServerMessage struct { type RoomFlagsServerMessage struct {
@ -746,9 +905,10 @@ type EventServerMessage struct {
Type string `json:"type"` Type string `json:"type"`
// Used for target "room" // Used for target "room"
Join []*EventServerMessageSessionEntry `json:"join,omitempty"` Join []*EventServerMessageSessionEntry `json:"join,omitempty"`
Leave []string `json:"leave,omitempty"` Leave []string `json:"leave,omitempty"`
Change []*EventServerMessageSessionEntry `json:"change,omitempty"` Change []*EventServerMessageSessionEntry `json:"change,omitempty"`
SwitchTo *EventServerMessageSwitchTo `json:"switchto,omitempty"`
// Used for target "roomlist" / "participants" // Used for target "roomlist" / "participants"
Invite *RoomEventServerMessage `json:"invite,omitempty"` Invite *RoomEventServerMessage `json:"invite,omitempty"`
@ -769,10 +929,10 @@ func (m *EventServerMessage) String() string {
} }
type EventServerMessageSessionEntry struct { type EventServerMessageSessionEntry struct {
SessionId string `json:"sessionid"` SessionId string `json:"sessionid"`
UserId string `json:"userid"` UserId string `json:"userid"`
User *json.RawMessage `json:"user,omitempty"` User json.RawMessage `json:"user,omitempty"`
RoomSessionId string `json:"roomsessionid,omitempty"` RoomSessionId string `json:"roomsessionid,omitempty"`
} }
func (e *EventServerMessageSessionEntry) Clone() *EventServerMessageSessionEntry { func (e *EventServerMessageSessionEntry) Clone() *EventServerMessageSessionEntry {
@ -784,6 +944,11 @@ func (e *EventServerMessageSessionEntry) Clone() *EventServerMessageSessionEntry
} }
} }
type EventServerMessageSwitchTo struct {
RoomId string `json:"roomid"`
Details json.RawMessage `json:"details,omitempty"`
}
// MCU-related types // MCU-related types
type AnswerOfferMessage struct { type AnswerOfferMessage struct {
@ -800,8 +965,9 @@ type AnswerOfferMessage struct {
type TransientDataClientMessage struct { type TransientDataClientMessage struct {
Type string `json:"type"` Type string `json:"type"`
Key string `json:"key,omitempty"` Key string `json:"key,omitempty"`
Value *json.RawMessage `json:"value,omitempty"` Value json.RawMessage `json:"value,omitempty"`
TTL time.Duration `json:"ttl,omitempty"`
} }
func (m *TransientDataClientMessage) CheckValid() error { func (m *TransientDataClientMessage) CheckValid() error {

View file

@ -81,6 +81,7 @@ func testMessages(t *testing.T, messageType string, valid_messages []testCheckVa
} }
func TestClientMessage(t *testing.T) { func TestClientMessage(t *testing.T) {
t.Parallel()
// The message needs a type. // The message needs a type.
msg := ClientMessage{} msg := ClientMessage{}
if err := msg.CheckValid(); err == nil { if err := msg.CheckValid(); err == nil {
@ -89,30 +90,31 @@ func TestClientMessage(t *testing.T) {
} }
func TestHelloClientMessage(t *testing.T) { func TestHelloClientMessage(t *testing.T) {
t.Parallel()
internalAuthParams := []byte("{\"backend\":\"https://domain.invalid\"}") internalAuthParams := []byte("{\"backend\":\"https://domain.invalid\"}")
tokenAuthParams := []byte("{\"token\":\"invalid-token\"}") tokenAuthParams := []byte("{\"token\":\"invalid-token\"}")
valid_messages := []testCheckValid{ valid_messages := []testCheckValid{
// Hello version 1 // Hello version 1
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'}, Params: json.RawMessage("{}"),
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Type: "client", Type: "client",
Params: &json.RawMessage{'{', '}'}, Params: json.RawMessage("{}"),
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Type: "internal", Type: "internal",
Params: (*json.RawMessage)(&internalAuthParams), Params: internalAuthParams,
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
@ -122,16 +124,16 @@ func TestHelloClientMessage(t *testing.T) {
// Hello version 2 // Hello version 2
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV2, Version: HelloVersionV2,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: (*json.RawMessage)(&tokenAuthParams), Params: tokenAuthParams,
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV2, Version: HelloVersionV2,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Type: "client", Type: "client",
Params: (*json.RawMessage)(&tokenAuthParams), Params: tokenAuthParams,
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
@ -147,75 +149,75 @@ func TestHelloClientMessage(t *testing.T) {
&HelloClientMessage{Version: HelloVersionV1}, &HelloClientMessage{Version: HelloVersionV1},
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'}, Params: json.RawMessage("{}"),
Type: "invalid-type", Type: "invalid-type",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'}, Params: json.RawMessage("{}"),
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: &json.RawMessage{'{', '}'}, Params: json.RawMessage("{}"),
Url: "invalid-url", Url: "invalid-url",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Type: "internal", Type: "internal",
Params: &json.RawMessage{'{', '}'}, Params: json.RawMessage("{}"),
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV1, Version: HelloVersionV1,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Type: "internal", Type: "internal",
Params: &json.RawMessage{'x', 'y', 'z'}, // Invalid JSON. Params: json.RawMessage("xyz"), // Invalid JSON.
}, },
}, },
// Hello version 2 // Hello version 2
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV2, Version: HelloVersionV2,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV2, Version: HelloVersionV2,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: (*json.RawMessage)(&tokenAuthParams), Params: tokenAuthParams,
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV2, Version: HelloVersionV2,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: (*json.RawMessage)(&tokenAuthParams), Params: tokenAuthParams,
Url: "invalid-url", Url: "invalid-url",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV2, Version: HelloVersionV2,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: (*json.RawMessage)(&internalAuthParams), Params: internalAuthParams,
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
&HelloClientMessage{ &HelloClientMessage{
Version: HelloVersionV2, Version: HelloVersionV2,
Auth: HelloClientMessageAuth{ Auth: &HelloClientMessageAuth{
Params: &json.RawMessage{'x', 'y', 'z'}, // Invalid JSON. Params: json.RawMessage("xyz"), // Invalid JSON.
Url: "https://domain.invalid", Url: "https://domain.invalid",
}, },
}, },
@ -233,26 +235,27 @@ func TestHelloClientMessage(t *testing.T) {
} }
func TestMessageClientMessage(t *testing.T) { func TestMessageClientMessage(t *testing.T) {
t.Parallel()
valid_messages := []testCheckValid{ valid_messages := []testCheckValid{
&MessageClientMessage{ &MessageClientMessage{
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
Type: "session", Type: "session",
SessionId: "the-session-id", SessionId: "the-session-id",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
&MessageClientMessage{ &MessageClientMessage{
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
Type: "user", Type: "user",
UserId: "the-user-id", UserId: "the-user-id",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
&MessageClientMessage{ &MessageClientMessage{
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
Type: "room", Type: "room",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
} }
invalid_messages := []testCheckValid{ invalid_messages := []testCheckValid{
@ -267,20 +270,20 @@ func TestMessageClientMessage(t *testing.T) {
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
Type: "session", Type: "session",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
&MessageClientMessage{ &MessageClientMessage{
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
Type: "session", Type: "session",
UserId: "the-user-id", UserId: "the-user-id",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
&MessageClientMessage{ &MessageClientMessage{
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
Type: "user", Type: "user",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
&MessageClientMessage{ &MessageClientMessage{
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
@ -293,13 +296,13 @@ func TestMessageClientMessage(t *testing.T) {
Type: "user", Type: "user",
SessionId: "the-user-id", SessionId: "the-user-id",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
&MessageClientMessage{ &MessageClientMessage{
Recipient: MessageClientMessageRecipient{ Recipient: MessageClientMessageRecipient{
Type: "unknown-type", Type: "unknown-type",
}, },
Data: &json.RawMessage{'{', '}'}, Data: json.RawMessage("{}"),
}, },
} }
testMessages(t, "message", valid_messages, invalid_messages) testMessages(t, "message", valid_messages, invalid_messages)
@ -314,6 +317,7 @@ func TestMessageClientMessage(t *testing.T) {
} }
func TestByeClientMessage(t *testing.T) { func TestByeClientMessage(t *testing.T) {
t.Parallel()
// Any "bye" message is valid. // Any "bye" message is valid.
valid_messages := []testCheckValid{ valid_messages := []testCheckValid{
&ByeClientMessage{}, &ByeClientMessage{},
@ -332,6 +336,7 @@ func TestByeClientMessage(t *testing.T) {
} }
func TestRoomClientMessage(t *testing.T) { func TestRoomClientMessage(t *testing.T) {
t.Parallel()
// Any "room" message is valid. // Any "room" message is valid.
valid_messages := []testCheckValid{ valid_messages := []testCheckValid{
&RoomClientMessage{}, &RoomClientMessage{},
@ -350,6 +355,7 @@ func TestRoomClientMessage(t *testing.T) {
} }
func TestErrorMessages(t *testing.T) { func TestErrorMessages(t *testing.T) {
t.Parallel()
id := "request-id" id := "request-id"
msg := ClientMessage{ msg := ClientMessage{
Id: id, Id: id,
@ -382,12 +388,13 @@ func TestErrorMessages(t *testing.T) {
} }
func TestIsChatRefresh(t *testing.T) { func TestIsChatRefresh(t *testing.T) {
t.Parallel()
var msg ServerMessage var msg ServerMessage
data_true := []byte("{\"type\":\"chat\",\"chat\":{\"refresh\":true}}") data_true := []byte("{\"type\":\"chat\",\"chat\":{\"refresh\":true}}")
msg = ServerMessage{ msg = ServerMessage{
Type: "message", Type: "message",
Message: &MessageServerMessage{ Message: &MessageServerMessage{
Data: (*json.RawMessage)(&data_true), Data: data_true,
}, },
} }
if !msg.IsChatRefresh() { if !msg.IsChatRefresh() {
@ -398,7 +405,7 @@ func TestIsChatRefresh(t *testing.T) {
msg = ServerMessage{ msg = ServerMessage{
Type: "message", Type: "message",
Message: &MessageServerMessage{ Message: &MessageServerMessage{
Data: (*json.RawMessage)(&data_false), Data: data_false,
}, },
} }
if msg.IsChatRefresh() { if msg.IsChatRefresh() {
@ -426,6 +433,7 @@ func assertEqualStrings(t *testing.T, expected, result []string) {
} }
func Test_Welcome_AddRemoveFeature(t *testing.T) { func Test_Welcome_AddRemoveFeature(t *testing.T) {
t.Parallel()
var msg WelcomeServerMessage var msg WelcomeServerMessage
assertEqualStrings(t, []string{}, msg.Features) assertEqualStrings(t, []string{}, msg.Features)

View file

@ -62,7 +62,7 @@ type asyncSubscriberNats struct {
client NatsClient client NatsClient
receiver chan *nats.Msg receiver chan *nats.Msg
closeChan chan bool closeChan chan struct{}
subscription NatsSubscription subscription NatsSubscription
processMessage func(*nats.Msg) processMessage func(*nats.Msg)
@ -80,7 +80,7 @@ func newAsyncSubscriberNats(key string, client NatsClient) (*asyncSubscriberNats
client: client, client: client,
receiver: receiver, receiver: receiver,
closeChan: make(chan bool), closeChan: make(chan struct{}),
subscription: sub, subscription: sub,
} }
return result, nil return result, nil
@ -280,6 +280,8 @@ func (e *asyncEventsNats) Close() {
sub.close() sub.close()
} }
}(e.sessionSubscriptions) }(e.sessionSubscriptions)
// Can't use clear(...) here as the maps are processed asynchronously by the
// goroutines above.
e.backendRoomSubscriptions = make(map[string]*asyncBackendRoomSubscriberNats) e.backendRoomSubscriptions = make(map[string]*asyncBackendRoomSubscriberNats)
e.roomSubscriptions = make(map[string]*asyncRoomSubscriberNats) e.roomSubscriptions = make(map[string]*asyncRoomSubscriberNats)
e.userSubscriptions = make(map[string]*asyncUserSubscriberNats) e.userSubscriptions = make(map[string]*asyncUserSubscriberNats)

View file

@ -39,6 +39,9 @@ import (
var ( var (
ErrNotRedirecting = errors.New("not redirecting to different host") ErrNotRedirecting = errors.New("not redirecting to different host")
ErrUnsupportedContentType = errors.New("unsupported_content_type") ErrUnsupportedContentType = errors.New("unsupported_content_type")
ErrIncompleteResponse = errors.New("incomplete OCS response")
ErrThrottledResponse = errors.New("throttled OCS response")
) )
type BackendClient struct { type BackendClient struct {
@ -191,11 +194,19 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ
if err := json.Unmarshal(body, &ocs); err != nil { if err := json.Unmarshal(body, &ocs); err != nil {
log.Printf("Could not decode OCS response %s from %s: %s", string(body), req.URL, err) log.Printf("Could not decode OCS response %s from %s: %s", string(body), req.URL, err)
return 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) log.Printf("Incomplete OCS response %s from %s", string(body), req.URL)
return fmt.Errorf("incomplete OCS response") return ErrIncompleteResponse
} 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)
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 return err
} }
} else if err := json.Unmarshal(body, response); err != nil { } else if err := json.Unmarshal(body, response); err != nil {

View file

@ -30,6 +30,7 @@ import (
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"reflect" "reflect"
"strings"
"testing" "testing"
"github.com/dlintw/goconf" "github.com/dlintw/goconf"
@ -44,9 +45,17 @@ func returnOCS(t *testing.T, w http.ResponseWriter, body []byte) {
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
Message: "OK", 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) data, err := json.Marshal(response)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -61,6 +70,8 @@ func returnOCS(t *testing.T, w http.ResponseWriter, body []byte) {
} }
func TestPostOnRedirect(t *testing.T) { func TestPostOnRedirect(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/ocs/v2.php/two", http.StatusTemporaryRedirect) http.Redirect(w, r, "/ocs/v2.php/two", http.StatusTemporaryRedirect)
@ -116,6 +127,8 @@ func TestPostOnRedirect(t *testing.T) {
} }
func TestPostOnRedirectDifferentHost(t *testing.T) { func TestPostOnRedirectDifferentHost(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) { 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) http.Redirect(w, r, "http://domain.invalid/ocs/v2.php/two", http.StatusTemporaryRedirect)
@ -156,6 +169,8 @@ func TestPostOnRedirectDifferentHost(t *testing.T) {
} }
func TestPostOnRedirectStatusFound(t *testing.T) { func TestPostOnRedirectStatusFound(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/ocs/v2.php/one", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/ocs/v2.php/two", http.StatusFound) http.Redirect(w, r, "/ocs/v2.php/two", http.StatusFound)
@ -206,3 +221,42 @@ func TestPostOnRedirectStatusFound(t *testing.T) {
t.Errorf("Expected empty response, got %+v", response) 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

@ -92,6 +92,7 @@ func testBackends(t *testing.T, config *BackendConfiguration, valid_urls [][]str
} }
func TestIsUrlAllowed_Compat(t *testing.T) { func TestIsUrlAllowed_Compat(t *testing.T) {
CatchLogForTest(t)
// Old-style configuration // Old-style configuration
valid_urls := []string{ valid_urls := []string{
"http://domain.invalid", "http://domain.invalid",
@ -114,6 +115,7 @@ func TestIsUrlAllowed_Compat(t *testing.T) {
} }
func TestIsUrlAllowed_CompatForceHttps(t *testing.T) { func TestIsUrlAllowed_CompatForceHttps(t *testing.T) {
CatchLogForTest(t)
// Old-style configuration, force HTTPS // Old-style configuration, force HTTPS
valid_urls := []string{ valid_urls := []string{
"https://domain.invalid", "https://domain.invalid",
@ -135,6 +137,7 @@ func TestIsUrlAllowed_CompatForceHttps(t *testing.T) {
} }
func TestIsUrlAllowed(t *testing.T) { func TestIsUrlAllowed(t *testing.T) {
CatchLogForTest(t)
valid_urls := [][]string{ valid_urls := [][]string{
{"https://domain.invalid/foo", string(testBackendSecret) + "-foo"}, {"https://domain.invalid/foo", string(testBackendSecret) + "-foo"},
{"https://domain.invalid/foo/", string(testBackendSecret) + "-foo"}, {"https://domain.invalid/foo/", string(testBackendSecret) + "-foo"},
@ -180,6 +183,7 @@ func TestIsUrlAllowed(t *testing.T) {
} }
func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) { func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) {
CatchLogForTest(t)
valid_urls := []string{} valid_urls := []string{}
invalid_urls := []string{ invalid_urls := []string{
"http://domain.invalid", "http://domain.invalid",
@ -197,6 +201,7 @@ func TestIsUrlAllowed_EmptyAllowlist(t *testing.T) {
} }
func TestIsUrlAllowed_AllowAll(t *testing.T) { func TestIsUrlAllowed_AllowAll(t *testing.T) {
CatchLogForTest(t)
valid_urls := []string{ valid_urls := []string{
"http://domain.invalid", "http://domain.invalid",
"https://domain.invalid", "https://domain.invalid",
@ -222,6 +227,7 @@ type ParseBackendIdsTestcase struct {
} }
func TestParseBackendIds(t *testing.T) { func TestParseBackendIds(t *testing.T) {
CatchLogForTest(t)
testcases := []ParseBackendIdsTestcase{ testcases := []ParseBackendIdsTestcase{
{"", nil}, {"", nil},
{"backend1", []string{"backend1"}}, {"backend1", []string{"backend1"}},
@ -241,6 +247,7 @@ func TestParseBackendIds(t *testing.T) {
} }
func TestBackendReloadNoChange(t *testing.T) { func TestBackendReloadNoChange(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent) current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile() original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2") original_config.AddOption("backend", "backends", "backend1, backend2")
@ -276,6 +283,7 @@ func TestBackendReloadNoChange(t *testing.T) {
} }
func TestBackendReloadChangeExistingURL(t *testing.T) { func TestBackendReloadChangeExistingURL(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent) current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile() original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2") original_config.AddOption("backend", "backends", "backend1, backend2")
@ -316,6 +324,7 @@ func TestBackendReloadChangeExistingURL(t *testing.T) {
} }
func TestBackendReloadChangeSecret(t *testing.T) { func TestBackendReloadChangeSecret(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent) current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile() original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2") original_config.AddOption("backend", "backends", "backend1, backend2")
@ -354,6 +363,7 @@ func TestBackendReloadChangeSecret(t *testing.T) {
} }
func TestBackendReloadAddBackend(t *testing.T) { func TestBackendReloadAddBackend(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent) current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile() original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1") original_config.AddOption("backend", "backends", "backend1")
@ -394,6 +404,7 @@ func TestBackendReloadAddBackend(t *testing.T) {
} }
func TestBackendReloadRemoveHost(t *testing.T) { func TestBackendReloadRemoveHost(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent) current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile() original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2") original_config.AddOption("backend", "backends", "backend1, backend2")
@ -431,6 +442,7 @@ func TestBackendReloadRemoveHost(t *testing.T) {
} }
func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) { func TestBackendReloadRemoveBackendFromSharedHost(t *testing.T) {
CatchLogForTest(t)
current := testutil.ToFloat64(statsBackendsCurrent) current := testutil.ToFloat64(statsBackendsCurrent)
original_config := goconf.NewConfigFile() original_config := goconf.NewConfigFile()
original_config.AddOption("backend", "backends", "backend1, backend2") original_config.AddOption("backend", "backends", "backend1, backend2")
@ -486,6 +498,8 @@ func mustParse(s string) *url.URL {
} }
func TestBackendConfiguration_Etcd(t *testing.T) { func TestBackendConfiguration_Etcd(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t) etcd, client := NewEtcdClientForTest(t)
url1 := "https://domain1.invalid/foo" url1 := "https://domain1.invalid/foo"
@ -505,8 +519,7 @@ func TestBackendConfiguration_Etcd(t *testing.T) {
defer cfg.Close() defer cfg.Close()
storage := cfg.storage.(*backendStorageEtcd) storage := cfg.storage.(*backendStorageEtcd)
ch := make(chan bool, 1) ch := storage.getWakeupChannelForTesting()
storage.SetWakeupForTesting(ch)
ctx, cancel := context.WithTimeout(context.Background(), testTimeout) ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel() defer cancel()
@ -618,3 +631,56 @@ func TestBackendConfiguration_Etcd(t *testing.T) {
t.Errorf("Should have removed host information for %s", "domain1.invalid") 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

@ -28,6 +28,7 @@ import (
"crypto/sha1" "crypto/sha1"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -35,8 +36,10 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
"regexp"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/dlintw/goconf" "github.com/dlintw/goconf"
@ -65,7 +68,7 @@ type BackendServer struct {
turnvalid time.Duration turnvalid time.Duration
turnservers []string turnservers []string
statsAllowedIps map[string]bool statsAllowedIps atomic.Pointer[AllowedIps]
invalidSecret []byte invalidSecret []byte
} }
@ -100,21 +103,16 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac
} }
statsAllowed, _ := config.GetString("stats", "allowed_ips") statsAllowed, _ := config.GetString("stats", "allowed_ips")
var statsAllowedIps map[string]bool statsAllowedIps, err := ParseAllowedIps(statsAllowed)
if statsAllowed == "" { if err != nil {
log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1") return nil, err
statsAllowedIps = map[string]bool{ }
"127.0.0.1": true,
} if !statsAllowedIps.Empty() {
log.Printf("Only allowing access to the stats endpoint from %s", statsAllowed)
} else { } else {
log.Printf("Only allowing access to the stats endpoing from %s", statsAllowed) log.Printf("No IPs configured for the stats endpoint, only allowing access from 127.0.0.1")
statsAllowedIps = make(map[string]bool) statsAllowedIps = DefaultAllowedIps()
for _, ip := range strings.Split(statsAllowed, ",") {
ip = strings.TrimSpace(ip)
if ip != "" {
statsAllowedIps[ip] = true
}
}
} }
invalidSecret := make([]byte, 32) invalidSecret := make([]byte, 32)
@ -122,7 +120,7 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac
return nil, err return nil, err
} }
return &BackendServer{ result := &BackendServer{
hub: hub, hub: hub,
events: hub.events, events: hub.events,
roomSessions: hub.roomSessions, roomSessions: hub.roomSessions,
@ -133,9 +131,27 @@ func NewBackendServer(config *goconf.ConfigFile, hub *Hub, version string) (*Bac
turnvalid: turnvalid, turnvalid: turnvalid,
turnservers: turnserverslist, turnservers: turnserverslist,
statsAllowedIps: statsAllowedIps, invalidSecret: invalidSecret,
invalidSecret: invalidSecret, }
}, nil
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 { func (b *BackendServer) Start(r *mux.Router) error {
@ -279,7 +295,7 @@ func (b *BackendServer) parseRequestBody(f func(http.ResponseWriter, *http.Reque
} }
} }
func (b *BackendServer) sendRoomInvite(roomid string, backend *Backend, userids []string, properties *json.RawMessage) { func (b *BackendServer) sendRoomInvite(roomid string, backend *Backend, userids []string, properties json.RawMessage) {
msg := &AsyncMessage{ msg := &AsyncMessage{
Type: "message", Type: "message",
Message: &ServerMessage{ Message: &ServerMessage{
@ -349,7 +365,7 @@ func (b *BackendServer) sendRoomDisinvite(roomid string, backend *Backend, reaso
wg.Wait() wg.Wait()
} }
func (b *BackendServer) sendRoomUpdate(roomid string, backend *Backend, notified_userids []string, all_userids []string, properties *json.RawMessage) { func (b *BackendServer) sendRoomUpdate(roomid string, backend *Backend, notified_userids []string, all_userids []string, properties json.RawMessage) {
msg := &AsyncMessage{ msg := &AsyncMessage{
Type: "message", Type: "message",
Message: &ServerMessage{ Message: &ServerMessage{
@ -546,7 +562,233 @@ func (b *BackendServer) sendRoomMessage(roomid string, backend *Backend, request
return b.events.PublishBackendRoomMessage(roomid, backend, message) 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) { 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) v := mux.Vars(r)
roomid := v["roomid"] roomid := v["roomid"]
@ -559,6 +801,7 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
if backend == nil { if backend == nil {
// Unknown backend URL passed, return immediately. // Unknown backend URL passed, return immediately.
throttle(r.Context())
http.Error(w, "Authentication check failed", http.StatusForbidden) http.Error(w, "Authentication check failed", http.StatusForbidden)
return return
} }
@ -580,12 +823,14 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
} }
if backend == nil { if backend == nil {
throttle(r.Context())
http.Error(w, "Authentication check failed", http.StatusForbidden) http.Error(w, "Authentication check failed", http.StatusForbidden)
return return
} }
} }
if !ValidateBackendChecksum(r, body, backend.Secret()) { if !ValidateBackendChecksum(r, body, backend.Secret()) {
throttle(r.Context())
http.Error(w, "Authentication check failed", http.StatusForbidden) http.Error(w, "Authentication check failed", http.StatusForbidden)
return return
} }
@ -599,7 +844,7 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
request.ReceivedTime = time.Now().UnixNano() request.ReceivedTime = time.Now().UnixNano()
var err error var response any
switch request.Type { switch request.Type {
case "invite": case "invite":
b.sendRoomInvite(roomid, backend, request.Invite.UserIds, request.Invite.Properties) b.sendRoomInvite(roomid, backend, request.Invite.UserIds, request.Invite.Properties)
@ -627,6 +872,10 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
err = b.sendRoomParticipantsUpdate(roomid, backend, &request) err = b.sendRoomParticipantsUpdate(roomid, backend, &request)
case "message": case "message":
err = b.sendRoomMessage(roomid, backend, &request) 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: default:
http.Error(w, "Unsupported request type: "+request.Type, http.StatusBadRequest) http.Error(w, "Unsupported request type: "+request.Type, http.StatusBadRequest)
return return
@ -638,22 +887,43 @@ func (b *BackendServer) roomHandler(w http.ResponseWriter, r *http.Request, body
return 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("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK) w.WriteHeader(responseStatus)
// TODO(jojo): Return better response struct. w.Write(responseData) // nolint
w.Write([]byte("{}")) // 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) { func (b *BackendServer) validateStatsRequest(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
addr := getRealUserIP(r) if !b.allowStatsAccess(r) {
if strings.Contains(addr, ":") {
if host, _, err := net.SplitHostPort(addr); err == nil {
addr = host
}
}
if !b.statsAllowedIps[addr] {
http.Error(w, "Authentication check failed", http.StatusForbidden) http.Error(w, "Authentication check failed", http.StatusForbidden)
return return
} }

View file

@ -30,8 +30,10 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/textproto"
"net/url" "net/url"
"reflect" "reflect"
"strings" "strings"
@ -78,11 +80,18 @@ func CreateBackendServerForTestFromConfig(t *testing.T, config *goconf.ConfigFil
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
config.AddOption("backend", "allowed", u.Host) if strings.Contains(t.Name(), "Compat") {
config.AddOption("backend", "allowed", u.Host)
config.AddOption("backend", "secret", string(testBackendSecret))
} else {
backendId := "backend1"
config.AddOption("backend", "backends", backendId)
config.AddOption(backendId, "url", server.URL)
config.AddOption(backendId, "secret", string(testBackendSecret))
}
if u.Scheme == "http" { if u.Scheme == "http" {
config.AddOption("backend", "allowhttp", "true") config.AddOption("backend", "allowhttp", "true")
} }
config.AddOption("backend", "secret", string(testBackendSecret))
config.AddOption("sessions", "hashkey", "12345678901234567890123456789012") config.AddOption("sessions", "hashkey", "12345678901234567890123456789012")
config.AddOption("sessions", "blockkey", "09876543210987654321098765432109") config.AddOption("sessions", "blockkey", "09876543210987654321098765432109")
config.AddOption("clients", "internalsecret", string(testInternalSecret)) config.AddOption("clients", "internalsecret", string(testInternalSecret))
@ -161,7 +170,7 @@ func CreateBackendServerWithClusteringForTestFromConfig(t *testing.T, config1 *g
t.Cleanup(func() { t.Cleanup(func() {
events1.Close() events1.Close()
}) })
client1 := NewGrpcClientsForTest(t, addr2) client1, _ := NewGrpcClientsForTest(t, addr2)
hub1, err := NewHub(config1, events1, grpcServer1, client1, nil, r1, "no-version") hub1, err := NewHub(config1, events1, grpcServer1, client1, nil, r1, "no-version")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -190,7 +199,7 @@ func CreateBackendServerWithClusteringForTestFromConfig(t *testing.T, config1 *g
t.Cleanup(func() { t.Cleanup(func() {
events2.Close() events2.Close()
}) })
client2 := NewGrpcClientsForTest(t, addr1) client2, _ := NewGrpcClientsForTest(t, addr1)
hub2, err := NewHub(config2, events2, grpcServer2, client2, nil, r2, "no-version") hub2, err := NewHub(config2, events2, grpcServer2, client2, nil, r2, "no-version")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -225,8 +234,8 @@ func CreateBackendServerWithClusteringForTestFromConfig(t *testing.T, config1 *g
return b1, b2, hub1, hub2, server1, server2 return b1, b2, hub1, hub2, server1, server2
} }
func performBackendRequest(url string, body []byte) (*http.Response, error) { func performBackendRequest(requestUrl string, body []byte) (*http.Response, error) {
request, err := http.NewRequest("POST", url, bytes.NewReader(body)) request, err := http.NewRequest("POST", requestUrl, bytes.NewReader(body))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -235,7 +244,11 @@ func performBackendRequest(url string, body []byte) (*http.Response, error) {
check := CalculateBackendChecksum(rnd, body, testBackendSecret) check := CalculateBackendChecksum(rnd, body, testBackendSecret)
request.Header.Set("Spreed-Signaling-Random", rnd) request.Header.Set("Spreed-Signaling-Random", rnd)
request.Header.Set("Spreed-Signaling-Checksum", check) request.Header.Set("Spreed-Signaling-Checksum", check)
request.Header.Set("Spreed-Signaling-Backend", url) u, err := url.Parse(requestUrl)
if err != nil {
return nil, err
}
request.Header.Set("Spreed-Signaling-Backend", u.Scheme+"://"+u.Host)
client := &http.Client{} client := &http.Client{}
return client.Do(request) return client.Do(request)
} }
@ -263,6 +276,8 @@ func expectRoomlistEvent(ch chan *AsyncMessage, msgType string) (*EventServerMes
} }
func TestBackendServer_NoAuth(t *testing.T) { func TestBackendServer_NoAuth(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, _, _, server := CreateBackendServerForTest(t) _, _, _, _, _, server := CreateBackendServerForTest(t)
roomId := "the-room-id" roomId := "the-room-id"
@ -289,6 +304,8 @@ func TestBackendServer_NoAuth(t *testing.T) {
} }
func TestBackendServer_InvalidAuth(t *testing.T) { func TestBackendServer_InvalidAuth(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, _, _, server := CreateBackendServerForTest(t) _, _, _, _, _, server := CreateBackendServerForTest(t)
roomId := "the-room-id" roomId := "the-room-id"
@ -317,6 +334,8 @@ func TestBackendServer_InvalidAuth(t *testing.T) {
} }
func TestBackendServer_OldCompatAuth(t *testing.T) { func TestBackendServer_OldCompatAuth(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, _, _, server := CreateBackendServerForTest(t) _, _, _, _, _, server := CreateBackendServerForTest(t)
roomId := "the-room-id" roomId := "the-room-id"
@ -331,7 +350,7 @@ func TestBackendServer_OldCompatAuth(t *testing.T) {
AllUserIds: []string{ AllUserIds: []string{
userid, userid,
}, },
Properties: &roomProperties, Properties: roomProperties,
}, },
} }
@ -366,6 +385,8 @@ func TestBackendServer_OldCompatAuth(t *testing.T) {
} }
func TestBackendServer_InvalidBody(t *testing.T) { func TestBackendServer_InvalidBody(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, _, _, server := CreateBackendServerForTest(t) _, _, _, _, _, server := CreateBackendServerForTest(t)
roomId := "the-room-id" roomId := "the-room-id"
@ -385,6 +406,8 @@ func TestBackendServer_InvalidBody(t *testing.T) {
} }
func TestBackendServer_UnsupportedRequest(t *testing.T) { func TestBackendServer_UnsupportedRequest(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, _, _, server := CreateBackendServerForTest(t) _, _, _, _, _, server := CreateBackendServerForTest(t)
msg := &BackendServerRoomRequest{ msg := &BackendServerRoomRequest{
@ -411,8 +434,10 @@ func TestBackendServer_UnsupportedRequest(t *testing.T) {
} }
func TestBackendServer_RoomInvite(t *testing.T) { func TestBackendServer_RoomInvite(t *testing.T) {
CatchLogForTest(t)
for _, backend := range eventBackendsForTest { for _, backend := range eventBackendsForTest {
t.Run(backend, func(t *testing.T) { t.Run(backend, func(t *testing.T) {
t.Parallel()
RunTestBackendServer_RoomInvite(t) RunTestBackendServer_RoomInvite(t)
}) })
} }
@ -456,7 +481,7 @@ func RunTestBackendServer_RoomInvite(t *testing.T) {
AllUserIds: []string{ AllUserIds: []string{
userid, userid,
}, },
Properties: &roomProperties, Properties: roomProperties,
}, },
} }
@ -485,14 +510,16 @@ func RunTestBackendServer_RoomInvite(t *testing.T) {
t.Errorf("Expected invite, got %+v", event) t.Errorf("Expected invite, got %+v", event)
} else if event.Invite.RoomId != roomId { } else if event.Invite.RoomId != roomId {
t.Errorf("Expected room %s, got %+v", roomId, event) t.Errorf("Expected room %s, got %+v", roomId, event)
} else if event.Invite.Properties == nil || !bytes.Equal(*event.Invite.Properties, roomProperties) { } else if !bytes.Equal(event.Invite.Properties, roomProperties) {
t.Errorf("Room properties don't match: expected %s, got %s", string(roomProperties), string(*event.Invite.Properties)) t.Errorf("Room properties don't match: expected %s, got %s", string(roomProperties), string(event.Invite.Properties))
} }
} }
func TestBackendServer_RoomDisinvite(t *testing.T) { func TestBackendServer_RoomDisinvite(t *testing.T) {
CatchLogForTest(t)
for _, backend := range eventBackendsForTest { for _, backend := range eventBackendsForTest {
t.Run(backend, func(t *testing.T) { t.Run(backend, func(t *testing.T) {
t.Parallel()
RunTestBackendServer_RoomDisinvite(t) RunTestBackendServer_RoomDisinvite(t)
}) })
} }
@ -556,7 +583,7 @@ func RunTestBackendServer_RoomDisinvite(t *testing.T) {
roomId + "-" + hello.Hello.SessionId, roomId + "-" + hello.Hello.SessionId,
}, },
AllUserIds: []string{}, AllUserIds: []string{},
Properties: &roomProperties, Properties: roomProperties,
}, },
} }
@ -584,8 +611,8 @@ func RunTestBackendServer_RoomDisinvite(t *testing.T) {
t.Errorf("Expected disinvite, got %+v", event) t.Errorf("Expected disinvite, got %+v", event)
} else if event.Disinvite.RoomId != roomId { } else if event.Disinvite.RoomId != roomId {
t.Errorf("Expected room %s, got %+v", roomId, event) t.Errorf("Expected room %s, got %+v", roomId, event)
} else if event.Disinvite.Properties != nil { } else if len(event.Disinvite.Properties) > 0 {
t.Errorf("Room properties should be omitted, got %s", string(*event.Disinvite.Properties)) t.Errorf("Room properties should be omitted, got %s", string(event.Disinvite.Properties))
} else if event.Disinvite.Reason != "disinvited" { } else if event.Disinvite.Reason != "disinvited" {
t.Errorf("Reason should be disinvited, got %s", event.Disinvite.Reason) t.Errorf("Reason should be disinvited, got %s", event.Disinvite.Reason)
} }
@ -604,6 +631,8 @@ func RunTestBackendServer_RoomDisinvite(t *testing.T) {
} }
func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) { func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t) _, _, _, hub, _, server := CreateBackendServerForTest(t)
client1 := NewTestClient(t, server, hub) client1 := NewTestClient(t, server, hub)
@ -625,7 +654,8 @@ func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err := client2.RunUntilHello(ctx); err != nil { hello2, err := client2.RunUntilHello(ctx)
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -634,16 +664,14 @@ func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) {
if _, err := client1.JoinRoom(ctx, roomId1); err != nil { if _, err := client1.JoinRoom(ctx, roomId1); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := client1.RunUntilJoined(ctx, hello1.Hello); err != nil {
t.Error(err)
}
roomId2 := "test-room2" roomId2 := "test-room2"
if _, err := client2.JoinRoom(ctx, roomId2); err != nil { if _, err := client2.JoinRoom(ctx, roomId2); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := client2.RunUntilJoined(ctx, hello2.Hello); err != nil {
// Ignore "join" events.
if err := client1.DrainMessages(ctx); err != nil {
t.Error(err)
}
if err := client2.DrainMessages(ctx); err != nil {
t.Error(err) t.Error(err)
} }
@ -701,6 +729,7 @@ func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) {
UserIds: []string{ UserIds: []string{
testDefaultUserId, testDefaultUserId,
}, },
Properties: testRoomProperties,
}, },
} }
@ -729,8 +758,10 @@ func TestBackendServer_RoomDisinviteDifferentRooms(t *testing.T) {
} }
func TestBackendServer_RoomUpdate(t *testing.T) { func TestBackendServer_RoomUpdate(t *testing.T) {
CatchLogForTest(t)
for _, backend := range eventBackendsForTest { for _, backend := range eventBackendsForTest {
t.Run(backend, func(t *testing.T) { t.Run(backend, func(t *testing.T) {
t.Parallel()
RunTestBackendServer_RoomUpdate(t) RunTestBackendServer_RoomUpdate(t)
}) })
} }
@ -750,7 +781,7 @@ func RunTestBackendServer_RoomUpdate(t *testing.T) {
if backend == nil { if backend == nil {
t.Fatalf("Did not find backend") t.Fatalf("Did not find backend")
} }
room, err := hub.createRoom(roomId, &emptyProperties, backend) room, err := hub.createRoom(roomId, emptyProperties, backend)
if err != nil { if err != nil {
t.Fatalf("Could not create room: %s", err) t.Fatalf("Could not create room: %s", err)
} }
@ -774,7 +805,7 @@ func RunTestBackendServer_RoomUpdate(t *testing.T) {
UserIds: []string{ UserIds: []string{
userid, userid,
}, },
Properties: &roomProperties, Properties: roomProperties,
}, },
} }
@ -802,8 +833,8 @@ func RunTestBackendServer_RoomUpdate(t *testing.T) {
t.Errorf("Expected update, got %+v", event) t.Errorf("Expected update, got %+v", event)
} else if event.Update.RoomId != roomId { } else if event.Update.RoomId != roomId {
t.Errorf("Expected room %s, got %+v", roomId, event) t.Errorf("Expected room %s, got %+v", roomId, event)
} else if event.Update.Properties == nil || !bytes.Equal(*event.Update.Properties, roomProperties) { } else if !bytes.Equal(event.Update.Properties, roomProperties) {
t.Errorf("Room properties don't match: expected %s, got %s", string(roomProperties), string(*event.Update.Properties)) t.Errorf("Room properties don't match: expected %s, got %s", string(roomProperties), string(event.Update.Properties))
} }
// TODO: Use event to wait for asynchronous messages. // TODO: Use event to wait for asynchronous messages.
@ -813,14 +844,16 @@ func RunTestBackendServer_RoomUpdate(t *testing.T) {
if room == nil { if room == nil {
t.Fatalf("Room %s does not exist", roomId) t.Fatalf("Room %s does not exist", roomId)
} }
if string(*room.Properties()) != string(roomProperties) { if string(room.Properties()) != string(roomProperties) {
t.Errorf("Expected properties %s for room %s, got %s", string(roomProperties), room.Id(), string(*room.Properties())) t.Errorf("Expected properties %s for room %s, got %s", string(roomProperties), room.Id(), string(room.Properties()))
} }
} }
func TestBackendServer_RoomDelete(t *testing.T) { func TestBackendServer_RoomDelete(t *testing.T) {
CatchLogForTest(t)
for _, backend := range eventBackendsForTest { for _, backend := range eventBackendsForTest {
t.Run(backend, func(t *testing.T) { t.Run(backend, func(t *testing.T) {
t.Parallel()
RunTestBackendServer_RoomDelete(t) RunTestBackendServer_RoomDelete(t)
}) })
} }
@ -840,7 +873,7 @@ func RunTestBackendServer_RoomDelete(t *testing.T) {
if backend == nil { if backend == nil {
t.Fatalf("Did not find backend") t.Fatalf("Did not find backend")
} }
if _, err := hub.createRoom(roomId, &emptyProperties, backend); err != nil { if _, err := hub.createRoom(roomId, emptyProperties, backend); err != nil {
t.Fatalf("Could not create room: %s", err) t.Fatalf("Could not create room: %s", err)
} }
@ -888,8 +921,8 @@ func RunTestBackendServer_RoomDelete(t *testing.T) {
t.Errorf("Expected disinvite, got %+v", event) t.Errorf("Expected disinvite, got %+v", event)
} else if event.Disinvite.RoomId != roomId { } else if event.Disinvite.RoomId != roomId {
t.Errorf("Expected room %s, got %+v", roomId, event) t.Errorf("Expected room %s, got %+v", roomId, event)
} else if event.Disinvite.Properties != nil { } else if len(event.Disinvite.Properties) > 0 {
t.Errorf("Room properties should be omitted, got %s", string(*event.Disinvite.Properties)) t.Errorf("Room properties should be omitted, got %s", string(event.Disinvite.Properties))
} else if event.Disinvite.Reason != "deleted" { } else if event.Disinvite.Reason != "deleted" {
t.Errorf("Reason should be deleted, got %s", event.Disinvite.Reason) t.Errorf("Reason should be deleted, got %s", event.Disinvite.Reason)
} }
@ -904,8 +937,10 @@ func RunTestBackendServer_RoomDelete(t *testing.T) {
} }
func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) { func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) {
CatchLogForTest(t)
for _, subtest := range clusteredTests { for _, subtest := range clusteredTests {
t.Run(subtest, func(t *testing.T) { t.Run(subtest, func(t *testing.T) {
t.Parallel()
var hub1 *Hub var hub1 *Hub
var hub2 *Hub var hub2 *Hub
var server1 *httptest.Server var server1 *httptest.Server
@ -1035,6 +1070,8 @@ func TestBackendServer_ParticipantsUpdatePermissions(t *testing.T) {
} }
func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) { func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t) _, _, _, hub, _, server := CreateBackendServerForTest(t)
client := NewTestClient(t, server, hub) client := NewTestClient(t, server, hub)
@ -1120,6 +1157,8 @@ func TestBackendServer_ParticipantsUpdateEmptyPermissions(t *testing.T) {
} }
func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) { func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t) _, _, _, hub, _, server := CreateBackendServerForTest(t)
client1 := NewTestClient(t, server, hub) client1 := NewTestClient(t, server, hub)
@ -1333,8 +1372,10 @@ func TestBackendServer_ParticipantsUpdateTimeout(t *testing.T) {
} }
func TestBackendServer_InCallAll(t *testing.T) { func TestBackendServer_InCallAll(t *testing.T) {
CatchLogForTest(t)
for _, subtest := range clusteredTests { for _, subtest := range clusteredTests {
t.Run(subtest, func(t *testing.T) { t.Run(subtest, func(t *testing.T) {
t.Parallel()
var hub1 *Hub var hub1 *Hub
var hub2 *Hub var hub2 *Hub
var server1 *httptest.Server var server1 *httptest.Server
@ -1459,8 +1500,8 @@ func TestBackendServer_InCallAll(t *testing.T) {
t.Error(err) t.Error(err)
} else if !in_call_1.All { } else if !in_call_1.All {
t.Errorf("All flag not set in message %+v", in_call_1) t.Errorf("All flag not set in message %+v", in_call_1)
} else if !bytes.Equal(*in_call_1.InCall, []byte("7")) { } else if !bytes.Equal(in_call_1.InCall, []byte("7")) {
t.Errorf("Expected inCall flag 7, got %s", string(*in_call_1.InCall)) t.Errorf("Expected inCall flag 7, got %s", string(in_call_1.InCall))
} }
if msg2_a, err := client2.RunUntilMessage(ctx); err != nil { if msg2_a, err := client2.RunUntilMessage(ctx); err != nil {
@ -1469,8 +1510,8 @@ func TestBackendServer_InCallAll(t *testing.T) {
t.Error(err) t.Error(err)
} else if !in_call_1.All { } else if !in_call_1.All {
t.Errorf("All flag not set in message %+v", in_call_1) t.Errorf("All flag not set in message %+v", in_call_1)
} else if !bytes.Equal(*in_call_1.InCall, []byte("7")) { } else if !bytes.Equal(in_call_1.InCall, []byte("7")) {
t.Errorf("Expected inCall flag 7, got %s", string(*in_call_1.InCall)) t.Errorf("Expected inCall flag 7, got %s", string(in_call_1.InCall))
} }
if !room1.IsSessionInCall(session1) { if !room1.IsSessionInCall(session1) {
@ -1540,8 +1581,8 @@ func TestBackendServer_InCallAll(t *testing.T) {
t.Error(err) t.Error(err)
} else if !in_call_1.All { } else if !in_call_1.All {
t.Errorf("All flag not set in message %+v", in_call_1) t.Errorf("All flag not set in message %+v", in_call_1)
} else if !bytes.Equal(*in_call_1.InCall, []byte("0")) { } else if !bytes.Equal(in_call_1.InCall, []byte("0")) {
t.Errorf("Expected inCall flag 0, got %s", string(*in_call_1.InCall)) t.Errorf("Expected inCall flag 0, got %s", string(in_call_1.InCall))
} }
if msg2_a, err := client2.RunUntilMessage(ctx); err != nil { if msg2_a, err := client2.RunUntilMessage(ctx); err != nil {
@ -1550,8 +1591,8 @@ func TestBackendServer_InCallAll(t *testing.T) {
t.Error(err) t.Error(err)
} else if !in_call_1.All { } else if !in_call_1.All {
t.Errorf("All flag not set in message %+v", in_call_1) t.Errorf("All flag not set in message %+v", in_call_1)
} else if !bytes.Equal(*in_call_1.InCall, []byte("0")) { } else if !bytes.Equal(in_call_1.InCall, []byte("0")) {
t.Errorf("Expected inCall flag 0, got %s", string(*in_call_1.InCall)) t.Errorf("Expected inCall flag 0, got %s", string(in_call_1.InCall))
} }
if room1.IsSessionInCall(session1) { if room1.IsSessionInCall(session1) {
@ -1583,6 +1624,8 @@ func TestBackendServer_InCallAll(t *testing.T) {
} }
func TestBackendServer_RoomMessage(t *testing.T) { func TestBackendServer_RoomMessage(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t) _, _, _, hub, _, server := CreateBackendServerForTest(t)
client := NewTestClient(t, server, hub) client := NewTestClient(t, server, hub)
@ -1616,7 +1659,7 @@ func TestBackendServer_RoomMessage(t *testing.T) {
msg := &BackendServerRoomRequest{ msg := &BackendServerRoomRequest{
Type: "message", Type: "message",
Message: &BackendRoomMessageRequest{ Message: &BackendRoomMessageRequest{
Data: &messageData, Data: messageData,
}, },
} }
@ -1642,12 +1685,14 @@ func TestBackendServer_RoomMessage(t *testing.T) {
t.Error(err) t.Error(err)
} else if message.RoomId != roomId { } else if message.RoomId != roomId {
t.Errorf("Expected message for room %s, got %s", roomId, message.RoomId) t.Errorf("Expected message for room %s, got %s", roomId, message.RoomId)
} else if !bytes.Equal(messageData, *message.Data) { } else if !bytes.Equal(messageData, message.Data) {
t.Errorf("Expected message data %s, got %s", string(messageData), string(*message.Data)) t.Errorf("Expected message data %s, got %s", string(messageData), string(message.Data))
} }
} }
func TestBackendServer_TurnCredentials(t *testing.T) { func TestBackendServer_TurnCredentials(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, _, _, server := CreateBackendServerForTestWithTurn(t) _, _, _, _, _, server := CreateBackendServerForTestWithTurn(t)
q := make(url.Values) q := make(url.Values)
@ -1689,3 +1734,497 @@ func TestBackendServer_TurnCredentials(t *testing.T) {
t.Errorf("Expected the list of servers as %s, got %s", turnServers, cred.URIs) t.Errorf("Expected the list of servers as %s, got %s", turnServers, cred.URIs)
} }
} }
func TestBackendServer_StatsAllowedIps(t *testing.T) {
CatchLogForTest(t)
config := goconf.NewConfigFile()
config.AddOption("app", "trustedproxies", "1.2.3.4")
config.AddOption("stats", "allowed_ips", "127.0.0.1, 192.168.0.1, 192.168.1.1/24")
_, backend, _, _, _, _ := CreateBackendServerForTestFromConfig(t, config)
allowed := []string{
"127.0.0.1",
"127.0.0.1:1234",
"192.168.0.1:1234",
"192.168.1.1:1234",
"192.168.1.100:1234",
}
notAllowed := []string{
"192.168.0.2:1234",
"10.1.2.3:1234",
}
for _, addr := range allowed {
addr := addr
t.Run(addr, func(t *testing.T) {
t.Parallel()
r1 := &http.Request{
RemoteAddr: addr,
}
if !backend.allowStatsAccess(r1) {
t.Errorf("should allow %s", addr)
}
if host, _, err := net.SplitHostPort(addr); err == nil {
addr = host
}
r2 := &http.Request{
RemoteAddr: "1.2.3.4:12345",
Header: http.Header{
textproto.CanonicalMIMEHeaderKey("x-real-ip"): []string{addr},
},
}
if !backend.allowStatsAccess(r2) {
t.Errorf("should allow %s", addr)
}
r3 := &http.Request{
RemoteAddr: "1.2.3.4:12345",
Header: http.Header{
textproto.CanonicalMIMEHeaderKey("x-forwarded-for"): []string{addr},
},
}
if !backend.allowStatsAccess(r3) {
t.Errorf("should allow %s", addr)
}
r4 := &http.Request{
RemoteAddr: "1.2.3.4:12345",
Header: http.Header{
textproto.CanonicalMIMEHeaderKey("x-forwarded-for"): []string{addr + ", 1.2.3.4:23456"},
},
}
if !backend.allowStatsAccess(r4) {
t.Errorf("should allow %s", addr)
}
})
}
for _, addr := range notAllowed {
addr := addr
t.Run(addr, func(t *testing.T) {
t.Parallel()
r := &http.Request{
RemoteAddr: addr,
}
if backend.allowStatsAccess(r) {
t.Errorf("should not allow %s", addr)
}
})
}
}
func Test_IsNumeric(t *testing.T) {
t.Parallel()
numeric := []string{
"0",
"1",
"12345",
}
nonNumeric := []string{
"",
" ",
" 0",
"0 ",
" 0 ",
"-1",
"1.2",
"1a",
"a1",
}
for _, s := range numeric {
if !isNumeric(s) {
t.Errorf("%s should be numeric", s)
}
}
for _, s := range nonNumeric {
if isNumeric(s) {
t.Errorf("%s should not be numeric", s)
}
}
}
func TestBackendServer_DialoutNoSipBridge(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t)
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
if err := client.SendHelloInternal(); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
_, err := client.RunUntilHello(ctx)
if err != nil {
t.Fatal(err)
}
roomId := "12345"
msg := &BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
data, err := json.Marshal(msg)
if err != nil {
t.Fatal(err)
}
res, err := performBackendRequest(server.URL+"/api/v1/room/"+roomId, data)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
if res.StatusCode != http.StatusNotFound {
t.Fatalf("Expected error %d, got %s: %s", http.StatusNotFound, res.Status, string(body))
}
var response BackendServerRoomResponse
if err := json.Unmarshal(body, &response); err != nil {
t.Fatal(err)
}
if response.Type != "dialout" || response.Dialout == nil {
t.Fatalf("expected type dialout, got %s", string(body))
}
if response.Dialout.Error == nil {
t.Fatalf("expected dialout error, got %s", string(body))
}
if expected := "no_client_available"; response.Dialout.Error.Code != expected {
t.Errorf("expected error code %s, got %s", expected, string(body))
}
}
func TestBackendServer_DialoutAccepted(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t)
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
if err := client.SendHelloInternalWithFeatures([]string{"start-dialout"}); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
_, err := client.RunUntilHello(ctx)
if err != nil {
t.Fatal(err)
}
roomId := "12345"
callId := "call-123"
stopped := make(chan struct{})
go func() {
defer close(stopped)
msg, err := client.RunUntilMessage(ctx)
if err != nil {
t.Error(err)
return
}
if msg.Type != "internal" || msg.Internal.Type != "dialout" {
t.Errorf("expected internal dialout message, got %+v", msg)
return
}
if msg.Internal.Dialout.RoomId != roomId {
t.Errorf("expected room id %s, got %+v", roomId, msg)
}
if url := server.URL + "/"; msg.Internal.Dialout.Backend != url {
t.Errorf("expected backend %s, got %+v", url, msg)
}
response := &ClientMessage{
Id: msg.Id,
Type: "internal",
Internal: &InternalClientMessage{
Type: "dialout",
Dialout: &DialoutInternalClientMessage{
Type: "status",
RoomId: msg.Internal.Dialout.RoomId,
Status: &DialoutStatusInternalClientMessage{
Status: "accepted",
CallId: callId,
},
},
},
}
if err := client.WriteJSON(response); err != nil {
t.Error(err)
}
}()
defer func() {
<-stopped
}()
msg := &BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
data, err := json.Marshal(msg)
if err != nil {
t.Fatal(err)
}
res, err := performBackendRequest(server.URL+"/api/v1/room/"+roomId, data)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
if res.StatusCode != http.StatusOK {
t.Fatalf("Expected error %d, got %s: %s", http.StatusOK, res.Status, string(body))
}
var response BackendServerRoomResponse
if err := json.Unmarshal(body, &response); err != nil {
t.Fatal(err)
}
if response.Type != "dialout" || response.Dialout == nil {
t.Fatalf("expected type dialout, got %s", string(body))
}
if response.Dialout.Error != nil {
t.Fatalf("expected dialout success, got %s", string(body))
}
if response.Dialout.CallId != callId {
t.Errorf("expected call id %s, got %s", callId, string(body))
}
}
func TestBackendServer_DialoutAcceptedCompat(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t)
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
if err := client.SendHelloInternalWithFeatures([]string{"start-dialout"}); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
_, err := client.RunUntilHello(ctx)
if err != nil {
t.Fatal(err)
}
roomId := "12345"
callId := "call-123"
stopped := make(chan struct{})
go func() {
defer close(stopped)
msg, err := client.RunUntilMessage(ctx)
if err != nil {
t.Error(err)
return
}
if msg.Type != "internal" || msg.Internal.Type != "dialout" {
t.Errorf("expected internal dialout message, got %+v", msg)
return
}
if msg.Internal.Dialout.RoomId != roomId {
t.Errorf("expected room id %s, got %+v", roomId, msg)
}
if url := server.URL + "/"; msg.Internal.Dialout.Backend != url {
t.Errorf("expected backend %s, got %+v", url, msg)
}
response := &ClientMessage{
Id: msg.Id,
Type: "internal",
Internal: &InternalClientMessage{
Type: "dialout",
Dialout: &DialoutInternalClientMessage{
Type: "status",
RoomId: msg.Internal.Dialout.RoomId,
Status: &DialoutStatusInternalClientMessage{
Status: "accepted",
CallId: callId,
},
},
},
}
if err := client.WriteJSON(response); err != nil {
t.Error(err)
}
}()
defer func() {
<-stopped
}()
msg := &BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
data, err := json.Marshal(msg)
if err != nil {
t.Fatal(err)
}
res, err := performBackendRequest(server.URL+"/api/v1/room/"+roomId, data)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
if res.StatusCode != http.StatusOK {
t.Fatalf("Expected error %d, got %s: %s", http.StatusOK, res.Status, string(body))
}
var response BackendServerRoomResponse
if err := json.Unmarshal(body, &response); err != nil {
t.Fatal(err)
}
if response.Type != "dialout" || response.Dialout == nil {
t.Fatalf("expected type dialout, got %s", string(body))
}
if response.Dialout.Error != nil {
t.Fatalf("expected dialout success, got %s", string(body))
}
if response.Dialout.CallId != callId {
t.Errorf("expected call id %s, got %s", callId, string(body))
}
}
func TestBackendServer_DialoutRejected(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
_, _, _, hub, _, server := CreateBackendServerForTest(t)
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
if err := client.SendHelloInternalWithFeatures([]string{"start-dialout"}); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
_, err := client.RunUntilHello(ctx)
if err != nil {
t.Fatal(err)
}
roomId := "12345"
errorCode := "error-code"
errorMessage := "rejected call"
stopped := make(chan struct{})
go func() {
defer close(stopped)
msg, err := client.RunUntilMessage(ctx)
if err != nil {
t.Error(err)
return
}
if msg.Type != "internal" || msg.Internal.Type != "dialout" {
t.Errorf("expected internal dialout message, got %+v", msg)
return
}
if msg.Internal.Dialout.RoomId != roomId {
t.Errorf("expected room id %s, got %+v", roomId, msg)
}
if url := server.URL + "/"; msg.Internal.Dialout.Backend != url {
t.Errorf("expected backend %s, got %+v", url, msg)
}
response := &ClientMessage{
Id: msg.Id,
Type: "internal",
Internal: &InternalClientMessage{
Type: "dialout",
Dialout: &DialoutInternalClientMessage{
Type: "error",
Error: NewError(errorCode, errorMessage),
},
},
}
if err := client.WriteJSON(response); err != nil {
t.Error(err)
}
}()
defer func() {
<-stopped
}()
msg := &BackendServerRoomRequest{
Type: "dialout",
Dialout: &BackendRoomDialoutRequest{
Number: "+1234567890",
},
}
data, err := json.Marshal(msg)
if err != nil {
t.Fatal(err)
}
res, err := performBackendRequest(server.URL+"/api/v1/room/"+roomId, data)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Error(err)
}
if res.StatusCode != http.StatusBadGateway {
t.Fatalf("Expected error %d, got %s: %s", http.StatusBadGateway, res.Status, string(body))
}
var response BackendServerRoomResponse
if err := json.Unmarshal(body, &response); err != nil {
t.Fatal(err)
}
if response.Type != "dialout" || response.Dialout == nil {
t.Fatalf("expected type dialout, got %s", string(body))
}
if response.Dialout.Error == nil {
t.Fatalf("expected dialout error, got %s", string(body))
}
if response.Dialout.Error.Code != errorCode {
t.Errorf("expected error code %s, got %s", errorCode, string(body))
}
if response.Dialout.Error.Message != errorMessage {
t.Errorf("expected error message %s, got %s", errorMessage, string(body))
}
}

View file

@ -24,10 +24,10 @@ package signaling
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"log" "log"
"net/url" "net/url"
"sync"
"time" "time"
"github.com/dlintw/goconf" "github.com/dlintw/goconf"
@ -43,8 +43,10 @@ type backendStorageEtcd struct {
initializedCtx context.Context initializedCtx context.Context
initializedFunc context.CancelFunc initializedFunc context.CancelFunc
initializedWg sync.WaitGroup wakeupChanForTesting chan struct{}
wakeupChanForTesting chan bool
closeCtx context.Context
closeFunc context.CancelFunc
} }
func NewBackendStorageEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient) (BackendStorage, error) { func NewBackendStorageEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient) (BackendStorage, error) {
@ -58,6 +60,7 @@ func NewBackendStorageEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient) (B
} }
initializedCtx, initializedFunc := context.WithCancel(context.Background()) initializedCtx, initializedFunc := context.WithCancel(context.Background())
closeCtx, closeFunc := context.WithCancel(context.Background())
result := &backendStorageEtcd{ result := &backendStorageEtcd{
backendStorageCommon: backendStorageCommon{ backendStorageCommon: backendStorageCommon{
backends: make(map[string][]*Backend), backends: make(map[string][]*Backend),
@ -68,6 +71,8 @@ func NewBackendStorageEtcd(config *goconf.ConfigFile, etcdClient *EtcdClient) (B
initializedCtx: initializedCtx, initializedCtx: initializedCtx,
initializedFunc: initializedFunc, initializedFunc: initializedFunc,
closeCtx: closeCtx,
closeFunc: closeFunc,
} }
etcdClient.AddListener(result) etcdClient.AddListener(result)
@ -83,75 +88,86 @@ func (s *backendStorageEtcd) WaitForInitialized(ctx context.Context) error {
} }
} }
func (s *backendStorageEtcd) SetWakeupForTesting(ch chan bool) {
s.mu.Lock()
defer s.mu.Unlock()
s.wakeupChanForTesting = ch
}
func (s *backendStorageEtcd) wakeupForTesting() { func (s *backendStorageEtcd) wakeupForTesting() {
if s.wakeupChanForTesting == nil { if s.wakeupChanForTesting == nil {
return return
} }
select { select {
case s.wakeupChanForTesting <- true: case s.wakeupChanForTesting <- struct{}{}:
default: default:
} }
} }
func (s *backendStorageEtcd) EtcdClientCreated(client *EtcdClient) { func (s *backendStorageEtcd) EtcdClientCreated(client *EtcdClient) {
s.initializedWg.Add(1)
go func() { go func() {
if err := client.Watch(context.Background(), s.keyPrefix, s, clientv3.WithPrefix()); err != nil { if err := client.WaitForConnection(s.closeCtx); err != nil {
log.Printf("Error processing watch for %s: %s", s.keyPrefix, err) if errors.Is(err, context.Canceled) {
return
}
panic(err)
} }
}()
go func() { backoff, err := NewExponentialBackoff(initialWaitDelay, maxWaitDelay)
client.WaitForConnection() if err != nil {
panic(err)
waitDelay := initialWaitDelay }
for { for s.closeCtx.Err() == nil {
response, err := s.getBackends(client, s.keyPrefix) response, err := s.getBackends(s.closeCtx, client, s.keyPrefix)
if err != nil { if err != nil {
if err == context.DeadlineExceeded { if errors.Is(err, context.Canceled) {
log.Printf("Timeout getting initial list of backends, retry in %s", waitDelay) return
} else if errors.Is(err, context.DeadlineExceeded) {
log.Printf("Timeout getting initial list of backends, retry in %s", backoff.NextWait())
} else { } else {
log.Printf("Could not get initial list of backends, retry in %s: %s", waitDelay, err) log.Printf("Could not get initial list of backends, retry in %s: %s", backoff.NextWait(), err)
} }
time.Sleep(waitDelay) backoff.Wait(s.closeCtx)
waitDelay = waitDelay * 2
if waitDelay > maxWaitDelay {
waitDelay = maxWaitDelay
}
continue continue
} }
for _, ev := range response.Kvs { for _, ev := range response.Kvs {
s.EtcdKeyUpdated(client, string(ev.Key), ev.Value) s.EtcdKeyUpdated(client, string(ev.Key), ev.Value, nil)
} }
s.initializedWg.Wait()
s.initializedFunc() 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 return
} }
}() }()
} }
func (s *backendStorageEtcd) EtcdWatchCreated(client *EtcdClient, key string) { func (s *backendStorageEtcd) EtcdWatchCreated(client *EtcdClient, key string) {
s.initializedWg.Done()
} }
func (s *backendStorageEtcd) getBackends(client *EtcdClient, keyPrefix string) (*clientv3.GetResponse, error) { func (s *backendStorageEtcd) getBackends(ctx context.Context, client *EtcdClient, keyPrefix string) (*clientv3.GetResponse, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel() defer cancel()
return client.Get(ctx, keyPrefix, clientv3.WithPrefix()) return client.Get(ctx, keyPrefix, clientv3.WithPrefix())
} }
func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data []byte) { func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data []byte, prevValue []byte) {
var info BackendInformationEtcd var info BackendInformationEtcd
if err := json.Unmarshal(data, &info); err != nil { if err := json.Unmarshal(data, &info); err != nil {
log.Printf("Could not decode backend information %s: %s", string(data), err) log.Printf("Could not decode backend information %s: %s", string(data), err)
@ -211,7 +227,7 @@ func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data
s.wakeupForTesting() s.wakeupForTesting()
} }
func (s *backendStorageEtcd) EtcdKeyDeleted(client *EtcdClient, key string) { func (s *backendStorageEtcd) EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -247,6 +263,7 @@ func (s *backendStorageEtcd) EtcdKeyDeleted(client *EtcdClient, key string) {
func (s *backendStorageEtcd) Close() { func (s *backendStorageEtcd) Close() {
s.etcdClient.RemoveListener(s) s.etcdClient.RemoveListener(s)
s.closeFunc()
} }
func (s *backendStorageEtcd) Reload(config *goconf.ConfigFile) { func (s *backendStorageEtcd) Reload(config *goconf.ConfigFile) {

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()
})
}

View file

@ -66,7 +66,7 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error)
} }
numBackends++ numBackends++
} else if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" { } else if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" {
for host, configuredBackends := range getConfiguredHosts(backendIds, config) { for host, configuredBackends := range getConfiguredHosts(backendIds, config, commonSecret) {
backends[host] = append(backends[host], configuredBackends...) backends[host] = append(backends[host], configuredBackends...)
for _, be := range configuredBackends { for _, be := range configuredBackends {
log.Printf("Backend %s added for %s", be.id, be.url) log.Printf("Backend %s added for %s", be.id, be.url)
@ -115,6 +115,10 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error)
} }
} }
if numBackends == 0 {
log.Printf("WARNING: No backends configured, client connections will not be possible.")
}
statsBackendsCurrent.Add(float64(numBackends)) statsBackendsCurrent.Add(float64(numBackends))
return &backendStorageStatic{ return &backendStorageStatic{
backendStorageCommon: backendStorageCommon{ backendStorageCommon: backendStorageCommon{
@ -192,7 +196,7 @@ func getConfiguredBackendIDs(backendIds string) (ids []string) {
return ids return ids
} }
func getConfiguredHosts(backendIds string, config *goconf.ConfigFile) (hosts map[string][]*Backend) { func getConfiguredHosts(backendIds string, config *goconf.ConfigFile, commonSecret string) (hosts map[string][]*Backend) {
hosts = make(map[string][]*Backend) hosts = make(map[string][]*Backend)
for _, id := range getConfiguredBackendIDs(backendIds) { for _, id := range getConfiguredBackendIDs(backendIds) {
u, _ := config.GetString(id, "url") u, _ := config.GetString(id, "url")
@ -216,6 +220,10 @@ func getConfiguredHosts(backendIds string, config *goconf.ConfigFile) (hosts map
} }
secret, _ := config.GetString(id, "secret") 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 == "" { if u == "" || secret == "" {
log.Printf("Backend %s is missing or incomplete, skipping", id) log.Printf("Backend %s is missing or incomplete, skipping", id)
continue continue
@ -265,8 +273,10 @@ func (s *backendStorageStatic) Reload(config *goconf.ConfigFile) {
return return
} }
commonSecret, _ := config.GetString("backend", "secret")
if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" { if backendIds, _ := config.GetString("backend", "backends"); backendIds != "" {
configuredHosts := getConfiguredHosts(backendIds, config) configuredHosts := getConfiguredHosts(backendIds, config, commonSecret)
// remove backends that are no longer configured // remove backends that are no longer configured
for hostname := range s.backends { for hostname := range s.backends {

View file

@ -28,6 +28,7 @@ import (
) )
func TestBackoff_Exponential(t *testing.T) { func TestBackoff_Exponential(t *testing.T) {
t.Parallel()
backoff, err := NewExponentialBackoff(100*time.Millisecond, 500*time.Millisecond) backoff, err := NewExponentialBackoff(100*time.Millisecond, 500*time.Millisecond)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -48,9 +48,6 @@ const (
maxInvalidateInterval = time.Minute maxInvalidateInterval = time.Minute
) )
// Can be overwritten by tests.
var getCapabilitiesNow = time.Now
type capabilitiesEntry struct { type capabilitiesEntry struct {
nextUpdate time.Time nextUpdate time.Time
capabilities map[string]interface{} capabilities map[string]interface{}
@ -59,6 +56,9 @@ type capabilitiesEntry struct {
type Capabilities struct { type Capabilities struct {
mu sync.RWMutex mu sync.RWMutex
// Can be overwritten by tests.
getNow func() time.Time
version string version string
pool *HttpClientPool pool *HttpClientPool
entries map[string]*capabilitiesEntry entries map[string]*capabilitiesEntry
@ -67,6 +67,8 @@ type Capabilities struct {
func NewCapabilities(version string, pool *HttpClientPool) (*Capabilities, error) { func NewCapabilities(version string, pool *HttpClientPool) (*Capabilities, error) {
result := &Capabilities{ result := &Capabilities{
getNow: time.Now,
version: version, version: version,
pool: pool, pool: pool,
entries: make(map[string]*capabilitiesEntry), entries: make(map[string]*capabilitiesEntry),
@ -86,15 +88,15 @@ type CapabilitiesVersion struct {
} }
type CapabilitiesResponse struct { type CapabilitiesResponse struct {
Version CapabilitiesVersion `json:"version"` Version CapabilitiesVersion `json:"version"`
Capabilities map[string]*json.RawMessage `json:"capabilities"` Capabilities map[string]json.RawMessage `json:"capabilities"`
} }
func (c *Capabilities) getCapabilities(key string) (map[string]interface{}, bool) { func (c *Capabilities) getCapabilities(key string) (map[string]interface{}, bool) {
c.mu.RLock() c.mu.RLock()
defer c.mu.RUnlock() defer c.mu.RUnlock()
now := getCapabilitiesNow() now := c.getNow()
if entry, found := c.entries[key]; found && entry.nextUpdate.After(now) { if entry, found := c.entries[key]; found && entry.nextUpdate.After(now) {
return entry.capabilities, true return entry.capabilities, true
} }
@ -103,14 +105,15 @@ func (c *Capabilities) getCapabilities(key string) (map[string]interface{}, bool
} }
func (c *Capabilities) setCapabilities(key string, capabilities map[string]interface{}) { func (c *Capabilities) setCapabilities(key string, capabilities map[string]interface{}) {
now := getCapabilitiesNow() c.mu.Lock()
defer c.mu.Unlock()
now := c.getNow()
entry := &capabilitiesEntry{ entry := &capabilitiesEntry{
nextUpdate: now.Add(CapabilitiesCacheDuration), nextUpdate: now.Add(CapabilitiesCacheDuration),
capabilities: capabilities, capabilities: capabilities,
} }
c.mu.Lock()
defer c.mu.Unlock()
c.entries[key] = entry c.entries[key] = entry
} }
@ -118,7 +121,7 @@ func (c *Capabilities) invalidateCapabilities(key string) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
now := getCapabilitiesNow() now := c.getNow()
if entry, found := c.nextInvalidate[key]; found && entry.After(now) { if entry, found := c.nextInvalidate[key]; found && entry.After(now) {
return return
} }
@ -188,25 +191,25 @@ func (c *Capabilities) loadCapabilities(ctx context.Context, u *url.URL) (map[st
if err := json.Unmarshal(body, &ocs); err != nil { if err := json.Unmarshal(body, &ocs); err != nil {
log.Printf("Could not decode OCS response %s from %s: %s", string(body), capUrl.String(), err) log.Printf("Could not decode OCS response %s from %s: %s", string(body), capUrl.String(), err)
return nil, false, err return nil, false, 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), u) log.Printf("Incomplete OCS response %s from %s", string(body), u)
return nil, false, fmt.Errorf("incomplete OCS response") return nil, false, fmt.Errorf("incomplete OCS response")
} }
var response CapabilitiesResponse var response CapabilitiesResponse
if err := json.Unmarshal(*ocs.Ocs.Data, &response); err != nil { 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) log.Printf("Could not decode OCS response body %s from %s: %s", string(ocs.Ocs.Data), capUrl.String(), err)
return nil, false, err return nil, false, err
} }
capaObj, found := response.Capabilities[AppNameSpreed] capaObj, found := response.Capabilities[AppNameSpreed]
if !found || capaObj == nil { if !found || len(capaObj) == 0 {
log.Printf("No capabilities received for app spreed from %s: %+v", capUrl.String(), response) log.Printf("No capabilities received for app spreed from %s: %+v", capUrl.String(), response)
return nil, false, nil return nil, false, nil
} }
var capa map[string]interface{} var capa map[string]interface{}
if err := json.Unmarshal(*capaObj, &capa); err != nil { if err := json.Unmarshal(capaObj, &capa); err != nil {
log.Printf("Unsupported capabilities received for app spreed from %s: %+v", capUrl.String(), response) log.Printf("Unsupported capabilities received for app spreed from %s: %+v", capUrl.String(), response)
return nil, false, nil return nil, false, nil
} }

View file

@ -80,9 +80,9 @@ func NewCapabilitiesForTestWithCallback(t *testing.T, callback func(*Capabilitie
Version: CapabilitiesVersion{ Version: CapabilitiesVersion{
Major: 20, Major: 20,
}, },
Capabilities: map[string]*json.RawMessage{ Capabilities: map[string]json.RawMessage{
"anotherApp": (*json.RawMessage)(&emptyArray), "anotherApp": emptyArray,
"spreed": (*json.RawMessage)(&spreedCapa), "spreed": spreedCapa,
}, },
} }
@ -102,7 +102,7 @@ func NewCapabilitiesForTestWithCallback(t *testing.T, callback func(*Capabilitie
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
Message: http.StatusText(http.StatusOK), Message: http.StatusText(http.StatusOK),
}, },
Data: (*json.RawMessage)(&data), Data: data,
} }
if data, err = json.Marshal(ocs); err != nil { if data, err = json.Marshal(ocs); err != nil {
t.Fatal(err) t.Fatal(err)
@ -120,16 +120,25 @@ func NewCapabilitiesForTest(t *testing.T) (*url.URL, *Capabilities) {
return NewCapabilitiesForTestWithCallback(t, nil) return NewCapabilitiesForTestWithCallback(t, nil)
} }
func SetCapabilitiesGetNow(t *testing.T, f func() time.Time) { func SetCapabilitiesGetNow(t *testing.T, capabilities *Capabilities, f func() time.Time) {
old := getCapabilitiesNow capabilities.mu.Lock()
defer capabilities.mu.Unlock()
old := capabilities.getNow
t.Cleanup(func() { t.Cleanup(func() {
getCapabilitiesNow = old capabilities.mu.Lock()
defer capabilities.mu.Unlock()
capabilities.getNow = old
}) })
getCapabilitiesNow = f capabilities.getNow = f
} }
func TestCapabilities(t *testing.T) { func TestCapabilities(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
url, capabilities := NewCapabilitiesForTest(t) url, capabilities := NewCapabilitiesForTest(t)
ctx, cancel := context.WithTimeout(context.Background(), testTimeout) ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
@ -192,9 +201,11 @@ func TestCapabilities(t *testing.T) {
} }
func TestInvalidateCapabilities(t *testing.T) { func TestInvalidateCapabilities(t *testing.T) {
var called uint32 t.Parallel()
CatchLogForTest(t)
var called atomic.Uint32
url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse) { url, capabilities := NewCapabilitiesForTestWithCallback(t, func(cr *CapabilitiesResponse) {
atomic.AddUint32(&called, 1) called.Add(1)
}) })
ctx, cancel := context.WithTimeout(context.Background(), testTimeout) ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
@ -209,7 +220,7 @@ func TestInvalidateCapabilities(t *testing.T) {
t.Errorf("expected direct response") t.Errorf("expected direct response")
} }
if value := atomic.LoadUint32(&called); value != 1 { if value := called.Load(); value != 1 {
t.Errorf("expected called %d, got %d", 1, value) t.Errorf("expected called %d, got %d", 1, value)
} }
@ -224,7 +235,7 @@ func TestInvalidateCapabilities(t *testing.T) {
t.Errorf("expected direct response") t.Errorf("expected direct response")
} }
if value := atomic.LoadUint32(&called); value != 2 { if value := called.Load(); value != 2 {
t.Errorf("expected called %d, got %d", 2, value) t.Errorf("expected called %d, got %d", 2, value)
} }
@ -239,12 +250,12 @@ func TestInvalidateCapabilities(t *testing.T) {
t.Errorf("expected cached response") t.Errorf("expected cached response")
} }
if value := atomic.LoadUint32(&called); value != 2 { if value := called.Load(); value != 2 {
t.Errorf("expected called %d, got %d", 2, value) t.Errorf("expected called %d, got %d", 2, value)
} }
// At a later time, invalidating can be done again. // At a later time, invalidating can be done again.
SetCapabilitiesGetNow(t, func() time.Time { SetCapabilitiesGetNow(t, capabilities, func() time.Time {
return time.Now().Add(2 * time.Minute) return time.Now().Add(2 * time.Minute)
}) })
@ -258,7 +269,7 @@ func TestInvalidateCapabilities(t *testing.T) {
t.Errorf("expected direct response") t.Errorf("expected direct response")
} }
if value := atomic.LoadUint32(&called); value != 3 { if value := called.Load(); value != 3 {
t.Errorf("expected called %d, got %d", 3, value) t.Errorf("expected called %d, got %d", 3, value)
} }
} }

View file

@ -27,26 +27,19 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"sync" "sync/atomic"
"time"
)
var (
// CertificateCheckInterval defines the interval in which certificate files
// are checked for modifications.
CertificateCheckInterval = time.Minute
) )
type CertificateReloader struct { type CertificateReloader struct {
mu sync.Mutex certFile string
certWatcher *FileWatcher
certFile string keyFile string
keyFile string keyWatcher *FileWatcher
certificate *tls.Certificate certificate atomic.Pointer[tls.Certificate]
lastModified time.Time
nextCheck time.Time reloadCounter atomic.Uint64
} }
func NewCertificateReloader(certFile string, keyFile string) (*CertificateReloader, error) { func NewCertificateReloader(certFile string, keyFile string) (*CertificateReloader, error) {
@ -55,52 +48,43 @@ func NewCertificateReloader(certFile string, keyFile string) (*CertificateReload
return nil, fmt.Errorf("could not load certificate / key: %w", err) return nil, fmt.Errorf("could not load certificate / key: %w", err)
} }
stat, err := os.Stat(certFile) reloader := &CertificateReloader{
if err != nil {
return nil, fmt.Errorf("could not stat %s: %w", certFile, err)
}
return &CertificateReloader{
certFile: certFile, certFile: certFile,
keyFile: keyFile, 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
}
certificate: &pair, return reloader, nil
lastModified: stat.ModTime(), }
nextCheck: time.Now().Add(CertificateCheckInterval), func (r *CertificateReloader) Close() {
}, nil 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) { func (r *CertificateReloader) getCertificate() (*tls.Certificate, error) {
r.mu.Lock() return r.certificate.Load(), nil
defer r.mu.Unlock()
now := time.Now()
if now.Before(r.nextCheck) {
return r.certificate, nil
}
r.nextCheck = now.Add(CertificateCheckInterval)
stat, err := os.Stat(r.certFile)
if err != nil {
log.Printf("could not stat %s: %s", r.certFile, err)
return r.certificate, nil
}
if !stat.ModTime().Equal(r.lastModified) {
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, nil
}
r.certificate = &pair
r.lastModified = stat.ModTime()
}
return r.certificate, nil
} }
func (r *CertificateReloader) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { func (r *CertificateReloader) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
@ -111,15 +95,17 @@ func (r *CertificateReloader) GetClientCertificate(i *tls.CertificateRequestInfo
return r.getCertificate() return r.getCertificate()
} }
func (r *CertificateReloader) GetReloadCounter() uint64 {
return r.reloadCounter.Load()
}
type CertPoolReloader struct { type CertPoolReloader struct {
mu sync.Mutex certFile string
certWatcher *FileWatcher
certFile string pool atomic.Pointer[x509.CertPool]
pool *x509.CertPool reloadCounter atomic.Uint64
lastModified time.Time
nextCheck time.Time
} }
func loadCertPool(filename string) (*x509.CertPool, error) { func loadCertPool(filename string) (*x509.CertPool, error) {
@ -142,49 +128,38 @@ func NewCertPoolReloader(certFile string) (*CertPoolReloader, error) {
return nil, err return nil, err
} }
stat, err := os.Stat(certFile) reloader := &CertPoolReloader{
certFile: certFile,
}
reloader.pool.Store(pool)
reloader.certWatcher, err = NewFileWatcher(certFile, reloader.reload)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not stat %s: %w", certFile, err) return nil, err
} }
return &CertPoolReloader{ return reloader, nil
certFile: certFile, }
pool: pool, func (r *CertPoolReloader) Close() {
lastModified: stat.ModTime(), r.certWatcher.Close()
}
nextCheck: time.Now().Add(CertificateCheckInterval), func (r *CertPoolReloader) reload(filename string) {
}, nil 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 { func (r *CertPoolReloader) GetCertPool() *x509.CertPool {
r.mu.Lock() return r.pool.Load()
defer r.mu.Unlock() }
now := time.Now() func (r *CertPoolReloader) GetReloadCounter() uint64 {
if now.Before(r.nextCheck) { return r.reloadCounter.Load()
return r.pool
}
r.nextCheck = now.Add(CertificateCheckInterval)
stat, err := os.Stat(r.certFile)
if err != nil {
log.Printf("could not stat %s: %s", r.certFile, err)
return r.pool
}
if !stat.ModTime().Equal(r.lastModified) {
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
}
r.pool = pool
r.lastModified = stat.ModTime()
}
return r.pool
} }

View file

@ -22,15 +22,41 @@
package signaling package signaling
import ( import (
"context"
"testing" "testing"
"time" "time"
) )
func UpdateCertificateCheckIntervalForTest(t *testing.T, interval time.Duration) { func UpdateCertificateCheckIntervalForTest(t *testing.T, interval time.Duration) {
old := CertificateCheckInterval t.Helper()
// Make sure test is not executed with "t.Parallel()"
t.Setenv("PARALLEL_CHECK", "1")
old := deduplicateWatchEvents.Load()
t.Cleanup(func() { t.Cleanup(func() {
CertificateCheckInterval = old deduplicateWatchEvents.Store(old)
}) })
CertificateCheckInterval = interval 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 ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors"
"log" "log"
"net"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"unsafe"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/mailru/easyjson" "github.com/mailru/easyjson"
@ -93,30 +95,59 @@ type WritableClientMessage interface {
CloseAfterSend(session Session) bool 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 { type Client struct {
ctx context.Context
conn *websocket.Conn conn *websocket.Conn
addr string addr string
agent string agent string
closed uint32 closed atomic.Int32
country *string country *string
logRTT bool logRTT bool
session unsafe.Pointer handlerMu sync.RWMutex
handler ClientHandler
session atomic.Pointer[Session]
sessionId atomic.Pointer[string]
mu sync.Mutex mu sync.Mutex
closeChan chan bool closer *Closer
messagesDone sync.WaitGroup closeOnce sync.Once
messageChan chan *bytes.Buffer messagesDone chan struct{}
messageProcessing uint32 messageChan chan *bytes.Buffer
OnLookupCountry func(*Client) string
OnClosed func(*Client)
OnMessageReceived func(*Client, []byte)
OnRTTReceived func(*Client, time.Duration)
} }
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) remoteAddress = strings.TrimSpace(remoteAddress)
if remoteAddress == "" { if remoteAddress == "" {
remoteAddress = "unknown remote address" remoteAddress = "unknown remote address"
@ -125,47 +156,82 @@ func NewClient(conn *websocket.Conn, remoteAddress string, agent string) (*Clien
if agent == "" { if agent == "" {
agent = "unknown user agent" agent = "unknown user agent"
} }
client := &Client{ client := &Client{
conn: conn, ctx: ctx,
addr: remoteAddress,
agent: agent, agent: agent,
logRTT: true, 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 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.conn = conn
c.addr = remoteAddress c.addr = remoteAddress
c.closeChan = make(chan bool, 1) c.SetHandler(handler)
c.closer = NewCloser()
c.messageChan = make(chan *bytes.Buffer, 16) c.messageChan = make(chan *bytes.Buffer, 16)
c.OnLookupCountry = func(client *Client) string { return unknownCountry } c.messagesDone = make(chan struct{})
c.OnClosed = func(client *Client) {} }
c.OnMessageReceived = func(client *Client, data []byte) {}
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 { func (c *Client) IsConnected() bool {
return atomic.LoadUint32(&c.closed) == 0 return c.closed.Load() == 0
} }
func (c *Client) IsAuthenticated() bool { func (c *Client) IsAuthenticated() bool {
return c.GetSession() != nil return c.GetSession() != nil
} }
func (c *Client) GetSession() *ClientSession { func (c *Client) GetSession() Session {
return (*ClientSession)(atomic.LoadPointer(&c.session)) session := c.session.Load()
if session == nil {
return nil
}
return *session
} }
func (c *Client) SetSession(session *ClientSession) { func (c *Client) SetSession(session Session) {
atomic.StorePointer(&c.session, unsafe.Pointer(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 { func (c *Client) RemoteAddr() string {
@ -178,7 +244,12 @@ func (c *Client) UserAgent() string {
func (c *Client) Country() string { func (c *Client) Country() string {
if c.country == nil { 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 c.country = &country
} }
@ -186,38 +257,36 @@ func (c *Client) Country() string {
} }
func (c *Client) Close() { 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 return
} }
c.mu.Lock() c.closeOnce.Do(func() {
if c.conn != nil { c.doClose()
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()
} }
func (c *Client) doClose() { func (c *Client) doClose() {
c.closeChan <- true closed := c.closed.Add(1)
c.messagesDone.Wait() 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.getHandler().OnClosed(c)
c.SetSession(nil) c.SetSession(nil)
c.mu.Lock()
if c.conn != nil {
c.conn.Close()
c.conn = nil
} }
c.mu.Unlock()
} }
func (c *Client) SendError(e *Error) bool { 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 { func (c *Client) SendByeResponseWithReason(message *ClientMessage, reason string) bool {
response := &ServerMessage{ response := &ServerMessage{
Type: "bye", Type: "bye",
Bye: &ByeServerMessage{},
} }
if message != nil { if message != nil {
response.Id = message.Id response.Id = message.Id
} }
if reason != "" { if reason != "" {
if response.Bye == nil {
response.Bye = &ByeServerMessage{}
}
response.Bye.Reason = reason response.Bye.Reason = reason
} }
return c.SendMessage(response) return c.SendMessage(response)
@ -252,10 +323,12 @@ func (c *Client) SendMessage(message WritableClientMessage) bool {
func (c *Client) ReadPump() { func (c *Client) ReadPump() {
defer func() { defer func() {
c.Close()
close(c.messageChan) close(c.messageChan)
c.Close()
}() }()
go c.processMessages()
addr := c.RemoteAddr() addr := c.RemoteAddr()
c.mu.Lock() c.mu.Lock()
conn := c.conn conn := c.conn
@ -276,29 +349,30 @@ func (c *Client) ReadPump() {
rtt := now.Sub(time.Unix(0, ts)) rtt := now.Sub(time.Unix(0, ts))
if c.logRTT { if c.logRTT {
rtt_ms := rtt.Nanoseconds() / time.Millisecond.Nanoseconds() rtt_ms := rtt.Nanoseconds() / time.Millisecond.Nanoseconds()
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Client %s has RTT of %d ms (%s)", session.PublicId(), rtt_ms, rtt) log.Printf("Client %s has RTT of %d ms (%s)", sessionId, rtt_ms, rtt)
} else { } else {
log.Printf("Client from %s has RTT of %d ms (%s)", addr, rtt_ms, rtt) 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 return nil
}) })
go c.processMessages()
for { for {
conn.SetReadDeadline(time.Now().Add(pongWait)) // nolint conn.SetReadDeadline(time.Now().Add(pongWait)) // nolint
messageType, reader, err := conn.NextReader() messageType, reader, err := conn.NextReader()
if err != nil { 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.CloseNormalClosure,
websocket.CloseGoingAway, websocket.CloseGoingAway,
websocket.CloseNoStatusReceived) { websocket.CloseNoStatusReceived) {
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Error reading from client %s: %v", session.PublicId(), err) log.Printf("Error reading from client %s: %v", sessionId, err)
} else { } else {
log.Printf("Error reading from %s: %v", addr, err) log.Printf("Error reading from %s: %v", addr, err)
} }
@ -307,8 +381,8 @@ func (c *Client) ReadPump() {
} }
if messageType != websocket.TextMessage { if messageType != websocket.TextMessage {
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Unsupported message type %v from client %s", messageType, session.PublicId()) log.Printf("Unsupported message type %v from client %s", messageType, sessionId)
} else { } else {
log.Printf("Unsupported message type %v from %s", messageType, addr) log.Printf("Unsupported message type %v from %s", messageType, addr)
} }
@ -320,8 +394,8 @@ func (c *Client) ReadPump() {
decodeBuffer.Reset() decodeBuffer.Reset()
if _, err := decodeBuffer.ReadFrom(reader); err != nil { if _, err := decodeBuffer.ReadFrom(reader); err != nil {
bufferPool.Put(decodeBuffer) bufferPool.Put(decodeBuffer)
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Error reading message from client %s: %v", session.PublicId(), err) log.Printf("Error reading message from client %s: %v", sessionId, err)
} else { } else {
log.Printf("Error reading message from %s: %v", addr, err) 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. // Stop processing if the client was closed.
if atomic.LoadUint32(&c.closed) != 0 { if !c.IsConnected() {
bufferPool.Put(decodeBuffer) bufferPool.Put(decodeBuffer)
break break
} }
c.messagesDone.Add(1)
c.messageChan <- decodeBuffer c.messageChan <- decodeBuffer
} }
} }
@ -346,16 +419,12 @@ func (c *Client) processMessages() {
break break
} }
atomic.StoreUint32(&c.messageProcessing, 1) c.getHandler().OnMessageReceived(c, buffer.Bytes())
c.OnMessageReceived(c, buffer.Bytes())
atomic.StoreUint32(&c.messageProcessing, 0)
c.messagesDone.Done()
bufferPool.Put(buffer) bufferPool.Put(buffer)
} }
if atomic.LoadUint32(&c.closed) == 2 { close(c.messagesDone)
c.doClose() c.doClose()
}
} }
func (c *Client) writeInternal(message json.Marshaler) bool { func (c *Client) writeInternal(message json.Marshaler) bool {
@ -379,8 +448,8 @@ func (c *Client) writeInternal(message json.Marshaler) bool {
return false return false
} }
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send message %+v to client %s: %v", message, session.PublicId(), err) log.Printf("Could not send message %+v to client %s: %v", message, sessionId, err)
} else { } else {
log.Printf("Could not send message %+v to %s: %v", message, c.RemoteAddr(), err) 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: close:
c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint
if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil { if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil {
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send close message to client %s: %v", session.PublicId(), err) log.Printf("Could not send close message to client %s: %v", sessionId, err)
} else { } else {
log.Printf("Could not send close message to %s: %v", c.RemoteAddr(), err) 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()) closeData := websocket.FormatCloseMessage(websocket.CloseInternalServerErr, e.Error())
c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint
if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil { if err := c.conn.WriteMessage(websocket.CloseMessage, closeData); err != nil {
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send close message to client %s: %v", session.PublicId(), err) log.Printf("Could not send close message to client %s: %v", sessionId, err)
} else { } else {
log.Printf("Could not send close message to %s: %v", c.RemoteAddr(), err) 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 session.Close()
} }
go c.Close() go c.Close()
return false
} }
return true return true
@ -468,8 +536,8 @@ func (c *Client) sendPing() bool {
msg := strconv.FormatInt(now, 10) msg := strconv.FormatInt(now, 10)
c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint c.conn.SetWriteDeadline(time.Now().Add(writeWait)) // nolint
if err := c.conn.WriteMessage(websocket.PingMessage, []byte(msg)); err != nil { if err := c.conn.WriteMessage(websocket.PingMessage, []byte(msg)); err != nil {
if session := c.GetSession(); session != nil { if sessionId := c.GetSessionId(); sessionId != "" {
log.Printf("Could not send ping to client %s: %v", session.PublicId(), err) log.Printf("Could not send ping to client %s: %v", sessionId, err)
} else { } else {
log.Printf("Could not send ping to %s: %v", c.RemoteAddr(), err) log.Printf("Could not send ping to %s: %v", c.RemoteAddr(), err)
} }
@ -493,7 +561,7 @@ func (c *Client) WritePump() {
if !c.sendPing() { if !c.sendPing() {
return return
} }
case <-c.closeChan: case <-c.closer.C:
return return
} }
} }

View file

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

View file

@ -31,35 +31,35 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"unsafe"
"github.com/pion/sdp/v3" "github.com/pion/sdp/v3"
) )
var ( var (
// Sessions expire 30 seconds after the connection closed.
sessionExpireDuration = 30 * time.Second
// Warn if a session has 32 or more pending messages. // Warn if a session has 32 or more pending messages.
warnPendingMessagesCount = 32 warnPendingMessagesCount = 32
PathToOcsSignalingBackend = "ocs/v2.php/apps/spreed/api/v1/signaling/backend" PathToOcsSignalingBackend = "ocs/v2.php/apps/spreed/api/v1/signaling/backend"
) )
type ClientSession struct { // ResponseHandlerFunc will return "true" has been fully processed.
roomJoinTime int64 type ResponseHandlerFunc func(message *ClientMessage) bool
type ClientSession struct {
hub *Hub hub *Hub
events AsyncEvents events AsyncEvents
privateId string privateId string
publicId string publicId string
data *SessionIdData data *SessionIdData
ctx context.Context
closeFunc context.CancelFunc
clientType string clientType string
features []string features []string
userId string userId string
userData *json.RawMessage userData json.RawMessage
inCall Flags
supportsPermissions bool supportsPermissions bool
permissions map[Permission]bool permissions map[Permission]bool
@ -67,18 +67,18 @@ type ClientSession struct {
backendUrl string backendUrl string
parsedBackendUrl *url.URL parsedBackendUrl *url.URL
expires time.Time
mu sync.Mutex mu sync.Mutex
client *Client client HandlerClient
room unsafe.Pointer room atomic.Pointer[Room]
roomSessionId string roomJoinTime atomic.Int64
publisherWaitersId uint64 roomSessionIdLock sync.RWMutex
publisherWaiters map[uint64]chan bool roomSessionId string
publishers map[string]McuPublisher publisherWaiters ChannelWaiters
publishers map[StreamType]McuPublisher
subscribers map[string]McuSubscriber subscribers map[string]McuSubscriber
pendingClientMessages []*ServerMessage pendingClientMessages []*ServerMessage
@ -86,15 +86,24 @@ type ClientSession struct {
hasPendingParticipantsUpdate bool hasPendingParticipantsUpdate bool
virtualSessions map[*VirtualSession]bool virtualSessions map[*VirtualSession]bool
seenJoinedLock sync.Mutex
seenJoinedEvents map[string]bool
responseHandlersLock sync.Mutex
responseHandlers map[string]ResponseHandlerFunc
} }
func NewClientSession(hub *Hub, privateId string, publicId string, data *SessionIdData, backend *Backend, hello *HelloClientMessage, auth *BackendClientAuthResponse) (*ClientSession, error) { func NewClientSession(hub *Hub, privateId string, publicId string, data *SessionIdData, backend *Backend, hello *HelloClientMessage, auth *BackendClientAuthResponse) (*ClientSession, error) {
ctx, closeFunc := context.WithCancel(context.Background())
s := &ClientSession{ s := &ClientSession{
hub: hub, hub: hub,
events: hub.events, events: hub.events,
privateId: privateId, privateId: privateId,
publicId: publicId, publicId: publicId,
data: data, data: data,
ctx: ctx,
closeFunc: closeFunc,
clientType: hello.Auth.Type, clientType: hello.Auth.Type,
features: hello.Features, features: hello.Features,
@ -106,6 +115,9 @@ func NewClientSession(hub *Hub, privateId string, publicId string, data *Session
if s.clientType == HelloClientTypeInternal { if s.clientType == HelloClientTypeInternal {
s.backendUrl = hello.Auth.internalParams.Backend s.backendUrl = hello.Auth.internalParams.Backend
s.parsedBackendUrl = hello.Auth.internalParams.parsedBackend s.parsedBackendUrl = hello.Auth.internalParams.parsedBackend
if !s.HasFeature(ClientFeatureInternalInCall) {
s.SetInCall(FlagInCall | FlagWithAudio)
}
} else { } else {
s.backendUrl = hello.Auth.Url s.backendUrl = hello.Auth.Url
s.parsedBackendUrl = hello.Auth.parsedUrl s.parsedBackendUrl = hello.Auth.parsedUrl
@ -135,6 +147,10 @@ func NewClientSession(hub *Hub, privateId string, publicId string, data *Session
return s, nil return s, nil
} }
func (s *ClientSession) Context() context.Context {
return s.ctx
}
func (s *ClientSession) PrivateId() string { func (s *ClientSession) PrivateId() string {
return s.privateId return s.privateId
} }
@ -144,6 +160,8 @@ func (s *ClientSession) PublicId() string {
} }
func (s *ClientSession) RoomSessionId() string { func (s *ClientSession) RoomSessionId() string {
s.roomSessionIdLock.RLock()
defer s.roomSessionIdLock.RUnlock()
return s.roomSessionId return s.roomSessionId
} }
@ -155,6 +173,19 @@ func (s *ClientSession) ClientType() string {
return s.clientType return s.clientType
} }
// GetInCall is only used for internal clients.
func (s *ClientSession) GetInCall() int {
return int(s.inCall.Get())
}
func (s *ClientSession) SetInCall(inCall int) bool {
if inCall < 0 {
inCall = 0
}
return s.inCall.Set(uint32(inCall))
}
func (s *ClientSession) GetFeatures() []string { func (s *ClientSession) GetFeatures() []string {
return s.features return s.features
} }
@ -284,40 +315,29 @@ func (s *ClientSession) UserId() string {
return userId return userId
} }
func (s *ClientSession) UserData() *json.RawMessage { func (s *ClientSession) UserData() json.RawMessage {
return s.userData return s.userData
} }
func (s *ClientSession) StartExpire() {
// The hub mutex must be held when calling this method.
s.expires = time.Now().Add(sessionExpireDuration)
s.hub.expiredSessions[s] = true
}
func (s *ClientSession) StopExpire() {
// The hub mutex must be held when calling this method.
delete(s.hub.expiredSessions, s)
}
func (s *ClientSession) IsExpired(now time.Time) bool {
return now.After(s.expires)
}
func (s *ClientSession) SetRoom(room *Room) { func (s *ClientSession) SetRoom(room *Room) {
atomic.StorePointer(&s.room, unsafe.Pointer(room)) s.room.Store(room)
if room != nil { if room != nil {
atomic.StoreInt64(&s.roomJoinTime, time.Now().UnixNano()) s.roomJoinTime.Store(time.Now().UnixNano())
} else { } else {
atomic.StoreInt64(&s.roomJoinTime, 0) s.roomJoinTime.Store(0)
} }
s.seenJoinedLock.Lock()
defer s.seenJoinedLock.Unlock()
s.seenJoinedEvents = nil
} }
func (s *ClientSession) GetRoom() *Room { func (s *ClientSession) GetRoom() *Room {
return (*Room)(atomic.LoadPointer(&s.room)) return s.room.Load()
} }
func (s *ClientSession) getRoomJoinTime() time.Time { func (s *ClientSession) getRoomJoinTime() time.Time {
t := atomic.LoadInt64(&s.roomJoinTime) t := s.roomJoinTime.Load()
if t == 0 { if t == 0 {
return time.Time{} return time.Time{}
} }
@ -327,8 +347,8 @@ func (s *ClientSession) getRoomJoinTime() time.Time {
func (s *ClientSession) releaseMcuObjects() { func (s *ClientSession) releaseMcuObjects() {
if len(s.publishers) > 0 { if len(s.publishers) > 0 {
go func(publishers map[string]McuPublisher) { go func(publishers map[StreamType]McuPublisher) {
ctx := context.TODO() ctx := context.Background()
for _, publisher := range publishers { for _, publisher := range publishers {
publisher.Close(ctx) publisher.Close(ctx)
} }
@ -337,7 +357,7 @@ func (s *ClientSession) releaseMcuObjects() {
} }
if len(s.subscribers) > 0 { if len(s.subscribers) > 0 {
go func(subscribers map[string]McuSubscriber) { go func(subscribers map[string]McuSubscriber) {
ctx := context.TODO() ctx := context.Background()
for _, subscriber := range subscribers { for _, subscriber := range subscribers {
subscriber.Close(ctx) subscriber.Close(ctx)
} }
@ -351,6 +371,7 @@ func (s *ClientSession) Close() {
} }
func (s *ClientSession) closeAndWait(wait bool) { func (s *ClientSession) closeAndWait(wait bool) {
s.closeFunc()
s.hub.removeSession(s) s.hub.removeSession(s)
s.mu.Lock() s.mu.Lock()
@ -383,9 +404,39 @@ func (s *ClientSession) SubscribeEvents() error {
return s.events.RegisterSessionListener(s.publicId, s.backend, s) return s.events.RegisterSessionListener(s.publicId, s.backend, s)
} }
func (s *ClientSession) UpdateRoomSessionId(roomSessionId string) error {
s.roomSessionIdLock.Lock()
defer s.roomSessionIdLock.Unlock()
if s.roomSessionId == roomSessionId {
return nil
}
if err := s.hub.roomSessions.SetRoomSession(s, roomSessionId); err != nil {
return err
}
if roomSessionId != "" {
if room := s.GetRoom(); room != nil {
log.Printf("Session %s updated room session id to %s in room %s", s.PublicId(), roomSessionId, room.Id())
} else {
log.Printf("Session %s updated room session id to %s in unknown room", s.PublicId(), roomSessionId)
}
} else {
if room := s.GetRoom(); room != nil {
log.Printf("Session %s cleared room session id in room %s", s.PublicId(), room.Id())
} else {
log.Printf("Session %s cleared room session id in unknown room", s.PublicId())
}
}
s.roomSessionId = roomSessionId
return nil
}
func (s *ClientSession) SubscribeRoomEvents(roomid string, roomSessionId string) error { func (s *ClientSession) SubscribeRoomEvents(roomid string, roomSessionId string) error {
s.mu.Lock() s.roomSessionIdLock.Lock()
defer s.mu.Unlock() defer s.roomSessionIdLock.Unlock()
if err := s.events.RegisterRoomListener(roomid, s.backend, s); err != nil { if err := s.events.RegisterRoomListener(roomid, s.backend, s); err != nil {
return err return err
@ -444,6 +495,9 @@ func (s *ClientSession) doUnsubscribeRoomEvents(notify bool) {
s.events.UnregisterRoomListener(room.Id(), s.Backend(), s) s.events.UnregisterRoomListener(room.Id(), s.Backend(), s)
} }
s.hub.roomSessions.DeleteRoomSession(s) s.hub.roomSessions.DeleteRoomSession(s)
s.roomSessionIdLock.Lock()
defer s.roomSessionIdLock.Unlock()
if notify && room != nil && s.roomSessionId != "" { if notify && room != nil && s.roomSessionId != "" {
// Notify // Notify
go func(sid string) { go func(sid string) {
@ -461,14 +515,14 @@ func (s *ClientSession) doUnsubscribeRoomEvents(notify bool) {
s.roomSessionId = "" s.roomSessionId = ""
} }
func (s *ClientSession) ClearClient(client *Client) { func (s *ClientSession) ClearClient(client HandlerClient) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
s.clearClientLocked(client) s.clearClientLocked(client)
} }
func (s *ClientSession) clearClientLocked(client *Client) { func (s *ClientSession) clearClientLocked(client HandlerClient) {
if s.client == nil { if s.client == nil {
return return
} else if client != nil && s.client != client { } else if client != nil && s.client != client {
@ -481,18 +535,18 @@ func (s *ClientSession) clearClientLocked(client *Client) {
prevClient.SetSession(nil) prevClient.SetSession(nil)
} }
func (s *ClientSession) GetClient() *Client { func (s *ClientSession) GetClient() HandlerClient {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.getClientUnlocked() return s.getClientUnlocked()
} }
func (s *ClientSession) getClientUnlocked() *Client { func (s *ClientSession) getClientUnlocked() HandlerClient {
return s.client return s.client
} }
func (s *ClientSession) SetClient(client *Client) *Client { func (s *ClientSession) SetClient(client HandlerClient) HandlerClient {
if client == nil { if client == nil {
panic("Use ClearClient to set the client to nil") panic("Use ClearClient to set the client to nil")
} }
@ -514,12 +568,12 @@ func (s *ClientSession) SetClient(client *Client) *Client {
return prev return prev
} }
func (s *ClientSession) sendOffer(client McuClient, sender string, streamType string, offer map[string]interface{}) { func (s *ClientSession) sendOffer(client McuClient, sender string, streamType StreamType, offer map[string]interface{}) {
offer_message := &AnswerOfferMessage{ offer_message := &AnswerOfferMessage{
To: s.PublicId(), To: s.PublicId(),
From: sender, From: sender,
Type: "offer", Type: "offer",
RoomType: streamType, RoomType: string(streamType),
Payload: offer, Payload: offer,
Sid: client.Sid(), Sid: client.Sid(),
} }
@ -535,19 +589,19 @@ func (s *ClientSession) sendOffer(client McuClient, sender string, streamType st
Type: "session", Type: "session",
SessionId: sender, SessionId: sender,
}, },
Data: (*json.RawMessage)(&offer_data), Data: offer_data,
}, },
} }
s.sendMessageUnlocked(response_message) s.sendMessageUnlocked(response_message)
} }
func (s *ClientSession) sendCandidate(client McuClient, sender string, streamType string, candidate interface{}) { func (s *ClientSession) sendCandidate(client McuClient, sender string, streamType StreamType, candidate interface{}) {
candidate_message := &AnswerOfferMessage{ candidate_message := &AnswerOfferMessage{
To: s.PublicId(), To: s.PublicId(),
From: sender, From: sender,
Type: "candidate", Type: "candidate",
RoomType: streamType, RoomType: string(streamType),
Payload: map[string]interface{}{ Payload: map[string]interface{}{
"candidate": candidate, "candidate": candidate,
}, },
@ -565,7 +619,7 @@ func (s *ClientSession) sendCandidate(client McuClient, sender string, streamTyp
Type: "session", Type: "session",
SessionId: sender, SessionId: sender,
}, },
Data: (*json.RawMessage)(&candidate_data), Data: candidate_data,
}, },
} }
@ -681,23 +735,6 @@ func (s *ClientSession) SubscriberClosed(subscriber McuSubscriber) {
} }
} }
type SdpError struct {
message string
}
func (e *SdpError) Error() string {
return e.message
}
type WrappedSdpError struct {
SdpError
err error
}
func (e *WrappedSdpError) Unwrap() error {
return e.err
}
type PermissionError struct { type PermissionError struct {
permission Permission permission Permission
} }
@ -710,23 +747,10 @@ func (e *PermissionError) Error() string {
return fmt.Sprintf("permission \"%s\" not found", e.permission) return fmt.Sprintf("permission \"%s\" not found", e.permission)
} }
func (s *ClientSession) isSdpAllowedToSendLocked(payload map[string]interface{}) (MediaType, error) { func (s *ClientSession) isSdpAllowedToSendLocked(sdp *sdp.SessionDescription) (MediaType, error) {
sdpValue, found := payload["sdp"] if sdp == nil {
if !found { // Should have already been checked when data was validated.
return 0, &SdpError{"payload does not contain a sdp"} return 0, ErrNoSdp
}
sdpText, ok := sdpValue.(string)
if !ok {
return 0, &SdpError{"payload does not contain a valid sdp"}
}
var sdp sdp.SessionDescription
if err := sdp.Unmarshal([]byte(sdpText)); err != nil {
return 0, &WrappedSdpError{
SdpError: SdpError{
message: fmt.Sprintf("could not parse sdp: %s", err),
},
err: err,
}
} }
var mediaTypes MediaType var mediaTypes MediaType
@ -764,8 +788,8 @@ func (s *ClientSession) IsAllowedToSend(data *MessageClientMessageData) error {
// Client is allowed to publish any media (audio / video). // Client is allowed to publish any media (audio / video).
return nil return nil
} else if data != nil && data.Type == "offer" { } else if data != nil && data.Type == "offer" {
// Parse SDP to check what user is trying to publish and check permissions accordingly. // Check what user is trying to publish and check permissions accordingly.
if _, err := s.isSdpAllowedToSendLocked(data.Payload); err != nil { if _, err := s.isSdpAllowedToSendLocked(data.offerSdp); err != nil {
return err return err
} }
@ -780,22 +804,22 @@ func (s *ClientSession) IsAllowedToSend(data *MessageClientMessageData) error {
} }
} }
func (s *ClientSession) CheckOfferType(streamType string, data *MessageClientMessageData) (MediaType, error) { func (s *ClientSession) CheckOfferType(streamType StreamType, data *MessageClientMessageData) (MediaType, error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.checkOfferTypeLocked(streamType, data) return s.checkOfferTypeLocked(streamType, data)
} }
func (s *ClientSession) checkOfferTypeLocked(streamType string, data *MessageClientMessageData) (MediaType, error) { func (s *ClientSession) checkOfferTypeLocked(streamType StreamType, data *MessageClientMessageData) (MediaType, error) {
if streamType == streamTypeScreen { if streamType == StreamTypeScreen {
if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) { if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) {
return 0, &PermissionError{PERMISSION_MAY_PUBLISH_SCREEN} return 0, &PermissionError{PERMISSION_MAY_PUBLISH_SCREEN}
} }
return MediaTypeScreen, nil return MediaTypeScreen, nil
} else if data != nil && data.Type == "offer" { } else if data != nil && data.Type == "offer" {
mediaTypes, err := s.isSdpAllowedToSendLocked(data.Payload) mediaTypes, err := s.isSdpAllowedToSendLocked(data.offerSdp)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -806,27 +830,7 @@ func (s *ClientSession) checkOfferTypeLocked(streamType string, data *MessageCli
return 0, nil return 0, nil
} }
func (s *ClientSession) wakeupPublisherWaiters() { func (s *ClientSession) GetOrCreatePublisher(ctx context.Context, mcu Mcu, streamType StreamType, data *MessageClientMessageData) (McuPublisher, error) {
for _, ch := range s.publisherWaiters {
ch <- true
}
}
func (s *ClientSession) addPublisherWaiter(ch chan bool) uint64 {
if s.publisherWaiters == nil {
s.publisherWaiters = make(map[uint64]chan bool)
}
id := s.publisherWaitersId + 1
s.publisherWaitersId = id
s.publisherWaiters[id] = ch
return id
}
func (s *ClientSession) removePublisherWaiter(id uint64) {
delete(s.publisherWaiters, id)
}
func (s *ClientSession) GetOrCreatePublisher(ctx context.Context, mcu Mcu, streamType string, data *MessageClientMessageData) (McuPublisher, error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -839,11 +843,12 @@ func (s *ClientSession) GetOrCreatePublisher(ctx context.Context, mcu Mcu, strea
if !found { if !found {
client := s.getClientUnlocked() client := s.getClientUnlocked()
s.mu.Unlock() s.mu.Unlock()
defer s.mu.Lock()
bitrate := data.Bitrate bitrate := data.Bitrate
if backend := s.Backend(); backend != nil { if backend := s.Backend(); backend != nil {
var maxBitrate int var maxBitrate int
if streamType == streamTypeScreen { if streamType == StreamTypeScreen {
maxBitrate = backend.maxScreenBitrate maxBitrate = backend.maxScreenBitrate
} else { } else {
maxBitrate = backend.maxStreamBitrate maxBitrate = backend.maxStreamBitrate
@ -856,17 +861,16 @@ func (s *ClientSession) GetOrCreatePublisher(ctx context.Context, mcu Mcu, strea
} }
var err error var err error
publisher, err = mcu.NewPublisher(ctx, s, s.PublicId(), data.Sid, streamType, bitrate, mediaTypes, client) publisher, err = mcu.NewPublisher(ctx, s, s.PublicId(), data.Sid, streamType, bitrate, mediaTypes, client)
s.mu.Lock()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if s.publishers == nil { if s.publishers == nil {
s.publishers = make(map[string]McuPublisher) s.publishers = make(map[StreamType]McuPublisher)
} }
if prev, found := s.publishers[streamType]; found { if prev, found := s.publishers[streamType]; found {
// Another thread created the publisher while we were waiting. // Another thread created the publisher while we were waiting.
go func(pub McuPublisher) { go func(pub McuPublisher) {
closeCtx := context.TODO() closeCtx := context.Background()
pub.Close(closeCtx) pub.Close(closeCtx)
}(publisher) }(publisher)
publisher = prev publisher = prev
@ -874,7 +878,7 @@ func (s *ClientSession) GetOrCreatePublisher(ctx context.Context, mcu Mcu, strea
s.publishers[streamType] = publisher s.publishers[streamType] = publisher
} }
log.Printf("Publishing %s as %s for session %s", streamType, publisher.Id(), s.PublicId()) log.Printf("Publishing %s as %s for session %s", streamType, publisher.Id(), s.PublicId())
s.wakeupPublisherWaiters() s.publisherWaiters.Wakeup()
} else { } else {
publisher.SetMedia(mediaTypes) publisher.SetMedia(mediaTypes)
} }
@ -882,18 +886,18 @@ func (s *ClientSession) GetOrCreatePublisher(ctx context.Context, mcu Mcu, strea
return publisher, nil return publisher, nil
} }
func (s *ClientSession) getPublisherLocked(streamType string) McuPublisher { func (s *ClientSession) getPublisherLocked(streamType StreamType) McuPublisher {
return s.publishers[streamType] return s.publishers[streamType]
} }
func (s *ClientSession) GetPublisher(streamType string) McuPublisher { func (s *ClientSession) GetPublisher(streamType StreamType) McuPublisher {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.getPublisherLocked(streamType) return s.getPublisherLocked(streamType)
} }
func (s *ClientSession) GetOrWaitForPublisher(ctx context.Context, streamType string) McuPublisher { func (s *ClientSession) GetOrWaitForPublisher(ctx context.Context, streamType StreamType) McuPublisher {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -902,9 +906,9 @@ func (s *ClientSession) GetOrWaitForPublisher(ctx context.Context, streamType st
return publisher return publisher
} }
ch := make(chan bool, 1) ch := make(chan struct{}, 1)
id := s.addPublisherWaiter(ch) id := s.publisherWaiters.Add(ch)
defer s.removePublisherWaiter(id) defer s.publisherWaiters.Remove(id)
for { for {
s.mu.Unlock() s.mu.Unlock()
@ -922,17 +926,18 @@ func (s *ClientSession) GetOrWaitForPublisher(ctx context.Context, streamType st
} }
} }
func (s *ClientSession) GetOrCreateSubscriber(ctx context.Context, mcu Mcu, id string, streamType string) (McuSubscriber, error) { func (s *ClientSession) GetOrCreateSubscriber(ctx context.Context, mcu Mcu, id string, streamType StreamType) (McuSubscriber, error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
// TODO(jojo): Add method to remove subscribers. // TODO(jojo): Add method to remove subscribers.
subscriber, found := s.subscribers[id+"|"+streamType] subscriber, found := s.subscribers[getStreamId(id, streamType)]
if !found { if !found {
client := s.getClientUnlocked()
s.mu.Unlock() s.mu.Unlock()
var err error var err error
subscriber, err = mcu.NewSubscriber(ctx, s, id, streamType) subscriber, err = mcu.NewSubscriber(ctx, s, id, streamType, client)
s.mu.Lock() s.mu.Lock()
if err != nil { if err != nil {
return nil, err return nil, err
@ -940,15 +945,15 @@ func (s *ClientSession) GetOrCreateSubscriber(ctx context.Context, mcu Mcu, id s
if s.subscribers == nil { if s.subscribers == nil {
s.subscribers = make(map[string]McuSubscriber) s.subscribers = make(map[string]McuSubscriber)
} }
if prev, found := s.subscribers[id+"|"+streamType]; found { if prev, found := s.subscribers[getStreamId(id, streamType)]; found {
// Another thread created the subscriber while we were waiting. // Another thread created the subscriber while we were waiting.
go func(sub McuSubscriber) { go func(sub McuSubscriber) {
closeCtx := context.TODO() closeCtx := context.Background()
sub.Close(closeCtx) sub.Close(closeCtx)
}(subscriber) }(subscriber)
subscriber = prev subscriber = prev
} else { } else {
s.subscribers[id+"|"+streamType] = subscriber s.subscribers[getStreamId(id, streamType)] = subscriber
} }
log.Printf("Subscribing %s from %s as %s in session %s", streamType, id, subscriber.Id(), s.PublicId()) log.Printf("Subscribing %s from %s as %s in session %s", streamType, id, subscriber.Id(), s.PublicId())
} }
@ -956,11 +961,11 @@ func (s *ClientSession) GetOrCreateSubscriber(ctx context.Context, mcu Mcu, id s
return subscriber, nil return subscriber, nil
} }
func (s *ClientSession) GetSubscriber(id string, streamType string) McuSubscriber { func (s *ClientSession) GetSubscriber(id string, streamType StreamType) McuSubscriber {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.subscribers[id+"|"+streamType] return s.subscribers[getStreamId(id, streamType)]
} }
func (s *ClientSession) ProcessAsyncRoomMessage(message *AsyncMessage) { func (s *ClientSession) ProcessAsyncRoomMessage(message *AsyncMessage) {
@ -984,10 +989,10 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) {
defer s.mu.Unlock() defer s.mu.Unlock()
if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_MEDIA) { if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_MEDIA) {
if publisher, found := s.publishers[streamTypeVideo]; found { if publisher, found := s.publishers[StreamTypeVideo]; found {
if (publisher.HasMedia(MediaTypeAudio) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_AUDIO)) || if (publisher.HasMedia(MediaTypeAudio) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_AUDIO)) ||
(publisher.HasMedia(MediaTypeVideo) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_VIDEO)) { (publisher.HasMedia(MediaTypeVideo) && !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_VIDEO)) {
delete(s.publishers, streamTypeVideo) delete(s.publishers, StreamTypeVideo)
log.Printf("Session %s is no longer allowed to publish media, closing publisher %s", s.PublicId(), publisher.Id()) log.Printf("Session %s is no longer allowed to publish media, closing publisher %s", s.PublicId(), publisher.Id())
go func() { go func() {
publisher.Close(context.Background()) publisher.Close(context.Background())
@ -997,8 +1002,8 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) {
} }
} }
if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) { if !s.hasPermissionLocked(PERMISSION_MAY_PUBLISH_SCREEN) {
if publisher, found := s.publishers[streamTypeScreen]; found { if publisher, found := s.publishers[StreamTypeScreen]; found {
delete(s.publishers, streamTypeScreen) delete(s.publishers, StreamTypeScreen)
log.Printf("Session %s is no longer allowed to publish screen, closing publisher %s", s.PublicId(), publisher.Id()) log.Printf("Session %s is no longer allowed to publish screen, closing publisher %s", s.PublicId(), publisher.Id())
go func() { go func() {
publisher.Close(context.Background()) publisher.Close(context.Background())
@ -1010,20 +1015,17 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) {
return return
case "message": case "message":
if message.Message.Type == "bye" && message.Message.Bye.Reason == "room_session_reconnected" { if message.Message.Type == "bye" && message.Message.Bye.Reason == "room_session_reconnected" {
s.mu.Lock() log.Printf("Closing session %s because same room session %s connected", s.PublicId(), s.RoomSessionId())
roomSessionId := s.RoomSessionId()
s.mu.Unlock()
log.Printf("Closing session %s because same room session %s connected", s.PublicId(), roomSessionId)
s.LeaveRoom(false) s.LeaveRoom(false)
defer s.closeAndWait(false) defer s.closeAndWait(false)
} }
case "sendoffer": case "sendoffer":
// Process asynchronously to not block other messages received. // Process asynchronously to not block other messages received.
go func() { go func() {
ctx, cancel := context.WithTimeout(context.Background(), s.hub.mcuTimeout) ctx, cancel := context.WithTimeout(s.Context(), s.hub.mcuTimeout)
defer cancel() defer cancel()
mc, err := s.GetOrCreateSubscriber(ctx, s.hub.mcu, message.SendOffer.SessionId, message.SendOffer.Data.RoomType) mc, err := s.GetOrCreateSubscriber(ctx, s.hub.mcu, message.SendOffer.SessionId, StreamType(message.SendOffer.Data.RoomType))
if err != nil { if err != nil {
log.Printf("Could not create MCU subscriber for session %s to process sendoffer in %s: %s", message.SendOffer.SessionId, s.PublicId(), err) log.Printf("Could not create MCU subscriber for session %s to process sendoffer in %s: %s", message.SendOffer.SessionId, s.PublicId(), err)
if err := s.events.PublishSessionMessage(message.SendOffer.SessionId, s.backend, &AsyncMessage{ if err := s.events.PublishSessionMessage(message.SendOffer.SessionId, s.backend, &AsyncMessage{
@ -1052,7 +1054,7 @@ func (s *ClientSession) processAsyncMessage(message *AsyncMessage) {
return return
} }
mc.SendMessage(context.TODO(), nil, message.SendOffer.Data, func(err error, response map[string]interface{}) { mc.SendMessage(s.Context(), nil, message.SendOffer.Data, func(err error, response map[string]interface{}) {
if err != nil { if err != nil {
log.Printf("Could not send MCU message %+v for session %s to %s: %s", message.SendOffer.Data, message.SendOffer.SessionId, s.PublicId(), err) log.Printf("Could not send MCU message %+v for session %s to %s: %s", message.SendOffer.Data, message.SendOffer.SessionId, s.PublicId(), err)
if err := s.events.PublishSessionMessage(message.SendOffer.SessionId, s.backend, &AsyncMessage{ if err := s.events.PublishSessionMessage(message.SendOffer.SessionId, s.backend, &AsyncMessage{
@ -1110,13 +1112,13 @@ func (s *ClientSession) storePendingMessage(message *ServerMessage) {
func filterDisplayNames(events []*EventServerMessageSessionEntry) []*EventServerMessageSessionEntry { func filterDisplayNames(events []*EventServerMessageSessionEntry) []*EventServerMessageSessionEntry {
result := make([]*EventServerMessageSessionEntry, 0, len(events)) result := make([]*EventServerMessageSessionEntry, 0, len(events))
for _, event := range events { for _, event := range events {
if event.User == nil { if len(event.User) == 0 {
result = append(result, event) result = append(result, event)
continue continue
} }
var userdata map[string]interface{} var userdata map[string]interface{}
if err := json.Unmarshal(*event.User, &userdata); err != nil { if err := json.Unmarshal(event.User, &userdata); err != nil {
result = append(result, event) result = append(result, event)
continue continue
} }
@ -1142,7 +1144,30 @@ func filterDisplayNames(events []*EventServerMessageSessionEntry) []*EventServer
} }
e := event.Clone() e := event.Clone()
e.User = (*json.RawMessage)(&data) e.User = data
result = append(result, e)
}
return result
}
func (s *ClientSession) filterDuplicateJoin(entries []*EventServerMessageSessionEntry) []*EventServerMessageSessionEntry {
s.seenJoinedLock.Lock()
defer s.seenJoinedLock.Unlock()
// 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.
result := make([]*EventServerMessageSessionEntry, 0, len(entries))
for _, e := range entries {
if s.seenJoinedEvents[e.SessionId] {
log.Printf("Session %s got duplicate joined event for %s, ignoring", s.publicId, e.SessionId)
continue
}
if s.seenJoinedEvents == nil {
s.seenJoinedEvents = make(map[string]bool)
}
s.seenJoinedEvents[e.SessionId] = true
result = append(result, e) result = append(result, e)
} }
return result return result
@ -1172,25 +1197,54 @@ func (s *ClientSession) filterMessage(message *ServerMessage) *ServerMessage {
case "room": case "room":
switch message.Event.Type { switch message.Event.Type {
case "join": case "join":
if s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) { join := s.filterDuplicateJoin(message.Event.Join)
if len(join) == 0 {
return nil
}
copied := false
if len(join) != len(message.Event.Join) {
// Create unique copy of message for only this client. // Create unique copy of message for only this client.
copied = true
message = &ServerMessage{ message = &ServerMessage{
Id: message.Id, Id: message.Id,
Type: message.Type, Type: message.Type,
Event: &EventServerMessage{ Event: &EventServerMessage{
Type: message.Event.Type, Type: message.Event.Type,
Target: message.Event.Target, Target: message.Event.Target,
Join: filterDisplayNames(message.Event.Join), Join: join,
}, },
} }
} }
if s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) {
if copied {
message.Event.Join = filterDisplayNames(message.Event.Join)
} else {
message = &ServerMessage{
Id: message.Id,
Type: message.Type,
Event: &EventServerMessage{
Type: message.Event.Type,
Target: message.Event.Target,
Join: filterDisplayNames(message.Event.Join),
},
}
}
}
case "leave":
s.seenJoinedLock.Lock()
defer s.seenJoinedLock.Unlock()
for _, e := range message.Event.Leave {
delete(s.seenJoinedEvents, e)
}
case "message": case "message":
if message.Event.Message == nil || message.Event.Message.Data == nil || len(*message.Event.Message.Data) == 0 || !s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) { if message.Event.Message == nil || len(message.Event.Message.Data) == 0 || !s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) {
return message return message
} }
var data RoomEventMessageData var data RoomEventMessageData
if err := json.Unmarshal(*message.Event.Message.Data, &data); err != nil { if err := json.Unmarshal(message.Event.Message.Data, &data); err != nil {
return message return message
} }
@ -1207,7 +1261,7 @@ func (s *ClientSession) filterMessage(message *ServerMessage) *ServerMessage {
Target: message.Event.Target, Target: message.Event.Target,
Message: &RoomEventMessage{ Message: &RoomEventMessage{
RoomId: message.Event.Message.RoomId, RoomId: message.Event.Message.RoomId,
Data: (*json.RawMessage)(&encoded), Data: encoded,
}, },
}, },
} }
@ -1217,9 +1271,9 @@ func (s *ClientSession) filterMessage(message *ServerMessage) *ServerMessage {
} }
} }
case "message": case "message":
if message.Message != nil && message.Message.Data != nil && len(*message.Message.Data) > 0 && s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) { if message.Message != nil && len(message.Message.Data) > 0 && s.HasPermission(PERMISSION_HIDE_DISPLAYNAMES) {
var data MessageServerMessageData var data MessageServerMessageData
if err := json.Unmarshal(*message.Message.Data, &data); err != nil { if err := json.Unmarshal(message.Message.Data, &data); err != nil {
return message return message
} }
@ -1260,7 +1314,7 @@ func (s *ClientSession) filterAsyncMessage(msg *AsyncMessage) *ServerMessage {
// Can happen mostly during tests where an older room async message // Can happen mostly during tests where an older room async message
// could be received by a subscriber that joined after it was sent. // could be received by a subscriber that joined after it was sent.
if joined := s.getRoomJoinTime(); joined.IsZero() || msg.SendTime.Before(joined) { if joined := s.getRoomJoinTime(); joined.IsZero() || msg.SendTime.Before(joined) {
log.Printf("Message %+v was sent before room was joined, ignoring", msg.Message) log.Printf("Message %+v was sent on %s before room was joined on %s, ignoring", msg.Message, msg.SendTime, joined)
return nil return nil
} }
} }
@ -1273,7 +1327,7 @@ func (s *ClientSession) filterAsyncMessage(msg *AsyncMessage) *ServerMessage {
} }
} }
func (s *ClientSession) NotifySessionResumed(client *Client) { func (s *ClientSession) NotifySessionResumed(client HandlerClient) {
s.mu.Lock() s.mu.Lock()
if len(s.pendingClientMessages) == 0 { if len(s.pendingClientMessages) == 0 {
s.mu.Unlock() s.mu.Unlock()
@ -1327,3 +1381,38 @@ func (s *ClientSession) GetVirtualSessions() []*VirtualSession {
} }
return result return result
} }
func (s *ClientSession) HandleResponse(id string, handler ResponseHandlerFunc) {
s.responseHandlersLock.Lock()
defer s.responseHandlersLock.Unlock()
if s.responseHandlers == nil {
s.responseHandlers = make(map[string]ResponseHandlerFunc)
}
s.responseHandlers[id] = handler
}
func (s *ClientSession) ClearResponseHandler(id string) {
s.responseHandlersLock.Lock()
defer s.responseHandlersLock.Unlock()
delete(s.responseHandlers, id)
}
func (s *ClientSession) ProcessResponse(message *ClientMessage) bool {
id := message.Id
if id == "" {
return false
}
s.responseHandlersLock.Lock()
cb, found := s.responseHandlers[id]
defer s.responseHandlersLock.Unlock()
if !found {
return false
}
return cb(message)
}

View file

@ -117,6 +117,7 @@ func Test_permissionsEqual(t *testing.T) {
for idx, test := range tests { for idx, test := range tests {
test := test test := test
t.Run(strconv.Itoa(idx), func(t *testing.T) { t.Run(strconv.Itoa(idx), func(t *testing.T) {
t.Parallel()
equal := permissionsEqual(test.a, test.b) equal := permissionsEqual(test.a, test.b)
if equal != test.equal { 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]) 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) { func TestBandwidth_Client(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
hub, _, _, server := CreateHubForTest(t) hub, _, _, server := CreateHubForTest(t)
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
mcu, err := NewTestMCU() mcu, err := NewTestMCU()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} else if err := mcu.Start(); err != nil { } else if err := mcu.Start(ctx); err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer mcu.Stop() defer mcu.Stop()
@ -145,9 +151,6 @@ func TestBandwidth_Client(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
hello, err := client.RunUntilHello(ctx) hello, err := client.RunUntilHello(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -198,6 +201,8 @@ func TestBandwidth_Client(t *testing.T) {
} }
func TestBandwidth_Backend(t *testing.T) { func TestBandwidth_Backend(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
hub, _, _, server := CreateHubWithMultipleBackendsForTest(t) hub, _, _, server := CreateHubWithMultipleBackendsForTest(t)
u, err := url.Parse(server.URL + "/one") u, err := url.Parse(server.URL + "/one")
@ -212,33 +217,33 @@ func TestBandwidth_Backend(t *testing.T) {
backend.maxScreenBitrate = 1000 backend.maxScreenBitrate = 1000
backend.maxStreamBitrate = 2000 backend.maxStreamBitrate = 2000
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
mcu, err := NewTestMCU() mcu, err := NewTestMCU()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} else if err := mcu.Start(); err != nil { } else if err := mcu.Start(ctx); err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer mcu.Stop() defer mcu.Stop()
hub.SetMcu(mcu) hub.SetMcu(mcu)
streamTypes := []string{ streamTypes := []StreamType{
streamTypeVideo, StreamTypeVideo,
streamTypeScreen, StreamTypeScreen,
} }
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
for _, streamType := range streamTypes { for _, streamType := range streamTypes {
t.Run(streamType, func(t *testing.T) { t.Run(string(streamType), func(t *testing.T) {
client := NewTestClient(t, server, hub) client := NewTestClient(t, server, hub)
defer client.CloseWithBye() defer client.CloseWithBye()
params := TestBackendClientAuthParams{ params := TestBackendClientAuthParams{
UserId: testDefaultUserId, UserId: testDefaultUserId,
} }
if err := client.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", params); err != nil { if err := client.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", nil, params); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -268,7 +273,7 @@ func TestBandwidth_Backend(t *testing.T) {
}, MessageClientMessageData{ }, MessageClientMessageData{
Type: "offer", Type: "offer",
Sid: "54321", Sid: "54321",
RoomType: streamType, RoomType: string(streamType),
Bitrate: bitrate, Bitrate: bitrate,
Payload: map[string]interface{}{ Payload: map[string]interface{}{
"sdp": MockSdpOfferAudioAndVideo, "sdp": MockSdpOfferAudioAndVideo,
@ -287,7 +292,7 @@ func TestBandwidth_Backend(t *testing.T) {
} }
var expectBitrate int var expectBitrate int
if streamType == streamTypeVideo { if streamType == StreamTypeVideo {
expectBitrate = backend.maxStreamBitrate expectBitrate = backend.maxStreamBitrate
} else { } else {
expectBitrate = backend.maxScreenBitrate 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 package signaling
// This file has been automatically generated, do not modify. // 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 ( var (
ContinentMap = map[string][]string{ ContinentMap = map[string][]string{

View file

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

View file

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

@ -37,7 +37,7 @@ RestrictRealtime=yes
RestrictSUIDSGID=yes RestrictSUIDSGID=yes
SystemCallArchitectures=native SystemCallArchitectures=native
SystemCallFilter=@system-service SystemCallFilter=@system-service
SystemCallFilter=~ @privileged @resources SystemCallFilter=~ @privileged
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

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: services:
spreedbackend: spreedbackend:
build: . build:
context: ..
dockerfile: docker/server/Dockerfile
platforms:
- "linux/amd64"
volumes: volumes:
- ./server.conf:/config/server.conf - ./server.conf:/config/server.conf
network_mode: host network_mode: host
@ -19,7 +23,7 @@ services:
network_mode: host network_mode: host
restart: unless-stopped restart: unless-stopped
janus: janus:
build: docker/janus build: janus
command: ["janus", "--full-trickle"] command: ["janus", "--full-trickle"]
network_mode: host network_mode: host
restart: unless-stopped restart: unless-stopped

View file

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

29
docker/proxy/Dockerfile Normal file
View file

@ -0,0 +1,29 @@
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 && \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} make proxy
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

29
docker/server/Dockerfile Normal file
View file

@ -0,0 +1,29 @@
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 && \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} make server
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

@ -48,3 +48,6 @@ The following metrics are available:
| `signaling_grpc_clients` | Gauge | 1.0.0 | The current number of GRPC clients | | | `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_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_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,6 +1,6 @@
jinja2==3.1.2 jinja2==3.1.4
markdown==3.3.7 markdown==3.6
mkdocs==1.3.1 mkdocs==1.6.0
readthedocs-sphinx-search==0.1.2 readthedocs-sphinx-search==0.3.2
sphinx==5.1.1 sphinx==7.3.7
sphinx_rtd_theme==1.0.0 sphinx_rtd_theme==2.0.0

View file

@ -141,6 +141,7 @@ Message format (Client -> Server):
"type": "hello", "type": "hello",
"hello": { "hello": {
"version": "the-protocol-version", "version": "the-protocol-version",
"features": ["optional", "list, "of", "client", "feature", "ids"],
"auth": { "auth": {
"url": "the-url-to-the-auth-backend", "url": "the-url-to-the-auth-backend",
"params": { "params": {
@ -307,6 +308,7 @@ Message format (Client -> Server):
"type": "hello", "type": "hello",
"hello": { "hello": {
"version": "the-protocol-version", "version": "the-protocol-version",
"features": ["optional", "list, "of", "client", "feature", "ids"],
"auth": { "auth": {
"type": "the-client-type", "type": "the-client-type",
...other attributes depending on the client type... ...other attributes depending on the client type...
@ -458,6 +460,26 @@ Message format (Server -> Client):
the current room or the properties of a room change. 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 ### Backend validation
Rooms are managed by the Nextcloud backend, so the signaling server has to Rooms are managed by the Nextcloud backend, so the signaling server has to
@ -771,6 +793,74 @@ Message format (Server -> Client, receive message)
- The `userid` is omitted if a message was sent by an anonymous user. - 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
Transient data can be used to share data in a room that is valid while sessions Transient data can be used to share data in a room that is valid while sessions
@ -795,14 +885,17 @@ Message format (Client -> Server):
"transient": { "transient": {
"type": "set", "type": "set",
"key": "sample-key", "key": "sample-key",
"value": "any-json-object" "value": "any-json-object",
"ttl": "optional-ttl"
} }
} }
- The `key` must be a string. - The `key` must be a string.
- The `value` can be of any type (i.e. string, number, array, object, etc.). - 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 - 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): Message format (Server -> Client):
@ -869,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 # Internal signaling server API
The signaling server provides an internal API that can be called from Nextcloud The signaling server provides an internal API that can be called from Nextcloud
@ -1030,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.

View file

@ -23,6 +23,7 @@ package signaling
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log" "log"
"strings" "strings"
@ -34,6 +35,8 @@ import (
"go.etcd.io/etcd/client/pkg/v3/srv" "go.etcd.io/etcd/client/pkg/v3/srv"
"go.etcd.io/etcd/client/pkg/v3/transport" "go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
) )
type EtcdClientListener interface { type EtcdClientListener interface {
@ -42,8 +45,8 @@ type EtcdClientListener interface {
type EtcdClientWatcher interface { type EtcdClientWatcher interface {
EtcdWatchCreated(client *EtcdClient, key string) EtcdWatchCreated(client *EtcdClient, key string)
EtcdKeyUpdated(client *EtcdClient, key string, value []byte) EtcdKeyUpdated(client *EtcdClient, key string, value []byte, prevValue []byte)
EtcdKeyDeleted(client *EtcdClient, key string) EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte)
} }
type EtcdClient struct { type EtcdClient struct {
@ -112,6 +115,17 @@ func (c *EtcdClient) load(config *goconf.ConfigFile, ignoreErrors bool) error {
DialTimeout: time.Second, 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") clientKey := c.getConfigStringWithFallback(config, "clientkey")
clientCert := c.getConfigStringWithFallback(config, "clientcert") clientCert := c.getConfigStringWithFallback(config, "clientcert")
caCert := c.getConfigStringWithFallback(config, "cacert") caCert := c.getConfigStringWithFallback(config, "cacert")
@ -176,8 +190,8 @@ func (c *EtcdClient) getEtcdClient() *clientv3.Client {
return client.(*clientv3.Client) return client.(*clientv3.Client)
} }
func (c *EtcdClient) syncClient() error { func (c *EtcdClient) syncClient(ctx context.Context) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel() defer cancel()
return c.getEtcdClient().Sync(ctx) return c.getEtcdClient().Sync(ctx)
@ -212,26 +226,32 @@ func (c *EtcdClient) RemoveListener(listener EtcdClientListener) {
delete(c.listeners, listener) delete(c.listeners, listener)
} }
func (c *EtcdClient) WaitForConnection() { func (c *EtcdClient) WaitForConnection(ctx context.Context) error {
waitDelay := initialWaitDelay backoff, err := NewExponentialBackoff(initialWaitDelay, maxWaitDelay)
if err != nil {
return err
}
for { for {
if err := c.syncClient(); err != nil { if err := ctx.Err(); err != nil {
if err == context.DeadlineExceeded { return err
log.Printf("Timeout waiting for etcd client to connect to the cluster, retry in %s", waitDelay) }
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 { } else {
log.Printf("Could not sync etcd client with the cluster, retry in %s: %s", waitDelay, err) log.Printf("Could not sync etcd client with the cluster, retry in %s: %s", backoff.NextWait(), err)
} }
time.Sleep(waitDelay) backoff.Wait(ctx)
waitDelay = waitDelay * 2
if waitDelay > maxWaitDelay {
waitDelay = maxWaitDelay
}
continue continue
} }
log.Printf("Client synced, using endpoints %+v", c.getEtcdClient().Endpoints()) log.Printf("Client synced, using endpoints %+v", c.getEtcdClient().Endpoints())
return return nil
} }
} }
@ -239,27 +259,37 @@ func (c *EtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOpt
return c.getEtcdClient().Get(ctx, key, opts...) return c.getEtcdClient().Get(ctx, key, opts...)
} }
func (c *EtcdClient) Watch(ctx context.Context, key string, watcher EtcdClientWatcher, opts ...clientv3.OpOption) error { 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", key) 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...) ch := c.getEtcdClient().Watch(clientv3.WithRequireLeader(ctx), key, opts...)
log.Printf("Watch created for %s", key) log.Printf("Watch created for %s", key)
watcher.EtcdWatchCreated(c, key) watcher.EtcdWatchCreated(c, key)
for response := range ch { for response := range ch {
if err := response.Err(); err != nil { if err := response.Err(); err != nil {
return err return nextRevision, err
} }
nextRevision = response.Header.Revision + 1
for _, ev := range response.Events { for _, ev := range response.Events {
switch ev.Type { switch ev.Type {
case clientv3.EventTypePut: case clientv3.EventTypePut:
watcher.EtcdKeyUpdated(c, string(ev.Kv.Key), ev.Kv.Value) 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: case clientv3.EventTypeDelete:
watcher.EtcdKeyDeleted(c, string(ev.Kv.Key)) var prevValue []byte
if ev.PrevKv != nil {
prevValue = ev.PrevKv.Value
}
watcher.EtcdKeyDeleted(c, string(ev.Kv.Key), prevValue)
default: default:
log.Printf("Unsupported watch event %s %q -> %q", ev.Type, ev.Kv.Key, ev.Kv.Value) log.Printf("Unsupported watch event %s %q -> %q", ev.Type, ev.Kv.Key, ev.Kv.Value)
} }
} }
} }
return nil return nextRevision, nil
} }

View file

@ -29,7 +29,6 @@ import (
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
"sync"
"syscall" "syscall"
"testing" "testing"
"time" "time"
@ -39,6 +38,8 @@ import (
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/embed" "go.etcd.io/etcd/server/v3/embed"
"go.etcd.io/etcd/server/v3/lease" "go.etcd.io/etcd/server/v3/lease"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
) )
var ( var (
@ -79,13 +80,17 @@ func NewEtcdForTest(t *testing.T) *embed.Etcd {
var etcd *embed.Etcd var etcd *embed.Etcd
for port := 50000; port < 50100; port++ { for port := 50000; port < 50100; port++ {
u.Host = net.JoinHostPort("localhost", strconv.Itoa(port)) u.Host = net.JoinHostPort("localhost", strconv.Itoa(port))
cfg.LCUrls = []url.URL{*u} cfg.ListenClientUrls = []url.URL{*u}
cfg.ACUrls = []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 := u
peerListener.Host = net.JoinHostPort("localhost", strconv.Itoa(port+1)) peerListener.Host = net.JoinHostPort("localhost", strconv.Itoa(port+2))
cfg.LPUrls = []url.URL{*peerListener} cfg.ListenPeerUrls = []url.URL{*peerListener}
cfg.APUrls = []url.URL{*peerListener} cfg.AdvertisePeerUrls = []url.URL{*peerListener}
cfg.InitialCluster = "default=" + peerListener.String() cfg.InitialCluster = "default=" + peerListener.String()
cfg.ZapLoggerBuilder = embed.NewZapLoggerBuilder(zaptest.NewLogger(t, zaptest.Level(zap.WarnLevel)))
etcd, err = embed.StartEtcd(cfg) etcd, err = embed.StartEtcd(cfg)
if isErrorAddressAlreadyInUse(err) { if isErrorAddressAlreadyInUse(err) {
continue continue
@ -100,6 +105,7 @@ func NewEtcdForTest(t *testing.T) *embed.Etcd {
t.Cleanup(func() { t.Cleanup(func() {
etcd.Close() etcd.Close()
<-etcd.Server.StopNotify()
}) })
// Wait for server to be ready. // Wait for server to be ready.
<-etcd.Server.ReadyNotify() <-etcd.Server.ReadyNotify()
@ -111,7 +117,8 @@ func NewEtcdClientForTest(t *testing.T) (*embed.Etcd, *EtcdClient) {
etcd := NewEtcdForTest(t) etcd := NewEtcdForTest(t)
config := goconf.NewConfigFile() config := goconf.NewConfigFile()
config.AddOption("etcd", "endpoints", etcd.Config().LCUrls[0].String()) config.AddOption("etcd", "endpoints", etcd.Config().ListenClientUrls[0].String())
config.AddOption("etcd", "loglevel", "error")
client, err := NewEtcdClient(config, "") client, err := NewEtcdClient(config, "")
if err != nil { if err != nil {
@ -140,6 +147,8 @@ func DeleteEtcdValue(etcd *embed.Etcd, key string) {
} }
func Test_EtcdClient_Get(t *testing.T) { func Test_EtcdClient_Get(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t) etcd, client := NewEtcdClientForTest(t)
if response, err := client.Get(context.Background(), "foo"); err != nil { if response, err := client.Get(context.Background(), "foo"); err != nil {
@ -162,6 +171,8 @@ func Test_EtcdClient_Get(t *testing.T) {
} }
func Test_EtcdClient_GetPrefix(t *testing.T) { func Test_EtcdClient_GetPrefix(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t) etcd, client := NewEtcdClientForTest(t)
if response, err := client.Get(context.Background(), "foo"); err != nil { if response, err := client.Get(context.Background(), "foo"); err != nil {
@ -193,6 +204,8 @@ type etcdEvent struct {
t mvccpb.Event_EventType t mvccpb.Event_EventType
key string key string
value string value string
prevValue string
} }
type EtcdClientTestListener struct { type EtcdClientTestListener struct {
@ -201,9 +214,8 @@ type EtcdClientTestListener struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
initial chan bool initial chan struct{}
initialWg sync.WaitGroup events chan etcdEvent
events chan etcdEvent
} }
func NewEtcdClientTestListener(ctx context.Context, t *testing.T) *EtcdClientTestListener { func NewEtcdClientTestListener(ctx context.Context, t *testing.T) *EtcdClientTestListener {
@ -214,7 +226,7 @@ func NewEtcdClientTestListener(ctx context.Context, t *testing.T) *EtcdClientTes
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
initial: make(chan bool), initial: make(chan struct{}),
events: make(chan etcdEvent), events: make(chan etcdEvent),
} }
} }
@ -224,20 +236,17 @@ func (l *EtcdClientTestListener) Close() {
} }
func (l *EtcdClientTestListener) EtcdClientCreated(client *EtcdClient) { func (l *EtcdClientTestListener) EtcdClientCreated(client *EtcdClient) {
l.initialWg.Add(1)
go func() { go func() {
if err := client.Watch(clientv3.WithRequireLeader(l.ctx), "foo", l, clientv3.WithPrefix()); err != nil { if err := client.WaitForConnection(l.ctx); err != nil {
l.t.Error(err) l.t.Errorf("error waiting for connection: %s", err)
return
} }
}()
go func() {
client.WaitForConnection()
ctx, cancel := context.WithTimeout(l.ctx, time.Second) ctx, cancel := context.WithTimeout(l.ctx, time.Second)
defer cancel() defer cancel()
if response, err := client.Get(ctx, "foo", clientv3.WithPrefix()); err != nil { response, err := client.Get(ctx, "foo", clientv3.WithPrefix())
if err != nil {
l.t.Error(err) l.t.Error(err)
} else if response.Count != 1 { } else if response.Count != 1 {
l.t.Errorf("expected 1 responses, got %d", response.Count) l.t.Errorf("expected 1 responses, got %d", response.Count)
@ -246,31 +255,47 @@ func (l *EtcdClientTestListener) EtcdClientCreated(client *EtcdClient) {
} else if string(response.Kvs[0].Value) != "1" { } else if string(response.Kvs[0].Value) != "1" {
l.t.Errorf("expected value \"1\", got \"%s\"", string(response.Kvs[0].Value)) l.t.Errorf("expected value \"1\", got \"%s\"", string(response.Kvs[0].Value))
} }
l.initialWg.Wait()
l.initial <- true 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) EtcdWatchCreated(client *EtcdClient, key string) {
l.initialWg.Done()
} }
func (l *EtcdClientTestListener) EtcdKeyUpdated(client *EtcdClient, key string, value []byte) { func (l *EtcdClientTestListener) EtcdKeyUpdated(client *EtcdClient, key string, value []byte, prevValue []byte) {
l.events <- etcdEvent{ evt := etcdEvent{
t: clientv3.EventTypePut, t: clientv3.EventTypePut,
key: string(key), key: string(key),
value: string(value), value: string(value),
} }
if len(prevValue) > 0 {
evt.prevValue = string(prevValue)
}
l.events <- evt
} }
func (l *EtcdClientTestListener) EtcdKeyDeleted(client *EtcdClient, key string) { func (l *EtcdClientTestListener) EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte) {
l.events <- etcdEvent{ evt := etcdEvent{
t: clientv3.EventTypeDelete, t: clientv3.EventTypeDelete,
key: string(key), key: string(key),
} }
if len(prevValue) > 0 {
evt.prevValue = string(prevValue)
}
l.events <- evt
} }
func Test_EtcdClient_Watch(t *testing.T) { func Test_EtcdClient_Watch(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd, client := NewEtcdClientForTest(t) etcd, client := NewEtcdClientForTest(t)
SetEtcdValue(etcd, "foo/a", []byte("1")) SetEtcdValue(etcd, "foo/a", []byte("1"))
@ -293,11 +318,23 @@ func Test_EtcdClient_Watch(t *testing.T) {
t.Errorf("expected value %s, got %s", "2", event.value) 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") DeleteEtcdValue(etcd, "foo/a")
event = <-listener.events event = <-listener.events
if event.t != clientv3.EventTypeDelete { if event.t != clientv3.EventTypeDelete {
t.Errorf("expected type %d, got %d", clientv3.EventTypeDelete, event.t) t.Errorf("expected type %d, got %d", clientv3.EventTypeDelete, event.t)
} else if event.key != "foo/a" { } else if event.key != "foo/a" {
t.Errorf("expected key %s, got %s", "foo/a", event.key) 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" "sync"
"time" "time"
"github.com/dlintw/goconf"
"github.com/oschwald/maxminddb-golang" "github.com/oschwald/maxminddb-golang"
) )
@ -156,36 +157,45 @@ func (g *GeoLookup) updateUrl() error {
} }
body := response.Body body := response.Body
if strings.HasSuffix(g.url, ".gz") { url := g.url
if strings.HasSuffix(url, ".gz") {
body, err = gzip.NewReader(body) body, err = gzip.NewReader(body)
if err != nil { if err != nil {
return err return err
} }
url = strings.TrimSuffix(url, ".gz")
} }
tarfile := tar.NewReader(body)
var geoipdata []byte var geoipdata []byte
for { if strings.HasSuffix(url, ".tar") || strings.HasSuffix(url, "=tar") {
header, err := tarfile.Next() tarfile := tar.NewReader(body)
if err == io.EOF { 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 break
} else if err != nil {
return err
} }
} else {
if !strings.HasSuffix(header.Name, ".mmdb") { geoipdata, err = io.ReadAll(body)
continue
}
geoipdata, err = io.ReadAll(tarfile)
if err != nil { if err != nil {
return err return err
} }
break
} }
if len(geoipdata) == 0 { 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) reader, err := maxminddb.FromBytes(geoipdata)
@ -267,3 +277,63 @@ func IsValidContinent(continent string) bool {
return false 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 ( import (
"archive/tar" "archive/tar"
"compress/gzip" "compress/gzip"
"fmt"
"io" "io"
"net" "net"
"net/http" "net/http"
"os" "os"
"strings" "strings"
"testing" "testing"
"time"
) )
func testGeoLookupReader(t *testing.T, reader *GeoLookup) { func testGeoLookupReader(t *testing.T, reader *GeoLookup) {
@ -57,13 +59,27 @@ func testGeoLookupReader(t *testing.T, reader *GeoLookup) {
} }
} }
func TestGeoLookup(t *testing.T) { func GetGeoIpUrlForTest(t *testing.T) string {
license := os.Getenv("MAXMIND_GEOLITE2_LICENSE") t.Helper()
if license == "" {
t.Skip("No MaxMind GeoLite2 license was set in MAXMIND_GEOLITE2_LICENSE environment variable.")
}
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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -77,12 +93,8 @@ func TestGeoLookup(t *testing.T) {
} }
func TestGeoLookupCaching(t *testing.T) { func TestGeoLookupCaching(t *testing.T) {
license := os.Getenv("MAXMIND_GEOLITE2_LICENSE") CatchLogForTest(t)
if license == "" { reader, err := NewGeoLookupFromUrl(GetGeoIpUrlForTest(t))
t.Skip("No MaxMind GeoLite2 license was set in MAXMIND_GEOLITE2_LICENSE environment variable.")
}
reader, err := NewGeoLookupFromUrl(GetGeoIpDownloadUrl(license))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -128,6 +140,7 @@ func TestGeoLookupContinent(t *testing.T) {
} }
func TestGeoLookupCloseEmpty(t *testing.T) { func TestGeoLookupCloseEmpty(t *testing.T) {
CatchLogForTest(t)
reader, err := NewGeoLookupFromUrl("ignore-url") reader, err := NewGeoLookupFromUrl("ignore-url")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -136,24 +149,23 @@ func TestGeoLookupCloseEmpty(t *testing.T) {
} }
func TestGeoLookupFromFile(t *testing.T) { func TestGeoLookupFromFile(t *testing.T) {
license := os.Getenv("MAXMIND_GEOLITE2_LICENSE") CatchLogForTest(t)
if license == "" { geoIpUrl := GetGeoIpUrlForTest(t)
t.Skip("No MaxMind GeoLite2 license was set in MAXMIND_GEOLITE2_LICENSE environment variable.")
}
url := GetGeoIpDownloadUrl(license) resp, err := http.Get(geoIpUrl)
resp, err := http.Get(url)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer resp.Body.Close() defer resp.Body.Close()
body := resp.Body body := resp.Body
if strings.HasSuffix(url, ".gz") { url := geoIpUrl
if strings.HasSuffix(geoIpUrl, ".gz") {
body, err = gzip.NewReader(body) body, err = gzip.NewReader(body)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
url = strings.TrimSuffix(url, ".gz")
} }
tmpfile, err := os.CreateTemp("", "geoipdb") tmpfile, err := os.CreateTemp("", "geoipdb")
@ -164,21 +176,33 @@ func TestGeoLookupFromFile(t *testing.T) {
os.Remove(tmpfile.Name()) os.Remove(tmpfile.Name())
}) })
tarfile := tar.NewReader(body)
foundDatabase := false foundDatabase := false
for { if strings.HasSuffix(url, ".tar") || strings.HasSuffix(url, "=tar") {
header, err := tarfile.Next() tarfile := tar.NewReader(body)
if err == io.EOF { 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 break
} else if err != nil {
t.Fatal(err)
} }
} else {
if !strings.HasSuffix(header.Name, ".mmdb") { if _, err := io.Copy(tmpfile, body); err != nil {
continue
}
if _, err := io.Copy(tmpfile, tarfile); err != nil {
tmpfile.Close() tmpfile.Close()
t.Fatal(err) t.Fatal(err)
} }
@ -186,11 +210,10 @@ func TestGeoLookupFromFile(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
foundDatabase = true foundDatabase = true
break
} }
if !foundDatabase { 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()) reader, err := NewGeoLookupFromFile(tmpfile.Name())

106
go.mod
View file

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

767
go.sum
View file

@ -1,101 +1,33 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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.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.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
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=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 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/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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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-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/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
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/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 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-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 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:I8/Qu5NTaiXi1TsEYmTeLDUlf7u9pEdbG+azjDvx8Vg=
github.com/dlintw/goconf v0.0.0-20120228082610-dcc070983490/go.mod h1:jWlUIP63OLr0cV2FGN2IEzSFsMAe58if8rk/SAE0JRE= 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= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@ -103,694 +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.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.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.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/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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/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.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/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/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 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 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/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/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 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.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.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.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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= 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/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.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.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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.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/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= 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-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 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 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 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
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/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 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.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/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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
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/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 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 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= 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-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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt/v2 v2.5.7/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I= github.com/nats-io/nats-server/v2 v2.10.16 h1:2jXaiydp5oB/nAx/Ytf9fdCi9QN6ItIc9eehX8kwVV0=
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.10.16/go.mod h1:Pksi38H2+6xLe1vQx0/EA4bzetM0NqyIHcIbmgXSkIU=
github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4= github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4= github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
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/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= 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/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 h1:EFU9iv8BMPyBo8iFMHvQleYlF5M3PY6zpAbxsngImjE=
github.com/notedit/janus-go v0.0.0-20200517101215-10eb8b95d1a0/go.mod h1:BN/Txse3qz8tZOmCm2OfajB2wHVujWmX3o9nVdsI6gE= 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/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.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
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/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= 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/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/sdp/v3 v3.0.5 h1:ouvI7IgGl+V4CrqskVtr3AaTrPvPisEOxwgpdktctkU= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.5/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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 v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
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/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 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/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
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/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 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.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= 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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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.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.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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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.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.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/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/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 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 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 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 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 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.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.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.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU=
go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI=
go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= go.etcd.io/etcd/client/v2 v2.305.14 h1:v5ASLyFuMlVd/gKU6uf6Cod+vSWKa4Rsv9+eghl0Nwk=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.14/go.mod h1:AWYT0lLEkBuqVaGw0UVMtA4rxCb3/oGE8PxZ8cUS4tI=
go.etcd.io/etcd/client/v2 v2.305.4 h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao= go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
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.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= go.etcd.io/etcd/pkg/v3 v3.5.14 h1:keuxhJiDCPjTKpW77GxJnnVVD5n4IsfvkDaqiqUMNEQ=
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.14/go.mod h1:7o+DL6a7DYz9KSjWByX+NGmQPYinoH3D36VAu/B3JqA=
go.etcd.io/etcd/pkg/v3 v3.5.4 h1:V5Dvl7S39ZDwjkKqJG2BfXgxZ3QREqqKifWQgIw5IM0= go.etcd.io/etcd/raft/v3 v3.5.14 h1:mHnpbljpBBftmK+YUfp+49ivaCc126aBPLAnwDw0DnE=
go.etcd.io/etcd/pkg/v3 v3.5.4/go.mod h1:OI+TtO+Aa3nhQSppMbwE4ld3uF1/fqqwbpfndbbrEe0= go.etcd.io/etcd/raft/v3 v3.5.14/go.mod h1:WnIK5blyJGRKsHA3efovdNoLv9QELTZHzpDOVIAuL2s=
go.etcd.io/etcd/raft/v3 v3.5.4 h1:YGrnAgRfgXloBNuqa+oBI/aRZMcK/1GS6trJePJ/Gqc= go.etcd.io/etcd/server/v3 v3.5.14 h1:l/3gdiSSoGU6MyKAYiL+8WSOMq9ySG+NqQ04euLtZfY=
go.etcd.io/etcd/raft/v3 v3.5.4/go.mod h1:SCuunjYvZFC0fBX0vxMSPjuZmpcSk+XaAcMrD6Do03w= go.etcd.io/etcd/server/v3 v3.5.14/go.mod h1:SPh0rUtGNDgOZd/aTbkAUYZV+5FFHw5sdbGnO2/byw0=
go.etcd.io/etcd/server/v3 v3.5.4 h1:CMAZd0g8Bn5NRhynW6pKhc4FRg41/0QYy3d7aNm9874= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M=
go.etcd.io/etcd/server/v3 v3.5.4/go.mod h1:S5/YTU15KxymM5l3T6b09sNOHPXqGYIZStpuuGbb65c= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU=
go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0=
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 h1:sO4WKdPAudZGKPcpZT4MJn6JaDmpyLrMPDGGyA1SttE= go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM=
go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
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.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 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/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 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.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-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-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-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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.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-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-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-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-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-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-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-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-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-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-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-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.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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-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-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-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-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-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-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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-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-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-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-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-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-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-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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-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-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-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-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-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-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-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.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-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-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-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= 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.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.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.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 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-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-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-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-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
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/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 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.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.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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 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/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.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.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.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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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-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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-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.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 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View file

@ -24,7 +24,9 @@ package signaling
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io"
"log" "log"
"net" "net"
"net/url" "net/url"
@ -37,6 +39,8 @@ import (
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"google.golang.org/grpc" "google.golang.org/grpc"
codes "google.golang.org/grpc/codes" codes "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
) )
@ -49,9 +53,9 @@ const (
) )
var ( var (
lookupGrpcIp = net.LookupIP // can be overwritten from tests ErrNoSuchResumeId = fmt.Errorf("unknown resume id")
customResolverPrefix uint64 customResolverPrefix atomic.Uint64
) )
func init() { func init() {
@ -75,12 +79,12 @@ func newGrpcClientImpl(conn grpc.ClientConnInterface) *grpcClientImpl {
} }
type GrpcClient struct { type GrpcClient struct {
isSelf uint32
ip net.IP ip net.IP
target string target string
conn *grpc.ClientConn conn *grpc.ClientConn
impl *grpcClientImpl impl *grpcClientImpl
isSelf atomic.Bool
} }
type customIpResolver struct { type customIpResolver struct {
@ -125,7 +129,7 @@ func NewGrpcClient(target string, ip net.IP, opts ...grpc.DialOption) (*GrpcClie
var conn *grpc.ClientConn var conn *grpc.ClientConn
var err error var err error
if ip != nil { if ip != nil {
prefix := atomic.AddUint64(&customResolverPrefix, 1) prefix := customResolverPrefix.Add(1)
addr := ip.String() addr := ip.String()
hostname := target hostname := target
if host, port, err := net.SplitHostPort(target); err == nil { if host, port, err := net.SplitHostPort(target); err == nil {
@ -138,9 +142,9 @@ func NewGrpcClient(target string, ip net.IP, opts ...grpc.DialOption) (*GrpcClie
hostname: hostname, hostname: hostname,
} }
opts = append(opts, grpc.WithResolvers(resolver)) opts = append(opts, grpc.WithResolvers(resolver))
conn, err = grpc.Dial(fmt.Sprintf("%s://%s", resolver.Scheme(), target), opts...) conn, err = grpc.NewClient(fmt.Sprintf("%s://%s", resolver.Scheme(), target), opts...)
} else { } else {
conn, err = grpc.Dial(target, opts...) conn, err = grpc.NewClient(target, opts...)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -168,15 +172,11 @@ func (c *GrpcClient) Close() error {
} }
func (c *GrpcClient) IsSelf() bool { func (c *GrpcClient) IsSelf() bool {
return atomic.LoadUint32(&c.isSelf) != 0 return c.isSelf.Load()
} }
func (c *GrpcClient) SetSelf(self bool) { func (c *GrpcClient) SetSelf(self bool) {
if self { c.isSelf.Store(self)
atomic.StoreUint32(&c.isSelf, 1)
} else {
atomic.StoreUint32(&c.isSelf, 0)
}
} }
func (c *GrpcClient) GetServerId(ctx context.Context) (string, error) { func (c *GrpcClient) GetServerId(ctx context.Context) (string, error) {
@ -189,6 +189,26 @@ func (c *GrpcClient) GetServerId(ctx context.Context) (string, error) {
return response.GetServerId(), nil 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) { func (c *GrpcClient) LookupSessionId(ctx context.Context, roomSessionId string, disconnectReason string) (string, error) {
statsGrpcClientCalls.WithLabelValues("LookupSessionId").Inc() statsGrpcClientCalls.WithLabelValues("LookupSessionId").Inc()
// TODO: Remove debug logging // TODO: Remove debug logging
@ -229,13 +249,13 @@ func (c *GrpcClient) IsSessionInCall(ctx context.Context, sessionId string, room
return response.GetInCall(), nil return response.GetInCall(), nil
} }
func (c *GrpcClient) GetPublisherId(ctx context.Context, sessionId string, streamType string) (string, string, net.IP, error) { func (c *GrpcClient) GetPublisherId(ctx context.Context, sessionId string, streamType StreamType) (string, string, net.IP, error) {
statsGrpcClientCalls.WithLabelValues("GetPublisherId").Inc() statsGrpcClientCalls.WithLabelValues("GetPublisherId").Inc()
// TODO: Remove debug logging // TODO: Remove debug logging
log.Printf("Get %s publisher id %s on %s", streamType, sessionId, c.Target()) log.Printf("Get %s publisher id %s on %s", streamType, sessionId, c.Target())
response, err := c.impl.GetPublisherId(ctx, &GetPublisherIdRequest{ response, err := c.impl.GetPublisherId(ctx, &GetPublisherIdRequest{
SessionId: sessionId, SessionId: sessionId,
StreamType: streamType, StreamType: string(streamType),
}, grpc.WaitForReady(true)) }, grpc.WaitForReady(true))
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return "", "", nil, nil return "", "", nil, nil
@ -262,37 +282,125 @@ func (c *GrpcClient) GetSessionCount(ctx context.Context, u *url.URL) (uint32, e
return response.GetCount(), nil 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 { type GrpcClients struct {
mu sync.RWMutex mu sync.RWMutex
clientsMap map[string][]*GrpcClient clientsMap map[string]*grpcClientsList
clients []*GrpcClient clients []*GrpcClient
dnsMonitor *DnsMonitor
dnsDiscovery bool dnsDiscovery bool
stopping chan bool
stopped chan bool
etcdClient *EtcdClient etcdClient *EtcdClient
targetPrefix string targetPrefix string
targetInformation map[string]*GrpcTargetInformationEtcd targetInformation map[string]*GrpcTargetInformationEtcd
dialOptions atomic.Value // []grpc.DialOption dialOptions atomic.Value // []grpc.DialOption
creds credentials.TransportCredentials
initializedCtx context.Context initializedCtx context.Context
initializedFunc context.CancelFunc initializedFunc context.CancelFunc
initializedWg sync.WaitGroup wakeupChanForTesting chan struct{}
wakeupChanForTesting chan bool
selfCheckWaitGroup sync.WaitGroup selfCheckWaitGroup sync.WaitGroup
closeCtx context.Context
closeFunc context.CancelFunc
} }
func NewGrpcClients(config *goconf.ConfigFile, etcdClient *EtcdClient) (*GrpcClients, error) { func NewGrpcClients(config *goconf.ConfigFile, etcdClient *EtcdClient, dnsMonitor *DnsMonitor) (*GrpcClients, error) {
initializedCtx, initializedFunc := context.WithCancel(context.Background()) initializedCtx, initializedFunc := context.WithCancel(context.Background())
closeCtx, closeFunc := context.WithCancel(context.Background())
result := &GrpcClients{ result := &GrpcClients{
dnsMonitor: dnsMonitor,
etcdClient: etcdClient, etcdClient: etcdClient,
initializedCtx: initializedCtx, initializedCtx: initializedCtx,
initializedFunc: initializedFunc, initializedFunc: initializedFunc,
closeCtx: closeCtx,
stopping: make(chan bool, 1), closeFunc: closeFunc,
stopped: make(chan bool, 1),
} }
if err := result.load(config, false); err != nil { if err := result.load(config, false); err != nil {
return nil, err return nil, err
@ -306,6 +414,13 @@ func (c *GrpcClients) load(config *goconf.ConfigFile, fromReload bool) error {
return err return err
} }
if c.creds != nil {
if cr, ok := c.creds.(*reloadableCredentials); ok {
cr.Close()
}
}
c.creds = creds
opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)} opts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
c.dialOptions.Store(opts) c.dialOptions.Store(opts)
@ -317,9 +432,6 @@ func (c *GrpcClients) load(config *goconf.ConfigFile, fromReload bool) error {
switch targetType { switch targetType {
case GrpcTargetTypeStatic: case GrpcTargetTypeStatic:
err = c.loadTargetsStatic(config, fromReload, opts...) err = c.loadTargetsStatic(config, fromReload, opts...)
if err == nil && c.dnsDiscovery {
go c.monitorGrpcIPs()
}
case GrpcTargetTypeEtcd: case GrpcTargetTypeEtcd:
err = c.loadTargetsEtcd(config, fromReload, opts...) err = c.loadTargetsEtcd(config, fromReload, opts...)
default: default:
@ -348,7 +460,7 @@ func (c *GrpcClients) isClientAvailable(target string, client *GrpcClient) bool
return false return false
} }
for _, entry := range entries { for _, entry := range entries.clients {
if entry == client { if entry == client {
return true return true
} }
@ -382,6 +494,10 @@ loop:
id, err := c.getServerIdWithTimeout(ctx, client) id, err := c.getServerIdWithTimeout(ctx, client)
if err != nil { if err != nil {
if errors.Is(err, context.Canceled) {
return
}
if status.Code(err) != codes.Canceled { if status.Code(err) != codes.Canceled {
log.Printf("Error checking GRPC server id of %s, retrying in %s: %s", client.Target(), backoff.NextWait(), err) log.Printf("Error checking GRPC server id of %s, retrying in %s: %s", client.Target(), backoff.NextWait(), err)
} }
@ -405,7 +521,20 @@ func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bo
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
clientsMap := make(map[string][]*GrpcClient) 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 var clients []*GrpcClient
removeTargets := make(map[string]bool, len(c.clientsMap)) removeTargets := make(map[string]bool, len(c.clientsMap))
for target, entries := range c.clientsMap { for target, entries := range c.clientsMap {
@ -421,7 +550,15 @@ func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bo
} }
if entries, found := clientsMap[target]; found { if entries, found := clientsMap[target]; found {
clients = append(clients, entries...) 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) delete(removeTargets, target)
continue continue
} }
@ -431,61 +568,59 @@ func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bo
host = h host = h
} }
var ips []net.IP if dnsDiscovery && net.ParseIP(host) == nil {
if net.ParseIP(host) == nil {
// Use dedicated client for each IP address. // Use dedicated client for each IP address.
var err error entry, err := c.dnsMonitor.Add(target, c.onLookup)
ips, err = lookupGrpcIp(host)
if err != nil { if err != nil {
log.Printf("Could not lookup %s: %s", host, err)
// Make sure updating continues even if initial lookup failed.
clientsMap[target] = nil
continue
}
} else {
// Connect directly to IP address.
ips = []net.IP{nil}
}
for _, ip := range ips {
client, err := NewGrpcClient(target, ip, opts...)
if err != nil {
for _, clients := range clientsMap {
for _, client := range clients {
c.closeClient(client)
}
}
return err return err
} }
c.selfCheckWaitGroup.Add(1) clientsMap[target] = &grpcClientsList{
go c.checkIsSelf(context.Background(), target, client) entry: entry,
}
log.Printf("Adding %s as GRPC target", client.Target()) continue
clientsMap[target] = append(clientsMap[target], client)
clients = append(clients, client)
} }
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 { for target := range removeTargets {
if clients, found := clientsMap[target]; found { if entry, found := clientsMap[target]; found {
for _, client := range clients { for _, client := range entry.clients {
log.Printf("Deleting GRPC target %s", client.Target()) log.Printf("Deleting GRPC target %s", client.Target())
c.closeClient(client) c.closeClient(client)
} }
delete(clientsMap, target)
}
}
dnsDiscovery, _ := config.GetBool("grpc", "dnsdiscovery") if entry.entry != nil {
if dnsDiscovery != c.dnsDiscovery { c.dnsMonitor.Remove(entry.entry)
if !dnsDiscovery && fromReload { entry.entry = nil
c.stopping <- true }
<-c.stopped delete(clientsMap, target)
}
c.dnsDiscovery = dnsDiscovery
if dnsDiscovery && fromReload {
go c.monitorGrpcIPs()
} }
} }
@ -496,91 +631,61 @@ func (c *GrpcClients) loadTargetsStatic(config *goconf.ConfigFile, fromReload bo
return nil return nil
} }
func (c *GrpcClients) monitorGrpcIPs() { func (c *GrpcClients) onLookup(entry *DnsMonitorEntry, all []net.IP, added []net.IP, keep []net.IP, removed []net.IP) {
log.Printf("Start monitoring GRPC client IPs")
ticker := time.NewTicker(updateDnsInterval)
for {
select {
case <-ticker.C:
c.updateGrpcIPs()
case <-c.stopping:
c.stopped <- true
return
}
}
}
func (c *GrpcClients) updateGrpcIPs() {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
target := entry.URL()
e, found := c.clientsMap[target]
if !found {
return
}
opts := c.dialOptions.Load().([]grpc.DialOption) opts := c.dialOptions.Load().([]grpc.DialOption)
mapModified := false mapModified := false
for target, clients := range c.clientsMap { var newClients []*GrpcClient
host := target for _, ip := range removed {
if h, _, err := net.SplitHostPort(target); err == nil { for _, client := range e.clients {
host = h if ip.Equal(client.ip) {
} mapModified = true
if net.ParseIP(host) != nil {
// No need to lookup endpoints that connect to IP addresses.
continue
}
ips, err := lookupGrpcIp(host)
if err != nil {
log.Printf("Could not lookup %s: %s", host, err)
continue
}
var newClients []*GrpcClient
changed := false
for _, client := range clients {
found := false
for idx, ip := range ips {
if ip.Equal(client.ip) {
ips = append(ips[:idx], ips[idx+1:]...)
found = true
newClients = append(newClients, client)
break
}
}
if !found {
changed = true
log.Printf("Removing connection to %s", client.Target()) log.Printf("Removing connection to %s", client.Target())
c.closeClient(client) c.closeClient(client)
c.wakeupForTesting() c.wakeupForTesting()
} }
} }
}
for _, ip := range ips { for _, ip := range keep {
client, err := NewGrpcClient(target, ip, opts...) for _, client := range e.clients {
if err != nil { if ip.Equal(client.ip) {
log.Printf("Error creating client to %s with IP %s: %s", target, ip.String(), err) newClients = append(newClients, client)
continue
} }
c.selfCheckWaitGroup.Add(1)
go c.checkIsSelf(context.Background(), target, client)
log.Printf("Adding %s as GRPC target", client.Target())
newClients = append(newClients, client)
changed = true
c.wakeupForTesting()
}
if changed {
c.clientsMap[target] = newClients
mapModified = true
} }
} }
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 { if mapModified {
c.clientsMap[target].clients = newClients
c.clients = make([]*GrpcClient, 0, len(c.clientsMap)) c.clients = make([]*GrpcClient, 0, len(c.clientsMap))
for _, clients := range c.clientsMap { for _, entry := range c.clientsMap {
c.clients = append(c.clients, clients...) c.clients = append(c.clients, entry.clients...)
} }
statsGrpcClients.Set(float64(len(c.clients))) statsGrpcClients.Set(float64(len(c.clients)))
} }
@ -605,52 +710,72 @@ func (c *GrpcClients) loadTargetsEtcd(config *goconf.ConfigFile, fromReload bool
} }
func (c *GrpcClients) EtcdClientCreated(client *EtcdClient) { func (c *GrpcClients) EtcdClientCreated(client *EtcdClient) {
c.initializedWg.Add(1)
go func() { go func() {
if err := client.Watch(context.Background(), c.targetPrefix, c, clientv3.WithPrefix()); err != nil { if err := client.WaitForConnection(c.closeCtx); err != nil {
log.Printf("Error processing watch for %s: %s", c.targetPrefix, err) if errors.Is(err, context.Canceled) {
} return
}() }
go func() { panic(err)
client.WaitForConnection() }
backoff, _ := NewExponentialBackoff(initialWaitDelay, maxWaitDelay) backoff, _ := NewExponentialBackoff(initialWaitDelay, maxWaitDelay)
for { var nextRevision int64
response, err := c.getGrpcTargets(client, c.targetPrefix) for c.closeCtx.Err() == nil {
response, err := c.getGrpcTargets(c.closeCtx, client, c.targetPrefix)
if err != nil { if err != nil {
if err == context.DeadlineExceeded { 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()) log.Printf("Timeout getting initial list of GRPC targets, retry in %s", backoff.NextWait())
} else { } else {
log.Printf("Could not get initial list of GRPC targets, retry in %s: %s", backoff.NextWait(), err) log.Printf("Could not get initial list of GRPC targets, retry in %s: %s", backoff.NextWait(), err)
} }
backoff.Wait(context.Background()) backoff.Wait(c.closeCtx)
continue continue
} }
for _, ev := range response.Kvs { for _, ev := range response.Kvs {
c.EtcdKeyUpdated(client, string(ev.Key), ev.Value) c.EtcdKeyUpdated(client, string(ev.Key), ev.Value, nil)
} }
c.initializedWg.Wait()
c.initializedFunc() c.initializedFunc()
return 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) EtcdWatchCreated(client *EtcdClient, key string) {
c.initializedWg.Done()
} }
func (c *GrpcClients) getGrpcTargets(client *EtcdClient, targetPrefix string) (*clientv3.GetResponse, error) { func (c *GrpcClients) getGrpcTargets(ctx context.Context, client *EtcdClient, targetPrefix string) (*clientv3.GetResponse, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel() defer cancel()
return client.Get(ctx, targetPrefix, clientv3.WithPrefix()) return client.Get(ctx, targetPrefix, clientv3.WithPrefix())
} }
func (c *GrpcClients) EtcdKeyUpdated(client *EtcdClient, key string, data []byte) { func (c *GrpcClients) EtcdKeyUpdated(client *EtcdClient, key string, data []byte, prevValue []byte) {
var info GrpcTargetInformationEtcd var info GrpcTargetInformationEtcd
if err := json.Unmarshal(data, &info); err != nil { if err := json.Unmarshal(data, &info); err != nil {
log.Printf("Could not decode GRPC target %s=%s: %s", key, string(data), err) log.Printf("Could not decode GRPC target %s=%s: %s", key, string(data), err)
@ -683,21 +808,23 @@ func (c *GrpcClients) EtcdKeyUpdated(client *EtcdClient, key string, data []byte
} }
c.selfCheckWaitGroup.Add(1) c.selfCheckWaitGroup.Add(1)
go c.checkIsSelf(context.Background(), info.Address, cl) go c.checkIsSelf(c.closeCtx, info.Address, cl)
log.Printf("Adding %s as GRPC target", cl.Target()) log.Printf("Adding %s as GRPC target", cl.Target())
if c.clientsMap == nil { if c.clientsMap == nil {
c.clientsMap = make(map[string][]*GrpcClient) c.clientsMap = make(map[string]*grpcClientsList)
}
c.clientsMap[info.Address] = &grpcClientsList{
clients: []*GrpcClient{cl},
} }
c.clientsMap[info.Address] = []*GrpcClient{cl}
c.clients = append(c.clients, cl) c.clients = append(c.clients, cl)
c.targetInformation[key] = &info c.targetInformation[key] = &info
statsGrpcClients.Inc() statsGrpcClients.Inc()
c.wakeupForTesting() c.wakeupForTesting()
} }
func (c *GrpcClients) EtcdKeyDeleted(client *EtcdClient, key string) { func (c *GrpcClients) EtcdKeyDeleted(client *EtcdClient, key string, prevValue []byte) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@ -713,19 +840,19 @@ func (c *GrpcClients) removeEtcdClientLocked(key string) {
} }
delete(c.targetInformation, key) delete(c.targetInformation, key)
clients, found := c.clientsMap[info.Address] entry, found := c.clientsMap[info.Address]
if !found { if !found {
return return
} }
for _, client := range clients { for _, client := range entry.clients {
log.Printf("Removing connection to %s (from %s)", client.Target(), key) log.Printf("Removing connection to %s (from %s)", client.Target(), key)
c.closeClient(client) c.closeClient(client)
} }
delete(c.clientsMap, info.Address) delete(c.clientsMap, info.Address)
c.clients = make([]*GrpcClient, 0, len(c.clientsMap)) c.clients = make([]*GrpcClient, 0, len(c.clientsMap))
for _, clients := range c.clientsMap { for _, entry := range c.clientsMap {
c.clients = append(c.clients, clients...) c.clients = append(c.clients, entry.clients...)
} }
statsGrpcClients.Dec() statsGrpcClients.Dec()
c.wakeupForTesting() c.wakeupForTesting()
@ -746,7 +873,7 @@ func (c *GrpcClients) wakeupForTesting() {
} }
select { select {
case c.wakeupChanForTesting <- true: case c.wakeupChanForTesting <- struct{}{}:
default: default:
} }
} }
@ -761,25 +888,32 @@ func (c *GrpcClients) Close() {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
for _, clients := range c.clientsMap { for _, entry := range c.clientsMap {
for _, client := range clients { for _, client := range entry.clients {
if err := client.Close(); err != nil { if err := client.Close(); err != nil {
log.Printf("Error closing client to %s: %s", client.Target(), err) 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.clients = nil
c.clientsMap = nil c.clientsMap = nil
if c.dnsDiscovery { c.dnsDiscovery = false
c.stopping <- true
<-c.stopped
c.dnsDiscovery = false
}
if c.etcdClient != nil { if c.etcdClient != nil {
c.etcdClient.RemoveListener(c) c.etcdClient.RemoveListener(c)
} }
if c.creds != nil {
if cr, ok := c.creds.(*reloadableCredentials); ok {
cr.Close()
}
}
c.closeFunc()
} }
func (c *GrpcClients) GetClients() []*GrpcClient { func (c *GrpcClients) GetClients() []*GrpcClient {

View file

@ -36,8 +36,22 @@ import (
"go.etcd.io/etcd/server/v3/embed" "go.etcd.io/etcd/server/v3/embed"
) )
func NewGrpcClientsForTestWithConfig(t *testing.T, config *goconf.ConfigFile, etcdClient *EtcdClient) *GrpcClients { func (c *GrpcClients) getWakeupChannelForTesting() <-chan struct{} {
client, err := NewGrpcClients(config, etcdClient) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -45,10 +59,10 @@ func NewGrpcClientsForTestWithConfig(t *testing.T, config *goconf.ConfigFile, et
client.Close() client.Close()
}) })
return client return client, dnsMonitor
} }
func NewGrpcClientsForTest(t *testing.T, addr string) *GrpcClients { func NewGrpcClientsForTest(t *testing.T, addr string) (*GrpcClients, *DnsMonitor) {
config := goconf.NewConfigFile() config := goconf.NewConfigFile()
config.AddOption("grpc", "targets", addr) config.AddOption("grpc", "targets", addr)
config.AddOption("grpc", "dnsdiscovery", "true") config.AddOption("grpc", "dnsdiscovery", "true")
@ -56,9 +70,9 @@ func NewGrpcClientsForTest(t *testing.T, addr string) *GrpcClients {
return NewGrpcClientsForTestWithConfig(t, config, nil) return NewGrpcClientsForTestWithConfig(t, config, nil)
} }
func NewGrpcClientsWithEtcdForTest(t *testing.T, etcd *embed.Etcd) *GrpcClients { func NewGrpcClientsWithEtcdForTest(t *testing.T, etcd *embed.Etcd) (*GrpcClients, *DnsMonitor) {
config := goconf.NewConfigFile() config := goconf.NewConfigFile()
config.AddOption("etcd", "endpoints", etcd.Config().LCUrls[0].String()) config.AddOption("etcd", "endpoints", etcd.Config().ListenClientUrls[0].String())
config.AddOption("grpc", "targettype", "etcd") config.AddOption("grpc", "targettype", "etcd")
config.AddOption("grpc", "targetprefix", "/grpctargets") config.AddOption("grpc", "targetprefix", "/grpctargets")
@ -76,7 +90,7 @@ func NewGrpcClientsWithEtcdForTest(t *testing.T, etcd *embed.Etcd) *GrpcClients
return NewGrpcClientsForTestWithConfig(t, config, etcdClient) return NewGrpcClientsForTestWithConfig(t, config, etcdClient)
} }
func drainWakeupChannel(ch chan bool) { func drainWakeupChannel(ch <-chan struct{}) {
for { for {
select { select {
case <-ch: case <-ch:
@ -86,32 +100,50 @@ func drainWakeupChannel(ch chan bool) {
} }
} }
func Test_GrpcClients_EtcdInitial(t *testing.T) { func waitForEvent(ctx context.Context, t *testing.T, ch <-chan struct{}) {
_, addr1 := NewGrpcServerForTest(t) t.Helper()
_, addr2 := NewGrpcServerForTest(t)
etcd := NewEtcdForTest(t) select {
case <-ch:
SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}")) return
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}")) case <-ctx.Done():
t.Error("timeout waiting for event")
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_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) { func Test_GrpcClients_EtcdUpdate(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd := NewEtcdForTest(t) etcd := NewEtcdForTest(t)
client := NewGrpcClientsWithEtcdForTest(t, etcd) client, _ := NewGrpcClientsWithEtcdForTest(t, etcd)
ch := make(chan bool, 1) ch := client.getWakeupChannelForTesting()
client.wakeupChanForTesting = ch
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
if clients := client.GetClients(); len(clients) != 0 { if clients := client.GetClients(); len(clients) != 0 {
t.Errorf("Expected no clients, got %+v", clients) t.Errorf("Expected no clients, got %+v", clients)
@ -120,7 +152,7 @@ func Test_GrpcClients_EtcdUpdate(t *testing.T) {
drainWakeupChannel(ch) drainWakeupChannel(ch)
_, addr1 := NewGrpcServerForTest(t) _, addr1 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}")) SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}"))
<-ch waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr1 { } else if clients[0].Target() != addr1 {
@ -130,7 +162,7 @@ func Test_GrpcClients_EtcdUpdate(t *testing.T) {
drainWakeupChannel(ch) drainWakeupChannel(ch)
_, addr2 := NewGrpcServerForTest(t) _, addr2 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}")) SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}"))
<-ch waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 2 { if clients := client.GetClients(); len(clients) != 2 {
t.Errorf("Expected two clients, got %+v", clients) t.Errorf("Expected two clients, got %+v", clients)
} else if clients[0].Target() != addr1 { } else if clients[0].Target() != addr1 {
@ -141,7 +173,7 @@ func Test_GrpcClients_EtcdUpdate(t *testing.T) {
drainWakeupChannel(ch) drainWakeupChannel(ch)
DeleteEtcdValue(etcd, "/grpctargets/one") DeleteEtcdValue(etcd, "/grpctargets/one")
<-ch waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr2 { } else if clients[0].Target() != addr2 {
@ -151,7 +183,7 @@ func Test_GrpcClients_EtcdUpdate(t *testing.T) {
drainWakeupChannel(ch) drainWakeupChannel(ch)
_, addr3 := NewGrpcServerForTest(t) _, addr3 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr3+"\"}")) SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr3+"\"}"))
<-ch waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr3 { } else if clients[0].Target() != addr3 {
@ -160,10 +192,14 @@ func Test_GrpcClients_EtcdUpdate(t *testing.T) {
} }
func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) { func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) {
t.Parallel()
CatchLogForTest(t)
etcd := NewEtcdForTest(t) etcd := NewEtcdForTest(t)
client := NewGrpcClientsWithEtcdForTest(t, etcd) client, _ := NewGrpcClientsWithEtcdForTest(t, etcd)
ch := make(chan bool, 1) ch := client.getWakeupChannelForTesting()
client.wakeupChanForTesting = ch
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
if clients := client.GetClients(); len(clients) != 0 { if clients := client.GetClients(); len(clients) != 0 {
t.Errorf("Expected no clients, got %+v", clients) t.Errorf("Expected no clients, got %+v", clients)
@ -172,7 +208,7 @@ func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) {
drainWakeupChannel(ch) drainWakeupChannel(ch)
_, addr1 := NewGrpcServerForTest(t) _, addr1 := NewGrpcServerForTest(t)
SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}")) SetEtcdValue(etcd, "/grpctargets/one", []byte("{\"address\":\""+addr1+"\"}"))
<-ch waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr1 { } else if clients[0].Target() != addr1 {
@ -183,7 +219,7 @@ func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) {
server2, addr2 := NewGrpcServerForTest(t) server2, addr2 := NewGrpcServerForTest(t)
server2.serverId = GrpcServerId server2.serverId = GrpcServerId
SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}")) SetEtcdValue(etcd, "/grpctargets/two", []byte("{\"address\":\""+addr2+"\"}"))
<-ch waitForEvent(ctx, t, ch)
client.selfCheckWaitGroup.Wait() client.selfCheckWaitGroup.Wait()
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
@ -193,7 +229,7 @@ func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) {
drainWakeupChannel(ch) drainWakeupChannel(ch)
DeleteEtcdValue(etcd, "/grpctargets/two") DeleteEtcdValue(etcd, "/grpctargets/two")
<-ch waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != addr1 { } else if clients[0].Target() != addr1 {
@ -202,82 +238,74 @@ func Test_GrpcClients_EtcdIgnoreSelf(t *testing.T) {
} }
func Test_GrpcClients_DnsDiscovery(t *testing.T) { func Test_GrpcClients_DnsDiscovery(t *testing.T) {
var ipsResult []net.IP CatchLogForTest(t)
lookupGrpcIp = func(host string) ([]net.IP, error) { ensureNoGoroutinesLeak(t, func(t *testing.T) {
if host == "testgrpc" { lookup := newMockDnsLookupForTest(t)
return ipsResult, nil 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)
} }
return nil, fmt.Errorf("unknown host") lookup.Set("testgrpc", []net.IP{ip1, ip2})
} drainWakeupChannel(ch)
target := "testgrpc:12345" dnsMonitor.checkHostnames()
ip1 := net.ParseIP("192.168.0.1") waitForEvent(ctx, t, ch)
ip2 := net.ParseIP("192.168.0.2")
targetWithIp1 := fmt.Sprintf("%s (%s)", target, ip1)
targetWithIp2 := fmt.Sprintf("%s (%s)", target, ip2)
ipsResult = []net.IP{ip1}
client := NewGrpcClientsForTest(t, target)
ch := make(chan bool, 1)
client.wakeupChanForTesting = ch
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 2 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected two client, got %+v", clients)
} else if clients[0].Target() != targetWithIp1 { } else if clients[0].Target() != targetWithIp1 {
t.Errorf("Expected target %s, got %s", targetWithIp1, clients[0].Target()) t.Errorf("Expected target %s, got %s", targetWithIp1, clients[0].Target())
} else if !clients[0].ip.Equal(ip1) { } else if !clients[0].ip.Equal(ip1) {
t.Errorf("Expected IP %s, got %s", ip1, clients[0].ip) 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)
}
ipsResult = []net.IP{ip1, ip2} lookup.Set("testgrpc", []net.IP{ip2})
drainWakeupChannel(ch) drainWakeupChannel(ch)
client.updateGrpcIPs() dnsMonitor.checkHostnames()
<-ch waitForEvent(ctx, t, ch)
if clients := client.GetClients(); len(clients) != 2 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected two client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
} else if clients[0].Target() != targetWithIp1 { } else if clients[0].Target() != targetWithIp2 {
t.Errorf("Expected target %s, got %s", targetWithIp1, clients[0].Target()) t.Errorf("Expected target %s, got %s", targetWithIp2, clients[0].Target())
} else if !clients[0].ip.Equal(ip1) { } else if !clients[0].ip.Equal(ip2) {
t.Errorf("Expected IP %s, got %s", ip1, clients[0].ip) t.Errorf("Expected IP %s, got %s", ip2, 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)
}
ipsResult = []net.IP{ip2}
drainWakeupChannel(ch)
client.updateGrpcIPs()
<-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) { func Test_GrpcClients_DnsDiscoveryInitialFailed(t *testing.T) {
var ipsResult []net.IP t.Parallel()
lookupGrpcIp = func(host string) ([]net.IP, error) { CatchLogForTest(t)
if host == "testgrpc" && len(ipsResult) > 0 { lookup := newMockDnsLookupForTest(t)
return ipsResult, nil
}
return nil, &net.DNSError{
Err: "no such host",
Name: host,
IsNotFound: true,
}
}
target := "testgrpc:12345" target := "testgrpc:12345"
ip1 := net.ParseIP("192.168.0.1") ip1 := net.ParseIP("192.168.0.1")
targetWithIp1 := fmt.Sprintf("%s (%s)", target, ip1) targetWithIp1 := fmt.Sprintf("%s (%s)", target, ip1)
client := NewGrpcClientsForTest(t, target) client, dnsMonitor := NewGrpcClientsForTest(t, target)
ch := make(chan bool, 1) ch := client.getWakeupChannelForTesting()
client.wakeupChanForTesting = ch
testCtx, testCtxCancel := context.WithTimeout(context.Background(), testTimeout)
defer testCtxCancel()
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() defer cancel()
@ -289,10 +317,10 @@ func Test_GrpcClients_DnsDiscoveryInitialFailed(t *testing.T) {
t.Errorf("Expected no client, got %+v", clients) t.Errorf("Expected no client, got %+v", clients)
} }
ipsResult = []net.IP{ip1} lookup.Set("testgrpc", []net.IP{ip1})
drainWakeupChannel(ch) drainWakeupChannel(ch)
client.updateGrpcIPs() dnsMonitor.checkHostnames()
<-ch waitForEvent(testCtx, t, ch)
if clients := client.GetClients(); len(clients) != 1 { if clients := client.GetClients(); len(clients) != 1 {
t.Errorf("Expected one client, got %+v", clients) t.Errorf("Expected one client, got %+v", clients)
@ -304,55 +332,58 @@ func Test_GrpcClients_DnsDiscoveryInitialFailed(t *testing.T) {
} }
func Test_GrpcClients_Encryption(t *testing.T) { func Test_GrpcClients_Encryption(t *testing.T) {
serverKey, err := rsa.GenerateKey(rand.Reader, 1024) CatchLogForTest(t)
if err != nil { ensureNoGoroutinesLeak(t, func(t *testing.T) {
t.Fatal(err) serverKey, err := rsa.GenerateKey(rand.Reader, 1024)
} if err != nil {
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) 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)
}
}
})
} }

View file

@ -125,6 +125,15 @@ func (c *reloadableCredentials) OverrideServerName(serverName string) error {
return nil 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) { func NewReloadableCredentials(config *goconf.ConfigFile, server bool) (credentials.TransportCredentials, error) {
var prefix string var prefix string
var caPrefix string var caPrefix string

View file

@ -22,11 +22,13 @@
package signaling package signaling
import ( import (
"context"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"errors"
"io/fs" "io/fs"
"math/big" "math/big"
"net" "net"
@ -35,6 +37,22 @@ import (
"time" "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 { func GenerateSelfSignedCertificateForTesting(t *testing.T, bits int, organization string, key *rsa.PrivateKey) []byte {
template := x509.Certificate{ template := x509.Certificate{
SerialNumber: big.NewInt(1), SerialNumber: big.NewInt(1),

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)
}
}
}
}

View file

@ -35,6 +35,7 @@ import (
"github.com/dlintw/goconf" "github.com/dlintw/goconf"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
) )
@ -54,22 +55,31 @@ func init() {
GrpcServerId = hex.EncodeToString(md.Sum(nil)) 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 { type GrpcServer struct {
UnimplementedRpcBackendServer UnimplementedRpcBackendServer
UnimplementedRpcInternalServer UnimplementedRpcInternalServer
UnimplementedRpcMcuServer UnimplementedRpcMcuServer
UnimplementedRpcSessionsServer UnimplementedRpcSessionsServer
creds credentials.TransportCredentials
conn *grpc.Server conn *grpc.Server
listener net.Listener listener net.Listener
serverId string // can be overwritten from tests serverId string // can be overwritten from tests
hub *Hub hub GrpcServerHub
} }
func NewGrpcServer(config *goconf.ConfigFile) (*GrpcServer, error) { func NewGrpcServer(config *goconf.ConfigFile) (*GrpcServer, error) {
var listener net.Listener var listener net.Listener
if addr, _ := config.GetString("grpc", "listen"); addr != "" { if addr, _ := GetStringOptionWithEnv(config, "grpc", "listen"); addr != "" {
var err error var err error
listener, err = net.Listen("tcp", addr) listener, err = net.Listen("tcp", addr)
if err != nil { if err != nil {
@ -84,6 +94,7 @@ func NewGrpcServer(config *goconf.ConfigFile) (*GrpcServer, error) {
conn := grpc.NewServer(grpc.Creds(creds)) conn := grpc.NewServer(grpc.Creds(creds))
result := &GrpcServer{ result := &GrpcServer{
creds: creds,
conn: conn, conn: conn,
listener: listener, listener: listener,
serverId: GrpcServerId, serverId: GrpcServerId,
@ -105,13 +116,30 @@ func (s *GrpcServer) Run() error {
func (s *GrpcServer) Close() { func (s *GrpcServer) Close() {
s.conn.GracefulStop() 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) { func (s *GrpcServer) LookupSessionId(ctx context.Context, request *LookupSessionIdRequest) (*LookupSessionIdReply, error) {
statsGrpcServerCalls.WithLabelValues("LookupSessionId").Inc() statsGrpcServerCalls.WithLabelValues("LookupSessionId").Inc()
// TODO: Remove debug logging // TODO: Remove debug logging
log.Printf("Lookup session id for room session id %s", request.RoomSessionId) log.Printf("Lookup session id for room session id %s", request.RoomSessionId)
sid, err := s.hub.roomSessions.GetSessionId(request.RoomSessionId) sid, err := s.hub.GetSessionIdByRoomSessionId(request.RoomSessionId)
if errors.Is(err, ErrNoSuchRoomSession) { if errors.Is(err, ErrNoSuchRoomSession) {
return nil, status.Error(codes.NotFound, "no such room session id") return nil, status.Error(codes.NotFound, "no such room session id")
} else if err != nil { } else if err != nil {
@ -171,7 +199,7 @@ func (s *GrpcServer) GetPublisherId(ctx context.Context, request *GetPublisherId
return nil, status.Error(codes.NotFound, "no such session") return nil, status.Error(codes.NotFound, "no such session")
} }
publisher := clientSession.GetOrWaitForPublisher(ctx, request.StreamType) publisher := clientSession.GetOrWaitForPublisher(ctx, StreamType(request.StreamType))
if publisher, ok := publisher.(*mcuProxyPublisher); ok { if publisher, ok := publisher.(*mcuProxyPublisher); ok {
reply := &GetPublisherIdReply{ reply := &GetPublisherIdReply{
PublisherId: publisher.Id(), PublisherId: publisher.Id(),
@ -201,7 +229,7 @@ func (s *GrpcServer) GetSessionCount(ctx context.Context, request *GetSessionCou
return nil, status.Error(codes.InvalidArgument, "invalid url") return nil, status.Error(codes.InvalidArgument, "invalid url")
} }
backend := s.hub.backend.GetBackend(u) backend := s.hub.GetBackend(u)
if backend == nil { if backend == nil {
return nil, status.Error(codes.NotFound, "no such backend") return nil, status.Error(codes.NotFound, "no such backend")
} }
@ -210,3 +238,21 @@ func (s *GrpcServer) GetSessionCount(ctx context.Context, request *GetSessionCou
Count: uint32(backend.Len()), Count: uint32(backend.Len()),
}, nil }, 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()
}

View file

@ -28,6 +28,7 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"errors"
"net" "net"
"os" "os"
"path" "path"
@ -40,6 +41,24 @@ import (
"google.golang.org/grpc/credentials" "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) { func NewGrpcServerForTestWithConfig(t *testing.T, config *goconf.ConfigFile) (server *GrpcServer, addr string) {
for port := 50000; port < 50100; port++ { for port := 50000; port < 50100; port++ {
addr = net.JoinHostPort("127.0.0.1", strconv.Itoa(port)) addr = net.JoinHostPort("127.0.0.1", strconv.Itoa(port))
@ -79,6 +98,7 @@ func NewGrpcServerForTest(t *testing.T) (server *GrpcServer, addr string) {
} }
func Test_GrpcServer_ReloadCerts(t *testing.T) { func Test_GrpcServer_ReloadCerts(t *testing.T) {
CatchLogForTest(t)
key, err := rsa.GenerateKey(rand.Reader, 1024) key, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -99,8 +119,8 @@ func Test_GrpcServer_ReloadCerts(t *testing.T) {
config.AddOption("grpc", "servercertificate", certFile) config.AddOption("grpc", "servercertificate", certFile)
config.AddOption("grpc", "serverkey", privkeyFile) config.AddOption("grpc", "serverkey", privkeyFile)
UpdateCertificateCheckIntervalForTest(t, time.Millisecond) UpdateCertificateCheckIntervalForTest(t, 0)
_, addr := NewGrpcServerForTestWithConfig(t, config) server, addr := NewGrpcServerForTestWithConfig(t, config)
cp1 := x509.NewCertPool() cp1 := x509.NewCertPool()
if !cp1.AppendCertsFromPEM(cert1) { if !cp1.AppendCertsFromPEM(cert1) {
@ -128,6 +148,13 @@ func Test_GrpcServer_ReloadCerts(t *testing.T) {
cert2 := GenerateSelfSignedCertificateForTesting(t, 1024, org2, key) cert2 := GenerateSelfSignedCertificateForTesting(t, 1024, org2, key)
replaceFile(t, certFile, cert2, 0755) 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() cp2 := x509.NewCertPool()
if !cp2.AppendCertsFromPEM(cert2) { if !cp2.AppendCertsFromPEM(cert2) {
t.Fatalf("could not add certificate") t.Fatalf("could not add certificate")
@ -152,6 +179,7 @@ func Test_GrpcServer_ReloadCerts(t *testing.T) {
} }
func Test_GrpcServer_ReloadCA(t *testing.T) { func Test_GrpcServer_ReloadCA(t *testing.T) {
CatchLogForTest(t)
serverKey, err := rsa.GenerateKey(rand.Reader, 1024) serverKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -180,8 +208,8 @@ func Test_GrpcServer_ReloadCA(t *testing.T) {
config.AddOption("grpc", "serverkey", privkeyFile) config.AddOption("grpc", "serverkey", privkeyFile)
config.AddOption("grpc", "clientca", caFile) config.AddOption("grpc", "clientca", caFile)
UpdateCertificateCheckIntervalForTest(t, time.Millisecond) UpdateCertificateCheckIntervalForTest(t, 0)
_, addr := NewGrpcServerForTestWithConfig(t, config) server, addr := NewGrpcServerForTestWithConfig(t, config)
pool := x509.NewCertPool() pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(serverCert) { if !pool.AppendCertsFromPEM(serverCert) {
@ -217,6 +245,10 @@ func Test_GrpcServer_ReloadCA(t *testing.T) {
clientCert2 := GenerateSelfSignedCertificateForTesting(t, 1024, org2, clientKey) clientCert2 := GenerateSelfSignedCertificateForTesting(t, 1024, org2, clientKey)
replaceFile(t, caFile, clientCert2, 0755) replaceFile(t, caFile, clientCert2, 0755)
if err := server.WaitForCertPoolReload(ctx1); err != nil {
t.Fatal(err)
}
pair2, err := tls.X509KeyPair(clientCert2, pem.EncodeToMemory(&pem.Block{ pair2, err := tls.X509KeyPair(clientCert2, pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY", Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(clientKey), Bytes: x509.MarshalPKCS1PrivateKey(clientKey),

View file

@ -26,8 +26,18 @@ option go_package = "github.com/strukturag/nextcloud-spreed-signaling;signaling"
package signaling; package signaling;
service RpcSessions { service RpcSessions {
rpc LookupResumeId(LookupResumeIdRequest) returns (LookupResumeIdReply) {}
rpc LookupSessionId(LookupSessionIdRequest) returns (LookupSessionIdReply) {} rpc LookupSessionId(LookupSessionIdRequest) returns (LookupSessionIdReply) {}
rpc IsSessionInCall(IsSessionInCallRequest) returns (IsSessionInCallReply) {} rpc IsSessionInCall(IsSessionInCallRequest) returns (IsSessionInCallReply) {}
rpc ProxySession(stream ClientSessionMessage) returns (stream ServerSessionMessage) {}
}
message LookupResumeIdRequest {
string resumeId = 1;
}
message LookupResumeIdReply {
string sessionId = 1;
} }
message LookupSessionIdRequest { message LookupSessionIdRequest {
@ -49,3 +59,11 @@ message IsSessionInCallRequest {
message IsSessionInCallReply { message IsSessionInCallReply {
bool inCall = 1; bool inCall = 1;
} }
message ClientSessionMessage {
bytes message = 1;
}
message ServerSessionMessage {
bytes message = 1;
}

View file

@ -29,10 +29,18 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"sync" "sync"
"github.com/prometheus/client_golang/prometheus"
) )
func init() {
RegisterHttpClientPoolStats()
}
type Pool struct { type Pool struct {
pool chan *http.Client pool chan *http.Client
currentConnections prometheus.Gauge
} }
func (p *Pool) get(ctx context.Context) (client *http.Client, err error) { func (p *Pool) get(ctx context.Context) (client *http.Client, err error) {
@ -40,21 +48,24 @@ func (p *Pool) get(ctx context.Context) (client *http.Client, err error) {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return nil, ctx.Err()
case client := <-p.pool: case client := <-p.pool:
p.currentConnections.Inc()
return client, nil return client, nil
} }
} }
func (p *Pool) Put(c *http.Client) { func (p *Pool) Put(c *http.Client) {
p.currentConnections.Dec()
p.pool <- c p.pool <- c
} }
func newPool(constructor func() *http.Client, size int) (*Pool, error) { func newPool(host string, constructor func() *http.Client, size int) (*Pool, error) {
if size <= 0 { if size <= 0 {
return nil, fmt.Errorf("can't create empty pool") return nil, fmt.Errorf("can't create empty pool")
} }
p := &Pool{ p := &Pool{
pool: make(chan *http.Client, size), pool: make(chan *http.Client, size),
currentConnections: connectionsPerHostCurrent.WithLabelValues(host),
} }
for size > 0 { for size > 0 {
c := constructor() c := constructor()
@ -84,6 +95,7 @@ func NewHttpClientPool(maxConcurrentRequestsPerHost int, skipVerify bool) (*Http
transport := &http.Transport{ transport := &http.Transport{
MaxIdleConnsPerHost: maxConcurrentRequestsPerHost, MaxIdleConnsPerHost: maxConcurrentRequestsPerHost,
TLSClientConfig: tlsconfig, TLSClientConfig: tlsconfig,
Proxy: http.ProxyFromEnvironment,
} }
result := &HttpClientPool{ result := &HttpClientPool{
@ -102,7 +114,7 @@ func (p *HttpClientPool) getPool(url *url.URL) (*Pool, error) {
return pool, nil return pool, nil
} }
pool, err := newPool(func() *http.Client { pool, err := newPool(url.Host, func() *http.Client {
return &http.Client{ return &http.Client{
Transport: p.transport, Transport: p.transport,
// Only send body in redirect if going to same scheme / host. // Only send body in redirect if going to same scheme / host.

View file

@ -0,0 +1,43 @@
/**
* 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 (
"github.com/prometheus/client_golang/prometheus"
)
var (
connectionsPerHostCurrent = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: "signaling",
Subsystem: "http_client_pool",
Name: "connections",
Help: "The current number of HTTP client connections per host",
}, []string{"host"})
httpClientPoolStats = []prometheus.Collector{
connectionsPerHostCurrent,
}
)
func RegisterHttpClientPoolStats() {
registerAll(httpClientPoolStats...)
}

View file

@ -29,6 +29,7 @@ import (
) )
func TestHttpClientPool(t *testing.T) { func TestHttpClientPool(t *testing.T) {
t.Parallel()
if _, err := NewHttpClientPool(0, false); err == nil { if _, err := NewHttpClientPool(0, false); err == nil {
t.Error("should not be possible to create empty pool") t.Error("should not be possible to create empty pool")
} }

899
hub.go

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -172,7 +172,7 @@ func unexpected(request string) error {
type transaction struct { type transaction struct {
ch chan interface{} ch chan interface{}
incoming chan interface{} incoming chan interface{}
quitChan chan bool closer *Closer
} }
func (t *transaction) run() { func (t *transaction) run() {
@ -180,7 +180,7 @@ func (t *transaction) run() {
select { select {
case msg := <-t.incoming: case msg := <-t.incoming:
t.ch <- msg t.ch <- msg
case <-t.quitChan: case <-t.closer.C:
return return
} }
} }
@ -191,18 +191,14 @@ func (t *transaction) add(msg interface{}) {
} }
func (t *transaction) quit() { func (t *transaction) quit() {
select { t.closer.Close()
case t.quitChan <- true:
default:
// Already scheduled to quit.
}
} }
func newTransaction() *transaction { func newTransaction() *transaction {
t := &transaction{ t := &transaction{
ch: make(chan interface{}, 1), ch: make(chan interface{}, 1),
incoming: make(chan interface{}, 8), incoming: make(chan interface{}, 8),
quitChan: make(chan bool, 1), closer: NewCloser(),
} }
return t return t
} }
@ -225,8 +221,6 @@ func (l *dummyGatewayListener) ConnectionInterrupted() {
// Gateway represents a connection to an instance of the Janus Gateway. // Gateway represents a connection to an instance of the Janus Gateway.
type JanusGateway struct { type JanusGateway struct {
nextTransaction uint64
listener GatewayListener listener GatewayListener
// Sessions is a map of the currently active sessions to the gateway. // Sessions is a map of the currently active sessions to the gateway.
@ -236,10 +230,11 @@ type JanusGateway struct {
// and Gateway.Unlock() methods provided by the embedded sync.Mutex. // and Gateway.Unlock() methods provided by the embedded sync.Mutex.
sync.Mutex sync.Mutex
conn *websocket.Conn conn *websocket.Conn
transactions map[uint64]*transaction nextTransaction atomic.Uint64
transactions map[uint64]*transaction
closeChan chan bool closer *Closer
writeMu sync.Mutex writeMu sync.Mutex
} }
@ -263,21 +258,22 @@ type JanusGateway struct {
// return gateway, nil // return gateway, nil
// } // }
func NewJanusGateway(wsURL string, listener GatewayListener) (*JanusGateway, error) { func NewJanusGateway(ctx context.Context, wsURL string, listener GatewayListener) (*JanusGateway, error) {
conn, _, err := janusDialer.Dial(wsURL, nil) conn, _, err := janusDialer.DialContext(ctx, wsURL, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
gateway := new(JanusGateway)
gateway.conn = conn
gateway.transactions = make(map[uint64]*transaction)
gateway.Sessions = make(map[uint64]*JanusSession)
gateway.closeChan = make(chan bool)
if listener == nil { if listener == nil {
listener = new(dummyGatewayListener) listener = new(dummyGatewayListener)
} }
gateway.listener = listener gateway := &JanusGateway{
conn: conn,
listener: listener,
transactions: make(map[uint64]*transaction),
Sessions: make(map[uint64]*JanusSession),
closer: NewCloser(),
}
go gateway.ping() go gateway.ping()
go gateway.recv() go gateway.recv()
@ -286,7 +282,7 @@ func NewJanusGateway(wsURL string, listener GatewayListener) (*JanusGateway, err
// Close closes the underlying connection to the Gateway. // Close closes the underlying connection to the Gateway.
func (gateway *JanusGateway) Close() error { func (gateway *JanusGateway) Close() error {
gateway.closeChan <- true gateway.closer.Close()
gateway.writeMu.Lock() gateway.writeMu.Lock()
if gateway.conn == nil { if gateway.conn == nil {
gateway.writeMu.Unlock() gateway.writeMu.Unlock()
@ -314,7 +310,7 @@ func (gateway *JanusGateway) cancelTransactions() {
t.quit() t.quit()
}(t) }(t)
} }
gateway.transactions = make(map[uint64]*transaction) clear(gateway.transactions)
gateway.Unlock() gateway.Unlock()
} }
@ -331,7 +327,7 @@ func (gateway *JanusGateway) removeTransaction(id uint64) {
} }
func (gateway *JanusGateway) send(msg map[string]interface{}, t *transaction) (uint64, error) { func (gateway *JanusGateway) send(msg map[string]interface{}, t *transaction) (uint64, error) {
id := atomic.AddUint64(&gateway.nextTransaction, 1) id := gateway.nextTransaction.Add(1)
msg["transaction"] = strconv.FormatUint(id, 10) msg["transaction"] = strconv.FormatUint(id, 10)
data, err := json.Marshal(msg) data, err := json.Marshal(msg)
if err != nil { if err != nil {
@ -382,7 +378,7 @@ loop:
if err != nil { if err != nil {
log.Println("Error sending ping to MCU:", err) log.Println("Error sending ping to MCU:", err)
} }
case <-gateway.closeChan: case <-gateway.closer.C:
break loop break loop
} }
} }
@ -769,9 +765,11 @@ GetMessage: // No tears..
// a new PeerConnection with a plugin. // a new PeerConnection with a plugin.
// candidate should be a single ICE candidate, or a completed object to // candidate should be a single ICE candidate, or a completed object to
// signify that all candidates have been sent: // signify that all candidates have been sent:
// { //
// "completed": true // {
// } // "completed": true
// }
//
// On success, an AckMsg will be returned and error will be nil. // On success, an AckMsg will be returned and error will be nil.
func (handle *JanusHandle) Trickle(ctx context.Context, candidate interface{}) (*janus.AckMsg, error) { func (handle *JanusHandle) Trickle(ctx context.Context, candidate interface{}) (*janus.AckMsg, error) {
req, ch := newRequest("trickle") req, ch := newRequest("trickle")

View file

@ -66,7 +66,7 @@ type McuInitiator interface {
} }
type Mcu interface { type Mcu interface {
Start() error Start(ctx context.Context) error
Stop() Stop()
Reload(config *goconf.ConfigFile) Reload(config *goconf.ConfigFile)
@ -75,14 +75,77 @@ type Mcu interface {
GetStats() interface{} GetStats() interface{}
NewPublisher(ctx context.Context, listener McuListener, id string, sid string, streamType string, bitrate int, mediaTypes MediaType, initiator McuInitiator) (McuPublisher, error) NewPublisher(ctx context.Context, listener McuListener, id string, sid string, streamType StreamType, bitrate int, mediaTypes MediaType, initiator McuInitiator) (McuPublisher, error)
NewSubscriber(ctx context.Context, listener McuListener, publisher string, streamType string) (McuSubscriber, error) NewSubscriber(ctx context.Context, listener McuListener, publisher string, streamType StreamType, initiator McuInitiator) (McuSubscriber, error)
}
// PublisherStream contains the available properties when creating a
// remote publisher in Janus.
type PublisherStream struct {
Mid string `json:"mid"`
Mindex int `json:"mindex"`
Type string `json:"type"`
Description string `json:"description,omitempty"`
Disabled bool `json:"disabled,omitempty"`
// For types "audio" and "video"
Codec string `json:"codec,omitempty"`
// For type "audio"
Stereo bool `json:"stereo,omitempty"`
Fec bool `json:"fec,omitempty"`
Dtx bool `json:"dtx,omitempty"`
// For type "video"
Simulcast bool `json:"simulcast,omitempty"`
Svc bool `json:"svc,omitempty"`
ProfileH264 string `json:"h264_profile,omitempty"`
ProfileVP9 string `json:"vp9_profile,omitempty"`
ExtIdVideoOrientation int `json:"videoorient_ext_id,omitempty"`
ExtIdPlayoutDelay int `json:"playoutdelay_ext_id,omitempty"`
}
type RemotePublisherController interface {
PublisherId() string
StartPublishing(ctx context.Context, publisher McuRemotePublisherProperties) error
GetStreams(ctx context.Context) ([]PublisherStream, error)
}
type RemoteMcu interface {
NewRemotePublisher(ctx context.Context, listener McuListener, controller RemotePublisherController, streamType StreamType) (McuRemotePublisher, error)
NewRemoteSubscriber(ctx context.Context, listener McuListener, publisher McuRemotePublisher) (McuRemoteSubscriber, error)
}
type StreamType string
const (
StreamTypeAudio StreamType = "audio"
StreamTypeVideo StreamType = "video"
StreamTypeScreen StreamType = "screen"
)
func IsValidStreamType(s string) bool {
switch s {
case string(StreamTypeAudio):
fallthrough
case string(StreamTypeVideo):
fallthrough
case string(StreamTypeScreen):
return true
default:
return false
}
} }
type McuClient interface { type McuClient interface {
Id() string Id() string
Sid() string Sid() string
StreamType() string StreamType() StreamType
MaxBitrate() int
Close(ctx context.Context) Close(ctx context.Context)
@ -94,6 +157,10 @@ type McuPublisher interface {
HasMedia(MediaType) bool HasMedia(MediaType) bool
SetMedia(MediaType) SetMedia(MediaType)
GetStreams(ctx context.Context) ([]PublisherStream, error)
PublishRemote(ctx context.Context, remoteId string, hostname string, port int, rtcpPort int) error
UnpublishRemote(ctx context.Context, remoteId string) error
} }
type McuSubscriber interface { type McuSubscriber interface {
@ -101,3 +168,18 @@ type McuSubscriber interface {
Publisher() string Publisher() string
} }
type McuRemotePublisherProperties interface {
Port() int
RtcpPort() int
}
type McuRemotePublisher interface {
McuClient
McuRemotePublisherProperties
}
type McuRemoteSubscriber interface {
McuSubscriber
}

View file

@ -28,3 +28,43 @@ import (
func TestCommonMcuStats(t *testing.T) { func TestCommonMcuStats(t *testing.T) {
collectAndLint(t, commonMcuStats...) collectAndLint(t, commonMcuStats...)
} }
type MockMcuListener struct {
publicId string
}
func (m *MockMcuListener) PublicId() string {
return m.publicId
}
func (m *MockMcuListener) OnUpdateOffer(client McuClient, offer map[string]interface{}) {
}
func (m *MockMcuListener) OnIceCandidate(client McuClient, candidate interface{}) {
}
func (m *MockMcuListener) OnIceCompleted(client McuClient) {
}
func (m *MockMcuListener) SubscriberSidUpdated(subscriber McuSubscriber) {
}
func (m *MockMcuListener) PublisherClosed(publisher McuPublisher) {
}
func (m *MockMcuListener) SubscriberClosed(subscriber McuSubscriber) {
}
type MockMcuInitiator struct {
country string
}
func (m *MockMcuInitiator) Country() string {
return m.country
}

File diff suppressed because it is too large Load diff

216
mcu_janus_client.go Normal file
View file

@ -0,0 +1,216 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2017 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"
"reflect"
"strconv"
"sync"
"github.com/notedit/janus-go"
)
type mcuJanusClient struct {
mcu *mcuJanus
listener McuListener
mu sync.Mutex // nolint
id uint64
session uint64
roomId uint64
sid string
streamType StreamType
maxBitrate int
handle *JanusHandle
handleId uint64
closeChan chan struct{}
deferred chan func()
handleEvent func(event *janus.EventMsg)
handleHangup func(event *janus.HangupMsg)
handleDetached func(event *janus.DetachedMsg)
handleConnected func(event *janus.WebRTCUpMsg)
handleSlowLink func(event *janus.SlowLinkMsg)
handleMedia func(event *janus.MediaMsg)
}
func (c *mcuJanusClient) Id() string {
return strconv.FormatUint(c.id, 10)
}
func (c *mcuJanusClient) Sid() string {
return c.sid
}
func (c *mcuJanusClient) StreamType() StreamType {
return c.streamType
}
func (c *mcuJanusClient) MaxBitrate() int {
return c.maxBitrate
}
func (c *mcuJanusClient) Close(ctx context.Context) {
}
func (c *mcuJanusClient) SendMessage(ctx context.Context, message *MessageClientMessage, data *MessageClientMessageData, callback func(error, map[string]interface{})) {
}
func (c *mcuJanusClient) closeClient(ctx context.Context) bool {
if handle := c.handle; handle != nil {
c.handle = nil
close(c.closeChan)
if _, err := handle.Detach(ctx); err != nil {
if e, ok := err.(*janus.ErrorMsg); !ok || e.Err.Code != JANUS_ERROR_HANDLE_NOT_FOUND {
log.Println("Could not detach client", handle.Id, err)
}
}
return true
}
return false
}
func (c *mcuJanusClient) run(handle *JanusHandle, closeChan <-chan struct{}) {
loop:
for {
select {
case msg := <-handle.Events:
switch t := msg.(type) {
case *janus.EventMsg:
c.handleEvent(t)
case *janus.HangupMsg:
c.handleHangup(t)
case *janus.DetachedMsg:
c.handleDetached(t)
case *janus.MediaMsg:
c.handleMedia(t)
case *janus.WebRTCUpMsg:
c.handleConnected(t)
case *janus.SlowLinkMsg:
c.handleSlowLink(t)
case *TrickleMsg:
c.handleTrickle(t)
default:
log.Println("Received unsupported event type", msg, reflect.TypeOf(msg))
}
case f := <-c.deferred:
f()
case <-closeChan:
break loop
}
}
}
func (c *mcuJanusClient) sendOffer(ctx context.Context, offer map[string]interface{}, callback func(error, map[string]interface{})) {
handle := c.handle
if handle == nil {
callback(ErrNotConnected, nil)
return
}
configure_msg := map[string]interface{}{
"request": "configure",
"audio": true,
"video": true,
"data": true,
}
answer_msg, err := handle.Message(ctx, configure_msg, offer)
if err != nil {
callback(err, nil)
return
}
callback(nil, answer_msg.Jsep)
}
func (c *mcuJanusClient) sendAnswer(ctx context.Context, answer map[string]interface{}, callback func(error, map[string]interface{})) {
handle := c.handle
if handle == nil {
callback(ErrNotConnected, nil)
return
}
start_msg := map[string]interface{}{
"request": "start",
"room": c.roomId,
}
start_response, err := handle.Message(ctx, start_msg, answer)
if err != nil {
callback(err, nil)
return
}
log.Println("Started listener", start_response)
callback(nil, nil)
}
func (c *mcuJanusClient) sendCandidate(ctx context.Context, candidate interface{}, callback func(error, map[string]interface{})) {
handle := c.handle
if handle == nil {
callback(ErrNotConnected, nil)
return
}
if _, err := handle.Trickle(ctx, candidate); err != nil {
callback(err, nil)
return
}
callback(nil, nil)
}
func (c *mcuJanusClient) handleTrickle(event *TrickleMsg) {
if event.Candidate.Completed {
c.listener.OnIceCompleted(c)
} else {
c.listener.OnIceCandidate(c, event.Candidate)
}
}
func (c *mcuJanusClient) selectStream(ctx context.Context, stream *streamSelection, callback func(error, map[string]interface{})) {
handle := c.handle
if handle == nil {
callback(ErrNotConnected, nil)
return
}
if stream == nil || !stream.HasValues() {
callback(nil, nil)
return
}
configure_msg := map[string]interface{}{
"request": "configure",
}
if stream != nil {
stream.AddToMessage(configure_msg)
}
_, err := handle.Message(ctx, configure_msg, nil)
if err != nil {
callback(err, nil)
return
}
callback(nil, nil)
}

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