From e8b875229bf843396ae56869fc39059e33586879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sun, 31 Jul 2022 13:52:41 +0200 Subject: [PATCH] Add mux support. (#58) * add mux support. * usemux to mux. * lint fix. * dev script mux. * add mux to readme. --- OpenApi.yml | 3 ++ client/src/api/api.ts | 6 ++++ client/src/components/RoomInfo.vue | 11 ++++-- client/src/components/RoomsCreate.vue | 29 +++++++++++++-- client/src/components/RoomsList.vue | 51 +++++++++++++++------------ dev/start | 1 + docs/README.md | 8 +++++ internal/config/room.go | 8 +++++ internal/room/manager.go | 46 ++++++++++++++++++------ internal/types/room.go | 7 ++-- 10 files changed, 130 insertions(+), 40 deletions(-) diff --git a/OpenApi.yml b/OpenApi.yml index d348697..330e120 100644 --- a/OpenApi.yml +++ b/OpenApi.yml @@ -282,6 +282,9 @@ components: storage_enabled: type: boolean example: true + uses_mux: + type: boolean + example: true RoomEntry: type: object diff --git a/client/src/api/api.ts b/client/src/api/api.ts index c64e202..2205155 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -590,6 +590,12 @@ export interface RoomsConfig { * @memberof RoomsConfig */ 'storage_enabled'?: boolean; + /** + * + * @type {boolean} + * @memberof RoomsConfig + */ + 'uses_mux'?: boolean; } /** diff --git a/client/src/components/RoomInfo.vue b/client/src/components/RoomInfo.vue index 0af047c..d9c07cf 100644 --- a/client/src/components/RoomInfo.vue +++ b/client/src/components/RoomInfo.vue @@ -25,10 +25,10 @@ :rotate="270" :size="100" :width="15" - :value="(stats.connections / settings.max_connections) * 100" + :value="usesMux ? 100 : ((stats.connections / settings.max_connections) * 100)" color="blue" > - {{ stats.connections }} / {{ settings.max_connections }} + {{ stats.connections }} @@ -99,7 +99,7 @@ Invite link for admins - Max connections {{ settings.max_connections }} + Max connections {{ settings.max_connections }} Control protection {{ settings.control_protection }} Implicit Control {{ settings.implicit_control }} @@ -274,6 +274,11 @@ export default class RoomInfo extends Vue { } } + get usesMux() { + // old rooms might not use mux despite enabled server wide, simple check for that + return this.$store.state.roomsConfig.uses_mux && this.settings?.max_connections == 1 + } + get allBrowserExtensions() { if (!this.settings?.browser_policy?.type) return {} diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index 712a6ff..8546f3f 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -56,7 +56,28 @@ - + + + + +
Users can gain control only if at least one admin is in the room.
+
+ + +
Users do not need to request control prior usage.
+
+
+ - private headers = [ - { - text: 'Deployment', - value: 'url', - sortable: false, - }, - { text: 'Name', value: 'name' }, - { text: 'Max connections', value: 'max_connections' }, - { text: 'Neko image', value: 'neko_image' }, - { text: 'Status', value: 'status' }, - { text: 'Created', value: 'created' }, - { - text: 'Actions', - value: 'actions', - sortable: false, - }, - { - text: 'Destroy', - value: 'destroy', - align: 'end', - sortable: false, - }, - ] + get headers() { + return [ + { + text: 'Deployment', + value: 'url', + sortable: false, + }, + { text: 'Name', value: 'name' }, + // when using mux we don't have max connections + ...(!this.$store.state.roomsConfig.uses_mux ? [ + { text: 'Max connections', value: 'max_connections' } + ] : []), + { text: 'Neko image', value: 'neko_image' }, + { text: 'Status', value: 'status' }, + { text: 'Created', value: 'created' }, + { + text: 'Actions', + value: 'actions', + sortable: false, + }, + { + text: 'Destroy', + value: 'destroy', + align: 'end', + sortable: false, + }, + ] + } get rooms() { return this.$store.state.rooms diff --git a/dev/start b/dev/start index 69f345a..fbfb64e 100755 --- a/dev/start +++ b/dev/start @@ -21,6 +21,7 @@ docker run --rm -it \ -v "`realpath ${DATA_PATH}`:/data" \ -e "TZ=${TZ}" \ -e "NEKO_ROOMS_EPR=${NEKO_ROOMS_EPR}" \ + -e "NEKO_ROOMS_MUX=true" \ -e "NEKO_ROOMS_NAT1TO1=${NEKO_ROOMS_NAT1TO1}" \ -e "NEKO_ROOMS_STORAGE_INTERNAL=/data" \ -e "NEKO_ROOMS_STORAGE_EXTERNAL=`realpath ${DATA_PATH}`" \ diff --git a/docs/README.md b/docs/README.md index bf957a0..b2e0f3c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,3 +21,11 @@ NEKO_ROOMS_PATH_PREFIX=/test/ ``` Now room will be available at `example.org/test/` instead of `example.org/`. + +## using mux + +When using mux, there will be allocated two ports per room: TCP and UDP port with the same number. + +``` +NEKO_ROOMS_MUX=true +``` diff --git a/internal/config/room.go b/internal/config/room.go index cf9b3da..272f524 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -14,6 +14,7 @@ import ( ) type Room struct { + Mux bool EprMin uint16 EprMax uint16 @@ -40,6 +41,11 @@ type Room struct { } func (Room) Init(cmd *cobra.Command) error { + cmd.PersistentFlags().StringSlice("usemux", []string{}, "use mux for connection - needs only one UDP and TCP port per room") + if err := viper.BindPFlag("usemux", cmd.PersistentFlags().Lookup("usemux")); err != nil { + return err + } + cmd.PersistentFlags().String("epr", "59000-59999", "limits the pool of ephemeral ports that ICE UDP connections can allocate from") if err := viper.BindPFlag("epr", cmd.PersistentFlags().Lookup("epr")); err != nil { return err @@ -147,6 +153,8 @@ func (Room) Init(cmd *cobra.Command) error { } func (s *Room) Set() { + s.Mux = viper.GetBool("mux") + min := uint16(59000) max := uint16(59999) epr := viper.GetString("epr") diff --git a/internal/room/manager.go b/internal/room/manager.go index 286dd81..f8c89d7 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -55,6 +55,7 @@ func (manager *RoomManagerCtx) Config() types.RoomsConfig { Connections: manager.config.EprMax - manager.config.EprMin + 1, NekoImages: manager.config.NekoImages, StorageEnabled: manager.config.StorageEnabled, + UsesMux: manager.config.Mux, } } @@ -115,27 +116,42 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro // Allocate ports // - epr, err := manager.allocatePorts(settings.MaxConnections) + portsNeeded := settings.MaxConnections + if manager.config.Mux { + portsNeeded = 1 + } + + epr, err := manager.allocatePorts(portsNeeded) if err != nil { return "", err } portBindings := nat.PortMap{} - exposedPorts := nat.PortSet{ - nat.Port(fmt.Sprintf("%d/tcp", frontendPort)): struct{}{}, - } - for port := epr.Min; port <= epr.Max; port++ { - portKey := nat.Port(fmt.Sprintf("%d/udp", port)) - - portBindings[portKey] = []nat.PortBinding{ + portBindings[nat.Port(fmt.Sprintf("%d/udp", port))] = []nat.PortBinding{ { HostIP: "0.0.0.0", HostPort: fmt.Sprintf("%d", port), }, } - exposedPorts[portKey] = struct{}{} + // expose TCP port as well when using mux + if manager.config.Mux { + portBindings[nat.Port(fmt.Sprintf("%d/tcp", port))] = []nat.PortBinding{ + { + HostIP: "0.0.0.0", + HostPort: fmt.Sprintf("%d", port), + }, + } + } + } + + exposedPorts := nat.PortSet{ + nat.Port(fmt.Sprintf("%d/tcp", frontendPort)): struct{}{}, + } + + for port := range portBindings { + exposedPorts[port] = struct{}{} } // @@ -229,10 +245,20 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro env := []string{ fmt.Sprintf("NEKO_BIND=:%d", frontendPort), - fmt.Sprintf("NEKO_EPR=%d-%d", epr.Min, epr.Max), "NEKO_ICELITE=true", } + if manager.config.Mux { + env = append(env, + fmt.Sprintf("NEKO_UDPMUX=%d", epr.Min), + fmt.Sprintf("NEKO_TCPMUX=%d", epr.Min), + ) + } else { + env = append(env, + fmt.Sprintf("NEKO_EPR=%d-%d", epr.Min, epr.Max), + ) + } + // optional nat mapping if len(manager.config.NAT1To1IPs) > 0 { env = append(env, fmt.Sprintf("NEKO_NAT1TO1=%s", strings.Join(manager.config.NAT1To1IPs, ","))) diff --git a/internal/types/room.go b/internal/types/room.go index 1290da9..e0e266d 100644 --- a/internal/types/room.go +++ b/internal/types/room.go @@ -19,6 +19,8 @@ var blacklistedEnvs = []string{ // ignore bunch of envs managed by neko-rooms "NEKO_BIND", "NEKO_EPR", + "NEKO_UDPMUX", + "NEKO_TCPMUX", "NEKO_NAT1TO1", "NEKO_ICELITE", } @@ -27,6 +29,7 @@ type RoomsConfig struct { Connections uint16 `json:"connections"` NekoImages []string `json:"neko_images"` StorageEnabled bool `json:"storage_enabled"` + UsesMux bool `json:"uses_mux"` } type RoomEntry struct { @@ -35,7 +38,7 @@ type RoomEntry struct { Name string `json:"name"` NekoImage string `json:"neko_image"` IsOutdated bool `json:"is_outdated"` - MaxConnections uint16 `json:"max_connections"` + MaxConnections uint16 `json:"max_connections"` // ignored when using mux Running bool `json:"running"` Status string `json:"status"` Created time.Time `json:"created"` @@ -66,7 +69,7 @@ type RoomResources struct { type RoomSettings struct { Name string `json:"name"` NekoImage string `json:"neko_image"` - MaxConnections uint16 `json:"max_connections"` + MaxConnections uint16 `json:"max_connections"` // ignored when using mux ControlProtection bool `json:"control_protection"` ImplicitControl bool `json:"implicit_control"`